Index: update.php =================================================================== RCS file: /cvs/drupal/drupal/update.php,v retrieving revision 1.211 diff -u -F^f -r1.211 update.php --- update.php 25 Dec 2006 21:22:03 -0000 1.211 +++ update.php 19 Feb 2007 18:24:06 -0000 @@ -403,8 +403,8 @@ function update_update_page() { function update_progress_page() { // Prevent browser from using cached drupal.js or update.js - drupal_add_js('misc/progress.js', 'core', 'header', FALSE, TRUE); - drupal_add_js('misc/update.js', 'core', 'header', FALSE, TRUE); + drupal_add_js('misc/progress.js', 'core', 'header', FALSE, FALSE); + drupal_add_js('misc/update.js', 'core', 'header', FALSE, FALSE); drupal_set_title('Updating'); $output = '
'; @@ -446,6 +446,7 @@ function update_do_updates() { cache_clear_all('*', 'cache_menu', TRUE); cache_clear_all('*', 'cache_filter', TRUE); drupal_clear_css_cache(); + drupal_clear_js_cache(); } return array($percentage, isset($update['module']) ? 'Updating '. $update['module'] .' module' : 'Updating complete'); Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.617 diff -u -F^f -r1.617 common.inc --- includes/common.inc 15 Feb 2007 11:40:17 -0000 1.617 +++ includes/common.inc 19 Feb 2007 18:24:08 -0000 @@ -1629,24 +1629,24 @@ function drupal_clear_css_cache() { * (optional) If set to FALSE, the JavaScript file is loaded anew on every page * call, that means, it is not cached. Defaults to TRUE. Used only when $type * references a JavaScript file. + * @param $preprocess + * (optional) Should this JS file be aggregated and compressed if this + * feature has been turned on under the performance section? * @return * If the first parameter is NULL, the JavaScript array that has been built so * far for $scope is returned. */ -function drupal_add_js($data = NULL, $type = 'module', $scope = 'header', $defer = FALSE, $cache = TRUE) { - if (!is_null($data)) { - _drupal_add_js('misc/jquery.js', 'core', 'header', FALSE, $cache); - _drupal_add_js('misc/drupal.js', 'core', 'header', FALSE, $cache); - } - return _drupal_add_js($data, $type, $scope, $defer, $cache); -} - -/** - * Helper function for drupal_add_js(). - */ -function _drupal_add_js($data, $type, $scope, $defer, $cache) { - static $javascript = array(); - +function drupal_add_js($data = NULL, $type = 'module', $scope = 'header', $defer = FALSE, $cache = TRUE, $preprocess = TRUE) { + static $javascript = array( + 'header' => array( + 'core' => array( + 'misc/jquery.js' => array('cache' => TRUE, 'defer' => FALSE, 'preprocess' => TRUE), + 'misc/drupal.js' => array('cache' => TRUE, 'defer' => FALSE, 'preprocess' => TRUE), + ), + 'module' => array(), 'theme' => array(), 'setting' => array(), 'inline' => array(), + ), + ); + if (!isset($javascript[$scope])) { $javascript[$scope] = array('core' => array(), 'module' => array(), 'theme' => array(), 'setting' => array(), 'inline' => array()); } @@ -1655,7 +1655,7 @@ function _drupal_add_js($data, $type, $s $javascript[$scope][$type] = array(); } - if (!is_null($data)) { + if (isset($data)) { switch ($type) { case 'setting': $javascript[$scope][$type][] = $data; @@ -1663,8 +1663,9 @@ function _drupal_add_js($data, $type, $s case 'inline': $javascript[$scope][$type][] = array('code' => $data, 'defer' => $defer); break; - default: - $javascript[$scope][$type][$data] = array('cache' => $cache, 'defer' => $defer); + default: + // If cache is false, then don't preprocess the JS file. + $javascript[$scope][$type][$data] = array('cache' => $cache, 'defer' => $defer, 'preprocess' => (!$cache ? FALSE : $preprocess)); } } @@ -1689,10 +1690,23 @@ function _drupal_add_js($data, $type, $s */ function drupal_get_js($scope = 'header', $javascript = NULL) { $output = ''; - if (is_null($javascript)) { + if (!isset($javascript)) { $javascript = drupal_add_js(NULL, NULL, $scope); + } + + if (count($javascript) < 1) { + return $output; } + $no_core_preprocess = ''; + $no_module_preprocess = ''; + $no_theme_preprocess = ''; + $files = array(); + $preprocess_js = variable_get('preprocess_js', FALSE); + $directory = file_directory_path(); + $is_writable = is_dir($directory) && is_writable($directory) && (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PUBLIC); + + foreach ($javascript as $type => $data) { if (!$data) continue; @@ -1706,13 +1720,86 @@ function drupal_get_js($scope = 'header' } break; default: + + // If JS preprocessing is off, we still need to output the scripts. + // Additionally, go through any remaining scripts if JS preprocessing is on and output the non-cached ones. foreach ($data as $path => $info) { - $output .= '\n"; + if (!$info['preprocess'] || !($is_writable && $info['preprocess']) || !$preprocess_js) { + // If a JS file is not to be preprocessed and it's a core JS file, it needs to *always* appear at the *top*, + // regardless of whether preprocessing is on or off. + if (!$info['preprocess'] && $type == 'core') { + $no_core_preprocess .= '\n"; + } + // If a JS file is not to be preprocessed and it's a module JS file, it needs to *always* appear at the *top*, + // regardless of whether preprocessing is on or off. + else if (!$info['preprocess'] && $type == 'module') { + $no_module_preprocess .= '\n"; + } + // If a JS file is not to be preprocessed and it's a theme JS file, it needs to *always* appear at the *bottom*, + // regardless of whether preprocessing is on or off. + else if (!$info['preprocess'] && $type == 'theme') { + $no_theme_preprocess .= '\n"; + } + else { + $output .= '\n"; + } + } + else { + $files[$path] = $info; + } } } } - return $output; + // Aggregate any remaining JS files that haven't already been output. + if ($is_writable && $preprocess_js && count($files) > 0) { + $filename = md5(serialize($files)) .'.js'; + $preprocess_file = drupal_build_js_cache($files, $filename); + $output .= ''. "\n"; + } + + return $no_core_preprocess . $no_module_preprocess . $output . $no_theme_preprocess; +} + +/** + * Aggregate JS files, putting them in the files directory. + * + * @param $files + * An array of JS files to aggregate and compress into one file. + * @param $filename + * The name of the aggregate JS file. + * @return + * The name of the JS file. + */ +function drupal_build_js_cache($files, $filename) { + $contents = ''; + + // Create the css/ within the files folder. + $jspath = file_create_path('js'); + file_check_directory($jspath, FILE_CREATE_DIRECTORY); + + if (!file_exists($jspath .'/'. $filename)) { + // Build aggregate JS file. + foreach ($files as $path => $info) { + if ($info['preprocess']) { + $contents .= file_get_contents($path); + } + } + + // Any compression to do should go here + + // Create the JS file. + file_save_data($contents, $jspath .'/'. $filename, FILE_EXISTS_REPLACE); + } + + return $jspath .'/'. $filename; +} + +/** + * Delete all cached JS files. + */ +function drupal_clear_js_cache() { + file_scan_directory(file_create_path('js'), '.*', array('.', '..', 'CVS'), 'file_delete', TRUE); } /** Index: modules/system/system.module =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.module,v retrieving revision 1.451 diff -u -F^f -r1.451 system.module --- modules/system/system.module 15 Feb 2007 11:40:18 -0000 1.451 +++ modules/system/system.module 19 Feb 2007 18:24:10 -0000 @@ -698,9 +698,19 @@ function system_performance_settings() { '#options' => array(t('Disabled'), t('Enabled')), '#description' => t("Some Drupal modules include their own CSS files. When these modules are enabled, each module's CSS file adds an additional HTTP request to the page, which can increase the load time of each page. These HTTP requests can also slightly increase server load. It is recommended to only turn this option on when your site is in production, as it can interfere with theme development. This option is disabled if you have not set up your files directory, or if your download method is set to private."), ); + + $form['bandwidth_optimizations']['preprocess_js'] = array( + '#type' => 'radios', + '#title' => t('Aggregate and compress JS files'), + '#default_value' => variable_get('preprocess_js', FALSE) && $is_writable, + '#disabled' => !$is_writable, + '#options' => array(t('Disabled'), t('Enabled')), + '#description' => t("Some Drupal modules include their own JS files. When these modules are enabled, each module's JS file adds an additional HTTP request to the page, which can increase the load time of each page. These HTTP requests can also slightly increase server load. It is recommended to only turn this option on when your site is in production, as it can interfere with module development. This option is disabled if you have not set up your files directory, or if your download method is set to private."), + ); $form['#submit']['system_settings_form_submit'] = array(); - $form['#submit']['drupal_clear_css_cache'] = array(); + $form['#submit']['drupal_clear_css_cache'] = array(); + $form['#submit']['drupal_clear_js_cache'] = array(); return system_settings_form($form); } @@ -1514,6 +1524,7 @@ function system_modules_submit($form_id, } drupal_clear_css_cache(); + drupal_clear_js_cache(); return 'admin/build/modules'; }