[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/src/applications/celerity/ -> CelerityResourceTransformer.php (source)

   1  <?php
   2  
   3  final class CelerityResourceTransformer {
   4  
   5    private $minify;
   6    private $rawURIMap;
   7    private $celerityMap;
   8    private $translateURICallback;
   9    private $currentPath;
  10  
  11    public function setTranslateURICallback($translate_uricallback) {
  12      $this->translateURICallback = $translate_uricallback;
  13      return $this;
  14    }
  15  
  16    public function setMinify($minify) {
  17      $this->minify = $minify;
  18      return $this;
  19    }
  20  
  21    public function setCelerityMap(CelerityResourceMap $celerity_map) {
  22      $this->celerityMap = $celerity_map;
  23      return $this;
  24    }
  25  
  26    public function setRawURIMap(array $raw_urimap) {
  27      $this->rawURIMap = $raw_urimap;
  28      return $this;
  29    }
  30  
  31    public function getRawURIMap() {
  32      return $this->rawURIMap;
  33    }
  34  
  35    /**
  36     * @phutil-external-symbol function jsShrink
  37     */
  38    public function transformResource($path, $data) {
  39      $type = self::getResourceType($path);
  40  
  41      switch ($type) {
  42        case 'css':
  43          $data = $this->replaceCSSPrintRules($path, $data);
  44          $data = $this->replaceCSSVariables($path, $data);
  45          $data = preg_replace_callback(
  46            '@url\s*\((\s*[\'"]?.*?)\)@s',
  47            nonempty(
  48              $this->translateURICallback,
  49              array($this, 'translateResourceURI')),
  50            $data);
  51          break;
  52      }
  53  
  54      if (!$this->minify) {
  55        return $data;
  56      }
  57  
  58      // Some resources won't survive minification (like Raphael.js), and are
  59      // marked so as not to be minified.
  60      if (strpos($data, '@'.'do-not-minify') !== false) {
  61        return $data;
  62      }
  63  
  64      switch ($type) {
  65        case 'css':
  66          // Remove comments.
  67          $data = preg_replace('@/\*.*?\*/@s', '', $data);
  68          // Remove whitespace around symbols.
  69          $data = preg_replace('@\s*([{}:;,])\s*@', '\1', $data);
  70          // Remove unnecessary semicolons.
  71          $data = preg_replace('@;}@', '}', $data);
  72          // Replace #rrggbb with #rgb when possible.
  73          $data = preg_replace(
  74            '@#([a-f0-9])\1([a-f0-9])\2([a-f0-9])\3@i',
  75            '#\1\2\3',
  76            $data);
  77          $data = trim($data);
  78          break;
  79        case 'js':
  80  
  81          // If `jsxmin` is available, use it. jsxmin is the Javelin minifier and
  82          // produces the smallest output, but is complicated to build.
  83          if (Filesystem::binaryExists('jsxmin')) {
  84            $future = new ExecFuture('jsxmin __DEV__:0');
  85            $future->write($data);
  86            list($err, $result) = $future->resolve();
  87            if (!$err) {
  88              $data = $result;
  89              break;
  90            }
  91          }
  92  
  93          // If `jsxmin` is not available, use `JsShrink`, which doesn't compress
  94          // quite as well but is always available.
  95          $root = dirname(phutil_get_library_root('phabricator'));
  96          require_once $root.'/externals/JsShrink/jsShrink.php';
  97          $data = jsShrink($data);
  98  
  99          break;
 100      }
 101  
 102      return $data;
 103    }
 104  
 105    public static function getResourceType($path) {
 106      return last(explode('.', $path));
 107    }
 108  
 109    public function translateResourceURI(array $matches) {
 110      $uri = trim($matches[1], "'\" \r\t\n");
 111      $tail = '';
 112  
 113      // If the resource URI has a query string or anchor, strip it off before
 114      // we go looking for the resource. We'll stitch it back on later. This
 115      // primarily affects FontAwesome.
 116  
 117      $parts = preg_split('/(?=[?#])/', $uri, 2);
 118      if (count($parts) == 2) {
 119        $uri = $parts[0];
 120        $tail = $parts[1];
 121      }
 122  
 123      $alternatives = array_unique(
 124        array(
 125          $uri,
 126          ltrim($uri, '/'),
 127        ));
 128  
 129      foreach ($alternatives as $alternative) {
 130        if ($this->rawURIMap !== null) {
 131          if (isset($this->rawURIMap[$alternative])) {
 132            $uri = $this->rawURIMap[$alternative];
 133            break;
 134          }
 135        }
 136  
 137        if ($this->celerityMap) {
 138          $resource_uri = $this->celerityMap->getURIForName($alternative);
 139          if ($resource_uri) {
 140            // Check if we can use a data URI for this resource. If not, just
 141            // use a normal Celerity URI.
 142            $data_uri = $this->generateDataURI($alternative);
 143            if ($data_uri) {
 144              $uri = $data_uri;
 145            } else {
 146              $uri = $resource_uri;
 147            }
 148            break;
 149          }
 150        }
 151      }
 152  
 153      return 'url('.$uri.$tail.')';
 154    }
 155  
 156    private function replaceCSSVariables($path, $data) {
 157      $this->currentPath = $path;
 158      return preg_replace_callback(
 159        '/{\$([^}]+)}/',
 160        array($this, 'replaceCSSVariable'),
 161        $data);
 162    }
 163  
 164    private function replaceCSSPrintRules($path, $data) {
 165      $this->currentPath = $path;
 166      return preg_replace_callback(
 167        '/!print\s+(.+?{.+?})/s',
 168        array($this, 'replaceCSSPrintRule'),
 169        $data);
 170    }
 171  
 172    public static function getCSSVariableMap() {
 173      return array(
 174        // Base Colors
 175        'red'           => '#c0392b',
 176        'lightred'      => '#f4dddb',
 177        'orange'        => '#e67e22',
 178        'lightorange'   => '#f7e2d4',
 179        'yellow'        => '#f1c40f',
 180        'lightyellow'   => '#fdf5d4',
 181        'green'         => '#139543',
 182        'lightgreen'    => '#d7eddf',
 183        'blue'          => '#2980b9',
 184        'lightblue'     => '#daeaf3',
 185        'sky'           => '#3498db',
 186        'lightsky'      => '#ddeef9',
 187        'indigo'        => '#6e5cb6',
 188        'lightindigo'   => '#eae6f7',
 189        'pink'          => '#da49be',
 190        'lightpink'     => '#fbeaf8',
 191        'violet'        => '#8e44ad',
 192        'lightviolet'   => '#ecdff1',
 193        'charcoal'      => '#4b4d51',
 194        'backdrop'      => '#dadee7',
 195        'hovergrey'     => '#c5cbcf',
 196        'hoverblue'     => '#eceff5',
 197        'hoverborder'   => '#dfe1e9',
 198        'hoverselectedgrey' => '#bbc4ca',
 199        'hoverselectedblue' => '#e6e9ee',
 200  
 201        // Base Greys
 202        'lightgreyborder'     => '#C7CCD9',
 203        'greyborder'          => '#A1A6B0',
 204        'darkgreyborder'      => '#676A70',
 205        'lightgreytext'       => '#92969D',
 206        'greytext'            => '#74777D',
 207        'darkgreytext'        => '#4B4D51',
 208        'lightgreybackground' => '#F7F7F7',
 209        'greybackground'      => '#EBECEE',
 210        'darkgreybackground'  => '#DFE0E2',
 211  
 212        // Base Blues
 213        'thinblueborder'      => '#DDE8EF',
 214        'lightblueborder'     => '#BFCFDA',
 215        'blueborder'          => '#8C98B8',
 216        'darkblueborder'      => '#626E82',
 217        'lightbluebackground' => '#F8F9FC',
 218        'bluebackground'      => '#DAE7FF',
 219        'lightbluetext'       => '#8C98B8',
 220        'bluetext'            => '#6B748C',
 221        'darkbluetext'        => '#464C5C',
 222  
 223        // Base Greens
 224        'lightgreenborder'      => '#bfdac1',
 225        'greenborder'           => '#8cb89c',
 226        'greentext'             => '#3e6d35',
 227        'lightgreenbackground'  => '#e6f2e4',
 228  
 229        // Base Red
 230        'lightredborder'        => '#f4c6c6',
 231        'redborder'             => '#eb9797',
 232        'redtext'               => '#802b2b',
 233        'lightredbackground'    => '#f5e1e1',
 234  
 235        // Base Violet
 236        'lightvioletborder'     => '#cfbddb',
 237        'violetborder'          => '#b589ba',
 238        'violettext'            => '#603c73',
 239        'lightvioletbackground' => '#e9dfee',
 240  
 241        // Shades are a more muted set of our base colors
 242        // better suited to blending into other UIs.
 243  
 244        // Shade Red
 245        'sh-lightredborder'     => '#efcfcf',
 246        'sh-redborder'          => '#d1abab',
 247        'sh-redicon'            => '#c85a5a',
 248        'sh-redtext'            => '#a53737',
 249        'sh-redbackground'      => '#f7e6e6',
 250  
 251        // Shade Orange
 252        'sh-lightorangeborder'  => '#f8dcc3',
 253        'sh-orangeborder'       => '#dbb99e',
 254        'sh-orangeicon'         => '#e78331',
 255        'sh-orangetext'         => '#ba6016',
 256        'sh-orangebackground'   => '#fbede1',
 257  
 258        // Shade Yellow
 259        'sh-lightyellowborder'  => '#e9dbcd',
 260        'sh-yellowborder'       => '#c9b8a8',
 261        'sh-yellowicon'         => '#9b946e',
 262        'sh-yellowtext'         => '#726f56',
 263        'sh-yellowbackground'   => '#fdf3da',
 264  
 265        // Shade Green
 266        'sh-lightgreenborder'   => '#c6e6c7',
 267        'sh-greenborder'        => '#a0c4a1',
 268        'sh-greenicon'          => '#4ca74e',
 269        'sh-greentext'          => '#326d34',
 270        'sh-greenbackground'    => '#ddefdd',
 271  
 272        // Shade Blue
 273        'sh-lightblueborder'    => '#cfdbe3',
 274        'sh-blueborder'         => '#a7b5bf',
 275        'sh-blueicon'           => '#6b748c',
 276        'sh-bluetext'           => '#464c5c',
 277        'sh-bluebackground'     => '#dee7f8',
 278  
 279        // Shade Indigo
 280        'sh-lightindigoborder'  => '#d1c9ee',
 281        'sh-indigoborder'       => '#bcb4da',
 282        'sh-indigoicon'         => '#8672d4',
 283        'sh-indigotext'         => '#6e5cb6',
 284        'sh-indigobackground'   => '#eae6f7',
 285  
 286        // Shade Violet
 287        'sh-lightvioletborder'  => '#e0d1e7',
 288        'sh-violetborder'       => '#bcabc5',
 289        'sh-violeticon'         => '#9260ad',
 290        'sh-violettext'         => '#69427f',
 291        'sh-violetbackground'   => '#efe8f3',
 292  
 293        // Shade Pink
 294        'sh-lightpinkborder'  => '#f6d5ef',
 295        'sh-pinkborder'       => '#d5aecd',
 296        'sh-pinkicon'         => '#e26fcb',
 297        'sh-pinktext'         => '#da49be',
 298        'sh-pinkbackground'   => '#fbeaf8',
 299  
 300        // Shade Grey
 301        'sh-lightgreyborder'    => '#d8d8d8',
 302        'sh-greyborder'         => '#b2b2b2',
 303        'sh-greyicon'           => '#757575',
 304        'sh-greytext'           => '#555555',
 305        'sh-greybackground'     => '#e7e7e7',
 306  
 307        // Shade Disabled
 308        'sh-lightdisabledborder'  => '#e5e5e5',
 309        'sh-disabledborder'       => '#cbcbcb',
 310        'sh-disabledicon'         => '#bababa',
 311        'sh-disabledtext'         => '#a6a6a6',
 312        'sh-disabledbackground'   => '#f3f3f3',
 313  
 314      );
 315    }
 316  
 317  
 318    public function replaceCSSVariable($matches) {
 319      static $map;
 320      if (!$map) {
 321        $map = self::getCSSVariableMap();
 322      }
 323  
 324      $var_name = $matches[1];
 325      if (empty($map[$var_name])) {
 326        $path = $this->currentPath;
 327        throw new Exception(
 328          "CSS file '{$path}' has unknown variable '{$var_name}'.");
 329      }
 330  
 331      return $map[$var_name];
 332    }
 333  
 334    public function replaceCSSPrintRule($matches) {
 335      $rule = $matches[1];
 336  
 337      $rules = array();
 338      $rules[] = '.printable '.$rule;
 339      $rules[] = "@media print {\n  ".str_replace("\n", "\n  ", $rule)."\n}\n";
 340  
 341      return implode("\n\n", $rules);
 342    }
 343  
 344  
 345    /**
 346     * Attempt to generate a data URI for a resource. We'll generate a data URI
 347     * if the resource is a valid resource of an appropriate type, and is
 348     * small enough. Otherwise, this method will return `null` and we'll end up
 349     * using a normal URI instead.
 350     *
 351     * @param string  Resource name to attempt to generate a data URI for.
 352     * @return string|null Data URI, or null if we declined to generate one.
 353     */
 354    private function generateDataURI($resource_name) {
 355      $ext = last(explode('.', $resource_name));
 356      switch ($ext) {
 357        case 'png':
 358          $type = 'image/png';
 359          break;
 360        case 'gif':
 361          $type = 'image/gif';
 362          break;
 363        case 'jpg':
 364          $type = 'image/jpeg';
 365          break;
 366        default:
 367          return null;
 368      }
 369  
 370      // In IE8, 32KB is the maximum supported URI length.
 371      $maximum_data_size = (1024 * 32);
 372  
 373      $data = $this->celerityMap->getResourceDataForName($resource_name);
 374      if (strlen($data) >= $maximum_data_size) {
 375        // If the data is already too large on its own, just bail before
 376        // encoding it.
 377        return null;
 378      }
 379  
 380      $uri = 'data:'.$type.';base64,'.base64_encode($data);
 381      if (strlen($uri) >= $maximum_data_size) {
 382        return null;
 383      }
 384  
 385      return $uri;
 386    }
 387  
 388  }


Generated: Sun Nov 30 09:20:46 2014 Cross-referenced by PHPXref 0.7.1