Michael Fillier Posted July 17, 2012 Share Posted July 17, 2012 I was not able to find anything in terms of css and javascript aggregation. Drupal does this wonderfully via a drupal_add_head() function that is used by themes and modules to add css and javascript to the head of the site. The theme then prints out a $head variable, instead of the hard-coded css and javascript includes. This allows you to enable a perfomance option of aggregation and compression. This then combines the files together, saves them on the server and links to the aggregated files instead of the individual files. This results in huge performance gains, especially if your site is using a css grid and any jquery plugins. Since each one comes with its own file and sometimes css styles. I will do some research into how Drupal processes the css and js files. But in the meantime, any feedback on where this fits into processwire would be appreciated. Link to comment Share on other sites More sharing options...
DaveP Posted July 17, 2012 Share Posted July 17, 2012 I think a similar effect can be achieved with http://code.google.com/p/minify/. I tend to get everything working without minify, just calling css and js in template files as required and then when everything works, minify into grouped css and js files as per the Minify docs. Link to comment Share on other sites More sharing options...
Pete Posted July 17, 2012 Share Posted July 17, 2012 Definitely worth checking out Minify if you have a lot of CSS and JS files (or even if you only have a few since it makes for less HTTP requests). It used to be a bit of a hassle to use in the early days, but it's really simple now and shrunk my JS files to less than half their original size, even squeezing already-minimised versions of the jQuery core down further (presumably these are compresse some other way). Link to comment Share on other sites More sharing options...
Soma Posted July 17, 2012 Share Posted July 17, 2012 ProcessWire has the functionality to add scripts and css using $config var. $config->scripts->add(pathto.js) $config->styles->append(pathto.css); Then the output <?php foreach($config->styles->unique() as $file) echo "\n <link type='text/css' href='$file' rel='stylesheet' />"; ?> <?php foreach($config->scripts->unique() as $file) echo "\n <script type='text/javascript' src='$file'></script>"; ?> This way a module could also add script using this. But there's no front-end module that does use it. It's up to the site builder. Link to comment Share on other sites More sharing options...
ryan Posted July 17, 2012 Share Posted July 17, 2012 ProcessWire doesn't get involved in markup generation by intention. But Soma is right that it does keep $config->styles and $config->scripts for you, if you happen to want to utilize them in generating your <head> markup. As for things to minify and combine scripts, I think they are worthwhile if the selection of files is reasonably static and the browser can cache and use it. But again, ProcessWire doesn't want to step on the developers toes by crossing the line into your output. We don't want to assume that we know what is best for every implementation and developer. ProcessWire may power many web sites but it's also powering mobile phone applications, web services, shell scripts and more. This forum has always been a great place to find out about new tools and resources we can use with ProcessWire too (I need to check out that Minify at Google Code). Link to comment Share on other sites More sharing options...
Soma Posted July 17, 2012 Share Posted July 17, 2012 I would question the need for a CMS to do this. This either can better and should be done using some setup server side or locally using grunt.js or similar there's many great resources. Edit: Not excluding that there could be a simple module helping setting it up. Not sure if that would make sense though. Link to comment Share on other sites More sharing options...
Martijn Geerts Posted July 17, 2012 Share Posted July 17, 2012 For minifying I sometimes use CodeKit It can minify css & js. It can combine files to one. Next to that if a css file is saved, the browser is refreshed without reloading the page & I can work with sass. This way you don't steal the resources from PHP . Link to comment Share on other sites More sharing options...
yellowled Posted July 17, 2012 Share Posted July 17, 2012 I usually build my sites using the HTML5 Boilerplate as a starting point, including a Sass version of its CSS files, an up-to-date jQuery and some additional stuff I usually need. H5BP also has an additional build script, which takes care of a lot of tasks, including minification and concatenation of CSS and JS files (although I don't really use CSS concatenation since Sass handles that). This also plays very nicely with ProcessWire's templates – a typical CSS reference like <link rel="stylesheet" href="<?php echo $config->urls->templates?>css/style.css"> is accepted by H5BP's build script (ant version) without any modification. The only thing I need to do is add all .inc files containing CSS/JS references (I like to put JS references at the bottom of the page) to the build script's project.properties files. That being said … well, with PW's user base (hopefully) growing, it will attract more "non-expert users", which is by no means a bad thing. But to these users, a module is usually much easier to use than an external PHP script or a build script which requires additional software to be installed on their system. So in the long run, PW might actually benefit from having that kind of module, if only because it makes it even easier to create well-performing sites. Link to comment Share on other sites More sharing options...
Michael Fillier Posted July 20, 2012 Author Share Posted July 20, 2012 Just took a look at an older Drupal module that aggregated and compressed javascript, here is a code snippet: /** * Helper function to minify and gzip files. */ function _javascript_aggregator_minify($scripts) { // Only process it is JavaScript Optimization is enabled. if (variable_get('preprocess_js', 0)) { // Strip out the aggregated JavaScript file. $path_to_files_directory = base_path() . file_directory_path(); $pattern = "!(<script type=\"text\/javascript\" src=\"(.*?)$path_to_files_directory)(.*?)(\"(.*?)><\/script>)!"; if (preg_match_all($pattern, $scripts, $matches) > 0) { $aggregated_file_name = $matches[3][0]; $jsmin_file_name = $aggregated_file_name .'min.js'; // Construct the final JSMin file path. $jsmin_file_path = file_directory_path() . $jsmin_file_name; // Create the JSMinified file if it doesn't exist yet. if (!file_exists($jsmin_file_path)) { if (variable_get('javascript_aggregator_jsminplus', FALSE)) { // JSMin+ the contents of the aggregated file. require_once(drupal_get_path('module', 'javascript_aggregator') .'/jsminplus.php'); // Strip Byte Order Marks (BOM's) from the file, JSMin+ cannot parse these. $file = str_replace(pack("CCC", 0xef, 0xbb, 0xbf), "", file_get_contents(file_directory_path() . $aggregated_file_name)); $contents = JSMinPlus::minify($file); } else { // JSMin the contents of the aggregated file. require_once(drupal_get_path('module', 'javascript_aggregator') .'/jsmin.php'); $contents = JSMin::minify(file_get_contents(file_directory_path() . $aggregated_file_name)); } // Code comments containing copyright notices and licensing information // are stripped when the file is minified. GPL and most other open // source licenses require the license text to be included whenever the // file is distributed, so include a reference to the un-minified file. $contents = '// Minified using Javascript Aggregator - see '. $path_to_files_directory . $aggregated_file_name ." for original source including licensing information.\n". $contents; // GZip the JavaScript if required. $htaccess = file_directory_path() . '/js/.htaccess'; if (variable_get('javascript_aggregator_gzip', FALSE)) { // Create the GZip file if it doesn't already exist. if (!file_exists($jsmin_file_path .'.gz')) { file_save_data(gzencode($contents, 9), $jsmin_file_path .'.gz', FILE_EXISTS_REPLACE); } // Make sure the .htaccess file is active to handle GZipped JavaScript files. if (!variable_get('javascript_aggregator_no_htaccess', FALSE) && !file_exists($htaccess)) { $rewrite_base = base_path() . file_directory_path() .'/js/'; $htaccess_contents = <<<EOT <Files *.js.gz> AddEncoding x-gzip .gz ForceType text/javascript </Files> <IfModule mod_rewrite.c> RewriteEngine on RewriteBase $rewrite_base RewriteCond %{HTTP_USER_AGENT} !".*Safari.*" RewriteCond %{HTTP:Accept-encoding} gzip RewriteCond %{REQUEST_FILENAME}.gz -f RewriteRule ^(.*)\.js $1.js.gz [L,QSA] </IfModule> EOT; file_save_data($htaccess_contents, $htaccess, FILE_EXISTS_REPLACE); } } else { // Delete .htaccess file so *.gz files do not get served. if (file_exists($htaccess)) { file_delete($htaccess); } } // Save the contents to the JavaScript file. file_save_data($contents, $jsmin_file_path, FILE_EXISTS_REPLACE); } // Replace the aggregated file with the minified JavaScript file. $scripts = str_replace($aggregated_file_name, $jsmin_file_name, $scripts); } } return $scripts; } I am not a very experienced coder, so I want to know what your take is on this approach. The drupal module can be downloaded here: http://ftp.drupal.org/files/projects/javascript_aggregator-6.x-1.6.zip Maybe we could adapt this to processwire and expand its functionality to css files as well. Link to comment Share on other sites More sharing options...
Pete Posted July 20, 2012 Share Posted July 20, 2012 I would still choose Minify since it already handles CSS as well and doesn't require the .htaccess: http://code.google.com/p/minify/ I've got it working on one site already, but I don't know that it's really worth a module to be honest since its already easy to set up (there's a helper script included that pretty much builds the URL for you). I just put mine in somewhere like /site/lib/min/ and put the relevant URL in head.inc for the JS and CSS. A module could save a little time, but in the case of Minify it would be about ten seconds EDIT: I did just think of a way a module could save a lot more time actually so I might build this module soon. EDIT 2: my first thought was to make it inject the relevant code before the head tag but that's nt always ideal plus you don't always want all scripts/stylesheets in every template. I think I might just make it give the relevant code to copy and paste into the template instead. Link to comment Share on other sites More sharing options...
Michael Fillier Posted July 20, 2012 Author Share Posted July 20, 2012 Since this is a common use case (I mean who doesn't like compressed, aggregated javascript and css?) I think it would be a good candidate for a module. Looking at minify further, perhaps we can use soma's concept and feed the files to minify. ProcessWire has the functionality to add scripts and css using $config var. $config->scripts->add(pathto.js) $config->styles->append(pathto.css); Then the output <?php foreach($config->styles->unique() as $file) echo "\n <link type='text/css' href='$file' rel='stylesheet' />"; ?> <?php foreach($config->scripts->unique() as $file) echo "\n <script type='text/javascript' src='$file'></script>"; ?> This way a module could also add script using this. But there's no front-end module that does use it. It's up to the site builder. But since this approach will require a site builder to adapt their code to use config instead of plain old link and script tags, it is probably not favourable. I think most logical approach would be for the module to preg_match_all linked css and javascript files, serve the urls to minify and then replace the individual linked references with the minify'd references. Is there a hook that would let a module modify the page markup before rendering the page? Or would the approach I mentioned need to involve code in the theme. Link to comment Share on other sites More sharing options...
Pete Posted July 20, 2012 Share Posted July 20, 2012 I'm halfway through a module for this already, but it will still just build a URL from a selection of files to paste into the template. The whole ethos of ProcessWire is not making assumptions about what people are going to do with it or how they're going to work, so reducing the Minify process to a simple copy and paste is about as far as I feel comfortable taking it. There are occasions where you won't want every JS or CSS file being called for every page so I don't want it to make assumptions or rewrite anyone's HTML markup if I can help it. It'll be a simple solution though - I promise You will always have code in your templates by the way so that's not a problem. Take a look through the default template files in the site/templates directory to see what I mean. 1 Link to comment Share on other sites More sharing options...
Michael Fillier Posted July 20, 2012 Author Share Posted July 20, 2012 Fair enough. How do you currently handle having different css and javascript files on different pages? Link to comment Share on other sites More sharing options...
Michael Fillier Posted July 20, 2012 Author Share Posted July 20, 2012 Also, interested in checking out the module when you have it ready. Thanks. Link to comment Share on other sites More sharing options...
Pete Posted July 20, 2012 Share Posted July 20, 2012 What I meant was different files for different templates, so on one site I load extra JS and CSS on a page containing a Google Map and in that case I simply set that page to use a different template and in the head.inc file check the current page's template and - if it matches 'map' for example - output the relevant extra JS and CSS. Module should be ready tomorrow or Sunday at the latest. Link to comment Share on other sites More sharing options...
Pete Posted July 21, 2012 Share Posted July 21, 2012 Here you go: 1 Link to comment Share on other sites More sharing options...
Michael Fillier Posted July 22, 2012 Author Share Posted July 22, 2012 What I meant was different files for different templates, so on one site I load extra JS and CSS on a page containing a Google Map and in that case I simply set that page to use a different template and in the head.inc file check the current page's template and - if it matches 'map' for example - output the relevant extra JS and CSS. Module should be ready tomorrow or Sunday at the latest. Thanks, I was wondering the best way to do that. Switching on the template in header.inc is a great solution. Taking a look at the module right now. Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now