PageRenderTime 43ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/html/build/tools/css-compressor/lib/Selectors.php

https://github.com/abuiles/bricker
PHP | 245 lines | 183 code | 12 blank | 50 comment | 12 complexity | c165f8869d002ddbc31819c292483b1e MD5 | raw file
  1. <?php
  2. /**
  3. * CSS Compressor [VERSION]
  4. * [DATE]
  5. * Corey Hart @ http://www.codenothing.com
  6. */
  7. Class CSSCompression_Selectors
  8. {
  9. /**
  10. * Selector patterns
  11. *
  12. * @class Control: Compression Controller
  13. * @param (string) token: Copy of the injection token
  14. * @param (regex) ridattr: ID Attribute matcher (combined with token)
  15. * @param (regex) rclassattr: class Attribute matcher (combined with token)
  16. * @param (array) options: Reference to options
  17. * @param (regex) rmark: Stop points during selector parsing
  18. * @param (regex) ridclassend: End of a id/class string
  19. * @param (regex) rescapedspace: for replacement in class attributes
  20. * @param (regex) rquote: Checks for the next quote character
  21. * @param (regex) rcomma: looks for an unescaped comma character
  22. * @param (regex) rspace: looks for an unescaped space character
  23. * @param (regex) rid: looks for an unescaped hash character
  24. * @param (regex) rpseudo: Add space after first-letter|line pseudo selector
  25. * --- when it occurs before comma or rule set
  26. */
  27. private $Control;
  28. private $token = '';
  29. private $ridattr = "";
  30. private $rclassattr = "";
  31. private $options = array();
  32. private $rmark = "/(?<!\\\)(#|\.|=)/";
  33. private $ridclassend = "/(?<!\\\)[:#>~\[\+\*\. ]/";
  34. private $rquote = "/(?<!\\\)(\"|')?\]/";
  35. private $rescapedspace = "/\\\ /";
  36. private $rcomma = "/(?<!\\\),/";
  37. private $rspace = "/(?<!\\\)\s/";
  38. private $rid = "/(?<!\\\)#/";
  39. private $rpseudo = "/:first-(letter|line)(,|$)/i";
  40. /**
  41. * Stash a reference to the controller on each instantiation
  42. *
  43. * @param (class) control: CSSCompression Controller
  44. */
  45. public function __construct( CSSCompression_Control $control ) {
  46. $this->Control = $control;
  47. $this->token = CSSCompression::TOKEN;
  48. $this->ridattr = "/\[id=$this->token(.*?)$this->token\]/";
  49. $this->rclassattr = "/\[class=$this->token(.*?)$this->token\]/";
  50. $this->options = &$control->Option->options;
  51. }
  52. /**
  53. * Selector specific optimizations
  54. *
  55. * @param (array) selectors: Array of selectors
  56. */
  57. public function selectors( &$selectors = array() ) {
  58. foreach ( $selectors as &$selector ) {
  59. // Auto ignore sections
  60. if ( strpos( $selector, $this->token ) === 0 ) {
  61. continue;
  62. }
  63. // Smart casing and token injection
  64. $selector = $this->parse( $selector );
  65. // Converting attr to shorthanded selectors
  66. if ( $this->options['attr2selector'] ) {
  67. // Use id hash instead of id attr
  68. $selector = $this->idAttribute( $selector );
  69. // Use class notation instead of class attr
  70. $selector = $this->classAttribute( $selector );
  71. }
  72. // Remove everything before final id in a selector
  73. if ( $this->options['strict-id'] ) {
  74. $selector = $this->strictid( $selector );
  75. }
  76. // Get rid of possible repeated selectors
  77. $selector = $this->repeats( $selector );
  78. // Add space after pseudo selectors (so ie6 doesn't complain)
  79. if ( $this->options['pseudo-space'] ) {
  80. $selector = $this->pseudoSpace( $selector );
  81. }
  82. }
  83. return $selectors;
  84. }
  85. /**
  86. * Converts selectors like BODY => body, DIV => div
  87. * and injects tokens wrappers for attribute values
  88. *
  89. * @param (string) selector: CSS Selector
  90. */
  91. private function parse( $selector ) {
  92. $clean = '';
  93. $substr = '';
  94. $pos = 0;
  95. while ( preg_match( $this->rmark, $selector, $match, PREG_OFFSET_CAPTURE, $pos ) ) {
  96. $substr = substr( $selector, $pos, $match[ 0 ][ 1 ] + 1 - $pos );
  97. $clean .= $this->options['lowercase-selectors'] ? strtolower( $substr ) : $substr;
  98. $pos = $match[ 0 ][ 1 ] + strlen( $match[ 1 ][ 0 ] );
  99. // Class or id match
  100. if ( $match[ 1 ][ 0 ] == '#' || $match[ 1 ][ 0 ] == '.' ) {
  101. if ( preg_match( $this->ridclassend, $selector, $m, PREG_OFFSET_CAPTURE, $pos ) ) {
  102. $clean .= substr( $selector, $pos, $m[ 0 ][ 1 ] - $pos );
  103. $pos = $m[ 0 ][ 1 ];
  104. }
  105. else {
  106. $clean .= substr( $selector, $pos );
  107. $pos = strlen( $selector );
  108. break;
  109. }
  110. }
  111. // Start of a string
  112. else if ( preg_match( $this->rquote, $selector, $m, PREG_OFFSET_CAPTURE, $pos ) ) {
  113. if ( $selector[ $pos ] == "\"" || $selector[ $pos ] == "'" ) {
  114. $pos++;
  115. }
  116. $clean .= $this->token . substr( $selector, $pos, $m[ 0 ][ 1 ] - $pos ) . $this->token . ']';
  117. $pos = $m[ 0 ][ 1 ] + strlen( $m[ 0 ][ 0 ] );
  118. }
  119. // No break points left
  120. else {
  121. $clean .= substr( $selector, $pos );
  122. $pos = strlen( $selector );
  123. break;
  124. }
  125. }
  126. return $clean . ( $this->options['lowercase-selectors'] ? strtolower( substr( $selector, $pos ) ) : substr( $selector, $pos ) );
  127. }
  128. /**
  129. * Convert [id=blah] attribute selectors into id form selector (#blah)
  130. *
  131. * @param (string) selector: CSS Selector
  132. */
  133. private function idAttribute( $selector ) {
  134. $pos = 0;
  135. while ( preg_match( $this->ridattr, $selector, $match, PREG_OFFSET_CAPTURE, $pos ) ) {
  136. // Don't convert if space found (not valid hash selector)
  137. if ( strpos( $match[ 1 ][ 0 ], ' ' ) !== false ) {
  138. $pos = $match[ 0 ][ 1 ] + strlen( $match[ 0 ][ 0 ] );
  139. continue;
  140. }
  141. $selector = substr_replace( $selector, '#' . $match[ 1 ][ 0 ], $match[ 0 ][ 1 ], strlen( $match[ 0 ][ 0 ] ) );
  142. $pos = $match[ 0 ][ 1 ] + strlen( $match[ 1 ][ 0 ] ) + 1;
  143. }
  144. return $selector;
  145. }
  146. /**
  147. * Convert [class=blah] attribute selectors into class form selector (.blah)
  148. *
  149. * @param (string) selector: CSS Selector
  150. */
  151. private function classAttribute( $selector ) {
  152. $pos = 0;
  153. while ( preg_match( $this->rclassattr, $selector, $match, PREG_OFFSET_CAPTURE, $pos ) ) {
  154. // Don't convert if prescense of dot separator found
  155. if ( strpos( $match[ 1 ][ 0 ], '.' ) !== false ) {
  156. $pos = $match[ 0 ][ 1 ] + strlen( $match[ 0 ][ 0 ] );
  157. continue;
  158. }
  159. $replace = '.' . preg_replace( $this->rescapedspace, ".", $match[ 1 ][ 0 ] );
  160. $selector = substr_replace( $selector, $replace, $match[ 0 ][ 1 ], strlen( $match[ 0 ][ 0 ] ) );
  161. $pos = $match[ 0 ][ 1 ] + strlen( $match[ 1 ][ 0 ] ) + 1;
  162. }
  163. return $selector;
  164. }
  165. /**
  166. * Promotes nested id's to the front of the selector
  167. *
  168. * @param (string) selector: CSS Selector
  169. */
  170. private function strictid( $selector ) {
  171. $parts = preg_split( $this->rcomma, $selector );
  172. foreach ( $parts as &$s ) {
  173. if ( preg_match( $this->rid, $s ) ) {
  174. $p = preg_split( $this->rid, $s );
  175. $s = '#' . array_pop( $p );
  176. }
  177. }
  178. return implode( ',', $parts );
  179. }
  180. /**
  181. * Removes repeated selectors that have been comma separated
  182. *
  183. * @param (string) selector: CSS Selector
  184. */
  185. private function repeats( $selector ) {
  186. $parts = preg_split( $this->rcomma, $selector );
  187. $parts = array_flip( $parts );
  188. $parts = array_flip( $parts );
  189. return implode( ',', $parts );
  190. }
  191. /**
  192. * Adds space after pseudo selector for ie6 like a:first-child{ => a:first-child {
  193. *
  194. * @param (string) selector: CSS Selector
  195. */
  196. private function pseudoSpace( $selector ) {
  197. return preg_replace( $this->rpseudo, ":first-$1 $2", $selector );
  198. }
  199. /**
  200. * Access to private methods for testing
  201. *
  202. * @param (string) method: Method to be called
  203. * @param (array) args: Array of paramters to be passed in
  204. */
  205. public function access( $method, $args ) {
  206. if ( method_exists( $this, $method ) ) {
  207. if ( $method == 'selectors' ) {
  208. return $this->selectors( $args[ 0 ] );
  209. }
  210. else {
  211. return call_user_func_array( array( $this, $method ), $args );
  212. }
  213. }
  214. else {
  215. throw new CSSCompression_Exception( "Unknown method in Selectors Class - " . $method );
  216. }
  217. }
  218. };
  219. ?>