Index: modules/color/color.module =================================================================== RCS file: /cvs/drupal/drupal/modules/color/color.module,v retrieving revision 1.59 diff -u -r1.59 color.module --- modules/color/color.module 27 May 2009 18:33:55 -0000 1.59 +++ modules/color/color.module 18 Jun 2009 21:05:57 -0000 @@ -169,8 +169,7 @@ $info = color_get_info($theme); // Add Farbtastic color picker. - drupal_add_css('misc/farbtastic/farbtastic.css', array('preprocess' => FALSE)); - drupal_add_js('misc/farbtastic/farbtastic.js', array('weight' => JS_LIBRARY)); + drupal_add_plugin('farbtastic'); // Add custom CSS and JS. drupal_add_css($base . '/color.css', array('preprocess' => FALSE)); Index: modules/system/system.api.php =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.api.php,v retrieving revision 1.42 diff -u -r1.42 system.api.php --- modules/system/system.api.php 18 Jun 2009 10:20:22 -0000 1.42 +++ modules/system/system.api.php 18 Jun 2009 21:05:58 -0000 @@ -183,6 +183,116 @@ } /** + * Registers any JavaScript plugins associated with the module. + * + * Modules implementing this return an array of arrays. The key to each sub-array + * is the name of the plugin. Each plugin array may contain the following items: + * + * - title: The human readable name of the plugin. + * - project_page: The URL of the plugin's website. + * - version: (required) A decimal value determining the version of the included + * plugin. + * - download: A URL to download the given version of the plugin. + * - js: An array of JavaScript elements to be processed when the plugin is + * being added to the page. The array keys represents the $data parameter of + * the drupal_add_js() call, and the value becomes the $options argument. If + * the key is an integer, then the value becomes the $data parameter. + * - css: Similar to the "js" flag, provides an array of CSS elements to to be + * processed when the plugin is being added the the page. + * - plugin: An array of dependent plugins that will also be added when processing + * the given plugin. The array key is the name of the plugin, while the value + * is an array of additional arguments that will be passed during the call to + * drupal_add_plugin(). + * - add: An array of function handlers that will called when this plugin is + * processed on the page. + * + * @return + * An array defining any JavaScript plugins associated with the module. + * @see system_js_plugin() + */ +function hook_js_plugin() { + // Plugin One. + $plugins['plugin-1'] = array( + 'title' => 'Plugin One', + 'project_page' => 'http://example.com/plugin-1', + 'version' => 1.2, + 'download' => 'http://example.com/plugin-1/plugin-1.js', + 'js' => array( + // Items that are just strings are interpreted as stright files. + drupal_get_path('module', 'my_module') . '/plugin-1.js', + ), + 'css' => array( + // By providing an array, it becomes the $options parameter during the + // drupal_add_js/drupal_add_css call. + drupal_get_path('module', 'my_module') . '/plugin-2.css' => array( + 'type' => 'file', + 'media' => 'screen', + ), + ), + ); + // Plugin Two. + $plugins['plugin-2'] = array( + 'title' => 'Plugin Two', + 'project_page' => 'http://example.com/plugin-2', + 'version' => 1.2, + 'download' => 'http://example.com/plugin-2/plugin-2.js', + 'js' => array( + // JavaScript settings can be added by sending in an array. + array( + 'type' => 'setting', + 'data' => array('plugin-2' => TRUE), + ), + ), + // Any depending plugins can be added by using the "plugin" key. + 'plugin' => array( + // Sending in a string will just add the depending plugin to the page. + 'plugin-1', + // Sending in the plugin name along with an array of parameters will be + // sent in as additional arguments to drupal_add_plugin(). + 'plugin-3' => array( + 'additional arguments', + ) + ), + // The plugin add handlers are all called when the plugin is added to the + // page. + 'add' => array( + // The function named "plugin_2_added" will be called when plugin-2 is + // added to the page. The arguments passed to this function are the + // additional arguments that were passed during the call to + // drupal_add_plugin(). + 'plugin_2_added', + ), + ); + return $plugins; +} + +/** + * Alters the JavaScript plugin registry. + * + * @param $plugins + * The JavaScript plugin registry. + * @return + * An array with the updated JavaScript plugin registry. + * @see hook_js_plugin() + */ +function hook_js_plugin_alter(&$plugins) { + // Update Plugin One to version 1.6. + if (isset($plugins['plugin-1'])) { + if ($plugins['plugin-1']['version'] < 1.6) { + // The version number and CSS/JS files will change. + $plugins['plugin-1']['version'] = 1.6; + $plugins['plugin-1']['js'] = array( + drupal_get_path('module', 'plugin_1_update') . '/plugin-1.js', + ); + $plugins['plugin-1']['css'] = array( + drupal_get_path('module', 'plugin_1_update') . '/plugin-1.css', + ); + } + } + return $plugins; +} + +/** * Perform alterations before a page is rendered. * * Use this hook when you want to add, remove, or alter elements at the page Index: modules/system/system.module =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.module,v retrieving revision 1.712 diff -u -r1.712 system.module --- modules/system/system.module 16 Jun 2009 08:41:35 -0000 1.712 +++ modules/system/system.module 18 Jun 2009 21:06:00 -0000 @@ -789,6 +789,38 @@ } /** + * Implementation of hook_js_plugin(). + */ +function system_js_plugin() { + // jQuery Form Plugin. + $plugins['form'] = array( + 'title' => 'jQuery Form Plugin', + 'project_page' => 'http://malsup.com/jquery/form/', + 'version' => 2.16, + 'download' => 'http://github.com/malsup/form/tarball/e52ed46bff664431a51d9562803acbcfbbd2e3b4', + 'js' => array( + 'misc/jquery.form.js', + ), + ); + + // Farbtastic. + $plugins['farbtastic'] = array( + 'title' => 'Farbtastic', + 'project_page' => 'http://acko.net/dev/farbtastic', + 'version' => 1.2, + 'download' => 'http://acko.net/files/farbtastic_/farbtastic12.zip', + 'js' => array( + 'misc/farbtastic/farbtastic.js', + ), + 'css' => array( + 'misc/farbtastic/farbtastic.css', + ), + ); + + return $plugins; +} + +/** * Retrieve a blocked IP address from the database. * * @param $iid integer Index: modules/simpletest/tests/common.test =================================================================== RCS file: /cvs/drupal/drupal/modules/simpletest/tests/common.test,v retrieving revision 1.46 diff -u -r1.46 common.test --- modules/simpletest/tests/common.test 12 Jun 2009 08:39:39 -0000 1.46 +++ modules/simpletest/tests/common.test 18 Jun 2009 21:05:57 -0000 @@ -425,7 +425,7 @@ /** * Store configured value for JavaScript preprocessing. */ - var $preprocess_js = NULL; + protected $preprocess_js = NULL; public static function getInfo() { return array( @@ -437,19 +437,22 @@ function setUp() { // Enable Locale and SimpleTest in the test environment. - parent::setUp('locale', 'simpletest'); + parent::setUp('locale', 'simpletest', 'common_test'); // Disable preprocessing $this->preprocess_js = variable_get('preprocess_js', 0); variable_set('preprocess_js', 0); - // Reset drupal_add_js() before each test. + // Reset drupal_add_js() and the plugin registry before each test. drupal_static_reset('drupal_add_js'); + drupal_static_reset('drupal_get_plugin'); } function tearDown() { // Restore configured value for JavaScript preprocessing. variable_set('preprocess_js', $this->preprocess_js); + // Clear any messages. + drupal_get_messages(); parent::tearDown(); } @@ -541,6 +544,41 @@ } /** + * Checks to make sure that JavaScript plugins registry is built correctly. + */ + function testPluginRegistry() { + $plugins = drupal_get_plugin(); + $this->assertTrue(isset($plugins['farbtastic']) && isset($plugins['form']), t('The JavaScript plugin registry is built correctly.')); + } + + /** + * Adds a JavaScript plugin to the page and tests for both it and its CSS. + */ + function testRenderPlugin() { + drupal_add_plugin('farbtastic'); + $javascript = drupal_get_js(); + $stylesheets = drupal_get_css(); + $this->assertTrue(strpos($javascript, 'misc/farbtastic/farbtastic.js') > 0, t('JavaScript plugins are rendered to the page.')); + $this->assertTrue(strpos($stylesheets, 'misc/farbtastic/farbtastic.css') > 0, t('JavaScript plugins render their CSS to the page.')); + } + + /** + * Adds a JavaScript plugin to the page and alters it. + * + * @see common_test_js_alter() + */ + function testAlterPlugin() { + // Empty out messages. + drupal_get_messages(); + + // Add farbtastic, which will fire common_test_js_alter(). + drupal_add_plugin('farbtastic'); + $plugin = drupal_get_plugin('farbtastic'); + $this->assertTrue(array_search('common_test_js_plugin_add', $plugin['add']) !== FALSE, t('JavaScript plugins are alterable.')); + $this->assertTrue(strpos(theme('status_messages'), t('Add callback for the @plugin plugin fired.', array('@plugin' => 'farbtastic'))) !== FALSE, t('JavaScript plugin "add" callbacks are fired.')); + } + + /** * Test adding a JavaScript file with a different weight. */ function testDifferentWeight() { Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.924 diff -u -r1.924 common.inc --- includes/common.inc 18 Jun 2009 16:03:30 -0000 1.924 +++ includes/common.inc 18 Jun 2009 21:05:55 -0000 @@ -2822,6 +2822,104 @@ } /** + * Adds multiple JavaScript files and CSS files for a JavaScript plugin. + * + * @param $plugin + * The name of the plugin to add. + * @param ... + * Additional arguments to pass to the add handlers associated with the plugin. + * @return + * An array containing the results from calling all the add handlers. The key + * of the array is the add handler name, while the value is what was returned + * from the call. + * @see hook_js_plugin() + * @see hook_js_plugin_alter() + */ +function drupal_add_plugin($plugin) { + $plugin = drupal_get_plugin($plugin); + if (!isset($plugin)) { + return NULL; + } + + // Add all the JavaScript, stylesheets and dependent plugins. + foreach (array('plugin', 'js', 'css') as $type) { + if (isset($plugin[$type]) && is_array($plugin[$type])) { + foreach ($plugin[$type] as $data => $options) { + // If the value is not an array, it's a filename or plugin name and + // passed as the first (and only) argument. + if (!is_array($options)) { + $data = $options; + $options = NULL; + } + // When drupal_add_js with 'type' => 'setting' is called, the first + // parameter ($data) is an array. Arrays can't be keys in PHP, so we + // have to get $data from the value array. + if (is_numeric($data)) { + $data = $options['data']; + unset($options['data']); + } + call_user_func('drupal_add_' . $type, $data, $options); + } + } + } + + // Call all of the add handlers associated with the plugin. + $output = array(); + if (isset($plugin['add']) && is_array($plugin['add'])) { + foreach ($plugin['add'] as $function) { + if (drupal_function_exists($function)) { + // Invoke the add handler, passing in the additional parameters. + $args = func_get_args(); + + // Remove the plugin name from the arguments before calling the handler. + $output[$function] = call_user_func_array($function, $args); + } + } + } + return $output; +} + +/** + * Retrieves information from the JavaScript plugin registry. + * + * @param $plugin + * (optional) If given, will provide the JavaScript plugin information for + * just the given plugin name. + * @return + * Returns all available JavaScript plugins, or the requested one from $plugin. + * @see drupal_add_plugin() + * @see hook_js_plugin() + */ +function drupal_get_plugin($plugin = NULL) { + $plugins = &drupal_static(__FUNCTION__); + + if (!isset($plugins)) { + if ($cache = cache_get('js_plugin')) { + $plugins = $cache->data; + } + else { + // Invoke hook_js_plugin() to create the plugin registry. + $plugins = module_invoke_all('js_plugin'); + + // Invoke hook_js_plugin_alter() to allow modification of the + // registry. This is helpful to allow modules to update any plugins + // to later versions. + drupal_alter('js_plugin', $plugins); + + // Save the registry to the cache. + cache_set('js_plugin', $plugins); + } + } + + if (isset($plugin)) { + return isset($plugins[$plugin]) ? $plugins[$plugin] : NULL; + } + else { + return $plugins; + } +} + +/** * Constructs an array of the defaults that are used for JavaScript items. * * @param $data Index: includes/form.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/form.inc,v retrieving revision 1.342 diff -u -r1.342 form.inc --- includes/form.inc 18 Jun 2009 15:48:13 -0000 1.342 +++ includes/form.inc 18 Jun 2009 21:05:56 -0000 @@ -2002,7 +2002,7 @@ // Adding the same javascript settings twice will cause a recursion error, // we avoid the problem by checking if the javascript has already been added. if ((isset($element['#ahah']['callback']) || isset($element['#ahah']['path'])) && isset($element['#ahah']['event']) && !isset($js_added[$element['#id']])) { - drupal_add_js('misc/jquery.form.js', array('weight' => JS_LIBRARY)); + drupal_add_plugin('form'); drupal_add_js('misc/ahah.js'); $ahah_binding = array( Index: modules/simpletest/tests/common_test.module =================================================================== RCS file: modules/simpletest/tests/common_test.module diff -N modules/simpletest/tests/common_test.module --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ modules/simpletest/tests/common_test.module 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,20 @@ + $plugin))); +} Index: modules/simpletest/tests/common_test.info =================================================================== RCS file: modules/simpletest/tests/common_test.info diff -N modules/simpletest/tests/common_test.info --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ modules/simpletest/tests/common_test.info 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,8 @@ +; $Id$ +name = "Common Test" +description = "Support module for core Drupal functionality tests." +core = 7.x +package = Testing +files[] = common_test.module +version = VERSION +hidden = TRUE