diff --git a/core/includes/common.inc b/core/includes/common.inc
index f3cdd69..e383edd 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -4043,77 +4043,43 @@ function drupal_region_class($region) {
  *     should load the JavaScript item. See
  *     drupal_pre_render_conditional_comments() for details.
  *
- * @return
+ * @return array
  *   The current array of JavaScript files, settings, and in-line code,
- *   including Drupal defaults, anything previously added with calls to
- *   drupal_add_js(), and this function call's additions.
+ *   anything previously added with calls to drupal_add_js(), this function
+ *   call's additions, and Drupal's default settings. The default jQuery and
+ *   Drupal libraries are only conditionally added later in
+ *   drupal_add_js_page_defaults().
  *
  * @see drupal_get_js()
+ * @see ajax_render()
+ * @see drupal_add_js_page_defaults()
  */
-function drupal_add_js($data = NULL, $options = NULL) {
-  $javascript = &drupal_static(__FUNCTION__, array());
+function &drupal_add_js($data = NULL, $options = NULL) {
+  $javascript = &drupal_static(__FUNCTION__);
 
-  // Construct the options, taking the defaults into consideration.
-  if (isset($options)) {
-    if (!is_array($options)) {
-      $options = array('type' => $options);
-    }
-  }
-  else {
-    $options = array();
+  // Initialize default settings on first call.
+  if (!isset($javascript)) {
+    $javascript = drupal_js_settings_default();
   }
-  $options += drupal_js_defaults($data);
-
-  // Preprocess can only be set if caching is enabled.
-  $options['preprocess'] = $options['cache'] ? $options['preprocess'] : FALSE;
-
-  // Tweak the weight so that files of the same weight are included in the
-  // order of the calls to drupal_add_js().
-  $options['weight'] += count($javascript) / 1000;
 
   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)) {
-      // url() generates the script and prefix using hook_url_outbound_alter().
-      // Instead of running the hook_url_outbound_alter() again here, extract
-      // them from url().
-      // @todo Make this less hacky: http://drupal.org/node/1547376.
-      $scriptPath = $GLOBALS['script_path'];
-      $pathPrefix = '';
-      url('', array('script' => &$scriptPath, 'prefix' => &$pathPrefix));
-      $javascript = array(
-        'settings' => array(
-          'data' => array(
-            array('basePath' => base_path()),
-            array('scriptPath' => $scriptPath),
-            array('pathPrefix' => $pathPrefix),
-          ),
-          'type' => 'setting',
-          'scope' => 'header',
-          'group' => JS_SETTING,
-          'every_page' => TRUE,
-          'weight' => 0,
-          'browsers' => array(),
-        ),
-        'core/misc/drupal.js' => array(
-          'data' => 'core/misc/drupal.js',
-          'type' => 'file',
-          'scope' => 'header',
-          'group' => JS_LIBRARY,
-          'every_page' => TRUE,
-          'weight' => -1,
-          'preprocess' => TRUE,
-          'cache' => TRUE,
-          'defer' => FALSE,
-          'browsers' => array(),
-        ),
-      );
-      // Register all required libraries.
-      drupal_add_library('system', 'jquery', TRUE);
-      drupal_add_library('system', 'jquery.once', TRUE);
-      drupal_add_library('system', 'html5shiv', TRUE);
+    // Construct the options, taking the defaults into consideration.
+    if (isset($options)) {
+      if (!is_array($options)) {
+        $options = array('type' => $options);
+      }
+    }
+    else {
+      $options = array();
     }
+    $options += drupal_js_defaults($data);
+
+    // Preprocess can only be set if caching is enabled.
+    $options['preprocess'] = $options['cache'] ? $options['preprocess'] : FALSE;
+
+    // Tweak the weight so that files of the same weight are included in the
+    // order of the calls to drupal_add_js().
+    $options['weight'] += count($javascript) / 1000;
 
     switch ($options['type']) {
       case 'setting':
@@ -4132,10 +4098,76 @@ function drupal_add_js($data = NULL, $options = NULL) {
         $javascript[$options['data']] = $options;
     }
   }
+
+  return $javascript;
+}
+
+/**
+ * The default JavaScript settings for Drupal.
+ *
+ * @see drupal_add_js()
+ */
+function drupal_js_settings_default() {
+  $javascript = array();
+
+  // url() generates the script and prefix using hook_url_outbound_alter().
+  // Instead of running the hook_url_outbound_alter() again here, extract
+  // them from url().
+  // @todo Make this less hacky: http://drupal.org/node/1547376.
+  $scriptPath = $GLOBALS['script_path'];
+  $pathPrefix = '';
+  url('', array('script' => &$scriptPath, 'prefix' => &$pathPrefix));
+
+  $javascript['settings'] = array(
+    'data' => array(
+      array('basePath' => base_path()),
+      array('scriptPath' => $scriptPath),
+      array('pathPrefix' => $pathPrefix),
+    ),
+    'type' => 'setting',
+    'scope' => 'header',
+    'group' => JS_SETTING,
+    'every_page' => TRUE,
+    'weight' => 0,
+    'browsers' => array(),
+  );
+
   return $javascript;
 }
 
 /**
+ * Conditionally adds the default Drupal/jQuery libraries to the page.
+ *
+ * @see drupal_add_js()
+ * @see drupal_get_js()
+ * @see template_process_html()
+ */
+function drupal_add_js_page_defaults() {
+  $javascript = &drupal_add_js();
+
+  // If any JavaScript (except settings) has been added, include the required
+  // base libraries.
+  if (array_diff_key($javascript, array('settings' => 0))) {
+    drupal_add_library('system', 'jquery', TRUE);
+    drupal_add_library('system', 'jquery.once', TRUE);
+
+    drupal_add_js('core/misc/drupal.js', array(
+      'group' => JS_LIBRARY,
+      'every_page' => TRUE,
+      'weight' => -1,
+    ));
+  }
+  // Otherwise, remove all settings.
+  else {
+    $javascript = array();
+  }
+
+  // Unconditionally add the html5shiv to all pages; it is required for
+  // backwards-compatible CSS styling and has no dependencies.
+  drupal_add_library('system', 'html5shiv', TRUE);
+}
+
+/**
  * Constructs an array of the defaults that are used for JavaScript items.
  *
  * @param $data
@@ -4186,7 +4218,7 @@ function drupal_js_defaults($data = NULL) {
  *   $javascript, useful when the calling function passes a $javascript array
  *   that has already been altered.
  *
- * @return
+ * @return string
  *   All JavaScript code segments and includes for the scope as HTML tags.
  *
  * @see drupal_add_js()
@@ -4201,36 +4233,36 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS
     return '';
   }
 
-  // Allow modules to alter the JavaScript.
-  if (!$skip_alter) {
-    drupal_alter('js', $javascript);
-  }
-
   // Filter out elements of the given scope.
   $items = array();
   foreach ($javascript as $key => $item) {
-    if ($item['scope'] == $scope) {
+    if (isset($item['scope']) && $item['scope'] == $scope) {
       $items[$key] = $item;
     }
   }
 
+  // Allow modules to alter the JavaScript for the given scope.
+  if (!$skip_alter) {
+    drupal_alter('js', $items);
+  }
+
   // Sort the JavaScript so that it appears in the correct order.
   uasort($items, 'drupal_sort_css_js');
 
   // Provide the page with information about the individual JavaScript files
   // used, information not otherwise available when aggregation is enabled.
-  $setting['ajaxPageState']['js'] = array_fill_keys(array_keys($items), 1);
-  unset($setting['ajaxPageState']['js']['settings']);
-  drupal_add_js($setting, 'setting');
-
-  // If we're outputting the header scope, then this might be the final time
-  // that drupal_get_js() is running, so add the setting to this output as well
-  // as to the drupal_add_js() cache. If $items['settings'] doesn't exist, it's
-  // because drupal_get_js() was intentionally passed a $javascript argument
-  // stripped of settings, potentially in order to override how settings get
-  // output, so in this case, do not add the setting to this output.
-  if ($scope == 'header' && isset($items['settings'])) {
-    $items['settings']['data'][] = $setting;
+  // If there is no 'settings' key or no actual JavaScript has been added
+  // to the page, then ajaxPageState settings are not needed either.
+  // @see drupal_add_js_page_defaults()
+  if (isset($items['settings']) && ($files = array_diff_key($items, array('settings' => 0)))) {
+    $setting['ajaxPageState']['js'] = array_fill_keys(array_keys($files), 1);
+    drupal_add_js($setting, 'setting');
+
+    // If the 'header' scope is output, then this is the final call to
+    // drupal_get_js() within page rendering, so directly inject the setting.
+    if ($scope == 'header') {
+      $items['settings']['data'][] = $setting;
+    }
   }
 
   // Render the HTML needed to load the JavaScript.
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index 2a88969..72430c0 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -2663,7 +2663,10 @@ function template_process_html(&$variables) {
   $variables['page_top'] = drupal_render($variables['page']['page_top']);
   $variables['page_bottom'] = drupal_render($variables['page']['page_bottom']);
   // Place the rendered HTML for the page body into a top level variable.
-  $variables['page']              = $variables['page']['#children'];
+  $variables['page'] = $variables['page']['#children'];
+
+  // Conditionally add the default Drupal/jQuery libraries to the page.
+  drupal_add_js_page_defaults();
   $variables['page_bottom'] .= drupal_get_js('footer');
 
   $variables['head']    = drupal_get_html_head();
diff --git a/core/modules/system/tests/common.test b/core/modules/system/tests/common.test
index 46b379e..ce891a7 100644
--- a/core/modules/system/tests/common.test
+++ b/core/modules/system/tests/common.test
@@ -1142,14 +1142,16 @@ class CommonJavaScriptTestCase extends DrupalWebTestCase {
    * Test default JavaScript is empty.
    */
   function testDefault() {
-    $this->assertEqual(array(), drupal_add_js(), t('Default JavaScript is empty.'));
+    $this->assertEqual(drupal_js_settings_default(), drupal_add_js(), t('Default JavaScript is empty.'));
   }
 
   /**
    * Test adding a JavaScript file.
    */
   function testAddFile() {
-    $javascript = drupal_add_js('core/misc/collapse.js');
+    drupal_add_js('core/misc/collapse.js');
+    drupal_add_js_page_defaults();
+    $javascript = drupal_add_js();
     $this->assertTrue(array_key_exists('core/misc/jquery.js', $javascript), t('jQuery is added when a file is added.'));
     $this->assertTrue(array_key_exists('core/misc/drupal.js', $javascript), t('Drupal.js is added when file is added.'));
     $this->assertTrue(array_key_exists('core/misc/html5.js', $javascript), t('html5.js is added when file is added.'));
@@ -1191,6 +1193,7 @@ class CommonJavaScriptTestCase extends DrupalWebTestCase {
     // Only the second of these two entries should appear in Drupal.settings.
     drupal_add_js(array('commonTestArray' => array('key' => 'commonTestOldValue')), 'setting');
     drupal_add_js(array('commonTestArray' => array('key' => 'commonTestNewValue')), 'setting');
+    drupal_add_js('core/misc/jquery.js');
 
     $javascript = drupal_get_js('header');
     $this->assertTrue(strpos($javascript, 'basePath') > 0, t('Rendered JavaScript header returns basePath setting.'));
@@ -1219,7 +1222,7 @@ class CommonJavaScriptTestCase extends DrupalWebTestCase {
   function testReset() {
     drupal_add_js('core/misc/collapse.js');
     drupal_static_reset('drupal_add_js');
-    $this->assertEqual(array(), drupal_add_js(), t('Resetting the JavaScript correctly empties the cache.'));
+    $this->assertEqual(drupal_js_settings_default(), drupal_add_js(), t('Resetting the JavaScript correctly empties the cache.'));
   }
 
   /**
@@ -1227,10 +1230,11 @@ class CommonJavaScriptTestCase extends DrupalWebTestCase {
    */
   function testAddInline() {
     $inline = 'jQuery(function () { });';
-    $javascript = drupal_add_js($inline, array('type' => 'inline', 'scope' => 'footer'));
+    drupal_add_js($inline, array('type' => 'inline', 'scope' => 'footer'));
+    drupal_add_js_page_defaults();
+    $javascript = drupal_add_js();
     $this->assertTrue(array_key_exists('core/misc/jquery.js', $javascript), t('jQuery is added when inline scripts are added.'));
-    $data = end($javascript);
-    $this->assertEqual($inline, $data['data'], t('Inline JavaScript is correctly added to the footer.'));
+    $this->assertEqual($inline, $javascript[0]['data'], t('Inline JavaScript is correctly added.'));
   }
 
   /**
@@ -1274,8 +1278,12 @@ class CommonJavaScriptTestCase extends DrupalWebTestCase {
    * Test adding a JavaScript file with a different weight.
    */
   function testDifferentWeight() {
-    $javascript = drupal_add_js('core/misc/collapse.js', array('weight' => 2));
-    $this->assertEqual($javascript['core/misc/collapse.js']['weight'], 2, t('Adding a JavaScript file with a different weight caches the given weight.'));
+    drupal_add_js('core/misc/collapse.js', array('weight' => 2));
+    drupal_add_js('core/misc/tabledrag.js', array('weight' => 999));
+    drupal_add_js_page_defaults();
+    $javascript = drupal_add_js();
+    $this->assertEqual($javascript['core/misc/collapse.js']['weight'], 2.001, t('Adding a JavaScript file with a different weight caches the given weight.'));
+    $this->assertEqual($javascript['core/misc/tabledrag.js']['weight'], 999.002, t('Adding a JavaScript file with a different weight caches the given weight.'));
   }
 
   /**
@@ -1321,6 +1329,7 @@ class CommonJavaScriptTestCase extends DrupalWebTestCase {
     drupal_add_js('core/misc/authorize.js', array('every_page' => TRUE));
     drupal_add_js('core/misc/autocomplete.js');
     drupal_add_js('core/misc/batch.js', array('every_page' => TRUE));
+    drupal_add_js_page_defaults();
     $javascript = drupal_get_js();
     $expected = implode("\n", array(
       '<script type="text/javascript" src="' . file_create_url('core/misc/authorize.js') . '?' . $default_query_string . '"></script>',
@@ -1340,6 +1349,7 @@ class CommonJavaScriptTestCase extends DrupalWebTestCase {
     drupal_add_js('core/misc/authorize.js', array('every_page' => TRUE));
     drupal_add_js('core/misc/autocomplete.js');
     drupal_add_js('core/misc/batch.js', array('every_page' => TRUE));
+    drupal_add_js_page_defaults();
     $js_items = drupal_add_js();
     $javascript = drupal_get_js();
     $expected = implode("\n", array(
@@ -1441,6 +1451,7 @@ class CommonJavaScriptTestCase extends DrupalWebTestCase {
     // flag, then by weight (see drupal_sort_css_js()), so to test the effect of
     // weight, we need the other two options to be the same.
     drupal_add_js('core/misc/collapse.js', array('group' => JS_LIBRARY, 'every_page' => TRUE, 'weight' => -21));
+    drupal_add_js_page_defaults();
     $javascript = drupal_get_js();
     $this->assertTrue(strpos($javascript, 'core/misc/collapse.js') < strpos($javascript, 'core/misc/jquery.js'), t('Rendering a JavaScript file above jQuery.'));
   }
@@ -1556,6 +1567,68 @@ class CommonJavaScriptTestCase extends DrupalWebTestCase {
 }
 
 /**
+ * Tests addition of jQuery and Drupal default libraries and settings.
+ */
+class CommonJavaScriptDefaultsTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'JavaScript defaults',
+      'description' => 'Tests addition of jQuery and Drupal default libraries and settings.',
+      'group' => 'Common',
+    );
+  }
+
+  /**
+   * Tests JavaScript default settings and libraries.
+   *
+   * Note: Where this test says "no JavaScript", it means no Drupal and jQuery
+   * scripts. The html5shiv is expected to appear always.
+   */
+  function testJavaScriptDefaults() {
+    // A regular page should not contain any JavaScript by default.
+    $this->drupalGet('');
+    $this->assertNoRaw('Drupal.settings', 'Drupal.settings not found.');
+    $this->assertNoRaw('jquery', 'jQuery not found.');
+    $this->assertRaw('html5.js', 'html5shiv found.');
+
+    // Repeat, with test helper.
+    drupal_static_reset();
+    $this->drupalRenderPage();
+    $this->assertNoRaw('Drupal.settings', 'Drupal.settings not found.');
+    $this->assertNoRaw('jquery', 'jQuery not found.');
+    $this->assertRaw('html5.js', 'html5shiv found.');
+
+    // When only settings are added in a request, no JavaScript should appear.
+    drupal_static_reset();
+    drupal_add_js(array('foo' => 'bar'), 'setting');
+    $this->drupalRenderPage();
+    $this->assertNoRaw('Drupal.settings', 'Drupal.settings not found.');
+    $this->assertNoRaw('jquery', 'jQuery not found.');
+    $this->assertRaw('html5.js', 'html5shiv found.');
+
+    // When adding any other JavaScript, default libraries and settings should
+    // appear.
+    drupal_static_reset();
+    drupal_add_js('core/misc/ajax.js');
+    $this->drupalRenderPage();
+    $this->assertRaw('Drupal.settings', 'Drupal.settings found.');
+    $this->assertRaw('jquery', 'jQuery found.');
+    $this->assertRaw('html5.js', 'html5shiv found.');
+  }
+
+  /**
+   * Sets the rendered JavaScript as browser content and logs it verbosely.
+   */
+  function drupalRenderPage() {
+    // drupal_get_js() is called for each region/scope during page rendering.
+    // @see template_process_html()
+    $page = drupal_render_page('');
+    $this->drupalSetContent($page);
+    $this->verbose($this->drupalGetContent());
+  }
+}
+
+/**
  * Tests for drupal_render().
  */
 class CommonDrupalRenderTestCase extends DrupalWebTestCase {
