? sites/default/files
? sites/default/settings.php
Index: install.php
===================================================================
RCS file: /cvs/drupal/drupal/install.php,v
retrieving revision 1.117
diff -u -p -r1.117 install.php
--- install.php	14 Apr 2008 17:48:33 -0000	1.117
+++ install.php	1 May 2008 03:34:20 -0000
@@ -708,7 +708,7 @@ function install_tasks($profile, $task) 
 
       // Add JavaScript validation.
       _user_password_dynamic_validation();
-      drupal_add_js(drupal_get_path('module', 'system') . '/system.js', 'module');
+      drupal_add_js(drupal_get_path('module', 'system') . '/system.js');
       // We add these strings as settings because JavaScript translation does not
       // work on install time.
       drupal_add_js(array('copyFieldValue' => array('edit-site-mail' => array('edit-account-mail')), 'cleanURL' => array('success' => st('Your server has been successfully tested to support this feature.'), 'failure' => st('Your system configuration does not currently support this feature. The <a href="http://drupal.org/node/15365">handbook page on Clean URLs</a> has additional troubleshooting information.'), 'testing' => st('Testing clean URLs...'))), 'setting');
Index: includes/batch.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/batch.inc,v
retrieving revision 1.15
diff -u -p -r1.15 batch.inc
--- includes/batch.inc	14 Apr 2008 17:48:33 -0000	1.15
+++ includes/batch.inc	1 May 2008 03:34:20 -0000
@@ -73,7 +73,7 @@ function _batch_progress_page_js() {
   // and the initialization and error messages.
   $current_set = _batch_current_set();
   drupal_set_title($current_set['title']);
-  drupal_add_js('misc/progress.js', 'core', 'header', FALSE, FALSE);
+  drupal_add_js('misc/progress.js', array('cache' => FALSE, 'weight' => -20));
 
   $url = url($batch['url'], array('query' => array('id' => $batch['id'])));
   $js_setting = array(
@@ -84,7 +84,7 @@ function _batch_progress_page_js() {
     ),
   );
   drupal_add_js($js_setting, 'setting');
-  drupal_add_js('misc/batch.js', 'core', 'header', FALSE, FALSE);
+  drupal_add_js('misc/batch.js', array('cache' => FALSE, 'weight' => -20));
 
   $output = '<div id="progress"></div>';
   return $output;
Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.764
diff -u -p -r1.764 common.inc
--- includes/common.inc	14 Apr 2008 17:48:33 -0000	1.764
+++ includes/common.inc	1 May 2008 03:34:20 -0000
@@ -1890,7 +1890,7 @@ function drupal_clear_css_cache() {
  * reference to an existing file or as inline code. The following actions can be
  * performed using this function:
  *
- * - Add a file ('core', 'module' and 'theme'):
+ * - Add a file ('file'):
  *   Adds a reference to a JavaScript file to the page. JavaScript files
  *   are placed in a certain order, from 'core' first, to 'module' and finally
  *   'theme' so that files, that are added later, can override previously added
@@ -1907,92 +1907,151 @@ function drupal_clear_css_cache() {
  *   will be accessible at Drupal.settings.
  *
  * @param $data
- *   (optional) If given, the value depends on the $type parameter:
- *   - 'core', 'module' or 'theme': Path to the file relative to base_path().
+ *   (optional) If given, the value depends on the $options parameter:
+ *   - 'file': Path to the file relative to base_path().
  *   - 'inline': The JavaScript code that should be placed in the given scope.
  *   - 'setting': An array with configuration options as associative array. The
  *       array is directly placed in Drupal.settings. You might want to wrap your
  *       actual configuration settings in another variable to prevent the pollution
  *       of the Drupal.settings namespace.
- * @param $type
- *   (optional) The type of JavaScript that should be added to the page. Allowed
- *   values are 'core', 'module', 'theme', 'inline' and 'setting'. You
- *   can, however, specify any value. It is treated as a reference to a JavaScript
- *   file. Defaults to 'module'.
- * @param $scope
- *   (optional) The location in which you want to place the script. Possible
- *   values are 'header' and 'footer' by default. If your theme implements
- *   different locations, however, you can also use these.
- * @param $defer
- *   (optional) If set to TRUE, the defer attribute is set on the <script> tag.
- *   Defaults to FALSE. This parameter is not used with $type == 'setting'.
- * @param $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 if this
- *   feature has been turned on under the performance section?
+ * @param $options
+ *   (optional) A string for the type ('file', 'inline', 'setting'), or an array which
+ *   can have any or all of the following keys (these are not valid with type => 'setting'):
+ *   - type
+ *       The type of JavaScript that should be added to the page. Allowed
+ *       values are 'file', 'inline' and 'setting'.
+ *   - scope
+ *       The location in which you want to place the script. Possible
+ *       values are 'header' and 'footer' by default. If your theme implements
+ *       different locations, however, you can also use these.
+ *   -  defer
+ *       If set to TRUE, the defer attribute is set on the <script> tag.
+ *       Defaults to FALSE. This parameter is not used with $type == 'setting'.
+ *   -  cache
+ *       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.
+ *   - preprocess
+ *       Should this JS file be aggregated if this feature has been turned
+ *       on under the performance section.
+ *   - reset
+ *       Resets the currently loaded JavaScript.
+ *   - attributes
+ *       An array of attributes to be added to the script.  Run through drupal_attributes.
  * @return
- *   If the first parameter is NULL, the JavaScript array that has been built so
- *   far for $scope is returned. If the first three parameters are NULL,
- *   an array with all scopes is returned.
+ *   An array of JavaScript files to include - the array has three keys: 
+ *    - 'files'
+ *        An array of files keyed by path (the value is the $options that was passed in).
+ *    - 'inline'
+ *        An array of code to be put inline.  The key is the code and the value is the
+ *        $options.
+ *    - 'setting'
+ *        This one is different; it is keyed by scope (region to put the javascript in).
+ *        Inside each scope is an aggregation of the variables that were added to the
+ *        settings.
  */
-function drupal_add_js($data = NULL, $type = 'module', $scope = 'header', $defer = FALSE, $cache = TRUE, $preprocess = TRUE) {
+function drupal_add_js($data = NULL, $options = array()) {
   static $javascript = array();
 
-  if (isset($data)) {
-
-    // Add jquery.js and drupal.js, as well as the basePath setting, the
-    // first time a Javascript file is added.
-    if (empty($javascript)) {
-      $javascript['header'] = array(
-        'core' => array(
-          'misc/jquery.js' => array('cache' => TRUE, 'defer' => FALSE, 'preprocess' => TRUE),
-          'misc/drupal.js' => array('cache' => TRUE, 'defer' => FALSE, 'preprocess' => TRUE),
+  // Add jquery.js and drupal.js, as well as the basePath setting, the
+  // first time a Javascript file is added.
+  if (empty($javascript) || (is_array($options) && isset($options['reset']) && $options['reset'])) {
+    $javascript = array(
+      'files' => array(
+        'misc/jquery.js' => array(
+          'weight' => -1000,
+          'scope' => 'header',
+          'cache' => TRUE,
+          'preprocess' => TRUE,
+          'attributes' => array('type' => 'text/javascript'),
+          'data' => 'misc/jquery.js',
         ),
-        'module' => array(),
-        'theme' => array(),
-        'setting' => array(
-          array('basePath' => base_path()),
+        'misc/drupal.js' => array(
+          'weight' => -999,
+          'scope' => 'header',
+          'cache' => TRUE,
+          'preprocess' => TRUE,
+          'attributes' => array('type' => 'text/javascript'),
+          'data' => 'misc/drupal.js',
         ),
-        'inline' => array(),
-      );
-    }
+      ),
+      'setting' => array(
+        'header' => array(
+          'basePath' => base_path(),
+        ),
+      ),
+      'inline' => array(),
+    );
+  }
+  if (is_array($options) && isset($options['reset']) && $options['reset'] && isset($data)) {
+    $javascript = $data;
+    return $javascript;
+  }
 
-    if (isset($scope) && !isset($javascript[$scope])) {
-      $javascript[$scope] = array('core' => array(), 'module' => array(), 'theme' => array(), 'setting' => array(), 'inline' => array());
+  if (isset($data)) {
+    if (isset($options) && !is_array($options)) {
+      $options = array('type' => $options);
     }
 
-    if (isset($type) && isset($scope) && !isset($javascript[$scope][$type])) {
-      $javascript[$scope][$type] = array();
+    $options += array(
+      'type' => 'file',
+      'weight' => 0,
+      'scope' => 'header',
+      'cache' => TRUE,
+      'preprocess' => ((isset($options['cache']) && $options['cache']) || !isset($options['cache']) ? TRUE : FALSE),
+      'delete' => FALSE,
+      'data' => $data,
+      'attributes' => array('type' => 'text/javascript'),
+    );
+
+    $type = $options['type'];
+    unset($options['type']);
+    $delete = FALSE;
+
+    if ($type == 'delete' || $options['delete']) {
+       $type = 'file';
+       $delete = TRUE;
     }
 
     switch ($type) {
       case 'setting':
-        $javascript[$scope][$type][] = $data;
+        $javascript['setting'][$options['scope']] = $data + $javascript['setting'][$options['scope']];
         break;
       case 'inline':
-        $javascript[$scope][$type][] = array('code' => $data, 'defer' => $defer);
+        if (!isset($javascript['files'][$data])) {
+          $javascript['inline'][$data] = $options;
+          $sort = array();
+          foreach ($javascript['inline'] as $code => $settings) {
+            $sort[$code] = $settings['weight'];
+          }
+          $inline_data = $javascript['inline'];
+          array_multisort($sort, SORT_NUMERIC, $inline_data);
+          $javascript['inline'] = $inline_data;
+        }
         break;
-      default:
-        // If cache is FALSE, don't preprocess the JS file.
-        $javascript[$scope][$type][$data] = array('cache' => $cache, 'defer' => $defer, 'preprocess' => (!$cache ? FALSE : $preprocess));
-    }
-  }
-
-  if (isset($scope)) {
-
-    if (isset($javascript[$scope])) {
-      return $javascript[$scope];
+      case 'file':
+        if (!isset($javascript['files'][$data])) {
+          $javascript['files'][$data] = $options;
+          $sort = array();
+          foreach ($javascript['files'] as $uri => $settings) {
+            $sort[$uri] = $settings['weight'];
+          }
+          $files = $javascript['files'];
+          array_multisort($sort, SORT_ASC, $files);
+          $javascript['files'] = $files;
+        }
     }
-    else {
-      return array();
+    if ($delete) {
+      if ($type == 'file') {
+        $type = 'files';
+      }
+      if (isset($javascript[$type][$data])) {
+        unset($javascript[$type][$data]);
+      }
     }
   }
-  else {
-    return $javascript;
-  }
+
+  return $javascript;
 }
 
 /**
@@ -2003,10 +2062,10 @@ function drupal_add_js($data = NULL, $ty
  * are added to the page. Then, all settings are output, followed by 'inline'
  * JavaScript code. If running update.php, all preprocessing is disabled.
  *
- * @parameter $scope
+ * @param $scope
  *   (optional) The scope for which the JavaScript rules should be returned.
  *   Defaults to 'header'.
- * @parameter $javascript
+ * @param $javascript
  *   (optional) An array with all JavaScript code. Defaults to the default
  *   JavaScript array for the given scope.
  * @return
@@ -2016,23 +2075,62 @@ function drupal_get_js($scope = 'header'
   if ((!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') && function_exists('locale_update_js_files')) {
     locale_update_js_files();
   }
-
+  
   if (!isset($javascript)) {
-    $javascript = drupal_add_js(NULL, NULL, $scope);
+    $javascript = drupal_add_js();
   }
 
   if (empty($javascript)) {
     return '';
   }
 
-  $output = '';
-  $preprocessed = '';
-  $no_preprocess = array('core' => '', 'module' => '', 'theme' => '');
-  $files = array();
+  // If JS preprocessing is enabled
   $preprocess_js = (variable_get('preprocess_js', FALSE) && (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update'));
+  // File directory path
   $directory = file_directory_path();
+  // Whether the file directory path is writable
   $is_writable = is_dir($directory) && is_writable($directory) && (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PUBLIC);
 
+  // The first step is to format it nicely for altering
+  $alterable_js = array();
+  if (!empty($javascript['files'])) {
+    foreach ($javascript['files'] as $path => $settings) {
+      // Array of files to include in the preprocessor
+      $files = array();
+      if ($settings['scope'] == $scope) {
+        if (!$settings['preprocess'] || !$is_writable || !$preprocess_js) {
+          $alterable_js['files'][$path] = $settings;
+        }
+        else {
+          $files[$path] = $settings;
+        }
+      }
+    }
+    // Process the files to be pre-processed
+    if ($is_writable && $preprocess_js && count($files) > 0) {
+      $filename = md5(serialize($files) . $query_string) . '.js';
+      $preprocess_file = drupal_build_js_cache($files, $filename);
+      $alterable_js['files'][base_path() . $preprocess_file] = array(
+        'weight' => 0,
+        'scope' => 'header',
+        'cache' => TRUE,
+        'preprocess' => TRUE,
+        'data' => $preprocess_file,
+        'attributes' => array(),
+      );
+    }
+  }
+  if (!empty($javascript['inline'])) {
+    foreach ($javascript['inline'] as $code => $settings) {
+      if ($settings['scope'] == $scope) {
+        $alterable_js['inline'][$path] = $settings;
+      }
+    }
+  }
+  $alterable_js['setting'] = (isset($javascript['setting'][$scope])? $javascript['setting'][$scope] : array());
+  drupal_alter('js', $alterable_js, $scope);
+  $javascript = $alterable_js;
+
   // A dummy query-string is added to filenames, to gain control over
   // browser-caching. The string changes on every update or full cache
   // flush, forcing browsers to load a new copy of the files, as the
@@ -2041,45 +2139,146 @@ function drupal_get_js($scope = 'header'
   // page request.
   $query_string = '?' . substr(variable_get('css_js_query_string', '0'), 0, 1);
 
-  foreach ($javascript as $type => $data) {
+  $renderable_js = array();
 
-    if (!$data) continue;
+  foreach ($javascript as $type => $data) {
+    if (!$data) {
+      continue;
+    }
 
     switch ($type) {
       case 'setting':
-        $output .= '<script type="text/javascript">jQuery.extend(Drupal.settings, ' . drupal_to_js(call_user_func_array('array_merge_recursive', $data)) . ");</script>\n";
+        $renderable_js[] = array(
+          'type' => 'setting',
+          'data' => $data,
+        );
         break;
       case 'inline':
         foreach ($data as $info) {
-          $output .= '<script type="text/javascript"' . ($info['defer'] ? ' defer="defer"' : '') . '>' . $info['code'] . "</script>\n";
+          $renderable_js[] = array(
+            'type' => 'inline',
+            'data' => $info['data'],
+            'attributes' => $info['attributes'],
+          );
         }
         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) {
-          if (!$info['preprocess'] || !$is_writable || !$preprocess_js) {
-            $no_preprocess[$type] .= '<script type="text/javascript"' . ($info['defer'] ? ' defer="defer"' : '') . ' src="' . base_path() . $path . ($info['cache'] ? $query_string : '?' . time()) . "\"></script>\n";
-          }
-          else {
-            $files[$path] = $info;
-          }
+          $info['attributes']['src'] = base_path() . $path . ($info['cache'] ? $query_string : '?' . time());
+          $renderable_js[] = array(
+            'type' => 'file',
+            'attributes' => $info['attributes'],
+            'path' => $path,
+          );
         }
     }
   }
+  return theme('scripts', $renderable_js, $scope);
+}
+
+/**
+ * Themes a list of scripts, which are then rendered to the page.
+ * 
+ * @param $scripts
+ *   An array of scripts to be output.
+ * @param $scope
+ *   The scope of the JavaScript: where it's going to be located
+ *   on the page.
+ * @return
+ *   HTML containing the script tags.
+ */
+function theme_scripts($scripts, $scope) {
+  $output = '';
+
+  foreach ($scripts as $script) {
+    switch ($script['type']) {
+      case 'setting':
+        $output .= '<script type="text/javascript">jQuery.extend(Drupal.settings, ' . drupal_to_js($script['data']) . ");</script>\n";
+        break;
+      case 'inline':
+        $output .= '<script' . drupal_attributes($script['attributes']) . '>' . $script['code'] . "</script>\n";
+        break;
+      case 'file':
+        $output .= '<script' . drupal_attributes($script['attributes']) . "></script>\n";
+    }
+  }
 
-  // Aggregate any remaining JS files that haven't already been output.
-  if ($is_writable && $preprocess_js && count($files) > 0) {
-    $filename = md5(serialize($files) . $query_string) . '.js';
-    $preprocess_file = drupal_build_js_cache($files, $filename);
-    $preprocessed .= '<script type="text/javascript" src="' . base_path() . $preprocess_file . '"></script>' . "\n";
+  return $output;
+}
+
+/**
+ * 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 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) . ';';
+      }
+    }
+
+    // Create the JS file.
+    file_save_data($contents, $jspath . '/' . $filename, FILE_EXISTS_REPLACE);
   }
 
-  // Keep the order of JS files consistent as some are preprocessed and others are not.
-  // Make sure any inline or JS setting variables appear last after libraries have loaded.
-  $output = $preprocessed . implode('', $no_preprocess) . $output;
+  return $jspath . '/' . $filename;
+}
 
-  return $output;
+/**
+ * Delete all cached JS files.
+ */
+function drupal_clear_js_cache() {
+  file_scan_directory(file_create_path('js'), '.*', array('.', '..', 'CVS'), 'file_delete', TRUE);
+  variable_set('javascript_parsed', array());
+}
+
+/**
+ * Converts a PHP variable into its Javascript equivalent.  We use 
+ * HTML-safe strings, i.e. with <, > and & escaped.
+ * 
+ * @param $var
+ *   The variable to encode
+ * @return
+ *   A string, which is a JavaScript-encoded version of $var.
+ * @see drupal_json
+ */
+function drupal_to_js($var) {
+  // json_encode() does not escape <, > and &, so we do it with str_replace()
+  return str_replace(array("<", ">", "&"), array('\x3c', '\x3e', '\x26'), json_encode($var));
+}
+
+/**
+ * Return data in JSON format.
+ *
+ * This function should be used for JavaScript callback functions returning
+ * data in JSON format. It sets the header for JavaScript output.
+ *
+ * @param $var
+ *   (optional) If set, the variable will be converted to JSON and output.
+ * @see drupal_to_js
+ */
+function drupal_json($var = NULL) {
+  // We are returning JavaScript, so tell the browser.
+  drupal_set_header('Content-Type: text/javascript; charset=utf-8');
+
+  if (isset($var)) {
+    echo drupal_to_js($var);
+  }
 }
 
 /**
@@ -2192,7 +2391,7 @@ function drupal_get_js($scope = 'header'
 function drupal_add_tabledrag($table_id, $action, $relationship, $group, $subgroup = NULL, $source = NULL, $hidden = TRUE, $limit = 0) {
   static $js_added = FALSE;
   if (!$js_added) {
-    drupal_add_js('misc/tabledrag.js', 'core');
+    drupal_add_js('misc/tabledrag.js', array('weight' => -20));
     $js_added = TRUE;
   }
 
@@ -2211,75 +2410,6 @@ 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.
- */
-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) . ';';
-      }
-    }
-
-    // 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);
-  variable_set('javascript_parsed', array());
-}
-
-/**
- * Converts a PHP variable into its Javascript equivalent.
- *
- * We use HTML-safe strings, i.e. with <, > and & escaped.
- */
-function drupal_to_js($var) {
-  // json_encode() does not escape <, > and &, so we do it with str_replace()
-  return str_replace(array("<", ">", "&"), array('\x3c', '\x3e', '\x26'), json_encode($var));
-}
-
-/**
- * Return data in JSON format.
- *
- * This function should be used for JavaScript callback functions returning
- * data in JSON format. It sets the header for JavaScript output.
- *
- * @param $var
- *   (optional) If set, the variable will be converted to JSON and output.
- */
-function drupal_json($var = NULL) {
-  // We are returning JavaScript, so tell the browser.
-  drupal_set_header('Content-Type: text/javascript; charset=utf-8');
-
-  if (isset($var)) {
-    echo drupal_to_js($var);
-  }
-}
-
-/**
  * Wrapper around urlencode() which avoids Apache quirks.
  *
  * Should be used when placing arbitrary data in an URL. Note that Drupal paths
@@ -3010,6 +3140,10 @@ function drupal_common_theme() {
     'form_element' => array(
       'arguments' => array('element' => NULL, 'value' => NULL),
     ),
+    // from form.inc
+    'scripts' => array(
+      'arguments' => array('scripts' => NULL, 'scope' => NULL),
+    ),
   );
 }
 
Index: includes/form.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/form.inc,v
retrieving revision 1.270
diff -u -p -r1.270 form.inc
--- includes/form.inc	14 Apr 2008 17:51:38 -0000	1.270
+++ includes/form.inc	1 May 2008 03:34:21 -0000
@@ -1486,7 +1486,7 @@ function form_get_options($element, $key
  */
 function theme_fieldset($element) {
   if ($element['#collapsible']) {
-    drupal_add_js('misc/collapse.js');
+    drupal_add_js('misc/collapse.js', array('weight' => -20));
 
     if (!isset($element['#attributes']['class'])) {
       $element['#attributes']['class'] = '';
@@ -1784,8 +1784,8 @@ function form_expand_ahah($element) {
   // 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');
-    drupal_add_js('misc/ahah.js');
+    drupal_add_js('misc/jquery.form.js', array('weight' => -20));
+    drupal_add_js('misc/ahah.js', array('weight' => -20));
 
     $ahah_binding = array(
       'url'      => url($element['#ahah']['path']),
@@ -1810,7 +1810,7 @@ function form_expand_ahah($element) {
 
     // Add progress.js if we're doing a bar display.
     if ($ahah_binding['progress']['type'] == 'bar') {
-      drupal_add_js('misc/progress.js');
+      drupal_add_js('misc/progress.js', array('weight' => -20));
     }
 
     drupal_add_js(array('ahah' => array($element['#id'] => $ahah_binding)), 'setting');
@@ -1998,7 +1998,7 @@ function theme_textfield($element) {
   $output = '';
 
   if ($element['#autocomplete_path']) {
-    drupal_add_js('misc/autocomplete.js');
+    drupal_add_js('misc/autocomplete.js', array('weight' => -20));
     $class[] = 'form-autocomplete';
     $extra =  '<input class="autocomplete" type="hidden" id="' . $element['#id'] . '-autocomplete" value="' . check_url(url($element['#autocomplete_path'], array('absolute' => TRUE))) . '" disabled="disabled" />';
   }
@@ -2050,16 +2050,14 @@ function theme_textarea($element) {
 
   // Add teaser behavior (must come before resizable)
   if (!empty($element['#teaser'])) {
-    drupal_add_js('misc/teaser.js');
-    // Note: arrays are merged in drupal_get_js().
-    drupal_add_js(array('teaserCheckbox' => array($element['#id'] => $element['#teaser_checkbox'])), 'setting');
-    drupal_add_js(array('teaser' => array($element['#id'] => $element['#teaser'])), 'setting');
+    drupal_add_js('misc/teaser.js', array('weight' => -20));
+    drupal_add_js(array('teaserCheckbox' => array($element['#id'] => $element['#teaser_checkbox']), 'teaser' => array($element['#id'] => $element['#teaser'])), 'setting');
     $class[] = 'teaser';
   }
 
   // Add resizable behavior
   if ($element['#resizable'] !== FALSE) {
-    drupal_add_js('misc/textarea.js');
+    drupal_add_js('misc/textarea.js', array('weight' => -20));
     $class[] = 'resizable';
   }
 
Index: includes/theme.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/theme.inc,v
retrieving revision 1.420
diff -u -p -r1.420 theme.inc
--- includes/theme.inc	14 Apr 2008 17:48:33 -0000	1.420
+++ includes/theme.inc	1 May 2008 03:34:21 -0000
@@ -145,7 +145,7 @@ function _init_theme($theme, $base_theme
 
   // Add scripts used by this theme.
   foreach ($final_scripts as $script) {
-    drupal_add_js($script, 'theme');
+    drupal_add_js($script, array('weight' => 20));
   }
 
   $theme_engine = NULL;
@@ -1239,7 +1239,7 @@ function theme_table($header, $rows, $at
 
   // Add sticky headers, if applicable.
   if (count($header)) {
-    drupal_add_js('misc/tableheader.js');
+    drupal_add_js('misc/tableheader.js', array('weight' => -20));
     // Add 'sticky-enabled' class to the table to identify it for JS.
     // This is needed to target tables constructed by this function.
     $attributes['class'] = empty($attributes['class']) ? 'sticky-enabled' : ($attributes['class'] . ' sticky-enabled');
@@ -1321,7 +1321,7 @@ function theme_table($header, $rows, $at
  * Returns a header cell for tables that have a select all functionality.
  */
 function theme_table_select_header_cell() {
-  drupal_add_js('misc/tableselect.js');
+  drupal_add_js('misc/tableselect.js', array('weight' => -20));
 
   return array('class' => 'select-all');
 }
Index: modules/block/block-admin-display-form.tpl.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/block/block-admin-display-form.tpl.php,v
retrieving revision 1.5
diff -u -p -r1.5 block-admin-display-form.tpl.php
--- modules/block/block-admin-display-form.tpl.php	16 Apr 2008 11:35:51 -0000	1.5
+++ modules/block/block-admin-display-form.tpl.php	1 May 2008 03:34:21 -0000
@@ -26,7 +26,7 @@
 ?>
 <?php
   // Add table javascript.
-  drupal_add_js('misc/tableheader.js');
+  drupal_add_js('misc/tableheader.js', array('weight' => -20));
   drupal_add_js(drupal_get_path('module', 'block') . '/block.js');
   foreach ($block_regions as $region => $title) {
     drupal_add_tabledrag('blocks', 'match', 'sibling', 'block-region-select', 'block-region-' . $region, NULL, FALSE);
Index: modules/color/color.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/color/color.module,v
retrieving revision 1.40
diff -u -p -r1.40 color.module
--- modules/color/color.module	14 Apr 2008 17:48:36 -0000	1.40
+++ modules/color/color.module	1 May 2008 03:34:21 -0000
@@ -153,14 +153,12 @@ function color_scheme_form(&$form_state,
 
   // Add Farbtastic color picker
   drupal_add_css('misc/farbtastic/farbtastic.css', 'module', 'all', FALSE);
-  drupal_add_js('misc/farbtastic/farbtastic.js');
+  drupal_add_js('misc/farbtastic/farbtastic.js', array('weight' => -20));
 
   // Add custom CSS/JS
   drupal_add_css($base . '/color.css', 'module', 'all', FALSE);
   drupal_add_js($base . '/color.js');
-  drupal_add_js(array('color' => array(
-    'reference' => color_get_palette($theme, true)
-  )), 'setting');
+  drupal_add_js(array('color' => array('reference' => color_get_palette($theme, TRUE))), 'setting');
 
   // See if we're using a predefined scheme
   $current = implode(',', variable_get('color_' . $theme . '_palette', array()));
Index: modules/locale/locale.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/locale/locale.module,v
retrieving revision 1.217
diff -u -p -r1.217 locale.module
--- modules/locale/locale.module	23 Apr 2008 20:01:52 -0000	1.217
+++ modules/locale/locale.module	1 May 2008 03:34:21 -0000
@@ -507,24 +507,18 @@ function locale_update_js_files() {
   // The first three parameters are NULL in order to get an array with all
   // scopes. This is necessary to prevent recreation of JS translation files
   // when new files are added for example in the footer.
-  $javascript = drupal_add_js(NULL, NULL, NULL);
+  $javascript = drupal_add_js();
   $files = $new_files = FALSE;
 
-  foreach ($javascript as $scope) {
-    foreach ($scope as $type => $data) {
-      if ($type != 'setting' && $type != 'inline') {
-        foreach ($data as $filepath => $info) {
-          $files = TRUE;
-          if (!in_array($filepath, $parsed)) {
-            // Don't parse our own translations files.
-            if (substr($filepath, 0, strlen($dir)) != $dir) {
-              locale_inc_callback('_locale_parse_js_file', $filepath);
-              watchdog('locale', 'Parsed JavaScript file %file.', array('%file' => $filepath));
-              $parsed[] = $filepath;
-              $new_files = TRUE;
-            }
-          }
-        }
+  foreach ($javascript['files'] as $filepath => $info) {
+    $files = TRUE;
+    if (!in_array($filepath, $parsed)) {
+      // Don't parse our own translations files.
+      if (substr($filepath, 0, strlen($dir)) != $dir) {
+        locale_inc_callback('_locale_parse_js_file', $filepath);
+        watchdog('locale', 'Parsed JavaScript file %file.', array('%file' => $filepath));
+        $parsed[] = $filepath;
+        $new_files = TRUE;
       }
     }
   }
Index: modules/node/node.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/node/node.admin.inc,v
retrieving revision 1.22
diff -u -p -r1.22 node.admin.inc
--- modules/node/node.admin.inc	14 Apr 2008 17:48:38 -0000	1.22
+++ modules/node/node.admin.inc	1 May 2008 03:34:21 -0000
@@ -259,7 +259,7 @@ function node_filter_form() {
     $form['filters']['buttons']['reset'] = array('#type' => 'submit', '#value' => t('Reset'));
   }
 
-  drupal_add_js('misc/form.js', 'core');
+  drupal_add_js('misc/form.js', array('weight' => -20));
 
   return $form;
 }
Index: modules/simpletest/simpletest.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/simpletest.module,v
retrieving revision 1.1
diff -u -p -r1.1 simpletest.module
--- modules/simpletest/simpletest.module	20 Apr 2008 18:23:29 -0000	1.1
+++ modules/simpletest/simpletest.module	1 May 2008 03:34:21 -0000
@@ -81,7 +81,7 @@ function simpletest_load() {
 function simpletest_entrypoint() {
   simpletest_load();
   drupal_add_css(drupal_get_path('module', 'simpletest') .'/simpletest.css', 'module');
-  drupal_add_js(drupal_get_path('module', 'simpletest') .'/simpletest.js', 'module');
+  drupal_add_js(drupal_get_path('module', 'simpletest') .'/simpletest.js');
   $output = drupal_get_form('simpletest_overview_form');
 
   if (simpletest_running_output()) {
Index: modules/system/system.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.test,v
retrieving revision 1.1
diff -u -p -r1.1 system.test
--- modules/system/system.test	20 Apr 2008 18:23:31 -0000	1.1
+++ modules/system/system.test	1 May 2008 03:34:21 -0000
@@ -126,3 +126,162 @@ class EnableDisableCoreTestCase extends 
     }
   }
 }
+
+class CommonIncTest extends DrupalWebTestCase {
+  /**
+   * Implementation of getInfo().
+   */
+  function getInfo() {
+    return array(
+      'name' => t('Common.inc tests'),
+      'description' => t('Tests for common.inc'),
+      'group' => t('Core'),
+    );
+  }
+
+  function test_drupal_add_js() {
+    $original_js = drupal_add_js();
+    // Reset the JavaScript.
+    drupal_add_js(NULL, array('reset' => TRUE));
+
+    // Default JavaScript array.
+    $default_js = array(
+      'files' => array(
+        'misc/jquery.js' => array(
+          'weight' => -1000,
+          'scope' => 'header',
+          'cache' => TRUE,
+          'preprocess' => TRUE,
+          'attributes' => array('type' => 'text/javascript'),
+          'data' => 'misc/jquery.js',
+        ),
+        'misc/drupal.js' => array(
+          'weight' => -999,
+          'scope' => 'header',
+          'cache' => TRUE,
+          'preprocess' => TRUE,
+          'attributes' => array('type' => 'text/javascript'),
+          'data' => 'misc/drupal.js',
+        ),
+      ),
+      'setting' => array(
+        'header' => array(
+          'basePath' => base_path(),
+        ),
+      ),
+      'inline' => array(),
+    );
+
+    // Second time - make sure it retains the defaults.
+    $this->assertIdentical(drupal_add_js(), $default_js, t('Test drupal_add_js without parameters.'));
+
+    // Try adding a duplicate file.
+    drupal_add_js('misc/jquery.js');
+    $this->assertIdentical(drupal_add_js(), $default_js, t('The duplicate file was only added once.'));
+
+    // Try the reset parameter.
+    drupal_add_js('misc/'. $this->randomName(8) .'.js');
+    drupal_add_js(NULL, array('reset' => TRUE));
+    $this->assertIdentical(drupal_add_js(), $default_js, t('reset actually reset the javascript.'));
+
+    // Test the weight system with files.
+    $weights = array(1, 10, -10);
+    $files = array();
+    $sort = array();
+    foreach ($weights as $weight) {
+      $js_file = 'misc/' . $this->randomName(8) . '.js';
+      $files[$js_file] = array(
+        'weight' => $weight,
+        'scope' => 'header',
+        'cache' => TRUE,
+        'preprocess' => TRUE,
+        'delete' => FALSE,
+        'data' => $js_file,
+        'attributes' => array('type' => 'text/javascript'),
+      );
+      $sort[$js_file] = $weight;
+    }
+    
+    $weight = 1;
+    foreach ($files as $file) {
+      drupal_add_js($file['data'], array('weight' => $file['weight']));
+    }
+    array_multisort($sort, SORT_NUMERIC, $files);
+
+    // Make a copy of the default JS.
+    $new_js = $default_js;
+    $new_js['files'] += $files;
+    $this->assertIdentical(drupal_add_js(), $new_js, t('The weights system works with files.'));
+
+    // Test deleting with $options['delete'] = TRUE.
+    $file = array_shift($files);
+    $new_js = $default_js;
+    $new_js['files'] += $files;
+    drupal_add_js($file['data'], array('delete' => TRUE));
+    $this->assertIdentical(drupal_add_js(), $new_js, t('The JavaScript was deleted from the array with delete => TRUE.'));
+
+    // Test deleting with $type = 'delete'.
+    $file = array_shift($files);
+    $new_js = $default_js;
+    $new_js['files'] += $files;
+    drupal_add_js($file['data'], 'delete');
+    $this->assertIdentical(drupal_add_js(), $new_js, t('The JavaScript was deleted from the array with $type = \'delete\'.'));
+
+    // Test resetting.  Although I can't triger the if (empty($javascript)), I can trigger the $options['reset'].
+    drupal_add_js(NULL, array('reset' => TRUE));
+    $this->assertIdentical(drupal_add_js(), $default_js, t('The JavaScript was reset.'));
+
+    drupal_add_js($new_js, array('reset' => TRUE));
+    $this->assertIdentical(drupal_add_js(), $new_js, t('The JavaScript was reset to an array.'));
+
+    // Test deleting without adding first.
+    drupal_add_js($this->randomName(), array('delete' => TRUE));
+    $this->assertIdentical(drupal_add_js(), $new_js, t('A file that has not been added, but deleted, doesn\'t show up.'));
+
+    // Test settings.
+    $settings = array();
+    $settings[$this->randomName()] = $this->randomName();
+    $settings[$this->randomName()] = array($this->randomName(), $this->randomName());
+    $settings[$this->randomName()] = array('test' => $this->randomName(), 'test2' => $this->randomName());
+    $new_js['setting']['header'] = $settings + $new_js['setting']['header'];
+    drupal_add_js($settings, 'setting');
+    $this->assertIdentical(drupal_add_js(), $new_js, t('The settings were added.'));
+
+    drupal_add_js($settings, 'setting');
+    $this->assertIdentical(drupal_add_js(), $new_js, t('Duplicate settings had no effect.'));
+
+    // Test adding settings with type => 'setting'.
+    $settings = array();
+    $settings[$this->randomName()] = $this->randomName();
+    $settings[$this->randomName()] = array($this->randomName(), $this->randomName());
+    $settings[$this->randomName()] = array('test' => $this->randomName(), 'test2' => $this->randomName());
+    $new_js['setting']['header'] = $settings + $new_js['setting']['header'];
+    drupal_add_js($settings, array('type' => 'setting'));
+    $this->assertIdentical(drupal_add_js(), $new_js, t('Added settings with type => \'setting\'.'));
+
+    // Test inline.
+    drupal_add_js("$(function() { $('test').hide(); });", 'inline');
+    $new_js['inline']["$(function() { $('test').hide(); });"] = array(
+      'weight' => 0,
+      'scope' => 'header',
+      'cache' => TRUE,
+      'preprocess' => TRUE,
+      'delete' => FALSE,
+      'data' => "$(function() { $('test').hide(); });",
+      'attributes' => array('type' => 'text/javascript'),
+    );
+    print_r($new_js);
+    print_r(drupal_add_js());
+    $this->assertIdentical(drupal_add_js(), $new_js, t('Added inline JavaScript.'));
+
+    // Test caching.
+    $file = $this->randomName();
+    drupal_add_js($file, array('cache' => FALSE));
+    $new_js = drupal_add_js();
+    $this->assertFalse($new_js['files'][$file]['cache'], t('The setting to turn caching off went through.'));
+    $this->assertFalse($new_js['files'][$file]['preprocess'], t('The setting to turn caching off, in turn turning preprocess off, went through.'));
+
+    // Reset the JavaScript.
+    drupal_add_js($original_js, array('reset' => TRUE));
+  }
+}
Index: modules/user/user.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.admin.inc,v
retrieving revision 1.21
diff -u -p -r1.21 user.admin.inc
--- modules/user/user.admin.inc	14 Apr 2008 17:48:43 -0000	1.21
+++ modules/user/user.admin.inc	1 May 2008 03:34:21 -0000
@@ -83,7 +83,7 @@ function user_filter_form() {
     );
   }
 
-  drupal_add_js('misc/form.js', 'core');
+  drupal_add_js('misc/form.js', array('weight' => -20));
 
   return $form;
 }
