From 25965b5d72667932262d45ec81912f83d75ed49f Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Tue, 3 May 2016 21:23:38 +0900 Subject: [PATCH 01/34] Initial support for LESS and SCSS stylesheets --- classes/file/FileHandler.class.php | 13 +- .../FrontEndFileHandler.class.php | 164 ++++++++++++------ classes/template/TemplateHandler.class.php | 2 + 3 files changed, 127 insertions(+), 52 deletions(-) diff --git a/classes/file/FileHandler.class.php b/classes/file/FileHandler.class.php index 20b6ead6e..e0f61a435 100644 --- a/classes/file/FileHandler.class.php +++ b/classes/file/FileHandler.class.php @@ -16,7 +16,18 @@ class FileHandler */ public static function getRealPath($source) { - return (strncmp($source, './', 2) === 0) ? (\RX_BASEDIR . substr($source, 2)) : $source; + if (strncmp($source, './', 2) === 0) + { + return \RX_BASEDIR . substr($source, 2); + } + elseif (strncmp($source, '/', 1) === 0) + { + return $source; + } + else + { + return \RX_BASEDIR . $source; + } } /** diff --git a/classes/frontendfile/FrontEndFileHandler.class.php b/classes/frontendfile/FrontEndFileHandler.class.php index 38758d4ef..bf609b193 100644 --- a/classes/frontendfile/FrontEndFileHandler.class.php +++ b/classes/frontendfile/FrontEndFileHandler.class.php @@ -87,7 +87,7 @@ class FrontEndFileHandler extends Handler * @param array $args Arguments * @return void * */ - function loadFile($args) + public function loadFile($args) { if(!is_array($args)) { @@ -113,7 +113,7 @@ class FrontEndFileHandler extends Handler $map = &$this->cssMap; $mapIndex = &$this->cssMapIndex; - $this->_arrangeCssIndex($pathInfo['dirname'], $file); + $this->_arrangeCssIndex($file->fileRealPath, $file); } else if($file->fileExtension == 'js') { @@ -131,7 +131,7 @@ class FrontEndFileHandler extends Handler if(!isset($mapIndex[$file->key]) || $mapIndex[$file->key] > $file->index) { - $this->unloadFile($args[0], $args[2], $args[1]); + //$this->unloadFile($args[0], $args[2], $args[1]); $map[$file->index][$file->key] = $file; $mapIndex[$file->key] = $file->index; } @@ -146,7 +146,7 @@ class FrontEndFileHandler extends Handler * @param bool $forceMinify Whether this file should be minified * @return stdClass The file information */ - private function getFileInfo($fileName, $targetIe = '', $media = 'all', $forceMinify = false) + protected function getFileInfo($fileName, $targetIe = '', $media = 'all', $forceMinify = false) { static $existsInfo = array(); @@ -165,6 +165,7 @@ class FrontEndFileHandler extends Handler $file->fileName = $pathInfo['basename']; $file->filePath = $this->_getAbsFileUrl($pathInfo['dirname']); $file->fileRealPath = FileHandler::getRealPath($pathInfo['dirname']); + $file->fileFullPath = $file->fileRealPath . '/' . $pathInfo['basename']; $file->fileExtension = strtolower($pathInfo['extension']); if(preg_match('/^(.+)\.min$/', $pathInfo['filename'], $matches)) { @@ -180,65 +181,46 @@ class FrontEndFileHandler extends Handler $file->isCachedScript = !$file->isExternalURL && strpos($file->filePath, 'files/cache/') !== false; $file->keyName = $file->fileNameNoExt . '.' . $file->fileExtension; $file->cdnPath = $this->_normalizeFilePath($pathInfo['dirname']); - $originalFilePath = $file->fileRealPath . '/' . $pathInfo['basename']; // Fix incorrectly minified URL - if($file->isMinified && !$file->isExternalURL && (!file_exists($originalFilePath) || is_link($originalFilePath) || - (filesize($originalFilePath) < 32 && trim(file_get_contents($originalFilePath)) === $file->keyName))) + if($file->isMinified && !$file->isExternalURL && (!file_exists($file->fileFullPath) || is_link($file->fileFullPath) || + (filesize($file->fileFullPath) < 32 && trim(file_get_contents($file->fileFullPath)) === $file->keyName))) { if(file_exists($file->fileRealPath . '/' . $file->fileNameNoExt . '.' . $file->fileExtension)) { $file->fileName = $file->fileNameNoExt . '.' . $file->fileExtension; $file->isMinified = false; - $originalFilePath = $file->fileRealPath . '/' . $file->fileNameNoExt . '.' . $file->fileExtension; + $file->fileFullPath = $file->fileRealPath . '/' . $file->fileNameNoExt . '.' . $file->fileExtension; } } // Decide whether to minify this file - if(self::$minify === 'all') + if ($file->isMinified || $file->isExternalURL || $file->isCachedScript || strpos($file->filePath, 'common/js/plugins') !== false || self::$minify === 'none') { - $minify_enabled = true; + $minify = false; } - elseif(self::$minify === 'none') + elseif (self::$minify === 'all') { - $minify_enabled = false; + $minify = true; } else { - $minify_enabled = $forceMinify; + $minify = $forceMinify; } - // Minify file - if($minify_enabled && !$file->isMinified && !$file->isExternalURL && !$file->isCachedScript && strpos($file->filePath, 'common/js/plugins') === false) + // Process according to file type + switch ($file->fileExtension) { - if(($file->fileExtension === 'css' || $file->fileExtension === 'js') && file_exists($originalFilePath)) - { - $minifiedFileName = $file->fileNameNoExt . '.min.' . $file->fileExtension; - $minifiedFileHash = ltrim(str_replace(array('/', '\\'), '.', $pathInfo['dirname']), '.'); - $minifiedFilePath = _XE_PATH_ . 'files/cache/minify/' . $minifiedFileHash . '.' . $minifiedFileName; - - if(!file_exists($minifiedFilePath) || filemtime($minifiedFilePath) < filemtime($originalFilePath)) - { - if($file->fileExtension === 'css') - { - $minifier = new MatthiasMullie\Minify\CSS($originalFilePath); - $content = $minifier->execute($minifiedFilePath); - } - else - { - $minifier = new MatthiasMullie\Minify\JS($originalFilePath); - $content = $minifier->execute($minifiedFilePath); - } - FileHandler::writeFile($minifiedFilePath, $content); - } - - $file->fileName = $minifiedFileHash . '.' . $minifiedFileName; - $file->filePath = $this->_getAbsFileUrl('./files/cache/minify'); - $file->fileRealPath = _XE_PATH_ . 'files/cache/minify'; - $file->keyName = $minifiedFileHash . '.' . $file->fileNameNoExt . '.' . $file->fileExtension; - $file->cdnPath = $this->_normalizeFilePath('./files/cache/minify'); - $file->isMinified = true; - } + case 'css': + case 'js': + $this->proc_CSS_JS($file, $minify); + break; + case 'less': + case 'scss': + $this->proc_LESS_SCSS($file, $minify); + break; + default: + break; } // Process targetIe and media attributes @@ -259,6 +241,82 @@ class FrontEndFileHandler extends Handler return $file; } + + /** + * Process CSS and JS file + * + * @param object $file + * @param bool $minify + * @return void + */ + protected function proc_CSS_JS($file, $minify) + { + if (!$minify || !file_exists($file->fileFullPath)) + { + return; + } + + $minifiedFileName = $file->fileNameNoExt . '.min.' . $file->fileExtension; + $minifiedFileHash = ltrim(str_replace(array('/', '\\'), '.', substr($file->fileRealPath, strlen(\RX_BASEDIR))), '.'); + $minifiedFilePath = \RX_BASEDIR . 'files/cache/minify/' . $minifiedFileHash . '.' . $minifiedFileName; + + if (!file_exists($minifiedFilePath) || filemtime($minifiedFilePath) < filemtime($file->fileFullPath)) + { + $method_name = 'minify' . $file->fileExtension; + $success = Rhymix\Framework\Formatter::$method_name($file->fileFullPath, $minifiedFilePath); + if ($success === false) + { + return; + } + } + + $file->fileName = $minifiedFileHash . '.' . $minifiedFileName; + $file->filePath = $this->_getAbsFileUrl('./files/cache/minify'); + $file->fileRealPath = \RX_BASEDIR . 'files/cache/minify'; + $file->fileFullPath = $minifiedFilePath; + $file->keyName = $minifiedFileHash . '.' . $file->fileNameNoExt . '.' . $file->fileExtension; + $file->cdnPath = $this->_normalizeFilePath('./files/cache/minify'); + $file->isMinified = true; + } + + /** + * Process LESS and SCSS file + * + * @param object $file + * @param bool $minify + * @param array $vars + * @return void + */ + protected function proc_LESS_SCSS($file, $minify, $vars = array()) + { + if (!file_exists($file->fileFullPath)) + { + return; + } + + $compiledFileName = $file->fileName . ($minify ? '.min' : '') . '.css'; + $compiledFileHash = ltrim(str_replace(array('/', '\\'), '.', substr($file->fileRealPath, strlen(\RX_BASEDIR))), '.'); + $compiledFilePath = \RX_BASEDIR . 'files/cache/minify/' . $compiledFileHash . '.' . $compiledFileName; + + if (!file_exists($compiledFilePath) || filemtime($compiledFilePath) < filemtime($file->fileFullPath)) + { + $method_name = 'compile' . $file->fileExtension; + $success = Rhymix\Framework\Formatter::$method_name($file->fileFullPath, $compiledFilePath, $vars, $minify); + if ($success === false) + { + return; + } + } + + $file->fileName = $compiledFileHash . '.' . $compiledFileName; + $file->filePath = $this->_getAbsFileUrl('./files/cache/minify'); + $file->fileRealPath = \RX_BASEDIR . 'files/cache/minify'; + $file->fileFullPath = $compiledFilePath; + $file->keyName = $compiledFileHash . '.' . $file->fileNameNoExt . '.' . $file->fileExtension; + $file->cdnPath = $this->_normalizeFilePath('./files/cache/minify'); + $file->isMinified = true; + $file->fileExtension = 'css'; + } /** * Unload front end file @@ -268,7 +326,7 @@ class FrontEndFileHandler extends Handler * @param string $media Media of file to unload. Only use when file is css. * @return void */ - function unloadFile($fileName, $targetIe = '', $media = 'all') + public function unloadFile($fileName, $targetIe = '', $media = 'all') { $file = $this->getFileInfo($fileName, $targetIe, $media); @@ -452,19 +510,23 @@ class FrontEndFileHandler extends Handler /** * Arrage css index * - * @param string $dirName First directory name of css path - * @param array $file file info. + * @param string $dirname + * @param object $file * @return void */ - function _arrangeCssIndex($dirName, &$file) + function _arrangeCssIndex($dirname, $file) { - if($file->index !== 0) + if ($file->index !== 0) { return; } - - $dirName = str_replace('./', '', $dirName); - $tmp = explode('/', $dirName); + + $dirname = substr($dirname, strlen(\RX_BASEDIR)); + if (strncmp($dirname, 'files/cache/minify/', 19) === 0) + { + $dirname = substr($dirname, 19); + } + $tmp = array_first(explode('/', strtr($dirname, '\\.', '//'))); $cssSortList = array('common' => -100000, 'layouts' => -90000, 'modules' => -80000, 'widgets' => -70000, 'addons' => -60000); $file->index = $cssSortList[$tmp[0]]; diff --git a/classes/template/TemplateHandler.class.php b/classes/template/TemplateHandler.class.php index 1d88f0b1c..103aea363 100644 --- a/classes/template/TemplateHandler.class.php +++ b/classes/template/TemplateHandler.class.php @@ -688,6 +688,8 @@ class TemplateHandler } break; case 'css': + case 'less': + case 'scss': if($doUnload) { $result = "Context::unloadFile('{$attr['target']}','{$attr['targetie']}','{$attr['media']}');"; From 7709eb97c619f7dbb18db35a3d9c75e00b53593c Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Tue, 3 May 2016 21:35:42 +0900 Subject: [PATCH 02/34] Support passing variables to LESS and SCSS stylesheets --- classes/frontendfile/FrontEndFileHandler.class.php | 8 +++++--- classes/template/TemplateHandler.class.php | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/classes/frontendfile/FrontEndFileHandler.class.php b/classes/frontendfile/FrontEndFileHandler.class.php index bf609b193..721d83968 100644 --- a/classes/frontendfile/FrontEndFileHandler.class.php +++ b/classes/frontendfile/FrontEndFileHandler.class.php @@ -82,6 +82,7 @@ class FrontEndFileHandler extends Handler * $args[1]: media * $args[2]: target IE * $args[3]: index + * $args[4]: vars for LESS and SCSS * * * @param array $args Arguments @@ -99,7 +100,7 @@ class FrontEndFileHandler extends Handler { return; } - $file = $this->getFileInfo($args[0], $args[2], $args[1], $isCommon); + $file = $this->getFileInfo($args[0], $args[2], $args[1], $args[4], $isCommon); $file->index = (int)$args[3]; $availableExtension = array('css' => 1, 'js' => 1); @@ -143,10 +144,11 @@ class FrontEndFileHandler extends Handler * @param string $fileName The file name * @param string $targetIe Target IE of file * @param string $media Media of file + * @param array $vars Variables for LESS and SCSS * @param bool $forceMinify Whether this file should be minified * @return stdClass The file information */ - protected function getFileInfo($fileName, $targetIe = '', $media = 'all', $forceMinify = false) + protected function getFileInfo($fileName, $targetIe = '', $media = 'all', $vars = array(), $forceMinify = false) { static $existsInfo = array(); @@ -217,7 +219,7 @@ class FrontEndFileHandler extends Handler break; case 'less': case 'scss': - $this->proc_LESS_SCSS($file, $minify); + $this->proc_LESS_SCSS($file, $minify, (array)$vars); break; default: break; diff --git a/classes/template/TemplateHandler.class.php b/classes/template/TemplateHandler.class.php index 103aea363..fc99576ea 100644 --- a/classes/template/TemplateHandler.class.php +++ b/classes/template/TemplateHandler.class.php @@ -697,7 +697,7 @@ class TemplateHandler else { $metafile = $attr['target']; - $result = "\$__tmp=array('{$attr['target']}','{$attr['media']}','{$attr['targetie']}','{$attr['index']}');Context::loadFile(\$__tmp);unset(\$__tmp);"; + $result = "\$__tmp=array('{$attr['target']}','{$attr['media']}','{$attr['targetie']}','{$attr['index']}'," . ($attr['vars'] ? self::_replaceVar($attr['vars']) : 'array()') . ");Context::loadFile(\$__tmp);unset(\$__tmp);"; } break; } From 99a86a072b9eb9d3cd17b0b6ab06c419f4f4dda5 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Tue, 3 May 2016 21:44:13 +0900 Subject: [PATCH 03/34] Clean up path handling in FrontEndFileHandler --- .../FrontEndFileHandler.class.php | 27 +++++-------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/classes/frontendfile/FrontEndFileHandler.class.php b/classes/frontendfile/FrontEndFileHandler.class.php index 721d83968..60286d88b 100644 --- a/classes/frontendfile/FrontEndFileHandler.class.php +++ b/classes/frontendfile/FrontEndFileHandler.class.php @@ -273,11 +273,11 @@ class FrontEndFileHandler extends Handler } $file->fileName = $minifiedFileHash . '.' . $minifiedFileName; - $file->filePath = $this->_getAbsFileUrl('./files/cache/minify'); + $file->filePath = \RX_BASEURL . 'files/cache/minify'; $file->fileRealPath = \RX_BASEDIR . 'files/cache/minify'; $file->fileFullPath = $minifiedFilePath; $file->keyName = $minifiedFileHash . '.' . $file->fileNameNoExt . '.' . $file->fileExtension; - $file->cdnPath = $this->_normalizeFilePath('./files/cache/minify'); + $file->cdnPath = './files/cache/minify'; $file->isMinified = true; } @@ -311,11 +311,11 @@ class FrontEndFileHandler extends Handler } $file->fileName = $compiledFileHash . '.' . $compiledFileName; - $file->filePath = $this->_getAbsFileUrl('./files/cache/minify'); + $file->filePath = \RX_BASEURL . 'files/cache/minify'; $file->fileRealPath = \RX_BASEDIR . 'files/cache/minify'; $file->fileFullPath = $compiledFilePath; $file->keyName = $compiledFileHash . '.' . $file->fileNameNoExt . '.' . $file->fileExtension; - $file->cdnPath = $this->_normalizeFilePath('./files/cache/minify'); + $file->cdnPath = './files/cache/minify'; $file->isMinified = true; $file->fileExtension = 'css'; } @@ -488,24 +488,11 @@ class FrontEndFileHandler extends Handler */ function _getAbsFileUrl($path) { - $path = $this->_normalizeFilePath($path); - - if(strpos($path, './') === 0) + $path = Rhymix\Framework\Filters\FilenameFilter::cleanPath($path); + if (!strncmp($path, \RX_BASEDIR, strlen(\RX_BASEDIR))) { - if(dirname($_SERVER['SCRIPT_NAME']) == '/' || dirname($_SERVER['SCRIPT_NAME']) == '\\') - { - $path = '/' . substr($path, 2); - } - else - { - $path = dirname($_SERVER['SCRIPT_NAME']) . '/' . substr($path, 2); - } + $path = \RX_BASEURL . substr($path, strlen(\RX_BASEDIR)); } - else if(strpos($file, '../') === 0) - { - $path = $this->_normalizeFilePath(dirname($_SERVER['SCRIPT_NAME']) . "/{$path}"); - } - return $path; } From b62bbef5159514ddfbe7d8b04af79f7c52d39c85 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Tue, 3 May 2016 21:48:52 +0900 Subject: [PATCH 04/34] Update TemplateHandlerTest to accommodate new syntax --- tests/unit/classes/TemplateHandlerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/classes/TemplateHandlerTest.php b/tests/unit/classes/TemplateHandlerTest.php index 028e6039a..34b2d33ea 100644 --- a/tests/unit/classes/TemplateHandlerTest.php +++ b/tests/unit/classes/TemplateHandlerTest.php @@ -110,7 +110,7 @@ class TemplateHandlerTest extends \Codeception\TestCase\Test // array( '', - '?>' + '?>' ), // array( From 5d23ac2f167d63d9e0872a3a8eff63d6c46f7afe Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Tue, 3 May 2016 21:57:37 +0900 Subject: [PATCH 05/34] Simplify path handling --- classes/frontendfile/FrontEndFileHandler.class.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/classes/frontendfile/FrontEndFileHandler.class.php b/classes/frontendfile/FrontEndFileHandler.class.php index 60286d88b..62b67d19b 100644 --- a/classes/frontendfile/FrontEndFileHandler.class.php +++ b/classes/frontendfile/FrontEndFileHandler.class.php @@ -395,9 +395,7 @@ class FrontEndFileHandler extends Handler { foreach($indexedMap as $file) { - $noneCache = (is_readable($file->cdnPath . '/' . $file->fileName)) ? '?' . date('YmdHis', filemtime($file->cdnPath . '/' . $file->fileName)) : ''; - $fullFilePath = $file->filePath . '/' . $file->fileName . $noneCache; - + $fullFilePath = $file->filePath . '/' . $file->fileName . '?' . date('YmdHis', filemtime($file->fileFullPath)); $result[] = array('file' => $fullFilePath, 'media' => $file->media, 'targetie' => $file->targetIe); } } @@ -431,9 +429,7 @@ class FrontEndFileHandler extends Handler { foreach($indexedMap as $file) { - $noneCache = (is_readable($file->cdnPath . '/' . $file->fileName)) ? '?' . date('YmdHis', filemtime($file->cdnPath . '/' . $file->fileName)) : ''; - $fullFilePath = $file->filePath . '/' . $file->fileName . $noneCache; - + $fullFilePath = $file->filePath . '/' . $file->fileName . '?' . date('YmdHis', filemtime($file->fileFullPath)); $result[] = array('file' => $fullFilePath, 'targetie' => $file->targetIe); } } From 753a70005a1b7ee37ace197ec95b0b416a425c0f Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Tue, 3 May 2016 22:07:43 +0900 Subject: [PATCH 06/34] Do not check mtime of remote or inaccessible scripts --- classes/frontendfile/FrontEndFileHandler.class.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/classes/frontendfile/FrontEndFileHandler.class.php b/classes/frontendfile/FrontEndFileHandler.class.php index 62b67d19b..fd1729db7 100644 --- a/classes/frontendfile/FrontEndFileHandler.class.php +++ b/classes/frontendfile/FrontEndFileHandler.class.php @@ -395,7 +395,11 @@ class FrontEndFileHandler extends Handler { foreach($indexedMap as $file) { - $fullFilePath = $file->filePath . '/' . $file->fileName . '?' . date('YmdHis', filemtime($file->fileFullPath)); + $fullFilePath = $file->filePath . '/' . $file->fileName; + if (!$file->isExternalURL && is_readable($file->fileFullPath)) + { + $fullFilePath .= '?' . date('YmdHis', filemtime($file->fileFullPath)); + } $result[] = array('file' => $fullFilePath, 'media' => $file->media, 'targetie' => $file->targetIe); } } @@ -429,7 +433,11 @@ class FrontEndFileHandler extends Handler { foreach($indexedMap as $file) { - $fullFilePath = $file->filePath . '/' . $file->fileName . '?' . date('YmdHis', filemtime($file->fileFullPath)); + $fullFilePath = $file->filePath . '/' . $file->fileName; + if (!$file->isExternalURL && is_readable($file->fileFullPath)) + { + $fullFilePath .= '?' . date('YmdHis', filemtime($file->fileFullPath)); + } $result[] = array('file' => $fullFilePath, 'targetie' => $file->targetIe); } } From 2a8c18eb01841ff5f7faecb3c2be2d59628c6457 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Wed, 4 May 2016 14:27:47 +0900 Subject: [PATCH 07/34] Use Formatter class to add multiple common scripts at the same time --- classes/display/HTMLDisplayHandler.php | 15 +++++------ .../FrontEndFileHandler.class.php | 15 +++-------- common/framework/formatter.php | 26 +++++++++++++++++-- 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/classes/display/HTMLDisplayHandler.php b/classes/display/HTMLDisplayHandler.php index cf2556c27..b44f1998a 100644 --- a/classes/display/HTMLDisplayHandler.php +++ b/classes/display/HTMLDisplayHandler.php @@ -434,13 +434,13 @@ class HTMLDisplayHandler Context::loadFile(array('./common/js/jquery-' . $jquery_version . '.min.js', 'head', '', -1730000), true); Context::loadFile(array('./common/js/plugins/jquery.migrate/jquery-migrate-1.2.1.min.js', 'head', '', -1720000), true); $concat_target_filename = 'files/cache/minify/xe.min.js'; - if(file_exists(_XE_PATH_ . $concat_target_filename)) + if(file_exists(\RX_BASEDIR . $concat_target_filename)) { - $concat_target_mtime = filemtime(_XE_PATH_ . $concat_target_filename); + $concat_target_mtime = filemtime(\RX_BASEDIR . $concat_target_filename); $original_mtime = 0; foreach($original_file_list as $filename) { - $original_mtime = max($original_mtime, filemtime(_XE_PATH_ . 'common/js/' . $filename . '.js')); + $original_mtime = max($original_mtime, filemtime(\RX_BASEDIR . 'common/js/' . $filename . '.js')); } if($concat_target_mtime > $original_mtime) { @@ -448,12 +448,9 @@ class HTMLDisplayHandler return; } } - $minifier = new MatthiasMullie\Minify\JS(); - foreach($original_file_list as $filename) - { - $minifier->add(_XE_PATH_ . 'common/js/' . $filename . '.js'); - } - FileHandler::writeFile(_XE_PATH_ . $concat_target_filename, $minifier->execute()); + Rhymix\Framework\Formatter::minifyJS(array_map(function($str) { + return \RX_BASEDIR . 'common/js/' . $str . '.js'; + }, $original_file_list), \RX_BASEDIR . $concat_target_filename); Context::loadFile(array('./' . $concat_target_filename, 'head', '', -100000), true); } } diff --git a/classes/frontendfile/FrontEndFileHandler.class.php b/classes/frontendfile/FrontEndFileHandler.class.php index fd1729db7..448b809ea 100644 --- a/classes/frontendfile/FrontEndFileHandler.class.php +++ b/classes/frontendfile/FrontEndFileHandler.class.php @@ -7,8 +7,9 @@ * */ class FrontEndFileHandler extends Handler { - - public static $isSSL = null; + /** + * Minification configuration. + */ public static $minify = null; /** @@ -55,15 +56,7 @@ class FrontEndFileHandler extends Handler */ function isSsl() { - if(!is_null(self::$isSSL)) - { - return self::$isSSL; - } - - $url_info = parse_url(Context::getRequestUrl()); - self::$isSSL = ($url_info['scheme'] == 'https'); - - return self::$isSSL; + return \RX_SSL; } /** diff --git a/common/framework/formatter.php b/common/framework/formatter.php index 6865a17c1..bfecc1a9d 100644 --- a/common/framework/formatter.php +++ b/common/framework/formatter.php @@ -251,7 +251,18 @@ class Formatter */ public static function minifyCSS($source_filename, $target_filename) { - $minifier = new \MatthiasMullie\Minify\CSS($source_filename); + $minifier = new \MatthiasMullie\Minify\CSS(); + if (is_array($source_filename)) + { + foreach ($source_filename as $filename) + { + $minifier->add($filename); + } + } + else + { + $minifier->add($source_filename); + } $content = $minifier->execute($target_filename); Storage::write($target_filename, $content); return strlen($content) ? true : false; @@ -266,7 +277,18 @@ class Formatter */ public static function minifyJS($source_filename, $target_filename) { - $minifier = new \MatthiasMullie\Minify\JS($source_filename); + $minifier = new \MatthiasMullie\Minify\JS(); + if (is_array($source_filename)) + { + foreach ($source_filename as $filename) + { + $minifier->add($filename); + } + } + else + { + $minifier->add($source_filename); + } $content = $minifier->execute($target_filename); Storage::write($target_filename, $content); return strlen($content) ? true : false; From e22558374fd1b5d2f47f29fe2aeb3b6bfc51021b Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Wed, 4 May 2016 17:19:17 +0900 Subject: [PATCH 08/34] Change Formatter class to allow direct access to CSS concatenation method --- common/framework/formatter.php | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/common/framework/formatter.php b/common/framework/formatter.php index bfecc1a9d..1e9c2857a 100644 --- a/common/framework/formatter.php +++ b/common/framework/formatter.php @@ -176,7 +176,7 @@ class Formatter public static function compileLESS($source_filename, $target_filename, $variables = array(), $minify = false) { // Get the cleaned and concatenated content. - $content = self::_concatenate($source_filename, $target_filename); + $content = self::concatCSS($source_filename, $target_filename); // Compile! try @@ -215,7 +215,7 @@ class Formatter public static function compileSCSS($source_filename, $target_filename, $variables = array(), $minify = false) { // Get the cleaned and concatenated content. - $content = self::_concatenate($source_filename, $target_filename); + $content = self::concatCSS($source_filename, $target_filename); // Compile! try @@ -301,7 +301,7 @@ class Formatter * @param string $target_filename * @return string */ - protected static function _concatenate($source_filename, $target_filename) + public static function concatCSS($source_filename, $target_filename) { $result = ''; @@ -311,6 +311,14 @@ class Formatter } foreach ($source_filename as $filename) { + if (is_array($filename) && count($filename) >= 2) + { + list($filename, $media) = $filename; + } + else + { + $media = null; + } $content = utf8_clean(file_get_contents($filename)); $path_converter = new \MatthiasMullie\PathConverter\Converter($filename, $target_filename); $content = preg_replace_callback('/\burl\\(([^)]+)\\)/iU', function($matches) use ($path_converter) { @@ -325,6 +333,10 @@ class Formatter } }, $content); unset($path_converter); + if ($media !== null) + { + $content = "@media $media {\n\n$content\n\n}"; + } $result .= trim($content) . "\n\n"; } From 90bce207943efc4efec670abf5afacf8177a03cc Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Wed, 4 May 2016 18:06:02 +0900 Subject: [PATCH 09/34] Add parser for IE conditional comments, to be used in JS concatenation --- common/framework/formatter.php | 102 ++++++++++++++++++++++++- tests/_data/formatter/scss.target1.css | 2 + tests/unit/framework/FormatterTest.php | 29 +++++++ 3 files changed, 132 insertions(+), 1 deletion(-) diff --git a/common/framework/formatter.php b/common/framework/formatter.php index 1e9c2857a..8bda5dbe0 100644 --- a/common/framework/formatter.php +++ b/common/framework/formatter.php @@ -337,9 +337,109 @@ class Formatter { $content = "@media $media {\n\n$content\n\n}"; } - $result .= trim($content) . "\n\n"; + $original_filename = starts_with(\RX_BASEDIR, $filename) ? substr($filename, strlen(\RX_BASEDIR)) : $filename; + $result .= '/* Original file: ' . $original_filename . ' */' . "\n\n" . trim($content) . "\n\n"; } return $result; } + + /** + * JS concatenation subroutine. + * + * @param string|array $source_filename + * @param string $target_filename + * @return string + */ + public static function concatJS($source_filename, $target_filename) + { + $result = ''; + + if (!is_array($source_filename)) + { + $source_filename = array($source_filename); + } + foreach ($source_filename as $filename) + { + if (is_array($filename) && count($filename) >= 2) + { + list($filename, $targetie) = $filename; + } + else + { + $targetie = null; + } + $content = utf8_clean(file_get_contents($filename)); + if ($targetie !== null) + { + $content = 'if (' . self::convertIECondition($targetie) . ') {' . "\n\n" . $content . "\n\n" . '}'; + } + $original_filename = starts_with(\RX_BASEDIR, $filename) ? substr($filename, strlen(\RX_BASEDIR)) : $filename; + $result .= '/* Original file: ' . $original_filename . ' */' . "\n\n" . trim($content) . "\n\n"; + } + + return $result; + } + + /** + * Convert IE conditional comments to JS conditions. + * + * @param string $condition + * @return string + */ + public static function convertIECondition($condition) + { + $conversions = array( + '/^true$/i' => 'true', + '/^false$/i' => 'false', + '/^IE$/i' => 'window.navigator.userAgent.match(/MSIE\s/)', + '/^IE\s*(\d+)$/i' => '(/MSIE (\d+)/.exec(window.navigator.userAgent) && /MSIE (\d+)/.exec(window.navigator.userAgent)[1] == %d)', + '/^gt IE\s*(\d+)$/i' => '(/MSIE (\d+)/.exec(window.navigator.userAgent) && /MSIE (\d+)/.exec(window.navigator.userAgent)[1] > %d)', + '/^gte IE\s*(\d+)$/i' => '(/MSIE (\d+)/.exec(window.navigator.userAgent) && /MSIE (\d+)/.exec(window.navigator.userAgent)[1] >= %d)', + '/^lt IE\s*(\d+)$/i' => '(/MSIE (\d+)/.exec(window.navigator.userAgent) && /MSIE (\d+)/.exec(window.navigator.userAgent)[1] < %d)', + '/^lte IE\s*(\d+)$/i' => '(/MSIE (\d+)/.exec(window.navigator.userAgent) && /MSIE (\d+)/.exec(window.navigator.userAgent)[1] <= %d)', + ); + + $result = array(); + $conditions = preg_split('/([\&\|])/', $condition, -1, \PREG_SPLIT_NO_EMPTY | \PREG_SPLIT_DELIM_CAPTURE); + foreach ($conditions as $condition) + { + $condition = trim(preg_replace('/[\(\)]/', '', $condition)); + if ($condition === '') + { + continue; + } + + if ($condition === '&' || $condition === '|') + { + $result[] = $condition . $condition; + continue; + } + + $negation = $condition[0] === '!'; + if ($negation) + { + $condition = trim(substr($condition, 1)); + } + + foreach ($conversions as $regexp => $replacement) + { + if (preg_match($regexp, $condition, $matches)) + { + if (count($matches) > 1) + { + array_shift($matches); + $result[] = ($negation ? '!' : '') . vsprintf($replacement, $matches); + } + else + { + $result[] = ($negation ? '!' : '') . $replacement; + } + break; + } + } + } + + return count($result) ? implode(' ', $result) : 'false'; + } } diff --git a/tests/_data/formatter/scss.target1.css b/tests/_data/formatter/scss.target1.css index 8e961b07d..2d78eacef 100644 --- a/tests/_data/formatter/scss.target1.css +++ b/tests/_data/formatter/scss.target1.css @@ -1,4 +1,6 @@ @charset "UTF-8"; +/* Original file: tests/_data/formatter/scss.source1.scss */ +/* Original file: tests/_data/formatter/scss.source2.scss */ .rhymix { color: #123456; background: url("../_data/formatter/foo/bar.jpg"); diff --git a/tests/unit/framework/FormatterTest.php b/tests/unit/framework/FormatterTest.php index b90d16fb4..8c034b6a4 100644 --- a/tests/unit/framework/FormatterTest.php +++ b/tests/unit/framework/FormatterTest.php @@ -131,4 +131,33 @@ class FormatterTest extends \Codeception\TestCase\Test unlink($test_target); } + + public function testConvertIECondition() + { + $this->assertEquals('window.navigator.userAgent.match(/MSIE\s/)', Rhymix\Framework\Formatter::convertIECondition('IE')); + $this->assertEquals('!window.navigator.userAgent.match(/MSIE\s/)', Rhymix\Framework\Formatter::convertIECondition('!IE')); + $this->assertEquals('!window.navigator.userAgent.match(/MSIE\s/)', Rhymix\Framework\Formatter::convertIECondition('!(IE)')); + $this->assertEquals('true && false', Rhymix\Framework\Formatter::convertIECondition('true&false')); + $this->assertEquals('false', Rhymix\Framework\Formatter::convertIECondition('gobbledygook')); + + $source = 'gt IE 7'; + $target = '(/MSIE (\d+)/.exec(window.navigator.userAgent) && /MSIE (\d+)/.exec(window.navigator.userAgent)[1] > 7)'; + $this->assertEquals($target, Rhymix\Framework\Formatter::convertIECondition($source)); + + $source = 'lte IE 8'; + $target = '(/MSIE (\d+)/.exec(window.navigator.userAgent) && /MSIE (\d+)/.exec(window.navigator.userAgent)[1] <= 8)'; + $this->assertEquals($target, Rhymix\Framework\Formatter::convertIECondition($source)); + + $source = '(gte IE 6) & (lt IE 8)'; + $target = '(/MSIE (\d+)/.exec(window.navigator.userAgent) && /MSIE (\d+)/.exec(window.navigator.userAgent)[1] >= 6) && (/MSIE (\d+)/.exec(window.navigator.userAgent) && /MSIE (\d+)/.exec(window.navigator.userAgent)[1] < 8)'; + $this->assertEquals($target, Rhymix\Framework\Formatter::convertIECondition($source)); + + $source = '!(gt IE 9)'; + $target = '!(/MSIE (\d+)/.exec(window.navigator.userAgent) && /MSIE (\d+)/.exec(window.navigator.userAgent)[1] > 9)'; + $this->assertEquals($target, Rhymix\Framework\Formatter::convertIECondition($source)); + + $source = '!lt IE 8|lt IE 6'; + $target = '!(/MSIE (\d+)/.exec(window.navigator.userAgent) && /MSIE (\d+)/.exec(window.navigator.userAgent)[1] < 8) || (/MSIE (\d+)/.exec(window.navigator.userAgent) && /MSIE (\d+)/.exec(window.navigator.userAgent)[1] < 6)'; + $this->assertEquals($target, Rhymix\Framework\Formatter::convertIECondition($source)); + } } From b15b39ece1a4948247a4416851041b67fd2451b8 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Wed, 4 May 2016 21:40:42 +0900 Subject: [PATCH 10/34] Initial implementation of CSS and JS concatenation --- .../FrontEndFileHandler.class.php | 228 +++++++++++++----- common/framework/formatter.php | 2 +- modules/admin/admin.admin.controller.php | 1 + modules/admin/admin.admin.view.php | 1 + modules/admin/lang/en.php | 6 + modules/admin/lang/ko.php | 8 +- modules/admin/tpl/config_advanced.html | 11 + 7 files changed, 193 insertions(+), 64 deletions(-) diff --git a/classes/frontendfile/FrontEndFileHandler.class.php b/classes/frontendfile/FrontEndFileHandler.class.php index 448b809ea..ac2d72a4e 100644 --- a/classes/frontendfile/FrontEndFileHandler.class.php +++ b/classes/frontendfile/FrontEndFileHandler.class.php @@ -141,15 +141,9 @@ class FrontEndFileHandler extends Handler * @param bool $forceMinify Whether this file should be minified * @return stdClass The file information */ - protected function getFileInfo($fileName, $targetIe = '', $media = 'all', $vars = array(), $forceMinify = false) + protected function getFileInfo($fileName, $targetIe = '', $media = 'all', $vars = array(), $isCommon = false) { static $existsInfo = array(); - - if(self::$minify === null) - { - self::$minify = config('view.minify_scripts') ?: 'common'; - } - if(isset($existsInfo[$existsKey])) { return $existsInfo[$existsKey]; @@ -162,7 +156,7 @@ class FrontEndFileHandler extends Handler $file->fileRealPath = FileHandler::getRealPath($pathInfo['dirname']); $file->fileFullPath = $file->fileRealPath . '/' . $pathInfo['basename']; $file->fileExtension = strtolower($pathInfo['extension']); - if(preg_match('/^(.+)\.min$/', $pathInfo['filename'], $matches)) + if (preg_match('/^(.+)\.min$/', $pathInfo['filename'], $matches)) { $file->fileNameNoExt = $matches[1]; $file->isMinified = true; @@ -174,8 +168,10 @@ class FrontEndFileHandler extends Handler } $file->isExternalURL = preg_match('@^(https?:)?//@i', $file->filePath) ? true : false; $file->isCachedScript = !$file->isExternalURL && strpos($file->filePath, 'files/cache/') !== false; + $file->isCommon = $isCommon; $file->keyName = $file->fileNameNoExt . '.' . $file->fileExtension; $file->cdnPath = $this->_normalizeFilePath($pathInfo['dirname']); + $file->vars = (array)$vars; // Fix incorrectly minified URL if($file->isMinified && !$file->isExternalURL && (!file_exists($file->fileFullPath) || is_link($file->fileFullPath) || @@ -188,36 +184,13 @@ class FrontEndFileHandler extends Handler $file->fileFullPath = $file->fileRealPath . '/' . $file->fileNameNoExt . '.' . $file->fileExtension; } } - - // Decide whether to minify this file - if ($file->isMinified || $file->isExternalURL || $file->isCachedScript || strpos($file->filePath, 'common/js/plugins') !== false || self::$minify === 'none') + + // Do not minify common JS plugins + if (strpos($file->filePath, 'common/js/plugins') !== false) { - $minify = false; - } - elseif (self::$minify === 'all') - { - $minify = true; - } - else - { - $minify = $forceMinify; + $file->isMinified = true; } - // Process according to file type - switch ($file->fileExtension) - { - case 'css': - case 'js': - $this->proc_CSS_JS($file, $minify); - break; - case 'less': - case 'scss': - $this->proc_LESS_SCSS($file, $minify, (array)$vars); - break; - default: - break; - } - // Process targetIe and media attributes $file->targetIe = $targetIe; if($file->fileExtension == 'css') @@ -279,10 +252,9 @@ class FrontEndFileHandler extends Handler * * @param object $file * @param bool $minify - * @param array $vars * @return void */ - protected function proc_LESS_SCSS($file, $minify, $vars = array()) + protected function proc_LESS_SCSS($file, $minify) { if (!file_exists($file->fileFullPath)) { @@ -296,7 +268,7 @@ class FrontEndFileHandler extends Handler if (!file_exists($compiledFilePath) || filemtime($compiledFilePath) < filemtime($file->fileFullPath)) { $method_name = 'compile' . $file->fileExtension; - $success = Rhymix\Framework\Formatter::$method_name($file->fileFullPath, $compiledFilePath, $vars, $minify); + $success = Rhymix\Framework\Formatter::$method_name($file->fileFullPath, $compiledFilePath, $file->vars, $minify); if ($success === false) { return; @@ -376,27 +348,81 @@ class FrontEndFileHandler extends Handler * * @return array Returns css file list. Array contains file, media, targetie. */ - function getCssFileList() + public function getCssFileList() { $map = &$this->cssMap; $mapIndex = &$this->cssMapIndex; - + $minify = self::$minify !== null ? self::$minify : (config('view.minify_scripts') ?: 'common'); + $concat = strpos(config('view.concat_scripts'), 'css') !== false; $this->_sortMap($map, $mapIndex); - - $result = array(); - foreach($map as $indexedMap) + + // Minify all scripts, and compile LESS/SCSS into CSS. + foreach ($map as $indexedMap) { - foreach($indexedMap as $file) + foreach ($indexedMap as $file) { - $fullFilePath = $file->filePath . '/' . $file->fileName; - if (!$file->isExternalURL && is_readable($file->fileFullPath)) + $minify_this_file = !$file->isMinified && !$file->isExternalURL && !$file->isCachedScript && (($file->isCommon && $minify !== 'none') || $minify === 'all'); + if ($file->fileExtension === 'css') { - $fullFilePath .= '?' . date('YmdHis', filemtime($file->fileFullPath)); + $this->proc_CSS_JS($file, $minify_this_file); + } + else + { + $this->proc_LESS_SCSS($file, $minify_this_file); + } + } + } + + // Add all files to the final result. + $result = array(); + if ($concat && count($concat_list = $this->_concatMap($map))) + { + foreach ($concat_list as $concat_fileset) + { + if (count($concat_fileset) === 1) + { + $file = reset($concat_fileset); + $url = $file->filePath . '/' . $file->fileName; + if (!$file->isExternalURL && is_readable($file->fileFullPath)) + { + $url .= '?' . date('YmdHis', filemtime($file->fileFullPath)); + } + $result[] = array('file' => $url, 'media' => $file->media, 'targetie' => $file->targetIe); + } + else + { + $concat_files = array(); + $concat_max_timestamp = 0; + foreach ($concat_fileset as $file) + { + $concat_files[] = $file->media === 'all' ? $file->fileFullPath : array($file->fileFullPath, $concat_file->media); + $concat_max_timestamp = max($concat_max_timestamp, filemtime($concat_file->fileFullPath)); + } + $concat_filename = 'files/cache/minify/concat.' . sha1(serialize($concat_files)) . '.css'; + if (!file_exists(\RX_BASEDIR . $concat_filename) || filemtime(\RX_BASEDIR . $concat_filename) < $concat_max_timestamp) + { + Rhymix\Framework\Storage::write(\RX_BASEDIR . $concat_filename, Rhymix\Framework\Formatter::concatCSS($concat_files, $concat_filename)); + } + $concat_filename .= '?' . date('YmdHis', filemtime(\RX_BASEDIR . $concat_filename)); + $result[] = array('file' => \RX_BASEURL . $concat_filename, 'media' => 'all', 'targetie' => ''); + } + } + } + else + { + foreach ($map as $indexedMap) + { + foreach ($indexedMap as $file) + { + $url = $file->filePath . '/' . $file->fileName; + if (!$file->isExternalURL && is_readable($file->fileFullPath)) + { + $url .= '?' . date('YmdHis', filemtime($file->fileFullPath)); + } + $result[] = array('file' => $url, 'media' => $file->media, 'targetie' => $file->targetIe); } - $result[] = array('file' => $fullFilePath, 'media' => $file->media, 'targetie' => $file->targetIe); } } - return $result; } @@ -406,7 +432,7 @@ class FrontEndFileHandler extends Handler * @param string $type Type of javascript. head, body * @return array Returns javascript file list. Array contains file, targetie. */ - function getJsFileList($type = 'head') + public function getJsFileList($type = 'head') { if($type == 'head') { @@ -418,26 +444,105 @@ class FrontEndFileHandler extends Handler $map = &$this->jsBodyMap; $mapIndex = &$this->jsBodyMapIndex; } - + + $minify = self::$minify !== null ? self::$minify : (config('view.minify_scripts') ?: 'common'); + $concat = strpos(config('view.concat_scripts'), 'js') !== false; $this->_sortMap($map, $mapIndex); - - $result = array(); - foreach($map as $indexedMap) + + // Minify all scripts. + foreach ($map as $indexedMap) { - foreach($indexedMap as $file) + foreach ($indexedMap as $file) { - $fullFilePath = $file->filePath . '/' . $file->fileName; - if (!$file->isExternalURL && is_readable($file->fileFullPath)) + if (!$file->isMinified && !$file->isExternalURL && !$file->isCachedScript && (($file->isCommon && $minify !== 'none') || $minify === 'all')) { - $fullFilePath .= '?' . date('YmdHis', filemtime($file->fileFullPath)); + $this->proc_CSS_JS($file, true); + } + } + } + + // Add all files to the final result. + $result = array(); + if ($concat && count($concat_list = $this->_concatMap($map))) + { + foreach ($concat_list as $concat_fileset) + { + if (count($concat_fileset) === 1) + { + $file = reset($concat_fileset); + $url = $file->filePath . '/' . $file->fileName; + if (!$file->isExternalURL && is_readable($file->fileFullPath)) + { + $url .= '?' . date('YmdHis', filemtime($file->fileFullPath)); + } + $result[] = array('file' => $url, 'targetie' => $file->targetIe); + } + else + { + $concat_files = array(); + $concat_max_timestamp = 0; + foreach ($concat_fileset as $file) + { + $concat_files[] = $file->targetIe ? $file->fileFullPath : array($file->fileFullPath, $concat_file->targetIe); + $concat_max_timestamp = max($concat_max_timestamp, filemtime($concat_file->fileFullPath)); + } + $concat_filename = 'files/cache/minify/concat.' . sha1(serialize($concat_files)) . '.js'; + if (!file_exists(\RX_BASEDIR . $concat_filename) || filemtime(\RX_BASEDIR . $concat_filename) < $concat_max_timestamp) + { + Rhymix\Framework\Storage::write(\RX_BASEDIR . $concat_filename, Rhymix\Framework\Formatter::concatJS($concat_files, $concat_filename)); + } + $concat_filename .= '?' . date('YmdHis', filemtime(\RX_BASEDIR . $concat_filename)); + $result[] = array('file' => \RX_BASEURL . $concat_filename, 'targetie' => ''); + } + } + } + else + { + foreach ($map as $indexedMap) + { + foreach ($indexedMap as $file) + { + $url = $file->filePath . '/' . $file->fileName; + if (!$file->isExternalURL && is_readable($file->fileFullPath)) + { + $url .= '?' . date('YmdHis', filemtime($file->fileFullPath)); + } + $result[] = array('file' => $url, 'targetie' => $file->targetIe); } - $result[] = array('file' => $fullFilePath, 'targetie' => $file->targetIe); } } - return $result; } - + + /** + * Create a concatenation map, skipping external URLs and unreadable scripts. + * + * @param array $map + * @return array + */ + protected function _concatMap(&$map) + { + $concat_list = array(); + $concat_key = 0; + foreach ($map as $indexedMap) + { + foreach ($indexedMap as $file) + { + if ($file->isExternalURL || ($file->fileExtension === 'css' && $file->targetIe) || !is_readable($file->fileFullPath)) + { + $concat_key++; + $concat_list[$concat_key][] = $file; + $concat_key++; + } + else + { + $concat_list[$concat_key][] = $file; + } + } + } + return $concat_list; + } + /** * Sort a map * @@ -517,7 +622,6 @@ class FrontEndFileHandler extends Handler $cssSortList = array('common' => -100000, 'layouts' => -90000, 'modules' => -80000, 'widgets' => -70000, 'addons' => -60000); $file->index = $cssSortList[$tmp[0]]; } - } /* End of file FrontEndFileHandler.class.php */ /* Location: ./classes/frontendfile/FrontEndFileHandler.class.php */ diff --git a/common/framework/formatter.php b/common/framework/formatter.php index 8bda5dbe0..38f3d5a44 100644 --- a/common/framework/formatter.php +++ b/common/framework/formatter.php @@ -375,7 +375,7 @@ class Formatter $content = 'if (' . self::convertIECondition($targetie) . ') {' . "\n\n" . $content . "\n\n" . '}'; } $original_filename = starts_with(\RX_BASEDIR, $filename) ? substr($filename, strlen(\RX_BASEDIR)) : $filename; - $result .= '/* Original file: ' . $original_filename . ' */' . "\n\n" . trim($content) . "\n\n"; + $result .= '/* Original file: ' . $original_filename . ' */' . "\n\n" . trim($content) . ";\n\n"; } return $result; diff --git a/modules/admin/admin.admin.controller.php b/modules/admin/admin.admin.controller.php index a0d287b68..f29cb7f40 100644 --- a/modules/admin/admin.admin.controller.php +++ b/modules/admin/admin.admin.controller.php @@ -699,6 +699,7 @@ class adminAdminController extends admin Rhymix\Framework\Config::set('session.delay', $vars->delay_session === 'Y'); Rhymix\Framework\Config::set('session.use_db', $vars->use_db_session === 'Y'); Rhymix\Framework\Config::set('view.minify_scripts', $vars->minify_scripts ?: 'common'); + Rhymix\Framework\Config::set('view.concat_scripts', $vars->concat_scripts ?: 'none'); Rhymix\Framework\Config::set('view.gzip', $vars->use_gzip === 'Y'); // Save diff --git a/modules/admin/admin.admin.view.php b/modules/admin/admin.admin.view.php index 7bc617e75..9542bfebc 100644 --- a/modules/admin/admin.admin.view.php +++ b/modules/admin/admin.admin.view.php @@ -530,6 +530,7 @@ class adminAdminView extends admin Context::set('delay_session', Rhymix\Framework\Config::get('session.delay')); Context::set('use_db_session', Rhymix\Framework\Config::get('session.use_db')); Context::set('minify_scripts', Rhymix\Framework\Config::get('view.minify_scripts')); + Context::set('concat_scripts', Rhymix\Framework\Config::get('view.concat_scripts')); Context::set('use_gzip', Rhymix\Framework\Config::get('view.gzip')); $this->setTemplateFile('config_advanced'); diff --git a/modules/admin/lang/en.php b/modules/admin/lang/en.php index 3e0edabf8..7a93fca6d 100644 --- a/modules/admin/lang/en.php +++ b/modules/admin/lang/en.php @@ -86,6 +86,12 @@ $lang->cmd_minify_all = 'All files'; $lang->cmd_minify_common = 'Common files only'; $lang->cmd_minify_none = 'None'; $lang->about_minify_scripts = 'Automatically minify all CSS and JS scripts in the Core and all modules.'; +$lang->concat_scripts = 'Concatenate scripts'; +$lang->cmd_concat_none = 'Do not concatenate'; +$lang->cmd_concat_css_only = 'Concatenate all CSS'; +$lang->cmd_concat_js_only = 'Concatenate all JS'; +$lang->cmd_concat_css_js = 'Concatenate both CSS and JS'; +$lang->about_concat_scripts = 'Automatically concatenate CSS and JS scripts into as few files as possible. External scripts are not concatenated.'; $lang->use_gzip = 'gzip Compression'; $lang->delay_session = 'Delay session start'; $lang->about_delay_session = 'To improve performance when using a caching proxy server such as Varnish, do not issue sessions to visitors until they log in.
Selecting this option may cause view counts and visitor counts to become inaccurate.'; diff --git a/modules/admin/lang/ko.php b/modules/admin/lang/ko.php index b115f00e9..4970668d2 100644 --- a/modules/admin/lang/ko.php +++ b/modules/admin/lang/ko.php @@ -85,7 +85,13 @@ $lang->minify_scripts = '스크립트 자동 압축'; $lang->cmd_minify_all = '모든 파일을 압축'; $lang->cmd_minify_common = '공통 파일만 압축'; $lang->cmd_minify_none = '압축하지 않음'; -$lang->about_minify_scripts = '코어와 모든 모듈에 포함된 CSS, JS 파일들을 자동으로 압축(minify)하여 전송합니다.'; +$lang->about_minify_scripts = 'CSS, JS 파일들을 자동으로 압축(minify)하여 전송합니다. 트래픽을 절약할 수 있습니다.'; +$lang->concat_scripts = '스크립트 합치기'; +$lang->cmd_concat_none = '합치지 않음'; +$lang->cmd_concat_css_only = 'CSS만 합침'; +$lang->cmd_concat_js_only = 'JS만 합침'; +$lang->cmd_concat_css_js = 'CSS와 JS를 모두 합침'; +$lang->about_concat_scripts = 'CSS, JS 파일들을 하나로 합쳐서 전송합니다. 외부에서 로딩하는 스크립트는 합쳐지지 않습니다.'; $lang->use_gzip = 'gzip 압축'; $lang->delay_session = '세션 시작 지연'; $lang->about_delay_session = 'Varnish 등의 프록시 캐싱 서버 사용시 성능 개선을 위해, 로그인하지 않은 사용자에게는 인증 세션을 부여하지 않습니다.
이 옵션을 선택할 경우 방문자 수 및 조회수 집계가 정확하게 이루어지지 않을 수 있습니다.'; diff --git a/modules/admin/tpl/config_advanced.html b/modules/admin/tpl/config_advanced.html index 2bc11806e..ee9ec8025 100644 --- a/modules/admin/tpl/config_advanced.html +++ b/modules/admin/tpl/config_advanced.html @@ -86,6 +86,17 @@

{$lang->about_minify_scripts}

+
+ +
+ + + + +
+

{$lang->about_concat_scripts}

+
+
From a6d1a3c93339e8afafa776883ad9e2909e288706 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Wed, 4 May 2016 21:42:23 +0900 Subject: [PATCH 11/34] Add scope to all members of FrontEndFileHandler --- .../FrontEndFileHandler.class.php | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/classes/frontendfile/FrontEndFileHandler.class.php b/classes/frontendfile/FrontEndFileHandler.class.php index ac2d72a4e..af9175b51 100644 --- a/classes/frontendfile/FrontEndFileHandler.class.php +++ b/classes/frontendfile/FrontEndFileHandler.class.php @@ -16,37 +16,37 @@ class FrontEndFileHandler extends Handler * Map for css * @var array */ - var $cssMap = array(); + public $cssMap = array(); /** * Map for Javascript at head * @var array */ - var $jsHeadMap = array(); + public $jsHeadMap = array(); /** * Map for Javascript at body * @var array */ - var $jsBodyMap = array(); + public $jsBodyMap = array(); /** * Index for css * @var array */ - var $cssMapIndex = array(); + public $cssMapIndex = array(); /** * Index for javascript at head * @var array */ - var $jsHeadMapIndex = array(); + public $jsHeadMapIndex = array(); /** * Index for javascript at body * @var array */ - var $jsBodyMapIndex = array(); + public $jsBodyMapIndex = array(); /** * Check SSL @@ -54,7 +54,7 @@ class FrontEndFileHandler extends Handler * @return bool If using ssl returns true, otherwise returns false. * @deprecated */ - function isSsl() + public function isSsl() { return \RX_SSL; } @@ -326,7 +326,7 @@ class FrontEndFileHandler extends Handler * @param string $type Type to unload. all, css, js * @return void */ - function unloadAllFiles($type = 'all') + public function unloadAllFiles($type = 'all') { if($type == 'css' || $type == 'all') { @@ -550,7 +550,7 @@ class FrontEndFileHandler extends Handler * @param array $index Not used * @return void */ - function _sortMap(&$map, &$index) + protected function _sortMap(&$map, &$index) { ksort($map); } @@ -561,7 +561,7 @@ class FrontEndFileHandler extends Handler * @param string $path Path to normalize * @return string Normalized path */ - function _normalizeFilePath($path) + protected function _normalizeFilePath($path) { if(strpos($path, '://') === FALSE && $path{0} != '/' && $path{0} != '.') { @@ -588,7 +588,7 @@ class FrontEndFileHandler extends Handler * @param string $path Path to get absolute url * @return string Absolute url */ - function _getAbsFileUrl($path) + protected function _getAbsFileUrl($path) { $path = Rhymix\Framework\Filters\FilenameFilter::cleanPath($path); if (!strncmp($path, \RX_BASEDIR, strlen(\RX_BASEDIR))) @@ -605,7 +605,7 @@ class FrontEndFileHandler extends Handler * @param object $file * @return void */ - function _arrangeCssIndex($dirname, $file) + protected function _arrangeCssIndex($dirname, $file) { if ($file->index !== 0) { From ad86e03c5e6db4be1eeab242b5242257ba11a4b7 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Wed, 4 May 2016 21:52:08 +0900 Subject: [PATCH 12/34] Fix inconsistent variable names --- classes/frontendfile/FrontEndFileHandler.class.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/classes/frontendfile/FrontEndFileHandler.class.php b/classes/frontendfile/FrontEndFileHandler.class.php index af9175b51..8bef8b58b 100644 --- a/classes/frontendfile/FrontEndFileHandler.class.php +++ b/classes/frontendfile/FrontEndFileHandler.class.php @@ -395,8 +395,8 @@ class FrontEndFileHandler extends Handler $concat_max_timestamp = 0; foreach ($concat_fileset as $file) { - $concat_files[] = $file->media === 'all' ? $file->fileFullPath : array($file->fileFullPath, $concat_file->media); - $concat_max_timestamp = max($concat_max_timestamp, filemtime($concat_file->fileFullPath)); + $concat_files[] = $file->media === 'all' ? $file->fileFullPath : array($file->fileFullPath, $file->media); + $concat_max_timestamp = max($concat_max_timestamp, filemtime($file->fileFullPath)); } $concat_filename = 'files/cache/minify/concat.' . sha1(serialize($concat_files)) . '.css'; if (!file_exists(\RX_BASEDIR . $concat_filename) || filemtime(\RX_BASEDIR . $concat_filename) < $concat_max_timestamp) @@ -483,8 +483,8 @@ class FrontEndFileHandler extends Handler $concat_max_timestamp = 0; foreach ($concat_fileset as $file) { - $concat_files[] = $file->targetIe ? $file->fileFullPath : array($file->fileFullPath, $concat_file->targetIe); - $concat_max_timestamp = max($concat_max_timestamp, filemtime($concat_file->fileFullPath)); + $concat_files[] = $file->targetIe ? array($file->fileFullPath, $file->targetIe) : $file->fileFullPath; + $concat_max_timestamp = max($concat_max_timestamp, filemtime($file->fileFullPath)); } $concat_filename = 'files/cache/minify/concat.' . sha1(serialize($concat_files)) . '.js'; if (!file_exists(\RX_BASEDIR . $concat_filename) || filemtime(\RX_BASEDIR . $concat_filename) < $concat_max_timestamp) From 9a30c10ce803dbb92516a16aadb7d17c88ceb56d Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Wed, 4 May 2016 21:57:48 +0900 Subject: [PATCH 13/34] Disable CSS/JS concatenation during legacy unit tests --- classes/frontendfile/FrontEndFileHandler.class.php | 7 ++++--- tests/unit/classes/FrontEndFileHandlerTest.php | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/classes/frontendfile/FrontEndFileHandler.class.php b/classes/frontendfile/FrontEndFileHandler.class.php index 8bef8b58b..b58a80284 100644 --- a/classes/frontendfile/FrontEndFileHandler.class.php +++ b/classes/frontendfile/FrontEndFileHandler.class.php @@ -8,9 +8,10 @@ class FrontEndFileHandler extends Handler { /** - * Minification configuration. + * Minification and concatenation configuration. */ public static $minify = null; + public static $concat = null; /** * Map for css @@ -353,7 +354,7 @@ class FrontEndFileHandler extends Handler $map = &$this->cssMap; $mapIndex = &$this->cssMapIndex; $minify = self::$minify !== null ? self::$minify : (config('view.minify_scripts') ?: 'common'); - $concat = strpos(config('view.concat_scripts'), 'css') !== false; + $concat = strpos(self::$concat !== null ? self::$concat : config('view.concat_scripts'), 'css') !== false; $this->_sortMap($map, $mapIndex); // Minify all scripts, and compile LESS/SCSS into CSS. @@ -446,7 +447,7 @@ class FrontEndFileHandler extends Handler } $minify = self::$minify !== null ? self::$minify : (config('view.minify_scripts') ?: 'common'); - $concat = strpos(config('view.concat_scripts'), 'js') !== false; + $concat = strpos(self::$concat !== null ? self::$concat : config('view.concat_scripts'), 'js') !== false; $this->_sortMap($map, $mapIndex); // Minify all scripts. diff --git a/tests/unit/classes/FrontEndFileHandlerTest.php b/tests/unit/classes/FrontEndFileHandlerTest.php index dfa45ec0c..bb2923020 100644 --- a/tests/unit/classes/FrontEndFileHandlerTest.php +++ b/tests/unit/classes/FrontEndFileHandlerTest.php @@ -15,6 +15,7 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test HTMLDisplayHandler::$reservedCSS = '/xxx$/'; HTMLDisplayHandler::$reservedJS = '/xxx$/'; FrontEndFileHandler::$minify = 'none'; + FrontEndFileHandler::$concat = 'none'; $this->specify("js(head)", function() { $handler = new FrontEndFileHandler(); From aa1af352b2a55659cadb3a24191b3c4bd63e0607 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Wed, 4 May 2016 22:07:49 +0900 Subject: [PATCH 14/34] Do not add @charset if it already exists --- common/framework/formatter.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/common/framework/formatter.php b/common/framework/formatter.php index 38f3d5a44..d0f402b79 100644 --- a/common/framework/formatter.php +++ b/common/framework/formatter.php @@ -189,7 +189,8 @@ class Formatter $less_compiler->setVariables($variables); } - $content = '@charset "UTF-8";' . "\n" . $less_compiler->compile($content) . "\n"; + $charset = strpos($content, '@charset') === false ? ('@charset "UTF-8";' . "\n") : ''; + $content = $charset . $less_compiler->compile($content) . "\n"; $result = true; } catch (\Exception $e) @@ -228,7 +229,8 @@ class Formatter $scss_compiler->setVariables($variables); } - $content = '@charset "UTF-8";' . "\n" . $scss_compiler->compile($content) . "\n"; + $charset = strpos($content, '@charset') === false ? ('@charset "UTF-8";' . "\n") : ''; + $content = $charset . $scss_compiler->compile($content) . "\n"; $result = true; } catch (\Exception $e) From 15c7dd58322eb6425397899c14581d2982babc1c Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Wed, 4 May 2016 22:08:13 +0900 Subject: [PATCH 15/34] Add missing extensions to CSS-specific tasks --- classes/frontendfile/FrontEndFileHandler.class.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/classes/frontendfile/FrontEndFileHandler.class.php b/classes/frontendfile/FrontEndFileHandler.class.php index b58a80284..bffeab024 100644 --- a/classes/frontendfile/FrontEndFileHandler.class.php +++ b/classes/frontendfile/FrontEndFileHandler.class.php @@ -97,13 +97,13 @@ class FrontEndFileHandler extends Handler $file = $this->getFileInfo($args[0], $args[2], $args[1], $args[4], $isCommon); $file->index = (int)$args[3]; - $availableExtension = array('css' => 1, 'js' => 1); + $availableExtension = array('css' => 1, 'js' => 1, 'less' => 1, 'scss' => 1); if(!isset($availableExtension[$file->fileExtension])) { return; } - if($file->fileExtension == 'css') + if($file->fileExtension == 'css' || $file->fileExtension == 'less' || $file->fileExtension == 'scss') { $map = &$this->cssMap; $mapIndex = &$this->cssMapIndex; @@ -194,7 +194,7 @@ class FrontEndFileHandler extends Handler // Process targetIe and media attributes $file->targetIe = $targetIe; - if($file->fileExtension == 'css') + if($file->fileExtension == 'css' || $file->fileExtension == 'less' || $file->fileExtension == 'scss') { $file->media = $media; if(!$file->media) From 74d5f4619518b7de54ad77c950cc6b79b7a3f3a3 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Wed, 4 May 2016 22:23:41 +0900 Subject: [PATCH 16/34] Convert xe.css into SCSS for easy maintenance --- classes/display/HTMLDisplayHandler.php | 2 +- common/css/rhymix.scss | 402 +++++++++++++++++++++++ common/css/xe.css | 432 ------------------------- 3 files changed, 403 insertions(+), 433 deletions(-) create mode 100644 common/css/rhymix.scss delete mode 100644 common/css/xe.css diff --git a/classes/display/HTMLDisplayHandler.php b/classes/display/HTMLDisplayHandler.php index b44f1998a..212ded171 100644 --- a/classes/display/HTMLDisplayHandler.php +++ b/classes/display/HTMLDisplayHandler.php @@ -416,7 +416,7 @@ class HTMLDisplayHandler */ private function _loadCommonJSCSS() { - Context::loadFile(array('./common/css/xe.css', '', '', -1600000), true); + Context::loadFile(array('./common/css/rhymix.scss', '', '', -1600000), true); $original_file_list = array('x', 'common', 'js_app', 'xml_handler', 'xml_js_filter'); $jquery_version = preg_match('/MSIE [5-8]\./', $_SERVER['HTTP_USER_AGENT']) ? '1.11.3' : '2.1.4'; diff --git a/common/css/rhymix.scss b/common/css/rhymix.scss new file mode 100644 index 000000000..3cbf9b11c --- /dev/null +++ b/common/css/rhymix.scss @@ -0,0 +1,402 @@ +@charset "UTF-8"; + +/* Element Reset */ +body, table, input, textarea, select, button { + font-family: sans-serif; + font-size: 12px; +} +article, aside, details, figcaption, figure, footer, header, hgroup, nav, section { + display: block; +} +body { + position: relative; +} +a img { + border: 0; +} +[hidden] { + display: none; +} +.xe_content img { + max-width: 100%; + height: auto; +} +@media \0screen { + img { + max-width: none; + } +} +.xe-clearfix:before, +.xe-clearfix:after { + content: " "; + display: table; +} +.xe-clearfix:after { + clear: both; +} +.xe-clearfix { + zoom: 1; +} +.xe-widget-wrapper { + overflow: hidden; +} + +/* Popup Menu Area */ +#popup_menu_area { + position: absolute; + z-index: 9999; + margin: 10px 0; + padding: 0; + border: 1px solid #eeeeee; + border-radius: 2px; + font-size: 12px; + box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23); + background: #fff; + min-width:80px; + ul { + margin: 0; + padding: 0; + list-style: none; + } + li { + margin: 0; + padding: 0; + line-height: 1.5; + } + a { + display: block; + padding: 5px; + text-decoration: none; + color: #212121; + &:hover, &:active, &:focus { + background: #eeeeee; + outline: none; + } + } +} +@media screen and (max-width: 400px) { + #popup_menu_area { + min-width:120px; + max-width:95%; + font-size: 13px; + a { + display: block; + padding: 10px; + text-decoration: none; + color: #212121; + } + } +} + +/* Message */ +.message { + position: relative; + margin: 1em 0; + padding: 0 1em; + border: 1px solid #ddd; + border-radius: 4px; + line-height: 1.4; + font-size: 13px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + background-color: #f8f8f8; + p { + margin: 1em 0 !important; + } + &.info { + border-color: #BCE8F1; + color: #3A87AD; + background-color: #D9EDF7; + } + &.error { + border-color: #EED3D7; + color: #B94A48; + background-color: #F2DEDE; + } + &.update { + border-color: #D6E9C6; + color: #468847; + background-color: #DFF0D8; + } +} +body > .message { + margin: 1em; +} + +/* Waiting for server response */ +.wfsr { + z-index: 100; + display: none; + position: fixed; + left: 0; + top: 0; + right: 0; + margin: 0; + padding: 20px 0 0 0; + border-bottom: 1px solid #ccc; + text-align: center; + font: bold 16px/60px "Helvetica Neue", Helvetica, Arial, 돋움, Dotum, sans-serif; + color: #fff; + opacity: .8; + filter: alpha(opacity=80); + box-shadow: 0 0 5px #000; + background: #333 url("../../common/img/msg.loading.gif") no-repeat center 15px; +} + +/* Button */ +.btnArea { + clear: both; + margin: 10px 0; + padding: 0; + text-align: right; + zoom: 1; + &:after { + clear: both; + display: block; + content: ""; + } +} +.btn { + display: inline-block; + *display: inline; + margin: 0; + padding: 0 12px !important; + height: 24px !important; + overflow: visible; + border: 1px solid #bbbbbb; + border-color: #e6e6e6 #e6e6e6 #bfbfbf; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + border-bottom-color: #a2a2a2; + border-radius: 2px; + text-decoration: none !important; + text-align: center; + text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); + vertical-align: top; + line-height: 24px !important; + font-family: inherit; + font-size: 12px; + color: #333333; + *zoom: 1; + cursor: pointer; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + background-color: #f5f5f5; + *background-color: #e6e6e6; + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -webkit-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(top, #ffffff, #e6e6e6); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0); + filter: progid: DXImageTransform.Microsoft.gradient(enabled=false); + &:hover, &:active, &[disabled] { + color: #333; + background-color: #e6e6e6; + *background-color: #d9d9d9; + } + >a, >button, >input, >span { + display: inline-block; + *zoom: 1; + margin: 0 -12px !important; + padding: 0 12px !important; + overflow: visible; + width: auto; + height: 24px; + border: 0; + vertical-align: top; + text-decoration: none !important; + line-height: 24px; + font-family: inherit; + font-size: 12px; + color: #333; + cursor: pointer; + background: none; + } +} +input.btn, button.btn { + height: 26px !important; +} +.btn-group { + position: relative; + display: inline-block; + *display: inline; + *margin-left: .3em; + white-space: nowrap; + vertical-align: middle; + font-size: 0; + *zoom: 1; + &:first-child { + *margin-left: 0; + } + &+.btn-group { + margin-left: 5px; + } + >.btn { + position: relative; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; + &+.btn { + margin-left: -1px; + } + &:first-child { + margin-left: 0; + -webkit-border-bottom-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; + border-bottom-left-radius: 4px; + -webkit-border-top-left-radius: 4px; + -moz-border-radius-topleft: 4px; + border-top-left-radius: 4px; + } + &:last-child { + -webkit-border-top-right-radius: 4px; + -moz-border-radius-topright: 4px; + border-top-right-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + border-bottom-right-radius: 4px; + } + &:hover, &:focus, &:active, &.active { + z-index: 2; + } + } +} + +/* Debug */ +#rhymix_debug_button { + display: none; + position: fixed; + left: 0; bottom: 40px; + background: #eeeeee; + background: linear-gradient(to bottom, #f4f4f4 0%, #eaeaea 100%); + border: 1px solid #ccc; border-left: 0; + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; + box-shadow: 0 0 3px 0 rgba(0, 0, 0, 0.18), 0 0 6px 0 rgba(0, 0, 0, 0.12); + z-index: 1073741824; + &:hover { + background: #dddddd; + background: linear-gradient(to bottom, #e8e8e8 0%, #d9d9d9 100%); + } + a { + display: block; + font: bold 12px/14px Arial, sans-serif; + color: #444; + text-decoration: none; + padding: 4px 8px; + &.has_errors { + color: #f44336; + } + } +} +#rhymix_debug_panel { + display: none; + position: fixed; + left: 0; top: 0; + max-width: 100%; + height: 100%; + overflow-y: scroll; + background: #fcfcfc; + box-sizing: border-box; + border-right: 1px solid #ccc; + box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.18), 0 0 8px 0 rgba(0, 0, 0, 0.12); + z-index: 1073741824; + .debug_header { + clear: both; + width: 100%; + height: 36px; + background: #444444; + background: linear-gradient(to right, #222222 0%, #444444 40%, #eeeeee 100%); + position: relative; + h2 { + font: bold 16px/20px Arial, sans-serif; + color: #fcfcfc; + position: absolute; + left: 10px; top: 10px; + margin: 0; padding: 0; + } + .debug_maximize { + font: normal 20px/24px Arial, sans-serif; + text-decoration: none; + color: #444444; + position: absolute; + right: 32px; top: 6px; + } + .debug_close { + font: normal 28px/28px Arial, sans-serif; + text-decoration: none; + color: #444444; + position: absolute; + right: 10px; top: 4px; + &:hover { + color: #f44336; + } + } + } + .debug_page { + clear: both; + margin: 12px 10px; + font: normal 12px/16px Arial, NanumBarunGothic, NanumGothic, "Malgun Gothic", sans-serif; + .debug_page_header { + padding-bottom: 8px; + border-bottom: 1px solid #ddd; + position: relative; + cursor: pointer; + h3 { + color: #444; + font: inherit; + font-size: 14px; + font-weight: bold; + margin: 0; + padding: 0; + } + } + .debug_page_collapse { + display: block; + position: absolute; + right: 0; top: 0; + color: #999; + font-size: 10px; + line-height: 12px; + text-decoration: none; + padding: 2px 2px; + } + .debug_page_body { + margin: 8px 4px 8px 10px; + h4 { + color: #444; + font: inherit; + font-size: 13px; + font-weight: bold; + margin: 0 0 8px 0; + padding: 0; + } + } + .debug_entry { + font-family: Consolas, "Courier New", monospace; + color: #444; + margin-left: 38px; + margin-bottom: 8px; + text-indent: -28px; + word-wrap: break-word; + word-break: break-all; + &.pre_wrap { + white-space: pre-wrap; + } + ul.debug_metadata { + margin: 0 0 0 -16px; padding: 0; + li { + list-style: disc; + margin: 0; padding: 0; text-indent: 0; + } + } + ul.debug_backtrace { + margin: 4px 0 0 16px; padding: 0; + li { + list-style: disc; + margin: 0; padding: 0; text-indent: 0; + color: #888; + } + } + } + } +} diff --git a/common/css/xe.css b/common/css/xe.css deleted file mode 100644 index 2db142d54..000000000 --- a/common/css/xe.css +++ /dev/null @@ -1,432 +0,0 @@ -@charset "utf-8"; -/*! Copyright (C) NAVER */ -/* @author NAVER */ -/*csslint unqualified-attributes: false, display-property-grouping: false*/ - -/* Element Reset */ -body, -table, -input, -textarea, -select, -button { - font-family: Tahoma, Geneva, sans-serif; - font-size: 12px; -} -article, -aside, -details, -figcaption, -figure, -footer, -header, -hgroup, -nav, -section { - display: block; -} -body { - position: relative; - word-wrap: break-word; -} -a img { - border: 0; -} -[hidden] { - display: none; -} -.xe_content img { - max-width: 100%; - height: auto; -} -@media \0screen { - img { - max-width: none; - } -} -.xe-clearfix:before, -.xe-clearfix:after { - content: " "; - display: table; -} -.xe-clearfix:after { - clear: both; -} -.xe-clearfix { - zoom: 1; -} - -.xe-widget-wrapper { - overflow: hidden; -} - -/* Popup Menu Area */ -#popup_menu_area { - position: absolute; - z-index:9999; - margin: 10px 0; - padding: 0; - border: 1px solid #eeeeee; - border-radius: 2px; - font-size: 12px; - box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23); - background: #fff; - min-width:80px; -} -#popup_menu_area ul { - margin: 0; - padding: 0; - list-style: none; -} -#popup_menu_area li { - margin: 0; - padding: 0; - line-height: 1.5; -} -#popup_menu_area a { - display: block; - padding: 5px; - text-decoration: none; - color: #212121; -} -#popup_menu_area a:hover, -#popup_menu_area a:active, -#popup_menu_area a:focus { - background: #eeeeee; - outline:none; -} -@media screen and (max-width: 400px) { - #popup_menu_area { - min-width:120px; - max-width:95%; - font-size: 13px; - } - #popup_menu_area a { - display: block; - padding: 10px; - text-decoration: none; - color: #212121; - } -} - -/* Message */ -.message { - position: relative; - margin: 1em 0; - padding: 0 1em; - border: 1px solid #ddd; - border-radius: 4px; - line-height: 1.4; - font-size: 13px; - text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); - background-color: #f8f8f8; -} -body>.message { - margin: 1em; -} -.message p { - margin: 1em 0 !important; -} -.message.info { - border-color: #BCE8F1; - color: #3A87AD; - background-color: #D9EDF7; -} -.message.error { - border-color: #EED3D7; - color: #B94A48; - background-color: #F2DEDE; -} -.message.update { - border-color: #D6E9C6; - color: #468847; - background-color: #DFF0D8; -} - -/* Waiting for server response */ -.wfsr { - z-index: 100; - display: none; - position: fixed; - left: 0; - top: 0; - right: 0; - margin: 0; - padding: 20px 0 0 0; - border-bottom: 1px solid #ccc; - text-align: center; - font: bold 16px/60px "Helvetica Neue", Helvetica, Arial, 돋움, Dotum, sans-serif; - color: #fff; - opacity: .8; - filter: alpha(opacity=80); - box-shadow: 0 0 5px #000; - background: #333 url("../../common/img/msg.loading.gif") no-repeat center 15px; -} - -/* Button */ -.btnArea { - clear: both; - margin: 10px 0; - padding: 0; - text-align: right; - zoom: 1; -} -.btnArea:after { - clear: both; - display: block; - content: ""; -} -.btn { - display: inline-block; - *display: inline; - margin: 0; - padding: 0 12px !important; - height: 24px !important; - overflow: visible; - border: 1px solid #bbbbbb; - border-color: #e6e6e6 #e6e6e6 #bfbfbf; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - border-bottom-color: #a2a2a2; - border-radius: 2px; - text-decoration: none !important; - text-align: center; - text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); - vertical-align: top; - line-height: 24px !important; - font-family: inherit; - font-size: 12px; - color: #333333; - *zoom: 1; - cursor: pointer; - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); - background-color: #f5f5f5; - *background-color: #e6e6e6; - background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); - background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); - background-image: -webkit-gradient(top, #ffffff, #e6e6e6); - background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); - background-image: linear-gradient(top, #ffffff, #e6e6e6); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0); - filter: progid: DXImageTransform.Microsoft.gradient(enabled=false); -} -input.btn, -button.btn { - height: 26px !important; -} -.btn:hover, -.btn:active, -.btn[disabled] { - color: #333; - background-color: #e6e6e6; - *background-color: #d9d9d9; -} -.btn>a, -.btn>button, -.btn>input, -.btn>span { - display: inline-block; - *zoom: 1; - margin: 0 -12px !important; - padding: 0 12px !important; - overflow: visible; - width: auto; - height: 24px; - border: 0; - vertical-align: top; - text-decoration: none !important; - line-height: 24px; - font-family: inherit; - font-size: 12px; - color: #333; - cursor: pointer; - background: none; -} -.btn-group { - position: relative; - display: inline-block; - *display: inline; - *margin-left: .3em; - white-space: nowrap; - vertical-align: middle; - font-size: 0; - *zoom: 1; -} -.btn-group:first-child { - *margin-left: 0; -} -.btn-group+.btn-group { - margin-left: 5px; -} -.btn-group>.btn { - position: relative; - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; -} -.btn-group>.btn+.btn { - margin-left: -1px; -} -.btn-group>.btn:first-child { - margin-left: 0; - -webkit-border-bottom-left-radius: 4px; - -moz-border-radius-bottomleft: 4px; - border-bottom-left-radius: 4px; - -webkit-border-top-left-radius: 4px; - -moz-border-radius-topleft: 4px; - border-top-left-radius: 4px; -} -.btn-group>.btn:last-child { - -webkit-border-top-right-radius: 4px; - -moz-border-radius-topright: 4px; - border-top-right-radius: 4px; - -webkit-border-bottom-right-radius: 4px; - -moz-border-radius-bottomright: 4px; - border-bottom-right-radius: 4px; -} -.btn-group>.btn:hover, -.btn-group>.btn:focus, -.btn-group>.btn:active, -.btn-group>.btn.active { - z-index: 2; -} - -/* Debug */ -#rhymix_debug_button { - display: none; - position: fixed; - left: 0; bottom: 40px; - background: #eeeeee; - background: linear-gradient(to bottom, #f4f4f4 0%, #eaeaea 100%); - border: 1px solid #ccc; border-left: 0; - border-top-right-radius: 4px; - border-bottom-right-radius: 4px; - box-shadow: 0 0 3px 0 rgba(0, 0, 0, 0.18), 0 0 6px 0 rgba(0, 0, 0, 0.12); - z-index: 1073741824; -} -#rhymix_debug_button:hover { - background: #dddddd; - background: linear-gradient(to bottom, #e8e8e8 0%, #d9d9d9 100%); -} -#rhymix_debug_button a { - display: block; - font: bold 12px/14px Arial, sans-serif; - color: #444; - text-decoration: none; - padding: 4px 8px; -} -#rhymix_debug_button a.has_errors { - color: #f44336; -} -#rhymix_debug_panel { - display: none; - position: fixed; - left: 0; top: 0; - max-width: 100%; - height: 100%; - overflow-y: scroll; - background: #fcfcfc; - box-sizing: border-box; - border-right: 1px solid #ccc; - box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.18), 0 0 8px 0 rgba(0, 0, 0, 0.12); - z-index: 1073741824; -} -#rhymix_debug_panel .debug_header { - clear: both; - width: 100%; - height: 36px; - background: #444444; - background: linear-gradient(to right, #222222 0%, #444444 40%, #eeeeee 100%); - position: relative; -} -#rhymix_debug_panel .debug_header h2 { - font: bold 16px/20px Arial, sans-serif; - color: #fcfcfc; - position: absolute; - left: 10px; top: 10px; - margin: 0; padding: 0; -} -#rhymix_debug_panel .debug_header .debug_maximize { - font: normal 20px/24px Arial, sans-serif; - text-decoration: none; - color: #444444; - position: absolute; - right: 32px; top: 6px; -} -#rhymix_debug_panel .debug_header .debug_close { - font: normal 28px/28px Arial, sans-serif; - text-decoration: none; - color: #444444; - position: absolute; - right: 10px; top: 4px; -} -#rhymix_debug_panel .debug_header .debug_close:hover { - color: #f44336; -} -#rhymix_debug_panel .debug_page { - clear: both; - margin: 12px 10px; - font: normal 12px/16px Arial, NanumBarunGothic, NanumGothic, "Malgun Gothic", sans-serif; -} -#rhymix_debug_panel .debug_page .debug_page_header { - padding-bottom: 8px; - border-bottom: 1px solid #ddd; - position: relative; - cursor: pointer; -} -#rhymix_debug_panel .debug_page .debug_page_header h3 { - color: #444; - font: inherit; - font-size: 14px; - font-weight: bold; - margin: 0; - padding: 0; -} -#rhymix_debug_panel .debug_page .debug_page_collapse { - display: block; - position: absolute; - right: 0; top: 0; - color: #999; - font-size: 10px; - line-height: 12px; - text-decoration: none; - padding: 2px 2px; -} -#rhymix_debug_panel .debug_page .debug_page_body { - margin: 8px 4px 8px 10px; -} -#rhymix_debug_panel .debug_page .debug_page_body h4 { - color: #444; - font: inherit; - font-size: 13px; - font-weight: bold; - margin: 0 0 8px 0; - padding: 0; -} -#rhymix_debug_panel .debug_page .debug_entry { - font-family: Consolas, "Courier New", monospace; - color: #444; - margin-left: 38px; - margin-bottom: 8px; - text-indent: -28px; - word-wrap: break-word; - word-break: break-all; -} -#rhymix_debug_panel .debug_page .debug_entry.pre_wrap { - white-space: pre-wrap; -} -#rhymix_debug_panel .debug_page .debug_entry ul.debug_metadata { - margin: 0 0 0 -16px; padding: 0; -} -#rhymix_debug_panel .debug_page .debug_entry ul.debug_metadata li { - list-style: disc; - margin: 0; padding: 0; text-indent: 0; -} -#rhymix_debug_panel .debug_page .debug_entry ul.debug_backtrace { - margin: 4px 0 0 16px; padding: 0; -} -#rhymix_debug_panel .debug_page .debug_entry ul.debug_backtrace li { - list-style: disc; - margin: 0; padding: 0; text-indent: 0; - color: #888; -} From 9b416c24731f262a5a04893fba5969bc00a810ce Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Wed, 4 May 2016 22:29:59 +0900 Subject: [PATCH 17/34] Update unit tests to use rhymix.scss --- tests/unit/classes/FrontEndFileHandlerTest.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/unit/classes/FrontEndFileHandlerTest.php b/tests/unit/classes/FrontEndFileHandlerTest.php index bb2923020..f5a41f250 100644 --- a/tests/unit/classes/FrontEndFileHandlerTest.php +++ b/tests/unit/classes/FrontEndFileHandlerTest.php @@ -37,11 +37,13 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $this->specify("css", function() { $handler = new FrontEndFileHandler(); - $handler->loadFile(array('./common/css/xe.css')); + $handler->loadFile(array('./common/css/rhymix.scss')); $handler->loadFile(array('./common/css/mobile.css')); - $expected[] = array('file' => '/rhymix/common/css/xe.css' . $this->_filemtime('common/css/xe.css'), 'media' => 'all', 'targetie' => null); + $expected[] = array('file' => '/rhymix/files/cache/minify/common.css.rhymix.scss.css', 'media' => 'all', 'targetie' => null); $expected[] = array('file' => '/rhymix/common/css/mobile.css' . $this->_filemtime('common/css/mobile.css'), 'media' => 'all', 'targetie' => null); - $this->assertEquals($handler->getCssFileList(), $expected); + $result = $handler->getCssFileList(); + $result[0]['file'] = preg_replace('/\?\d+$/', '', $result[0]['file']); + $this->assertEquals($result, $expected); }); $this->specify("order (duplicate)", function() { @@ -149,9 +151,9 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $this->specify("minify", function() { $handler = new FrontEndFileHandler(); - $handler->loadFile(array('./common/css/xe.css')); + $handler->loadFile(array('./common/css/rhymix.scss')); $handler->loadFile(array('./common/css/mobile.css')); - $expected[] = array('file' => '/rhymix/files/cache/minify/common.css.xe.min.css', 'media' => 'all', 'targetie' => null); + $expected[] = array('file' => '/rhymix/files/cache/minify/common.css.rhymix.scss.min.css', 'media' => 'all', 'targetie' => null); $expected[] = array('file' => '/rhymix/files/cache/minify/common.css.mobile.min.css', 'media' => 'all', 'targetie' => null); $result = $handler->getCssFileList(); $result[0]['file'] = preg_replace('/\?\d+$/', '', $result[0]['file']); From 8af57f904f3213d3bed7ccaf8128971f208d6cad Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Wed, 4 May 2016 22:33:57 +0900 Subject: [PATCH 18/34] Update regexp in HTMLDisplayHandler to recognize LESS/SCSS files as common --- classes/display/HTMLDisplayHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/display/HTMLDisplayHandler.php b/classes/display/HTMLDisplayHandler.php index 212ded171..8fd650c59 100644 --- a/classes/display/HTMLDisplayHandler.php +++ b/classes/display/HTMLDisplayHandler.php @@ -6,7 +6,7 @@ class HTMLDisplayHandler /** * Reserved scripts */ - public static $reservedCSS = '@\bcommon/css/(?:xe|mobile)\.(?:min\.)?css$@'; + public static $reservedCSS = '@\bcommon/css/(?:xe|rhymix|mobile)\.(?:min\.)?(?:s?css|less)$@'; public static $reservedJS = '@\bcommon/js/(?:jquery(?:-[123][0-9.x-]+)?|xe?|common|js_app|xml_handler|xml_js_filter)\.(?:min\.)?js$@'; /** From a421c78a4df804af1dc5e8d2039016f7f68cd33e Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Thu, 5 May 2016 01:36:43 +0900 Subject: [PATCH 19/34] Reorganize temporary directory structure --- classes/display/HTMLDisplayHandler.php | 2 +- .../FrontEndFileHandler.class.php | 29 +++++++++++-------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/classes/display/HTMLDisplayHandler.php b/classes/display/HTMLDisplayHandler.php index 8fd650c59..b106ef3cf 100644 --- a/classes/display/HTMLDisplayHandler.php +++ b/classes/display/HTMLDisplayHandler.php @@ -433,7 +433,7 @@ class HTMLDisplayHandler { Context::loadFile(array('./common/js/jquery-' . $jquery_version . '.min.js', 'head', '', -1730000), true); Context::loadFile(array('./common/js/plugins/jquery.migrate/jquery-migrate-1.2.1.min.js', 'head', '', -1720000), true); - $concat_target_filename = 'files/cache/minify/xe.min.js'; + $concat_target_filename = 'files/cache/assets/minified/rhymix.min.js'; if(file_exists(\RX_BASEDIR . $concat_target_filename)) { $concat_target_mtime = filemtime(\RX_BASEDIR . $concat_target_filename); diff --git a/classes/frontendfile/FrontEndFileHandler.class.php b/classes/frontendfile/FrontEndFileHandler.class.php index bffeab024..5c96df733 100644 --- a/classes/frontendfile/FrontEndFileHandler.class.php +++ b/classes/frontendfile/FrontEndFileHandler.class.php @@ -12,6 +12,11 @@ class FrontEndFileHandler extends Handler */ public static $minify = null; public static $concat = null; + + /** + * Directory for minified, compiled, and concatenated CSS/JS assets. + */ + public static $assetdir = 'files/cache/assets'; /** * Map for css @@ -227,7 +232,7 @@ class FrontEndFileHandler extends Handler $minifiedFileName = $file->fileNameNoExt . '.min.' . $file->fileExtension; $minifiedFileHash = ltrim(str_replace(array('/', '\\'), '.', substr($file->fileRealPath, strlen(\RX_BASEDIR))), '.'); - $minifiedFilePath = \RX_BASEDIR . 'files/cache/minify/' . $minifiedFileHash . '.' . $minifiedFileName; + $minifiedFilePath = \RX_BASEDIR . self::$assetdir . '/minified/' . $minifiedFileHash . '.' . $minifiedFileName; if (!file_exists($minifiedFilePath) || filemtime($minifiedFilePath) < filemtime($file->fileFullPath)) { @@ -240,11 +245,11 @@ class FrontEndFileHandler extends Handler } $file->fileName = $minifiedFileHash . '.' . $minifiedFileName; - $file->filePath = \RX_BASEURL . 'files/cache/minify'; - $file->fileRealPath = \RX_BASEDIR . 'files/cache/minify'; + $file->filePath = \RX_BASEURL . self::$assetdir . '/minified'; + $file->fileRealPath = \RX_BASEDIR . self::$assetdir . '/minified'; $file->fileFullPath = $minifiedFilePath; $file->keyName = $minifiedFileHash . '.' . $file->fileNameNoExt . '.' . $file->fileExtension; - $file->cdnPath = './files/cache/minify'; + $file->cdnPath = './' . self::$assetdir . '/minified'; $file->isMinified = true; } @@ -264,7 +269,7 @@ class FrontEndFileHandler extends Handler $compiledFileName = $file->fileName . ($minify ? '.min' : '') . '.css'; $compiledFileHash = ltrim(str_replace(array('/', '\\'), '.', substr($file->fileRealPath, strlen(\RX_BASEDIR))), '.'); - $compiledFilePath = \RX_BASEDIR . 'files/cache/minify/' . $compiledFileHash . '.' . $compiledFileName; + $compiledFilePath = \RX_BASEDIR . self::$assetdir . '/compiled/' . $compiledFileHash . '.' . $compiledFileName; if (!file_exists($compiledFilePath) || filemtime($compiledFilePath) < filemtime($file->fileFullPath)) { @@ -277,11 +282,11 @@ class FrontEndFileHandler extends Handler } $file->fileName = $compiledFileHash . '.' . $compiledFileName; - $file->filePath = \RX_BASEURL . 'files/cache/minify'; - $file->fileRealPath = \RX_BASEDIR . 'files/cache/minify'; + $file->filePath = \RX_BASEURL . self::$assetdir . '/compiled'; + $file->fileRealPath = \RX_BASEDIR . self::$assetdir . '/compiled'; $file->fileFullPath = $compiledFilePath; $file->keyName = $compiledFileHash . '.' . $file->fileNameNoExt . '.' . $file->fileExtension; - $file->cdnPath = './files/cache/minify'; + $file->cdnPath = './' . self::$assetdir . '/compiled'; $file->isMinified = true; $file->fileExtension = 'css'; } @@ -399,7 +404,7 @@ class FrontEndFileHandler extends Handler $concat_files[] = $file->media === 'all' ? $file->fileFullPath : array($file->fileFullPath, $file->media); $concat_max_timestamp = max($concat_max_timestamp, filemtime($file->fileFullPath)); } - $concat_filename = 'files/cache/minify/concat.' . sha1(serialize($concat_files)) . '.css'; + $concat_filename = self::$assetdir . '/combined/' . sha1(serialize($concat_files)) . '.css'; if (!file_exists(\RX_BASEDIR . $concat_filename) || filemtime(\RX_BASEDIR . $concat_filename) < $concat_max_timestamp) { Rhymix\Framework\Storage::write(\RX_BASEDIR . $concat_filename, Rhymix\Framework\Formatter::concatCSS($concat_files, $concat_filename)); @@ -487,7 +492,7 @@ class FrontEndFileHandler extends Handler $concat_files[] = $file->targetIe ? array($file->fileFullPath, $file->targetIe) : $file->fileFullPath; $concat_max_timestamp = max($concat_max_timestamp, filemtime($file->fileFullPath)); } - $concat_filename = 'files/cache/minify/concat.' . sha1(serialize($concat_files)) . '.js'; + $concat_filename = self::$assetdir . '/combined/' . sha1(serialize($concat_files)) . '.js'; if (!file_exists(\RX_BASEDIR . $concat_filename) || filemtime(\RX_BASEDIR . $concat_filename) < $concat_max_timestamp) { Rhymix\Framework\Storage::write(\RX_BASEDIR . $concat_filename, Rhymix\Framework\Formatter::concatJS($concat_files, $concat_filename)); @@ -614,9 +619,9 @@ class FrontEndFileHandler extends Handler } $dirname = substr($dirname, strlen(\RX_BASEDIR)); - if (strncmp($dirname, 'files/cache/minify/', 19) === 0) + if (strncmp($dirname, self::$assetdir . '/', strlen(self::$assetdir) + 1) === 0) { - $dirname = substr($dirname, 19); + $dirname = substr($dirname, strlen(self::$assetdir) + 1); } $tmp = array_first(explode('/', strtr($dirname, '\\.', '//'))); From 83fb32be283505d96a5025045962f2e1083180c3 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Thu, 5 May 2016 01:37:25 +0900 Subject: [PATCH 20/34] Change English description of concat_scripts, and add Japanese language --- modules/admin/lang/en.php | 14 +++++++------- modules/admin/lang/ja.php | 6 ++++++ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/modules/admin/lang/en.php b/modules/admin/lang/en.php index 7a93fca6d..f2f7cc5e5 100644 --- a/modules/admin/lang/en.php +++ b/modules/admin/lang/en.php @@ -81,17 +81,17 @@ $lang->about_server_ports = 'If your web server does not use 80 for HTTP or 443 $lang->use_db_session = 'Use Session DB'; $lang->about_db_session = 'This setting will use PHP session as DB during authentication. For the Websites which do not use web server frequently, you can uncheck this setting to improve response time. However, session DB will make it impossible to get current users, so you cannot use related functions.'; $lang->qmail_compatibility = 'Enable Qmail'; -$lang->minify_scripts = 'Auto-minify scripts'; +$lang->minify_scripts = 'Minify scripts'; $lang->cmd_minify_all = 'All files'; $lang->cmd_minify_common = 'Common files only'; $lang->cmd_minify_none = 'None'; $lang->about_minify_scripts = 'Automatically minify all CSS and JS scripts in the Core and all modules.'; -$lang->concat_scripts = 'Concatenate scripts'; -$lang->cmd_concat_none = 'Do not concatenate'; -$lang->cmd_concat_css_only = 'Concatenate all CSS'; -$lang->cmd_concat_js_only = 'Concatenate all JS'; -$lang->cmd_concat_css_js = 'Concatenate both CSS and JS'; -$lang->about_concat_scripts = 'Automatically concatenate CSS and JS scripts into as few files as possible. External scripts are not concatenated.'; +$lang->concat_scripts = 'Combine scripts'; +$lang->cmd_concat_none = 'Do not combine'; +$lang->cmd_concat_css_only = 'Combine all CSS'; +$lang->cmd_concat_js_only = 'Combine all JS'; +$lang->cmd_concat_css_js = 'Combine both CSS and JS'; +$lang->about_concat_scripts = 'Automatically combine CSS and JS scripts into as few files as possible. External scripts are not combined.'; $lang->use_gzip = 'gzip Compression'; $lang->delay_session = 'Delay session start'; $lang->about_delay_session = 'To improve performance when using a caching proxy server such as Varnish, do not issue sessions to visitors until they log in.
Selecting this option may cause view counts and visitor counts to become inaccurate.'; diff --git a/modules/admin/lang/ja.php b/modules/admin/lang/ja.php index caa9b2df6..be7615755 100644 --- a/modules/admin/lang/ja.php +++ b/modules/admin/lang/ja.php @@ -83,6 +83,12 @@ $lang->cmd_minify_all = '全てのファイルを圧縮'; $lang->cmd_minify_common = '共通のファイルを圧縮'; $lang->cmd_minify_none = '圧縮されません'; $lang->about_minify_scripts = 'コアとすべてのモジュールに含まれたCSS、JSファイルを自動的に圧縮(minify)して配信します。'; +$lang->concat_scripts = 'スクリプト自動結合'; +$lang->cmd_concat_none = '結合しません'; +$lang->cmd_concat_css_only = 'CSSのみ結合'; +$lang->cmd_concat_js_only = 'JSのみ結合'; +$lang->cmd_concat_css_js = 'CSSやJSの両方を結合'; +$lang->about_concat_scripts = 'CSS、JSファイルを一つにまとめて送信されます。外部からロードするスクリプトは、合わせてされません.'; $lang->use_gzip = 'gzip 圧縮'; $lang->delay_session = 'セッションの開始を遅延'; $lang->about_delay_session = 'Varnishなどのプロキシキャッシュサーバ使用時のパフォーマンスを向上させるために、ログインしていないユーザーには、認証セッションを付与しません。
このオプションを選択した場合、訪問者数とヒット集計が正確でない場合があります。'; From a7f0ba19642279d8f9d94d246767f5d7691c6675 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Thu, 5 May 2016 01:38:34 +0900 Subject: [PATCH 21/34] Update tests with new directory structure --- tests/unit/classes/FrontEndFileHandlerTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unit/classes/FrontEndFileHandlerTest.php b/tests/unit/classes/FrontEndFileHandlerTest.php index f5a41f250..1ae85ac90 100644 --- a/tests/unit/classes/FrontEndFileHandlerTest.php +++ b/tests/unit/classes/FrontEndFileHandlerTest.php @@ -39,7 +39,7 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $handler = new FrontEndFileHandler(); $handler->loadFile(array('./common/css/rhymix.scss')); $handler->loadFile(array('./common/css/mobile.css')); - $expected[] = array('file' => '/rhymix/files/cache/minify/common.css.rhymix.scss.css', 'media' => 'all', 'targetie' => null); + $expected[] = array('file' => '/rhymix/files/cache/assets/compiled/common.css.rhymix.scss.css', 'media' => 'all', 'targetie' => null); $expected[] = array('file' => '/rhymix/common/css/mobile.css' . $this->_filemtime('common/css/mobile.css'), 'media' => 'all', 'targetie' => null); $result = $handler->getCssFileList(); $result[0]['file'] = preg_replace('/\?\d+$/', '', $result[0]['file']); @@ -153,8 +153,8 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $handler = new FrontEndFileHandler(); $handler->loadFile(array('./common/css/rhymix.scss')); $handler->loadFile(array('./common/css/mobile.css')); - $expected[] = array('file' => '/rhymix/files/cache/minify/common.css.rhymix.scss.min.css', 'media' => 'all', 'targetie' => null); - $expected[] = array('file' => '/rhymix/files/cache/minify/common.css.mobile.min.css', 'media' => 'all', 'targetie' => null); + $expected[] = array('file' => '/rhymix/files/cache/assets/compiled/common.css.rhymix.scss.min.css', 'media' => 'all', 'targetie' => null); + $expected[] = array('file' => '/rhymix/files/cache/assets/minified/common.css.mobile.min.css', 'media' => 'all', 'targetie' => null); $result = $handler->getCssFileList(); $result[0]['file'] = preg_replace('/\?\d+$/', '', $result[0]['file']); $result[1]['file'] = preg_replace('/\?\d+$/', '', $result[1]['file']); From 67cc7d3d001fa2fadfa33582916a93e17a76a7b6 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Thu, 5 May 2016 10:42:15 +0900 Subject: [PATCH 22/34] Compile LESS/SCSS again if variables change --- .../FrontEndFileHandler.class.php | 2 +- .../unit/classes/FrontEndFileHandlerTest.php | 21 +++++++++++-------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/classes/frontendfile/FrontEndFileHandler.class.php b/classes/frontendfile/FrontEndFileHandler.class.php index 5c96df733..855f8ebf6 100644 --- a/classes/frontendfile/FrontEndFileHandler.class.php +++ b/classes/frontendfile/FrontEndFileHandler.class.php @@ -268,7 +268,7 @@ class FrontEndFileHandler extends Handler } $compiledFileName = $file->fileName . ($minify ? '.min' : '') . '.css'; - $compiledFileHash = ltrim(str_replace(array('/', '\\'), '.', substr($file->fileRealPath, strlen(\RX_BASEDIR))), '.'); + $compiledFileHash = sha1($file->fileRealPath . ':' . serialize($vars)); $compiledFilePath = \RX_BASEDIR . self::$assetdir . '/compiled/' . $compiledFileHash . '.' . $compiledFileName; if (!file_exists($compiledFilePath) || filemtime($compiledFilePath) < filemtime($file->fileFullPath)) diff --git a/tests/unit/classes/FrontEndFileHandlerTest.php b/tests/unit/classes/FrontEndFileHandlerTest.php index 1ae85ac90..fd3a5df81 100644 --- a/tests/unit/classes/FrontEndFileHandlerTest.php +++ b/tests/unit/classes/FrontEndFileHandlerTest.php @@ -39,11 +39,13 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $handler = new FrontEndFileHandler(); $handler->loadFile(array('./common/css/rhymix.scss')); $handler->loadFile(array('./common/css/mobile.css')); - $expected[] = array('file' => '/rhymix/files/cache/assets/compiled/common.css.rhymix.scss.css', 'media' => 'all', 'targetie' => null); - $expected[] = array('file' => '/rhymix/common/css/mobile.css' . $this->_filemtime('common/css/mobile.css'), 'media' => 'all', 'targetie' => null); $result = $handler->getCssFileList(); - $result[0]['file'] = preg_replace('/\?\d+$/', '', $result[0]['file']); - $this->assertEquals($result, $expected); + $this->assertRegexp('/\.rhymix\.scss\.css\b/', $result[0]['file']); + $this->assertEquals('all', $result[0]['media']); + $this->assertEmpty($result[0]['targetie']); + $this->assertEquals('/rhymix/common/css/mobile.css' . $this->_filemtime('common/css/mobile.css'), $result[1]['file']); + $this->assertEquals('all', $result[1]['media']); + $this->assertEmpty($result[1]['targetie']); }); $this->specify("order (duplicate)", function() { @@ -153,12 +155,13 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $handler = new FrontEndFileHandler(); $handler->loadFile(array('./common/css/rhymix.scss')); $handler->loadFile(array('./common/css/mobile.css')); - $expected[] = array('file' => '/rhymix/files/cache/assets/compiled/common.css.rhymix.scss.min.css', 'media' => 'all', 'targetie' => null); - $expected[] = array('file' => '/rhymix/files/cache/assets/minified/common.css.mobile.min.css', 'media' => 'all', 'targetie' => null); $result = $handler->getCssFileList(); - $result[0]['file'] = preg_replace('/\?\d+$/', '', $result[0]['file']); - $result[1]['file'] = preg_replace('/\?\d+$/', '', $result[1]['file']); - $this->assertEquals($result, $expected); + $this->assertRegexp('/\.rhymix\.scss\.min\.css\b/', $result[0]['file']); + $this->assertEquals('all', $result[0]['media']); + $this->assertEmpty($result[0]['targetie']); + $this->assertRegexp('/minified\/common\.css\.mobile\.min\.css\b/', $result[1]['file']); + $this->assertEquals('all', $result[1]['media']); + $this->assertEmpty($result[1]['targetie']); }); $this->specify("external file", function() { From 398c36885aba2e67b6ca54a9ca5c28a8e01204ef Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Thu, 5 May 2016 11:05:06 +0900 Subject: [PATCH 23/34] Use font config in rhymix.scss to set defaults --- .../frontendfile/FrontEndFileHandler.class.php | 11 ++++++----- common/css/rhymix.scss | 12 ++++++++++++ modules/editor/editor.controller.php | 16 +++++++++++----- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/classes/frontendfile/FrontEndFileHandler.class.php b/classes/frontendfile/FrontEndFileHandler.class.php index 855f8ebf6..bd3d116b4 100644 --- a/classes/frontendfile/FrontEndFileHandler.class.php +++ b/classes/frontendfile/FrontEndFileHandler.class.php @@ -267,18 +267,19 @@ class FrontEndFileHandler extends Handler return; } + if ($default_font_config = Context::get('default_font_config')) + { + $file->vars = array_merge($file->vars, $default_font_config); + } + $compiledFileName = $file->fileName . ($minify ? '.min' : '') . '.css'; - $compiledFileHash = sha1($file->fileRealPath . ':' . serialize($vars)); + $compiledFileHash = sha1($file->fileRealPath . ':' . serialize($file->vars)); $compiledFilePath = \RX_BASEDIR . self::$assetdir . '/compiled/' . $compiledFileHash . '.' . $compiledFileName; if (!file_exists($compiledFilePath) || filemtime($compiledFilePath) < filemtime($file->fileFullPath)) { $method_name = 'compile' . $file->fileExtension; $success = Rhymix\Framework\Formatter::$method_name($file->fileFullPath, $compiledFilePath, $file->vars, $minify); - if ($success === false) - { - return; - } } $file->fileName = $compiledFileHash . '.' . $compiledFileName; diff --git a/common/css/rhymix.scss b/common/css/rhymix.scss index 3cbf9b11c..b911c63a2 100644 --- a/common/css/rhymix.scss +++ b/common/css/rhymix.scss @@ -17,6 +17,18 @@ a img { [hidden] { display: none; } +.xe_content { + font-family: $default_font_family; + font-size: $default_font_size; + line-height: $default_line_height; + word-break: $default_word_break; + @if $default_word_break == 'none' { + white-space: nowrap; + } +} +.xe_content p { + margin: 0 0 $default_paragraph_spacing 0; +} .xe_content img { max-width: 100%; height: auto; diff --git a/modules/editor/editor.controller.php b/modules/editor/editor.controller.php index 801c53851..5a519769c 100644 --- a/modules/editor/editor.controller.php +++ b/modules/editor/editor.controller.php @@ -184,11 +184,16 @@ class editorController extends editor } } } - $content_font = $editor_config->content_font; - $content_font_size = $editor_config->content_font_size; - $content_line_height = $editor_config->content_line_height; - $content_paragraph_spacing = $editor_config->content_paragraph_spacing; - $content_word_break = $editor_config->content_word_break; + + Context::set('default_font_config', array( + 'default_font_family' => $editor_config->content_font ?: 'inherit', + 'default_font_size' => $editor_config->content_font_size ?: '13px', + 'default_line_height' => $editor_config->content_line_height ?: '160%', + 'default_paragraph_spacing' => $editor_config->content_paragraph_spacing ?: '0', + 'default_word_break' => $editor_config->content_word_break ?: 'normal', + )); + + /* $buff = array(); $buff[] = ''; Context::addHtmlHeader(implode(' ', $buff)); + */ } $content = $this->transComponent($content); From 007d563f3464eeef8aebbcc33e6a93d880ae26d0 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Thu, 5 May 2016 11:09:47 +0900 Subject: [PATCH 24/34] Clean up default styles in rhymix.scss --- common/css/rhymix.scss | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/common/css/rhymix.scss b/common/css/rhymix.scss index b911c63a2..90db366aa 100644 --- a/common/css/rhymix.scss +++ b/common/css/rhymix.scss @@ -17,6 +17,8 @@ a img { [hidden] { display: none; } + +/* Content Default Styles */ .xe_content { font-family: $default_font_family; font-size: $default_font_size; @@ -25,28 +27,29 @@ a img { @if $default_word_break == 'none' { white-space: nowrap; } -} -.xe_content p { - margin: 0 0 $default_paragraph_spacing 0; -} -.xe_content img { - max-width: 100%; - height: auto; + p { + margin: 0 0 $default_paragraph_spacing 0; + } + img { + max-width: 100%; + height: auto; + } } @media \0screen { img { max-width: none; } } -.xe-clearfix:before, -.xe-clearfix:after { - content: " "; - display: table; -} -.xe-clearfix:after { - clear: both; -} + +/* Clearfix */ .xe-clearfix { + &:before, &:after { + content: " "; + display: table; + } + &:after { + clear: both; + } zoom: 1; } .xe-widget-wrapper { From b36ec1bd68293f43faf23e439b9e7a2abae24808 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Thu, 5 May 2016 11:18:45 +0900 Subject: [PATCH 25/34] Always fill default font information --- modules/editor/editor.controller.php | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/modules/editor/editor.controller.php b/modules/editor/editor.controller.php index 5a519769c..9479be8c3 100644 --- a/modules/editor/editor.controller.php +++ b/modules/editor/editor.controller.php @@ -159,8 +159,15 @@ class editorController extends editor $module_srl = $module_info->module_srl; if($module_srl) { - $oEditorModel = getModel('editor'); - $editor_config = $oEditorModel->getEditorConfig($module_srl); + $editor_config = getModel('editor')->getEditorConfig($module_srl); + } + else + { + $editor_config = getModel('module')->getModuleConfig('editor'); + } + + if ($editor_config) + { $content_style = $editor_config->content_style; if($content_style) { @@ -222,6 +229,16 @@ class editorController extends editor Context::addHtmlHeader(implode(' ', $buff)); */ } + else + { + Context::set('default_font_config', array( + 'default_font_family' => 'inherit', + 'default_font_size' => '13px', + 'default_line_height' => '160%', + 'default_paragraph_spacing' => '0', + 'default_word_break' => 'normal', + )); + } $content = $this->transComponent($content); return new Object(); From 7750a278a1ba02d1d944de02c39b63567f40b8b3 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Thu, 5 May 2016 11:19:12 +0900 Subject: [PATCH 26/34] Do not combine scripts in the body section --- classes/frontendfile/FrontEndFileHandler.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/frontendfile/FrontEndFileHandler.class.php b/classes/frontendfile/FrontEndFileHandler.class.php index bd3d116b4..fa9d44b9a 100644 --- a/classes/frontendfile/FrontEndFileHandler.class.php +++ b/classes/frontendfile/FrontEndFileHandler.class.php @@ -470,7 +470,7 @@ class FrontEndFileHandler extends Handler // Add all files to the final result. $result = array(); - if ($concat && count($concat_list = $this->_concatMap($map))) + if ($concat && $type === 'head' && count($concat_list = $this->_concatMap($map))) { foreach ($concat_list as $concat_fileset) { From 768118fda3e780ccc9bf9dbe38f9cf9687bd58e0 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Thu, 5 May 2016 11:24:14 +0900 Subject: [PATCH 27/34] Fix line wrapping --- common/css/rhymix.scss | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/common/css/rhymix.scss b/common/css/rhymix.scss index 90db366aa..674786d9b 100644 --- a/common/css/rhymix.scss +++ b/common/css/rhymix.scss @@ -23,9 +23,11 @@ a img { font-family: $default_font_family; font-size: $default_font_size; line-height: $default_line_height; - word-break: $default_word_break; @if $default_word_break == 'none' { white-space: nowrap; + } @else { + word-break: $default_word_break; + word-wrap: break-word; } p { margin: 0 0 $default_paragraph_spacing 0; From 7363f71d4a73e62089892f90f17f2270b3eee8bb Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Thu, 5 May 2016 13:29:24 +0900 Subject: [PATCH 28/34] Fix incorrect conversion of absolute URLs --- common/framework/formatter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/framework/formatter.php b/common/framework/formatter.php index d0f402b79..dcfccc819 100644 --- a/common/framework/formatter.php +++ b/common/framework/formatter.php @@ -325,7 +325,7 @@ class Formatter $path_converter = new \MatthiasMullie\PathConverter\Converter($filename, $target_filename); $content = preg_replace_callback('/\burl\\(([^)]+)\\)/iU', function($matches) use ($path_converter) { $url = trim($matches[1], '\'"'); - if (!strlen($url) || $url[0] === '/') + if (!strlen($url) || $url[0] === '/' || preg_match('#^(?:https?|data):#', $url)) { return $matches[0]; } From b5a10e850f24235ae767bce8e7b6427f1fcd2250 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Thu, 5 May 2016 13:45:57 +0900 Subject: [PATCH 29/34] Add unit tests for concatCSS() and concatJS() --- common/framework/formatter.php | 4 ++-- tests/_data/formatter/concat.source1.css | 4 ++++ tests/_data/formatter/concat.source1.js | 6 +++++ tests/_data/formatter/concat.source2.css | 7 ++++++ tests/_data/formatter/concat.source2.js | 3 +++ tests/_data/formatter/concat.target1.css | 16 +++++++++++++ tests/_data/formatter/concat.target1.js | 14 +++++++++++ tests/_data/formatter/concat.target2.css | 20 ++++++++++++++++ tests/_data/formatter/concat.target2.js | 18 ++++++++++++++ tests/unit/framework/FormatterTest.php | 30 ++++++++++++++++++++++++ 10 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 tests/_data/formatter/concat.source1.css create mode 100644 tests/_data/formatter/concat.source1.js create mode 100644 tests/_data/formatter/concat.source2.css create mode 100644 tests/_data/formatter/concat.source2.js create mode 100644 tests/_data/formatter/concat.target1.css create mode 100644 tests/_data/formatter/concat.target1.js create mode 100644 tests/_data/formatter/concat.target2.css create mode 100644 tests/_data/formatter/concat.target2.js diff --git a/common/framework/formatter.php b/common/framework/formatter.php index dcfccc819..81325fb34 100644 --- a/common/framework/formatter.php +++ b/common/framework/formatter.php @@ -337,7 +337,7 @@ class Formatter unset($path_converter); if ($media !== null) { - $content = "@media $media {\n\n$content\n\n}"; + $content = "@media $media {\n\n" . trim($content) . "\n\n}"; } $original_filename = starts_with(\RX_BASEDIR, $filename) ? substr($filename, strlen(\RX_BASEDIR)) : $filename; $result .= '/* Original file: ' . $original_filename . ' */' . "\n\n" . trim($content) . "\n\n"; @@ -374,7 +374,7 @@ class Formatter $content = utf8_clean(file_get_contents($filename)); if ($targetie !== null) { - $content = 'if (' . self::convertIECondition($targetie) . ') {' . "\n\n" . $content . "\n\n" . '}'; + $content = 'if (' . self::convertIECondition($targetie) . ') {' . "\n\n" . trim($content) . ";\n\n" . '}'; } $original_filename = starts_with(\RX_BASEDIR, $filename) ? substr($filename, strlen(\RX_BASEDIR)) : $filename; $result .= '/* Original file: ' . $original_filename . ' */' . "\n\n" . trim($content) . ";\n\n"; diff --git a/tests/_data/formatter/concat.source1.css b/tests/_data/formatter/concat.source1.css new file mode 100644 index 000000000..6a1940909 --- /dev/null +++ b/tests/_data/formatter/concat.source1.css @@ -0,0 +1,4 @@ +@charset "UTF-8"; +.rhymix { + background: url("foo/bar.jpg"); +} diff --git a/tests/_data/formatter/concat.source1.js b/tests/_data/formatter/concat.source1.js new file mode 100644 index 000000000..315c8d4d5 --- /dev/null +++ b/tests/_data/formatter/concat.source1.js @@ -0,0 +1,6 @@ +(function($) { + $(".foo").click(function(event) { + event.preventDefault(); + $(this).attr("bar", "baz"); + }); +})(jQuery); diff --git a/tests/_data/formatter/concat.source2.css b/tests/_data/formatter/concat.source2.css new file mode 100644 index 000000000..2051ecf95 --- /dev/null +++ b/tests/_data/formatter/concat.source2.css @@ -0,0 +1,7 @@ +@charset "UTF-8"; +.wordpress { + border-radius: 4px; +} +.xpressengine { + margin: 320px; +} diff --git a/tests/_data/formatter/concat.source2.js b/tests/_data/formatter/concat.source2.js new file mode 100644 index 000000000..43f22ddab --- /dev/null +++ b/tests/_data/formatter/concat.source2.js @@ -0,0 +1,3 @@ +(function($) { + $(".xe").hide(); +})(jQuery); diff --git a/tests/_data/formatter/concat.target1.css b/tests/_data/formatter/concat.target1.css new file mode 100644 index 000000000..3144bf83b --- /dev/null +++ b/tests/_data/formatter/concat.target1.css @@ -0,0 +1,16 @@ +/* Original file: tests/_data/formatter/concat.source1.css */ + +@charset "UTF-8"; +.rhymix { + background: url("../_data/formatter/foo/bar.jpg"); +} + +/* Original file: tests/_data/formatter/concat.source2.css */ + +@charset "UTF-8"; +.wordpress { + border-radius: 4px; +} +.xpressengine { + margin: 320px; +} diff --git a/tests/_data/formatter/concat.target1.js b/tests/_data/formatter/concat.target1.js new file mode 100644 index 000000000..7ad72cf6c --- /dev/null +++ b/tests/_data/formatter/concat.target1.js @@ -0,0 +1,14 @@ +/* Original file: tests/_data/formatter/concat.source1.js */ + +(function($) { + $(".foo").click(function(event) { + event.preventDefault(); + $(this).attr("bar", "baz"); + }); +})(jQuery);; + +/* Original file: tests/_data/formatter/concat.source2.js */ + +(function($) { + $(".xe").hide(); +})(jQuery);; diff --git a/tests/_data/formatter/concat.target2.css b/tests/_data/formatter/concat.target2.css new file mode 100644 index 000000000..f69d350ac --- /dev/null +++ b/tests/_data/formatter/concat.target2.css @@ -0,0 +1,20 @@ +/* Original file: tests/_data/formatter/concat.source1.css */ + +@media screen and (max-width: 640px) { + +@charset "UTF-8"; +.rhymix { + background: url("../_data/formatter/foo/bar.jpg"); +} + +} + +/* Original file: tests/_data/formatter/concat.source2.css */ + +@charset "UTF-8"; +.wordpress { + border-radius: 4px; +} +.xpressengine { + margin: 320px; +} diff --git a/tests/_data/formatter/concat.target2.js b/tests/_data/formatter/concat.target2.js new file mode 100644 index 000000000..d7e8e3ba6 --- /dev/null +++ b/tests/_data/formatter/concat.target2.js @@ -0,0 +1,18 @@ +/* Original file: tests/_data/formatter/concat.source1.js */ + +(function($) { + $(".foo").click(function(event) { + event.preventDefault(); + $(this).attr("bar", "baz"); + }); +})(jQuery);; + +/* Original file: tests/_data/formatter/concat.source2.js */ + +if ((/MSIE (\d+)/.exec(window.navigator.userAgent) && /MSIE (\d+)/.exec(window.navigator.userAgent)[1] >= 6) && (/MSIE (\d+)/.exec(window.navigator.userAgent) && /MSIE (\d+)/.exec(window.navigator.userAgent)[1] <= 8)) { + +(function($) { + $(".xe").hide(); +})(jQuery);; + +}; diff --git a/tests/unit/framework/FormatterTest.php b/tests/unit/framework/FormatterTest.php index 8c034b6a4..1f8e26c37 100644 --- a/tests/unit/framework/FormatterTest.php +++ b/tests/unit/framework/FormatterTest.php @@ -132,6 +132,36 @@ class FormatterTest extends \Codeception\TestCase\Test unlink($test_target); } + public function testConcatCSS() + { + $source1 = \RX_BASEDIR . 'tests/_data/formatter/concat.source1.css'; + $source2 = \RX_BASEDIR . 'tests/_data/formatter/concat.source2.css'; + $real_target1 = \RX_BASEDIR . 'tests/_data/formatter/concat.target1.css'; + $real_target2 = \RX_BASEDIR . 'tests/_data/formatter/concat.target2.css'; + $test_target = \RX_BASEDIR . 'tests/_output/concat.target.css'; + + $test_without_media_query = Rhymix\Framework\Formatter::concatCSS(array($source1, $source2), $test_target); + $this->assertEquals(trim(file_get_contents($real_target1)), trim($test_without_media_query)); + + $test_with_media_query = Rhymix\Framework\Formatter::concatCSS(array(array($source1, 'screen and (max-width: 640px)'), $source2), $test_target); + $this->assertEquals(trim(file_get_contents($real_target2)), trim($test_with_media_query)); + } + + public function testConcatJS() + { + $source1 = \RX_BASEDIR . 'tests/_data/formatter/concat.source1.js'; + $source2 = \RX_BASEDIR . 'tests/_data/formatter/concat.source2.js'; + $real_target1 = \RX_BASEDIR . 'tests/_data/formatter/concat.target1.js'; + $real_target2 = \RX_BASEDIR . 'tests/_data/formatter/concat.target2.js'; + $test_target = \RX_BASEDIR . 'tests/_output/concat.target.js'; + + $test_without_targetie = Rhymix\Framework\Formatter::concatJS(array($source1, $source2), $test_target); + $this->assertEquals(trim(file_get_contents($real_target1)), trim($test_without_targetie)); + + $test_with_targetie = Rhymix\Framework\Formatter::concatJS(array($source1, array($source2, '(gte IE 6) & (lte IE 8)')), $test_target); + $this->assertEquals(trim(file_get_contents($real_target2)), trim($test_with_targetie)); + } + public function testConvertIECondition() { $this->assertEquals('window.navigator.userAgent.match(/MSIE\s/)', Rhymix\Framework\Formatter::convertIECondition('IE')); From 2682793ac222f748b942bb8ccf554c9c55da0ed9 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Thu, 5 May 2016 16:22:17 +0900 Subject: [PATCH 30/34] Add minify/concat tests to FrontEndFileHandlerTest --- .../unit/classes/FrontEndFileHandlerTest.php | 94 ++++++++++++++----- 1 file changed, 73 insertions(+), 21 deletions(-) diff --git a/tests/unit/classes/FrontEndFileHandlerTest.php b/tests/unit/classes/FrontEndFileHandlerTest.php index fd3a5df81..7d782ccf1 100644 --- a/tests/unit/classes/FrontEndFileHandlerTest.php +++ b/tests/unit/classes/FrontEndFileHandlerTest.php @@ -17,7 +17,7 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test FrontEndFileHandler::$minify = 'none'; FrontEndFileHandler::$concat = 'none'; - $this->specify("js(head)", function() { + $this->specify("js (head)", function() { $handler = new FrontEndFileHandler(); $handler->loadFile(array('./common/js/js_app.js', 'head')); $handler->loadFile(array('./common/js/common.js', 'body')); @@ -25,22 +25,22 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $handler->loadFile(array('./common/js/xml_js_filter.js', 'body')); $expected[] = array('file' => '/rhymix/common/js/js_app.js' . $this->_filemtime('common/js/js_app.js'), 'targetie' => null); $expected[] = array('file' => '/rhymix/common/js/common.js' . $this->_filemtime('common/js/common.js'), 'targetie' => null); - $this->assertEquals($handler->getJsFileList(), $expected); + $this->assertEquals($expected, $handler->getJsFileList()); }); - $this->specify("js(body)", function() { + $this->specify("js (body)", function() { $handler = new FrontEndFileHandler(); $handler->loadFile(array('./common/js/xml_js_filter.js', 'head')); $expected = array(); - $this->assertEquals($handler->getJsFileList('body'), $expected); + $this->assertEquals($expected, $handler->getJsFileList('body')); }); - $this->specify("css", function() { + $this->specify("css and scss", function() { $handler = new FrontEndFileHandler(); $handler->loadFile(array('./common/css/rhymix.scss')); $handler->loadFile(array('./common/css/mobile.css')); $result = $handler->getCssFileList(); - $this->assertRegexp('/\.rhymix\.scss\.css\b/', $result[0]['file']); + $this->assertRegexp('/\.rhymix\.scss\.css\?\d+$/', $result[0]['file']); $this->assertEquals('all', $result[0]['media']); $this->assertEmpty($result[0]['targetie']); $this->assertEquals('/rhymix/common/css/mobile.css' . $this->_filemtime('common/css/mobile.css'), $result[1]['file']); @@ -62,7 +62,7 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $expected[] = array('file' => '/rhymix/common/js/common.js' . $this->_filemtime('common/js/common.js'), 'targetie' => null); $expected[] = array('file' => '/rhymix/common/js/xml_handler.js' . $this->_filemtime('common/js/xml_handler.js'), 'targetie' => null); $expected[] = array('file' => '/rhymix/common/js/xml_js_filter.js' . $this->_filemtime('common/js/xml_js_filter.js'), 'targetie' => null); - $this->assertEquals($handler->getJsFileList(), $expected); + $this->assertEquals($expected, $handler->getJsFileList()); }); $this->specify("order (redefine)", function() { @@ -75,7 +75,7 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $expected[] = array('file' => '/rhymix/common/js/common.js' . $this->_filemtime('common/js/common.js'), 'targetie' => null); $expected[] = array('file' => '/rhymix/common/js/xml_js_filter.js' . $this->_filemtime('common/js/xml_js_filter.js'), 'targetie' => null); $expected[] = array('file' => '/rhymix/common/js/xml_handler.js' . $this->_filemtime('common/js/xml_handler.js'), 'targetie' => null); - $this->assertEquals($handler->getJsFileList(), $expected); + $this->assertEquals($expected, $handler->getJsFileList()); }); $this->specify("unload", function() { @@ -88,10 +88,10 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $expected[] = array('file' => '/rhymix/common/js/common.js' . $this->_filemtime('common/js/common.js'), 'targetie' => null); $expected[] = array('file' => '/rhymix/common/js/xml_handler.js' . $this->_filemtime('common/js/xml_handler.js'), 'targetie' => null); $expected[] = array('file' => '/rhymix/common/js/xml_js_filter.js' . $this->_filemtime('common/js/xml_js_filter.js'), 'targetie' => null); - $this->assertEquals($handler->getJsFileList(), $expected); + $this->assertEquals($expected, $handler->getJsFileList()); }); - $this->specify("target IE(js)", function() { + $this->specify("target IE (js)", function() { $handler = new FrontEndFileHandler(); $handler->loadFile(array('./common/js/js_app.js', 'head', 'ie6')); $handler->loadFile(array('./common/js/js_app.js', 'head', 'ie7')); @@ -99,7 +99,7 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $expected[] = array('file' => '/rhymix/common/js/js_app.js' . $this->_filemtime('common/js/js_app.js'), 'targetie' => 'ie6'); $expected[] = array('file' => '/rhymix/common/js/js_app.js' . $this->_filemtime('common/js/js_app.js'), 'targetie' => 'ie7'); $expected[] = array('file' => '/rhymix/common/js/js_app.js' . $this->_filemtime('common/js/js_app.js'), 'targetie' => 'ie8'); - $this->assertEquals($handler->getJsFileList(), $expected); + $this->assertEquals($expected, $handler->getJsFileList()); }); $this->specify("external file - schemaless", function() { @@ -113,7 +113,7 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $expected[] = array('file' => 'https://external.host/js/script.js', 'targetie' => null); $expected[] = array('file' => '//external.host/js/script1.js', 'targetie' => null); $expected[] = array('file' => '//external.host/js/script2.js', 'targetie' => null); - $this->assertEquals($handler->getJsFileList(), $expected); + $this->assertEquals($expected, $handler->getJsFileList()); }); $this->specify("external file - schemaless", function() { @@ -122,10 +122,10 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $handler->loadFile(array('///external.host/js/script.js')); $expected[] = array('file' => '//external.host/js/script.js', 'targetie' => null); - $this->assertEquals($handler->getJsFileList(), $expected); + $this->assertEquals($expected, $handler->getJsFileList()); }); - $this->specify("target IE(css)", function() { + $this->specify("target IE (css)", function() { $handler = new FrontEndFileHandler(); $handler->loadFile(array('./common/css/common.css', null, 'ie6')); $handler->loadFile(array('./common/css/common.css', null, 'ie7')); @@ -134,7 +134,7 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $expected[] = array('file' => '/rhymix/common/css/common.css', 'media'=>'all', 'targetie' => 'ie6'); $expected[] = array('file' => '/rhymix/common/css/common.css','media'=>'all', 'targetie' => 'ie7'); $expected[] = array('file' => '/rhymix/common/css/common.css', 'media'=>'all', 'targetie' => 'ie8'); - $this->assertEquals($handler->getCssFileList(), $expected); + $this->assertEquals($expected, $handler->getCssFileList()); }); $this->specify("media", function() { @@ -146,12 +146,12 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $expected[] = array('file' => '/rhymix/common/css/common.css', 'media'=>'all', 'targetie' => null); $expected[] = array('file' => '/rhymix/common/css/common.css','media'=>'screen', 'targetie' => null); $expected[] = array('file' => '/rhymix/common/css/common.css', 'media'=>'handled', 'targetie' => null); - $this->assertEquals($handler->getCssFileList(), $expected); + $this->assertEquals($expected, $handler->getCssFileList()); }); FrontEndFileHandler::$minify = 'all'; - $this->specify("minify", function() { + $this->specify("minify (css)", function() { $handler = new FrontEndFileHandler(); $handler->loadFile(array('./common/css/rhymix.scss')); $handler->loadFile(array('./common/css/mobile.css')); @@ -159,11 +159,63 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $this->assertRegexp('/\.rhymix\.scss\.min\.css\b/', $result[0]['file']); $this->assertEquals('all', $result[0]['media']); $this->assertEmpty($result[0]['targetie']); - $this->assertRegexp('/minified\/common\.css\.mobile\.min\.css\b/', $result[1]['file']); + $this->assertRegexp('/minified\/common\.css\.mobile\.min\.css\?\d+$/', $result[1]['file']); $this->assertEquals('all', $result[1]['media']); $this->assertEmpty($result[1]['targetie']); }); - + + $this->specify("minify (js)", function() { + $handler = new FrontEndFileHandler(); + $handler->loadFile(array('./common/js/common.js', 'head')); + $result = $handler->getJsFileList(); + $this->assertRegexp('/minified\/common\.js\.common\.min\.js\?\d+$/', $result[0]['file']); + $this->assertEmpty($result[0]['targetie']); + }); + + FrontEndFileHandler::$minify = 'none'; + + FrontEndFileHandler::$concat = 'css'; + + $this->specify("concat (css)", function() { + $handler = new FrontEndFileHandler(); + $handler->loadFile(array('./common/css/rhymix.scss')); + $handler->loadFile(array('./common/css/mobile.css')); + $handler->loadFile(array('http://external.host/style.css')); + $handler->loadFile(array('./common/css/bootstrap.css', null, 'IE')); + $handler->loadFile(array('./tests/_data/formatter/concat.source1.css')); + $handler->loadFile(array('./tests/_data/formatter/concat.source2.css')); + $handler->loadFile(array('./tests/_data/formatter/concat.target1.css')); + $handler->loadFile(array('./tests/_data/formatter/concat.target2.css')); + $result = $handler->getCssFileList(); + $this->assertEquals(4, count($result)); + $this->assertRegexp('/combined\/[0-9a-f]+\.css\?\d+$/', $result[0]['file']); + $this->assertEquals('http://external.host/style.css', $result[1]['file']); + $this->assertEquals('/rhymix/common/css/bootstrap.css' . $this->_filemtime('common/css/bootstrap.css'), $result[2]['file']); + $this->assertEquals('IE', $result[2]['targetie']); + $this->assertRegexp('/combined\/[0-9a-f]+\.css\?\d+$/', $result[3]['file']); + }); + + FrontEndFileHandler::$concat = 'js'; + + $this->specify("concat (js)", function() { + $handler = new FrontEndFileHandler(); + $handler->loadFile(array('./common/js/common.js', 'head')); + $handler->loadFile(array('./common/js/debug.js', 'head')); + $handler->loadFile(array('./common/js/html5.js', 'head')); + $handler->loadFile(array('///external.host/js/script.js')); + $handler->loadFile(array('./tests/_data/formatter/concat.source1.js', 'head', 'lt IE 8')); + $handler->loadFile(array('./tests/_data/formatter/concat.source2.js', 'head', 'gt IE 7')); + $handler->loadFile(array('./tests/_data/formatter/concat.target1.js')); + $handler->loadFile(array('./tests/_data/formatter/concat.target2.js')); + $result = $handler->getJsFileList(); + $this->assertEquals(3, count($result)); + $this->assertRegexp('/combined\/[0-9a-f]+\.js\?\d+$/', $result[0]['file']); + $this->assertEquals('//external.host/js/script.js', $result[1]['file']); + $this->assertRegexp('/combined\/[0-9a-f]+\.js\?\d+$/', $result[2]['file']); + }); + + FrontEndFileHandler::$concat = 'none'; + $this->specify("external file", function() { $handler = new FrontEndFileHandler(); $handler->loadFile(array('http://external.host/css/style1.css')); @@ -171,7 +223,7 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $expected[] = array('file' => 'http://external.host/css/style1.css', 'media'=>'all', 'targetie' => null); $expected[] = array('file' => 'https://external.host/css/style2.css', 'media'=>'all', 'targetie' => null); - $this->assertEquals($handler->getCssFileList(), $expected); + $this->assertEquals($expected, $handler->getCssFileList()); }); $this->specify("external file - schemaless", function() { @@ -181,7 +233,7 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $expected[] = array('file' => '//external.host/css/style.css', 'media'=>'all', 'targetie' => null); $expected[] = array('file' => '//external.host/css2/style2.css', 'media'=>'all', 'targetie' => null); - $this->assertEquals($handler->getCssFileList(), $expected); + $this->assertEquals($expected, $handler->getCssFileList()); }); From 01315e62aa74664231cbecce4f4a0dc325d915ed Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Thu, 5 May 2016 19:55:09 +0900 Subject: [PATCH 31/34] Fix relative path conversion in CSS/LESS/SCSS imports --- common/framework/formatter.php | 38 +++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/common/framework/formatter.php b/common/framework/formatter.php index 81325fb34..8d0d46215 100644 --- a/common/framework/formatter.php +++ b/common/framework/formatter.php @@ -311,8 +311,10 @@ class Formatter { $source_filename = array($source_filename); } + foreach ($source_filename as $filename) { + // Get the media query. if (is_array($filename) && count($filename) >= 2) { list($filename, $media) = $filename; @@ -321,7 +323,11 @@ class Formatter { $media = null; } + + // Clean the content. $content = utf8_clean(file_get_contents($filename)); + + // Convert all paths to be relative to the new filename. $path_converter = new \MatthiasMullie\PathConverter\Converter($filename, $target_filename); $content = preg_replace_callback('/\burl\\(([^)]+)\\)/iU', function($matches) use ($path_converter) { $url = trim($matches[1], '\'"'); @@ -331,14 +337,36 @@ class Formatter } else { - return 'url("' . escape_dqstr($path_converter->convert($url)) . '")'; + return 'url("' . str_replace('\\$', '$', escape_dqstr($path_converter->convert($url))) . '")'; } }, $content); unset($path_converter); + + // Convert all paths in LESS and SCSS imports, too. + $import_type = ends_with('.scss', $filename) ? 'scss' : 'normal'; + $content = preg_replace_callback('/@import\s+(?:\\([^()]+\\))?([^;]+);/', function($matches) use($filename, $target_filename, $import_type) { + $import_content = ''; + $import_files = array_map(function($str) use($filename, $import_type) { + $str = trim(trim(trim($str), '"\'')); + return dirname($filename) . '/' . ($import_type === 'scss' ? "_$str.scss" : $str); + }, explode(',', $matches[1])); + foreach ($import_files as $import_filename) + { + if (file_exists($import_filename)) + { + $import_content .= self::concatCSS($import_filename, $target_filename); + } + } + return $import_content; + }, $content); + + // Wrap the content in a media query if there is one. if ($media !== null) { $content = "@media $media {\n\n" . trim($content) . "\n\n}"; } + + // Append to the result string. $original_filename = starts_with(\RX_BASEDIR, $filename) ? substr($filename, strlen(\RX_BASEDIR)) : $filename; $result .= '/* Original file: ' . $original_filename . ' */' . "\n\n" . trim($content) . "\n\n"; } @@ -361,8 +389,10 @@ class Formatter { $source_filename = array($source_filename); } + foreach ($source_filename as $filename) { + // Get the IE condition. if (is_array($filename) && count($filename) >= 2) { list($filename, $targetie) = $filename; @@ -371,11 +401,17 @@ class Formatter { $targetie = null; } + + // Clean the content. $content = utf8_clean(file_get_contents($filename)); + + // Wrap the content in an IE condition if there is one. if ($targetie !== null) { $content = 'if (' . self::convertIECondition($targetie) . ') {' . "\n\n" . trim($content) . ";\n\n" . '}'; } + + // Append to the result string. $original_filename = starts_with(\RX_BASEDIR, $filename) ? substr($filename, strlen(\RX_BASEDIR)) : $filename; $result .= '/* Original file: ' . $original_filename . ' */' . "\n\n" . trim($content) . ";\n\n"; } From 1650b79a991d140a5efbd1f134c9a020d4cbf378 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Thu, 5 May 2016 19:55:41 +0900 Subject: [PATCH 32/34] Fix variable escaping for LESS --- classes/frontendfile/FrontEndFileHandler.class.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/classes/frontendfile/FrontEndFileHandler.class.php b/classes/frontendfile/FrontEndFileHandler.class.php index fa9d44b9a..e2c69ac95 100644 --- a/classes/frontendfile/FrontEndFileHandler.class.php +++ b/classes/frontendfile/FrontEndFileHandler.class.php @@ -271,6 +271,12 @@ class FrontEndFileHandler extends Handler { $file->vars = array_merge($file->vars, $default_font_config); } + if ($file->fileExtension === 'less') + { + $file->vars = array_map(function($str) { + return preg_match('/^[0-9a-zA-Z\.%_-]+$/', $str) ? $str : ('~"' . str_replace('"', '\\"', $str) . '"'); + }, $file->vars); + } $compiledFileName = $file->fileName . ($minify ? '.min' : '') . '.css'; $compiledFileHash = sha1($file->fileRealPath . ':' . serialize($file->vars)); From 7385d2f097047c87b18341a31388c62dddfd8e95 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Thu, 5 May 2016 20:18:14 +0900 Subject: [PATCH 33/34] Process imports before converting url() relative paths --- common/framework/formatter.php | 36 +++++++++++++++++----------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/common/framework/formatter.php b/common/framework/formatter.php index 8d0d46215..4ce0d84f5 100644 --- a/common/framework/formatter.php +++ b/common/framework/formatter.php @@ -327,6 +327,24 @@ class Formatter // Clean the content. $content = utf8_clean(file_get_contents($filename)); + // Convert all paths in LESS and SCSS imports, too. + $import_type = ends_with('.scss', $filename) ? 'scss' : 'normal'; + $content = preg_replace_callback('/@import\s+(?:\\([^()]+\\))?([^;]+);/', function($matches) use($filename, $target_filename, $import_type) { + $import_content = ''; + $import_files = array_map(function($str) use($filename, $import_type) { + $str = trim(trim(trim(preg_replace('/^url\\(([^()]+)\\)$/', '$1', trim($str))), '"\'')); + return dirname($filename) . '/' . ($import_type === 'scss' ? "_$str.scss" : $str); + }, explode(',', $matches[1])); + foreach ($import_files as $import_filename) + { + if (file_exists($import_filename)) + { + $import_content .= self::concatCSS($import_filename, $target_filename); + } + } + return trim($import_content); + }, $content); + // Convert all paths to be relative to the new filename. $path_converter = new \MatthiasMullie\PathConverter\Converter($filename, $target_filename); $content = preg_replace_callback('/\burl\\(([^)]+)\\)/iU', function($matches) use ($path_converter) { @@ -342,24 +360,6 @@ class Formatter }, $content); unset($path_converter); - // Convert all paths in LESS and SCSS imports, too. - $import_type = ends_with('.scss', $filename) ? 'scss' : 'normal'; - $content = preg_replace_callback('/@import\s+(?:\\([^()]+\\))?([^;]+);/', function($matches) use($filename, $target_filename, $import_type) { - $import_content = ''; - $import_files = array_map(function($str) use($filename, $import_type) { - $str = trim(trim(trim($str), '"\'')); - return dirname($filename) . '/' . ($import_type === 'scss' ? "_$str.scss" : $str); - }, explode(',', $matches[1])); - foreach ($import_files as $import_filename) - { - if (file_exists($import_filename)) - { - $import_content .= self::concatCSS($import_filename, $target_filename); - } - } - return $import_content; - }, $content); - // Wrap the content in a media query if there is one. if ($media !== null) { From cb8bb2e1b0d62128a7f8339970c808fc7f202b6f Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Thu, 5 May 2016 20:18:45 +0900 Subject: [PATCH 34/34] Add unit tests for CSS imports and relative path conversion --- tests/_data/formatter/concat.source2.css | 1 + tests/_data/formatter/concat.source3.css | 4 ++++ tests/_data/formatter/concat.target1.css | 6 ++++++ tests/_data/formatter/concat.target2.css | 6 ++++++ 4 files changed, 17 insertions(+) create mode 100644 tests/_data/formatter/concat.source3.css diff --git a/tests/_data/formatter/concat.source2.css b/tests/_data/formatter/concat.source2.css index 2051ecf95..639cfbcdd 100644 --- a/tests/_data/formatter/concat.source2.css +++ b/tests/_data/formatter/concat.source2.css @@ -1,4 +1,5 @@ @charset "UTF-8"; +@import url(concat.source3.css); .wordpress { border-radius: 4px; } diff --git a/tests/_data/formatter/concat.source3.css b/tests/_data/formatter/concat.source3.css new file mode 100644 index 000000000..6dbf2de24 --- /dev/null +++ b/tests/_data/formatter/concat.source3.css @@ -0,0 +1,4 @@ +.imported { + background-image: url("test.jpg"); + font-family: sans-serif; +} diff --git a/tests/_data/formatter/concat.target1.css b/tests/_data/formatter/concat.target1.css index 3144bf83b..be34a85e9 100644 --- a/tests/_data/formatter/concat.target1.css +++ b/tests/_data/formatter/concat.target1.css @@ -8,6 +8,12 @@ /* Original file: tests/_data/formatter/concat.source2.css */ @charset "UTF-8"; +/* Original file: tests/_data/formatter/concat.source3.css */ + +.imported { + background-image: url("../_data/_data/formatter/test.jpg"); + font-family: sans-serif; +} .wordpress { border-radius: 4px; } diff --git a/tests/_data/formatter/concat.target2.css b/tests/_data/formatter/concat.target2.css index f69d350ac..59145933c 100644 --- a/tests/_data/formatter/concat.target2.css +++ b/tests/_data/formatter/concat.target2.css @@ -12,6 +12,12 @@ /* Original file: tests/_data/formatter/concat.source2.css */ @charset "UTF-8"; +/* Original file: tests/_data/formatter/concat.source3.css */ + +.imported { + background-image: url("../_data/_data/formatter/test.jpg"); + font-family: sans-serif; +} .wordpress { border-radius: 4px; }