=== modified file 'includes/common.inc' --- includes/common.inc 2009-07-31 07:27:59 +0000 +++ includes/common.inc 2009-07-31 16:32:06 +0000 @@ -2954,8 +2954,12 @@ function drupal_get_js($scope = 'header' $no_preprocess = ''; $files = array(); $preprocess_js = (variable_get('preprocess_js', FALSE) && (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update')); - $directory = file_directory_path(); - $is_writable = is_dir($directory) && is_writable($directory) && (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PUBLIC); + if ($preprocess_js) { + // Only check the preprocessor requirements if it's going to be + // preprocessing. If these requirements are not met, preprocessing + // will be disabled. + $preprocess_js = drupal_js_preprocessor()->requirements(); + } // A dummy query-string is added to filenames, to gain control over // browser-caching. The string changes on every update or full cache @@ -2986,7 +2990,7 @@ function drupal_get_js($scope = 'header' break; case 'file': - if (!$item['preprocess'] || !$is_writable || !$preprocess_js) { + if (!$item['preprocess'] || !$preprocess_js) { $no_preprocess .= '\n"; } else { @@ -3002,12 +3006,8 @@ function drupal_get_js($scope = 'header' } // Aggregate any remaining JS files that haven't already been output. - if ($is_writable && $preprocess_js && count($files) > 0) { - // Prefix filename to prevent blocking by firewalls which reject files - // starting with "ad*". - $filename = 'js_' . md5(serialize($files) . $query_string) . '.js'; - $preprocess_file = drupal_build_js_cache($files, $filename); - $preprocessed .= '' . "\n"; + if ($preprocess_js && count($files)) { + $preprocessed .= drupal_js_preprocessor()->preprocess($files); } // Keep the order of JS files consistent as some are preprocessed and others are not. @@ -3265,44 +3265,66 @@ function drupal_add_tabledrag($table_id, } /** - * 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. + * Returns a JavaScript preprocessing object that implements JSPreprocessingInterface. */ -function drupal_build_js_cache($files, $filename) { - $contents = ''; - - // Create the js/ 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']) { - // Append a ';' after each JS file to prevent them from running together. - $contents .= file_get_contents($path) . ';'; +function drupal_js_preprocessor() { + static $instance; + if (empty($instance)) { + // Retrieve the preprocess system. + $class = variable_get('preprocess_js_system', 'DrupalPreprocessJS'); + + // Allow for lazy loading of the class. + if (drupal_autoload_class($class)) { + $interfaces = class_implements($class); + if (isset($interfaces['JSPreprocessingInterface'])) { + $instance = new $class; + } + else { + throw new Exception(t('Class %class does not implement interface %interface', array('%class' => $class, '%interface' => 'JSPreprocessingInterface'))); } } - - // Create the JS file. - file_unmanaged_save_data($contents, $jspath . '/' . $filename, FILE_EXISTS_REPLACE); + else { + throw new Exception(t('Class %class was not found.', array('%class' => $class))); + } } + return $instance; +} + +/** + * The interface for preprocessing JavaScript. + */ +interface JSPreprocessingInterface { + /** + * Check preprocessing requirements. + * + * @return + * TRUE if requirements met to preform preprocessing. + */ + public function requirements(); + + /** + * Preprocess JavaScript files. + * + * @param $files + * An array of JavaScript files. + * + * @return + * HTML for preprocessed JavaScript files. + */ + public function preprocess($files); - return $jspath . '/' . $filename; + /** + * Clear JS cache. + */ + public function clear(); } + /** - * Delete all cached JS files. + * Clear JS cache. */ function drupal_clear_js_cache() { - file_scan_directory(file_create_path('js'), '/.*/', array('callback' => 'file_unmanaged_delete')); - variable_set('javascript_parsed', array()); + drupal_js_preprocessor()->clear(); } /** === modified file 'modules/simpletest/tests/common.test' --- modules/simpletest/tests/common.test 2009-07-30 19:57:09 +0000 +++ modules/simpletest/tests/common.test 2009-07-31 17:03:42 +0000 @@ -504,6 +504,7 @@ class JavaScriptTestCase extends DrupalW * Store configured value for JavaScript preprocessing. */ protected $preprocess_js = NULL; + protected $preprocess_plugin = NULL; public static function getInfo() { return array( @@ -520,6 +521,7 @@ class JavaScriptTestCase extends DrupalW // Disable preprocessing $this->preprocess_js = variable_get('preprocess_js', 0); variable_set('preprocess_js', 0); + $this->preprocess_plugin = variable_get('preprocess_js_system', 'DrupalPreprocessJS'); // Reset drupal_add_js() and drupal_add_library() statics before each test. drupal_static_reset('drupal_add_js'); @@ -529,6 +531,7 @@ class JavaScriptTestCase extends DrupalW function tearDown() { // Restore configured value for JavaScript preprocessing. variable_set('preprocess_js', $this->preprocess_js); + variable_set('preprocess_js_system', $this->preprocess_plugin); parent::tearDown(); } @@ -744,6 +747,23 @@ class JavaScriptTestCase extends DrupalW $scripts = drupal_get_js(); $this->assertTrue(strpos($scripts, 'unknown') === FALSE, t('Unknown library was not added to the page.')); } + + /** + * Tests the pluggable JavaScript preprocessor system. + */ + function testPluggablePreprocessor() { + // Switch the preprocessor. + variable_set('preprocess_js', TRUE); + variable_set('preprocess_js_system', 'testJSPreprocessing'); + + // Add some JavaScript to test the preprocessor system. + drupal_add_js('misc/tableselect.js'); + + // Retrieve the JavaScript, running through the JavaScriptTestCase preprocessor. + $javascript = drupal_get_js(); + $this->assertTrue(drupal_js_preprocessor()->requirements(), t('Pluggable preprocessor system requirements are checked.')); + $this->assertTrue(strpos($javascript, 'has been preprocessed') > 0, t('Preprocessor system is pluggable.')); + } } /** === modified file 'modules/simpletest/tests/common_test.module' --- modules/simpletest/tests/common_test.module 2009-07-04 18:26:42 +0000 +++ modules/simpletest/tests/common_test.module 2009-07-31 17:03:29 +0000 @@ -55,3 +55,17 @@ function common_test_library() { ); return $libraries; } + +class testJSPreprocessing implements JSPreprocessingInterface { + public function requirements() { + return TRUE; + } + + public function preprocess($files) { + return 'JavaScript has been preprocessed.'; + } + + public function clear() { + + } +} \ No newline at end of file