Version Notes
This module was tested in Magento Commerce version 1.4.0.1 and 1.5.0.1, and work in this version smoothly.
Download this release
Release Info
| Developer | Magento Core Team |
| Extension | PSystem_OptimizationHead |
| Version | 1.0.0 |
| Comparing to | |
| See all releases | |
Version 1.0.0
- app/code/community/PSystem/OptimizationHead/Block/Head.php +239 -0
- app/code/community/PSystem/OptimizationHead/Block/Head/Abstract.php +19 -0
- app/code/community/PSystem/OptimizationHead/Model/Observer.php +38 -0
- app/code/community/PSystem/OptimizationHead/etc/adminhtml.xml +28 -0
- app/code/community/PSystem/OptimizationHead/etc/config.xml +41 -0
- app/code/community/PSystem/OptimizationHead/etc/system.xml +119 -0
- app/code/community/PSystem/OptimizationHead/lib/JSMin.php +314 -0
- app/code/community/PSystem/OptimizationHead/lib/Minify/CSS.php +83 -0
- app/code/community/PSystem/OptimizationHead/lib/Minify/CSS/Compressor.php +250 -0
- app/code/community/PSystem/OptimizationHead/lib/Minify/CSS/UriRewriter.php +270 -0
- app/code/community/PSystem/OptimizationHead/lib/Minify/CommentPreserver.php +90 -0
- app/code/community/PSystem/OptimizationHead/lib/PSystemHtmlMinify.php +30 -0
- app/etc/modules/PSystem_OptimizationHead.xml +20 -0
- js/pascalsystem/combine.php +231 -0
- package.xml +18 -0
app/code/community/PSystem/OptimizationHead/Block/Head.php
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
/**
|
| 3 |
+
* @category PSystem
|
| 4 |
+
* @package PSystem_OptimizationHead
|
| 5 |
+
* @author Pascal System <info@pascalsystem.pl>
|
| 6 |
+
* @version 1.0.0
|
| 7 |
+
*/
|
| 8 |
+
|
| 9 |
+
/**
|
| 10 |
+
* Pascal Head combine
|
| 11 |
+
*
|
| 12 |
+
* @category PSystem
|
| 13 |
+
* @package PSystem_OptimizationHead
|
| 14 |
+
* @author Pascal System <info@pascalsystem.pl>
|
| 15 |
+
* @version 1.0.0
|
| 16 |
+
*/
|
| 17 |
+
class PSystem_OptimizationHead_Block_Head extends PSystem_OptimizationHead_Block_Head_Abstract {
|
| 18 |
+
/**
|
| 19 |
+
* Get HEAD HTML with CSS/JS/RSS definitions
|
| 20 |
+
* (actually it also renders other elements, TODO: fix it up or rename this method)
|
| 21 |
+
*
|
| 22 |
+
* @return string
|
| 23 |
+
*/
|
| 24 |
+
public function getCssJsHtml() {
|
| 25 |
+
if (Mage::getStoreConfig('psoptimizationhead/extension/disable')) {
|
| 26 |
+
return parent::getCssJsHtml();
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
// separate items by types
|
| 30 |
+
$lines = array();
|
| 31 |
+
foreach ($this->_data['items'] as $item) {
|
| 32 |
+
if (!is_null($item['cond']) && !$this->getData($item['cond']) || !isset($item['name'])) {
|
| 33 |
+
continue;
|
| 34 |
+
}
|
| 35 |
+
$if = !empty($item['if']) ? $item['if'] : '';
|
| 36 |
+
$params = !empty($item['params']) ? $item['params'] : '';
|
| 37 |
+
switch ($item['type']) {
|
| 38 |
+
case 'js': // js/*.js
|
| 39 |
+
case 'skin_js': // skin/*/*.js
|
| 40 |
+
case 'js_css': // js/*.css
|
| 41 |
+
case 'skin_css': // skin/*/*.css
|
| 42 |
+
$lines[$if][$item['type']][$params][$item['name']] = $item['name'];
|
| 43 |
+
break;
|
| 44 |
+
default:
|
| 45 |
+
$this->_separateOtherHtmlHeadElements($lines, $if, $item['type'], $params, $item['name'], $item);
|
| 46 |
+
break;
|
| 47 |
+
}
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
//prepare HTML
|
| 51 |
+
$shouldMergeJs = Mage::getStoreConfigFlag('dev/js/merge_files');
|
| 52 |
+
$shouldMergeCss = Mage::getStoreConfigFlag('dev/css/merge_css_files');
|
| 53 |
+
$html = '';
|
| 54 |
+
|
| 55 |
+
$designPackage = Mage::getDesign();
|
| 56 |
+
foreach ($lines as $cond => $types) {
|
| 57 |
+
foreach ($types as $type => $params) {
|
| 58 |
+
foreach ($params as $param => $items) {
|
| 59 |
+
foreach ($items as $itemKey => $itemVal) {
|
| 60 |
+
if ($type == 'js') {
|
| 61 |
+
if (!isset($lines[$cond][$type][$param]['combine']))
|
| 62 |
+
$lines[$cond][$type][$param]['combine'] = '';
|
| 63 |
+
|
| 64 |
+
if ($lines[$cond][$type][$param]['combine']) {
|
| 65 |
+
$lines[$cond][$type][$param]['combine'].= ','.$itemVal;
|
| 66 |
+
} else {
|
| 67 |
+
$lines[$cond][$type][$param]['combine'].= 'file='.$itemVal;
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
unset($lines[$cond][$type][$param][$itemKey]);
|
| 71 |
+
} elseif ($type == 'skin_js') {
|
| 72 |
+
if (!isset($lines[$cond][$type][$param]['combineskin']))
|
| 73 |
+
$lines[$cond][$type][$param]['combineskin'] = '';
|
| 74 |
+
|
| 75 |
+
if ($lines[$cond][$type][$param]['combineskin']) {
|
| 76 |
+
$lines[$cond][$type][$param]['combineskin'].= ','.$this->getSkinFile($designPackage,$itemVal);
|
| 77 |
+
} else {
|
| 78 |
+
$lines[$cond][$type][$param]['combineskin'].= 'file='.$this->getSkinFile($designPackage,$itemVal);
|
| 79 |
+
}
|
| 80 |
+
unset($lines[$cond][$type][$param][$itemKey]);
|
| 81 |
+
} elseif ($type == 'js_css') {
|
| 82 |
+
if (!isset($lines[$cond][$type][$param]['combinecss']))
|
| 83 |
+
$lines[$cond][$type][$param]['combinecss'] = '';
|
| 84 |
+
|
| 85 |
+
if ($lines[$cond][$type][$param]['combinecss']) {
|
| 86 |
+
$lines[$cond][$type][$param]['combinecss'].= ','.$itemVal;
|
| 87 |
+
} else {
|
| 88 |
+
$lines[$cond][$type][$param]['combinecss'].= 'file='.$itemVal;
|
| 89 |
+
}
|
| 90 |
+
unset($lines[$cond][$type][$param][$itemKey]);
|
| 91 |
+
} elseif ($type == 'skin_css') {
|
| 92 |
+
if (!isset($lines[$cond][$type][$param]['combineskincss']))
|
| 93 |
+
$lines[$cond][$type][$param]['combineskincss'] = '';
|
| 94 |
+
|
| 95 |
+
if ($lines[$cond][$type][$param]['combineskincss']) {
|
| 96 |
+
$lines[$cond][$type][$param]['combineskincss'].= ','.$this->getSkinFile($designPackage,$itemVal);
|
| 97 |
+
} else {
|
| 98 |
+
$lines[$cond][$type][$param]['combineskincss'].= 'file='.$this->getSkinFile($designPackage,$itemVal);
|
| 99 |
+
}
|
| 100 |
+
unset($lines[$cond][$type][$param][$itemKey]);
|
| 101 |
+
}
|
| 102 |
+
}
|
| 103 |
+
}
|
| 104 |
+
}
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
foreach ($lines as $if => $items) {
|
| 108 |
+
if (empty($items)) {
|
| 109 |
+
continue;
|
| 110 |
+
}
|
| 111 |
+
if (!empty($if)) {
|
| 112 |
+
$html .= '<!--[if '.$if.']>'."\n";
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
// static and skin css
|
| 116 |
+
$html .= $this->_prepareStaticAndSkinElements('<link rel="stylesheet" type="text/css" href="%s"%s />' . "\n",
|
| 117 |
+
empty($items['js_css']) ? array() : $items['js_css'],
|
| 118 |
+
empty($items['skin_css']) ? array() : $items['skin_css'],
|
| 119 |
+
$shouldMergeCss ? array(Mage::getDesign(), 'getMergedCssUrl') : null
|
| 120 |
+
);
|
| 121 |
+
|
| 122 |
+
// static and skin javascripts
|
| 123 |
+
$html .= $this->_prepareStaticAndSkinElements('<script type="text/javascript" src="%s"%s></script>' . "\n",
|
| 124 |
+
empty($items['js']) ? array() : $items['js'],
|
| 125 |
+
empty($items['skin_js']) ? array() : $items['skin_js'],
|
| 126 |
+
$shouldMergeJs ? array(Mage::getDesign(), 'getMergedJsUrl') : null
|
| 127 |
+
);
|
| 128 |
+
|
| 129 |
+
// other stuff
|
| 130 |
+
if (!empty($items['other'])) {
|
| 131 |
+
$html .= $this->_prepareOtherHtmlHeadElements($items['other']) . "\n";
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
if (!empty($if)) {
|
| 135 |
+
$html .= '<![endif]-->'."\n";
|
| 136 |
+
}
|
| 137 |
+
}
|
| 138 |
+
return $html;
|
| 139 |
+
}
|
| 140 |
+
|
| 141 |
+
/**
|
| 142 |
+
* Merge static and skin files of the same format into 1 set of HEAD directives or even into 1 directive
|
| 143 |
+
*
|
| 144 |
+
* Will attempt to merge into 1 directive, if merging callback is provided. In this case it will generate
|
| 145 |
+
* filenames, rather than render urls.
|
| 146 |
+
* The merger callback is responsible for checking whether files exist, merging them and giving result URL
|
| 147 |
+
*
|
| 148 |
+
* @param string $format - HTML element format for sprintf('<element src="%s"%s />', $src, $params)
|
| 149 |
+
* @param array $staticItems - array of relative names of static items to be grabbed from js/ folder
|
| 150 |
+
* @param array $skinItems - array of relative names of skin items to be found in skins according to design config
|
| 151 |
+
* @param callback $mergeCallback
|
| 152 |
+
* @return string
|
| 153 |
+
*/
|
| 154 |
+
protected function &_prepareStaticAndSkinElements($format, array $staticItems, array $skinItems, $mergeCallback = null) {
|
| 155 |
+
$designPackage = Mage::getDesign();
|
| 156 |
+
$baseJsUrl = Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_JS);
|
| 157 |
+
|
| 158 |
+
$additionalParams = '';
|
| 159 |
+
if ($timeRefreshing = intval(Mage::getStoreConfig('psoptimizationhead/compress/refresh'))) {
|
| 160 |
+
$additionalParams.= 'refresh='.$timeRefreshing.'&';
|
| 161 |
+
}
|
| 162 |
+
if (Mage::getStoreConfig('psoptimizationhead/compress/nocache')) {
|
| 163 |
+
$additionalParams.= 'nocache=1&';
|
| 164 |
+
}
|
| 165 |
+
if (Mage::getStoreConfig('psoptimizationhead/compress/gzipoff')) {
|
| 166 |
+
$additionalParams.= 'gzipoff=1&';
|
| 167 |
+
}
|
| 168 |
+
if (Mage::getStoreConfig('psoptimizationhead/compress/minifyoff')) {
|
| 169 |
+
$additionalParams.= 'minoff=1&';
|
| 170 |
+
}
|
| 171 |
+
|
| 172 |
+
|
| 173 |
+
$baseCombineUrl = $baseJsUrl.'pascalsystem/combine.php?type=js&'.$additionalParams;
|
| 174 |
+
$baseCombineSkinUrl = $baseJsUrl.'pascalsystem/combine.php?type=jsskin&'.$additionalParams;
|
| 175 |
+
$baseCombineCss = $baseJsUrl.'pascalsystem/combine.php?type=css&'.$additionalParams;
|
| 176 |
+
$baseCombineSkinCss = $baseJsUrl.'pascalsystem/combine.php?type=cssskin&'.$additionalParams;
|
| 177 |
+
$items = array();
|
| 178 |
+
if ($mergeCallback && !is_callable($mergeCallback)) {
|
| 179 |
+
$mergeCallback = null;
|
| 180 |
+
}
|
| 181 |
+
|
| 182 |
+
// get static files from the js folder, no need in lookups
|
| 183 |
+
foreach ($staticItems as $params => $rows) {
|
| 184 |
+
foreach ($rows as $key => $name) {
|
| 185 |
+
if ($key == 'combine') {
|
| 186 |
+
$items[$params][] = $baseCombineUrl.$name;
|
| 187 |
+
} elseif ($key == 'combinecss') {
|
| 188 |
+
$items[$params][] = $baseCombineCss.$name;
|
| 189 |
+
} else {
|
| 190 |
+
$items[$params][] = $mergeCallback ? Mage::getBaseDir() . DS . 'js' . DS . $name : $baseJsUrl . $name;
|
| 191 |
+
}
|
| 192 |
+
}
|
| 193 |
+
}
|
| 194 |
+
|
| 195 |
+
// lookup each file basing on current theme configuration
|
| 196 |
+
foreach ($skinItems as $params => $rows) {
|
| 197 |
+
foreach ($rows as $key => $name) {
|
| 198 |
+
if ($key == 'combineskin') {
|
| 199 |
+
$items[$params][] = $baseCombineSkinUrl.$name;
|
| 200 |
+
} elseif ($key == 'combineskincss') {
|
| 201 |
+
$items[$params][] = $baseCombineSkinCss.$name;
|
| 202 |
+
} else {
|
| 203 |
+
$items[$params][] = $mergeCallback ? $designPackage->getFilename($name, array('_type' => 'skin')):$designPackage->getSkinUrl($name, array());
|
| 204 |
+
}
|
| 205 |
+
}
|
| 206 |
+
}
|
| 207 |
+
|
| 208 |
+
$html = '';
|
| 209 |
+
foreach ($items as $params => $rows) {
|
| 210 |
+
// attempt to merge
|
| 211 |
+
$mergedUrl = false;
|
| 212 |
+
if ($mergeCallback) {
|
| 213 |
+
$mergedUrl = call_user_func($mergeCallback, $rows);
|
| 214 |
+
}
|
| 215 |
+
// render elements
|
| 216 |
+
$params = trim($params);
|
| 217 |
+
$params = $params ? ' ' . $params : '';
|
| 218 |
+
if ($mergedUrl) {
|
| 219 |
+
$html .= sprintf($format, $mergedUrl, $params);
|
| 220 |
+
} else {
|
| 221 |
+
foreach ($rows as $src) {
|
| 222 |
+
$html .= sprintf($format, $src, $params);
|
| 223 |
+
}
|
| 224 |
+
}
|
| 225 |
+
}
|
| 226 |
+
return $html;
|
| 227 |
+
}
|
| 228 |
+
|
| 229 |
+
/**
|
| 230 |
+
* Create skin url and cut base skin url
|
| 231 |
+
*
|
| 232 |
+
* @param Mage_Core_Model_Design_Package $designPackage
|
| 233 |
+
* @param string $fileName
|
| 234 |
+
* @return string
|
| 235 |
+
*/
|
| 236 |
+
protected function getSkinFile(Mage_Core_Model_Design_Package $designPackage, $fileName) {
|
| 237 |
+
return str_replace(Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_SKIN),'',$designPackage->getSkinUrl($fileName));
|
| 238 |
+
}
|
| 239 |
+
}
|
app/code/community/PSystem/OptimizationHead/Block/Head/Abstract.php
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
/**
|
| 3 |
+
* @category PSystem
|
| 4 |
+
* @package PSystem_OptimizationHead
|
| 5 |
+
* @author Pascal System <info@pascalsystem.pl>
|
| 6 |
+
* @version 1.0.0
|
| 7 |
+
*/
|
| 8 |
+
|
| 9 |
+
/**
|
| 10 |
+
* Pascal Head combine abstract
|
| 11 |
+
*
|
| 12 |
+
* @category PSystem
|
| 13 |
+
* @package PSystem_OptimizationHead
|
| 14 |
+
* @author Pascal System <info@pascalsystem.pl>
|
| 15 |
+
* @version 1.0.0
|
| 16 |
+
*/
|
| 17 |
+
$extClass = Mage::getStoreConfig('psoptimizationhead/extension/classname');
|
| 18 |
+
if (!$extClass) $extClass = 'Mage_Page_Block_Html_Head';
|
| 19 |
+
eval('abstract class PSystem_OptimizationHead_Block_Head_Abstract extends '.$extClass.' {}');
|
app/code/community/PSystem/OptimizationHead/Model/Observer.php
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
/**
|
| 3 |
+
* @category PSystem
|
| 4 |
+
* @package PSystem_OptimizationHead
|
| 5 |
+
* @author Pascal System <info@pascalsystem.pl>
|
| 6 |
+
* @version 1.0.0
|
| 7 |
+
*/
|
| 8 |
+
|
| 9 |
+
/**
|
| 10 |
+
* @see PSystemHtmlMinify
|
| 11 |
+
*/
|
| 12 |
+
require_once 'PSystem/OptimizationHead/lib/PSystemHtmlMinify.php';
|
| 13 |
+
|
| 14 |
+
/**
|
| 15 |
+
* Pascal Optimization observer
|
| 16 |
+
*
|
| 17 |
+
* @category PSystem
|
| 18 |
+
* @package PSystem_OptimizationHead
|
| 19 |
+
* @author Pascal System <info@pascalsystem.pl>
|
| 20 |
+
* @version 1.0.0
|
| 21 |
+
*/
|
| 22 |
+
class PSystem_OptimizationHead_Model_Observer {
|
| 23 |
+
/**
|
| 24 |
+
* Post dispatch proccess compress html file
|
| 25 |
+
*
|
| 26 |
+
* @param Varien_Event_Observer $event
|
| 27 |
+
*/
|
| 28 |
+
public function postDispatch(Varien_Event_Observer $event) {
|
| 29 |
+
if (Mage::getStoreConfig('psoptimizationhead/html/disable'))
|
| 30 |
+
return;
|
| 31 |
+
|
| 32 |
+
/* @var $controller Mage_Core_Controller_Varien_Action */
|
| 33 |
+
$controller = $event->getControllerAction();
|
| 34 |
+
$allHtml = $controller->getResponse()->getBody();
|
| 35 |
+
$allHtml = PSystemHtmlMinify::min($allHtml);
|
| 36 |
+
$controller->getResponse()->setBody($allHtml);
|
| 37 |
+
}
|
| 38 |
+
}
|
app/code/community/PSystem/OptimizationHead/etc/adminhtml.xml
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0"?>
|
| 2 |
+
<!--
|
| 3 |
+
/**
|
| 4 |
+
* @category PSystem
|
| 5 |
+
* @package PSystem_OptimizationHead
|
| 6 |
+
* @author Pascal System <info@pascalsystem.pl>
|
| 7 |
+
* @version 1.0.0
|
| 8 |
+
*/
|
| 9 |
+
-->
|
| 10 |
+
<config>
|
| 11 |
+
<acl>
|
| 12 |
+
<resources>
|
| 13 |
+
<admin>
|
| 14 |
+
<children>
|
| 15 |
+
<system>
|
| 16 |
+
<children>
|
| 17 |
+
<config>
|
| 18 |
+
<children>
|
| 19 |
+
<psoptimizationhead />
|
| 20 |
+
</children>
|
| 21 |
+
</config>
|
| 22 |
+
</children>
|
| 23 |
+
</system>
|
| 24 |
+
</children>
|
| 25 |
+
</admin>
|
| 26 |
+
</resources>
|
| 27 |
+
</acl>
|
| 28 |
+
</config>
|
app/code/community/PSystem/OptimizationHead/etc/config.xml
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0"?>
|
| 2 |
+
<!--
|
| 3 |
+
/**
|
| 4 |
+
* @category PSystem
|
| 5 |
+
* @package PSystem_OptimizationHead
|
| 6 |
+
* @author Pascal System <info@pascalsystem.pl>
|
| 7 |
+
* @version 1.0.0
|
| 8 |
+
*/
|
| 9 |
+
-->
|
| 10 |
+
<config>
|
| 11 |
+
<modules>
|
| 12 |
+
<PSystem_OptimizationHead>
|
| 13 |
+
<version>1.0.0</version>
|
| 14 |
+
</PSystem_OptimizationHead>
|
| 15 |
+
</modules>
|
| 16 |
+
<global>
|
| 17 |
+
<blocks>
|
| 18 |
+
<page>
|
| 19 |
+
<rewrite>
|
| 20 |
+
<html_head>PSystem_OptimizationHead_Block_Head</html_head>
|
| 21 |
+
</rewrite>
|
| 22 |
+
</page>
|
| 23 |
+
</blocks>
|
| 24 |
+
<models>
|
| 25 |
+
<psoptimizationhead>
|
| 26 |
+
<class>PSystem_OptimizationHead_Model</class>
|
| 27 |
+
</psoptimizationhead>
|
| 28 |
+
</models>
|
| 29 |
+
<events>
|
| 30 |
+
<controller_action_postdispatch>
|
| 31 |
+
<observers>
|
| 32 |
+
<psoptimizationhead_postdispatch>
|
| 33 |
+
<type>singleton</type>
|
| 34 |
+
<class>psoptimizationhead/observer</class>
|
| 35 |
+
<method>postDispatch</method>
|
| 36 |
+
</psoptimizationhead_postdispatch>
|
| 37 |
+
</observers>
|
| 38 |
+
</controller_action_postdispatch>
|
| 39 |
+
</events>
|
| 40 |
+
</global>
|
| 41 |
+
</config>
|
app/code/community/PSystem/OptimizationHead/etc/system.xml
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0"?>
|
| 2 |
+
<!--
|
| 3 |
+
/**
|
| 4 |
+
* @category PSystem
|
| 5 |
+
* @package PSystem_OptimizationHead
|
| 6 |
+
* @author Pascal System <info@pascalsystem.pl>
|
| 7 |
+
* @version 1.0.0
|
| 8 |
+
*/
|
| 9 |
+
-->
|
| 10 |
+
<config>
|
| 11 |
+
<sections>
|
| 12 |
+
<psoptimizationhead translate="label" module="psbase">
|
| 13 |
+
<label>Optimization Head</label>
|
| 14 |
+
<tab>psystem</tab>
|
| 15 |
+
<frontend_type>text</frontend_type>
|
| 16 |
+
<sort_order>10</sort_order>
|
| 17 |
+
<show_in_default>1</show_in_default>
|
| 18 |
+
<show_in_website>0</show_in_website>
|
| 19 |
+
<show_in_store>0</show_in_store>
|
| 20 |
+
<groups>
|
| 21 |
+
<extension translate="label">
|
| 22 |
+
<label>Head extension</label>
|
| 23 |
+
<show_in_default>1</show_in_default>
|
| 24 |
+
<show_in_website>0</show_in_website>
|
| 25 |
+
<show_in_store>0</show_in_store>
|
| 26 |
+
<sort_order>5</sort_order>
|
| 27 |
+
<fields>
|
| 28 |
+
<disable translate="label,comment">
|
| 29 |
+
<label>Disable module</label>
|
| 30 |
+
<comment><![CDATA[default: No]]></comment>
|
| 31 |
+
<frontend_type>select</frontend_type>
|
| 32 |
+
<source_model>adminhtml/system_config_source_yesno</source_model>
|
| 33 |
+
<sort_order>5</sort_order>
|
| 34 |
+
<show_in_default>1</show_in_default>
|
| 35 |
+
<show_in_website>0</show_in_website>
|
| 36 |
+
<show_in_store>0</show_in_store>
|
| 37 |
+
</disable>
|
| 38 |
+
<classname translate="label,comment">
|
| 39 |
+
<label>Block extension class name</label>
|
| 40 |
+
<comment><![CDATA[default: Mage_Page_Block_Html_Head]]></comment>
|
| 41 |
+
<frontend_type>text</frontend_type>
|
| 42 |
+
<sort_order>10</sort_order>
|
| 43 |
+
<show_in_default>1</show_in_default>
|
| 44 |
+
<show_in_website>0</show_in_website>
|
| 45 |
+
<show_in_store>0</show_in_store>
|
| 46 |
+
</classname>
|
| 47 |
+
</fields>
|
| 48 |
+
</extension>
|
| 49 |
+
<compress>
|
| 50 |
+
<label>Compress JS/CSS</label>
|
| 51 |
+
<show_in_default>1</show_in_default>
|
| 52 |
+
<show_in_website>0</show_in_website>
|
| 53 |
+
<show_in_store>0</show_in_store>
|
| 54 |
+
<sort_order>10</sort_order>
|
| 55 |
+
<fields>
|
| 56 |
+
<refresh>
|
| 57 |
+
<label>Refresh time (second)</label>
|
| 58 |
+
<comment><![CDATA[default: 3days - 259200]]></comment>
|
| 59 |
+
<frontend_type>text</frontend_type>
|
| 60 |
+
<sort_order>5</sort_order>
|
| 61 |
+
<show_in_default>1</show_in_default>
|
| 62 |
+
<show_in_website>0</show_in_website>
|
| 63 |
+
<show_in_store>0</show_in_store>
|
| 64 |
+
</refresh>
|
| 65 |
+
<nocache translate="label,comment">
|
| 66 |
+
<label>No cache file</label>
|
| 67 |
+
<comment><![CDATA[default: No]]></comment>
|
| 68 |
+
<frontend_type>select</frontend_type>
|
| 69 |
+
<source_model>adminhtml/system_config_source_yesno</source_model>
|
| 70 |
+
<sort_order>10</sort_order>
|
| 71 |
+
<show_in_default>1</show_in_default>
|
| 72 |
+
<show_in_website>0</show_in_website>
|
| 73 |
+
<show_in_store>0</show_in_store>
|
| 74 |
+
</nocache>
|
| 75 |
+
<gzipoff translate="label,comment">
|
| 76 |
+
<label>GZip handler off</label>
|
| 77 |
+
<comment><![CDATA[default: No]]></comment>
|
| 78 |
+
<frontend_type>select</frontend_type>
|
| 79 |
+
<source_model>adminhtml/system_config_source_yesno</source_model>
|
| 80 |
+
<sort_order>15</sort_order>
|
| 81 |
+
<show_in_default>1</show_in_default>
|
| 82 |
+
<show_in_website>0</show_in_website>
|
| 83 |
+
<show_in_store>0</show_in_store>
|
| 84 |
+
</gzipoff>
|
| 85 |
+
<minifyoff>
|
| 86 |
+
<label>Minify off</label>
|
| 87 |
+
<comment><![CDATA[default: No]]></comment>
|
| 88 |
+
<frontend_type>select</frontend_type>
|
| 89 |
+
<source_model>adminhtml/system_config_source_yesno</source_model>
|
| 90 |
+
<sort_order>20</sort_order>
|
| 91 |
+
<show_in_default>1</show_in_default>
|
| 92 |
+
<show_in_website>0</show_in_website>
|
| 93 |
+
<show_in_store>0</show_in_store>
|
| 94 |
+
</minifyoff>
|
| 95 |
+
</fields>
|
| 96 |
+
</compress>
|
| 97 |
+
<html>
|
| 98 |
+
<label>HTML</label>
|
| 99 |
+
<show_in_default>1</show_in_default>
|
| 100 |
+
<show_in_website>0</show_in_website>
|
| 101 |
+
<show_in_store>0</show_in_store>
|
| 102 |
+
<sort_order>15</sort_order>
|
| 103 |
+
<fields>
|
| 104 |
+
<disable>
|
| 105 |
+
<label>Compress html off</label>
|
| 106 |
+
<comment><![CDATA[default: no]]></comment>
|
| 107 |
+
<frontend_type>select</frontend_type>
|
| 108 |
+
<source_model>adminhtml/system_config_source_yesno</source_model>
|
| 109 |
+
<sort_order>5</sort_order>
|
| 110 |
+
<show_in_default>1</show_in_default>
|
| 111 |
+
<show_in_website>0</show_in_website>
|
| 112 |
+
<show_in_store>0</show_in_store>
|
| 113 |
+
</disable>
|
| 114 |
+
</fields>
|
| 115 |
+
</html>
|
| 116 |
+
</groups>
|
| 117 |
+
</psoptimizationhead>
|
| 118 |
+
</sections>
|
| 119 |
+
</config>
|
app/code/community/PSystem/OptimizationHead/lib/JSMin.php
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
/**
|
| 3 |
+
* jsmin.php - PHP implementation of Douglas Crockford's JSMin.
|
| 4 |
+
*
|
| 5 |
+
* This is a direct port of jsmin.c to PHP with a few PHP performance tweaks and
|
| 6 |
+
* modifications to preserve some comments (see below). Also, rather than using
|
| 7 |
+
* stdin/stdout, JSMin::minify() accepts a string as input and returns another
|
| 8 |
+
* string as output.
|
| 9 |
+
*
|
| 10 |
+
* Comments containing IE conditional compilation are preserved, as are multi-line
|
| 11 |
+
* comments that begin with "/*!" (for documentation purposes). In the latter case
|
| 12 |
+
* newlines are inserted around the comment to enhance readability.
|
| 13 |
+
*
|
| 14 |
+
* PHP 5 or higher is required.
|
| 15 |
+
*
|
| 16 |
+
* Permission is hereby granted to use this version of the library under the
|
| 17 |
+
* same terms as jsmin.c, which has the following license:
|
| 18 |
+
*
|
| 19 |
+
* --
|
| 20 |
+
* Copyright (c) 2002 Douglas Crockford (www.crockford.com)
|
| 21 |
+
*
|
| 22 |
+
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
| 23 |
+
* this software and associated documentation files (the "Software"), to deal in
|
| 24 |
+
* the Software without restriction, including without limitation the rights to
|
| 25 |
+
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
| 26 |
+
* of the Software, and to permit persons to whom the Software is furnished to do
|
| 27 |
+
* so, subject to the following conditions:
|
| 28 |
+
*
|
| 29 |
+
* The above copyright notice and this permission notice shall be included in all
|
| 30 |
+
* copies or substantial portions of the Software.
|
| 31 |
+
*
|
| 32 |
+
* The Software shall be used for Good, not Evil.
|
| 33 |
+
*
|
| 34 |
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 35 |
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 36 |
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 37 |
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 38 |
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 39 |
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 40 |
+
* SOFTWARE.
|
| 41 |
+
* --
|
| 42 |
+
*
|
| 43 |
+
* @package JSMin
|
| 44 |
+
* @author Ryan Grove <ryan@wonko.com> (PHP port)
|
| 45 |
+
* @author Steve Clay <steve@mrclay.org> (modifications + cleanup)
|
| 46 |
+
* @author Andrea Giammarchi <http://www.3site.eu> (spaceBeforeRegExp)
|
| 47 |
+
* @copyright 2002 Douglas Crockford <douglas@crockford.com> (jsmin.c)
|
| 48 |
+
* @copyright 2008 Ryan Grove <ryan@wonko.com> (PHP port)
|
| 49 |
+
* @license http://opensource.org/licenses/mit-license.php MIT License
|
| 50 |
+
* @link http://code.google.com/p/jsmin-php/
|
| 51 |
+
*/
|
| 52 |
+
|
| 53 |
+
class JSMin {
|
| 54 |
+
const ORD_LF = 10;
|
| 55 |
+
const ORD_SPACE = 32;
|
| 56 |
+
const ACTION_KEEP_A = 1;
|
| 57 |
+
const ACTION_DELETE_A = 2;
|
| 58 |
+
const ACTION_DELETE_A_B = 3;
|
| 59 |
+
|
| 60 |
+
protected $a = "\n";
|
| 61 |
+
protected $b = '';
|
| 62 |
+
protected $input = '';
|
| 63 |
+
protected $inputIndex = 0;
|
| 64 |
+
protected $inputLength = 0;
|
| 65 |
+
protected $lookAhead = null;
|
| 66 |
+
protected $output = '';
|
| 67 |
+
|
| 68 |
+
/**
|
| 69 |
+
* Minify Javascript
|
| 70 |
+
*
|
| 71 |
+
* @param string $js Javascript to be minified
|
| 72 |
+
* @return string
|
| 73 |
+
*/
|
| 74 |
+
public static function minify($js)
|
| 75 |
+
{
|
| 76 |
+
$jsmin = new JSMin($js);
|
| 77 |
+
return $jsmin->min();
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
/**
|
| 81 |
+
* Setup process
|
| 82 |
+
*/
|
| 83 |
+
public function __construct($input)
|
| 84 |
+
{
|
| 85 |
+
$this->input = str_replace("\r\n", "\n", $input);
|
| 86 |
+
$this->inputLength = strlen($this->input);
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
/**
|
| 90 |
+
* Perform minification, return result
|
| 91 |
+
*/
|
| 92 |
+
public function min()
|
| 93 |
+
{
|
| 94 |
+
if ($this->output !== '') { // min already run
|
| 95 |
+
return $this->output;
|
| 96 |
+
}
|
| 97 |
+
$this->action(self::ACTION_DELETE_A_B);
|
| 98 |
+
|
| 99 |
+
while ($this->a !== null) {
|
| 100 |
+
// determine next command
|
| 101 |
+
$command = self::ACTION_KEEP_A; // default
|
| 102 |
+
if ($this->a === ' ') {
|
| 103 |
+
if (! $this->isAlphaNum($this->b)) {
|
| 104 |
+
$command = self::ACTION_DELETE_A;
|
| 105 |
+
}
|
| 106 |
+
} elseif ($this->a === "\n") {
|
| 107 |
+
if ($this->b === ' ') {
|
| 108 |
+
$command = self::ACTION_DELETE_A_B;
|
| 109 |
+
} elseif (false === strpos('{[(+-', $this->b)
|
| 110 |
+
&& ! $this->isAlphaNum($this->b)) {
|
| 111 |
+
$command = self::ACTION_DELETE_A;
|
| 112 |
+
}
|
| 113 |
+
} elseif (! $this->isAlphaNum($this->a)) {
|
| 114 |
+
if ($this->b === ' '
|
| 115 |
+
|| ($this->b === "\n"
|
| 116 |
+
&& (false === strpos('}])+-"\'', $this->a)))) {
|
| 117 |
+
$command = self::ACTION_DELETE_A_B;
|
| 118 |
+
}
|
| 119 |
+
}
|
| 120 |
+
$this->action($command);
|
| 121 |
+
}
|
| 122 |
+
$this->output = trim($this->output);
|
| 123 |
+
return $this->output;
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
/**
|
| 127 |
+
* ACTION_KEEP_A = Output A. Copy B to A. Get the next B.
|
| 128 |
+
* ACTION_DELETE_A = Copy B to A. Get the next B.
|
| 129 |
+
* ACTION_DELETE_A_B = Get the next B.
|
| 130 |
+
*/
|
| 131 |
+
protected function action($command)
|
| 132 |
+
{
|
| 133 |
+
switch ($command) {
|
| 134 |
+
case self::ACTION_KEEP_A:
|
| 135 |
+
$this->output .= $this->a;
|
| 136 |
+
// fallthrough
|
| 137 |
+
case self::ACTION_DELETE_A:
|
| 138 |
+
$this->a = $this->b;
|
| 139 |
+
if ($this->a === "'" || $this->a === '"') { // string literal
|
| 140 |
+
$str = $this->a; // in case needed for exception
|
| 141 |
+
while (true) {
|
| 142 |
+
$this->output .= $this->a;
|
| 143 |
+
$this->a = $this->get();
|
| 144 |
+
if ($this->a === $this->b) { // end quote
|
| 145 |
+
break;
|
| 146 |
+
}
|
| 147 |
+
if (ord($this->a) <= self::ORD_LF) {
|
| 148 |
+
throw new JSMin_UnterminatedStringException(
|
| 149 |
+
'Unterminated String: ' . var_export($str, true));
|
| 150 |
+
}
|
| 151 |
+
$str .= $this->a;
|
| 152 |
+
if ($this->a === '\\') {
|
| 153 |
+
$this->output .= $this->a;
|
| 154 |
+
$this->a = $this->get();
|
| 155 |
+
$str .= $this->a;
|
| 156 |
+
}
|
| 157 |
+
}
|
| 158 |
+
}
|
| 159 |
+
// fallthrough
|
| 160 |
+
case self::ACTION_DELETE_A_B:
|
| 161 |
+
$this->b = $this->next();
|
| 162 |
+
if ($this->b === '/' && $this->isRegexpLiteral()) { // RegExp literal
|
| 163 |
+
$this->output .= $this->a . $this->b;
|
| 164 |
+
$pattern = '/'; // in case needed for exception
|
| 165 |
+
while (true) {
|
| 166 |
+
$this->a = $this->get();
|
| 167 |
+
$pattern .= $this->a;
|
| 168 |
+
if ($this->a === '/') { // end pattern
|
| 169 |
+
break; // while (true)
|
| 170 |
+
} elseif ($this->a === '\\') {
|
| 171 |
+
$this->output .= $this->a;
|
| 172 |
+
$this->a = $this->get();
|
| 173 |
+
$pattern .= $this->a;
|
| 174 |
+
} elseif (ord($this->a) <= self::ORD_LF) {
|
| 175 |
+
throw new JSMin_UnterminatedRegExpException(
|
| 176 |
+
'Unterminated RegExp: '. var_export($pattern, true));
|
| 177 |
+
}
|
| 178 |
+
$this->output .= $this->a;
|
| 179 |
+
}
|
| 180 |
+
$this->b = $this->next();
|
| 181 |
+
}
|
| 182 |
+
// end case ACTION_DELETE_A_B
|
| 183 |
+
}
|
| 184 |
+
}
|
| 185 |
+
|
| 186 |
+
protected function isRegexpLiteral()
|
| 187 |
+
{
|
| 188 |
+
if (false !== strpos("\n{;(,=:[!&|?", $this->a)) { // we aren't dividing
|
| 189 |
+
return true;
|
| 190 |
+
}
|
| 191 |
+
if (' ' === $this->a) {
|
| 192 |
+
$length = strlen($this->output);
|
| 193 |
+
if ($length < 2) { // weird edge case
|
| 194 |
+
return true;
|
| 195 |
+
}
|
| 196 |
+
// you can't divide a keyword
|
| 197 |
+
if (preg_match('/(?:case|else|in|return|typeof)$/', $this->output, $m)) {
|
| 198 |
+
if ($this->output === $m[0]) { // odd but could happen
|
| 199 |
+
return true;
|
| 200 |
+
}
|
| 201 |
+
// make sure it's a keyword, not end of an identifier
|
| 202 |
+
$charBeforeKeyword = substr($this->output, $length - strlen($m[0]) - 1, 1);
|
| 203 |
+
if (! $this->isAlphaNum($charBeforeKeyword)) {
|
| 204 |
+
return true;
|
| 205 |
+
}
|
| 206 |
+
}
|
| 207 |
+
}
|
| 208 |
+
return false;
|
| 209 |
+
}
|
| 210 |
+
|
| 211 |
+
/**
|
| 212 |
+
* Get next char. Convert ctrl char to space.
|
| 213 |
+
*/
|
| 214 |
+
protected function get()
|
| 215 |
+
{
|
| 216 |
+
$c = $this->lookAhead;
|
| 217 |
+
$this->lookAhead = null;
|
| 218 |
+
if ($c === null) {
|
| 219 |
+
if ($this->inputIndex < $this->inputLength) {
|
| 220 |
+
$c = $this->input[$this->inputIndex];
|
| 221 |
+
$this->inputIndex += 1;
|
| 222 |
+
} else {
|
| 223 |
+
return null;
|
| 224 |
+
}
|
| 225 |
+
}
|
| 226 |
+
if ($c === "\r" || $c === "\n") {
|
| 227 |
+
return "\n";
|
| 228 |
+
}
|
| 229 |
+
if (ord($c) < self::ORD_SPACE) { // control char
|
| 230 |
+
return ' ';
|
| 231 |
+
}
|
| 232 |
+
return $c;
|
| 233 |
+
}
|
| 234 |
+
|
| 235 |
+
/**
|
| 236 |
+
* Get next char. If is ctrl character, translate to a space or newline.
|
| 237 |
+
*/
|
| 238 |
+
protected function peek()
|
| 239 |
+
{
|
| 240 |
+
$this->lookAhead = $this->get();
|
| 241 |
+
return $this->lookAhead;
|
| 242 |
+
}
|
| 243 |
+
|
| 244 |
+
/**
|
| 245 |
+
* Is $c a letter, digit, underscore, dollar sign, escape, or non-ASCII?
|
| 246 |
+
*/
|
| 247 |
+
protected function isAlphaNum($c)
|
| 248 |
+
{
|
| 249 |
+
return (preg_match('/^[0-9a-zA-Z_\\$\\\\]$/', $c) || ord($c) > 126);
|
| 250 |
+
}
|
| 251 |
+
|
| 252 |
+
protected function singleLineComment()
|
| 253 |
+
{
|
| 254 |
+
$comment = '';
|
| 255 |
+
while (true) {
|
| 256 |
+
$get = $this->get();
|
| 257 |
+
$comment .= $get;
|
| 258 |
+
if (ord($get) <= self::ORD_LF) { // EOL reached
|
| 259 |
+
// if IE conditional comment
|
| 260 |
+
if (preg_match('/^\\/@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
|
| 261 |
+
return "/{$comment}";
|
| 262 |
+
}
|
| 263 |
+
return $get;
|
| 264 |
+
}
|
| 265 |
+
}
|
| 266 |
+
}
|
| 267 |
+
|
| 268 |
+
protected function multipleLineComment()
|
| 269 |
+
{
|
| 270 |
+
$this->get();
|
| 271 |
+
$comment = '';
|
| 272 |
+
while (true) {
|
| 273 |
+
$get = $this->get();
|
| 274 |
+
if ($get === '*') {
|
| 275 |
+
if ($this->peek() === '/') { // end of comment reached
|
| 276 |
+
$this->get();
|
| 277 |
+
// if comment preserved by YUI Compressor
|
| 278 |
+
if (0 === strpos($comment, '!')) {
|
| 279 |
+
return "\n/*" . substr($comment, 1) . "*/\n";
|
| 280 |
+
}
|
| 281 |
+
// if IE conditional comment
|
| 282 |
+
if (preg_match('/^@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
|
| 283 |
+
return "/*{$comment}*/";
|
| 284 |
+
}
|
| 285 |
+
return ' ';
|
| 286 |
+
}
|
| 287 |
+
} elseif ($get === null) {
|
| 288 |
+
throw new JSMin_UnterminatedCommentException('Unterminated Comment: ' . var_export('/*' . $comment, true));
|
| 289 |
+
}
|
| 290 |
+
$comment .= $get;
|
| 291 |
+
}
|
| 292 |
+
}
|
| 293 |
+
|
| 294 |
+
/**
|
| 295 |
+
* Get the next character, skipping over comments.
|
| 296 |
+
* Some comments may be preserved.
|
| 297 |
+
*/
|
| 298 |
+
protected function next()
|
| 299 |
+
{
|
| 300 |
+
$get = $this->get();
|
| 301 |
+
if ($get !== '/') {
|
| 302 |
+
return $get;
|
| 303 |
+
}
|
| 304 |
+
switch ($this->peek()) {
|
| 305 |
+
case '/': return $this->singleLineComment();
|
| 306 |
+
case '*': return $this->multipleLineComment();
|
| 307 |
+
default: return $get;
|
| 308 |
+
}
|
| 309 |
+
}
|
| 310 |
+
}
|
| 311 |
+
|
| 312 |
+
class JSMin_UnterminatedStringException extends Exception {}
|
| 313 |
+
class JSMin_UnterminatedCommentException extends Exception {}
|
| 314 |
+
class JSMin_UnterminatedRegExpException extends Exception {}
|
app/code/community/PSystem/OptimizationHead/lib/Minify/CSS.php
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
/**
|
| 3 |
+
* Class Minify_CSS
|
| 4 |
+
* @package Minify
|
| 5 |
+
*/
|
| 6 |
+
|
| 7 |
+
/**
|
| 8 |
+
* Minify CSS
|
| 9 |
+
*
|
| 10 |
+
* This class uses Minify_CSS_Compressor and Minify_CSS_UriRewriter to
|
| 11 |
+
* minify CSS and rewrite relative URIs.
|
| 12 |
+
*
|
| 13 |
+
* @package Minify
|
| 14 |
+
* @author Stephen Clay <steve@mrclay.org>
|
| 15 |
+
* @author http://code.google.com/u/1stvamp/ (Issue 64 patch)
|
| 16 |
+
*/
|
| 17 |
+
class Minify_CSS {
|
| 18 |
+
|
| 19 |
+
/**
|
| 20 |
+
* Minify a CSS string
|
| 21 |
+
*
|
| 22 |
+
* @param string $css
|
| 23 |
+
*
|
| 24 |
+
* @param array $options available options:
|
| 25 |
+
*
|
| 26 |
+
* 'preserveComments': (default true) multi-line comments that begin
|
| 27 |
+
* with "/*!" will be preserved with newlines before and after to
|
| 28 |
+
* enhance readability.
|
| 29 |
+
*
|
| 30 |
+
* 'prependRelativePath': (default null) if given, this string will be
|
| 31 |
+
* prepended to all relative URIs in import/url declarations
|
| 32 |
+
*
|
| 33 |
+
* 'currentDir': (default null) if given, this is assumed to be the
|
| 34 |
+
* directory of the current CSS file. Using this, minify will rewrite
|
| 35 |
+
* all relative URIs in import/url declarations to correctly point to
|
| 36 |
+
* the desired files. For this to work, the files *must* exist and be
|
| 37 |
+
* visible by the PHP process.
|
| 38 |
+
*
|
| 39 |
+
* 'symlinks': (default = array()) If the CSS file is stored in
|
| 40 |
+
* a symlink-ed directory, provide an array of link paths to
|
| 41 |
+
* target paths, where the link paths are within the document root. Because
|
| 42 |
+
* paths need to be normalized for this to work, use "//" to substitute
|
| 43 |
+
* the doc root in the link paths (the array keys). E.g.:
|
| 44 |
+
* <code>
|
| 45 |
+
* array('//symlink' => '/real/target/path') // unix
|
| 46 |
+
* array('//static' => 'D:\\staticStorage') // Windows
|
| 47 |
+
* </code>
|
| 48 |
+
*
|
| 49 |
+
* @return string
|
| 50 |
+
*/
|
| 51 |
+
public static function minify($css, $options = array())
|
| 52 |
+
{
|
| 53 |
+
require_once 'Minify/CSS/Compressor.php';
|
| 54 |
+
if (isset($options['preserveComments'])
|
| 55 |
+
&& !$options['preserveComments']) {
|
| 56 |
+
$css = Minify_CSS_Compressor::process($css, $options);
|
| 57 |
+
} else {
|
| 58 |
+
require_once 'Minify/CommentPreserver.php';
|
| 59 |
+
$css = Minify_CommentPreserver::process(
|
| 60 |
+
$css
|
| 61 |
+
,array('Minify_CSS_Compressor', 'process')
|
| 62 |
+
,array($options)
|
| 63 |
+
);
|
| 64 |
+
}
|
| 65 |
+
if (! isset($options['currentDir']) && ! isset($options['prependRelativePath'])) {
|
| 66 |
+
return $css;
|
| 67 |
+
}
|
| 68 |
+
require_once 'Minify/CSS/UriRewriter.php';
|
| 69 |
+
if (isset($options['currentDir'])) {
|
| 70 |
+
return Minify_CSS_UriRewriter::rewrite(
|
| 71 |
+
$css
|
| 72 |
+
,$options['currentDir']
|
| 73 |
+
,isset($options['docRoot']) ? $options['docRoot'] : $_SERVER['DOCUMENT_ROOT']
|
| 74 |
+
,isset($options['symlinks']) ? $options['symlinks'] : array()
|
| 75 |
+
);
|
| 76 |
+
} else {
|
| 77 |
+
return Minify_CSS_UriRewriter::prepend(
|
| 78 |
+
$css
|
| 79 |
+
,$options['prependRelativePath']
|
| 80 |
+
);
|
| 81 |
+
}
|
| 82 |
+
}
|
| 83 |
+
}
|
app/code/community/PSystem/OptimizationHead/lib/Minify/CSS/Compressor.php
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
/**
|
| 3 |
+
* Class Minify_CSS_Compressor
|
| 4 |
+
* @package Minify
|
| 5 |
+
*/
|
| 6 |
+
|
| 7 |
+
/**
|
| 8 |
+
* Compress CSS
|
| 9 |
+
*
|
| 10 |
+
* This is a heavy regex-based removal of whitespace, unnecessary
|
| 11 |
+
* comments and tokens, and some CSS value minimization, where practical.
|
| 12 |
+
* Many steps have been taken to avoid breaking comment-based hacks,
|
| 13 |
+
* including the ie5/mac filter (and its inversion), but expect tricky
|
| 14 |
+
* hacks involving comment tokens in 'content' value strings to break
|
| 15 |
+
* minimization badly. A test suite is available.
|
| 16 |
+
*
|
| 17 |
+
* @package Minify
|
| 18 |
+
* @author Stephen Clay <steve@mrclay.org>
|
| 19 |
+
* @author http://code.google.com/u/1stvamp/ (Issue 64 patch)
|
| 20 |
+
*/
|
| 21 |
+
class Minify_CSS_Compressor {
|
| 22 |
+
|
| 23 |
+
/**
|
| 24 |
+
* Minify a CSS string
|
| 25 |
+
*
|
| 26 |
+
* @param string $css
|
| 27 |
+
*
|
| 28 |
+
* @param array $options (currently ignored)
|
| 29 |
+
*
|
| 30 |
+
* @return string
|
| 31 |
+
*/
|
| 32 |
+
public static function process($css, $options = array())
|
| 33 |
+
{
|
| 34 |
+
$obj = new Minify_CSS_Compressor($options);
|
| 35 |
+
return $obj->_process($css);
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
/**
|
| 39 |
+
* @var array options
|
| 40 |
+
*/
|
| 41 |
+
protected $_options = null;
|
| 42 |
+
|
| 43 |
+
/**
|
| 44 |
+
* @var bool Are we "in" a hack?
|
| 45 |
+
*
|
| 46 |
+
* I.e. are some browsers targetted until the next comment?
|
| 47 |
+
*/
|
| 48 |
+
protected $_inHack = false;
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
/**
|
| 52 |
+
* Constructor
|
| 53 |
+
*
|
| 54 |
+
* @param array $options (currently ignored)
|
| 55 |
+
*
|
| 56 |
+
* @return null
|
| 57 |
+
*/
|
| 58 |
+
private function __construct($options) {
|
| 59 |
+
$this->_options = $options;
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
/**
|
| 63 |
+
* Minify a CSS string
|
| 64 |
+
*
|
| 65 |
+
* @param string $css
|
| 66 |
+
*
|
| 67 |
+
* @return string
|
| 68 |
+
*/
|
| 69 |
+
protected function _process($css)
|
| 70 |
+
{
|
| 71 |
+
$css = str_replace("\r\n", "\n", $css);
|
| 72 |
+
|
| 73 |
+
// preserve empty comment after '>'
|
| 74 |
+
// http://www.webdevout.net/css-hacks#in_css-selectors
|
| 75 |
+
$css = preg_replace('@>/\\*\\s*\\*/@', '>/*keep*/', $css);
|
| 76 |
+
|
| 77 |
+
// preserve empty comment between property and value
|
| 78 |
+
// http://css-discuss.incutio.com/?page=BoxModelHack
|
| 79 |
+
$css = preg_replace('@/\\*\\s*\\*/\\s*:@', '/*keep*/:', $css);
|
| 80 |
+
$css = preg_replace('@:\\s*/\\*\\s*\\*/@', ':/*keep*/', $css);
|
| 81 |
+
|
| 82 |
+
// apply callback to all valid comments (and strip out surrounding ws
|
| 83 |
+
$css = preg_replace_callback('@\\s*/\\*([\\s\\S]*?)\\*/\\s*@'
|
| 84 |
+
,array($this, '_commentCB'), $css);
|
| 85 |
+
|
| 86 |
+
// remove ws around { } and last semicolon in declaration block
|
| 87 |
+
$css = preg_replace('/\\s*{\\s*/', '{', $css);
|
| 88 |
+
$css = preg_replace('/;?\\s*}\\s*/', '}', $css);
|
| 89 |
+
|
| 90 |
+
// remove ws surrounding semicolons
|
| 91 |
+
$css = preg_replace('/\\s*;\\s*/', ';', $css);
|
| 92 |
+
|
| 93 |
+
// remove ws around urls
|
| 94 |
+
$css = preg_replace('/
|
| 95 |
+
url\\( # url(
|
| 96 |
+
\\s*
|
| 97 |
+
([^\\)]+?) # 1 = the URL (really just a bunch of non right parenthesis)
|
| 98 |
+
\\s*
|
| 99 |
+
\\) # )
|
| 100 |
+
/x', 'url($1)', $css);
|
| 101 |
+
|
| 102 |
+
// remove ws between rules and colons
|
| 103 |
+
$css = preg_replace('/
|
| 104 |
+
\\s*
|
| 105 |
+
([{;]) # 1 = beginning of block or rule separator
|
| 106 |
+
\\s*
|
| 107 |
+
([\\*_]?[\\w\\-]+) # 2 = property (and maybe IE filter)
|
| 108 |
+
\\s*
|
| 109 |
+
:
|
| 110 |
+
\\s*
|
| 111 |
+
(\\b|[#\'"]) # 3 = first character of a value
|
| 112 |
+
/x', '$1$2:$3', $css);
|
| 113 |
+
|
| 114 |
+
// remove ws in selectors
|
| 115 |
+
$css = preg_replace_callback('/
|
| 116 |
+
(?: # non-capture
|
| 117 |
+
\\s*
|
| 118 |
+
[^~>+,\\s]+ # selector part
|
| 119 |
+
\\s*
|
| 120 |
+
[,>+~] # combinators
|
| 121 |
+
)+
|
| 122 |
+
\\s*
|
| 123 |
+
[^~>+,\\s]+ # selector part
|
| 124 |
+
{ # open declaration block
|
| 125 |
+
/x'
|
| 126 |
+
,array($this, '_selectorsCB'), $css);
|
| 127 |
+
|
| 128 |
+
// minimize hex colors
|
| 129 |
+
$css = preg_replace('/([^=])#([a-f\\d])\\2([a-f\\d])\\3([a-f\\d])\\4([\\s;\\}])/i'
|
| 130 |
+
, '$1#$2$3$4$5', $css);
|
| 131 |
+
|
| 132 |
+
// remove spaces between font families
|
| 133 |
+
$css = preg_replace_callback('/font-family:([^;}]+)([;}])/'
|
| 134 |
+
,array($this, '_fontFamilyCB'), $css);
|
| 135 |
+
|
| 136 |
+
$css = preg_replace('/@import\\s+url/', '@import url', $css);
|
| 137 |
+
|
| 138 |
+
// replace any ws involving newlines with a single newline
|
| 139 |
+
$css = preg_replace('/[ \\t]*\\n+\\s*/', "\n", $css);
|
| 140 |
+
|
| 141 |
+
// separate common descendent selectors w/ newlines (to limit line lengths)
|
| 142 |
+
$css = preg_replace('/([\\w#\\.\\*]+)\\s+([\\w#\\.\\*]+){/', "$1\n$2{", $css);
|
| 143 |
+
|
| 144 |
+
// Use newline after 1st numeric value (to limit line lengths).
|
| 145 |
+
$css = preg_replace('/
|
| 146 |
+
((?:padding|margin|border|outline):\\d+(?:px|em)?) # 1 = prop : 1st numeric value
|
| 147 |
+
\\s+
|
| 148 |
+
/x'
|
| 149 |
+
,"$1\n", $css);
|
| 150 |
+
|
| 151 |
+
// prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/
|
| 152 |
+
$css = preg_replace('/:first-l(etter|ine)\\{/', ':first-l$1 {', $css);
|
| 153 |
+
|
| 154 |
+
return trim($css);
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
/**
|
| 158 |
+
* Replace what looks like a set of selectors
|
| 159 |
+
*
|
| 160 |
+
* @param array $m regex matches
|
| 161 |
+
*
|
| 162 |
+
* @return string
|
| 163 |
+
*/
|
| 164 |
+
protected function _selectorsCB($m)
|
| 165 |
+
{
|
| 166 |
+
// remove ws around the combinators
|
| 167 |
+
return preg_replace('/\\s*([,>+~])\\s*/', '$1', $m[0]);
|
| 168 |
+
}
|
| 169 |
+
|
| 170 |
+
/**
|
| 171 |
+
* Process a comment and return a replacement
|
| 172 |
+
*
|
| 173 |
+
* @param array $m regex matches
|
| 174 |
+
*
|
| 175 |
+
* @return string
|
| 176 |
+
*/
|
| 177 |
+
protected function _commentCB($m)
|
| 178 |
+
{
|
| 179 |
+
$hasSurroundingWs = (trim($m[0]) !== $m[1]);
|
| 180 |
+
$m = $m[1];
|
| 181 |
+
// $m is the comment content w/o the surrounding tokens,
|
| 182 |
+
// but the return value will replace the entire comment.
|
| 183 |
+
if ($m === 'keep') {
|
| 184 |
+
return '/**/';
|
| 185 |
+
}
|
| 186 |
+
if ($m === '" "') {
|
| 187 |
+
// component of http://tantek.com/CSS/Examples/midpass.html
|
| 188 |
+
return '/*" "*/';
|
| 189 |
+
}
|
| 190 |
+
if (preg_match('@";\\}\\s*\\}/\\*\\s+@', $m)) {
|
| 191 |
+
// component of http://tantek.com/CSS/Examples/midpass.html
|
| 192 |
+
return '/*";}}/* */';
|
| 193 |
+
}
|
| 194 |
+
if ($this->_inHack) {
|
| 195 |
+
// inversion: feeding only to one browser
|
| 196 |
+
if (preg_match('@
|
| 197 |
+
^/ # comment started like /*/
|
| 198 |
+
\\s*
|
| 199 |
+
(\\S[\\s\\S]+?) # has at least some non-ws content
|
| 200 |
+
\\s*
|
| 201 |
+
/\\* # ends like /*/ or /**/
|
| 202 |
+
@x', $m, $n)) {
|
| 203 |
+
// end hack mode after this comment, but preserve the hack and comment content
|
| 204 |
+
$this->_inHack = false;
|
| 205 |
+
return "/*/{$n[1]}/**/";
|
| 206 |
+
}
|
| 207 |
+
}
|
| 208 |
+
if (substr($m, -1) === '\\') { // comment ends like \*/
|
| 209 |
+
// begin hack mode and preserve hack
|
| 210 |
+
$this->_inHack = true;
|
| 211 |
+
return '/*\\*/';
|
| 212 |
+
}
|
| 213 |
+
if ($m !== '' && $m[0] === '/') { // comment looks like /*/ foo */
|
| 214 |
+
// begin hack mode and preserve hack
|
| 215 |
+
$this->_inHack = true;
|
| 216 |
+
return '/*/*/';
|
| 217 |
+
}
|
| 218 |
+
if ($this->_inHack) {
|
| 219 |
+
// a regular comment ends hack mode but should be preserved
|
| 220 |
+
$this->_inHack = false;
|
| 221 |
+
return '/**/';
|
| 222 |
+
}
|
| 223 |
+
// Issue 107: if there's any surrounding whitespace, it may be important, so
|
| 224 |
+
// replace the comment with a single space
|
| 225 |
+
return $hasSurroundingWs // remove all other comments
|
| 226 |
+
? ' '
|
| 227 |
+
: '';
|
| 228 |
+
}
|
| 229 |
+
|
| 230 |
+
/**
|
| 231 |
+
* Process a font-family listing and return a replacement
|
| 232 |
+
*
|
| 233 |
+
* @param array $m regex matches
|
| 234 |
+
*
|
| 235 |
+
* @return string
|
| 236 |
+
*/
|
| 237 |
+
protected function _fontFamilyCB($m)
|
| 238 |
+
{
|
| 239 |
+
$m[1] = preg_replace('/
|
| 240 |
+
\\s*
|
| 241 |
+
(
|
| 242 |
+
"[^"]+" # 1 = family in double qutoes
|
| 243 |
+
|\'[^\']+\' # or 1 = family in single quotes
|
| 244 |
+
|[\\w\\-]+ # or 1 = unquoted family
|
| 245 |
+
)
|
| 246 |
+
\\s*
|
| 247 |
+
/x', '$1', $m[1]);
|
| 248 |
+
return 'font-family:' . $m[1] . $m[2];
|
| 249 |
+
}
|
| 250 |
+
}
|
app/code/community/PSystem/OptimizationHead/lib/Minify/CSS/UriRewriter.php
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
/**
|
| 3 |
+
* Class Minify_CSS_UriRewriter
|
| 4 |
+
* @package Minify
|
| 5 |
+
*/
|
| 6 |
+
|
| 7 |
+
/**
|
| 8 |
+
* Rewrite file-relative URIs as root-relative in CSS files
|
| 9 |
+
*
|
| 10 |
+
* @package Minify
|
| 11 |
+
* @author Stephen Clay <steve@mrclay.org>
|
| 12 |
+
*/
|
| 13 |
+
class Minify_CSS_UriRewriter {
|
| 14 |
+
|
| 15 |
+
/**
|
| 16 |
+
* Defines which class to call as part of callbacks, change this
|
| 17 |
+
* if you extend Minify_CSS_UriRewriter
|
| 18 |
+
* @var string
|
| 19 |
+
*/
|
| 20 |
+
protected static $className = 'Minify_CSS_UriRewriter';
|
| 21 |
+
|
| 22 |
+
/**
|
| 23 |
+
* rewrite() and rewriteRelative() append debugging information here
|
| 24 |
+
* @var string
|
| 25 |
+
*/
|
| 26 |
+
public static $debugText = '';
|
| 27 |
+
|
| 28 |
+
/**
|
| 29 |
+
* Rewrite file relative URIs as root relative in CSS files
|
| 30 |
+
*
|
| 31 |
+
* @param string $css
|
| 32 |
+
*
|
| 33 |
+
* @param string $currentDir The directory of the current CSS file.
|
| 34 |
+
*
|
| 35 |
+
* @param string $docRoot The document root of the web site in which
|
| 36 |
+
* the CSS file resides (default = $_SERVER['DOCUMENT_ROOT']).
|
| 37 |
+
*
|
| 38 |
+
* @param array $symlinks (default = array()) If the CSS file is stored in
|
| 39 |
+
* a symlink-ed directory, provide an array of link paths to
|
| 40 |
+
* target paths, where the link paths are within the document root. Because
|
| 41 |
+
* paths need to be normalized for this to work, use "//" to substitute
|
| 42 |
+
* the doc root in the link paths (the array keys). E.g.:
|
| 43 |
+
* <code>
|
| 44 |
+
* array('//symlink' => '/real/target/path') // unix
|
| 45 |
+
* array('//static' => 'D:\\staticStorage') // Windows
|
| 46 |
+
* </code>
|
| 47 |
+
*
|
| 48 |
+
* @return string
|
| 49 |
+
*/
|
| 50 |
+
public static function rewrite($css, $currentDir, $docRoot = null, $symlinks = array())
|
| 51 |
+
{
|
| 52 |
+
self::$_docRoot = self::_realpath(
|
| 53 |
+
$docRoot ? $docRoot : $_SERVER['DOCUMENT_ROOT']
|
| 54 |
+
);
|
| 55 |
+
self::$_currentDir = self::_realpath($currentDir);
|
| 56 |
+
self::$_symlinks = array();
|
| 57 |
+
|
| 58 |
+
// normalize symlinks
|
| 59 |
+
foreach ($symlinks as $link => $target) {
|
| 60 |
+
$link = ($link === '//')
|
| 61 |
+
? self::$_docRoot
|
| 62 |
+
: str_replace('//', self::$_docRoot . '/', $link);
|
| 63 |
+
$link = strtr($link, '/', DIRECTORY_SEPARATOR);
|
| 64 |
+
self::$_symlinks[$link] = self::_realpath($target);
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
self::$debugText .= "docRoot : " . self::$_docRoot . "\n"
|
| 68 |
+
. "currentDir : " . self::$_currentDir . "\n";
|
| 69 |
+
if (self::$_symlinks) {
|
| 70 |
+
self::$debugText .= "symlinks : " . var_export(self::$_symlinks, 1) . "\n";
|
| 71 |
+
}
|
| 72 |
+
self::$debugText .= "\n";
|
| 73 |
+
|
| 74 |
+
$css = self::_trimUrls($css);
|
| 75 |
+
|
| 76 |
+
// rewrite
|
| 77 |
+
$css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/'
|
| 78 |
+
,array(self::$className, '_processUriCB'), $css);
|
| 79 |
+
$css = preg_replace_callback('/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
|
| 80 |
+
,array(self::$className, '_processUriCB'), $css);
|
| 81 |
+
|
| 82 |
+
return $css;
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
/**
|
| 86 |
+
* Prepend a path to relative URIs in CSS files
|
| 87 |
+
*
|
| 88 |
+
* @param string $css
|
| 89 |
+
*
|
| 90 |
+
* @param string $path The path to prepend.
|
| 91 |
+
*
|
| 92 |
+
* @return string
|
| 93 |
+
*/
|
| 94 |
+
public static function prepend($css, $path)
|
| 95 |
+
{
|
| 96 |
+
self::$_prependPath = $path;
|
| 97 |
+
|
| 98 |
+
$css = self::_trimUrls($css);
|
| 99 |
+
|
| 100 |
+
// append
|
| 101 |
+
$css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/'
|
| 102 |
+
,array(self::$className, '_processUriCB'), $css);
|
| 103 |
+
$css = preg_replace_callback('/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
|
| 104 |
+
,array(self::$className, '_processUriCB'), $css);
|
| 105 |
+
|
| 106 |
+
self::$_prependPath = null;
|
| 107 |
+
return $css;
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
/**
|
| 112 |
+
* @var string directory of this stylesheet
|
| 113 |
+
*/
|
| 114 |
+
private static $_currentDir = '';
|
| 115 |
+
|
| 116 |
+
/**
|
| 117 |
+
* @var string DOC_ROOT
|
| 118 |
+
*/
|
| 119 |
+
private static $_docRoot = '';
|
| 120 |
+
|
| 121 |
+
/**
|
| 122 |
+
* @var array directory replacements to map symlink targets back to their
|
| 123 |
+
* source (within the document root) E.g. '/var/www/symlink' => '/var/realpath'
|
| 124 |
+
*/
|
| 125 |
+
private static $_symlinks = array();
|
| 126 |
+
|
| 127 |
+
/**
|
| 128 |
+
* @var string path to prepend
|
| 129 |
+
*/
|
| 130 |
+
private static $_prependPath = null;
|
| 131 |
+
|
| 132 |
+
private static function _trimUrls($css)
|
| 133 |
+
{
|
| 134 |
+
return preg_replace('/
|
| 135 |
+
url\\( # url(
|
| 136 |
+
\\s*
|
| 137 |
+
([^\\)]+?) # 1 = URI (assuming does not contain ")")
|
| 138 |
+
\\s*
|
| 139 |
+
\\) # )
|
| 140 |
+
/x', 'url($1)', $css);
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
private static function _processUriCB($m)
|
| 144 |
+
{
|
| 145 |
+
// $m matched either '/@import\\s+([\'"])(.*?)[\'"]/' or '/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
|
| 146 |
+
$isImport = ($m[0][0] === '@');
|
| 147 |
+
// determine URI and the quote character (if any)
|
| 148 |
+
if ($isImport) {
|
| 149 |
+
$quoteChar = $m[1];
|
| 150 |
+
$uri = $m[2];
|
| 151 |
+
} else {
|
| 152 |
+
// $m[1] is either quoted or not
|
| 153 |
+
$quoteChar = ($m[1][0] === "'" || $m[1][0] === '"')
|
| 154 |
+
? $m[1][0]
|
| 155 |
+
: '';
|
| 156 |
+
$uri = ($quoteChar === '')
|
| 157 |
+
? $m[1]
|
| 158 |
+
: substr($m[1], 1, strlen($m[1]) - 2);
|
| 159 |
+
}
|
| 160 |
+
// analyze URI
|
| 161 |
+
if ('/' !== $uri[0] // root-relative
|
| 162 |
+
&& false === strpos($uri, '//') // protocol (non-data)
|
| 163 |
+
&& 0 !== strpos($uri, 'data:') // data protocol
|
| 164 |
+
) {
|
| 165 |
+
// URI is file-relative: rewrite depending on options
|
| 166 |
+
$uri = (self::$_prependPath !== null)
|
| 167 |
+
? (self::$_prependPath . $uri)
|
| 168 |
+
: self::rewriteRelative($uri, self::$_currentDir, self::$_docRoot, self::$_symlinks);
|
| 169 |
+
}
|
| 170 |
+
return $isImport
|
| 171 |
+
? "@import {$quoteChar}{$uri}{$quoteChar}"
|
| 172 |
+
: "url({$quoteChar}{$uri}{$quoteChar})";
|
| 173 |
+
}
|
| 174 |
+
|
| 175 |
+
/**
|
| 176 |
+
* Rewrite a file relative URI as root relative
|
| 177 |
+
*
|
| 178 |
+
* <code>
|
| 179 |
+
* Minify_CSS_UriRewriter::rewriteRelative(
|
| 180 |
+
* '../img/hello.gif'
|
| 181 |
+
* , '/home/user/www/css' // path of CSS file
|
| 182 |
+
* , '/home/user/www' // doc root
|
| 183 |
+
* );
|
| 184 |
+
* // returns '/img/hello.gif'
|
| 185 |
+
*
|
| 186 |
+
* // example where static files are stored in a symlinked directory
|
| 187 |
+
* Minify_CSS_UriRewriter::rewriteRelative(
|
| 188 |
+
* 'hello.gif'
|
| 189 |
+
* , '/var/staticFiles/theme'
|
| 190 |
+
* , '/home/user/www'
|
| 191 |
+
* , array('/home/user/www/static' => '/var/staticFiles')
|
| 192 |
+
* );
|
| 193 |
+
* // returns '/static/theme/hello.gif'
|
| 194 |
+
* </code>
|
| 195 |
+
*
|
| 196 |
+
* @param string $uri file relative URI
|
| 197 |
+
*
|
| 198 |
+
* @param string $realCurrentDir realpath of the current file's directory.
|
| 199 |
+
*
|
| 200 |
+
* @param string $realDocRoot realpath of the site document root.
|
| 201 |
+
*
|
| 202 |
+
* @param array $symlinks (default = array()) If the file is stored in
|
| 203 |
+
* a symlink-ed directory, provide an array of link paths to
|
| 204 |
+
* real target paths, where the link paths "appear" to be within the document
|
| 205 |
+
* root. E.g.:
|
| 206 |
+
* <code>
|
| 207 |
+
* array('/home/foo/www/not/real/path' => '/real/target/path') // unix
|
| 208 |
+
* array('C:\\htdocs\\not\\real' => 'D:\\real\\target\\path') // Windows
|
| 209 |
+
* </code>
|
| 210 |
+
*
|
| 211 |
+
* @return string
|
| 212 |
+
*/
|
| 213 |
+
public static function rewriteRelative($uri, $realCurrentDir, $realDocRoot, $symlinks = array())
|
| 214 |
+
{
|
| 215 |
+
// prepend path with current dir separator (OS-independent)
|
| 216 |
+
$path = strtr($realCurrentDir, '/', DIRECTORY_SEPARATOR)
|
| 217 |
+
. DIRECTORY_SEPARATOR . strtr($uri, '/', DIRECTORY_SEPARATOR);
|
| 218 |
+
|
| 219 |
+
self::$debugText .= "file-relative URI : {$uri}\n"
|
| 220 |
+
. "path prepended : {$path}\n";
|
| 221 |
+
|
| 222 |
+
// "unresolve" a symlink back to doc root
|
| 223 |
+
foreach ($symlinks as $link => $target) {
|
| 224 |
+
if (0 === strpos($path, $target)) {
|
| 225 |
+
// replace $target with $link
|
| 226 |
+
$path = $link . substr($path, strlen($target));
|
| 227 |
+
|
| 228 |
+
self::$debugText .= "symlink unresolved : {$path}\n";
|
| 229 |
+
|
| 230 |
+
break;
|
| 231 |
+
}
|
| 232 |
+
}
|
| 233 |
+
// strip doc root
|
| 234 |
+
$path = substr($path, strlen($realDocRoot));
|
| 235 |
+
|
| 236 |
+
self::$debugText .= "docroot stripped : {$path}\n";
|
| 237 |
+
|
| 238 |
+
// fix to root-relative URI
|
| 239 |
+
|
| 240 |
+
$uri = strtr($path, '/\\', '//');
|
| 241 |
+
|
| 242 |
+
// remove /./ and /../ where possible
|
| 243 |
+
$uri = str_replace('/./', '/', $uri);
|
| 244 |
+
// inspired by patch from Oleg Cherniy
|
| 245 |
+
do {
|
| 246 |
+
$uri = preg_replace('@/[^/]+/\\.\\./@', '/', $uri, 1, $changed);
|
| 247 |
+
} while ($changed);
|
| 248 |
+
|
| 249 |
+
self::$debugText .= "traversals removed : {$uri}\n\n";
|
| 250 |
+
|
| 251 |
+
return $uri;
|
| 252 |
+
}
|
| 253 |
+
|
| 254 |
+
/**
|
| 255 |
+
* Get realpath with any trailing slash removed. If realpath() fails,
|
| 256 |
+
* just remove the trailing slash.
|
| 257 |
+
*
|
| 258 |
+
* @param string $path
|
| 259 |
+
*
|
| 260 |
+
* @return mixed path with no trailing slash
|
| 261 |
+
*/
|
| 262 |
+
protected static function _realpath($path)
|
| 263 |
+
{
|
| 264 |
+
$realPath = realpath($path);
|
| 265 |
+
if ($realPath !== false) {
|
| 266 |
+
$path = $realPath;
|
| 267 |
+
}
|
| 268 |
+
return rtrim($path, '/\\');
|
| 269 |
+
}
|
| 270 |
+
}
|
app/code/community/PSystem/OptimizationHead/lib/Minify/CommentPreserver.php
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
/**
|
| 3 |
+
* Class Minify_CommentPreserver
|
| 4 |
+
* @package Minify
|
| 5 |
+
*/
|
| 6 |
+
|
| 7 |
+
/**
|
| 8 |
+
* Process a string in pieces preserving C-style comments that begin with "/*!"
|
| 9 |
+
*
|
| 10 |
+
* @package Minify
|
| 11 |
+
* @author Stephen Clay <steve@mrclay.org>
|
| 12 |
+
*/
|
| 13 |
+
class Minify_CommentPreserver {
|
| 14 |
+
|
| 15 |
+
/**
|
| 16 |
+
* String to be prepended to each preserved comment
|
| 17 |
+
*
|
| 18 |
+
* @var string
|
| 19 |
+
*/
|
| 20 |
+
public static $prepend = "\n";
|
| 21 |
+
|
| 22 |
+
/**
|
| 23 |
+
* String to be appended to each preserved comment
|
| 24 |
+
*
|
| 25 |
+
* @var string
|
| 26 |
+
*/
|
| 27 |
+
public static $append = "\n";
|
| 28 |
+
|
| 29 |
+
/**
|
| 30 |
+
* Process a string outside of C-style comments that begin with "/*!"
|
| 31 |
+
*
|
| 32 |
+
* On each non-empty string outside these comments, the given processor
|
| 33 |
+
* function will be called. The first "!" will be removed from the
|
| 34 |
+
* preserved comments, and the comments will be surrounded by
|
| 35 |
+
* Minify_CommentPreserver::$preprend and Minify_CommentPreserver::$append.
|
| 36 |
+
*
|
| 37 |
+
* @param string $content
|
| 38 |
+
* @param callback $processor function
|
| 39 |
+
* @param array $args array of extra arguments to pass to the processor
|
| 40 |
+
* function (default = array())
|
| 41 |
+
* @return string
|
| 42 |
+
*/
|
| 43 |
+
public static function process($content, $processor, $args = array())
|
| 44 |
+
{
|
| 45 |
+
$ret = '';
|
| 46 |
+
while (true) {
|
| 47 |
+
list($beforeComment, $comment, $afterComment) = self::_nextComment($content);
|
| 48 |
+
if ('' !== $beforeComment) {
|
| 49 |
+
$callArgs = $args;
|
| 50 |
+
array_unshift($callArgs, $beforeComment);
|
| 51 |
+
$ret .= call_user_func_array($processor, $callArgs);
|
| 52 |
+
}
|
| 53 |
+
if (false === $comment) {
|
| 54 |
+
break;
|
| 55 |
+
}
|
| 56 |
+
$ret .= $comment;
|
| 57 |
+
$content = $afterComment;
|
| 58 |
+
}
|
| 59 |
+
return $ret;
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
/**
|
| 63 |
+
* Extract comments that YUI Compressor preserves.
|
| 64 |
+
*
|
| 65 |
+
* @param string $in input
|
| 66 |
+
*
|
| 67 |
+
* @return array 3 elements are returned. If a YUI comment is found, the
|
| 68 |
+
* 2nd element is the comment and the 1st and 2nd are the surrounding
|
| 69 |
+
* strings. If no comment is found, the entire string is returned as the
|
| 70 |
+
* 1st element and the other two are false.
|
| 71 |
+
*/
|
| 72 |
+
private static function _nextComment($in)
|
| 73 |
+
{
|
| 74 |
+
if (
|
| 75 |
+
false === ($start = strpos($in, '/*!'))
|
| 76 |
+
|| false === ($end = strpos($in, '*/', $start + 3))
|
| 77 |
+
) {
|
| 78 |
+
return array($in, false, false);
|
| 79 |
+
}
|
| 80 |
+
$ret = array(
|
| 81 |
+
substr($in, 0, $start)
|
| 82 |
+
,self::$prepend . '/*' . substr($in, $start + 3, $end - $start - 1) . self::$append
|
| 83 |
+
);
|
| 84 |
+
$endChars = (strlen($in) - $end - 2);
|
| 85 |
+
$ret[] = (0 === $endChars)
|
| 86 |
+
? ''
|
| 87 |
+
: substr($in, -$endChars);
|
| 88 |
+
return $ret;
|
| 89 |
+
}
|
| 90 |
+
}
|
app/code/community/PSystem/OptimizationHead/lib/PSystemHtmlMinify.php
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
/**
|
| 3 |
+
* @category PSystem
|
| 4 |
+
* @package PSystem_OptimizationHead
|
| 5 |
+
* @author Pascal System <info@pascalsystem.pl>
|
| 6 |
+
* @version 1.0.0
|
| 7 |
+
*/
|
| 8 |
+
|
| 9 |
+
/**
|
| 10 |
+
* Pascal Optimization observer
|
| 11 |
+
*
|
| 12 |
+
* @category PSystem
|
| 13 |
+
* @package PSystem_OptimizationHead
|
| 14 |
+
* @author Pascal System <info@pascalsystem.pl>
|
| 15 |
+
* @version 1.0.0
|
| 16 |
+
*/
|
| 17 |
+
class PSystemHtmlMinify {
|
| 18 |
+
/**
|
| 19 |
+
* Compress html
|
| 20 |
+
*
|
| 21 |
+
* @param string $content
|
| 22 |
+
* @return string
|
| 23 |
+
*/
|
| 24 |
+
public static function min($content) {
|
| 25 |
+
$content = str_replace("\n",'',$content);
|
| 26 |
+
$content = str_replace("\r",'',$content);
|
| 27 |
+
$content = preg_replace('/([\s]{2,})/',' ',$content);
|
| 28 |
+
return $content;
|
| 29 |
+
}
|
| 30 |
+
}
|
app/etc/modules/PSystem_OptimizationHead.xml
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0"?>
|
| 2 |
+
<!--
|
| 3 |
+
/**
|
| 4 |
+
* @category PSystem
|
| 5 |
+
* @package PSystem_OptimizationHead
|
| 6 |
+
* @author Pascal System <info@pascalsystem.pl>
|
| 7 |
+
* @version 1.0.0
|
| 8 |
+
*/
|
| 9 |
+
-->
|
| 10 |
+
<config>
|
| 11 |
+
<modules>
|
| 12 |
+
<PSystem_OptimizationHead>
|
| 13 |
+
<active>true</active>
|
| 14 |
+
<codePool>community</codePool>
|
| 15 |
+
<depends>
|
| 16 |
+
<PSystem_Base/>
|
| 17 |
+
</depends>
|
| 18 |
+
</PSystem_OptimizationHead>
|
| 19 |
+
</modules>
|
| 20 |
+
</config>
|
js/pascalsystem/combine.php
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
/**
|
| 3 |
+
* Pascal combine js and css
|
| 4 |
+
*
|
| 5 |
+
* @category PSystem
|
| 6 |
+
* @package PSystem_OptimizationHead
|
| 7 |
+
* @author Pascal System <info@pascalsystem.pl>
|
| 8 |
+
* @version 1.0.0
|
| 9 |
+
*/
|
| 10 |
+
|
| 11 |
+
/**
|
| 12 |
+
* Return clear cache time in second
|
| 13 |
+
*
|
| 14 |
+
* @return int
|
| 15 |
+
*/
|
| 16 |
+
function getRefreshTime() {
|
| 17 |
+
if (!empty($_GET['refresh']) && ($time = intval($_GET['refresh'])))
|
| 18 |
+
return $time;
|
| 19 |
+
return 259200;
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
/**
|
| 23 |
+
* Set css header
|
| 24 |
+
*
|
| 25 |
+
* @return void
|
| 26 |
+
*/
|
| 27 |
+
function setCssHeader() {
|
| 28 |
+
if (empty($_GET['gzipoff'])) {
|
| 29 |
+
ob_start('ob_gzhandler');
|
| 30 |
+
header('Content-Encoding: gzip');
|
| 31 |
+
}
|
| 32 |
+
header('Content-type: text/css; charset:UTF-8');
|
| 33 |
+
header('Cache-Control: must-revalidate');
|
| 34 |
+
$offset = getRefreshTime();
|
| 35 |
+
$ExpStr = 'Expires: '.gmdate('D, d M Y H:i:s', time() + $offset) . ' GMT';
|
| 36 |
+
header($ExpStr);
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
/**
|
| 40 |
+
* Set js header
|
| 41 |
+
*
|
| 42 |
+
* @return void
|
| 43 |
+
*/
|
| 44 |
+
function setJsHeader() {
|
| 45 |
+
if (empty($_GET['gzipoff'])) {
|
| 46 |
+
ob_start('ob_gzhandler');
|
| 47 |
+
header('Content-Encoding: gzip');
|
| 48 |
+
}
|
| 49 |
+
header('Content-type: text/javascript; charset: UTF-8');
|
| 50 |
+
header('Cache-Control: must-revalidate');
|
| 51 |
+
$offset = getRefreshTime();
|
| 52 |
+
$ExpStr = 'Expires: '.gmdate('D, d M Y H:i:s', time() + $offset) . ' GMT';
|
| 53 |
+
header($ExpStr);
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
/**
|
| 57 |
+
* Minify css
|
| 58 |
+
*
|
| 59 |
+
* @param string $content
|
| 60 |
+
* @return string
|
| 61 |
+
*/
|
| 62 |
+
function minifyCss($content) {
|
| 63 |
+
if (!empty($_GET['minoff']))
|
| 64 |
+
return $content;
|
| 65 |
+
require_once 'Minify/CSS.php';
|
| 66 |
+
return Minify_CSS::minify($content);
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
/**
|
| 70 |
+
* Minify js
|
| 71 |
+
*
|
| 72 |
+
* @param string $content
|
| 73 |
+
* @return string
|
| 74 |
+
*/
|
| 75 |
+
function minifyJs($content) {
|
| 76 |
+
if (!empty($_GET['minoff']))
|
| 77 |
+
return $content;
|
| 78 |
+
require_once 'JSMin.php';
|
| 79 |
+
return JSMin::minify($content);
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
/**
|
| 83 |
+
* Get media cache dir
|
| 84 |
+
*
|
| 85 |
+
* @return string
|
| 86 |
+
*/
|
| 87 |
+
function getCacheDir() {
|
| 88 |
+
$dir = dirname(__FILE__).'/../../media/optimizationhead';
|
| 89 |
+
if (!is_dir($dir)) mkdir($dir, 0777);
|
| 90 |
+
return $dir.'/';
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
/**
|
| 94 |
+
* Get base dir
|
| 95 |
+
*
|
| 96 |
+
* @param string $type
|
| 97 |
+
* @return string
|
| 98 |
+
*/
|
| 99 |
+
function getBaseDir($type = '') {
|
| 100 |
+
$path = dirname(__FILE__).'/../..';
|
| 101 |
+
if ($type)
|
| 102 |
+
$path.= '/'.$type;
|
| 103 |
+
return $path;
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
/**
|
| 107 |
+
* Get base url
|
| 108 |
+
*
|
| 109 |
+
* @reutrn string
|
| 110 |
+
*/
|
| 111 |
+
function getBaseUrl() {
|
| 112 |
+
if (isset($GLOBALS['BASE_URL']))
|
| 113 |
+
return $GLOBALS['BASE_URL'];
|
| 114 |
+
$httpUrl = '';
|
| 115 |
+
if (empty($_SERVER['REQUEST_URI']))
|
| 116 |
+
return '/';
|
| 117 |
+
$cutPos = strpos($_SERVER['REQUEST_URI'], 'js/pascalsystem');
|
| 118 |
+
if ($cutPos===false)
|
| 119 |
+
$httpUrl.= '/';
|
| 120 |
+
else
|
| 121 |
+
$httpUrl.= substr($_SERVER['REQUEST_URI'], 0, $cutPos);
|
| 122 |
+
$GLOBALS['BASE_URL'] = $httpUrl;
|
| 123 |
+
return $GLOBALS['BASE_URL'];
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
/**
|
| 127 |
+
* Translate url in css file
|
| 128 |
+
*
|
| 129 |
+
* @param string $content
|
| 130 |
+
* @param string $file
|
| 131 |
+
* @param string $prefix
|
| 132 |
+
* @return string
|
| 133 |
+
*/
|
| 134 |
+
function translateUrlInCss($content, $file, $prefix) {
|
| 135 |
+
preg_match_all('/url\(([\'\"])?([^\)\'\"]+)([\'\"])?\)/', $content, $matches);
|
| 136 |
+
$absPath = getBaseUrl().$prefix.'/';
|
| 137 |
+
$absFilePart = explode('/',dirname($file));
|
| 138 |
+
$absFileNum = count($absFilePart);
|
| 139 |
+
$replaces = array();
|
| 140 |
+
foreach ($matches[0] as $num => $value) {
|
| 141 |
+
$temps = explode('/',$matches[2][$num]);
|
| 142 |
+
$numTemps = count($temps);
|
| 143 |
+
for ($i=0;$i<$numTemps;$i++) {
|
| 144 |
+
if ($temps[$i]=='..')
|
| 145 |
+
continue;
|
| 146 |
+
break;
|
| 147 |
+
}
|
| 148 |
+
if ($i>$absFileNum) {
|
| 149 |
+
$i==$absFileNum;
|
| 150 |
+
}
|
| 151 |
+
if ($i>0) {
|
| 152 |
+
$temps = array_slice($temps, $i);
|
| 153 |
+
}
|
| 154 |
+
$pathSlice = array_slice($absFilePart, 0, $absFileNum-$i);
|
| 155 |
+
$replaces[$num] = 'url('.$matches[1][$num].$absPath.implode('/',$pathSlice).'/'.implode('/',$temps).$matches[3][$num].')';
|
| 156 |
+
}
|
| 157 |
+
return str_replace($matches[0], $replaces, $content);
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
/**
|
| 161 |
+
* Combine files
|
| 162 |
+
*
|
| 163 |
+
* @param array $files
|
| 164 |
+
* @param string $prefix
|
| 165 |
+
* @param string $ext
|
| 166 |
+
* @return string
|
| 167 |
+
*/
|
| 168 |
+
function combineFiles(array $files, $prefix, $ext) {
|
| 169 |
+
$baseDir = getBaseDir($prefix).'/';
|
| 170 |
+
$cacheDir = getCacheDir();
|
| 171 |
+
|
| 172 |
+
$filesKey = md5(serialize($files)).'.'.$ext;
|
| 173 |
+
|
| 174 |
+
$noCache = empty($_GET['nocache'])?false:true;
|
| 175 |
+
|
| 176 |
+
if (!$noCache && file_exists($cacheDir.$filesKey) && (filemtime($cacheDir.$filesKey)+getRefreshTime() > time())) {
|
| 177 |
+
$allData = file_get_contents($cacheDir.$filesKey);
|
| 178 |
+
} else {
|
| 179 |
+
$allData = '';
|
| 180 |
+
foreach ($files as $file) {
|
| 181 |
+
if (!file_exists($baseDir.$file))
|
| 182 |
+
continue;
|
| 183 |
+
|
| 184 |
+
$data = file_get_contents($baseDir.$file);
|
| 185 |
+
if ($ext == 'css') {
|
| 186 |
+
$data = translateUrlInCss($data, $file, $prefix);
|
| 187 |
+
}
|
| 188 |
+
|
| 189 |
+
if ($allData) {
|
| 190 |
+
$allData.= "\n\n";
|
| 191 |
+
}
|
| 192 |
+
$allData.= $data;
|
| 193 |
+
}
|
| 194 |
+
|
| 195 |
+
if ($ext == 'css') {
|
| 196 |
+
$allData = minifyCss($allData);
|
| 197 |
+
} else {
|
| 198 |
+
$allData = minifyJs($allData);
|
| 199 |
+
}
|
| 200 |
+
|
| 201 |
+
if (!$noCache) {
|
| 202 |
+
file_put_contents($cacheDir.$filesKey, $allData);
|
| 203 |
+
}
|
| 204 |
+
}
|
| 205 |
+
|
| 206 |
+
echo $allData;
|
| 207 |
+
}
|
| 208 |
+
|
| 209 |
+
set_include_path(get_include_path() . PATH_SEPARATOR . dirname(__FILE__).'/../../app/code/community/PSystem/OptimizationHead/lib');
|
| 210 |
+
$type = empty($_GET['type'])?'':$_GET['type'];
|
| 211 |
+
$files = empty($_GET['file'])?array():explode(',',$_GET['file']);
|
| 212 |
+
switch ($type) {
|
| 213 |
+
case 'css' :
|
| 214 |
+
setCssHeader();
|
| 215 |
+
combineFiles($files, 'js', 'css');
|
| 216 |
+
exit;
|
| 217 |
+
case 'cssskin' :
|
| 218 |
+
setCssHeader();
|
| 219 |
+
combineFiles($files, 'skin', 'css');
|
| 220 |
+
exit;
|
| 221 |
+
case 'js' :
|
| 222 |
+
setJsHeader();
|
| 223 |
+
combineFiles($files, 'js', 'js');
|
| 224 |
+
exit;
|
| 225 |
+
case 'jsskin' :
|
| 226 |
+
setJsHeader();
|
| 227 |
+
combineFiles($files, 'skin', 'js');
|
| 228 |
+
exit;
|
| 229 |
+
}
|
| 230 |
+
header("HTTP/1.0 404 Not Found");
|
| 231 |
+
header("Status: 404 Not Found");
|
package.xml
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0"?>
|
| 2 |
+
<package>
|
| 3 |
+
<name>PSystem_OptimizationHead</name>
|
| 4 |
+
<version>1.0.0</version>
|
| 5 |
+
<stability>stable</stability>
|
| 6 |
+
<license>OSL v3.0</license>
|
| 7 |
+
<channel>community</channel>
|
| 8 |
+
<extends/>
|
| 9 |
+
<summary>Optimize css, js and html for your magento shop</summary>
|
| 10 |
+
<description>This module combine (merge) your css and js files. Additionaly this module compress your files by gzip and minify. In addition this module compress your html.</description>
|
| 11 |
+
<notes>This module was tested in Magento Commerce version 1.4.0.1 and 1.5.0.1, and work in this version smoothly.</notes>
|
| 12 |
+
<authors><author><name>Pascal System</name><user>auto-converted</user><email>info@pascalsystem.pl</email></author></authors>
|
| 13 |
+
<date>2011-03-28</date>
|
| 14 |
+
<time>22:37:05</time>
|
| 15 |
+
<contents><target name="mage"><dir name="js"><dir name="pascalsystem"><file name="combine.php" hash="ac34bd3b233b17472565872470e5c8f8"/></dir></dir></target><target name="mageetc"><dir name="modules"><file name="PSystem_OptimizationHead.xml" hash="c275af0fe2e0a37029a739101d980241"/></dir></target><target name="magecommunity"><dir name="PSystem"><dir name="OptimizationHead"><dir name="Block"><file name="Head.php" hash="80fbc8fdcc33ab57bcfe9c986658e623"/><dir name="Head"><file name="Abstract.php" hash="1a4757995d9a9bdbbea1ed9b3f16f715"/></dir></dir><dir name="etc"><file name="adminhtml.xml" hash="4c98ce2468f6321c3faf18740ef703f7"/><file name="config.xml" hash="5d260c92bd8136cd80022e0b0559bad0"/><file name="system.xml" hash="8af6addcbcb3f5796e2b5f26075981a5"/></dir><dir name="lib"><file name="JSMin.php" hash="5716028656e1d402c98a43ee18648dd3"/><file name="PSystemHtmlMinify.php" hash="2d841f14aa166efb568b1aeee3cef522"/><dir name="Minify"><file name="CommentPreserver.php" hash="86bab05265083b57935503bdd735ce74"/><file name="CSS.php" hash="cdeb49c0f35a6cef166c6cfee7f1dd95"/><dir name="CSS"><file name="Compressor.php" hash="b514fdc1f7c010031631994c77a18bf6"/><file name="UriRewriter.php" hash="5ab5dfdb489e28a39a4e9d6ad70117d6"/></dir></dir></dir><dir name="Model"><file name="Observer.php" hash="1f6e674fd06697a48a1f02b153d53a13"/></dir></dir></dir></target></contents>
|
| 16 |
+
<compatible/>
|
| 17 |
+
<dependencies><required><package><name>PSystem_Base</name><channel>community</channel><min></min><max></max></package></required></dependencies>
|
| 18 |
+
</package>
|
