Index: includes/form.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/form.inc,v
retrieving revision 1.322
diff -u -r1.322 form.inc
--- includes/form.inc	8 Mar 2009 21:25:17 -0000	1.322
+++ includes/form.inc	10 Mar 2009 00:00:36 -0000
@@ -1897,7 +1897,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_js('form', 'plugin');
     drupal_add_js('misc/ahah.js');
 
     $ahah_binding = array(
Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.870
diff -u -r1.870 common.inc
--- includes/common.inc	28 Feb 2009 07:36:06 -0000	1.870
+++ includes/common.inc	10 Mar 2009 00:00:36 -0000
@@ -2342,6 +2342,11 @@
  *   local server. Note that these external JavaScript references do not get
  *   aggregated when preprocessing is on.
  *
+ * - Add a JavaScript plugin ('plugin'):
+ *   Adds multiple JavaScript files and CSS files for a JavaScript plugin. All
+ *   values within the $options array are passed through to the plugin when it
+ *   is being added. These plugins are registered through hook_js_plugin().
+ *
  * - Add settings ('setting'):
  *   Adds a setting to Drupal's global storage of JavaScript settings. Per-page
  *   settings are required by some modules to function properly. All settings
@@ -2367,19 +2372,21 @@
  *       array is directly placed in Drupal.settings. All modules should wrap
  *       their actual configuration settings in another variable to prevent
  *       the pollution of the Drupal.settings namespace.
+ *   - 'plugin': The name of the plugin to add. The values within the $options
+ *       array are passed through when adding the plugin.
  *   - 'reset': Anything in $data will be ignored and the JavaScript added so
  *       far will be reset.
  * @param $options
  *   (optional) A string defining the type of JavaScript that is being added
- *   in the $data parameter ('file'/'setting'/'inline'), or an array which
- *   can have any or all of the following keys. JavaScript settings should
+ *   in the $data parameter ('file'/'setting'/'inline'/'plugin'), or an array
+ *   which can have any or all of the following keys. JavaScript settings should
  *   always pass the string 'setting' only.
  *   - type
  *       The type of JavaScript that is to be added to the page. Allowed
- *       values are 'file', 'inline', 'external', 'setting', or 'reset'. Defaults
- *       to 'file'. Note that if type is 'reset', then $data and all other
- *       $options will be ignored and the JavaScript added so far will be
- *       reset.
+ *       values are 'file', 'inline', 'external', 'plugin', 'setting', or
+ *       'reset'. Defaults to 'file'. Note that if type is 'reset', then $data
+ *       and all other $options will be ignored and the JavaScript added so far
+ *       will be reset.
  *   - scope
  *       The location in which you want to place the script. Possible values
  *       are 'header' or 'footer'. If your theme implements different regions,
@@ -2416,6 +2423,9 @@
  * @return
  *   The contructed array of JavaScript files.
  * @see drupal_get_js()
+ * @see hook_js_plugin()
+ * @see hook_js_plugin_add()
+ * @see hook_js_plugin_alter()
  */
 function drupal_add_js($data = NULL, $options = NULL) {
   static $javascript = array();
@@ -2484,6 +2494,48 @@
         $javascript[] = $options;
         break;
 
+      case 'plugin':
+        // Statically cache the JavaScript plugin registry.
+        static $plugins;
+        if (!isset($plugins)) {
+          // Load the JavaScript plugin registry from the cache.
+          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);
+          }
+        }
+
+        // Add all the JavaScript and stylesheets associated with the plugin.
+        foreach (array('js', 'css') as $type) {
+          if (isset($plugins[$data]['files'][$type])) {
+            foreach ($plugins[$data]['files'][$type] as $item_data => $item_options) {
+              // Allow the possibility of adding more options, otherwise, just
+              // interpret the item as a file.
+              if (!is_array($item_options)) {
+                $item_data = $item_options;
+                $item_options = array('weight' => JS_LIBRARY);
+              }
+              // Call drupal_add_js or drupal_add_css based on the $type.
+              call_user_func('drupal_add_' . $type, $item_data, $item_options);
+            }
+          }
+        }
+
+        // Invoke hook_js_plugin_add() to allow any additional actions.
+        module_invoke_all('js_plugin_add', $data, $options);
+        break;
+
       default: // 'file' and 'external'
         // Local and external files must keep their name as the associative key
         // so the same JavaScript file is not be added twice.
