Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.845
diff -u -r1.845 common.inc
--- includes/common.inc	11 Jan 2009 08:39:07 -0000	1.845
+++ includes/common.inc	12 Jan 2009 19:13:49 -0000
@@ -2322,6 +2322,11 @@
  *   settings are required by some modules to function properly. All settings
  *   will be accessible at Drupal.settings.
  *
+ * - 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().
+ *
  * Examples:
  * @code
  *   drupal_add_js('misc/collapse.js');
@@ -2330,6 +2335,7 @@
  *   drupal_add_js('$(document).ready(function(){alert("Hello!");});',
  *     array('type' => 'inline', 'scope' => 'footer', 'weight' => 5)
  *   );
+ *   drupal_add_js('farbtastic', 'plugin');
  * @endcode
  *
  * @param $data
@@ -2340,6 +2346,8 @@
  *       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.
  * @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
@@ -2347,7 +2355,7 @@
  *   always pass the string 'setting' only.
  *   - type
  *       The type of JavaScript that is to be added to the page. Allowed
- *       values are 'file', 'inline' or 'setting'. Defaults to 'file'.
+ *       values are 'file', 'inline', 'setting' or 'plugin'. Defaults to 'file'.
  *   - scope
  *       The location in which you want to place the script. Possible values
  *       are 'header' or 'footer'. If your theme implements different regions,
@@ -2385,6 +2393,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, $reset = FALSE) {
   static $javascript = array();
@@ -2458,6 +2469,46 @@
         // JavaScript files can not be added twice.
         $javascript[$options['data']] = $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 the plugin JavaScript files.
+        if (is_array($plugins[$data]['files']['js'])) {
+          foreach ($plugins[$data]['files']['js'] as $file) {
+            drupal_add_js($file, array('weight' => JS_LIBRARY));
+          }
+        }
+
+        // Add any CSS files for the plugin.
+        if (is_array($plugins[$data]['files']['css'])) {
+          foreach ($plugins[$data]['files']['css'] as $file) {
+            drupal_add_css($file);
+          }
+        }
+
+        // Invoke hook_js_plugin_add() to allow any additional actions.
+        module_invoke_all('js_plugin_add', $data, $options);
+        break;
     }
   }
   return $javascript;
Index: includes/form.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/form.inc,v
retrieving revision 1.312
diff -u -r1.312 form.inc
--- includes/form.inc	12 Jan 2009 06:23:57 -0000	1.312
+++ includes/form.inc	12 Jan 2009 19:13:49 -0000
@@ -1902,7 +1902,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']['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: modules/system/system.api.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.api.php,v
retrieving revision 1.9
diff -u -r1.9 system.api.php
--- modules/system/system.api.php	4 Jan 2009 19:56:51 -0000	1.9
+++ modules/system/system.api.php	12 Jan 2009 19:13:49 -0000
@@ -180,6 +180,138 @@
 }
 
 /**
+ * Registers any JavaScript plugins associated with the module.
+ *
+ * @return
+ *   An array defining any JavaScript plugins associated with the module.
+ */
+function hook_js_plugin() {
+  // Farbtastic
+  $plugins['jcarousel'] = array(
+    'title' => 'jCarousel',
+    'project_page' => 'http://sorgalla.com/jcarousel/',
+    'version' => 0.23,
+    'files' => array(
+      'js' => array(
+        drupal_get_path('module', 'jcarousel') . '/jquery.jcarousel.js',
+      ),
+      'css' => array(
+        drupal_get_path('module', 'jcarousel') . '/jquery.jcarousel.css',
+      ),
+    ),
+  );
+  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'); 
+  }
+}
+
+/**
+ * Registers any JavaScript plugins associated with the module.
+ *
+ * @return
+ *   An array defining any JavaScript plugins associated with the module.
+ */
+function hook_js_plugin() {
+  // Farbtastic
+  $plugins['jcarousel'] = array(
+    'title' => 'jCarousel',
+    'project_page' => 'http://sorgalla.com/jcarousel/',
+    'version' => 0.23,
+    'files' => array(
+      'js' => array(
+        drupal_get_path('module', 'jcarousel') . '/jquery.jcarousel.js',
+      ),
+      'css' => array(
+        drupal_get_path('module', 'jcarousel') . '/jquery.jcarousel.css',
+      ),
+    ),
+  );
+  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.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	12 Jan 2009 19:13:49 -0000
@@ -8,4 +8,6 @@
 files[] = system.admin.inc
 files[] = image.gd.inc
 files[] = system.install
+files[] = system.js.inc
+files[] = system.js.inc
 required = TRUE
Index: modules/simpletest/tests/common.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/common.test,v
retrieving revision 1.20
diff -u -r1.20 common.test
--- modules/simpletest/tests/common.test	8 Jan 2009 19:09:49 -0000	1.20
+++ modules/simpletest/tests/common.test	12 Jan 2009 19:13:49 -0000
@@ -438,6 +438,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/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	12 Jan 2009 19:13:49 -0000
@@ -7,3 +7,5 @@
 core = 7.x
 files[] = color.module
 files[] = color.install
+files[] = color.js.inc
+files[] = color.js.inc
Index: modules/color/color.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/color/color.module,v
retrieving revision 1.51
diff -u -r1.51 color.module
--- modules/color/color.module	30 Dec 2008 16:43:16 -0000	1.51
+++ modules/color/color.module	12 Jan 2009 19:13:49 -0000
@@ -149,19 +149,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/system/system.js.inc
===================================================================
RCS file: modules/system/system.js.inc
diff -N modules/system/system.js.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ modules/system/system.js.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.js.inc
===================================================================
RCS file: modules/color/color.js.inc
diff -N modules/color/color.js.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ modules/color/color.js.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');
+  }
+}
