I’ve created two plugins that minify CSS and JS (currently without compression) after the document has been rendered by ModX. No modifications to templates are needed, one have only to create plugin and directory in /assets/components/minify. Plugin output is stored in cache files, they get updated when ModX cache is updated or if you manually delete cache files or change script/css filename. Cache files are specific for script/css combinations within templates and contexts. There should be no issues for most of sites.
Here are the scripts:
MinifyCSS
<?php
$properties['css_path'] = $modx->getOption('css_path',$scriptProperties,MODX_ASSETS_PATH . 'components/minify/');
$properties['css_url'] = $modx->getOption('css_url',$scriptProperties,MODX_ASSETS_URL . 'components/minify/');
$properties[xPDO::OPT_CACHE_KEY] = $modx->getOption('cache_resource_key', $properties, 'default');
$properties[xPDO::OPT_CACHE_HANDLER] = $modx->getOption('cache_resource_handler', $properties, 'xPDOFileCache');
$properties[xPDO::OPT_CACHE_EXPIRES] = (integer) $modx->getOption(xPDO::OPT_CACHE_EXPIRES, $properties, 0);
$properties['cacheOptions'] = array(
xPDO::OPT_CACHE_KEY => $properties[xPDO::OPT_CACHE_KEY],
xPDO::OPT_CACHE_HANDLER => $properties[xPDO::OPT_CACHE_HANDLER],
xPDO::OPT_CACHE_EXPIRES => $properties[xPDO::OPT_CACHE_EXPIRES],
);
$scripts = preg_match_all('!<link(.*)href="(.*\.css)"(.*)>!i',$modx->documentOutput,$matches,PREG_OFFSET_CAPTURE);
$k='';
foreach($matches[2] as $k) {
$key .= $k[0];
}
$properties['cachePageKey'] = $modx->context->get('key') . '/elements/minify/'. md5($key);
$properties['cacheFileName'] = $modx->context->get('key'). '.' . md5($key) . '.css';
$cached = false;
if ($modx->getCacheManager()) {
$cached = $modx->cacheManager->get($properties['cachePageKey'], $properties['cacheOptions']);
} else
return false;
if (empty($cached) || !isset($cached['properties']) || !isset($cached['output'])) {
$js = '';
foreach($matches[2] as $script) {
$fp = fopen($script[0],'r+');
$jsTmp = fread($fp,filesize($script[0]));
$js .= preg_replace('!url\((.*)\)!i','url('.MODX_BASE_URL.dirname($script[0]).'/\\1)',$jsTmp);
fclose($fp);
}
if ($modx->getCacheManager()) {
$cached = array('properties' => $properties, 'output' => $js);
$modx->cacheManager->set($properties['cachePageKey'], $cached, $properties[xPDO::OPT_CACHE_EXPIRES], $properties['cacheOptions']);
$fp = fopen($properties['css_path'].$properties['cacheFileName'],'w');
fwrite($fp,$js);
fclose($fp);
}
}
$modx->documentOutput = preg_replace('!<link(.*)href="(.*\.css)"(.*)>!i','',$modx->documentOutput);
$modx->documentOutput = substr($modx->documentOutput,0,$matches[0][0][1]) . '<link rel="stylesheet" type="text/css" href="'.$properties['css_url'].$properties['cacheFileName'].'" />' . substr($modx->documentOutput,$matches[0][0][1]);
MinifyJS
<?php
$properties['js_path'] = $modx->getOption('js_path',$scriptProperties,MODX_ASSETS_PATH . 'components/minify/');
$properties['js_url'] = $modx->getOption('js_url',$scriptProperties,MODX_ASSETS_URL . 'components/minify/');
$properties[xPDO::OPT_CACHE_KEY] = $modx->getOption('cache_resource_key', $properties, 'default');
$properties[xPDO::OPT_CACHE_HANDLER] = $modx->getOption('cache_resource_handler', $properties, 'xPDOFileCache');
$properties[xPDO::OPT_CACHE_EXPIRES] = (integer) $modx->getOption(xPDO::OPT_CACHE_EXPIRES, $properties, 0);
$properties['cacheOptions'] = array(
xPDO::OPT_CACHE_KEY => $properties[xPDO::OPT_CACHE_KEY],
xPDO::OPT_CACHE_HANDLER => $properties[xPDO::OPT_CACHE_HANDLER],
xPDO::OPT_CACHE_EXPIRES => $properties[xPDO::OPT_CACHE_EXPIRES],
);
$scripts = preg_match_all('!<script(.*)src="(.*)"(.*)></script>!i',$modx->documentOutput,$matches,PREG_OFFSET_CAPTURE);
$k='';
foreach($matches[2] as $k) {
$key .= $k[0];
}
$properties['cachePageKey'] = $modx->context->get('key') . '/elements/minify/'. md5($key);
$properties['cacheFileName'] = $modx->context->get('key'). '.' . md5($key) . '.js';
$cached = false;
if ($modx->getCacheManager()) {
$cached = $modx->cacheManager->get($properties['cachePageKey'], $properties['cacheOptions']);
} else
return false;
if (empty($cached) || !isset($cached['properties']) || !isset($cached['output'])) {
$js = '';
foreach($matches[2] as $script) {
$fp = fopen($script[0],'r+');
$js .= fread($fp,filesize($script[0]));
fclose($fp);
}
if ($modx->getCacheManager()) {
$cached = array('properties' => $properties, 'output' => $js);
$modx->cacheManager->set($properties['cachePageKey'], $cached, $properties[xPDO::OPT_CACHE_EXPIRES], $properties['cacheOptions']);
$fp = fopen($properties['js_path'].$properties['cacheFileName'],'w');
fwrite($fp,$js);
fclose($fp);
}
}
$modx->documentOutput = preg_replace('!<script(.*)src="(.*)"(.*)></script>!i','',$modx->documentOutput);
$modx->documentOutput = substr($modx->documentOutput,0,$matches[0][0][1]) . '<script type="text/javascript" src="'.$properties['js_url'].$properties['cacheFileName'].'"></script>' . substr($modx->documentOutput,$matches[0][0][1]);
For both check OnWebPagePrerender event.
hope those will help you earning some YSlow points ;-)
The scripts are not optimized in any way, and currently I do not plan to convert them to packages - lacking of time ;-) As of my observations page rendering time is not affected significantly.
MinifyCSS works only for relative paths - it is ok for all my works.
You can see them work here
http://www.iqfm.eu (have a look at source)
If you need to work on css or javascript just disable plugins and everything will be untouched ;-)
ps sorry for my eng, 15 years after I learned and not using too often now.