Index: modules/color/color.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/color/color.module,v
retrieving revision 1.56
diff -u -r1.56 color.module
--- modules/color/color.module	18 Feb 2009 14:28:22 -0000	1.56
+++ modules/color/color.module	10 Mar 2009 00:00:36 -0000
@@ -165,19 +165,10 @@
  * Form callback. Returns the configuration form.
  */
 function color_scheme_form(&$form_state, $theme) {
-  $base = drupal_get_path('module', 'color');
   $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));
-
-  // Add custom CSS and JS.
-  drupal_add_css($base . '/color.css', array('preprocess' => FALSE));
-  drupal_add_js($base . '/color.js');
-  drupal_add_js(array('color' => array(
-    'reference' => color_get_palette($theme, TRUE)
-  )), 'setting');
+  drupal_add_js('farbtastic', array('type' => 'plugin', 'theme' => $theme));
 
   // See if we're using a predefined scheme.
   $current = implode(',', variable_get('color_' . $theme . '_palette', array()));
Index: modules/color/color.info
===================================================================
RCS file: /cvs/drupal/drupal/modules/color/color.info,v
retrieving revision 1.9
diff -u -r1.9 color.info
--- modules/color/color.info	11 Oct 2008 02:32:40 -0000	1.9
+++ modules/color/color.info	10 Mar 2009 00:00:36 -0000
@@ -7,3 +7,4 @@
 core = 7.x
 files[] = color.module
 files[] = color.install
