From 19bae03b6f86776599d0f95b521d739ba66d5e76 Mon Sep 17 00:00:00 2001
From: emkael <emkael@tlen.pl>
Date: Fri, 29 Apr 2016 14:36:48 +0200
Subject:  * CSS minification in client script manager

---
 app/php/web/ClientScriptManager.php | 220 +++++++++++++++++++++++++++++++++++-
 1 file changed, 215 insertions(+), 5 deletions(-)

diff --git a/app/php/web/ClientScriptManager.php b/app/php/web/ClientScriptManager.php
index 749bbcc..2dab6e9 100644
--- a/app/php/web/ClientScriptManager.php
+++ b/app/php/web/ClientScriptManager.php
@@ -16,11 +16,14 @@ class ClientScriptManager extends TClientScriptManager {
 
     private function _getBasePaths($urls) {
         $basePath = $this->_getBasePath();
-        return array_map(
-            function($path) use($basePath) {
-                return $basePath . DIRECTORY_SEPARATOR . $path;
-            },
-            $urls
+        return array_combine(
+            $urls,
+            array_map(
+                function($path) use($basePath) {
+                    return $basePath . DIRECTORY_SEPARATOR . $path;
+                },
+                $urls
+            )
         );
     }
 
@@ -247,6 +250,213 @@ class ClientScriptManager extends TClientScriptManager {
         return parent::registerHeadScriptFile($key, $file);
     }
 
+    // Stylesheets
+
+    private $_renderedSheetsInitialized = FALSE;
+
+    private function _getRenderedSheets() {
+        $sessionKey = $this->_getRenderedSheetsStoreKey();
+        if ($this->_page->IsCallBack || $this->_renderedSheetsInitialized) {
+            return $this->_getCache()->get($sessionKey) ?: [];
+        } else {
+            $this->_getCache()->delete($sessionKey);
+            $this->_renderedSheetsInitialized = TRUE;
+            return [];
+        }
+    }
+
+    private function _appendRenderedSheets(array $newSheets, $compiledFileKey) {
+        $sheets = $this->_getRenderedSheets();
+        if (!isset($sheets[$compiledFileKey])) {
+            $sheets[$compiledFileKey] = [];
+        }
+        $sheets[$compiledFileKey] = array_merge(
+            $sheets[$compiledFileKey],
+            $newSheets
+        );
+        $this->_getCache()->set(
+            $this->_getRenderedSheetsStoreKey(),
+            $sheets
+        );
+    }
+
+    private function _fixStyleSheetPaths($content, $originalUrl) {
+        $originalDir = dirname($originalUrl);
+        return preg_replace_callback(
+            '/url\s*\([\'"]?(.*?)[\'"]?\)/',
+            function($matches) use($originalDir) {
+                $url = parse_url($matches[1]);
+                if (isset($url['scheme']) || isset($url['host'])) {
+                    return $matches[0];
+                }
+                return str_replace(
+                    $matches[1],
+                    $originalDir . '/' . $matches[1],
+                    $matches[0]
+                );
+            },
+            $content
+        );
+    }
+
+    private function _compileSheetFiles($files) {
+        $paths = $this->_getBasePaths(
+            array_map('reset', $files)
+        );
+        $cacheKey = $this->_getFileCollectionCacheKey($paths);
+        $cacheFile = $this->_getCacheFilePath($cacheKey . '.css', 'styles');
+        $this->_appendRenderedSheets($files, $cacheFile);
+        if (!file_exists($cacheFile)
+            || (filemtime($cacheFile)
+                < $this->_getFileCollectionMTime($paths))) {
+            Prado::using('Lib.cssmin.CssMin');
+            $styleContent = implode(
+                PHP_EOL,
+                array_map(
+                    function($origPath, $path) {
+                        return trim(
+                            CssMin::minify(
+                                $this->_fixStyleSheetPaths(
+                                    file_get_contents($path),
+                                    $origPath
+                                )
+                            )
+                        );
+                    },
+                    array_keys($paths),
+                    $paths
+                )
+            );
+            file_put_contents($cacheFile, $styleContent);
+        }
+        return $this->Application->AssetManager->publishFilePath($cacheFile);
+    }
+
+    private function _determineLocalSheetFiles($files) {
+        $basePath = $this->_getBasePath();
+        return array_filter(
+            $files,
+            function($file) use($basePath) {
+                return file_exists($basePath . DIRECTORY_SEPARATOR . $file[0]);
+            }
+        );
+    }
+
+    private function _renderSheetFileTag(THtmlWriter $writer, $href, $media) {
+        $writer->addAttribute('rel', 'stylesheet');
+        $writer->addAttribute('type', 'text/css');
+        $writer->addAttribute('media', $media);
+        $writer->addAttribute('href', $href);
+        $writer->renderBeginTag('link');
+        $writer->write(PHP_EOL);
+    }
+
+    private function _renderLocalSheetFiles(THtmlWriter $writer, $localFiles) {
+        if ($localFiles) {
+            $fileTypes = [];
+            foreach ($localFiles as $file) {
+                $type = $file[1] ?: 'all';
+                if (!isset($fileTypes[$type])) {
+                    $fileTypes[$type] = [];
+                }
+                $fileTypes[$type][] = $file;
+            }
+            foreach ($fileTypes as $type => $files) {
+                $assetPath = $this->_compileSheetFiles($files);
+                $this->_renderSheetFileTag($writer, $assetPath, $type);
+            }
+        }
+    }
+
+    private function _renderExternalSheetFiles(THtmlWriter $writer, $externalFiles) {
+        if ($externalFiles) {
+            foreach ($externalFiles as $file) {
+                $this->_appendRenderedSheets([$file], $file[0]);
+                $this->_renderSheetFileTag($writer, $file[0], $file[1] ?: 'all');
+            }
+        }
+    }
+
+    public function renderStyleSheetFiles($writer) {
+        if ($this->getApplication()->getMode() !== TApplicationMode::Debug) {
+            $files = $this->_styles;
+            if ($files) {
+                $localFiles = $this->_determineLocalSheetFiles($files);
+                $this->_renderLocalSheetFiles($writer, $localFiles);
+                $externalFiles = array_diff_key($files, $localFiles);
+                $this->_renderExternalSheetFiles($writer, $externalFiles);
+            }
+        } else {
+            parent::renderStyleSheetFiles($writer);
+        }
+    }
+
+    private function _getRenderedSheetUrl($registeredSheet) {
+        $renderedSheets = $this->_getRenderedSheets();
+        foreach ($renderedSheets as $compiledFile => $sheets) {
+            foreach ($sheets as $sheet) {
+                if ($registeredSheet[0] == $sheet[0]) {
+                    if (file_exists($compiledFile)) {
+                        return $this->Application->AssetManager->getPublishedUrl(
+                            $compiledFile
+                        );
+                    } else {
+                        return $registeredSheet[0];
+                    }
+                }
+            }
+        }
+        return FALSE;
+    }
+
+    public function getStyleSheetUrls() {
+        if ($this->getApplication()->getMode() !== TApplicationMode::Debug) {
+            $registeredSheets = $this->_styles;
+            $sheetUrls = [];
+            $newSheets = [];
+            foreach ($registeredSheets as $registeredSheet) {
+                $renderedSheetUrl = $this->_getRenderedSheetUrl(
+                    $registeredSheet
+                );
+                if ($renderedSheetUrl) {
+                    $sheetUrls[] = $renderedSheetUrl;
+                } else {
+                    $newSheets[] = $registeredSheet;
+                }
+            }
+            $newLocalSheets = $this->_determineLocalSheetFiles($newSheets);
+            $newLocalUrls = array_map('reset', $newLocalSheets);
+            $newRemoteSheets = array_filter(
+                $newSheets,
+                function($sheet) use($newLocalUrls) {
+                    return !in_array($sheet[0], $newLocalUrls);
+                }
+            );
+            $newRemoteUrls = array_map('reset', $newRemoteSheets);
+            if ($newLocalSheets) {
+                $sheetUrls[] = $this->_compileSheetFiles($newLocalSheets);
+            }
+            $sheetUrls = array_values(
+                array_unique(array_merge($sheetUrls, $newRemoteUrls))
+            );
+            return $sheetUrls;
+        }
+        return parent::getStyleSheetUrls();
+    }
+
+    private $_styles = [];
+    private $_themeStyles = [];
+
+    public function registerStyleSheetFile($key, $file, $media = '') {
+        $this->_styles[$key] = [$file, $media];
+        return parent::registerStyleSheetFile($key, $file, $media);
+    }
+
+    public function registerThemeStyleSheetFile($key, $file, $media = '') {
+        $this->_themeStyles[$key] = $file;
+        return $this->registerStyleSheetFile($key, $file, $media);
+    }
+
 }
 
 ?>
-- 
cgit v1.2.3