* @version 1.2 * @license http://www.opensource.org/licenses/bsd-license.php BSD licensed. */ /* =============================================================================================== USAGE =============================================================================================== Load the library as normal: ----------------------------------------------------------------------------------------------- $this->load->library('carabiner'); ----------------------------------------------------------------------------------------------- Configure it like so: ----------------------------------------------------------------------------------------------- $carabiner_config = array( 'script_dir' => 'assets/scripts/', 'style_dir' => 'assets/styles/', 'cache_dir' => 'assets/cache/', 'base_uri' => $base, 'combine' => TRUE, 'dev' => FALSE ); $this->carabiner->config($carabiner_config); ----------------------------------------------------------------------------------------------- There are 8 options. 4 are required: script_dir STRING Path to the script directory. Relative to the CI front controller (index.php) style_dir STRING Path to the style directory. Relative to the CI front controller (index.php) cache_dir STRING Path to the cache directory. Must be writable. Relative to the CI front controller (index.php) base_uri STRING Base uri of the site, like http://www.example.com/ 4 are not required: dev BOOL Flags whether your in a development environment or not. See above for what this means. Defaults to FALSE. combine BOOLEAN Flags whether to combine files. Defaults to TRUE. minify_js BOOLEAN Flags whether to minify javascript. Defaults to TRUE. minify_css BOOLEAN Flags whether to minify CSS. Defaults to TRUE. Add assets like so: ----------------------------------------------------------------------------------------------- // add a js file $this->carabiner->js('scripts.js'); // add a css file $this->carabiner->css('reset.css'); // add a css file with a mediatype $this->carabiner->css('admin/print.css','print'); ----------------------------------------------------------------------------------------------- To set a (prebuilt) production version of a js asset: ----------------------------------------------------------------------------------------------- // JS: pass a second string to the method with a path to the production version $this->carabiner->js('wymeditor/wymeditor.js', 'wymeditor/wymeditor.pack.js' ); // add a css file with prebuilt production version $this->carabiner->css('framework/type.css', 'screen', 'framework/type.pack.css'); ----------------------------------------------------------------------------------------------- And to prevent a file from being combined: ----------------------------------------------------------------------------------------------- // JS: pass a boolean FALSE as the third attribute of the method $this->carabiner->js('wymeditor/wymeditor.js', 'wymeditor.pack.js', FALSE ); // CSS: pass a boolean FALSE as the fourth attribute of the method $this->carabiner->css('framework/type.css', 'screen', 'framework/type.pack.css', FALSE); ----------------------------------------------------------------------------------------------- You can also pass arrays (and arrays of arrays) to these methods. Like so: ----------------------------------------------------------------------------------------------- // a single array $this->carabiner->css( array('mobile.css', 'handheld', 'mobile.prod.css') ); // an array of arrays $js_assets = array( array('dev/jquery.js', 'prod/jquery.js'), array('dev/jquery.ext.js', 'prod/jquery.ext.js'), ) $this->carabiner->js( $js_assets ); ----------------------------------------------------------------------------------------------- To output your assets, including appropriate markup: ----------------------------------------------------------------------------------------------- // display css $this->carabiner->display('css'); //display js $this->carabiner->display('js'); ----------------------------------------------------------------------------------------------- Since Carabiner won't delete old cached files, you'll need to clear them out manually. To do so programatically: ----------------------------------------------------------------------------------------------- // clear css cache $this->carabiner->empty_cache('css'); //clear js cache $this->carabiner->empty_cache('js'); // clear both $this->carabiner->empty_cache(); ----------------------------------------------------------------------------------------------- =============================================================================================== */ class Carabiner { var $base_uri = ''; var $script_dir = ''; var $script_path = ''; var $script_uri = ''; var $style_dir = ''; var $style_path = ''; var $style_uri = ''; var $cache_dir = ''; var $cache_path = ''; var $cache_uri = ''; var $dev = FALSE; var $combine = TRUE; var $minify_js = TRUE; var $minify_css = TRUE; var $js = array(); var $css = array(); var $CI; /** * Class Constructor */ public function __construct() { $this->CI =& get_instance(); log_message('debug', 'Carabiner Asset Management library initialized.'); } /** * Load Config * @param Array of config variables. Requires script_dir(string), style_dir(string), cache_dir(string), and base_uri(string). * dev(bool), combine(bool), minify_js(bool), minify_css(bool) are optional. */ public function config($config) { foreach ($config as $key => $value) { $this->$key = $value; } // use the provided values to define the rest of them //$this->script_path = dirname(BASEPATH).'/'.$this->script_dir; $this->script_path = $this->script_dir; $this->script_uri = $this->base_uri.$this->script_dir; //$this->style_path = dirname(BASEPATH).'/'.$this->style_dir; $this->style_path = $this->style_dir; $this->style_uri = $this->base_uri.$this->style_dir; //$this->cache_path = dirname(BASEPATH).'/'.$this->cache_dir; $this->cache_path = $this->cache_dir; $this->cache_uri = $this->base_uri.$this->cache_dir; } /** * Add JS file to queue * @param String of the path to development version of the JS file. Could also be an array, or array of arrays. * @param String of the path to production version of the JS file. NOT REQUIRED * @param Flag whether the file is to be combined NOT REQUIRED */ public function js($dev_file, $prod_file = '', $combine = TRUE) { if( is_array($dev_file) ){ if( is_array($dev_file[0]) ){ foreach($dev_file as $file){ $d = $file[0]; $p = (isset($file[1])) ? $file[1] : ''; $c = (isset($file[2])) ? $file[2] : $combine; $this->_asset('js', $d, $p, $c); } }else{ $d = $dev_file[0]; $p = (isset($dev_file[1])) ? $dev_file[1] : ''; $c = (isset($dev_file[2])) ? $dev_file[2] : $combine; $this->_asset('js', $d, $p, $c); } }else{ $this->_asset('js', $dev_file, $prod_file, $combine); } } /** * Add CSS file to queue * @param String of the path to development version of the CSS file. Could also be an array, or array of arrays. * @param String of the media type, usually one of (screen, print, handheld) for css. Defaults to screen. * @param String of the path to production version of the CSS file. NOT REQUIRED * @param Flag whether the file is to be combined. NOT REQUIRED */ public function css($dev_file, $media = 'all', $prod_file = '', $combine = TRUE) { if( is_array($dev_file) ){ if( is_array($dev_file[0]) ){ foreach($dev_file as $file){ $d = $file[0]; $m = (isset($file[1])) ? $file[1] : $media; $p = (isset($file[2])) ? $file[2] : ''; $c = (isset($file[3])) ? $file[3] : $combine; $this->_asset('css', $d, $p, $c, $m); } }else{ $d = $dev_file[0]; $m = (isset($file[1])) ? $file[1] : $media; $p = (isset($dev_file[2])) ? $dev_file[2] : ''; $c = (isset($dev_file[3])) ? $dev_file[3] : $combine; $this->_asset('css', $d, $p, $c, $m); } }else{ $this->_asset('css', $dev_file, $prod_file, $combine, $media); } } /** * Add an asset to queue * @param String of the type of asset (lowercase) . css | js * @param String of the path to development version of the asset. * @param String of the path to production version of the asset. NOT REQUIRED * @param Flag whether the file is to be combined. Defaults to true. NOT REQUIRED * @param String of the media type associated with the asset. Only applicable to CSS assets. NOT REQUIRED */ private function _asset($type, $dev_file, $prod_file = '', $combine = TRUE, $media = 'screen') { if ($type == 'css') : $this->css[$media][] = array( 'dev'=>$dev_file ); $index = count($this->css[$media]) - 1; if($prod_file != '') $this->{$type}[$media][$index]['prod'] = $prod_file; $this->css[$media][$index]['combine'] = $combine; else : $this->{$type}[] = array( 'dev'=>$dev_file ); $index = count($this->{$type}) - 1; if($prod_file != '') $this->js[$index]['prod'] = $prod_file; $this->js[$index]['combine'] = $combine; endif; } /** * Display HTML references to the assets * @param Flag the asset type: css|js */ public function display($flag) { switch($flag){ case 'js': if( empty($this->js) ) return; // if there aren't any js files, just stop! // if we're in a dev environment if($this->dev){ foreach($this->js as $ref): echo $this->_tag('js', $ref['dev']); endforeach; // if we're combining files } elseif($this->combine) { $lastmodified = 0; $files = array(); $filenames = ''; $not_combined = ''; foreach ($this->js as $ref) { // get the last modified date of the most recently modified file $lastmodified = max( $lastmodified , filemtime( realpath($this->script_path.$ref['dev']) ) ); $filenames .= $ref['dev']; if($ref['combine'] == false): $not_combined .= (isset($ref['prod'])) ? $this->_tag('js', $ref['prod']) : $this->_tag('js', $ref['dev']); else: $files[] = (isset($ref['prod'])) ? array('prod'=>$ref['prod'], 'dev'=>$ref['dev'] ) : array('dev'=>$ref['dev']); endif; } $filename = $lastmodified . md5($filenames).'.js'; if( !file_exists($this->cache_path.$filename) ) $this->_combine('js', $files, $filename); echo $not_combined; echo $this->_tag('js', $filename, TRUE); // if we're minifying. but not combining } elseif(!$this->combine && $this->minify_js) { // minify each file, cache it, and serve it up. Oy. foreach($this->js as $ref): if( isset($ref['prod']) ){ $f = $ref['prod']; echo $this->_tag('js', $f, FALSE); } else { $f = filemtime( realpath( $this->script_path . $ref['dev'] ) ) . md5($ref['dev']) . '.js'; if( !file_exists($this->cache_path.$f) ): $c = $this->_minify( 'js', $ref['dev'] ); $this->_cache($f, $c); endif; echo $this->_tag('js', $f, TRUE); } endforeach; }else{ foreach($this->js as $ref): $f = (isset($ref['prod'])) ? $ref['prod'] : $ref['dev']; echo $this->_tag('js', $f); endforeach; } break; case 'css': if( empty($this->css) ) return; // there aren't any css assets, so just stop! if($this->dev){ // we're in a development environment foreach($this->css as $media => $refs): foreach($refs as $ref): echo $this->_tag('css', $ref['dev'], FALSE, $media); endforeach; endforeach; } elseif($this->combine) { // we're combining and minifying foreach($this->css as $media => $refs): // lets try to cache it, shall we? $lastmodified = 0; $files = array(); $filenames = ''; $not_combined = ''; foreach ($refs as $ref): $lastmodified = max($lastmodified, filemtime( realpath( $this->style_path . $ref['dev'] ) ) ); $filenames .= $ref['dev']; if($ref['combine'] == false): $not_combined .= (isset($ref['prod'])) ? $this->_tag('css', $ref['prod'], $media) : $this->_tag('css', $ref['dev'], $media); else: $files[] = (isset($ref['prod'])) ? array('prod'=>$ref['prod'], 'dev'=>$ref['dev'] ) : array('dev'=>$ref['dev']); endif; endforeach; $filename = $lastmodified . md5($filenames).'.css'; if( !file_exists($this->cache_path.$filename) ) $this->_combine('css', $files, $filename); echo $not_combined; echo $this->_tag('css', $filename, TRUE, $media); endforeach; } elseif(!$this->combine && $this->minify_css) { // we want to minify, but not combine foreach($this->css as $media => $refs): foreach($refs as $ref): if( isset($ref['prod']) ){ $f = $this->style_uri . $ref['prod']; } else { $f = filemtime( realpath( $this->style_path . $ref['dev'] ) ) . md5($ref['dev']) . '.css'; if( !file_exists($this->cache_path.$f) ): $c = $this->_minify( 'css', $ref['dev'] ); $this->_cache($f, $c); endif; } echo $this->_tag('css', $f, TRUE, $media); endforeach; endforeach; }else{ // we're in a production environment, but not minifying or combining foreach($this->css as $media => $refs): foreach($refs as $ref): $f = (isset($ref['prod'])) ? $ref['prod'] : $ref['dev']; echo $this->_tag('css', $f, FALSE, $media); endforeach; endforeach; } break; } } /** * Internal function for compressing/combining scripts * @param Flag the asset type: css|js * @param array of file references to be combined. Should contain arrays, as included in primary asset arrays: ('dev'=>$dev, 'prod'=>$prod) * @param Filename of the file-to-be */ private function _combine($flag, $files, $filename) { $file_data = ''; $path = ($flag == 'css') ? $this->style_path : $this->script_path; $minify = ($flag == 'css') ? $this->minify_css : $this->minify_js; foreach($files as $file): if($minify): $file_data .= $this->_minify( $flag, $file['dev'] ) . "\n"; else: $file_data .= file_get_contents( realpath($path.$file['dev']) ) ."\n"; endif; endforeach; $this->_cache( $filename, $file_data ); } /** * Internal function for minifying assets * @param Flag the asset type: css|js * @param Contents to be minified */ private function _minify($flag, $file_ref) { switch($flag){ case 'js': $this->CI->load->library('jsmin'); $contents = file_get_contents( realpath($this->script_path.$file_ref) ); return $this->CI->jsmin->minify($contents); break; case 'css': $this->CI->load->library('cssmin'); $this->CI->cssmin->config(array('relativePath'=>$this->style_uri)); $contents = file_get_contents( realpath($this->style_path.$file_ref) ); return $this->CI->cssmin->minify($contents); break; } } /** * Internal function for writing cache files * @param filename of the new file * @param Contents of the new file */ private function _cache($filename, $file_data) { $filepath = $this->cache_path . $filename; //change ernscht: $filepath = realpath($this->cache_path . $filename); file_put_contents( $filepath, $file_data ); } /** * Internal function for making tag strings * @param flag for type: css|js * @param Reference of file. * @param Flag for cache dir. Defaults to FALSE. * @param Media type for the tag. Only applies to CSS links. defaults to 'screen' */ private function _tag($flag, $ref, $cache = FALSE, $media = 'screen') { switch($flag){ case 'css': $dir = ($cache) ? $this->cache_uri : $this->style_uri; echo ''."\r\n"; break; case 'js': $dir = ($cache) ? $this->cache_uri : $this->script_uri; echo ''."\r\n"; break; } } /** * Function used to clear the asset cache. If no flag is set, both CSS and JS will be emptied. * @param Flag the asset type: css|js|both */ public function empty_cache($flag) { $this->CI->load->helper('file'); $files = get_filenames($this->cache_path); switch($flag){ case 'js': case 'css': foreach( $files as $file ){ $ext = substr( strrchr( $file, '.' ), 1 ); if ( $ext == $flag ) { unlink( $this->cache_path . $file ); } } break; default: foreach( $files as $file ){ $ext = substr( strrchr( $file, '.' ), 1 ); if ( $ext == 'js' || $ext == 'css') { unlink( $this->cache_path . $file ); } } break; } } }