+files[] = color.inc
Index: modules/simpletest/tests/common.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/common.test,v
retrieving revision 1.30
diff -u -r1.30 common.test
--- modules/simpletest/tests/common.test	28 Feb 2009 07:36:06 -0000	1.30
+++ modules/simpletest/tests/common.test	10 Mar 2009 00:00:36 -0000
@@ -482,6 +482,17 @@
   }
 
   /**
+   * Adds a JavaScript pluging to the page and tests for both it and its CSS.
+   */
+  function testRenderPlugin() {
+    drupal_add_js('farbtastic', 'plugin');
+    $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.'));
+  }
+
+  /**
    * Test adding a JavaScript file with a different weight.
    */
   function testDifferentWeight() {
Index: modules/system/system.info
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.info,v
retrieving revision 1.11
diff -u -r1.11 system.info
--- modules/system/system.info	12 Oct 2008 01:23:06 -0000	1.11
+++ modules/system/system.info	10 Mar 2009 00:00:36 -0000
@@ -8,4 +8,5 @@
 files[] = system.admin.inc
 files[] = image.gd.inc
 files[] = system.install
+files[] = system.inc
 required = TRUE
Index: modules/system/system.api.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.api.php,v
retrieving revision 1.22
diff -u -r1.22 system.api.php
--- modules/system/system.api.php	9 Mar 2009 11:44:54 -0000	1.22
+++ modules/system/system.api.php	10 Mar 2009 00:00:36 -0000
@@ -237,6 +237,79 @@
 }
 
 /**
+ * Registers any JavaScript plugins associated with the module.
+ *
+ * @return
+ *   An array defining any JavaScript plugins associated with the module.
+ */
+function hook_js_plugin() {
+  // jCarousel.
+  $plugins['jcarousel'] = array(
+    'title' => 'jCarousel',
+    'project_page' => 'http://sorgalla.com/jcarousel/',
+    'version' => 0.23,
+    'files' => array(
+      'js' => array(
+        // Items that are just strings are interpreted as stright files. 
+        drupal_get_path('module', 'jcarousel') . '/jquery.jcarousel.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', 'jcarousel') . '/jquery.jcarousel.css' => array(
+          'type' => 'file',
+          'preprocess' => FALSE,
+          'media' => 'screen',
+        ),
+      ),
+    ),
+  );
+  return $plugins;
+}
+
+/**
+ * Alters the JavaScript plugin registry.
+ *
+ * @param $plugins
+ *   The JavaScript plugin registry.
+ * @return
+ *   An array with the updated JavaScript plugin registry.
+ */
+function hook_js_plugin_alter(&$plugins) {
+  // Update the jCarousel plugin to version 1.0.
+  if (isset($plugins['jcarousel'])) {
+    if ($plugins['jcarousel']['version'] < 1.0) {
+      $plugins['jcarousel']['version'] = 1.0;
+      $plugins['jcarousel']['files']['js'] = drupal_get_path('module', 'jcarousel_update') . '/jquery.jcarousel.js';
+      $plugins['jcarousel']['files']['css'] = drupal_get_path('module', 'jcarousel_update') . '/jquery.jcarousel.css';
+    }
+  }
+  return $plugins;
+}
+
+/**
+ * A JavaScript plugin is being added to the page.
+ *
+ * This is helpful when special actions are to be taken when the plugin is
+ * added. Some plugins, for example, require special CSS files, or settings
+ * to be added to the page.
+ *
+ * @param $plugin
+ *   The JavaScript plugin being added.
+ * @param $options
+ *   The options that were passed when the plugin was added.
+ */
+function hook_js_plugin_add($plugin, $options = array()) {
+  if ($plugin == 'jcarousel') {
+    // Add our JavaScript which registers the Drupal behaviors.
+    drupal_add_js(drupal_get_path('module', 'jcarousel') . '/jcarousel.js');
+
+    // Add the settings so that the Drupal behaviors act on them.
+    drupal_add_js($options['args'], 'setting'); 
+  }
+}
+
+/**
  * Perform alterations before a form is rendered.
  *
  * One popular use of this hook is to add form elements to the node form. When
Index: modules/system/system.inc
===================================================================
RCS file: modules/system/system.inc
diff -N modules/system/system.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ modules/system/system.inc	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,36 @@
+<?php
+// $Id$
+
+/**
+ * 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,
+    'files' => array(
+      'js' => array(
+        'misc/jquery.form.js',
+      ),
+    ),
+  );
+
+  // Farbtastic.
+  $plugins['farbtastic'] = array(
+    'title' => 'Farbtastic',
+    'project_page' => 'http://acko.net/dev/farbtastic',
+    'version' => 1.2,
+    'files' => array(
+      'js' => array(
+        'misc/farbtastic/farbtastic.js',
+      ),
+      'css' => array(
+        'misc/farbtastic/farbtastic.css',
+      ),
+    ),
+  );
+
+  return $plugins;
+}
Index: modules/color/color.inc
===================================================================
RCS file: modules/color/color.inc
diff -N modules/color/color.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ modules/color/color.inc	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,18 @@
+<?php
+// $Id$
+
+/**
+ * Implementation of hook_js_plugin_add().
+ */
+function color_js_plugin_add($plugin, $options = NULL) {
+  if ($plugin == 'farbtastic') {
+    // Add custom CSS and JS for Farbtastic.
+    $base = drupal_get_path('module', 'color');
+    drupal_add_css($base . '/color.css', array('preprocess' => FALSE));
+    drupal_add_js($base . '/color.js');
+    $reference = isset($options['theme']) ? color_get_palette($options['theme'], TRUE) : NULL;
+    drupal_add_js(array('color' => array(
+      'reference' => $reference,
+    )), 'setting');
+  }
+}
