Now all modules are in core modules folder
This commit is contained in:
parent
5ba1cdfa0b
commit
05b6a91b0c
1907 changed files with 0 additions and 0 deletions
|
@ -0,0 +1,218 @@
|
|||
<?php
|
||||
/**
|
||||
* CSS Compressor [VERSION]
|
||||
* [DATE]
|
||||
* Corey Hart @ http://www.codenothing.com
|
||||
*/
|
||||
|
||||
class CSSCompression_Cleanup {
|
||||
|
||||
/**
|
||||
* Cleanup patterns
|
||||
*
|
||||
* @class Control: Compression Controller
|
||||
* @param (string) token: Copy of the injection token
|
||||
* @param (regex) rtoken: Token regex built upon instantiation
|
||||
* @param (array) options: Reference to options
|
||||
* @param (regex) rsemi: Checks for last semit colon in details
|
||||
* @param (regex) rsemicolon: Checks for semicolon without an escape '\' character before it
|
||||
* @param (regex) rspace: Checks for space without an escape '\' character before it
|
||||
* @param (regex) rcolon: Checks for colon without an escape '\' character before it
|
||||
* @param (regex) rquote: Checks for quote (') without an escape '\' character before it
|
||||
* @param (array) rescape: Array of patterns for groupings that should be escaped
|
||||
* @param (array) escaped: Contains patterns and replacements for espaced characters
|
||||
*/
|
||||
private $Control;
|
||||
private $token = '';
|
||||
private $rtoken = '';
|
||||
private $options = array();
|
||||
private $rsemi = "/;$/";
|
||||
private $rsemicolon = "/(?<!\\\);/";
|
||||
private $rspace = "/(?<!\\\)\s/";
|
||||
private $rcolon = "/(?<!\\\):/";
|
||||
private $rquote = "/(?<!\\\)'/";
|
||||
private $rescape = array(
|
||||
"/((?<!\\\)\")(.*?)((?<!\\\)\")/",
|
||||
"/((?<!\\\)')(.*?)((?<!\\\)')/",
|
||||
"/(url\()(.*?)(\))/",
|
||||
);
|
||||
private $escaped = array(
|
||||
'search' => array(
|
||||
"\\:",
|
||||
"\\;",
|
||||
"\\}",
|
||||
"\\{",
|
||||
"\\@",
|
||||
"\\!",
|
||||
"\\,",
|
||||
"\\>",
|
||||
"\\+",
|
||||
"\\~",
|
||||
"\\/",
|
||||
"\\*",
|
||||
"\\.",
|
||||
"\\=",
|
||||
"\\#",
|
||||
"\\r",
|
||||
"\\n",
|
||||
"\\t",
|
||||
"\\ ",
|
||||
),
|
||||
'replace' => array(
|
||||
":",
|
||||
";",
|
||||
"}",
|
||||
"{",
|
||||
"@",
|
||||
"!",
|
||||
",",
|
||||
">",
|
||||
"+",
|
||||
"~",
|
||||
"/",
|
||||
"*",
|
||||
".",
|
||||
"=",
|
||||
"#",
|
||||
"\r",
|
||||
"\n",
|
||||
"\t",
|
||||
" ",
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Build the token regex based on defined token
|
||||
*
|
||||
* @param (class) control: CSSCompression Controller
|
||||
*/
|
||||
public function __construct( CSSCompression_Control $control ) {
|
||||
$this->Control = $control;
|
||||
$this->token = CSSCompression::TOKEN;
|
||||
$this->options = &$control->Option->options;
|
||||
|
||||
// Have to build the token regexs after initialization
|
||||
$this->rtoken = "/($this->token)(.*?)($this->token)/";
|
||||
array_push( $this->rescape, $this->rtoken );
|
||||
}
|
||||
|
||||
/**
|
||||
* Central cleanup process, removes all injections
|
||||
*
|
||||
* @param (array) selectors: Array of selectors
|
||||
* @param (array) details: Array of details
|
||||
*/
|
||||
public function cleanup( &$selectors, &$details ) {
|
||||
foreach ( $details as $i => &$value ) {
|
||||
// Auto skip sections
|
||||
if ( isset( $selectors[$i] ) && strpos( $selectors[$i], $this->token ) === 0 ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Removing dupes
|
||||
if ( $this->options['rm-multi-define'] ) {
|
||||
$value = $this->removeMultipleDefinitions( $value );
|
||||
}
|
||||
|
||||
$value = $this->removeUnnecessarySemicolon( $value );
|
||||
}
|
||||
|
||||
return array($selectors, $details);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes '\' from possible splitter characters in URLs
|
||||
*
|
||||
* @param (string) css: Full css sheet
|
||||
*/
|
||||
public function removeInjections( $css ) {
|
||||
// Remove escaped characters
|
||||
foreach ( $this->rescape as $regex ) {
|
||||
$pos = 0;
|
||||
while ( preg_match( $regex, $css, $match, PREG_OFFSET_CAPTURE, $pos ) ) {
|
||||
$value = $match[1][0]
|
||||
. str_replace( $this->escaped['search'], $this->escaped['replace'], $match[2][0] )
|
||||
. $match[3][0];
|
||||
$css = substr_replace( $css, $value, $match[0][1], strlen( $match[0][0] ) );
|
||||
$pos = $match[0][1] + strlen( $value ) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove token injections
|
||||
$pos = 0;
|
||||
while ( preg_match( $this->rtoken, $css, $match, PREG_OFFSET_CAPTURE, $pos ) ) {
|
||||
$value = $match[2][0];
|
||||
$id = substr( $css, $match[0][1] - 4, 4 ) == '[id=' ? true : false;
|
||||
$class = substr( $css, $match[0][1] - 7, 7 ) == '[class=' ? true : false;
|
||||
|
||||
if ( preg_match( $this->rspace, $value ) || ( ! $id && ! $class ) ) {
|
||||
$quote = preg_match( $this->rquote, $value ) ? "\"" : "'";
|
||||
$value = "$quote$value$quote";
|
||||
$css = substr_replace( $css, $value, $match[0][1], strlen( $match[0][0] ) );
|
||||
$pos = $match[0][1] + strlen( $value ) + 1;
|
||||
}
|
||||
else {
|
||||
$css = substr_replace( $css, $value, $match[0][1], strlen( $match[0][0] ) );
|
||||
$pos = $match[0][1] + strlen( $value ) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return $css;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes multiple definitions that were created during compression
|
||||
*
|
||||
* @param (string) val: CSS Selector Properties
|
||||
*/
|
||||
private function removeMultipleDefinitions( $val = '' ) {
|
||||
$storage = array();
|
||||
$arr = preg_split( $this->rsemicolon, $val );
|
||||
|
||||
foreach ( $arr as $x ) {
|
||||
if ( $x ) {
|
||||
list( $a, $b ) = preg_split( $this->rcolon, $x, 2 );
|
||||
$storage[$a] = $b;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $storage ) {
|
||||
$val = '';
|
||||
foreach ( $storage as $x => $y ) {
|
||||
$val .= "$x:$y;";
|
||||
}
|
||||
}
|
||||
|
||||
// Return converted val
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes last semicolons on the final property of a set
|
||||
*
|
||||
* @param (string) value: rule set
|
||||
*/
|
||||
private function removeUnnecessarySemicolon( $value ) {
|
||||
return preg_replace( $this->rsemi, '', $value );
|
||||
}
|
||||
|
||||
/**
|
||||
* Access to private methods for testing
|
||||
*
|
||||
* @param (string) method: Method to be called
|
||||
* @param (array) args: Array of paramters to be passed in
|
||||
*/
|
||||
public function access( $method, $args ) {
|
||||
if ( method_exists( $this, $method ) ) {
|
||||
if ( $method == 'cleanup' ) {
|
||||
return $this->cleanup( $args[0], $args[1] );
|
||||
}
|
||||
else {
|
||||
return call_user_func_array( array($this, $method), $args );
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new CSSCompression_Exception( "Unknown method in Cleanup Class - " . $method );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,196 @@
|
|||
<?php
|
||||
/**
|
||||
* CSS Compressor [VERSION]
|
||||
* [DATE]
|
||||
* Corey Hart @ http://www.codenothing.com
|
||||
*/
|
||||
|
||||
class CSSCompression_Color {
|
||||
/**
|
||||
* Color Patterns
|
||||
*
|
||||
* @class Control: Compression Controller
|
||||
* @param (array) options: Reference to options array
|
||||
* @param (regex) rrgb: Checks for rgb notation
|
||||
* @param (regex) rhex: Checks for hex code
|
||||
* @param (regex) rfullhex: Checks for full 6 character hex code
|
||||
* @static (array) color2hex: Long color name to hex code conversions
|
||||
* @static (array) hex2short: Hex code to short color name conversions
|
||||
* @static (array) hex2short_safe: CSS Level 1 safe color names that are shorter than hex codes
|
||||
* @static (array) files: List of static helpers with their class vars
|
||||
*/
|
||||
private $Control;
|
||||
private $options = array();
|
||||
private $rrgb = "/^rgb\((\d{1,3}\%?(,\d{1,3}\%?,\d{1,3}\%?)?)\)$/i";
|
||||
private $rhex = "/^#([0-9a-f]{3}|[0-9a-f]{6})$/i";
|
||||
private $rfullhex = "/^#([0-9a-f]{6})$/i";
|
||||
private static $color2hex = array();
|
||||
private static $hex2short = array();
|
||||
private static $hex2short_safe = array();
|
||||
private static $files = array(
|
||||
'color2hex' => 'long2hex-colors.json',
|
||||
'hex2short' => 'hex2short-colors.json',
|
||||
'hex2short_safe' => 'hex2short-safe.json',
|
||||
);
|
||||
|
||||
/**
|
||||
* Stash a reference to the controller on each instantiation
|
||||
* and install conversion helpers
|
||||
*
|
||||
* @param (class) control: CSSCompression Controller
|
||||
*/
|
||||
public function __construct( CSSCompression_Control $control ) {
|
||||
$this->Control = $control;
|
||||
$this->options = &$control->Option->options;
|
||||
|
||||
if ( ! self::$color2hex ) {
|
||||
foreach ( self::$files as $v => $file ) {
|
||||
self::$$v = CSSCompression::getJSON( $file );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Central handler for all color conversions.
|
||||
*
|
||||
* @param (string) val: Color to be parsed
|
||||
*/
|
||||
public function color( $val ) {
|
||||
// Converts rgb values to hex codes
|
||||
if ( $this->options['color-rgb2hex'] ) {
|
||||
$val = $this->rgb2hex( $val );
|
||||
}
|
||||
|
||||
// Convert long color names to hex codes
|
||||
if ( $this->options['color-long2hex'] ) {
|
||||
$val = $this->color2hex( $val );
|
||||
}
|
||||
|
||||
// Ensure all hex codes are lowercase
|
||||
if ( preg_match( $this->rhex, $val ) ) {
|
||||
$val = strtolower( $val );
|
||||
}
|
||||
|
||||
// Convert large hex codes to small codes
|
||||
if ( $this->options['color-hex2shorthex'] ) {
|
||||
$val = $this->hex2short( $val );
|
||||
}
|
||||
|
||||
// Convert 6 digit hex codes to short color names
|
||||
if ( $this->options['color-hex2shortcolor'] ) {
|
||||
$val = $this->hex2color( $val );
|
||||
}
|
||||
|
||||
// Convert safe css level1 color names
|
||||
if ( $this->options['color-hex2safe'] ) {
|
||||
$val = $this->hex2safe( $val );
|
||||
}
|
||||
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts rgb values to hex codes
|
||||
*
|
||||
* @param (string) val: Color to be converted
|
||||
*/
|
||||
private function rgb2hex( $val ) {
|
||||
if ( ! preg_match( $this->rrgb, $val, $match ) ) {
|
||||
return $val;
|
||||
}
|
||||
|
||||
// locals
|
||||
$hex = '0123456789abcdef';
|
||||
$str = explode( ',', $match[1] );
|
||||
$new = '';
|
||||
|
||||
// Incase rgb was defined with single val
|
||||
if ( ! $str ) {
|
||||
$str = array($match[1]);
|
||||
}
|
||||
|
||||
foreach ( $str as $x ) {
|
||||
$x = strpos( $x, '%' ) !== false ? intval( ( intval( $x ) / 100 ) * 255 ) : intval( $x );
|
||||
|
||||
if ( $x > 255 ) {
|
||||
$x = 255;
|
||||
}
|
||||
|
||||
if ( $x < 0 ) {
|
||||
$x = 0;
|
||||
}
|
||||
|
||||
$new .= $hex[( $x - $x % 16 ) / 16];
|
||||
$new .= $hex[$x % 16];
|
||||
}
|
||||
|
||||
// Repeat hex code to complete 6 digit hex requirement for single definitions
|
||||
if ( count( $str ) == 1 ) {
|
||||
$new .= $new . $new;
|
||||
}
|
||||
|
||||
// Replace with hex value
|
||||
return "#$new";
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert long color names to hex codes
|
||||
*
|
||||
* @param (string) val: Color to be converted
|
||||
*/
|
||||
private function color2hex( $val ) {
|
||||
return isset( self::$color2hex[$val] ) ? self::$color2hex[$val] : $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert large hex codes to small codes
|
||||
*
|
||||
* @param (string) val: Hex to be shortened
|
||||
*/
|
||||
private function hex2short( $val ) {
|
||||
if ( ! preg_match( $this->rfullhex, $val, $match ) ) {
|
||||
return $val;
|
||||
}
|
||||
|
||||
// See if we can convert to 3 char hex
|
||||
$hex = $match[1];
|
||||
if ( $hex[0] == $hex[1] && $hex[2] == $hex[3] && $hex[4] == $hex[5] ) {
|
||||
$val = '#' . $hex[0] . $hex[2] . $hex[4];
|
||||
}
|
||||
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert large hex codes to small codes
|
||||
*
|
||||
* @param (string) val: Color to be converted
|
||||
*/
|
||||
private function hex2color( $val ) {
|
||||
return isset( self::$hex2short[$val] ) ? self::$hex2short[$val] : $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert large hex codes to small codes
|
||||
*
|
||||
* @param (string) val: Color to be converted
|
||||
*/
|
||||
private function hex2safe( $val ) {
|
||||
return isset( self::$hex2short_safe[$val] ) ? self::$hex2short_safe[$val] : $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access to private methods for testing
|
||||
*
|
||||
* @param (string) method: Method to be called
|
||||
* @param (array) args: Array of paramters to be passed in
|
||||
*/
|
||||
public function access( $method, $args ) {
|
||||
if ( method_exists( $this, $method ) ) {
|
||||
return call_user_func_array( array($this, $method), $args );
|
||||
}
|
||||
else {
|
||||
throw new CSSCompression_Exception( "Unknown method in Color Class - " . $method );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,188 @@
|
|||
<?php
|
||||
/**
|
||||
* CSS Compressor [VERSION]
|
||||
* [DATE]
|
||||
* Corey Hart @ http://www.codenothing.com
|
||||
*/
|
||||
|
||||
class CSSCompression_Combine {
|
||||
/**
|
||||
* Combine Patterns
|
||||
*
|
||||
* @class Control: Compression Controller
|
||||
* @param (string) token: Copy of the injection token
|
||||
* @param (array) options: Reference to options
|
||||
* @param (regex) rspace: Checks for space without an escape '\' character before it
|
||||
* @param (regex) rslash: Checks for unescaped slash character
|
||||
* @param (regex) rimportant: Checking props for uncombinables
|
||||
* @param (array) methods: List of options with their corresponding class
|
||||
*/
|
||||
private $Control;
|
||||
private $token = '';
|
||||
private $options = array();
|
||||
private $rspace = "/(?<!\\\)\s/";
|
||||
private $rslash = "/(?<!\\\)\//";
|
||||
private $rimportant = "/inherit|\!important|\!ie|(?<!\\\)\s/i";
|
||||
private $methods = array(
|
||||
'csw-combine' => 'BorderOutline',
|
||||
'border-radius-combine' => 'BorderRadius',
|
||||
'border-combine' => 'Border',
|
||||
'mp-combine' => 'MarginPadding',
|
||||
'background-combine' => 'Background',
|
||||
'auralcp-combine' => 'Aural',
|
||||
'font-combine' => 'Font',
|
||||
'list-combine' => 'List',
|
||||
);
|
||||
|
||||
/**
|
||||
* Sub Comination Classes
|
||||
*
|
||||
* @class BorderOutline: Handles Color/Style/With combinations of border/outline properties
|
||||
* @class BorderRadius: Handles border-radius combinations
|
||||
* @class Border: Handles normal border combinations
|
||||
* @class MarginPadding: Handles margin/padding combinations
|
||||
* @class Background: Handles background combinations
|
||||
* @class Aural: Handles aural combinations
|
||||
* @class Font: Handles font combinations
|
||||
* @class List: Handles list combinations
|
||||
* @param (array) subcombines: Array holding all subcombination classes
|
||||
*/
|
||||
public $BorderOutline;
|
||||
public $BorderRadius;
|
||||
public $Border;
|
||||
public $MarginPadding;
|
||||
public $Background;
|
||||
public $Aural;
|
||||
public $Font;
|
||||
public $List;
|
||||
private $subcombines = array(
|
||||
'BorderOutline',
|
||||
'BorderRadius',
|
||||
'Border',
|
||||
'MarginPadding',
|
||||
'Background',
|
||||
'Aural',
|
||||
'Font',
|
||||
'List',
|
||||
);
|
||||
|
||||
/**
|
||||
* Stash a reference to the controller on each instantiation
|
||||
*
|
||||
* @param (class) control: CSSCompression Controller
|
||||
*/
|
||||
public function __construct( CSSCompression_Control $control ) {
|
||||
$this->Control = $control;
|
||||
$this->token = CSSCompression::TOKEN;
|
||||
$this->options = &$control->Option->options;
|
||||
|
||||
// Include classes if not already done so
|
||||
if ( ! class_exists( "CSSCompression_Combine_Border", false ) ) {
|
||||
$path = dirname(__FILE__) . '/Combine/';
|
||||
foreach ( $this->subcombines as $class ) {
|
||||
require( $path . $class . '.inc' );
|
||||
}
|
||||
}
|
||||
|
||||
// Instantiate each sub combine
|
||||
foreach ( $this->subcombines as $class ) {
|
||||
$full = "CSSCompression_Combine_$class";
|
||||
$this->$class = new $full( $control, $this );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads through each detailed package and checks for cross defn combinations
|
||||
*
|
||||
* @param (array) selectors: Array of selectors
|
||||
* @param (array) details: Array of details
|
||||
*/
|
||||
public function combine( &$selectors = array(), &$details = array() ) {
|
||||
foreach ( $details as $i => &$value ) {
|
||||
if ( isset( $selectors[$i] ) && strpos( $selectors[$i], $this->token ) === 0 ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ( $this->methods as $option => $class ) {
|
||||
if ( $this->options[$option] ) {
|
||||
$value = $this->$class->combine( $value );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array($selectors, $details);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to ensure flagged words don't get
|
||||
* overridden
|
||||
*
|
||||
* @param (mixed) obj: Array/String of definitions to be checked
|
||||
*/
|
||||
public function checkUncombinables( $obj ) {
|
||||
if ( is_array( $obj ) ) {
|
||||
foreach ( $obj as $item ) {
|
||||
if ( preg_match( $this->rimportant, $item ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return preg_match( $this->rimportant, $obj );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to ensure all values of search array
|
||||
* exist within the storage array
|
||||
*
|
||||
* @param (string) prop: CSS Property
|
||||
* @param (array) storage: Array of definitions found
|
||||
* @param (array) search: Array of definitions requred
|
||||
*/
|
||||
public function searchDefinitions( $prop, $storage, $search ) {
|
||||
// Return if storage & search don't match
|
||||
if ( count( $storage ) != count( $search ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$str = "$prop:";
|
||||
foreach ( $search as $value ) {
|
||||
if ( ! isset( $storage[$value] ) || $this->checkUncombinables( $storage[$value] ) ) {
|
||||
return false;
|
||||
}
|
||||
$str .= $storage[$value] . ' ';
|
||||
}
|
||||
return trim( $str ) . ';';
|
||||
}
|
||||
|
||||
/**
|
||||
* Access to private methods for testing
|
||||
*
|
||||
* @param (string) subclass: Name of subclass to focus on
|
||||
* @param (string) method: Method to be called
|
||||
* @param (array) args: Array of paramters to be passed in
|
||||
*/
|
||||
public function access( $subclass, $method, $args ) {
|
||||
if ( $subclass == 'Combine' ) {
|
||||
if ( method_exists( $this, $method ) ) {
|
||||
if ( $method == 'combine' ) {
|
||||
return $this->combine( $args[0], $args[1] );
|
||||
}
|
||||
else {
|
||||
return call_user_func_array( array($this, $method), $args );
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new CSSCompression_Exception( "Unknown method in Combine Class - " . $method );
|
||||
}
|
||||
}
|
||||
else if ( in_array( $subclass, $this->subcombines ) ) {
|
||||
return $this->$subclass->access( $method, $args );
|
||||
}
|
||||
else {
|
||||
throw new CSSCompression_Exception( "Unknown Sub Combine Class - " . $subclass );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
<?php
|
||||
/**
|
||||
* CSS Compressor [VERSION]
|
||||
* [DATE]
|
||||
* Corey Hart @ http://www.codenothing.com
|
||||
*/
|
||||
|
||||
class CSSCompression_Combine_Aural {
|
||||
/**
|
||||
* Combine Patterns
|
||||
*
|
||||
* @class Control: Compression Controller
|
||||
* @class Combine: Combine Controller
|
||||
* @param (regex) raural: Aurual matching
|
||||
*/
|
||||
private $Control;
|
||||
private $Combine;
|
||||
private $raural = "/(^|(?<!\\\);)(cue|pause)-(before|after):(.*?)((?<!\\\);|$)/";
|
||||
|
||||
/**
|
||||
* Stash a reference to the controller & combiner
|
||||
*
|
||||
* @param (class) control: CSSCompression Controller
|
||||
* @param (class) combine: CSSCompression Combiner
|
||||
*/
|
||||
public function __construct( CSSCompression_Control $control, CSSCompression_Combine $combine ) {
|
||||
$this->Control = $control;
|
||||
$this->Combine = $combine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines Aural properties (currently being depreciated in W3C Standards)
|
||||
*
|
||||
* @param (string) val: Rule Set
|
||||
*/
|
||||
public function combine( $val ) {
|
||||
$storage = $this->storage( $val );
|
||||
$pos = 0;
|
||||
|
||||
// Replace first occurance with it's prop, and remove all following occurances
|
||||
while ( preg_match( $this->raural, $val, $match, PREG_OFFSET_CAPTURE, $pos ) ) {
|
||||
$prop = $match[2][0];
|
||||
if ( isset( $storage[$prop] ) ) {
|
||||
$colon = strlen( $match[1][0] );
|
||||
$val = substr_replace( $val, $storage[$prop], $match[0][1] + $colon, strlen( $match[0][0] ) - $colon );
|
||||
$pos = $match[0][1] + strlen( $storage[$prop] ) - $colon - 1;
|
||||
$storage[$prop] = '';
|
||||
}
|
||||
else {
|
||||
$pos = $match[0][1] + strlen( $match[0][0] ) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Return converted val
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a storage object for iteration
|
||||
*
|
||||
* @param (string) val: Rule Set
|
||||
*/
|
||||
private function storage( $val ) {
|
||||
$storage = array();
|
||||
|
||||
// Find all possible occurences and build the replacement
|
||||
$pos = 0;
|
||||
while ( preg_match( $this->raural, $val, $match, PREG_OFFSET_CAPTURE, $pos ) ) {
|
||||
if ( ! isset( $storage[$match[2][0]] ) ) {
|
||||
$storage[$match[2][0]] = array($match[3][0] => $match[4][0]);
|
||||
}
|
||||
|
||||
// Override double written properties
|
||||
$storage[$match[2][0]][$match[3][0]] = $match[4][0];
|
||||
$pos = $match[0][1] + strlen( $match[0][0] ) - 1;
|
||||
}
|
||||
|
||||
// Go through each tag for possible combination
|
||||
foreach ( $storage as $tag => $arr ) {
|
||||
// All three have to be defined
|
||||
if ( count( $arr ) == 2 && ! $this->Combine->checkUncombinables( $arr ) ) {
|
||||
$storage[$tag] = "$tag:" . $arr['before'] . ' ' . $arr['after'] . ';';
|
||||
}
|
||||
else {
|
||||
unset( $storage[$tag] );
|
||||
}
|
||||
}
|
||||
|
||||
return $storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access to private methods for testing
|
||||
*
|
||||
* @param (string) method: Method to be called
|
||||
* @param (array) args: Array of paramters to be passed in
|
||||
*/
|
||||
public function access( $method, $args ) {
|
||||
if ( method_exists( $this, $method ) ) {
|
||||
return call_user_func_array( array($this, $method), $args );
|
||||
}
|
||||
else {
|
||||
throw new CSSCompression_Exception( "Unknown method in Aural Class - " . $method );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
/**
|
||||
* CSS Compressor [VERSION]
|
||||
* [DATE]
|
||||
* Corey Hart @ http://www.codenothing.com
|
||||
*/
|
||||
|
||||
class CSSCompression_Combine_Background {
|
||||
/**
|
||||
* Combine Patterns
|
||||
*
|
||||
* @class Control: Compression Controller
|
||||
* @class Combine: Combine Controller
|
||||
* @param (regex) rbackground: Background matching
|
||||
* @param (array) groupings: List of background combinations
|
||||
*/
|
||||
private $Control;
|
||||
private $Combine;
|
||||
private $rbackground = "/(^|(?<!\\\);)background-(color|image|repeat|attachment|position):(.*?)((?<!\\\);|$)/";
|
||||
private $groupings = array(
|
||||
// With color
|
||||
array('color', 'image', 'repeat', 'attachment', 'position'),
|
||||
array('color', 'image', 'attachment', 'position'),
|
||||
array('color', 'image', 'repeat', 'position'),
|
||||
array('color', 'image', 'repeat', 'attachment'),
|
||||
array('color', 'image', 'repeat'),
|
||||
array('color', 'image', 'attachment'),
|
||||
array('color', 'image', 'position'),
|
||||
array('color', 'image'),
|
||||
// Without Color
|
||||
array('image', 'attachment', 'position'),
|
||||
array('image', 'repeat', 'position'),
|
||||
array('image', 'repeat', 'attachment'),
|
||||
array('image', 'repeat'),
|
||||
array('image', 'attachment'),
|
||||
array('image', 'position'),
|
||||
// Just Color/Image
|
||||
array('image'),
|
||||
array('color'),
|
||||
);
|
||||
|
||||
/**
|
||||
* Stash a reference to the controller & combiner
|
||||
*
|
||||
* @param (class) control: CSSCompression Controller
|
||||
* @param (class) combine: CSSCompression Combiner
|
||||
*/
|
||||
public function __construct( CSSCompression_Control $control, CSSCompression_Combine $combine ) {
|
||||
$this->Control = $control;
|
||||
$this->Combine = $combine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines multiple background props into single definition
|
||||
*
|
||||
* @param (string) val: Rule Set
|
||||
*/
|
||||
public function combine( $val ) {
|
||||
$storage = array();
|
||||
|
||||
// Find all possible occurences and build the replacement
|
||||
$pos = 0;
|
||||
while ( preg_match( $this->rbackground, $val, $match, PREG_OFFSET_CAPTURE, $pos ) ) {
|
||||
$storage[$match[2][0]] = $match[3][0];
|
||||
$pos = $match[0][1] + strlen( $match[0][0] ) - 1;
|
||||
}
|
||||
|
||||
// Run background checks and get replacement str
|
||||
foreach ( $this->groupings as $props ) {
|
||||
if ( $replace = $this->Combine->searchDefinitions( 'background', $storage, $props ) ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If replacement string found, run it on all declarations
|
||||
if ( $replace ) {
|
||||
$pos = 0;
|
||||
while ( preg_match( $this->rbackground, $val, $match, PREG_OFFSET_CAPTURE, $pos ) ) {
|
||||
$colon = strlen( $match[1][0] );
|
||||
$val = substr_replace( $val, $replace, $match[0][1] + $colon, strlen( $match[0][0] ) - $colon );
|
||||
$pos = $match[0][1] + strlen( $replace ) - $colon - 1;
|
||||
$replace = '';
|
||||
}
|
||||
}
|
||||
|
||||
// Return converted val
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access to private methods for testing
|
||||
*
|
||||
* @param (string) method: Method to be called
|
||||
* @param (array) args: Array of paramters to be passed in
|
||||
*/
|
||||
public function access( $method, $args ) {
|
||||
if ( method_exists( $this, $method ) ) {
|
||||
return call_user_func_array( array($this, $method), $args );
|
||||
}
|
||||
else {
|
||||
throw new CSSCompression_Exception( "Unknown method in Background Class - " . $method );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
/**
|
||||
* CSS Compressor [VERSION]
|
||||
* [DATE]
|
||||
* Corey Hart @ http://www.codenothing.com
|
||||
*/
|
||||
|
||||
class CSSCompression_Combine_Border {
|
||||
/**
|
||||
* Combine Patterns
|
||||
*
|
||||
* @class Control: Compression Controller
|
||||
* @class Combine: Combine Controller
|
||||
* @param (regex) rborder: Border matching
|
||||
*/
|
||||
private $Control;
|
||||
private $Combine;
|
||||
private $rborder = "/(^|(?<!\\\);)border-(top|right|bottom|left):(.*?)((?<!\\\);|$)/";
|
||||
|
||||
/**
|
||||
* Stash a reference to the controller & combiner
|
||||
*
|
||||
* @param (class) control: CSSCompression Controller
|
||||
* @param (class) combine: CSSCompression Combiner
|
||||
*/
|
||||
public function __construct( CSSCompression_Control $control, CSSCompression_Combine $combine ) {
|
||||
$this->Control = $control;
|
||||
$this->Combine = $combine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines multiple border properties into single definition
|
||||
*
|
||||
* @param (string) val: Rule Set
|
||||
*/
|
||||
public function combine( $val ) {
|
||||
if ( ( $replace = $this->replace( $val ) ) === false ) {
|
||||
return $val;
|
||||
}
|
||||
|
||||
// Rebuild the rule set with the combinations found
|
||||
$pos = 0;
|
||||
while ( preg_match( $this->rborder, $val, $match, PREG_OFFSET_CAPTURE, $pos ) ) {
|
||||
$colon = strlen( $match[1][0] );
|
||||
$val = substr_replace( $val, $replace, $match[0][1] + $colon, strlen( $match[0][0] ) - $colon );
|
||||
$pos = $match[0][1] + strlen( $replace ) - $colon - 1;
|
||||
$replace = '';
|
||||
}
|
||||
|
||||
// Return converted val
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a replacement string
|
||||
*
|
||||
* @param (string) val: Rule Set
|
||||
*/
|
||||
private function replace( $val ) {
|
||||
$storage = array();
|
||||
|
||||
// Find all possible occurences and build the replacement
|
||||
$pos = 0;
|
||||
while ( preg_match( $this->rborder, $val, $match, PREG_OFFSET_CAPTURE, $pos ) ) {
|
||||
// Override double written properties
|
||||
$storage[$match[2][0]] = $match[3][0];
|
||||
$pos = $match[0][1] + strlen( $match[0][0] ) - 1;
|
||||
}
|
||||
|
||||
// All 4 have to be defined
|
||||
if ( count( $storage ) == 4 &&
|
||||
$storage['top'] == $storage['bottom'] &&
|
||||
$storage['left'] == $storage['right'] &&
|
||||
$storage['top'] == $storage['right'] ) {
|
||||
return "border:" . $storage['top'] . ';';
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access to private methods for testing
|
||||
*
|
||||
* @param (string) method: Method to be called
|
||||
* @param (array) args: Array of paramters to be passed in
|
||||
*/
|
||||
public function access( $method, $args ) {
|
||||
if ( method_exists( $this, $method ) ) {
|
||||
return call_user_func_array( array($this, $method), $args );
|
||||
}
|
||||
else {
|
||||
throw new CSSCompression_Exception( "Unknown method in Border Class - " . $method );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
<?php
|
||||
/**
|
||||
* CSS Compressor [VERSION]
|
||||
* [DATE]
|
||||
* Corey Hart @ http://www.codenothing.com
|
||||
*/
|
||||
|
||||
class CSSCompression_Combine_BorderOutline {
|
||||
/**
|
||||
* Combine Patterns
|
||||
*
|
||||
* @class Control: Compression Controller
|
||||
* @class Combine: Combine Controller
|
||||
* @param (regex) rcsw: Border/Outline matching
|
||||
*/
|
||||
private $Control;
|
||||
private $Combine;
|
||||
private $rcsw = "/(^|(?<!\\\);)(border|border-top|border-bottom|border-left|border-right|outline)-(color|style|width):(.*?)((?<!\\\);|$)/";
|
||||
|
||||
/**
|
||||
* Stash a reference to the controller & combiner
|
||||
*
|
||||
* @param (class) control: CSSCompression Controller
|
||||
* @param (class) combine: CSSCompression Combiner
|
||||
*/
|
||||
public function __construct( CSSCompression_Control $control, CSSCompression_Combine $combine ) {
|
||||
$this->Control = $control;
|
||||
$this->Combine = $combine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines color/style/width of border/outline properties
|
||||
*
|
||||
* @param (string) val: Rule Set
|
||||
*/
|
||||
public function combine( $val ) {
|
||||
$storage = $this->storage( $val );
|
||||
$pos = 0;
|
||||
|
||||
// Now rebuild the string replacing all instances
|
||||
while ( preg_match( $this->rcsw, $val, $match, PREG_OFFSET_CAPTURE, $pos ) ) {
|
||||
$prop = $match[2][0];
|
||||
if ( isset( $storage[$prop] ) ) {
|
||||
$colon = strlen( $match[1][0] );
|
||||
$val = substr_replace( $val, $storage[$prop], $match[0][1] + $colon, strlen( $match[0][0] ) - $colon );
|
||||
$pos = $match[0][1] + strlen( $storage[$prop] ) - $colon - 1;
|
||||
$storage[$prop] = '';
|
||||
}
|
||||
else {
|
||||
$pos = $match[0][1] + strlen( $match[0][0] ) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Return converted val
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a storage object for iteration
|
||||
*
|
||||
* @param (string) val: Rule Set
|
||||
*/
|
||||
private function storage( $val ) {
|
||||
$storage = array();
|
||||
$pos = 0;
|
||||
|
||||
// Find all possible occurences and build the replacement
|
||||
while ( preg_match( $this->rcsw, $val, $match, PREG_OFFSET_CAPTURE, $pos ) ) {
|
||||
if ( ! isset( $storage[$match[2][0]] ) ) {
|
||||
$storage[$match[2][0]] = array($match[3][0] => $match[4][0]);
|
||||
}
|
||||
|
||||
// Override double written properties
|
||||
$storage[$match[2][0]][$match[3][0]] = $match[4][0];
|
||||
$pos = $match[0][1] + strlen( $match[0][0] ) - 1;
|
||||
}
|
||||
|
||||
// Go through each tag for possible combination
|
||||
foreach ( $storage as $tag => $arr ) {
|
||||
// All three have to be defined
|
||||
if ( count( $arr ) == 3 && ! $this->Combine->checkUncombinables( $arr ) ) {
|
||||
$storage[$tag] = "$tag:" . $arr['width'] . ' ' . $arr['style'] . ' ' . $arr['color'] . ';';
|
||||
}
|
||||
else {
|
||||
unset( $storage[$tag] );
|
||||
}
|
||||
}
|
||||
|
||||
return $storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access to private methods for testing
|
||||
*
|
||||
* @param (string) method: Method to be called
|
||||
* @param (array) args: Array of paramters to be passed in
|
||||
*/
|
||||
public function access( $method, $args ) {
|
||||
if ( method_exists( $this, $method ) ) {
|
||||
return call_user_func_array( array($this, $method), $args );
|
||||
}
|
||||
else {
|
||||
throw new CSSCompression_Exception( "Unknown method in BorderOutline Class - " . $method );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,263 @@
|
|||
<?php
|
||||
/**
|
||||
* CSS Compressor [VERSION]
|
||||
* [DATE]
|
||||
* Corey Hart @ http://www.codenothing.com
|
||||
*/
|
||||
|
||||
class CSSCompression_Combine_BorderRadius {
|
||||
/**
|
||||
* Combine Patterns
|
||||
*
|
||||
* @class Control: Compression Controller
|
||||
* @class Combine: Combine Controller
|
||||
* @param (regex) rspace: Checks for space without an escape '\' character before it
|
||||
* @param (regex) rslash: Checks for unescaped slash character
|
||||
* @param (array) borderRadius: Various border radii components
|
||||
*/
|
||||
private $Control;
|
||||
private $Combine;
|
||||
private $rspace = "/(?<!\\\)\s/";
|
||||
private $rslash = "/(?<!\\\)\//";
|
||||
private $borderRadius = array(
|
||||
'css3' => array(
|
||||
'mod' => '',
|
||||
'base' => "/(^|(?<!\\\);)border-radius:(.*?)((?<!\\\);|$)/",
|
||||
'full' => "/(^|(?<!\\\);)border-(top|bottom)-(left|right)-radius:(.*?)((?<!\\\);|$)/",
|
||||
),
|
||||
'moz' => array(
|
||||
'mod' => '-moz-',
|
||||
'base' => "/(^|(?<!\\\);)-moz-border-radius:(.*?)((?<!\\\);|$)/",
|
||||
'full' => "/(^|(?<!\\\);)-moz-border-radius-(top|bottom)(left|right):(.*?)((?<!\\\);|$)/",
|
||||
),
|
||||
'webkit' => array(
|
||||
'mod' => '-webkit-',
|
||||
'base' => "/(^|(?<!\\\);)-webkit-border-radius:(.*?)((?<!\\\);|$)/",
|
||||
'full' => "/(^|(?<!\\\);)-webkit-border-(top|bottom)-(left|right)-radius:(.*?)((?<!\\\);|$)/",
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Stash a reference to the controller & combiner
|
||||
*
|
||||
* @param (class) control: CSSCompression Controller
|
||||
* @param (class) combine: CSSCompression Combiner
|
||||
*/
|
||||
public function __construct( CSSCompression_Control $control, CSSCompression_Combine $combine ) {
|
||||
$this->Control = $control;
|
||||
$this->Combine = $combine;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Main handler to combine border-radii into a single rule
|
||||
*
|
||||
* @param (string) val: Rule Set
|
||||
*/
|
||||
public function combine( $val ) {
|
||||
foreach ( $this->borderRadius as $regex ) {
|
||||
$val = $this->fix( $val, $regex );
|
||||
}
|
||||
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the actual combining
|
||||
*
|
||||
* @param (string) val: Rule Set
|
||||
*/
|
||||
private function fix( $val, $regex ) {
|
||||
$val = $this->base( $val, $regex );
|
||||
$replace = $regex['mod'];
|
||||
|
||||
// Storage builder
|
||||
if ( ( $storage = $this->storage( $val, $regex ) ) === false ) {
|
||||
return $val;
|
||||
}
|
||||
|
||||
// Setup horizontal/vertical radii
|
||||
foreach ( $storage as $dir => &$config ) {
|
||||
// Verticals are optional
|
||||
if ( $dir == 'vertical' && ! $config['keep'] ) {
|
||||
break;
|
||||
}
|
||||
// All 4 are the same
|
||||
else if ( $config['top-left'] == $config['top-right'] &&
|
||||
$config['top-right'] == $config['bottom-right'] &&
|
||||
$config['bottom-right'] == $config['bottom-left'] ) {
|
||||
$config['replace'] .= $config['top-left'];
|
||||
}
|
||||
// Opposites are the same
|
||||
else if ( $config['top-left'] == $config['bottom-right'] && $config['top-right'] == $config['bottom-left'] ) {
|
||||
$config['replace'] .= $config['top-left'] . ' ' . $config['top-right'];
|
||||
}
|
||||
// 3-point directional
|
||||
else if ( $config['top-right'] == $config['bottom-left'] ) {
|
||||
$config['replace'] .= $config['top-left'] . ' ' . $config['top-right'] . ' ' . $config['bottom-right'];
|
||||
}
|
||||
// none are the same, but can still use shorthand notation
|
||||
else {
|
||||
$config['replace'] .= $config['top-left'] . ' ' . $config['top-right'] . ' '
|
||||
. $config['bottom-right'] . ' ' . $config['bottom-left'];
|
||||
}
|
||||
}
|
||||
|
||||
// Now rebuild the string replacing all instances of margin/padding if shorthand exists
|
||||
$pos = 0;
|
||||
$replace = $regex['mod'] . "border-radius:" . $storage['horizontal']['replace'] . $storage['vertical']['replace'] . ';';
|
||||
while ( preg_match( $regex['full'], $val, $match, PREG_OFFSET_CAPTURE, $pos ) ) {
|
||||
$colon = strlen( $match[1][0] );
|
||||
$val = substr_replace( $val, $replace, $match[0][1] + $colon, strlen( $match[0][0] ) - $colon );
|
||||
$pos = $match[0][1] + strlen( $replace ) - $colon - 1;
|
||||
$replace = '';
|
||||
}
|
||||
|
||||
// Return converted val
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expands short handed border radius props for combination
|
||||
*
|
||||
* @param (string) val: Rule Set
|
||||
*/
|
||||
private function base( $val, $regex ) {
|
||||
$pos = 0;
|
||||
while ( preg_match( $regex['base'], $val, $match, PREG_OFFSET_CAPTURE, $pos ) ) {
|
||||
$replace = '';
|
||||
$colon = strlen( $match[1][0] );
|
||||
$parts = preg_split( $this->rslash, trim( $match[2][0] ), 2 );
|
||||
$positions = array(
|
||||
'top-left' => 0,
|
||||
'top-right' => 0,
|
||||
'bottom-right' => 0,
|
||||
'bottom-left' => 0,
|
||||
);
|
||||
$base = array(
|
||||
'horizontal' => array(
|
||||
'parts' => preg_split( $this->rspace, trim( $parts[0] ) ),
|
||||
'pos' => $positions,
|
||||
),
|
||||
'vertical' => array(
|
||||
'parts' => isset( $parts[1] ) ? preg_split( $this->rspace, trim( $parts[1] ) ) : '',
|
||||
'pos' => $positions,
|
||||
),
|
||||
);
|
||||
|
||||
foreach ( $base as &$config ) {
|
||||
// Skip uncombinables
|
||||
if ( $this->Combine->checkUncombinables( $config['parts'] ) ) {
|
||||
$pos = $match[0][1] + strlen( $match[0][0] ) - 1;
|
||||
continue 2;
|
||||
}
|
||||
// Might not have verticals
|
||||
else if ( $config['parts'] === '' ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Each position needs a value
|
||||
switch ( count( $config['parts'] ) ) {
|
||||
case 1:
|
||||
$config['pos']['top-left'] = $config['pos']['top-right'] = $config['parts'][0];
|
||||
$config['pos']['bottom-left'] = $config['pos']['bottom-right'] = $config['parts'][0];
|
||||
break;
|
||||
case 2:
|
||||
$config['pos']['top-left'] = $config['pos']['bottom-right'] = $config['parts'][0];
|
||||
$config['pos']['bottom-left'] = $config['pos']['top-right'] = $config['parts'][1];
|
||||
break;
|
||||
case 3:
|
||||
$config['pos']['top-left'] = $config['parts'][0];
|
||||
$config['pos']['bottom-left'] = $config['pos']['top-right'] = $config['parts'][1];
|
||||
$config['pos']['bottom-right'] = $config['parts'][2];
|
||||
break;
|
||||
case 4:
|
||||
$config['pos']['top-left'] = $config['parts'][0];
|
||||
$config['pos']['top-right'] = $config['parts'][1];
|
||||
$config['pos']['bottom-right'] = $config['parts'][2];
|
||||
$config['pos']['bottom-left'] = $config['parts'][3];
|
||||
break;
|
||||
default:
|
||||
continue 2;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Build the replacement
|
||||
foreach ( $positions as $p => $v ) {
|
||||
if ( $regex['mod'] == '-moz-' ) {
|
||||
$replace .= "-moz-border-radius-" . preg_replace( "/-/", '', $p ) . ":"
|
||||
. $base['horizontal']['pos'][$p]
|
||||
. ( $base['vertical']['parts'] === '' ? '' : ' ' . $base['vertical']['pos'][$p] )
|
||||
. ';';
|
||||
}
|
||||
else {
|
||||
$replace .= $regex['mod'] . "border-$p-radius:"
|
||||
. $base['horizontal']['pos'][$p]
|
||||
. ( $base['vertical']['parts'] === '' ? '' : ' ' . $base['vertical']['pos'][$p] )
|
||||
. ';';
|
||||
}
|
||||
}
|
||||
$pos += strlen( $replace );
|
||||
$val = substr_replace( $val, $replace, $match[0][1] + $colon, strlen( $match[0][0] ) - $colon );
|
||||
}
|
||||
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the storage object for border radius props
|
||||
*
|
||||
* @param (string) val: Rule Set
|
||||
* @param (array) regex: Current border radius type checking props
|
||||
*/
|
||||
private function storage( $val, $regex ) {
|
||||
$storage = array(
|
||||
'horizontal' => array('replace' => ''),
|
||||
'vertical' => array(
|
||||
'replace' => '',
|
||||
'keep' => false,
|
||||
),
|
||||
);
|
||||
|
||||
// Find all possible occurences of this border-radius type and mark their directional value
|
||||
$pos = 0;
|
||||
while ( preg_match( $regex['full'], $val, $match, PREG_OFFSET_CAPTURE, $pos ) ) {
|
||||
$pos = $match[0][1] + strlen( $match[0][0] ) - 1;
|
||||
$parts = preg_split( $this->rspace, $match[4][0], 2 );
|
||||
$storage['horizontal'][$match[2][0] . '-' . $match[3][0]] = trim( $parts[0] );
|
||||
if ( isset( $parts[1] ) ) {
|
||||
$storage['vertical'][$match[2][0] . '-' . $match[3][0]] = trim( $parts[1] );
|
||||
$storage['vertical']['keep'] = true;
|
||||
$storage['vertical']['replace'] = '/';
|
||||
}
|
||||
else {
|
||||
$storage['vertical'][$match[2][0] . '-' . $match[3][0]] = '0';
|
||||
}
|
||||
}
|
||||
|
||||
// Only combine if all 4 definitions are found (5 including replace)
|
||||
if ( count( $storage['horizontal'] ) != 5 ||
|
||||
$this->Combine->checkUncombinables( $storage['horizontal'] ) ||
|
||||
$this->Combine->checkUncombinables( $storage['vertical'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access to private methods for testing
|
||||
*
|
||||
* @param (string) method: Method to be called
|
||||
* @param (array) args: Array of paramters to be passed in
|
||||
*/
|
||||
public function access( $method, $args ) {
|
||||
if ( method_exists( $this, $method ) ) {
|
||||
return call_user_func_array( array($this, $method), $args );
|
||||
}
|
||||
else {
|
||||
throw new CSSCompression_Exception( "Unknown method in BorderRadius Class - " . $method );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
<?php
|
||||
/**
|
||||
* CSS Compressor [VERSION]
|
||||
* [DATE]
|
||||
* Corey Hart @ http://www.codenothing.com
|
||||
*/
|
||||
|
||||
class CSSCompression_Combine_Font {
|
||||
/**
|
||||
* Combine Patterns
|
||||
*
|
||||
* @class Control: Compression Controller
|
||||
* @class Combine: Combine Controller
|
||||
* @param (regex) rfont: Font matching
|
||||
* @param (array) groupings: Set of font combinationals
|
||||
*/
|
||||
private $Control;
|
||||
private $Combine;
|
||||
private $rfont = "/(^|(?<!\\\);)(font|line)-(style|variant|weight|size|height|family):(.*?)((?<!\\\);|$)/";
|
||||
private $groupings = array(
|
||||
array('font-style', 'font-variant', 'font-weight', 'size/height', 'font-family'),
|
||||
array('font-style', 'font-variant', 'font-weight', 'font-size', 'font-family'),
|
||||
array('font-style', 'font-variant', 'size/height', 'font-family'),
|
||||
array('font-style', 'font-variant', 'font-size', 'font-family'),
|
||||
array('font-style', 'font-weight', 'size/height', 'font-family'),
|
||||
array('font-style', 'font-weight', 'font-size', 'font-family'),
|
||||
array('font-variant', 'font-weight', 'size/height', 'font-family'),
|
||||
array('font-variant', 'font-weight', 'font-size', 'font-family'),
|
||||
array('font-weight', 'size/height', 'font-family'),
|
||||
array('font-weight', 'font-size', 'font-family'),
|
||||
array('font-variant', 'size/height', 'font-family'),
|
||||
array('font-variant', 'font-size', 'font-family'),
|
||||
array('font-style', 'size/height', 'font-family'),
|
||||
array('font-style', 'font-size', 'font-family'),
|
||||
array('size/height', 'font-family'),
|
||||
array('font-size', 'font-family'),
|
||||
);
|
||||
|
||||
/**
|
||||
* Stash a reference to the controller & combiner
|
||||
*
|
||||
* @param (class) control: CSSCompression Controller
|
||||
* @param (class) combine: CSSCompression Combiner
|
||||
*/
|
||||
public function __construct( CSSCompression_Control $control, CSSCompression_Combine $combine ) {
|
||||
$this->Control = $control;
|
||||
$this->Combine = $combine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines multiple font-definitions into single definition
|
||||
*
|
||||
* @param (string) val: Rule Set
|
||||
*/
|
||||
public function combine( $val ) {
|
||||
$storage = $this->storage( $val );
|
||||
|
||||
// Loop through each property check and see if they can be replaced
|
||||
foreach ( $this->groupings as $props ) {
|
||||
if ( $replace = $this->Combine->searchDefinitions( 'font', $storage, $props ) ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If replacement string found, run it on all declarations
|
||||
if ( $replace ) {
|
||||
$pos = 0;
|
||||
while ( preg_match( $this->rfont, $val, $match, PREG_OFFSET_CAPTURE, $pos ) ) {
|
||||
if ( ! isset( $storage['line-height'] ) && stripos( $match[0][0], 'line-height') === 0 ) {
|
||||
$pos = $match[0][1] + strlen( $match[0][0] ) - 1;
|
||||
continue;
|
||||
}
|
||||
$colon = strlen( $match[1][0] );
|
||||
$val = substr_replace( $val, $replace, $match[0][1] + $colon, strlen( $match[0][0] ) - $colon );
|
||||
$pos = $match[0][1] + strlen( $replace ) - $colon - 1;
|
||||
$replace = '';
|
||||
}
|
||||
}
|
||||
|
||||
// Return converted val
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a storage object for iteration
|
||||
*
|
||||
* @param (string) val: Rule Set
|
||||
*/
|
||||
private function storage( $val ) {
|
||||
$storage = array();
|
||||
|
||||
// Find all possible occurences and build the replacement
|
||||
$pos = 0;
|
||||
while ( preg_match( $this->rfont, $val, $match, PREG_OFFSET_CAPTURE, $pos ) ) {
|
||||
$storage[$match[2][0] . '-' . $match[3][0]] = $match[4][0];
|
||||
$pos = $match[0][1] + strlen( $match[0][0] ) - 1;
|
||||
}
|
||||
|
||||
// Combine font-size & line-height if possible
|
||||
if ( isset( $storage['font-size'] ) && isset( $storage['line-height'] ) ) {
|
||||
$storage['size/height'] = $storage['font-size'] . '/' . $storage['line-height'];
|
||||
unset( $storage['font-size'], $storage['line-height'] );
|
||||
}
|
||||
|
||||
return $storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access to private methods for testing
|
||||
*
|
||||
* @param (string) method: Method to be called
|
||||
* @param (array) args: Array of paramters to be passed in
|
||||
*/
|
||||
public function access( $method, $args ) {
|
||||
if ( method_exists( $this, $method ) ) {
|
||||
return call_user_func_array( array($this, $method), $args );
|
||||
}
|
||||
else {
|
||||
throw new CSSCompression_Exception( "Unknown method in Font Class - " . $method );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
<?php
|
||||
/**
|
||||
* CSS Compressor [VERSION]
|
||||
* [DATE]
|
||||
* Corey Hart @ http://www.codenothing.com
|
||||
*/
|
||||
|
||||
class CSSCompression_Combine_List {
|
||||
/**
|
||||
* Combine Patterns
|
||||
*
|
||||
* @class Control: Compression Controller
|
||||
* @class Combine: Combine Controller
|
||||
* @param (regex) rlist: List style matching
|
||||
* @param (array) groupings: Group of list combinationals
|
||||
*/
|
||||
private $Control;
|
||||
private $Combine;
|
||||
private $rlist = "/(^|(?<!\\\);)list-style-(type|position|image):(.*?)((?<!\\\);|$)/";
|
||||
private $groupings = array(
|
||||
array('type', 'position', 'image'),
|
||||
array('type', 'position'),
|
||||
array('type', 'image'),
|
||||
array('position', 'image'),
|
||||
array('type'),
|
||||
array('position'),
|
||||
array('image'),
|
||||
);
|
||||
|
||||
/**
|
||||
* Stash a reference to the controller & combiner
|
||||
*
|
||||
* @param (class) control: CSSCompression Controller
|
||||
* @param (class) combine: CSSCompression Combiner
|
||||
*/
|
||||
public function __construct( CSSCompression_Control $control, CSSCompression_Combine $combine ) {
|
||||
$this->Control = $control;
|
||||
$this->Combine = $combine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines multiple list style props into single definition
|
||||
*
|
||||
* @param (string) val: Rule Set
|
||||
*/
|
||||
public function combine( $val ) {
|
||||
// If replacement string found, run it on all declarations
|
||||
if ( ( $replace = $this->replace( $val ) ) !== false ) {
|
||||
$pos = 0;
|
||||
while ( preg_match( $this->rlist, $val, $match, PREG_OFFSET_CAPTURE, $pos ) ) {
|
||||
$colon = strlen( $match[1][0] );
|
||||
$val = substr_replace( $val, $replace, $match[0][1] + $colon, strlen( $match[0][0] ) - $colon );
|
||||
$pos = $match[0][1] + strlen( $replace ) - $colon - 1;
|
||||
$replace = '';
|
||||
}
|
||||
}
|
||||
|
||||
// Return converted val
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the replacement string for list props
|
||||
*
|
||||
* @param (string) val: Rule Set
|
||||
*/
|
||||
private function replace( $val ) {
|
||||
$storage = array();
|
||||
$pos = 0;
|
||||
|
||||
// Find all possible occurences and build the replacement
|
||||
while ( preg_match( $this->rlist, $val, $match, PREG_OFFSET_CAPTURE, $pos ) ) {
|
||||
$storage[$match[2][0]] = $match[3][0];
|
||||
$pos = $match[0][1] + strlen( $match[0][0] ) - 1;
|
||||
}
|
||||
|
||||
// Run background checks and get replacement str
|
||||
foreach ( $this->groupings as $props ) {
|
||||
if ( $replace = $this->Combine->searchDefinitions( 'list-style', $storage, $props ) ) {
|
||||
return $replace;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access to private methods for testing
|
||||
*
|
||||
* @param (string) method: Method to be called
|
||||
* @param (array) args: Array of paramters to be passed in
|
||||
*/
|
||||
public function access( $method, $args ) {
|
||||
if ( method_exists( $this, $method ) ) {
|
||||
return call_user_func_array( array($this, $method), $args );
|
||||
}
|
||||
else {
|
||||
throw new CSSCompression_Exception( "Unknown method in List Class - " . $method );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,187 @@
|
|||
<?php
|
||||
/**
|
||||
* CSS Compressor [VERSION]
|
||||
* [DATE]
|
||||
* Corey Hart @ http://www.codenothing.com
|
||||
*/
|
||||
|
||||
class CSSCompression_Combine_MarginPadding {
|
||||
/**
|
||||
* Combine Patterns
|
||||
*
|
||||
* @class Control: Compression Controller
|
||||
* @class Combine: Combine Controller
|
||||
* @param (regex) rspace: Checks for space without an escape '\' character before it
|
||||
* @param (regex) rmp: Margin/Padding matching
|
||||
* @param (regex) rmpbase: Margin/Padding base match
|
||||
*/
|
||||
private $Control;
|
||||
private $Combine;
|
||||
private $rspace = "/(?<!\\\)\s/";
|
||||
private $rmp = "/(^|(?<!\\\);)(margin|padding)-(top|right|bottom|left):(.*?)((?<!\\\);|$)/";
|
||||
private $rmpbase = "/(^|(?<!\\\);)(margin|padding):(.*?)((?<!\\\);|$)/";
|
||||
|
||||
/**
|
||||
* Stash a reference to the controller & combiner
|
||||
*
|
||||
* @param (class) control: CSSCompression Controller
|
||||
* @param (class) combine: CSSCompression Combiner
|
||||
*/
|
||||
public function __construct( CSSCompression_Control $control, CSSCompression_Combine $combine ) {
|
||||
$this->Control = $control;
|
||||
$this->Combine = $combine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines multiple directional properties of
|
||||
* margin/padding into single definition.
|
||||
*
|
||||
* @param (string) val: Rule Set
|
||||
*/
|
||||
public function combine( $val ) {
|
||||
$val = $this->expand( $val );
|
||||
$storage = $this->storage( $val );
|
||||
$pos = 0;
|
||||
|
||||
// Now rebuild the string replacing all instances of margin/padding if shorthand exists
|
||||
while ( preg_match( $this->rmp, $val, $match, PREG_OFFSET_CAPTURE, $pos ) ) {
|
||||
$prop = $match[2][0];
|
||||
if ( isset( $storage[$prop] ) ) {
|
||||
$colon = strlen( $match[1][0] );
|
||||
$val = substr_replace( $val, $storage[$prop], $match[0][1] + $colon, strlen( $match[0][0] ) - $colon );
|
||||
$pos = $match[0][1] + strlen( $storage[$prop] ) - $colon - 1;
|
||||
$storage[$prop] = '';
|
||||
}
|
||||
else {
|
||||
$pos = $match[0][1] + strlen( $match[0][0] ) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Return converted val
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the storage object for iteration
|
||||
*
|
||||
* @param (string) val: Rule Set
|
||||
*/
|
||||
private function storage( $val ) {
|
||||
$storage = array();
|
||||
$pos = 0;
|
||||
|
||||
// Find all possible occurences of margin/padding and mark their directional value
|
||||
while ( preg_match( $this->rmp, $val, $match, PREG_OFFSET_CAPTURE, $pos ) ) {
|
||||
if ( ! isset( $storage[$match[2][0]] ) ) {
|
||||
$storage[$match[2][0]] = array($match[3][0] => $match[4][0]);
|
||||
}
|
||||
|
||||
// Override double written properties
|
||||
$storage[$match[2][0]][$match[3][0]] = $match[4][0];
|
||||
$pos = $match[0][1] + strlen( $match[0][0] ) - 1;
|
||||
}
|
||||
|
||||
// Go through each tag for possible combination
|
||||
foreach ( $storage as $tag => $arr ) {
|
||||
// Only combine if all 4 definitions are found
|
||||
if ( count( $arr ) == 4 && ! $this->Combine->checkUncombinables( $arr ) ) {
|
||||
// All 4 are the same
|
||||
if ( $arr['top'] == $arr['bottom'] && $arr['left'] == $arr['right'] && $arr['top'] == $arr['left'] ) {
|
||||
$storage[$tag] = "$tag:" . $arr['top'] . ';';
|
||||
}
|
||||
// Opposites are the same
|
||||
else if ( $arr['top'] == $arr['bottom'] && $arr['left'] == $arr['right'] ) {
|
||||
$storage[$tag] = "$tag:" . $arr['top'] . ' ' . $arr['left'] . ';';
|
||||
}
|
||||
// 3-point directional
|
||||
else if ( $arr['right'] == $arr['left'] ) {
|
||||
$storage[$tag] = "$tag:" . $arr['top'] . ' ' . $arr['right'] . ' ' . $arr['bottom'] . ';';
|
||||
}
|
||||
// none are the same, but can still use shorthand notation
|
||||
else {
|
||||
$storage[$tag] = "$tag:" . $arr['top'] . ' ' . $arr['right'] . ' ' . $arr['bottom'] . ' ' . $arr['left'] . ';';
|
||||
}
|
||||
}
|
||||
else {
|
||||
unset( $storage[$tag] );
|
||||
}
|
||||
}
|
||||
|
||||
return $storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Explodes shorthanded margin/padding properties for later combination
|
||||
*
|
||||
* @param (string) val: Rule set
|
||||
*/
|
||||
private function expand( $val ) {
|
||||
$pos = 0;
|
||||
while ( preg_match( $this->rmpbase, $val, $match, PREG_OFFSET_CAPTURE, $pos ) ) {
|
||||
$replace = '';
|
||||
$prop = $match[2][0];
|
||||
$value = preg_split( $this->rspace, trim( $match[3][0] ) );
|
||||
$positions = array(
|
||||
'top' => 0,
|
||||
'right' => 0,
|
||||
'bottom' => 0,
|
||||
'left' => 0,
|
||||
);
|
||||
|
||||
// Skip uncombinables
|
||||
if ( $this->Combine->checkUncombinables( $value ) ) {
|
||||
$pos = $match[0][1] + strlen( $match[0][0] );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Each position needs a value
|
||||
switch ( count( $value ) ) {
|
||||
case 1:
|
||||
$positions['top'] = $positions['right'] = $positions['bottom'] = $positions['left'] = $value[0];
|
||||
break;
|
||||
case 2:
|
||||
$positions['top'] = $positions['bottom'] = $value[0];
|
||||
$positions['right'] = $positions['left'] = $value[1];
|
||||
break;
|
||||
case 3:
|
||||
$positions['top'] = $value[0];
|
||||
$positions['right'] = $positions['left'] = $value[1];
|
||||
$positions['bottom'] = $value[2];
|
||||
break;
|
||||
case 4:
|
||||
$positions['top'] = $value[0];
|
||||
$positions['right'] = $value[1];
|
||||
$positions['bottom'] = $value[2];
|
||||
$positions['left'] = $value[3];
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
// Build the replacement
|
||||
foreach ( $positions as $p => $v ) {
|
||||
$replace .= "$prop-$p:$v;";
|
||||
}
|
||||
$colon = strlen( $match[1][0] );
|
||||
$val = substr_replace( $val, $replace, $match[0][1] + $colon, strlen( $match[0][0] ) - $colon );
|
||||
$pos = $match[0][1] + strlen( $replace ) - $colon - 1;
|
||||
}
|
||||
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access to private methods for testing
|
||||
*
|
||||
* @param (string) method: Method to be called
|
||||
* @param (array) args: Array of paramters to be passed in
|
||||
*/
|
||||
public function access( $method, $args ) {
|
||||
if ( method_exists( $this, $method ) ) {
|
||||
return call_user_func_array( array($this, $method), $args );
|
||||
}
|
||||
else {
|
||||
throw new CSSCompression_Exception( "Unknown method in MarginPadding Class - " . $method );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,209 @@
|
|||
<?php
|
||||
/**
|
||||
* CSS Compressor [VERSION]
|
||||
* [DATE]
|
||||
* Corey Hart @ http://www.codenothing.com
|
||||
*/
|
||||
|
||||
class CSSCompression_Compress {
|
||||
/**
|
||||
* Trim Patterns
|
||||
*
|
||||
* @param (array) options: Reference to options
|
||||
* @param (array) stats: Reference to stats
|
||||
* @param (regex) rsemicolon: Checks for semicolon without an escape '\' character before it
|
||||
* @param (regex) rcolon: Checks for colon without an escape '\' character before it
|
||||
* @param (regex) rspace: Checks for space without an escape '\' character before it
|
||||
*/
|
||||
private $options = array();
|
||||
private $stats = array();
|
||||
private $rsemicolon = "/(?<!\\\);/";
|
||||
private $rcolon = "/(?<!\\\):/";
|
||||
private $rspace = "/(?<!\\\)\s/";
|
||||
|
||||
/**
|
||||
* Other inner classes that get used within compression
|
||||
*
|
||||
* @class Control: Compression Controller
|
||||
* @class Trim: Trim Instance
|
||||
* @class Setup: Setup Instance
|
||||
* @class Format: Formatting Instance
|
||||
* @class Combine: Combine Instance
|
||||
* @class Cleanup: Cleanup Instance
|
||||
* @class Organize: Organize Instance
|
||||
* @class Selectors: Selectors Instance
|
||||
* @param (array) others: List of above classes for copying
|
||||
*/
|
||||
private $Control;
|
||||
private $Trim;
|
||||
private $Setup;
|
||||
private $Format;
|
||||
private $Combine;
|
||||
private $Cleanup;
|
||||
private $Organize;
|
||||
private $Selectors;
|
||||
private $others = array(
|
||||
'Trim',
|
||||
'Setup',
|
||||
'Format',
|
||||
'Combine',
|
||||
'Cleanup',
|
||||
'Organize',
|
||||
'Selectors',
|
||||
);
|
||||
|
||||
/**
|
||||
* Stash a reference to the controller on each instantiation
|
||||
*
|
||||
* @param (class) control: CSSCompression Controller
|
||||
*/
|
||||
public function __construct( CSSCompression_Control $control ) {
|
||||
$this->Control = $control;
|
||||
$this->options = &$control->Option->options;
|
||||
$this->stats = &$control->stats;
|
||||
|
||||
foreach ( $this->others as $class ) {
|
||||
$this->$class = $control->$class;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Centralized function to run css compression.
|
||||
*
|
||||
* @param (string) css: Stylesheet to compresss
|
||||
*/
|
||||
public function compress( $css ) {
|
||||
$setup = $this->setup( $css );
|
||||
$setup = $this->rulesets( $setup );
|
||||
$css = $this->readability( $setup );
|
||||
|
||||
// Attach plea to top of page with unknown blocks
|
||||
if ( $this->options['add-unknown'] && count( $setup['unknown'] ) ) {
|
||||
$css = "/*\nThere are unknown blocks in the sheet, please please please open an issue with your sheet attached to it:\n"
|
||||
. "https://github.com/codenothing/css-compressor/issues\n"
|
||||
. "Thank You --\n\n"
|
||||
. implode( "\n", $setup['unknown'] )
|
||||
. "\n*/\n"
|
||||
. $css;
|
||||
}
|
||||
|
||||
// Mark final file size
|
||||
$this->stats['after']['size'] = strlen( $css = trim( $css ) );
|
||||
|
||||
// Return compressed css
|
||||
return $css;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs css through initial setup handlers
|
||||
*
|
||||
* @param (string) css: Sheet to compress
|
||||
*/
|
||||
private function setup( $css ) {
|
||||
// Initial stats
|
||||
$this->stats['before']['time'] = microtime( true );
|
||||
$this->stats['before']['size'] = strlen( $css );
|
||||
|
||||
// Initial trimming
|
||||
$css = $this->Trim->trim( $css );
|
||||
|
||||
// Do a little tokenizing, compress each property individually
|
||||
$setup = $this->Setup->setup( $css );
|
||||
|
||||
// Mark number of selectors pre-combine
|
||||
$this->stats['before']['selectors'] = count( $setup['selectors'] );
|
||||
|
||||
return $setup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Focus compressions on each rule set
|
||||
*
|
||||
* @param (array) setup: Array containing selectors and rule sets
|
||||
*/
|
||||
private function rulesets( $setup ) {
|
||||
// Do selector specific compressions
|
||||
$this->Selectors->selectors( $setup['selectors'] );
|
||||
|
||||
// Look at each group of properties as a whole, and compress/combine similiar definitions
|
||||
$this->Combine->combine( $setup['selectors'], $setup['details'] );
|
||||
|
||||
// If order isn't important, run comination functions before and after compressions to catch all instances
|
||||
// Be sure to prune before hand for higher chance of matching
|
||||
if ( $this->options['organize'] ) {
|
||||
$this->Cleanup->cleanup( $setup['selectors'], $setup['details'] );
|
||||
$this->Organize->organize( $setup['selectors'], $setup['details'] );
|
||||
$this->Combine->combine( $setup['selectors'], $setup['details'] );
|
||||
}
|
||||
|
||||
// Do final maintenace work, remove injected property/values
|
||||
$this->Cleanup->cleanup( $setup['selectors'], $setup['details'] );
|
||||
|
||||
// Run final counters before full cleanup
|
||||
$this->finalCount( $setup['selectors'], $setup['details'] );
|
||||
|
||||
return $setup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs final counts on selectors and props
|
||||
*
|
||||
* @param (array) selectors: Selector rules
|
||||
* @param (array) details: Rule sets
|
||||
*/
|
||||
private function finalCount( $selectors, $details ) {
|
||||
// Selectors and props
|
||||
$this->stats['after']['selectors'] = count( $selectors );
|
||||
foreach ( $details as $item ) {
|
||||
$props = preg_split( $this->rsemicolon, $item );
|
||||
|
||||
// Make sure count is true
|
||||
foreach ( $props as $k => $v ) {
|
||||
if ( ! isset( $v ) || $v == '' ) {
|
||||
unset( $props[$k] );
|
||||
}
|
||||
}
|
||||
$this->stats['after']['props'] += count( $props );
|
||||
}
|
||||
|
||||
// Final count for stats
|
||||
$this->stats['after']['time'] = microtime( true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the compressed rule sets into a stylesheet
|
||||
*
|
||||
* @param (array) setup: Array containing selectors and rule sets
|
||||
*/
|
||||
private function readability( $setup ) {
|
||||
// Format css to users preference
|
||||
$css = $this->Format->readability( $this->options['readability'], $setup['selectors'], $setup['details'] );
|
||||
|
||||
// Intros
|
||||
foreach ( $setup as $value ) {
|
||||
if ( $value && is_string( $value ) ) {
|
||||
$css = $value . $css;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove escapables
|
||||
$css = $this->Cleanup->removeInjections( $css );
|
||||
|
||||
return $css;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access to private methods for testing
|
||||
*
|
||||
* @param (string) method: Method to be called
|
||||
* @param (array) args: Array of paramters to be passed in
|
||||
*/
|
||||
public function access( $method, $args ) {
|
||||
if ( method_exists( $this, $method ) ) {
|
||||
return call_user_func_array( array($this, $method), $args );
|
||||
}
|
||||
else {
|
||||
throw new CSSCompression_Exception( "Unknown method in Compress Class - " . $method );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,233 @@
|
|||
<?php
|
||||
/**
|
||||
* CSS Compressor [VERSION]
|
||||
* [DATE]
|
||||
* Corey Hart @ http://www.codenothing.com
|
||||
*/
|
||||
|
||||
class CSSCompression_Control {
|
||||
/**
|
||||
* Control Patterns
|
||||
*
|
||||
* @param (string) css: Holds compressed css string
|
||||
* @param (string) mode: Current compression mode state
|
||||
* @param (array) stats: Holds compression stats
|
||||
* @param (array) getters: Array of accessible getters
|
||||
*/
|
||||
public $css = '';
|
||||
public $mode = '';
|
||||
public $stats = array();
|
||||
private $getters = array(
|
||||
'css',
|
||||
'mode',
|
||||
'stats',
|
||||
);
|
||||
|
||||
/**
|
||||
* Subclasses that do the ground work for this compressor
|
||||
*
|
||||
* @class CSSCompression: Public facing compression class
|
||||
* @class Option: Option handling
|
||||
* @class Trim: Does the initial trimming for the css
|
||||
* @class Format: Formats the output
|
||||
* @class Numeric: Handles numeric compression
|
||||
* @class Color: Handles color compression
|
||||
* @class Individuals: Runs compression algorithms on individual properties and values
|
||||
* @class Selectors: Runs selector specific compressions
|
||||
* @class Combine: Handles combining of various properties
|
||||
* @class Organize: Reorganizes the sheet for futher compression
|
||||
* @class Cleanup: Cleans out all injected characters during compression
|
||||
* @class Compress: Central compression unit.
|
||||
* @param (array) subclasses: Array holding all the subclasses for inlusion
|
||||
*/
|
||||
public $CSSCompression;
|
||||
public $Option;
|
||||
public $Trim;
|
||||
public $Format;
|
||||
public $Numeric;
|
||||
public $Color;
|
||||
public $Individuals;
|
||||
public $Selectors;
|
||||
public $Combine;
|
||||
public $Organize;
|
||||
public $Cleanup;
|
||||
public $Setup;
|
||||
public $Compress;
|
||||
private $subclasses = array(
|
||||
'Option',
|
||||
'Trim',
|
||||
'Format',
|
||||
'Numeric',
|
||||
'Color',
|
||||
'Individuals',
|
||||
'Selectors',
|
||||
'Combine',
|
||||
'Organize',
|
||||
'Cleanup',
|
||||
'Setup',
|
||||
'Compress',
|
||||
);
|
||||
|
||||
/**
|
||||
* Pull in the Compression instance and build the subclasses
|
||||
*
|
||||
* @param (class) CSSCompression: CSSCompression Instance
|
||||
*/
|
||||
public function __construct( CSSCompression $CSSCompression ) {
|
||||
$this->CSSCompression = $CSSCompression;
|
||||
|
||||
// Load all subclasses on demand
|
||||
if ( ! class_exists( "CSSCompression_Option", false ) ) {
|
||||
$path = dirname(__FILE__) . '/';
|
||||
foreach ( $this->subclasses as $class ) {
|
||||
require( $path . $class . '.inc' );
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize each subclass
|
||||
foreach ( $this->subclasses as $class ) {
|
||||
$full = "CSSCompression_$class";
|
||||
$this->$class = new $full( $this );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Control access to properties
|
||||
*
|
||||
* - Getting stats/mode/css returns the current value of that property
|
||||
* - Getting options will return the current full options array
|
||||
* - Getting anything else returns that current value in the options array or NULL
|
||||
*
|
||||
* @param (string) name: Name of property that you want to access
|
||||
*/
|
||||
public function get( $name ) {
|
||||
if ( in_array( $name, $this->getters ) ) {
|
||||
return $this->$name;
|
||||
}
|
||||
else if ( $name == 'options' ) {
|
||||
return $this->Option->options;
|
||||
}
|
||||
else {
|
||||
return $this->Option->option( $name );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The setter method only allows access to setting values in the options array
|
||||
*
|
||||
* @param (string) name: Key name of the option you want to set
|
||||
* @param (mixed) value: Value of the option you want to set
|
||||
*/
|
||||
public function set( $name, $value ) {
|
||||
// Allow for passing array of options to merge into current ones
|
||||
if ( $name === 'options' && is_array( $value ) ) {
|
||||
return $this->Option->merge( $value );
|
||||
}
|
||||
else if ( isset( CSSCompression::$defaults[$name] ) ) {
|
||||
return $this->Option->option( $name, $value );
|
||||
}
|
||||
else {
|
||||
throw new CSSCompression_Exception( "Invalid Private Access to $name in CSSCompression." );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges a predefined set options
|
||||
*
|
||||
* @param (string) mode: Name of mode to use.
|
||||
*/
|
||||
public function mode( $mode = NULL ) {
|
||||
return $this->Options->merge( $mode );
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets options to their defaults, and flushes out variables
|
||||
*
|
||||
* @params none
|
||||
*/
|
||||
public function reset() {
|
||||
$this->Option->reset();
|
||||
return $this->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans out class variables for next run
|
||||
*
|
||||
* @params none
|
||||
*/
|
||||
public function flush() {
|
||||
$this->css = '';
|
||||
$this->stats = array(
|
||||
'before' => array(
|
||||
'props' => 0,
|
||||
'selectors' => 0,
|
||||
'size' => 0,
|
||||
'time' => 0,
|
||||
),
|
||||
'after' => array(
|
||||
'props' => 0,
|
||||
'selectors' => 0,
|
||||
'size' => 0,
|
||||
'time' => 0,
|
||||
),
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Proxy to run Compression on the sheet passed. Handle options here.
|
||||
*
|
||||
* @param (string) css: Stylesheet to be compressed
|
||||
* @param (mixed) options: Array of options or mode to use.
|
||||
*/
|
||||
public function compress( $css = NULL, $options = NULL ) {
|
||||
// Flush out old stats and variables
|
||||
$this->flush();
|
||||
|
||||
// If no additional options, just run compression
|
||||
if ( $options === NULL ) {
|
||||
return $this->css = $this->Compress->compress( $css );
|
||||
}
|
||||
|
||||
// Store old params
|
||||
$old = $this->mode == '__custom' ? $this->Option->option() : $this->mode;
|
||||
$readability = $this->Option->option( 'readability' );
|
||||
|
||||
// Compress with new set of options
|
||||
$this->Option->merge( $options );
|
||||
$css = $this->Compress->compress( $css );
|
||||
|
||||
// Reset original options
|
||||
$this->reset();
|
||||
$this->Option->merge( $old );
|
||||
|
||||
// Return the compressed css
|
||||
return $this->css = $css;
|
||||
}
|
||||
|
||||
/**
|
||||
* Backdoor access to subclasses
|
||||
* ONLY FOR DEVELOPMENT/TESTING.
|
||||
*
|
||||
* @param (string) class: Name of the focus class
|
||||
* @param (array) config: Contains name reference and test arguments
|
||||
*/
|
||||
public function access( $class, $method, $args ) {
|
||||
if ( $class == 'Control' ) {
|
||||
return call_user_func_array( array($class, $method), $args );
|
||||
}
|
||||
else if ( strpos( $class, '.' ) !== false ) {
|
||||
$parts = explode( '.', $class );
|
||||
$class = $parts[0];
|
||||
$subclass = $parts[1];
|
||||
return $this->$class->access( $subclass, $method, $args );
|
||||
}
|
||||
else if ( in_array( $class, $this->subclasses ) ) {
|
||||
return $this->$class->access( $method, $args );
|
||||
}
|
||||
else {
|
||||
throw new CSSCompression_Exception( "Unknown Class Access - " . $class );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
/**
|
||||
* CSS Compressor [VERSION]
|
||||
* [DATE]
|
||||
* Corey Hart @ http://www.codenothing.com
|
||||
*/
|
||||
|
||||
|
||||
class CSSCompression_Exception extends Exception {
|
||||
/**
|
||||
* Custom exception handler
|
||||
*
|
||||
* @param (string) message: Error message
|
||||
* @param (int) code: Error code
|
||||
* @instance (Exception Instance) previous: Previous exception
|
||||
*/
|
||||
public function __construct( $message = 'Unknown Exception', $code = 0, Exception $previous = NULL ) {
|
||||
parent::__construct( $message, $code, $previous );
|
||||
}
|
||||
|
||||
/**
|
||||
* String version of this custom exception
|
||||
*
|
||||
* @params none
|
||||
*/
|
||||
public function __toString() {
|
||||
return "CSSCompression Exception: [" . $this->code . "] " . $this->message . "\n";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,184 @@
|
|||
<?php
|
||||
/**
|
||||
* CSS Compressor [VERSION]
|
||||
* [DATE]
|
||||
* Corey Hart @ http://www.codenothing.com
|
||||
*/
|
||||
|
||||
class CSSCompression_Format {
|
||||
/**
|
||||
* Format Patterns
|
||||
*
|
||||
* @class Control: Compression Controller
|
||||
* @param (string) token: Copy of the injection token
|
||||
* @param (array) options: Reference to options
|
||||
* @param (regex) rsemicolon: Checks for semicolon without an escape '\' character before it
|
||||
* @param (regex) rcolon: Checks for colon without an escape '\' character before it
|
||||
* @param (array) readability: Mapping to readability functions
|
||||
*/
|
||||
private $Control;
|
||||
private $token = '';
|
||||
private $options = array();
|
||||
private $rsemicolon = "/(?<!\\\);/";
|
||||
private $rcolon = "/(?<!\\\):/";
|
||||
private $readability = array(
|
||||
CSSCompression::READ_MAX => 'maximum',
|
||||
CSSCompression::READ_MED => 'medium',
|
||||
CSSCompression::READ_MIN => 'minimum',
|
||||
CSSCompression::READ_NONE => 'none',
|
||||
);
|
||||
|
||||
/**
|
||||
* Stash a reference to the controller on each instantiation
|
||||
*
|
||||
* @param (class) control: CSSCompression Controller
|
||||
*/
|
||||
public function __construct( CSSCompression_Control $control ) {
|
||||
$this->Control = $control;
|
||||
$this->token = CSSCompression::TOKEN;
|
||||
$this->options = &$control->Option->options;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reformats compressed CSS into specified format
|
||||
*
|
||||
* @param (int) readability: Readability level of compressed output
|
||||
* @param (array) selectors: Array of selectors
|
||||
* @param (array) details: Array of declarations
|
||||
*/
|
||||
public function readability( $readability = CSSCompression::READ_NONE, $selectors = array(), $details = array() ) {
|
||||
if ( isset( $this->readability[$readability] ) ) {
|
||||
$fn = $this->readability[$readability];
|
||||
return trim( $this->$fn( $selectors, $details ) );
|
||||
}
|
||||
else {
|
||||
return 'Invalid Readability Value';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns maxium readability, breaking on every selector, brace, and property
|
||||
*
|
||||
* @param (array) selectors: Array of selectors
|
||||
* @param (array) details: Array of declarations
|
||||
*/
|
||||
private function maximum( $selectors, $details ) {
|
||||
$css = '';
|
||||
foreach ( $selectors as $k => $v ) {
|
||||
if ( strpos( $v, $this->token ) === 0 ) {
|
||||
$css .= substr( $v, strlen( $this->token ) );
|
||||
$css .= $details[$k];
|
||||
continue;
|
||||
}
|
||||
else if ( ! $details[$k] || trim( $details[$k] ) == '' ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$v = str_replace( '>', ' > ', $v );
|
||||
$v = str_replace( '+', ' + ', $v );
|
||||
$v = str_replace( ',', ', ', $v );
|
||||
$css .= "$v {\n";
|
||||
$arr = preg_split( $this->rsemicolon, $details[$k] );
|
||||
|
||||
foreach ( $arr as $item ) {
|
||||
if ( ! $item ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
list( $prop, $val ) = preg_split( $this->rcolon, $item, 2 );
|
||||
$css .= "\t$prop: $val;\n";
|
||||
}
|
||||
|
||||
// Kill that last semicolon at users request
|
||||
if ( $this->options['unnecessary-semicolons'] ) {
|
||||
$css = preg_replace( "/;\n$/", "\n", $css );
|
||||
}
|
||||
|
||||
$css .= "}\n\n";
|
||||
}
|
||||
|
||||
return $css;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns medium readability, putting selectors and rule sets on new lines
|
||||
*
|
||||
* @param (array) selectors: Array of selectors
|
||||
* @param (array) details: Array of declarations
|
||||
*/
|
||||
private function medium( $selectors, $details ) {
|
||||
$css = '';
|
||||
foreach ( $selectors as $k => $v ) {
|
||||
if ( strpos( $v, $this->token ) === 0 ) {
|
||||
$css .= substr( $v, strlen( $this->token ) );
|
||||
$css .= $details[$k];
|
||||
continue;
|
||||
}
|
||||
else if ( $details[$k] && $details[$k] != '' ) {
|
||||
$css .= "$v {\n\t" . $details[$k] . "\n}\n";
|
||||
}
|
||||
}
|
||||
|
||||
return $css;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns minimum readability, breaking after every selector and it's rule set
|
||||
*
|
||||
* @param (array) selectors: Array of selectors
|
||||
* @param (array) details: Array of declarations
|
||||
*/
|
||||
private function minimum( $selectors, $details ) {
|
||||
$css = '';
|
||||
foreach ( $selectors as $k => $v ) {
|
||||
if ( strpos( $v, $this->token ) === 0 ) {
|
||||
$css .= substr( $v, strlen( $this->token ) );
|
||||
$css .= $details[$k];
|
||||
continue;
|
||||
}
|
||||
else if ( $details[$k] && $details[$k] != '' ) {
|
||||
$css .= "$v{" . $details[$k] . "}\n";
|
||||
}
|
||||
}
|
||||
|
||||
return $css;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unreadable, but fully compressed script
|
||||
*
|
||||
* @param (array) selectors: Array of selectors
|
||||
* @param (array) details: Array of declarations
|
||||
*/
|
||||
private function none( $selectors, $details ) {
|
||||
$css = '';
|
||||
foreach ( $selectors as $k => $v ) {
|
||||
if ( strpos( $v, $this->token ) === 0 ) {
|
||||
$css .= substr( $v, strlen( $this->token ) );
|
||||
$css .= $details[$k];
|
||||
continue;
|
||||
}
|
||||
else if ( $details[$k] && $details[$k] != '' ) {
|
||||
$css .= trim( "$v{" . $details[$k] . "}" );
|
||||
}
|
||||
}
|
||||
|
||||
return $css;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access to private methods for testing
|
||||
*
|
||||
* @param (string) method: Method to be called
|
||||
* @param (array) args: Array of paramters to be passed in
|
||||
*/
|
||||
public function access( $method, $args ) {
|
||||
if ( method_exists( $this, $method ) ) {
|
||||
return call_user_func_array( array($this, $method), $args );
|
||||
}
|
||||
else {
|
||||
throw new CSSCompression_Exception( "Unknown method in Format Class - " . $method );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,304 @@
|
|||
<?php
|
||||
/**
|
||||
* CSS Compressor [VERSION]
|
||||
* [DATE]
|
||||
* Corey Hart @ http://www.codenothing.com
|
||||
*/
|
||||
|
||||
class CSSCompression_Individuals {
|
||||
/**
|
||||
* Individual patterns
|
||||
*
|
||||
* @class Control: Compression Controller
|
||||
* @class Numeric: Numeric handler
|
||||
* @class Color: Color Handler
|
||||
* @param (array) options: Reference to options
|
||||
* @param (regex) rdirectional: Properties that may have multiple directions
|
||||
* @param (regex) rborderradius: Checks property for border-radius declaration
|
||||
* @param (regex) rnoneprop: Properties that can have none as their value(will be converted to 0)
|
||||
* @param (regex) rclip: Looks for rect grouping in clip declaration
|
||||
* @param (regex) rsplitter: Checks font properties for font-size/line-height split
|
||||
* @param (regex) rfilter: Special alpha filter for msie
|
||||
* @param (regex) rspace: Checks for unescaped space
|
||||
* @param (regex) rspace: Checks for unescaped slash
|
||||
* @param (array) weights: Array of font-weight name conversions to their numeric counterpart
|
||||
*/
|
||||
private $Control;
|
||||
private $Numeric;
|
||||
private $Color;
|
||||
private $options = array();
|
||||
private $rdirectional = "/^(margin|padding|border-spacing)$/";
|
||||
private $rborderradius = "/border[a-z-]*radius/";
|
||||
private $rradiusfull = "/^(-moz-|-webkit-)?border-radius$/";
|
||||
private $rnoneprop = "/^(border|background|border-(top|right|bottom|left))$/";
|
||||
private $rclip = "/^rect\(\s*(\-?\d*\.?\d*?\w*)(,|\s)(\-?\d*\.?\d*?\w*)(,|\s)(\-?\d*\.?\d*?\w*)(,|\s)(\-?\d*\.?\d*?\w*)\s*\)$/";
|
||||
private $rsplitter = "/(^|(?<!\\\)\s)([^\/ ]+)\/([^\/ ]+)((?<!\\\)\s|$)/";
|
||||
private $rfilter = "/[\"']?PROGID\\\?:DXImageTransform\\\?.Microsoft\\\?.Alpha\(Opacity\\\?=(\d+\\\?\.?\d*)\)[\"']?/i";
|
||||
private $rspace = "/(?<!\\\)\s/";
|
||||
private $rslash = "/(?<!\\\)\//";
|
||||
private $weights = array(
|
||||
"normal" => 400,
|
||||
"bold" => 700,
|
||||
);
|
||||
|
||||
/**
|
||||
* Stash a reference to the controller on each instantiation
|
||||
*
|
||||
* @param (class) control: CSSCompression Controller
|
||||
*/
|
||||
public function __construct( CSSCompression_Control $control ) {
|
||||
$this->Control = $control;
|
||||
$this->Numeric = $control->Numeric;
|
||||
$this->Color = $control->Color;
|
||||
$this->options = &$control->Option->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs special unit/directional compressions
|
||||
*
|
||||
* @param (string) prop: CSS Property
|
||||
* @param (string) val: Value of CSS Property
|
||||
*/
|
||||
public function individuals( $prop, $val ) {
|
||||
// Properties should always be lowercase
|
||||
$prop = strtolower( $prop );
|
||||
|
||||
// Split up each definiton for color and numeric compressions
|
||||
$parts = preg_split( $this->rspace, $val );
|
||||
foreach ( $parts as &$v ) {
|
||||
if ( ! $v || $v == '' ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove uneeded decimals/units
|
||||
if ( $this->options['format-units'] ) {
|
||||
$v = $this->Numeric->numeric( $v );
|
||||
}
|
||||
|
||||
// Color compression
|
||||
$v = $this->Color->color( $v );
|
||||
}
|
||||
$val = trim( implode( ' ', $parts ) );
|
||||
|
||||
// Special border radius handling
|
||||
if ( preg_match( $this->rborderradius, $prop ) ) {
|
||||
$val = $this->borderRadius( $prop, $val );
|
||||
}
|
||||
// Remove uneeded side definitions if possible
|
||||
else if ( $this->options['directional-compress'] && count( $parts ) > 1 && preg_match( $this->rdirectional, $prop ) ) {
|
||||
$val = $this->directionals( strtolower( $val ) );
|
||||
}
|
||||
|
||||
// Font-weight converter
|
||||
if ( $this->options['fontweight2num'] && ( $prop == 'font-weight' || $prop == 'font' ) ) {
|
||||
$val = $this->fontweight( $val );
|
||||
}
|
||||
|
||||
// Special font value conversions
|
||||
if ( $prop == 'font' ) {
|
||||
$val = $this->font( $val );
|
||||
}
|
||||
|
||||
// Special clip value compressions
|
||||
if ( $prop == 'clip' ) {
|
||||
$val = $this->clip( $val );
|
||||
}
|
||||
|
||||
// None to 0 converter
|
||||
$val = $this->none( $prop, $val );
|
||||
|
||||
// MSIE Filters
|
||||
$val = $this->filter( $prop, $val );
|
||||
|
||||
// Return for list retrival
|
||||
return array($prop, $val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Preps border radius for directional compression
|
||||
*
|
||||
* @param (string) prop: Property Declaration
|
||||
* @param (string) val: Declaration Value
|
||||
*/
|
||||
private function borderRadius( $prop, $val ) {
|
||||
if ( preg_match( $this->rslash, $val ) ) {
|
||||
$parts = preg_split( $this->rslash, $val, 2 );
|
||||
// We have to redo numeric compression because the slash may hav intruded
|
||||
foreach ( $parts as &$row ) {
|
||||
$p = preg_split( $this->rspace, $row );
|
||||
foreach ( $p as &$v ) {
|
||||
if ( ! $v || $v == '' ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove uneeded decimals/units
|
||||
if ( $this->options['format-units'] ) {
|
||||
$v = $this->Numeric->numeric( $v );
|
||||
}
|
||||
}
|
||||
$row = implode( ' ', $p );
|
||||
if ( $this->options['directional-compress'] ) {
|
||||
$row = $this->directionals( strtolower( $row ) );
|
||||
}
|
||||
}
|
||||
$val = implode( '/', $parts );
|
||||
}
|
||||
else if ( $this->options['directional-compress'] && preg_match( $this->rradiusfull, $prop ) ) {
|
||||
$val = $this->directionals( strtolower( $val ) );
|
||||
}
|
||||
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds directional compression on methods like margin/padding
|
||||
*
|
||||
* @param (string) val: Value of CSS Property
|
||||
*/
|
||||
private function directionals( $val ) {
|
||||
// Split up each definiton
|
||||
$direction = preg_split( $this->rspace, $val );
|
||||
|
||||
// 4 Direction reduction
|
||||
$count = count( $direction );
|
||||
if ( $count == 4 ) {
|
||||
// All 4 sides are the same, combine into 1 definition
|
||||
if ( $direction[0] == $direction[1] && $direction[2] == $direction[3] && $direction[0] == $direction[3] ) {
|
||||
$direction = array($direction[0]);
|
||||
}
|
||||
// top-bottom/left-right are the same, reduce definition
|
||||
else if ( $direction[0] == $direction[2] && $direction[1] == $direction[3] ) {
|
||||
$direction = array($direction[0], $direction[1]);
|
||||
}
|
||||
// Only left-right are the same
|
||||
else if ( $direction[1] == $direction[3] ) {
|
||||
$direction = array($direction[0], $direction[1], $direction[2]);
|
||||
}
|
||||
}
|
||||
// 3 Direction reduction
|
||||
else if ( $count == 3 ) {
|
||||
// All directions are the same
|
||||
if ( $direction[0] == $direction[1] && $direction[1] == $direction[2] ) {
|
||||
$direction = array($direction[0]);
|
||||
}
|
||||
// Only top(first) and bottom(last) are the same
|
||||
else if ( $direction[0] == $direction[2] ) {
|
||||
$direction = array($direction[0], $direction[1]);
|
||||
}
|
||||
}
|
||||
// 2 Direction reduction
|
||||
// Both directions are the same, combine into single definition
|
||||
else if ( $count == 2 && $direction[0] == $direction[1] ) {
|
||||
$direction = array($direction[0]);
|
||||
}
|
||||
|
||||
// Return the combined version of the directions
|
||||
// Single entries will just return
|
||||
return implode( ' ', $direction );
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts font-weight names to numbers
|
||||
*
|
||||
* @param (string) val: font-weight prop value
|
||||
*/
|
||||
private function fontweight( $val ) {
|
||||
if ( preg_match( $this->rspace, $val ) ) {
|
||||
$parts = preg_split( $this->rspace, $val );
|
||||
foreach ( $parts as &$item ) {
|
||||
$lower = strtolower( $item );
|
||||
if ( isset( $this->weights[$lower] ) && $lower != 'normal' ) {
|
||||
$item = $this->weights[$lower];
|
||||
}
|
||||
}
|
||||
$val = implode( ' ', $parts );
|
||||
}
|
||||
else if ( isset( $this->weights[strtolower( $val )] ) ) {
|
||||
$val = $this->weights[strtolower( $val )];
|
||||
}
|
||||
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Special font conversions
|
||||
*
|
||||
* @param (string) val: property value
|
||||
*/
|
||||
private function font( $val ) {
|
||||
// Split out the font-size/line-height split and run through numerical handlers
|
||||
if ( preg_match( $this->rsplitter, $val, $match, PREG_OFFSET_CAPTURE ) ) {
|
||||
$size = $this->Numeric->numeric( $match[2][0] );
|
||||
$height = $this->Numeric->numeric( $match[3][0] );
|
||||
$concat = $match[1][0] . $size . '/' . $height . $match[4][0];
|
||||
$val = substr_replace( $val, $concat, $match[0][1], strlen( $match[0][0] ) );
|
||||
}
|
||||
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Special clip conversions
|
||||
*
|
||||
* @param (string) val: property value
|
||||
*/
|
||||
private function clip( $val ) {
|
||||
if ( preg_match( $this->rclip, $val, $match ) ) {
|
||||
$positions = array(1, 3, 5, 7);
|
||||
$clean = 'rect(';
|
||||
foreach ( $positions as $pos ) {
|
||||
if ( ! isset( $match[$pos] ) ) {
|
||||
return $val;
|
||||
}
|
||||
|
||||
$clean .= $this->Numeric->numeric( $match[$pos] ) . ( isset( $match[$pos + 1] ) ? $match[$pos + 1] : '' );
|
||||
}
|
||||
$val = $clean . ')';
|
||||
}
|
||||
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert none vals to 0
|
||||
*
|
||||
* @param (string) prop: Current Property
|
||||
* @param (string) val: property value
|
||||
*/
|
||||
private function none( $prop, $val ) {
|
||||
if ( preg_match( $this->rnoneprop, $prop ) && $val == 'none' ) {
|
||||
$val = '0';
|
||||
}
|
||||
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* MSIE Filter Conversion
|
||||
*
|
||||
* @param (string) prop: Current Property
|
||||
* @param (string) val: property value
|
||||
*/
|
||||
private function filter( $prop, $val ) {
|
||||
if ( preg_match( "/filter/", $prop ) ) {
|
||||
$val = preg_replace( $this->rfilter, "alpha(opacity=$1)", $val );
|
||||
}
|
||||
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access to private methods for testing
|
||||
*
|
||||
* @param (string) method: Method to be called
|
||||
* @param (array) args: Array of paramters to be passed in
|
||||
*/
|
||||
public function access( $method, $args ) {
|
||||
if ( method_exists( $this, $method ) ) {
|
||||
return call_user_func_array( array($this, $method), $args );
|
||||
}
|
||||
else {
|
||||
throw new CSSCompression_Exception( "Unknown method in Individuals Class - " . $method );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
/**
|
||||
* CSS Compressor [VERSION]
|
||||
* [DATE]
|
||||
* Corey Hart @ http://www.codenothing.com
|
||||
*/
|
||||
|
||||
class CSSCompression_Numeric {
|
||||
/**
|
||||
* Numeric Patterns
|
||||
*
|
||||
* @class Control: Compression Controller
|
||||
* @param (array) options: Reference to options
|
||||
* @param (regex) rdecimal: Checks for zero decimal
|
||||
* @param (regex) rzero: Checks for preceding 0 to decimal unit
|
||||
* @param (regex) runit: Checks for suffix on 0 unit
|
||||
*/
|
||||
private $Control;
|
||||
private $options = array();
|
||||
private $rdecimal = "/^(\+|\-)?(\d*\.[1-9]*0*)(\%|[a-z]{2})$/i";
|
||||
private $rzero = "/^(\+|\-)?0(\.\d+)(\%|[a-z]{2})?$/i";
|
||||
private $runit = "/^0(\%|[a-z]{2})$/i";
|
||||
|
||||
/**
|
||||
* Stash a reference to the controller on each instantiation
|
||||
*
|
||||
* @param (class) control: CSSCompression Controller
|
||||
*/
|
||||
public function __construct( CSSCompression_Control $control ) {
|
||||
$this->Control = $control;
|
||||
$this->options = &$control->Option->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs all numeric operations
|
||||
*
|
||||
* @param (string) str: Unit string
|
||||
*/
|
||||
public function numeric( $str ) {
|
||||
$str = $this->decimal( $str );
|
||||
$str = $this->zeroes( $str );
|
||||
$str = $this->units( $str );
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove's unecessary decimal, ie 13.0px => 13px
|
||||
*
|
||||
* @param (string) str: Unit string
|
||||
*/
|
||||
private function decimal( $str ) {
|
||||
if ( preg_match( $this->rdecimal, $str, $match ) ) {
|
||||
$str = ( $match[1] == '-' ? '-' : '' ) . floatval( $match[2] ) . $match[3];
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes suffix from 0 unit, ie 0px; => 0;
|
||||
*
|
||||
* @param (string) str: Unit string
|
||||
*/
|
||||
private function units( $str ) {
|
||||
if ( preg_match( $this->runit, $str, $match ) ) {
|
||||
$str = '0';
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes leading zero in decimal, ie 0.33px => .33px
|
||||
*
|
||||
* @param (string) str: Unit string
|
||||
*/
|
||||
private function zeroes( $str ) {
|
||||
if ( preg_match( $this->rzero, $str, $match ) ) {
|
||||
$str = ( isset( $match[1] ) && $match[1] == '-' ? '-' : '' ) . $match[2] . ( isset( $match[3] ) ? $match[3] : '' );
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access to private methods for testing
|
||||
*
|
||||
* @param (string) method: Method to be called
|
||||
* @param (array) args: Array of paramters to be passed in
|
||||
*/
|
||||
public function access( $method, $args ) {
|
||||
if ( method_exists( $this, $method ) ) {
|
||||
return call_user_func_array( array($this, $method), $args );
|
||||
}
|
||||
else {
|
||||
throw new CSSCompression_Exception( "Unknown method in Numeric Class - " . $method );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
<?php
|
||||
/**
|
||||
* CSS Compressor [VERSION]
|
||||
* [DATE]
|
||||
* Corey Hart @ http://www.codenothing.com
|
||||
*/
|
||||
|
||||
class CSSCompression_Option {
|
||||
/**
|
||||
* Option Patterns
|
||||
*
|
||||
* @class Control: Compression Controller
|
||||
* @param (string) custom: Name of the custom mode
|
||||
* @param (array) options: Instance settings
|
||||
*/
|
||||
private $Control;
|
||||
private $custom = '__custom';
|
||||
public $options = array();
|
||||
|
||||
/**
|
||||
* Stash a reference to the controller on each instantiation
|
||||
*
|
||||
* @param (class) control: CSSCompression Controller
|
||||
*/
|
||||
public function __construct( CSSCompression_Control $control ) {
|
||||
$this->Control = $control;
|
||||
$this->options = CSSCompression::$defaults;
|
||||
$control->mode = $this->custom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maintainable access to the options array
|
||||
*
|
||||
* - Passing no arguments returns the entire options array
|
||||
* - Passing a single name argument returns the value for the option
|
||||
* - Passing both a name and value, sets the value to the name key, and returns the value
|
||||
* - Passing an array will merge the options with the array passed, for object like extension
|
||||
*
|
||||
* @param (mixed) name: The key name of the option
|
||||
* @param (mixed) value: Value to set the option
|
||||
*/
|
||||
public function option( $name = NULL, $value = NULL ) {
|
||||
if ( $name === NULL ) {
|
||||
return $this->options;
|
||||
}
|
||||
else if ( is_array( $name ) ) {
|
||||
return $this->merge( $name );
|
||||
}
|
||||
else if ( $value === NULL ) {
|
||||
return isset( $this->options[$name] ) ? $this->options[$name] : NULL;
|
||||
}
|
||||
else {
|
||||
// Readability doesn't signify custom settings
|
||||
if ( $name != 'readability' ) {
|
||||
$this->Control->mode = $this->custom;
|
||||
}
|
||||
|
||||
return ( $this->options[$name] = $value );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset's the default options
|
||||
*
|
||||
* @params none;
|
||||
*/
|
||||
public function reset() {
|
||||
// Reset and return the new options
|
||||
return $this->options = CSSCompression::$defaults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend like function to merge an array of preferences into
|
||||
* the options array.
|
||||
*
|
||||
* @param (mixed) options: Array of preferences to merge into options
|
||||
*/
|
||||
public function merge( $options = array() ) {
|
||||
$modes = CSSCompression::modes();
|
||||
if ( $options && is_array( $options ) && count( $options ) ) {
|
||||
$this->Control->mode = $this->custom;
|
||||
foreach ( $this->options as $key => $value ) {
|
||||
if ( ! isset( $options[$key] ) ) {
|
||||
continue;
|
||||
}
|
||||
else if ( strtolower( $options[$key] ) == 'on' ) {
|
||||
$this->options[$key] = true;
|
||||
}
|
||||
else if ( strtolower( $options[$key] ) == 'off' ) {
|
||||
$this->options[$key] = false;
|
||||
}
|
||||
else {
|
||||
$this->options[$key] = intval( $options[$key] );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( $options && is_string( $options ) && array_key_exists( $options, $modes ) ) {
|
||||
$this->Control->mode = $options;
|
||||
|
||||
// Default all to true, the mode has to force false
|
||||
foreach ( $this->options as $key => $value ) {
|
||||
if ( $key != 'readability' ) {
|
||||
$this->options[$key] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Merge mode into options
|
||||
foreach ( $modes[$options] as $key => $value ) {
|
||||
$this->options[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access to private methods for testing
|
||||
*
|
||||
* @param (string) method: Method to be called
|
||||
* @param (array) args: Array of paramters to be passed in
|
||||
*/
|
||||
public function access( $method, $args ) {
|
||||
if ( method_exists( $this, $method ) ) {
|
||||
return call_user_func_array( array($this, $method), $args );
|
||||
}
|
||||
else {
|
||||
throw new CSSCompression_Exception( "Unknown method in Option Class - " . $method );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
<?php
|
||||
/**
|
||||
* CSS Compressor [VERSION]
|
||||
* [DATE]
|
||||
* Corey Hart @ http://www.codenothing.com
|
||||
*/
|
||||
|
||||
class CSSCompression_Organize {
|
||||
/**
|
||||
* Organize Patterns
|
||||
*
|
||||
* @class Control: Compression Controller
|
||||
* @param (array) options: Reference to options
|
||||
* @param (regex) rsemicolon: Checks for semicolon without an escape '\' character before it
|
||||
* @param (regex) rlastsemi: Checks for semicolon at the end of the string
|
||||
*/
|
||||
private $Control;
|
||||
private $options = array();
|
||||
private $rsemicolon = "/(?<!\\\);/";
|
||||
private $rlastsemi = "/(?<!\\\);$/";
|
||||
|
||||
/**
|
||||
* Stash a reference to the controller on each instantiation
|
||||
*
|
||||
* @param (class) control: CSSCompression Controller
|
||||
*/
|
||||
public function __construct( CSSCompression_Control $control ) {
|
||||
$this->Control = $control;
|
||||
$this->options = &$control->Option->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look to see if we can combine selectors to reduce the number
|
||||
* of definitions.
|
||||
*
|
||||
* @param (array) selectors: Array of selectors, map directly to details
|
||||
* @param (array) details: Array of rule sets, map directly to selectors
|
||||
*/
|
||||
public function organize( &$selectors = array(), &$details = array() ) {
|
||||
// Combining defns based on similar selectors
|
||||
list( $selectors, $details ) = $this->reduceSelectors( $selectors, $details );
|
||||
|
||||
// Combining defns based on similar details
|
||||
list( $selectors, $details ) = $this->reduceDetails( $selectors, $details );
|
||||
|
||||
// Return in package form
|
||||
return array($selectors, $details);
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines multiply defined selectors by merging the rule sets,
|
||||
* latter declarations overide declaratins at top of file
|
||||
*
|
||||
* @param (array) selectors: Array of selectors broken down by setup
|
||||
* @param (array) details: Array of rule sets broken down by setup
|
||||
*/
|
||||
private function reduceSelectors( $selectors, $details ) {
|
||||
$keys = array_keys( $selectors );
|
||||
$max = array_pop( $keys ) + 1;
|
||||
|
||||
for ( $i = 0; $i < $max; $i++ ) {
|
||||
if ( ! isset( $selectors[$i] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for ( $k = $i + 1; $k < $max; $k++ ) {
|
||||
if ( ! isset( $selectors[$k] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( $selectors[$i] == $selectors[$k] ) {
|
||||
// Prevent noticies
|
||||
if ( ! isset( $details[$i] ) ) {
|
||||
$details[$i] = '';
|
||||
}
|
||||
if ( ! isset( $details[$k] ) ) {
|
||||
$details[$k] = '';
|
||||
}
|
||||
|
||||
// We kill the last semicolon before organization, so account for that.
|
||||
if ( $details[$i] != '' && $details[$k] != '' && ! preg_match( $this->rlastsemi, $details[$i] ) ) {
|
||||
$details[$i] .= ';' . $details[$k];
|
||||
}
|
||||
else {
|
||||
$details[$i] .= $details[$k];
|
||||
}
|
||||
|
||||
// Remove the second part
|
||||
unset( $selectors[$k], $details[$k] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array($selectors, $details);
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines multiply defined rule sets by merging the selectors
|
||||
* in comma seperated format
|
||||
*
|
||||
* @param (array) selectors: Array of selectors broken down by setup
|
||||
* @param (array) details: Array of rule sets broken down by setup
|
||||
*/
|
||||
private function reduceDetails( $selectors, $details ) {
|
||||
$keys = array_keys( $selectors );
|
||||
$max = array_pop( $keys ) + 1;
|
||||
for ( $i = 0; $i < $max; $i++ ) {
|
||||
if ( ! isset( $selectors[$i] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$arr = preg_split( $this->rsemicolon, isset( $details[$i] ) ? $details[$i] : '' );
|
||||
for ( $k = $i + 1; $k < $max; $k++ ) {
|
||||
if ( ! isset( $selectors[$k] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$match = preg_split( $this->rsemicolon, isset( $details[$k] ) ? $details[$k] : '' );
|
||||
$x = array_diff( $arr, $match );
|
||||
$y = array_diff( $match, $arr );
|
||||
|
||||
if ( count( $x ) < 1 && count( $y ) < 1 ) {
|
||||
$selectors[$i] .= ',' . $selectors[$k];
|
||||
unset( $details[$k], $selectors[$k] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array($selectors, $details);
|
||||
}
|
||||
|
||||
/**
|
||||
* Access to private methods for testing
|
||||
*
|
||||
* @param (string) method: Method to be called
|
||||
* @param (array) args: Array of paramters to be passed in
|
||||
*/
|
||||
public function access( $method, $args ) {
|
||||
if ( method_exists( $this, $method ) ) {
|
||||
return call_user_func_array( array($this, $method), $args );
|
||||
}
|
||||
else {
|
||||
throw new CSSCompression_Exception( "Unknown method in Organize Class - " . $method );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,242 @@
|
|||
<?php
|
||||
/**
|
||||
* CSS Compressor [VERSION]
|
||||
* [DATE]
|
||||
* Corey Hart @ http://www.codenothing.com
|
||||
*/
|
||||
|
||||
class CSSCompression_Selectors {
|
||||
/**
|
||||
* Selector patterns
|
||||
*
|
||||
* @class Control: Compression Controller
|
||||
* @param (string) token: Copy of the injection token
|
||||
* @param (regex) ridattr: ID Attribute matcher (combined with token)
|
||||
* @param (regex) rclassattr: class Attribute matcher (combined with token)
|
||||
* @param (array) options: Reference to options
|
||||
* @param (regex) rmark: Stop points during selector parsing
|
||||
* @param (regex) ridclassend: End of a id/class string
|
||||
* @param (regex) rescapedspace: for replacement in class attributes
|
||||
* @param (regex) rquote: Checks for the next quote character
|
||||
* @param (regex) rcomma: looks for an unescaped comma character
|
||||
* @param (regex) rspace: looks for an unescaped space character
|
||||
* @param (regex) rid: looks for an unescaped hash character
|
||||
* @param (regex) rpseudo: Add space after first-letter|line pseudo selector
|
||||
* --- when it occurs before comma or rule set
|
||||
*/
|
||||
private $Control;
|
||||
private $token = '';
|
||||
private $ridattr = "";
|
||||
private $rclassattr = "";
|
||||
private $options = array();
|
||||
private $rmark = "/(?<!\\\)(#|\.|=)/";
|
||||
private $ridclassend = "/(?<!\\\)[:#>~\[\+\*\. ]/";
|
||||
private $rquote = "/(?<!\\\)(\"|')?\]/";
|
||||
private $rescapedspace = "/\\\ /";
|
||||
private $rcomma = "/(?<!\\\),/";
|
||||
private $rspace = "/(?<!\\\)\s/";
|
||||
private $rid = "/(?<!\\\)#/";
|
||||
private $rpseudo = "/:first-(letter|line)(,|$)/i";
|
||||
|
||||
/**
|
||||
* Stash a reference to the controller on each instantiation
|
||||
*
|
||||
* @param (class) control: CSSCompression Controller
|
||||
*/
|
||||
public function __construct( CSSCompression_Control $control ) {
|
||||
$this->Control = $control;
|
||||
$this->token = CSSCompression::TOKEN;
|
||||
$this->ridattr = "/\[id=$this->token(.*?)$this->token\]/";
|
||||
$this->rclassattr = "/\[class=$this->token(.*?)$this->token\]/";
|
||||
$this->options = &$control->Option->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selector specific optimizations
|
||||
*
|
||||
* @param (array) selectors: Array of selectors
|
||||
*/
|
||||
public function selectors( &$selectors = array() ) {
|
||||
foreach ( $selectors as &$selector ) {
|
||||
// Auto ignore sections
|
||||
if ( strpos( $selector, $this->token ) === 0 ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Smart casing and token injection
|
||||
$selector = $this->parse( $selector );
|
||||
|
||||
// Converting attr to shorthanded selectors
|
||||
if ( $this->options['attr2selector'] ) {
|
||||
// Use id hash instead of id attr
|
||||
$selector = $this->idAttribute( $selector );
|
||||
|
||||
// Use class notation instead of class attr
|
||||
$selector = $this->classAttribute( $selector );
|
||||
}
|
||||
|
||||
// Remove everything before final id in a selector
|
||||
if ( $this->options['strict-id'] ) {
|
||||
$selector = $this->strictid( $selector );
|
||||
}
|
||||
|
||||
// Get rid of possible repeated selectors
|
||||
$selector = $this->repeats( $selector );
|
||||
|
||||
// Add space after pseudo selectors (so ie6 doesn't complain)
|
||||
if ( $this->options['pseudo-space'] ) {
|
||||
$selector = $this->pseudoSpace( $selector );
|
||||
}
|
||||
}
|
||||
|
||||
return $selectors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts selectors like BODY => body, DIV => div
|
||||
* and injects tokens wrappers for attribute values
|
||||
*
|
||||
* @param (string) selector: CSS Selector
|
||||
*/
|
||||
private function parse( $selector ) {
|
||||
$clean = '';
|
||||
$substr = '';
|
||||
$pos = 0;
|
||||
|
||||
while ( preg_match( $this->rmark, $selector, $match, PREG_OFFSET_CAPTURE, $pos ) ) {
|
||||
$substr = substr( $selector, $pos, $match[0][1] + 1 - $pos );
|
||||
$clean .= $this->options['lowercase-selectors'] ? strtolower( $substr ) : $substr;
|
||||
$pos = $match[0][1] + strlen( $match[1][0] );
|
||||
|
||||
// Class or id match
|
||||
if ( $match[1][0] == '#' || $match[1][0] == '.' ) {
|
||||
if ( preg_match( $this->ridclassend, $selector, $m, PREG_OFFSET_CAPTURE, $pos ) ) {
|
||||
$clean .= substr( $selector, $pos, $m[0][1] - $pos );
|
||||
$pos = $m[0][1];
|
||||
}
|
||||
else {
|
||||
$clean .= substr( $selector, $pos );
|
||||
$pos = strlen( $selector );
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Start of a string
|
||||
else if ( preg_match( $this->rquote, $selector, $m, PREG_OFFSET_CAPTURE, $pos ) ) {
|
||||
if ( $selector[$pos] == "\"" || $selector[$pos] == "'" ) {
|
||||
$pos++;
|
||||
}
|
||||
$clean .= $this->token . substr( $selector, $pos, $m[0][1] - $pos ) . $this->token . ']';
|
||||
$pos = $m[0][1] + strlen( $m[0][0] );
|
||||
}
|
||||
// No break points left
|
||||
else {
|
||||
$clean .= substr( $selector, $pos );
|
||||
$pos = strlen( $selector );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $clean . ( $this->options['lowercase-selectors'] ? strtolower( substr( $selector, $pos ) ) : substr( $selector, $pos ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert [id=blah] attribute selectors into id form selector (#blah)
|
||||
*
|
||||
* @param (string) selector: CSS Selector
|
||||
*/
|
||||
private function idAttribute( $selector ) {
|
||||
$pos = 0;
|
||||
while ( preg_match( $this->ridattr, $selector, $match, PREG_OFFSET_CAPTURE, $pos ) ) {
|
||||
// Don't convert if space found (not valid hash selector)
|
||||
if ( strpos( $match[1][0], ' ' ) !== false ) {
|
||||
$pos = $match[0][1] + strlen( $match[0][0] );
|
||||
continue;
|
||||
}
|
||||
|
||||
$selector = substr_replace( $selector, '#' . $match[1][0], $match[0][1], strlen( $match[0][0] ) );
|
||||
$pos = $match[0][1] + strlen( $match[1][0] ) + 1;
|
||||
}
|
||||
|
||||
return $selector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert [class=blah] attribute selectors into class form selector (.blah)
|
||||
*
|
||||
* @param (string) selector: CSS Selector
|
||||
*/
|
||||
private function classAttribute( $selector ) {
|
||||
$pos = 0;
|
||||
while ( preg_match( $this->rclassattr, $selector, $match, PREG_OFFSET_CAPTURE, $pos ) ) {
|
||||
// Don't convert if prescense of dot separator found
|
||||
if ( strpos( $match[1][0], '.' ) !== false ) {
|
||||
$pos = $match[0][1] + strlen( $match[0][0] );
|
||||
continue;
|
||||
}
|
||||
|
||||
$replace = '.' . preg_replace( $this->rescapedspace, ".", $match[1][0] );
|
||||
$selector = substr_replace( $selector, $replace, $match[0][1], strlen( $match[0][0] ) );
|
||||
$pos = $match[0][1] + strlen( $match[1][0] ) + 1;
|
||||
}
|
||||
|
||||
return $selector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Promotes nested id's to the front of the selector
|
||||
*
|
||||
* @param (string) selector: CSS Selector
|
||||
*/
|
||||
private function strictid( $selector ) {
|
||||
$parts = preg_split( $this->rcomma, $selector );
|
||||
foreach ( $parts as &$s ) {
|
||||
if ( preg_match( $this->rid, $s ) ) {
|
||||
$p = preg_split( $this->rid, $s );
|
||||
$s = '#' . array_pop( $p );
|
||||
}
|
||||
}
|
||||
|
||||
return implode( ',', $parts );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes repeated selectors that have been comma separated
|
||||
*
|
||||
* @param (string) selector: CSS Selector
|
||||
*/
|
||||
private function repeats( $selector ) {
|
||||
$parts = preg_split( $this->rcomma, $selector );
|
||||
$parts = array_flip( $parts );
|
||||
$parts = array_flip( $parts );
|
||||
return implode( ',', $parts );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds space after pseudo selector for ie6 like a:first-child{ => a:first-child {
|
||||
*
|
||||
* @param (string) selector: CSS Selector
|
||||
*/
|
||||
private function pseudoSpace( $selector ) {
|
||||
return preg_replace( $this->rpseudo, ":first-$1 $2", $selector );
|
||||
}
|
||||
|
||||
/**
|
||||
* Access to private methods for testing
|
||||
*
|
||||
* @param (string) method: Method to be called
|
||||
* @param (array) args: Array of paramters to be passed in
|
||||
*/
|
||||
public function access( $method, $args ) {
|
||||
if ( method_exists( $this, $method ) ) {
|
||||
if ( $method == 'selectors' ) {
|
||||
return $this->selectors( $args[0] );
|
||||
}
|
||||
else {
|
||||
return call_user_func_array( array($this, $method), $args );
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new CSSCompression_Exception( "Unknown method in Selectors Class - " . $method );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,289 @@
|
|||
<?php
|
||||
/**
|
||||
* CSS Compressor [VERSION]
|
||||
* [DATE]
|
||||
* Corey Hart @ http://www.codenothing.com
|
||||
*/
|
||||
|
||||
class CSSCompression_Setup {
|
||||
/**
|
||||
* Trim Patterns
|
||||
*
|
||||
* @class Control: Compression Controller
|
||||
* @class Individuals: Individuals Instance
|
||||
* @instance instance: CSSCompression Instance
|
||||
* @param (string) token: Copy of the injection token
|
||||
* @param (array) options: Reference to options
|
||||
* @param (array) stats: Reference to stats
|
||||
* @param (regex) rsemicolon: Checks for semicolon without an escape '\' character before it
|
||||
* @param (regex) rcolon: Checks for colon without an escape '\' character before it
|
||||
* @param (regex) rbang: Checks for '!' without an escape '\' character before it
|
||||
* @param (regex) rspacebank: Checks for an unescaped space before a bang character
|
||||
* @param (regex) rliner: Matching known 1-line intros
|
||||
* @param (regex) rnested: Matching known subsection handlers
|
||||
* @param (regex) rurl: url wrapper matching
|
||||
* @param (regex) rsinglequote: Checks for unescaped escaped single quote (mouthfull)
|
||||
* @param (array) rsetup: Expanding stylesheet for semi-tokenizing
|
||||
*/
|
||||
private $Control;
|
||||
private $Individuals;
|
||||
private $instance;
|
||||
private $token = '';
|
||||
private $options = array();
|
||||
private $stats = array();
|
||||
private $rsemicolon = "/(?<!\\\);/";
|
||||
private $rcolon = "/(?<!\\\):/";
|
||||
private $rbang = "/(?<!\\\)\!/";
|
||||
private $rspacebang = "/(?<!\\\)\s\!/";
|
||||
private $rliner = "/^@(import|charset|namespace)/i";
|
||||
private $rmedia = "/^@media/i";
|
||||
private $rurl = "/url\((.*?)\)/";
|
||||
private $rsinglequote = "/(?<!\\\)\\\'/";
|
||||
private $rsetup = array(
|
||||
'patterns' => array(
|
||||
"/(?<!\\\){/",
|
||||
"/(?<!\\\)}/",
|
||||
"/(?<!\\\)@/",
|
||||
"/(@(charset|import)[^;]*(?<!\\\);)/",
|
||||
),
|
||||
'replacements' => array(
|
||||
"\n{\n",
|
||||
"\n}\n",
|
||||
"\n@",
|
||||
"$1\n",
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Stash a reference to the controller on each instantiation
|
||||
*
|
||||
* @param (class) control: CSSCompression Controller
|
||||
*/
|
||||
public function __construct( CSSCompression_Control $control ) {
|
||||
$this->Control = $control;
|
||||
$this->Individuals = $control->Individuals;
|
||||
$this->token = CSSCompression::TOKEN;
|
||||
$this->options = &$control->Option->options;
|
||||
$this->stats = &$control->stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup selector and details arrays for compression methods
|
||||
*
|
||||
* @param (string) css: Trimed stylesheet
|
||||
*/
|
||||
public function setup( $css ) {
|
||||
// Seperate the element from the elements details
|
||||
$css = explode( "\n", preg_replace( $this->rsetup['patterns'], $this->rsetup['replacements'], $css ) );
|
||||
$newline = $this->options['readability'] > CSSCompression::READ_NONE ? "\n" : '';
|
||||
$setup = array(
|
||||
'selectors' => array(),
|
||||
'details' => array(),
|
||||
'unknown' => array(),
|
||||
'introliner' => '',
|
||||
'namespace' => '',
|
||||
'import' => '',
|
||||
'charset' => '',
|
||||
);
|
||||
|
||||
while ( count( $css ) ) {
|
||||
$row = trim( array_shift( $css ) );
|
||||
|
||||
if ( $row == '' ) {
|
||||
continue;
|
||||
}
|
||||
// Single block At-Rule set
|
||||
else if ( $row[0] == '@' && $css[0] == '{' && trim( $css[1] ) != '' && $css[2] == '}' ) {
|
||||
// Stash selector
|
||||
array_push( $setup['selectors'], $row );
|
||||
|
||||
// Stash details (after the opening brace)
|
||||
array_push( $setup['details'], $this->details( trim( $css[1] ) ) );
|
||||
|
||||
// drop the details from the stack
|
||||
$css = array_slice( $css, 3 );
|
||||
}
|
||||
// Single line At-Rules (import/charset/namespace)
|
||||
else if ( preg_match( $this->rliner, $row, $match ) ) {
|
||||
$setup[$match[1]] .= $this->liner( $row ) . $newline;
|
||||
}
|
||||
// Nested At-Rule declaration blocks
|
||||
else if ( $row[0] == '@' && $css[0] == '{' ) {
|
||||
// Stash atrule as selector
|
||||
array_push( $setup['selectors'], $this->token . $row );
|
||||
|
||||
// Stash details (after the opening brace)
|
||||
array_push( $setup['details'], $this->nested( $css, preg_match( $this->rmedia, $row ) ) . $newline );
|
||||
}
|
||||
// Unknown single line At-Rules
|
||||
else if ( $row[0] == '@' && substr( $row, -1 ) == ';' ) {
|
||||
$setup['introliner'] .= $row . $newline;
|
||||
}
|
||||
// Declaration Block
|
||||
else if ( count( $css ) >= 3 && $css[0] == '{' && $css[2] == '}' ) {
|
||||
// Stash selector
|
||||
array_push( $setup['selectors'], $row );
|
||||
|
||||
// Stash details (after the opening brace)
|
||||
array_push( $setup['details'], $this->details( trim( $css[1] ) ) );
|
||||
|
||||
// drop the details from the stack
|
||||
$css = array_slice( $css, 3 );
|
||||
}
|
||||
// Last catch, store unknown artifacts as selectors with a token
|
||||
// and give it an empty rule set
|
||||
else {
|
||||
// Still add to unknown stack, for notification
|
||||
array_push( $setup['unknown'], $row );
|
||||
|
||||
// Stash unknown artifacts as selectors with a token
|
||||
array_push( $setup['selectors'], $this->token . $row );
|
||||
|
||||
// Give it an empty rule set
|
||||
array_push( $setup['details'], '' );
|
||||
}
|
||||
}
|
||||
|
||||
return $setup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run nested elements through a separate instance of compression
|
||||
*
|
||||
* @param (array) css: Reference to the original css array
|
||||
* @param (bool) organize: Whether or not to organize the subsection (only true for media sections)
|
||||
*/
|
||||
private function nested( &$css = array(), $organize = false ) {
|
||||
$options = $this->options;
|
||||
$left = 0;
|
||||
$right = 0;
|
||||
$row = '';
|
||||
$independent = '';
|
||||
$content = '';
|
||||
$spacing = '';
|
||||
$newline = $this->options['readability'] > CSSCompression::READ_NONE ? "\n" : '';
|
||||
|
||||
// Find the end of the nested section
|
||||
while ( count( $css ) && ( $left < 1 || $left > $right ) ) {
|
||||
$row = trim( array_shift( $css ) );
|
||||
|
||||
if ( $row == '' ) {
|
||||
continue;
|
||||
}
|
||||
else if ( $row == '{' ) {
|
||||
$left++;
|
||||
}
|
||||
else if ( $row == '}' ) {
|
||||
$right++;
|
||||
}
|
||||
else if ( count( $css ) && substr( $row, 0, 1 ) != '@' && substr( $css[0], 0, 1 ) == '@' && substr( $row, -1 ) == ';' ) {
|
||||
$independent .= $row;
|
||||
continue;
|
||||
}
|
||||
|
||||
$content .= $row;
|
||||
}
|
||||
|
||||
// Ensure copy of instance exists
|
||||
if ( ! $this->instance ) {
|
||||
$this->instance = new CSSCompression();
|
||||
}
|
||||
|
||||
// Fresh start
|
||||
$this->instance->reset();
|
||||
|
||||
// Compress the nested section independently after removing the wrapping braces
|
||||
// Also make sure to only organize media sections
|
||||
if ( $options['organize'] == true && $organize == false ) {
|
||||
$options['organize'] = false;
|
||||
}
|
||||
// Independent sections should be prepended to the next compressed section
|
||||
$content = ( $independent == '' ? '' : $independent . $newline )
|
||||
. $this->instance->compress( substr( $content, 1, -1 ), $options );
|
||||
|
||||
// Formatting for anything higher then 0 readability
|
||||
if ( $newline == "\n" ) {
|
||||
$content = "\n\t" . str_replace( "\n", "\n\t", $content ) . "\n";
|
||||
$spacing = $this->options['readability'] > CSSCompression::READ_MIN ? ' ' : '';
|
||||
}
|
||||
|
||||
// Stash the compressed nested script
|
||||
return "$spacing{" . $content . "}$newline";
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts import/namespace urls into strings
|
||||
*
|
||||
* @param (string) row: At-rule
|
||||
*/
|
||||
private function liner( $row ) {
|
||||
$pos = 0;
|
||||
while ( preg_match( $this->rurl, $row, $match, PREG_OFFSET_CAPTURE, $pos ) ) {
|
||||
$quote = preg_match( $this->rsinglequote, $match[1][0] ) ? '"' : "'";
|
||||
$replace = $quote . $match[1][0] . $quote;
|
||||
$row = substr_replace( $row, $replace, $match[0][1], strlen( $match[0][0] ) );
|
||||
$pos = $match[0][1] + strlen( $replace );
|
||||
}
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run individual compression techniques on each property of a selector
|
||||
*
|
||||
* @param (string) row: Selector properties
|
||||
*/
|
||||
private function details( $row ) {
|
||||
$row = preg_split( $this->rsemicolon, $row );
|
||||
$parts = array();
|
||||
$details = '';
|
||||
|
||||
foreach ( $row as $line ) {
|
||||
// Set loopers
|
||||
$parts = preg_split( $this->rcolon, $line, 2 );
|
||||
$prop = '';
|
||||
$value = '';
|
||||
|
||||
// Property
|
||||
if ( isset( $parts[0] ) && ( $parts[0] = trim( $parts[0] ) ) != '' ) {
|
||||
$prop = $parts[0];
|
||||
}
|
||||
|
||||
// Value
|
||||
if ( isset( $parts[1] ) && ( $parts[1] = trim( $parts[1] ) ) != '' ) {
|
||||
$value = preg_replace( $this->rbang, ' !', $parts[1] );
|
||||
}
|
||||
|
||||
// Fail safe, remove unspecified property/values
|
||||
if ( $prop == '' || $value == '' ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Run the tag/element through each compression
|
||||
list( $prop, $value ) = $this->Individuals->individuals( $prop, $value );
|
||||
|
||||
// Add counter to before stats
|
||||
$this->stats['before']['props']++;
|
||||
|
||||
// Store the compressed element
|
||||
$details .= "$prop:" . preg_replace( $this->rspacebang, '!', $value ) . ";";
|
||||
}
|
||||
|
||||
return $details;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access to private methods for testing
|
||||
*
|
||||
* @param (string) method: Method to be called
|
||||
* @param (array) args: Array of paramters to be passed in
|
||||
*/
|
||||
public function access( $method, $args ) {
|
||||
if ( method_exists( $this, $method ) ) {
|
||||
return call_user_func_array( array($this, $method), $args );
|
||||
}
|
||||
else {
|
||||
throw new CSSCompression_Exception( "Unknown method in Setup Class - " . $method );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,210 @@
|
|||
<?php
|
||||
/**
|
||||
* CSS Compressor [VERSION]
|
||||
* [DATE]
|
||||
* Corey Hart @ http://www.codenothing.com
|
||||
*/
|
||||
|
||||
class CSSCompression_Trim {
|
||||
/**
|
||||
* Trim Patterns
|
||||
*
|
||||
* @class Control: Compression Controller
|
||||
* @param (array) options: Reference to options
|
||||
* @param (regex) rcmark: Marking point when traversing through sheet for comments
|
||||
* @param (regex) rendcomment: Finds the ending comment point
|
||||
* @param (regex) rendquote: Finds the ending quote point
|
||||
* @param (regex) rendsinglequote: Finds the ending single quote point
|
||||
* @param (array) rescape: Array of patterns of groupings that should be escaped
|
||||
* @param (array) trimmings: Stylesheet trimming patterns/replacements
|
||||
* @param (array) escaped: Array of characters that need to be escaped
|
||||
*/
|
||||
private $Control;
|
||||
private $options = array();
|
||||
private $rcmark = "/((?<!\\\)\/\*|(?<!\\\)\"|(?<!\\\)')/";
|
||||
private $rendcomment = "/\*\//";
|
||||
private $rendquote = "/(?<!\\\)\"/";
|
||||
private $rendsinglequote = "/(?<!\\\)'/";
|
||||
private $rescape = array(
|
||||
"/(url\()([^'\"].*?)(\))/is",
|
||||
"/((?<!\\\)\")(.*?)((?<!\\\)\")/s",
|
||||
"/((?<!\\\)')(.*?)((?<!\\\)')/s",
|
||||
);
|
||||
private $trimmings = array(
|
||||
'patterns' => array(
|
||||
"/(?<!\\\)(\s+)?(?<!\\\)([!,{};>\~\+\/])(?<!\\\)(\s+)?/s", // Remove un-needed spaces around special characters
|
||||
"/url\((?<!\\\)\"(.*?)(?<!\\\)\"\)/is", // Remove quotes from urls
|
||||
"/url\((?<!\\\)'(.*?)(?<!\\\)'\)/is", // Remove single quotes from urls
|
||||
"/url\((.*?)\)/is", // Lowercase url wrapper
|
||||
"/(?<!\\\);{2,}/", // Remove unecessary semi-colons
|
||||
"/(?<!\\\)\s+/s", // Compress all spaces into single space
|
||||
),
|
||||
'replacements' => array(
|
||||
'$2',
|
||||
'url($1)',
|
||||
'url($1)',
|
||||
'url($1)',
|
||||
';',
|
||||
' ',
|
||||
),
|
||||
);
|
||||
private $escaped = array(
|
||||
'search' => array(
|
||||
":",
|
||||
";",
|
||||
"}",
|
||||
"{",
|
||||
"@",
|
||||
"!",
|
||||
",",
|
||||
">",
|
||||
"+",
|
||||
"~",
|
||||
"/",
|
||||
"*",
|
||||
".",
|
||||
"=",
|
||||
"#",
|
||||
"\r",
|
||||
"\n",
|
||||
"\t",
|
||||
" ",
|
||||
),
|
||||
'replace' => array(
|
||||
"\\:",
|
||||
"\\;",
|
||||
"\\}",
|
||||
"\\{",
|
||||
"\\@",
|
||||
"\\!",
|
||||
"\\,",
|
||||
"\\>",
|
||||
"\\+",
|
||||
"\\~",
|
||||
"\\/",
|
||||
"\\*",
|
||||
"\\.",
|
||||
"\\=",
|
||||
"\\#",
|
||||
"\\r",
|
||||
"\\n",
|
||||
"\\t",
|
||||
"\\ ",
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Stash a reference to the controller on each instantiation
|
||||
*
|
||||
* @param (class) control: CSSCompression Controller
|
||||
*/
|
||||
public function __construct( CSSCompression_Control $control ) {
|
||||
$this->Control = $control;
|
||||
$this->options = &$control->Option->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Central trim handler
|
||||
*
|
||||
* @param (string) css: Stylesheet to trim
|
||||
*/
|
||||
public function trim( $css ) {
|
||||
$css = $this->comments( $css );
|
||||
$css = $this->escape( $css );
|
||||
$css = $this->strip( $css );
|
||||
return $css;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does a quick run through the script to remove all comments from the sheet,
|
||||
*
|
||||
* @param (string) css: Stylesheet to trim
|
||||
*/
|
||||
private function comments( $css ) {
|
||||
$pos = 0;
|
||||
while ( preg_match( $this->rcmark, $css, $match, PREG_OFFSET_CAPTURE, $pos ) ) {
|
||||
switch ( $match[1][0] ) {
|
||||
// Start of comment block
|
||||
case "/*":
|
||||
if ( preg_match( $this->rendcomment, $css, $m, PREG_OFFSET_CAPTURE, $match[1][1] + 1 ) ) {
|
||||
$end = $m[0][1] - $match[1][1] + strlen( $m[0][0] );
|
||||
$css = substr_replace( $css, '', $match[1][1], $end );
|
||||
$pos = $match[0][1];
|
||||
}
|
||||
else {
|
||||
$css = substr( $css, 0, $match[1][1] );
|
||||
break 2;
|
||||
}
|
||||
break;
|
||||
// Start of string
|
||||
case "\"":
|
||||
if ( preg_match( $this->rendquote, $css, $m, PREG_OFFSET_CAPTURE, $match[1][1] + 1 ) ) {
|
||||
$pos = $m[0][1] + strlen( $m[0][0] );
|
||||
}
|
||||
else {
|
||||
break 2;
|
||||
}
|
||||
break;
|
||||
// Start of string
|
||||
case "'":
|
||||
if ( preg_match( $this->rendsinglequote, $css, $m, PREG_OFFSET_CAPTURE, $match[1][1] + 1 ) ) {
|
||||
$pos = $m[0][1] + strlen( $m[0][0] );
|
||||
}
|
||||
else {
|
||||
break 2;
|
||||
}
|
||||
break;
|
||||
// Should have never gotten here
|
||||
default:
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
|
||||
return $css;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape out possible splitter characters within urls
|
||||
*
|
||||
* @param (string) css: Full stylesheet
|
||||
*/
|
||||
private function escape( $css ) {
|
||||
foreach ( $this->rescape as $regex ) {
|
||||
$start = 0;
|
||||
while ( preg_match( $regex, $css, $match, PREG_OFFSET_CAPTURE, $start ) ) {
|
||||
$value = $match[1][0]
|
||||
. str_replace( $this->escaped['search'], $this->escaped['replace'], $match[2][0] )
|
||||
. $match[3][0];
|
||||
$css = substr_replace( $css, $value, $match[0][1], strlen( $match[0][0] ) );
|
||||
$start = $match[0][1] + strlen( $value ) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return $css;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs initial formatting to setup for compression
|
||||
*
|
||||
* @param (string) css: CSS Contents
|
||||
*/
|
||||
private function strip( $css ) {
|
||||
// Run replacements
|
||||
return trim( preg_replace( $this->trimmings['patterns'], $this->trimmings['replacements'], $css ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Access to private methods for testing
|
||||
*
|
||||
* @param (string) method: Method to be called
|
||||
* @param (array) args: Array of paramters to be passed in
|
||||
*/
|
||||
public function access( $method, $args ) {
|
||||
if ( method_exists( $this, $method ) ) {
|
||||
return call_user_func_array( array($this, $method), $args );
|
||||
}
|
||||
else {
|
||||
throw new CSSCompression_Exception( "Unknown method in Trim Class - " . $method );
|
||||
}
|
||||
}
|
||||
}
|
Reference in a new issue