in my project i need also groups for differ js files. For example, for one language - one set, for other language - other set. That’s why i took last version from that thread and added groups:
i have to change registerScriptFile and registerCssFile to add groups. Usage,
registerCssFile($url, $media=’’, $group = ‘all’)
registerScriptFile($url, $position = 0, $group = ‘all’)
Yes, default group is always ‘all’.
P.S.: still need to solve problem with themes. Current version doesn’t support CSS grouping and theming 
<?php
/**
* Compress and cache used JS and CSS files.
* Needs jsmin in helpers and csstidy in extensions.
*
* Ties into the 1.0.4 (or > SVN 813) Yii CClientScript functions.
*
* @author Maxximus <maxximus007@gmail.com>
* @author Alexander Makarov <sam@rmcreative.ru>
* @author Andrey gayvoronsky <plandem@gmail.com>
*
* @link http://www.yiiframework.com/
* @copyright Copyright © 2008-2009
* @license http://www.yiiframework.com/license/
* @version 0.7
*/
class ExtendedClientScript extends CClientScript {
/**
* Compress all Javascript files with JSMin. JSMin must be installed as an extension in dir jsmin.
* code.google.com/p/jsmin-php/
*/
public $compressJs = false;
/**
* Compress all CSS files with CssTidy. CssTidy must be installed as an extension in dir csstidy.
* Specific browserhacks will be removed, so don't add them in to be compressed CSS files
* csstidy.sourceforge.net
*/
public $compressCss = false;
/**
* Combine all JS files into one.
*
* @var boolean
*/
public $combineJs = false;
/**
* Combine all CSS files into one. Be careful with relative paths in CSS.
*
* @var boolean
*/
public $combineCss = false;
/**
* Exclude certain files from inclusion. array('/path/to/excluded/file') Useful for fixed base
* and incidental additional JS.
*/
public $excludeFiles = array();
/**
* Path where the combined/compressed file will be stored. Will use coreScriptUrl if not defined
*/
public $filePath;
/**
* If true, all files to be included will be checked if they are modified.
* To enhance speed (eg production) set to false.
*/
public $autoRefresh = true;
/**
* Relative Url where the combined/compressed file can be found
*/
public $fileUrl;
/**
* Path where files can be found
*/
public $basePath;
/**
* Used for garbage collection. If not accessed for that period: remove.
*/
public $ttlDays = 1;
/**
* prefix for the combined/compressed files
*/
public $prefix = 'c_';
/**
* CssTidy template. See CssTidy for more information
*/
public $cssTidyTemplate = "highest_compression";
/**
* CssTidy parameters. See CssTidy for more information
*/
public $cssTidyConfig = array(
'css_level' => 'CSS2.1',
'discard_invalid_properties' => FALSE,
'lowercase_s' => FALSE,
'sort_properties' => FALSE,
'sort_selectors' => FALSE,
'preserve_css' => FALSE,
'timestamp' => FALSE,
'remove_bslash' => TRUE,
'compress_colors' => TRUE,
'compress_font-weight' => TRUE,
'remove_last_,' => TRUE,
'optimise_shorthands' => 1,
'case_properties' => 1,
'merge_selectors' => 2,
);
/**
* Used for grouping
*/
public $groups = array(
'js' => array(), // array(url => group)
'css' => array(), // array(url => group)
);
/**
* Will combine/compress JS and CSS if wanted/needed, and will continue with original
* renderHead afterwards
*
* @param string $output
*/
public function renderHead(&$output) {
if ($this->combineJs) {
if (isset($this->scriptFiles[parent::POS_HEAD]) && count($this->scriptFiles[parent::POS_HEAD]) !== 0) {
$jsFiles = $this->scriptFiles[parent::POS_HEAD];
if (!empty($this->excludeFiles)) {
foreach ($jsFiles as &$fileName)
(in_array($fileName, $this->excludeFiles)) AND $fileName = false;
$jsFiles = array_filter($jsFiles);
}
$this->combineAndCompress('js', $jsFiles, parent::POS_HEAD);
}
}
if ($this->combineCss) {
if (count($this->cssFiles) !== 0) {
foreach ($this->cssFiles as $url => $media)
$cssFiles[$media][$url] = $url;
foreach ($cssFiles as $media => $url)
$this->combineAndCompress('css', $url, $media);
}
}
parent::renderHead($output);
}
/**
* Will combine/compress JS if wanted/needed, and will continue with original
* renderBodyEnd afterwards
*
* @param string $output
*/
public function renderBodyBegin(&$output) {
if ($this->combineJs) {
if (isset($this->scriptFiles[parent::POS_BEGIN]) && count($this->scriptFiles[parent::POS_BEGIN]) !== 0) {
$jsFiles = $this->scriptFiles[parent::POS_BEGIN];
if (!empty($this->excludeFiles)) {
foreach ($jsFiles as &$fileName)
(in_array($fileName, $this->excludeFiles)) AND $fileName = false;
$jsFiles = array_filter($jsFiles);
}
$this->combineAndCompress('js', $jsFiles, parent::POS_BEGIN);
}
}
parent::renderBodyBegin($output);
}
/**
* Will combine/compress JS if wanted/needed, and will continue with original
* renderBodyEnd afterwards
*
* @param string $output
*/
public function renderBodyEnd(&$output) {
if ($this->combineJs) {
if (isset($this->scriptFiles[parent::POS_END]) && count($this->scriptFiles[parent::POS_END]) !== 0) {
$jsFiles = $this->scriptFiles[parent::POS_END];
if (!empty($this->excludeFiles)) {
foreach ($jsFiles as &$fileName)
(in_array($fileName, $this->excludeFiles)) AND $fileName = false;
$jsFiles = array_filter($jsFiles);
}
$this->combineAndCompress('js', $jsFiles, parent::POS_END);
}
}
parent::renderBodyEnd($output);
}
/**
* Performs the actual combining and compressing
*
* @param string $type
* @param array $urls
* @param string $pos
*/
private function combineAndCompress($type, $urls, $pos) {
$this->fileUrl or $this->fileUrl = $this->getCoreScriptUrl();
$this->basePath or $this->basePath = Yii::getPathOfAlias('webroot');
$this->filePath or $this->filePath = Yii::getPathOfAlias('webroot') . $this->fileUrl;
$optionsHash = ($type == 'js')
? md5($this->basePath . $this->compressJs . $this->ttlDays . $this->prefix)
: md5($this->basePath . $this->compressCss . $this->ttlDays . $this->prefix . serialize($this->cssTidyConfig));
$group_changes = array();
if ($this->autoRefresh) {
$mtimes = array();
foreach($this->groups[$type] as $group => $group_urls) {
foreach($group_urls as $file) {
$fileName = $this->basePath . '/' . trim($file, '/');
if (file_exists($fileName)) {
$mtimes[] = filemtime($fileName);
}
}
$group_changes[$group] = md5(serialize($mtimes));
}
}
$files = array();
$changes = sizeof($group_changes);
foreach($this->groups[$type] as $group => $group_urls) {
$changedHash = ($changes) ? $group_changes[$group]: '';
$combineHash = md5(implode('', $group_urls));
$fileName = $this->prefix . md5($combineHash . $optionsHash . $changedHash) . ".{$type}";
$renewFile = (file_exists($this->filePath . '/' . $fileName)) ? false : true;
$files[$group] = array(
'hash' => $combineHash,
'name' => $fileName,
'renew' => $renewFile,
);
}
$cleared = false;
foreach($files as $group => $c_file) {
if($c_file['renew']) {
if(!$cleared) {
$this->garbageCollect($type);
$cleared = true;
}
$combinedFile = '';
foreach($this->groups[$type][$group] as $file)
$combinedFile .= file_get_contents($this->basePath . '/' . $file);
if ($type == 'js' && $this->compressJs)
$combinedFile = $this->minifyJs($combinedFile);
if ($type == 'css' && $this->compressCss)
$combinedFile = $this->minifyCss($combinedFile);
file_put_contents($this->filePath . '/' . $c_file['name'], $combinedFile);
}
}
foreach($this->groups[$type] as $group => $group_urls) {
foreach($group_urls as $url) {
$this->scriptMap[basename($url)] = $this->fileUrl . '/' . $files[$group]['name'];
}
}
$this->remapScripts();
}
private function garbageCollect($type) {
$files = CFileHelper::findFiles($this->filePath, array('fileTypes' => array($type), 'level' => 0));
foreach ($files as $file) {
if (strpos($file, $this->prefix) !== false && $this->fileTTL($file)) {
unlink($file);
}
}
}
/**
* See if file is ready for deletion
*
* @param string $file
*/
private function fileTTL($file) {
if (!file_exists($file)) return false;
$ttl = $this->ttlDays * 60 * 60 * 24;
return ((fileatime($file) + $ttl) < time()) ? true : false;
}
/**
* Minify javascript with JSMin
*
* @param string $js
*/
private function minifyJs($js) {
Yii::import('application.extensions.jsmin.*');
require_once('JSMin.php');
return JSMin::minify($js);
}
/**
* Yii-ified version of CSS.php of the Minify package with fixed options
*
* @param string $css
*/
private function minifyCss($css) {
Yii::import('application.extensions.csstidy.*');
require_once('class.csstidy.php');
$cssTidy = new csstidy();
$cssTidy->load_template($this->cssTidyTemplate);
foreach ($this->cssTidyConfig as $k => $v)
$cssTidy->set_cfg($k, $v);
$cssTidy->parse($css);
return $cssTidy->print->plain();
}
/**
* Registers a javascript file.
* @param string URL of the javascript file
* @param integer the position of the JavaScript code. Valid values include the following:
* <ul>
* <li>CClientScript::POS_HEAD : the script is inserted in the head section right before the title element.</li>
* <li>CClientScript::POS_BEGIN : the script is inserted at the beginning of the body section.</li>
* <li>CClientScript::POS_END : the script is inserted at the end of the body section.</li>
* </ul>
* @param string group. Using for grouping (compressing/minimazing). Default group - 'all'
*/
public function registerScriptFile($url, $position = 0, $group = 'all') {
parent::registerScriptFile($url, $position);
if(!isset($this->groups['js'][$group]))
$this->groups['js'][$group] = array();
$this->groups['js'][$group][] = $url;
}
/**
* Registers a CSS file
* @param string URL of the CSS file
* @param string media that the CSS file should be applied to. If empty, it means all media types.
* @param string group. Using for grouping (compressing/minimazing). Default group - 'all'
*/
public function registerCssFile($url, $media='', $group = 'all') {
parent::registerCssFile($url, $media);
if(!isset($this->groups['css'][$group]))
$this->groups['css'][$group] = array();
$this->groups['css'][$group][] = $url;
}
}