diff --git a/core/lib/Drupal/Core/Extension/ThemeHandler.php b/core/lib/Drupal/Core/Extension/ThemeHandler.php
index a723087..5193a75 100644
--- a/core/lib/Drupal/Core/Extension/ThemeHandler.php
+++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php
@@ -252,6 +252,7 @@ public function rebuildThemeData() {
     // Set defaults for theme info.
     $defaults = array(
       'engine' => 'twig',
+      'base theme' => 'stable',
       'regions' => array(
         'sidebar_first' => 'Left sidebar',
         'sidebar_second' => 'Right sidebar',
@@ -282,6 +283,11 @@ public function rebuildThemeData() {
       $theme->status = (int) isset($installed[$key]);
 
       $theme->info = $this->infoParser->parse($theme->getPathname()) + $defaults;
+      // Remove the default Stable base theme when 'base theme: false' is set in
+      // a theme .info.yml file or the Twig templating engine is not being used.
+      if (empty($theme->info['base theme']) || $theme->info['engine'] != 'twig') {
+        unset($theme->info['base theme']);
+      }
 
       // Add the info file modification time, so it becomes available for
       // contributed modules to use for ordering theme lists.
diff --git a/core/modules/comment/src/Tests/CommentCSSTest.php b/core/modules/comment/src/Tests/CommentCSSTest.php
index 47a31cc..2dc4973 100644
--- a/core/modules/comment/src/Tests/CommentCSSTest.php
+++ b/core/modules/comment/src/Tests/CommentCSSTest.php
@@ -111,7 +111,7 @@ function testCommentClasses() {
         // user (the viewer) was the author of the comment. We do this in Java-
         // Script to prevent breaking the render cache.
         $this->assertIdentical(1, count($this->xpath('//*[contains(@class, "comment") and @data-comment-user-id="' . $case['comment_uid'] . '"]')), 'data-comment-user-id attribute is set on comment.');
-        $this->assertRaw(drupal_get_path('module', 'comment') . '/js/comment-by-viewer.js', 'drupal.comment-by-viewer library is present.');
+        $this->assertRaw(drupal_get_path('theme', 'stable') . '/js/comment/comment-by-viewer.js', 'drupal.comment-by-viewer library is present.');
       }
 
       // Verify the unpublished class.
diff --git a/core/modules/config/src/Tests/ConfigImportInstallProfileTest.php b/core/modules/config/src/Tests/ConfigImportInstallProfileTest.php
index 64b2d5d..4014856 100644
--- a/core/modules/config/src/Tests/ConfigImportInstallProfileTest.php
+++ b/core/modules/config/src/Tests/ConfigImportInstallProfileTest.php
@@ -67,6 +67,7 @@ public function testInstallProfileValidation() {
     $core['module']['testing_config_import'] = 0;
     unset($core['module']['syslog']);
     unset($core['theme']['stark']);
+    $core['theme']['stable'] = 0;
     $core['theme']['classy'] = 0;
     $staging->write('core.extension', $core);
     $staging->deleteAll('syslog.');
diff --git a/core/modules/config_translation/src/Tests/ConfigTranslationUiTest.php b/core/modules/config_translation/src/Tests/ConfigTranslationUiTest.php
index a18bc95..6e1fcc7 100644
--- a/core/modules/config_translation/src/Tests/ConfigTranslationUiTest.php
+++ b/core/modules/config_translation/src/Tests/ConfigTranslationUiTest.php
@@ -408,6 +408,11 @@ public function testContactConfigEntityTranslation() {
    * Tests date format translation.
    */
   public function testDateFormatTranslation() {
+    \Drupal::service('theme_handler')->install(array('stark'));
+    $this->config('system.theme')
+      ->set('default', 'stark')
+      ->save();
+
     $this->drupalLogin($this->adminUser);
 
     $this->drupalGet('admin/config/regional/date-time');
diff --git a/core/modules/editor/src/Tests/EditorLoadingTest.php b/core/modules/editor/src/Tests/EditorLoadingTest.php
index 002ccd3..8f978ac 100644
--- a/core/modules/editor/src/Tests/EditorLoadingTest.php
+++ b/core/modules/editor/src/Tests/EditorLoadingTest.php
@@ -50,6 +50,11 @@ class EditorLoadingTest extends WebTestBase {
   protected function setUp() {
     parent::setUp();
 
+    \Drupal::service('theme_handler')->install(array('stark'));
+    $this->config('system.theme')
+      ->set('default', 'stark')
+      ->save();
+
     // Let there be T-rex.
     \Drupal::state()->set('editor_test_give_me_a_trex_thanks', TRUE);
     \Drupal::service('plugin.manager.editor')->clearCachedDefinitions();
diff --git a/core/modules/locale/src/Tests/LocaleJavascriptTranslationTest.php b/core/modules/locale/src/Tests/LocaleJavascriptTranslationTest.php
index 1436527..ceabad1 100644
--- a/core/modules/locale/src/Tests/LocaleJavascriptTranslationTest.php
+++ b/core/modules/locale/src/Tests/LocaleJavascriptTranslationTest.php
@@ -131,7 +131,7 @@ public function testLocaleTranslationJsDependencies() {
       ->getStrings(array(
         'source' => 'Show description',
         'type' => 'javascript',
-        'name' => 'core/modules/locale/locale.admin.js',
+        'name' => 'core/themes/stable/js/locale/locale.admin.js',
       ));
     $string = $strings[0];
 
@@ -144,7 +144,7 @@ public function testLocaleTranslationJsDependencies() {
     $js_filename = $prefix . '_' . $js_translation_files[$prefix] . '.js';
 
     // Assert translations JS is included before drupal.js.
-    $this->assertTrue(strpos($this->content, $js_filename) < strpos($this->content, 'core/misc/drupal.js'), 'Translations are included before Drupal.t.');
+    $this->assertTrue(strpos($this->content, $js_filename) < strpos($this->content, 'core/themes/stable/js/core/drupal.js'), 'Translations are included before Drupal.t.');
   }
 
 }
diff --git a/core/modules/locale/src/Tests/LocaleLibraryAlterTest.php b/core/modules/locale/src/Tests/LocaleLibraryAlterTest.php
index 682af83..ddd959b 100644
--- a/core/modules/locale/src/Tests/LocaleLibraryAlterTest.php
+++ b/core/modules/locale/src/Tests/LocaleLibraryAlterTest.php
@@ -34,6 +34,6 @@ public function testLibraryAlter() {
     $assets = new AttachedAssets();
     $assets->setLibraries(['core/jquery.ui.datepicker']);
     $js_assets = $this->container->get('asset.resolver')->getJsAssets($assets, FALSE)[1];
-    $this->assertTrue(array_key_exists('core/modules/locale/locale.datepicker.js', $js_assets), 'locale.datepicker.js added to scripts.');
+    $this->assertTrue(array_key_exists('core/themes/stable/js/locale/locale.datepicker.js', $js_assets), 'locale.datepicker.js added to scripts.');
   }
 }
diff --git a/core/modules/statistics/src/Tests/StatisticsLoggingTest.php b/core/modules/statistics/src/Tests/StatisticsLoggingTest.php
index 7f15574..1c9a0b3 100644
--- a/core/modules/statistics/src/Tests/StatisticsLoggingTest.php
+++ b/core/modules/statistics/src/Tests/StatisticsLoggingTest.php
@@ -96,6 +96,11 @@ protected function setUp() {
    * Verifies node hit counter logging and script placement.
    */
   function testLogging() {
+    \Drupal::service('theme_handler')->install(array('stark'));
+    $this->config('system.theme')
+      ->set('default', 'stark')
+      ->save();
+
     $path = 'node/' . $this->node->id();
     $module_path = drupal_get_path('module', 'statistics');
     $stats_path = base_path() . $module_path . '/statistics.php';
diff --git a/core/modules/system/src/Tests/Form/ElementTest.php b/core/modules/system/src/Tests/Form/ElementTest.php
index 1cc6cd8..76d20f7 100644
--- a/core/modules/system/src/Tests/Form/ElementTest.php
+++ b/core/modules/system/src/Tests/Form/ElementTest.php
@@ -158,7 +158,7 @@ public function testFormAutocomplete() {
     $this->drupalGet('form-test/autocomplete');
 
     // Make sure that the autocomplete library is added.
-    $this->assertRaw('core/misc/autocomplete.js');
+    $this->assertRaw('stable/js/core/autocomplete.js');
 
     $result = $this->xpath('//input[@id="edit-autocomplete-1" and contains(@data-autocomplete-path, "form-test/autocomplete-1")]');
     $this->assertEqual(count($result), 1, 'Ensure that the user does have access to the autocompletion');
diff --git a/core/modules/system/src/Tests/Form/ElementsVerticalTabsTest.php b/core/modules/system/src/Tests/Form/ElementsVerticalTabsTest.php
index d4b16e4..5614bc8 100644
--- a/core/modules/system/src/Tests/Form/ElementsVerticalTabsTest.php
+++ b/core/modules/system/src/Tests/Form/ElementsVerticalTabsTest.php
@@ -54,8 +54,8 @@ protected function setUp() {
    */
   function testJavaScriptOrdering() {
     $this->drupalGet('form_test/vertical-tabs');
-    $position1 = strpos($this->content, 'core/misc/vertical-tabs.js');
-    $position2 = strpos($this->content, 'core/misc/collapse.js');
+    $position1 = strpos($this->content, 'stable/js/core/vertical-tabs.js');
+    $position2 = strpos($this->content, 'stable/js/core/collapse.js');
     $this->assertTrue($position1 !== FALSE && $position2 !== FALSE && $position1 < $position2, 'vertical-tabs.js is included before collapse.js');
   }
 
diff --git a/core/modules/system/src/Tests/Render/AjaxPageStateTest.php b/core/modules/system/src/Tests/Render/AjaxPageStateTest.php
index 11922f0..53556f1 100644
--- a/core/modules/system/src/Tests/Render/AjaxPageStateTest.php
+++ b/core/modules/system/src/Tests/Render/AjaxPageStateTest.php
@@ -25,6 +25,12 @@ class AjaxPageStateTest extends WebTestBase {
 
   protected function setUp() {
     parent::setUp();
+
+    \Drupal::service('theme_handler')->install(array('stark'));
+    $this->config('system.theme')
+      ->set('default', 'stark')
+      ->save();
+
     // Create an administrator with all permissions.
     $this->adminUser = $this->drupalCreateUser(array_keys(\Drupal::service('user.permissions')
       ->getPermissions()));
diff --git a/core/modules/system/src/Tests/System/SiteMaintenanceTest.php b/core/modules/system/src/Tests/System/SiteMaintenanceTest.php
index b89dc81..d418e75 100644
--- a/core/modules/system/src/Tests/System/SiteMaintenanceTest.php
+++ b/core/modules/system/src/Tests/System/SiteMaintenanceTest.php
@@ -29,6 +29,11 @@ class SiteMaintenanceTest extends WebTestBase {
   protected function setUp() {
     parent::setUp();
 
+    \Drupal::service('theme_handler')->install(array('stark'));
+    $this->config('system.theme')
+      ->set('default', 'stark')
+      ->save();
+
     // Configure 'node' as front page.
     $this->config('system.site')->set('page.front', '/node')->save();
     $this->config('system.performance')->set('js.preprocess', 1)->save();
diff --git a/core/modules/system/src/Tests/System/SystemAuthorizeTest.php b/core/modules/system/src/Tests/System/SystemAuthorizeTest.php
index 3b302bd..f4e6419 100644
--- a/core/modules/system/src/Tests/System/SystemAuthorizeTest.php
+++ b/core/modules/system/src/Tests/System/SystemAuthorizeTest.php
@@ -26,6 +26,11 @@ class SystemAuthorizeTest extends WebTestBase {
   protected function setUp() {
     parent::setUp();
 
+    \Drupal::service('theme_handler')->install(array('stark'));
+    $this->config('system.theme')
+      ->set('default', 'stark')
+      ->save();
+
     // Create an administrator user.
     $this->drupalLogin ($this->drupalCreateUser(array('administer software updates')));
   }
@@ -60,6 +65,6 @@ function testFileTransferHooks() {
     $this->assertText('System Test Username');
     // Test that \Drupal\Core\Render\BareHtmlPageRenderer adds assets as
     // expected to the first page of the authorize.php script.
-    $this->assertRaw('core/misc/states.js');
+    $this->assertRaw('stable/js/core/states.js');
   }
 }
diff --git a/core/modules/system/src/Tests/Theme/StableThemeTest.php b/core/modules/system/src/Tests/Theme/StableThemeTest.php
new file mode 100644
index 0000000..0e2d5a6
--- /dev/null
+++ b/core/modules/system/src/Tests/Theme/StableThemeTest.php
@@ -0,0 +1,70 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Tests\Theme\StableThemeTest.
+ */
+
+namespace Drupal\system\Tests\Theme;
+
+use Drupal\simpletest\KernelTestBase;
+
+/**
+ * Tests the behavior of Stable theme.
+ *
+ * @group Theme
+ */
+class StableThemeTest extends KernelTestBase {
+
+  public static $modules = ['system'];
+
+  /**
+   * The theme handler.
+   *
+   * @var \Drupal\Core\Extension\ThemeHandlerInterface.
+   */
+  protected $themeHandler;
+
+  /**
+   * The theme manager.
+   *
+   * @var \Drupal\Core\Theme\ThemeManagerInterface.
+   */
+  protected $themeManager;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+
+    $this->themeHandler = $this->container->get('theme_handler');
+    $this->themeManager = $this->container->get('theme.manager');
+  }
+
+  /**
+   * Ensure Stable is used by default when no base theme has been defined.
+   */
+  public function testStableIsDefault() {
+    $this->themeHandler->install(['test_stable']);
+    $this->config('system.theme')->set('default', 'test_stable')->save();
+    $theme = $this->themeManager->getActiveTheme();
+    /** @var \Drupal\Core\Theme\ActiveTheme $base_theme */
+    $base_themes = $theme->getBaseThemes();
+    $base_theme = reset($base_themes);
+    $this->assertTrue($base_theme->getName() == 'stable', "Stable theme has been set on if theme haven't decided to opt-out.");
+  }
+
+  /**
+   * Ensure that its possible to disable Stable by setting base theme to false.
+   */
+  public function testWildWest() {
+    $this->themeHandler->install(['test_wild_west']);
+    $this->config('system.theme')->set('default', 'test_wild_west')->save();
+    $theme = $this->themeManager->getActiveTheme();
+    /** @var \Drupal\Core\Theme\ActiveTheme $base_theme */
+    $base_themes = $theme->getBaseThemes();
+    $this->assertTrue(empty($base_themes), 'No base theme has been set.');
+  }
+
+}
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 7ec8d0f..ddfd9e0 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -906,6 +906,12 @@ function system_get_info($type, $name = NULL) {
     foreach ($list as $shortname => $item) {
       if (!empty($item->status)) {
         $info[$shortname] = $item->info;
+        // Remove the default Stable base theme when 'base theme: false' is set
+        // in a theme .info.yml file or when the Twig templating engine is not
+        // being used.
+        if ($type == 'theme' && (empty($info[$shortname]['base theme']) || $info[$shortname]['engine'] != 'twig')) {
+          unset($info[$shortname]['base theme']);
+        }
       }
     }
   }
diff --git a/core/modules/system/tests/themes/test_basetheme/test_basetheme.info.yml b/core/modules/system/tests/themes/test_basetheme/test_basetheme.info.yml
index 7b76ded..afa55cb 100644
--- a/core/modules/system/tests/themes/test_basetheme/test_basetheme.info.yml
+++ b/core/modules/system/tests/themes/test_basetheme/test_basetheme.info.yml
@@ -3,6 +3,7 @@ type: theme
 description: 'Test theme which acts as a base theme for other test subthemes.'
 version: VERSION
 core: 8.x
+base theme: false
 libraries:
   - test_basetheme/global-styling
 stylesheets-remove:
diff --git a/core/modules/system/tests/themes/test_stable/test_stable.info.yml b/core/modules/system/tests/themes/test_stable/test_stable.info.yml
new file mode 100644
index 0000000..52f3fd1
--- /dev/null
+++ b/core/modules/system/tests/themes/test_stable/test_stable.info.yml
@@ -0,0 +1,5 @@
+name: Test Stable
+type: theme
+description: A theme to test that stable is set as the default.
+version: VERSION
+core: 8.x
diff --git a/core/modules/system/tests/themes/test_wild_west/test_wild_west.info.yml b/core/modules/system/tests/themes/test_wild_west/test_wild_west.info.yml
new file mode 100644
index 0000000..d1fcb82
--- /dev/null
+++ b/core/modules/system/tests/themes/test_wild_west/test_wild_west.info.yml
@@ -0,0 +1,6 @@
+name: Test Wild West
+type: theme
+description: A theme that doesn't use Stable as its base. It tests the wild west instead.
+version: VERSION
+base theme: false
+core: 8.x
diff --git a/core/modules/views/src/Tests/Plugin/StyleGridTest.php b/core/modules/views/src/Tests/Plugin/StyleGridTest.php
index ea212dd..aa2353f 100644
--- a/core/modules/views/src/Tests/Plugin/StyleGridTest.php
+++ b/core/modules/views/src/Tests/Plugin/StyleGridTest.php
@@ -54,7 +54,7 @@ public function testGrid() {
 
     // Ensure styles are properly added for grid views.
     $this->drupalGet('test-grid');
-    $this->assertRaw('views/css/views.module.css');
+    $this->assertRaw('stable/css/views/views.module.css');
   }
 
   /**
diff --git a/core/themes/classy/classy.info.yml b/core/themes/classy/classy.info.yml
index 7e74173..4217c4a 100644
--- a/core/themes/classy/classy.info.yml
+++ b/core/themes/classy/classy.info.yml
@@ -4,6 +4,7 @@ description: 'A base theme with sensible default CSS classes added. Learn how to
 package: Core
 version: VERSION
 core: 8.x
+base theme: stable
 
 libraries:
   - classy/base
diff --git a/core/themes/classy/templates/block/block--local-actions-block.html.twig b/core/themes/classy/templates/block/block--local-actions-block.html.twig
index 2a0f5c4..dbe2557 100644
--- a/core/themes/classy/templates/block/block--local-actions-block.html.twig
+++ b/core/themes/classy/templates/block/block--local-actions-block.html.twig
@@ -1,4 +1,4 @@
-{% extends "@block/block.html.twig" %}
+{% extends "@stable/block/block.html.twig" %}
 {#
 /**
  * @file
diff --git a/core/themes/seven/templates/block--local-actions-block.html.twig b/core/themes/seven/templates/block--local-actions-block.html.twig
index 6539758..bae3a53 100644
--- a/core/themes/seven/templates/block--local-actions-block.html.twig
+++ b/core/themes/seven/templates/block--local-actions-block.html.twig
@@ -1,4 +1,4 @@
-{% extends "@block/block.html.twig" %}
+{% extends "@stable/block/block.html.twig" %}
 {#
 /**
  * @file
diff --git a/core/themes/stable/css/block/block.admin.css b/core/themes/stable/css/block/block.admin.css
new file mode 100644
index 0000000..ed12038
--- /dev/null
+++ b/core/themes/stable/css/block/block.admin.css
@@ -0,0 +1,42 @@
+/* Block listing page */
+.region-title__action {
+  display: inline-block;
+  margin-left: 1em; /* LTR */
+}
+[dir="rtl"] .region-title__action {
+  margin-left: 0;
+  margin-right: 1em;
+}
+
+/* Block demo mode */
+.block-region {
+  background-color: #ff6;
+  margin-top: 4px;
+  margin-bottom: 4px;
+  padding: 3px;
+}
+a.block-demo-backlink,
+a.block-demo-backlink:link,
+a.block-demo-backlink:visited {
+  background-color: #b4d7f0;
+  border-radius: 0 0 10px 10px;
+  color: #000;
+  font-family: "Lucida Grande", Verdana, sans-serif;
+  font-size: small;
+  line-height: 20px;
+  left: 20px; /*LTR*/
+  padding: 5px 10px;
+  position: fixed;
+  z-index: 499;
+}
+a.block-demo-backlink:hover {
+  text-decoration: underline;
+}
+
+/* Configure block form - Block description */
+.block-form .form-item-settings-admin-label label {
+  display: inline;
+}
+.block-form .form-item-settings-admin-label label:after {
+  content: ':';
+}
diff --git a/core/themes/stable/css/ckeditor/ckeditor-iframe.css b/core/themes/stable/css/ckeditor/ckeditor-iframe.css
new file mode 100644
index 0000000..8996334
--- /dev/null
+++ b/core/themes/stable/css/ckeditor/ckeditor-iframe.css
@@ -0,0 +1,23 @@
+/**
+ * CSS added to iframe-based instances only.
+ */
+body {
+  font-family: Arial, Verdana, sans-serif;
+  font-size: 15px;
+  color: #222;
+  background-color: #fff;
+  margin: 8px;
+}
+
+@media screen and (max-width: 600px) {
+  /* A font-size of 16px prevents iOS from zooming. */
+  body {
+    font-size: 16px;
+  }
+}
+
+ol, ul, dl {
+  /* Preserved spaces for list items with text direction other than the list.
+   * (CKEditor issues #6249,#8049) */
+  padding: 0 40px;
+}
diff --git a/core/themes/stable/css/ckeditor/ckeditor.admin.css b/core/themes/stable/css/ckeditor/ckeditor.admin.css
new file mode 100644
index 0000000..d318737
--- /dev/null
+++ b/core/themes/stable/css/ckeditor/ckeditor.admin.css
@@ -0,0 +1,316 @@
+/**
+ * @file
+ * Styles for configuration of CKEditor module.
+ *
+ * Many of these styles are adapted directly from the default CKEditor theme
+ * "moono".
+ */
+
+
+
+.ckeditor-toolbar {
+  border: 1px solid #b6b6b6;
+  padding: 0.1667em 0.1667em 0.08em;
+  box-shadow: 0 1px 0 white inset;
+  background: #cfd1cf;
+  background-image: -webkit-linear-gradient(top, whiteSmoke, #cfd1cf);
+  background-image: linear-gradient(top, whiteSmoke, #cfd1cf);
+  margin: 5px 0;
+  /* Disallow any user selections in the drag-and-drop toolbar config UI. */
+  -webkit-user-select: none;
+  -moz-user-select:    none;
+  -ms-user-select:     none;
+  user-select:         none;
+}
+.ckeditor-toolbar-active {
+  margin-top: 0.25em;
+}
+.ckeditor-toolbar-disabled {
+  margin-bottom: 0.5em;
+}
+.ckeditor-toolbar ul,
+.ckeditor-toolbar-disabled ul {
+  list-style: none;
+  margin: 0;
+  padding: 0;
+}
+.ckeditor-row {
+  padding: 2px 0 3px;
+  border-radius: 3px;
+}
+.ckeditor-group-names-are-visible .ckeditor-row {
+  border: 1px solid whitesmoke;
+}
+.ckeditor-row + .ckeditor-row {
+  margin-top: 0.25em;
+}
+.ckeditor-toolbar-group,
+.ckeditor-toolbar-group-placeholder,
+.ckeditor-add-new-group {
+  float: left; /* LTR */
+}
+[dir="rtl"] .ckeditor-toolbar-group,
+[dir="rtl"] .ckeditor-toolbar-group-placeholder,
+[dir="rtl"] .ckeditor-add-new-group {
+  float: right;
+}
+.ckeditor-toolbar-groups {
+  min-height: 2em;
+}
+.ckeditor-toolbar-group {
+  margin: 0 0.3333em;
+  cursor: move;
+}
+.ckeditor-group-names-are-visible .ckeditor-toolbar-group,
+.ckeditor-add-new-group {
+  border: 1px dotted #a6a6a6;
+  border-radius: 3px;
+  padding: 0.2em 0.4em;
+}
+.ckeditor-toolbar-group.placeholder,
+.ckeditor-toolbar-group.placeholder .ckeditor-toolbar-group-name {
+  cursor: not-allowed;
+}
+.ckeditor-toolbar-group.placeholder .ckeditor-toolbar-group-name {
+  font-style: italic;
+}
+.ckeditor-toolbar-group-name {
+  display: none;
+  font-size: 1em;
+  font-weight: normal;
+  margin: 0.25em 0;
+}
+.ckeditor-group-names-are-visible .ckeditor-toolbar-group-name {
+  display: block;
+  cursor: pointer;
+}
+.ckeditor-toolbar-active .placeholder,
+.ckeditor-toolbar-active .ckeditor-add-new-group {
+  display: none;
+}
+.ckeditor-group-names-are-visible .placeholder,
+.ckeditor-group-names-are-visible .ckeditor-add-new-group {
+  display: block;
+}
+.ckeditor-toolbar-group-buttons {
+  float: left; /* LTR */
+}
+[dir="rtl"] .ckeditor-toolbar-group-buttons {
+  float: right;
+}
+.ckeditor-groupnames-toggle {
+  cursor: pointer;
+  float: right; /* LTR */
+}
+[dir="rtl"] .ckeditor-groupnames-toggle {
+  float: left;
+}
+.ckeditor-toolbar .ckeditor-toolbar-group > li {
+  border: 1px solid white;
+  border-radius: 5px;
+  background-image: -webkit-linear-gradient(transparent 60%, rgba(0, 0, 0, 0.1));
+  background-image: linear-gradient(transparent 60%, rgba(0, 0, 0, 0.1));
+  margin: 3px 6px;
+  padding: 3px;
+}
+.ckeditor-toolbar-configuration .fieldset-description{
+  margin-bottom: 1em;
+}
+.ckeditor-toolbar-disabled .ckeditor-toolbar-available,
+.ckeditor-toolbar-disabled .ckeditor-toolbar-dividers {
+  box-sizing: border-box;
+}
+.ckeditor-toolbar-disabled .ckeditor-toolbar-available {
+  float: left;
+  width: 80%;
+}
+.ckeditor-toolbar-disabled .ckeditor-toolbar-dividers {
+  float: right;
+  width: 20%;
+}
+.ckeditor-toolbar-disabled .ckeditor-buttons li a,
+.ckeditor-toolbar .ckeditor-buttons,
+.ckeditor-add-new-group button {
+  border: 1px solid #a6a6a6;
+  border-bottom-color: #979797;
+  border-radius: 3px;
+  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.5), 0 0 2px rgba(255, 255, 255, 0.15) inset, 0 1px 0 rgba(255, 255, 255, 0.15) inset;
+}
+.ckeditor-toolbar-disabled .ckeditor-buttons {
+  border: 0;
+}
+.ckeditor-toolbar-disabled .ckeditor-buttons li {
+  margin: 2px;
+}
+.ckeditor-buttons {
+  min-height: 26px;
+  min-width: 26px;
+}
+.ckeditor-buttons li {
+  padding: 0;
+  margin: 0;
+  float: left; /* LTR */
+}
+[dir="rtl"] .ckeditor-buttons li {
+  float: right;
+}
+.ckeditor-buttons li a,
+.ckeditor-add-new-group button {
+  background: #e4e4e4;
+  background-image: -webkit-linear-gradient(top, white, #e4e4e4);
+  background-image: linear-gradient(top, white, #e4e4e4);
+  color: #474747;
+}
+.ckeditor-buttons li a {
+  border: 0;
+  cursor: move;
+  display: block;
+  min-height: 18px;
+  line-height: 1.4;
+  padding: 4px 6px;
+  position: relative;
+  text-decoration: none;
+  text-shadow: 0 1px 0 rgba(255,255,255,.5);
+  white-space: nowrap;
+}
+.ckeditor-toolbar-dividers {
+  float: right; /* LTR */
+}
+[dir="rtl"] .ckeditor-toolbar-dividers {
+  float: left;
+}
+.ckeditor-buttons li .cke-icon-only {
+  text-indent: -9999px;
+  width: 16px;
+  /* Firefox includes the offscreen text in the focus indicator, resulting in a
+     far too wide focus indicator. This fixes that. */
+  overflow: hidden;
+}
+.ckeditor-buttons li .cke_ltr {
+  direction: ltr;
+}
+.ckeditor-buttons li .cke_rtl {
+  direction: rtl;
+}
+.ckeditor-buttons li a:focus,
+.ckeditor-buttons li a:active,
+.ckeditor-multiple-buttons li a:focus {
+  z-index: 11; /* Ensure focused buttons show their outline on all sides. */
+}
+.ckeditor-buttons li:first-child a {
+  border-top-left-radius: 2px; /* LTR */
+  border-bottom-left-radius: 2px; /* LTR */
+}
+[dir="rtl"] .ckeditor-buttons li:first-child a {
+  border-top-right-radius: 2px;
+  border-bottom-right-radius: 2px;
+}
+.ckeditor-buttons li:last-child a {
+  border-top-right-radius: 2px; /* LTR */
+  border-bottom-right-radius: 2px; /* LTR */
+}
+[dir="rtl"] .ckeditor-buttons li:last-child a {
+  border-top-left-radius: 2px;
+  border-bottom-left-radius: 2px;
+}
+.ckeditor-button-placeholder,
+.ckeditor-toolbar-group-placeholder {
+  background: #9dcae7;
+}
+.ckeditor-toolbar-group-placeholder {
+  border-radius: 4px;
+}
+.ckeditor-multiple-buttons {
+  padding: 1px 2px;
+  margin: 5px;
+  list-style: none;
+  float: left; /* LTR */
+}
+[dir="rtl"] .ckeditor-multiple-buttons {
+  float: right;
+}
+.ckeditor-multiple-buttons li {
+  float: left; /* LTR */
+  margin: 0;
+  padding: 0;
+}
+[dir="rtl"] .ckeditor-multiple-buttons li {
+  float: right;
+}
+.ckeditor-multiple-buttons li a {
+  cursor: move;
+  display: inline-block;
+  min-height: 18px;
+  line-height: 1.4;
+  margin: 0;
+  padding: 2px 0;
+}
+.ckeditor-buttons .ckeditor-group-button-separator,
+.ckeditor-multiple-buttons .ckeditor-group-button-separator {
+  margin: -1px -3px -2px;
+}
+.ckeditor-buttons .ckeditor-group-button-separator a,
+.ckeditor-multiple-buttons .ckeditor-group-button-separator a {
+  background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAAdCAMAAABG4xbVAAAAhFBMVEUAAACmpqampqampqb////l5eX////5+fmmpqatra2urq6vr6+1tbW2tra4uLi6urq8vLzb29ve3t7i4uLl5eXn5+fo6Ojp6enq6urr6+vs7Ozt7e3u7u7v7+/w8PDx8fHy8vLz8/P09PT19fX29vb39/f4+Pj5+fn6+vr7+/v8/Pz+/v7qIQO+AAAACHRSTlMATVmAi8XM29MuWToAAABjSURBVBiVrc5BCoAwDETRMKhtRBduev9LKm1xjItWRBBE6Nt9QkIwOTcUzk0Imi8aoMssxbgoTHMtqsFMLta0vPh2N49HyfdelPg6k9uvX/a+Bmggt1qJRNzQFVgjEnkUZDoBmH57VSypjg4AAAAASUVORK5CYII=) no-repeat center center;
+  width: 13px;
+  padding: 0;
+  height: 29px;
+  position: relative;
+  z-index: 10;
+}
+ul.ckeditor-buttons li.ckeditor-button-separator a {
+  background: #e4e4e4;
+  background-image: -webkit-linear-gradient(#e4e4e4, #b4b4b4);
+  background-image: linear-gradient(#e4e4e4, #b4b4b4);
+  height: 24px;
+  margin: 1px 0 0;
+  padding: 0;
+  position: relative;
+  width: 1px;
+  z-index: 10;
+}
+.ckeditor-multiple-buttons .ckeditor-button-separator a {
+  width: 2px;
+  padding: 0;
+  height: 26px;
+  margin: 0 10px;
+}
+.ckeditor-separator {
+  background-color: silver;
+  background-color: rgba(0, 0, 0, 0.2);
+  margin: 5px 0;
+  height: 18px;
+  width: 1px;
+  display: block;
+  box-shadow: 1px 0 1px rgba(255, 255, 255, 0.5)
+}
+.ckeditor-button-arrow {
+  width: 0;
+  text-align: center;
+  border-left: 3px solid transparent;
+  border-right: 3px solid transparent;
+  border-top: 3px solid #333;
+  display: inline-block;
+  margin: 0 4px 2px;
+}
+.ckeditor-row-controls {
+  float: right; /* LTR */
+  font-size: 18px;
+  width: 40px;
+  text-align: right; /* LTR */
+}
+[dir="rtl"] .ckeditor-row-controls {
+  float: left;
+  text-align: left;
+}
+.ckeditor-row-controls a {
+  display: inline-block;
+  box-sizing: border-box;
+  padding: 6px 2px;
+  height: 28px;
+  width: 20px;
+  line-height: 0.9;
+  font-weight: bold;
+  color: #333;
+  text-decoration: none;
+}
diff --git a/core/themes/stable/css/ckeditor/ckeditor.css b/core/themes/stable/css/ckeditor/ckeditor.css
new file mode 100644
index 0000000..b4b1e16
--- /dev/null
+++ b/core/themes/stable/css/ckeditor/ckeditor.css
@@ -0,0 +1,39 @@
+.ckeditor-dialog-loading {
+  position: absolute;
+  top: 0;
+  width: 100%;
+  text-align: center;
+}
+
+.ckeditor-dialog-loading-link {
+  border-radius: 0 0 5px 5px;
+  border: 1px solid #B6B6B6;
+  border-top: none;
+  background: white;
+  padding: 3px 10px;
+  box-shadow: 0 0 10px -3px #000;
+  display: inline-block;
+  font-size: 14px;
+  position: relative;
+  top: 0;
+  -webkit-touch-callout: none;
+  -webkit-user-select: none;
+  -khtml-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  user-select: none;
+}
+
+/**
+ * Adjust the style of in-place editing CKEditor instances.
+ */
+.quickedit-toolgroup.wysiwyg-main .cke_chrome,
+.quickedit-toolgroup.wysiwyg-main .cke_inner,
+.quickedit-toolgroup.wysiwyg-main .cke_top {
+  background: transparent;
+  border-top: none;
+  border-right: none;
+  border-bottom: none;
+  border-left: none;
+  box-shadow: none;
+}
diff --git a/core/themes/stable/css/ckeditor/plugins/drupalimagecaption/ckeditor.drupalimagecaption.css b/core/themes/stable/css/ckeditor/plugins/drupalimagecaption/ckeditor.drupalimagecaption.css
new file mode 100644
index 0000000..8082585
--- /dev/null
+++ b/core/themes/stable/css/ckeditor/plugins/drupalimagecaption/ckeditor.drupalimagecaption.css
@@ -0,0 +1,21 @@
+/**
+ * @file
+ * Image Caption: overrides to make centered alignment work inside CKEditor.
+ */
+
+/**
+ * Since .align-center is set on the non-captioned image's parent block element
+ * in CKEditor, the image must be centered separately.
+ */
+p[data-widget="image"].align-center {
+  text-align: center;
+}
+
+/**
+ * Since .align-center is set on captioned widget's wrapper element in CKEditor,
+ * the alignment of internals must be set separately.
+ */
+div[data-cke-widget-wrapper].align-center > figure[data-widget="image"] {
+  margin-left: auto;
+  margin-right: auto;
+}
diff --git a/core/themes/stable/css/color/color.admin.css b/core/themes/stable/css/color/color.admin.css
new file mode 100644
index 0000000..3dfa0dc
--- /dev/null
+++ b/core/themes/stable/css/color/color.admin.css
@@ -0,0 +1,150 @@
+/**
+ * @file
+ * Stylesheet for the administration pages of the Color module.
+ */
+.color-form {
+  max-width: 50em;
+}
+.farbtastic {
+  margin: 0 auto;
+}
+.color-form .form-item {
+  margin: 0.5em 0;
+  height: 2em;
+  padding: 0.5em;
+}
+.color-form label {
+  clear: left; /* LTR */
+}
+[dir="rtl"] .color-form label {
+  clear: right;
+}
+.color-form .form-text {
+  float: left; /* LTR */
+  width: 86%;
+  text-align: center;
+  cursor: pointer;
+}
+[dir="rtl"] .color-form .form-text {
+  float: right;
+}
+.color-palette__hook {
+  float: left; /* LTR */
+  width: 16px;
+  height: 16px;
+}
+[dir="rtl"] .color-palette__hook {
+  float: right;
+}
+.color-palette__hook.is-down,
+.color-palette__hook.is-up,
+.color-palette__hook.is-both {
+  background: url(../../../../modules/color/images/hook.png) no-repeat 100% 0; /* LTR */
+}
+[dir="rtl"] .color-palette__hook.is-down,
+[dir="rtl"] .color-palette__hook.is-up,
+[dir="rtl"] .color-palette__hook.is-both {
+  background: url(../../../../modules/color/images/hook-rtl.png) no-repeat 0 0;
+}
+.color-palette__hook.is-up {
+  background-position: 100% -27px; /* LTR */
+}
+[dir="rtl"] .color-palette__hook.is-up {
+  background-position: 0 -27px;
+}
+.color-palette__hook.is-both {
+  background-position: 100% -54px; /* LTR */
+}
+[dir="rtl"] .color-palette__hook.is-both {
+  background-position: 0 -54px;
+}
+/**
+ * The button also inherits from .link, which hides the background. Use a more
+ * specific selector to overwrite.
+ */
+button.color-palette__lock,
+.color-palette__lock {
+  float: left; /* LTR */
+  width: 20px;
+  height: 19px;
+  background: url(../../../../modules/color/images/lock.png) no-repeat 50% 0;
+  cursor: pointer;
+  position: relative;
+  top: -1.7em;
+  left: -10px;
+  direction: ltr;
+  text-indent: -9999px;
+}
+[dir="rtl"] button.color-palette__lock,
+[dir="rtl"] .color-palette__lock {
+  float: right;
+}
+/* Same as above .color-palette__lock rule. */
+button.is-unlocked,
+.is-unlocked {
+  background-position: 50% -22px;
+}
+
+/* wide viewport. */
+@media screen and (min-width: 37.5625em) { /* 601px */
+  .color-placeholder {
+    float: right; /* LTR */
+  }
+  [dir="rtl"] .color-placeholder {
+    float: left;
+  }
+  .color-form .form-item {
+    margin: 0.5em 195px 0.5em 0; /* LTR */
+  }
+  [dir="rtl"] .color-form .form-item {
+    margin: 0.5em 0 0.5em 195px;
+  }
+  .color-form label {
+    float: left; /* LTR */
+    clear: left; /* LTR */
+    width: 15em;
+  }
+  [dir="rtl"] .color-form label {
+    float: right;
+    clear: right;
+  }
+  .color-form .form-text,
+  .color-form .form-select {
+    float: left; /* LTR */
+    width: auto;
+  }
+  [dir="rtl"] .color-form .form-text,
+  [dir="rtl"] .color-form .form-select {
+    float: right;
+  }
+  .color-palette__hook {
+    float: left; /* LTR */
+    margin-top: 3px;
+  }
+  [dir="rtl"] .color-palette__hook {
+    float: right;
+  }
+}
+.item-selected {
+  background: #eee;
+}
+
+/* Preview */
+.color-preview {
+  display: none;
+}
+.js .color-preview {
+  display: block;
+  position: relative;
+  float: left; /* LTR */
+}
+.js[dir="rtl"] .color-preview {
+  float: right;
+}
+@media screen and (max-width: 30em) { /* 480px */
+  .color-form .color-preview-sidebar,
+  .color-form .color-preview-content {
+    width: auto;
+    margin: 0;
+  }
+}
diff --git a/core/themes/stable/css/config_translation/config_translation.admin.css b/core/themes/stable/css/config_translation/config_translation.admin.css
new file mode 100644
index 0000000..8e862fb
--- /dev/null
+++ b/core/themes/stable/css/config_translation/config_translation.admin.css
@@ -0,0 +1,24 @@
+/**
+ * @file
+ * Styles for Configuration Translation.
+ */
+
+/**
+ * Hide the label, in an accessible way, for responsive screens which show the
+ * form in one column.
+ */
+.translation-set__translated label {
+  clip: rect(1px, 1px, 1px, 1px);
+  height: 1px;
+  overflow: hidden;
+  position: absolute;
+  width: 1px;
+}
+
+@media screen and (min-width: 38em) {
+  .translation-set__translated label {
+    height: auto;
+    position: inherit;
+    width: auto;
+  }
+}
diff --git a/core/themes/stable/css/content_translation/content_translation.admin.css b/core/themes/stable/css/content_translation/content_translation.admin.css
new file mode 100644
index 0000000..45fb024
--- /dev/null
+++ b/core/themes/stable/css/content_translation/content_translation.admin.css
@@ -0,0 +1,33 @@
+/**
+ * @file
+ * Styles for the content language administration page.
+ */
+
+.language-content-settings-form .bundle {
+  width: 24%;
+}
+.language-content-settings-form .field {
+  padding-left: 3em; /* LTR */
+  width: 24%;
+}
+[dir="rtl"] .language-content-settings-form .field {
+  padding-right: 3em;
+  padding-left: 1em;
+}
+.language-content-settings-form .column {
+  padding-left: 5em; /* LTR */
+}
+[dir="rtl"] .language-content-settings-form .column {
+  padding-right: 5em;
+  padding-left: 1em;
+}
+.language-content-settings-form .field label,
+.language-content-settings-form .column label {
+  font-weight: normal;
+}
+.language-content-settings-form .translatable {
+  width: 1%;
+}
+.language-content-settings-form .operations {
+  width: 75%;
+}
diff --git a/core/themes/stable/css/contextual/contextual.icons.theme.css b/core/themes/stable/css/contextual/contextual.icons.theme.css
new file mode 100644
index 0000000..df86dcb
--- /dev/null
+++ b/core/themes/stable/css/contextual/contextual.icons.theme.css
@@ -0,0 +1,39 @@
+/**
+ * @file
+ * Styling for contextual module icons.
+ */
+
+/**
+ * Toolbar tab icon.
+ */
+.toolbar-bar .toolbar-icon-edit:before {
+  background-image: url(../../../../misc/icons/bebebe/pencil.svg);
+}
+.toolbar-bar .toolbar-icon-edit:active:before,
+.toolbar-bar .toolbar-icon-edit.is-active:before {
+  background-image: url(../../../../misc/icons/ffffff/pencil.svg);
+}
+
+/**
+ * Contextual trigger.
+ */
+.contextual .trigger {
+  background-image: url(../../../../misc/icons/bebebe/pencil.svg);
+  background-position: center center;
+  background-repeat: no-repeat;
+  background-size: 16px 16px;
+  /* Override the .focusable height: auto */
+  height: 26px !important;
+  /* Override the .focusable height: auto */
+  width: 26px !important;
+  text-indent: -9999px;
+}
+
+.contextual .trigger:hover {
+  background-image: url(../../../../misc/icons/787878/pencil.svg);
+}
+
+.contextual .trigger:focus {
+  background-image: url(../../../../misc/icons/5181c6/pencil.svg);
+  outline: none;
+}
diff --git a/core/themes/stable/css/contextual/contextual.module.css b/core/themes/stable/css/contextual/contextual.module.css
new file mode 100644
index 0000000..2a21e1b
--- /dev/null
+++ b/core/themes/stable/css/contextual/contextual.module.css
@@ -0,0 +1,18 @@
+/**
+ * @file
+ * Generic base styles for contextual module.
+ */
+
+.contextual-region {
+  position: relative;
+}
+.contextual .trigger:focus {
+  /* Override the .focusable position: static */
+  position: relative !important;
+}
+.contextual-links {
+  display: none;
+}
+.contextual.open .contextual-links {
+  display: block;
+}
diff --git a/core/themes/stable/css/contextual/contextual.theme.css b/core/themes/stable/css/contextual/contextual.theme.css
new file mode 100644
index 0000000..065065d
--- /dev/null
+++ b/core/themes/stable/css/contextual/contextual.theme.css
@@ -0,0 +1,112 @@
+/**
+ * @file
+ * Styling for contextual module.
+ */
+
+/**
+ * Contextual links wrappers.
+ */
+.contextual {
+  position: absolute;
+  right: 0; /* LTR */
+  top: 6px;
+  z-index: 500;
+}
+[dir="rtl"] .contextual {
+  left: 0;
+  right: auto;
+}
+
+/**
+ * Contextual region.
+ */
+.contextual-region.focus {
+  outline: 1px dashed #d6d6d6;
+  outline-offset: 1px;
+}
+
+/**
+ * Contextual trigger.
+ */
+.contextual .trigger {
+  background-attachment: scroll;
+  background-color: #fff;
+  border: 1px solid #ccc;
+  border-radius: 13px;
+  float: right; /* LTR */
+  margin: 0;
+  overflow: hidden;
+  padding: 0 2px;
+  position: relative;
+  right: 6px; /* LTR */
+  cursor: pointer;
+}
+[dir="rtl"] .contextual .trigger {
+  float: left;
+  right: auto;
+  left: 6px;
+}
+.contextual.open .trigger {
+  border: 1px solid #ccc;
+  border-bottom-color: transparent;
+  border-radius: 13px 13px 0 0;
+  box-shadow: none;
+  z-index: 2;
+}
+
+/**
+ * Contextual links.
+ *
+ * The following selectors are heavy to discourage theme overriding.
+ */
+.contextual-region .contextual .contextual-links {
+  background-color: #fff;
+  border: 1px solid #ccc;
+  border-radius: 4px 0 4px 4px; /* LTR */
+  clear: both;
+  float: right; /* LTR */
+  margin: 0;
+  padding: 0.25em 0;
+  position: relative;
+  right: 6px; /* LTR */
+  text-align: left; /* LTR */
+  top: -1px;
+  white-space: nowrap;
+}
+[dir="rtl"] .contextual-region .contextual .contextual-links {
+  border-radius: 0 4px 4px 4px;
+  float: left;
+  left: 6px;
+  right: auto;
+  text-align: right;
+}
+.contextual-region .contextual .contextual-links li {
+  background-color: #fff;
+  border: none;
+  list-style: none;
+  list-style-image: none;
+  margin: 0;
+  padding: 0;
+  line-height: 100%;
+}
+.contextual-region .contextual .contextual-links a {
+  background-color: #fff;
+  color: #333;
+  display: block;
+  font-family: sans-serif;
+  font-size: small;
+  line-height: 0.8em;
+  margin: 0.25em 0;
+  padding: 0.4em 0.6em;
+}
+.touch .contextual-region .contextual .contextual-links a {
+  font-size: large;
+}
+.contextual-region .contextual .contextual-links a,
+.contextual-region .contextual .contextual-links a:hover {
+  text-decoration: none;
+}
+.no-touch .contextual-region .contextual .contextual-links li a:hover {
+  color: #000;
+  background: #f7fcff;
+}
diff --git a/core/themes/stable/css/contextual/contextual.toolbar.css b/core/themes/stable/css/contextual/contextual.toolbar.css
new file mode 100644
index 0000000..dd573a7
--- /dev/null
+++ b/core/themes/stable/css/contextual/contextual.toolbar.css
@@ -0,0 +1,24 @@
+/**
+ * @file
+ * Styling for contextual module's toolbar tab.
+ */
+
+/* Tab appearance. */
+.toolbar .toolbar-bar .contextual-toolbar-tab.toolbar-tab {
+  float: right; /* LTR */
+}
+[dir="rtl"] .toolbar .toolbar-bar .contextual-toolbar-tab.toolbar-tab {
+  float: left;
+}
+.toolbar .toolbar-bar .contextual-toolbar-tab .toolbar-item {
+  margin: 0;
+}
+.toolbar .toolbar-bar .contextual-toolbar-tab .toolbar-item.is-active {
+  background-image:-webkit-linear-gradient(rgb(78,159,234) 0%, rgb(69,132,221) 100%);
+  background-image:linear-gradient(rgb(78,159,234) 0%,rgb(69,132,221) 100%);
+}
+
+/* @todo get rid of this declaration by making toolbar.module's CSS less specific */
+.toolbar .toolbar-bar .contextual-toolbar-tab.toolbar-tab.hidden {
+  display: none;
+}
diff --git a/core/themes/stable/css/core/dropbutton/dropbutton.css b/core/themes/stable/css/core/dropbutton/dropbutton.css
new file mode 100644
index 0000000..5990514
--- /dev/null
+++ b/core/themes/stable/css/core/dropbutton/dropbutton.css
@@ -0,0 +1,164 @@
+
+/**
+ * @file
+ * Base styles for dropbuttons.
+ */
+
+/**
+ * When a dropbutton has only one option, it is simply a button.
+ */
+.dropbutton-wrapper,
+.dropbutton-wrapper div {
+  box-sizing: border-box;
+}
+.js .dropbutton-wrapper,
+.js .dropbutton-widget {
+  display: block;
+  position: relative;
+}
+
+@media screen and (max-width:600px) {
+  .js .dropbutton-wrapper {
+    width: 100%;
+  }
+}
+
+/* Splitbuttons */
+@media screen and (min-width:600px) {
+  .form-actions .dropbutton-wrapper {
+    float: left; /* LTR */
+  }
+  [dir="rtl"] .form-actions .dropbutton-wrapper {
+    float: right;
+  }
+}
+.js .form-actions .dropbutton-widget {
+  position: static;
+}
+.js td .dropbutton-widget {
+  position: absolute;
+}
+.js td .dropbutton-wrapper {
+  min-height: 2em;
+}
+.js td .dropbutton-multiple {
+  padding-right: 10em; /* LTR */
+  margin-right: 2em; /* LTR */
+  max-width: 100%;
+}
+[dir="rtl"].js td .dropbutton-multiple {
+  padding-right: 0;
+  margin-right: 0;
+  padding-left: 10em;
+  margin-left: 2em;
+}
+.js td .dropbutton-multiple .dropbutton-action a,
+.js td .dropbutton-multiple .dropbutton-action input,
+.js td .dropbutton-multiple .dropbutton-action button {
+  width: auto;
+}
+
+/* UL styles are over-scoped in core, so this selector needs weight parity. */
+.js .dropbutton-widget .dropbutton {
+  list-style-image: none;
+  list-style-type: none;
+  margin: 0;
+  overflow: hidden;
+  padding: 0;
+}
+.js .dropbutton li,
+.js .dropbutton a {
+  display: block;
+  outline: none;
+}
+
+.js .dropbutton li:hover,
+.js .dropbutton li:focus,
+.js .dropbutton a:hover,
+.js .dropbutton a:focus {
+  outline: initial;
+}
+
+/**
+ * The dropbutton styling.
+ *
+ * A dropbutton is a widget that displays a list of action links as a button
+ * with a primary action. Secondary actions are hidden behind a click on a
+ * twisty arrow.
+ *
+ * The arrow is created using border on a zero-width, zero-height span.
+ * The arrow inherits the link color, but can be overridden with border colors.
+ */
+.js .dropbutton-multiple .dropbutton-widget {
+  padding-right: 2em; /* LTR */
+}
+.js[dir="rtl"] .dropbutton-multiple .dropbutton-widget {
+  padding-left: 2em;
+  padding-right: 0;
+}
+.dropbutton-multiple.open,
+.dropbutton-multiple.open .dropbutton-widget {
+  max-width: none;
+}
+.dropbutton-multiple.open {
+  z-index: 100;
+}
+.dropbutton-multiple .dropbutton .secondary-action {
+  display: none;
+}
+.dropbutton-multiple.open .dropbutton .secondary-action {
+  display: block;
+}
+.dropbutton-toggle {
+  bottom: 0;
+  display: block;
+  position: absolute;
+  right: 0; /* LTR */
+  text-indent: 110%;
+  top: 0;
+  white-space: nowrap;
+  width: 2em;
+}
+[dir="rtl"] .dropbutton-toggle {
+  left: 0;
+  right: auto;
+}
+.dropbutton-toggle button {
+  background: none;
+  border: 0;
+  cursor: pointer;
+  display: block;
+  height: 100%;
+  margin: 0;
+  padding: 0;
+  width: 100%;
+}
+.dropbutton-toggle button:hover,
+.dropbutton-toggle button:focus {
+  outline: initial;
+}
+.dropbutton-arrow {
+  border-bottom-color: transparent;
+  border-left-color: transparent;
+  border-right-color: transparent;
+  border-style: solid;
+  border-width: 0.3333em 0.3333em 0;
+  display: block;
+  height: 0;
+  line-height: 0;
+  position: absolute;
+  right: 40%; /* 0.6667em; */ /* LTR */
+  top: 50%;
+  margin-top: -0.1666em;
+  width: 0;
+  overflow: hidden;
+}
+[dir="rtl"] .dropbutton-arrow {
+  left: 0.6667em;
+  right: auto;
+}
+.dropbutton-multiple.open .dropbutton-arrow {
+  border-bottom: 0.3333em solid;
+  border-top-color: transparent;
+  top: 0.6667em;
+}
diff --git a/core/themes/stable/css/core/print.css b/core/themes/stable/css/core/print.css
new file mode 100644
index 0000000..6e69a3b
--- /dev/null
+++ b/core/themes/stable/css/core/print.css
@@ -0,0 +1,25 @@
+
+body {
+  margin: 1em;
+  background-color: #fff;
+}
+[dir="rtl"] body {
+  direction: rtl;
+}
+th {
+  text-align: left; /* LTR */
+  color: #006;
+  border-bottom: 1px solid #ccc;
+}
+[dir="rtl"] th {
+  text-align: right;
+}
+tr:nth-child(odd) {
+  background-color: #ddd;
+}
+tr:nth-child(even){
+  background-color: #fff;
+}
+td {
+  padding: 5px;
+}
diff --git a/core/themes/stable/css/core/vertical-tabs.css b/core/themes/stable/css/core/vertical-tabs.css
new file mode 100644
index 0000000..ec5d3c1
--- /dev/null
+++ b/core/themes/stable/css/core/vertical-tabs.css
@@ -0,0 +1,69 @@
+/**
+ * @file
+ * Vertical Tabs.
+ */
+
+.vertical-tabs {
+  margin: 1em 0 1em 15em; /* LTR */
+  border: 1px solid #ccc;
+}
+[dir="rtl"] .vertical-tabs {
+   margin-left: 0;
+   margin-right: 15em;
+}
+.vertical-tabs__menu {
+  float: left; /* LTR */
+  width: 15em;
+  margin: -1px 0 -1px -15em; /* LTR */
+  padding: 0;
+  border-top: 1px solid #ccc;
+  list-style: none;
+}
+[dir="rtl"] .vertical-tabs__menu {
+  float: right;
+  margin-left: 0;
+  margin-right: -15em;
+}
+.vertical-tabs__pane {
+  margin: 0;
+  border: 0;
+}
+.vertical-tabs__pane > summary {
+  display: none;
+}
+
+/* Layout of each tab. */
+.vertical-tabs__menu-item {
+  border: 1px solid #ccc;
+  border-top: 0;
+  background: #eee;
+}
+.vertical-tabs__menu-item a {
+  display: block;
+  padding: 0.5em 0.6em;
+  text-decoration: none;
+}
+.vertical-tabs__menu-item a:focus .vertical-tabs__menu-item-title,
+.vertical-tabs__menu-item a:active .vertical-tabs__menu-item-title,
+.vertical-tabs__menu-item a:hover .vertical-tabs__menu-item-title {
+  text-decoration: underline;
+}
+.vertical-tabs__menu-item a:hover {
+  outline: 1px dotted;
+}
+.vertical-tabs__menu-item.is-selected {
+  border-right-width: 0; /* LTR */
+  background-color: #fff;
+}
+[dir="rtl"] .vertical-tabs__menu-item.is-selected {
+  border-left-width: 0;
+  border-right-width: 1px;
+}
+.vertical-tabs__menu-item.is-selected .vertical-tabs__menu-item-title {
+  color: #000;
+}
+.vertical-tabs__menu-item-summary {
+  display: block;
+  margin-bottom: 0;
+  line-height: normal;
+}
diff --git a/core/themes/stable/css/dblog/dblog.module.css b/core/themes/stable/css/dblog/dblog.module.css
new file mode 100644
index 0000000..1140686
--- /dev/null
+++ b/core/themes/stable/css/dblog/dblog.module.css
@@ -0,0 +1,37 @@
+/**
+ * @file
+ * Admin styles for the Database Logging module.
+ */
+.dblog-filter-form .form-item-type,
+.dblog-filter-form .form-item-severity {
+  display: inline-block;
+  margin: .1em .9em .1em .1em; /* LTR */
+  max-width: 30%;
+}
+[dir="rtl"] .dblog-filter-form .form-item-type,
+[dir="rtl"] .dblog-filter-form .form-item-severity {
+  margin: .1em .1em .1em .9em;
+}
+.dblog-filter-form .form-actions {
+  display: inline-block;
+  padding: 3ex 0 0;
+  vertical-align: top;
+}
+.admin-dblog .icon,
+.admin-dblog .dblog-warning .icon,
+.admin-dblog .dblog-error .icon,
+.admin-dblog .dblog-critical .icon,
+.admin-dblog .dblog-alert .icon,
+.admin-dblog .dblog-emergency .icon {
+  background: no-repeat center;
+  width: 16px;
+}
+.admin-dblog .dblog-warning .icon {
+  background-image: url(../../../../misc/icons/e29700/warning.svg);
+}
+.admin-dblog .dblog-error .icon,
+.admin-dblog .dblog-critical .icon,
+.admin-dblog .dblog-alert .icon,
+.admin-dblog .dblog-emergency .icon {
+  background-image: url(../../../../misc/icons/e32700/error.svg);
+}
diff --git a/core/themes/stable/css/editor/editor.css b/core/themes/stable/css/editor/editor.css
new file mode 100644
index 0000000..9d1c660
--- /dev/null
+++ b/core/themes/stable/css/editor/editor.css
@@ -0,0 +1,16 @@
+/**
+ * @file
+ * Styles for text editors.
+ */
+
+.editor-dialog {
+  /* This !important is required to override inline CSS of jQuery UI. */
+  width: 80% !important;
+  max-width: 500px;
+}
+
+@media screen and (max-width: 600px) {
+  .editor-dialog {
+    width: 95% !important;
+  }
+}
diff --git a/core/themes/stable/css/field_ui/field_ui.admin.css b/core/themes/stable/css/field_ui/field_ui.admin.css
new file mode 100644
index 0000000..0cf7a68
--- /dev/null
+++ b/core/themes/stable/css/field_ui/field_ui.admin.css
@@ -0,0 +1,41 @@
+/**
+ * @file
+ * Stylesheet for the Field UI module.
+ */
+
+/* 'Manage fields' and 'Manage display' overviews */
+.field-ui-overview .region-title td {
+  font-weight: bold;
+}
+.field-ui-overview .region-message td {
+  font-style: italic;
+}
+
+/* 'Manage form display' and 'Manage display' overview */
+.field-ui-overview .field-plugin-summary-cell {
+  line-height: 1em;
+}
+.field-ui-overview .field-plugin-summary {
+  float: left;
+  font-size: .9em;
+}
+.field-ui-overview .field-plugin-summary-cell .warning {
+  display: block;
+  float: left;
+  margin-right: .5em;
+}
+.field-ui-overview .field-plugin-settings-edit-wrapper {
+  float: right;
+}
+.field-ui-overview .field-plugin-settings-edit {
+  float: right;
+}
+.field-ui-overview .field-plugin-settings-editing td {
+  vertical-align: top;
+}
+.field-ui-overview .field-plugin-settings-editing .field-plugin-type {
+  display: none;
+}
+.field-ui-overview .field-plugin-settings-edit-form .plugin-name {
+  font-weight: bold;
+}
diff --git a/core/themes/stable/css/file/file.admin.css b/core/themes/stable/css/file/file.admin.css
new file mode 100644
index 0000000..6a45644
--- /dev/null
+++ b/core/themes/stable/css/file/file.admin.css
@@ -0,0 +1,18 @@
+/**
+ * @file
+ * Admin stylesheet for file module.
+ */
+
+/* File upload widget.*/
+.form-managed-file .form-submit {
+  margin: 0 0.5em;
+}
+.form-managed-file div.ajax-progress-bar {
+  display: none;
+  margin-top: 4px;
+  padding: 0;
+  width: 28em;
+}
+.form-managed-file .ajax-progress-bar .bar {
+  margin: 0;
+}
diff --git a/core/themes/stable/css/filter/filter.admin.css b/core/themes/stable/css/filter/filter.admin.css
new file mode 100644
index 0000000..bfe883f
--- /dev/null
+++ b/core/themes/stable/css/filter/filter.admin.css
@@ -0,0 +1,81 @@
+
+/**
+ * @file
+ * Admin styling for the Filter module.
+ */
+
+/**
+ * Filter information under field.
+ */
+.text-format-wrapper > .form-item {
+  margin-bottom: 0;
+}
+
+.filter-wrapper {
+  border: 1px solid #ccc;
+  border-top: 0;
+  margin: 0;
+  padding: 0.5em 0.666em;
+  overflow: hidden;
+}
+.filter-wrapper .form-item {
+  margin: 0;
+}
+.filter-wrapper .form-item label {
+  display: inline;
+}
+
+.filter-help {
+  float: right; /* LTR */
+}
+[dir="rtl"] .filter-help {
+  float: left;
+}
+.filter-guidelines .filter-guidelines-item {
+  margin-top: 1em;
+}
+.filter-help p {
+  margin: 0;
+}
+.filter-help a {
+  position: relative;
+  margin: 0 20px 0 0; /* LTR */
+}
+[dir="rtl"] .filter-help a {
+  margin: 0 0 0 20px;
+}
+.filter-help a:after {
+  position: absolute;
+  top: 0;
+  right: -20px; /* LTR */
+  content: '';
+  display: block;
+  width: 16px;
+  height: 16px;
+  background: transparent url(../../../../misc/help.png);
+}
+[dir="rtl"] .filter-help a:after {
+  right: auto;
+  left: -20px;
+}
+
+.text-format-wrapper .description {
+  margin-top: 0.5em;
+}
+.tips {
+  font-size: 0.9em;
+  margin-bottom: 0;
+  margin-top: 0;
+  padding-bottom: 0;
+  padding-top: 0;
+}
+
+/**
+ * Improve filter tips position.
+ */
+.tips {
+  padding-left: 0; /* LTR */
+}
+[dir="rtl"] .tips {
+  padding-right: 0;
+}
diff --git a/core/themes/stable/css/filter/filter.caption.css b/core/themes/stable/css/filter/filter.caption.css
new file mode 100644
index 0000000..c436f1b
--- /dev/null
+++ b/core/themes/stable/css/filter/filter.caption.css
@@ -0,0 +1,30 @@
+/**
+ * @file
+ * Caption filter: default styling for displaying image captions.
+ */
+
+/**
+ * Essentials, based on http://stackoverflow.com/a/13363408.
+ */
+.caption {
+  display: table;
+}
+.caption > * {
+  display: block;
+  max-width: 100%;
+}
+.caption > figcaption {
+  display: table-caption;
+  caption-side: bottom;
+  max-width: none;
+}
+
+/**
+ * While editing and whenever the caption is empty, show a placeholder.
+ *
+ * Based on http://codepen.io/flesler/pen/AEIFc.
+ */
+.caption > figcaption[contenteditable=true]:empty:before {
+  content: attr(data-placeholder);
+  font-style: italic;
+}
diff --git a/core/themes/stable/css/image/image.admin.css b/core/themes/stable/css/image/image.admin.css
new file mode 100644
index 0000000..9f9878a
--- /dev/null
+++ b/core/themes/stable/css/image/image.admin.css
@@ -0,0 +1,74 @@
+
+/**
+ * Image style configuration pages.
+ */
+.image-style-new,
+.image-style-new div {
+  display: inline;
+}
+.image-style-preview .preview-image-wrapper {
+  float: left;
+  padding-bottom: 2em;
+  text-align: center;
+  top: 50%;
+  width: 48%;
+}
+.image-style-preview .preview-image {
+  margin: auto;
+  position: relative;
+}
+.image-style-preview .preview-image .width {
+  border: 1px solid #666;
+  border-top: none;
+  bottom: -6px;
+  height: 2px;
+  left: -1px;
+  position: absolute;
+  box-sizing: content-box;
+}
+.image-style-preview .preview-image .width span {
+  position: relative;
+  top: 4px;
+}
+.image-style-preview .preview-image .height {
+  border: 1px solid #666;
+  border-left: none;
+  position: absolute;
+  right: -6px;
+  top: -1px;
+  width: 2px;
+  box-sizing: content-box;
+}
+.image-style-preview .preview-image .height span {
+  height: 2em;
+  left: 10px;
+  margin-top: -1em;
+  position: absolute;
+  top: 50%;
+}
+
+/**
+ * Improve image style preview on narrow viewports.
+ */
+@media screen and (max-width: 470px) {
+  .image-style-preview .preview-image-wrapper {
+    float: none;
+    margin-bottom: 1em;
+  }
+  .image-style-preview .preview-image-wrapper:last-child {
+    margin-bottom: 0;
+  }
+}
+
+/**
+ * Image anchor element.
+ */
+.image-anchor {
+  width: auto;
+}
+.image-anchor tr {
+  background: none;
+}
+.image-anchor td {
+  border: 1px solid #ccc;
+}
diff --git a/core/themes/stable/css/language/language.admin.css b/core/themes/stable/css/language/language.admin.css
new file mode 100644
index 0000000..910279e
--- /dev/null
+++ b/core/themes/stable/css/language/language.admin.css
@@ -0,0 +1,11 @@
+/**
+ * @file
+ * Styles for the content language administration page.
+ */
+
+#language-content-settings-form table .bundle {
+  width: 25%;
+}
+#language-content-settings-form table .operations {
+  width: 75%;
+}
diff --git a/core/themes/stable/css/locale/locale.admin.css b/core/themes/stable/css/locale/locale.admin.css
new file mode 100644
index 0000000..aa05e88
--- /dev/null
+++ b/core/themes/stable/css/locale/locale.admin.css
@@ -0,0 +1,135 @@
+.locale-translate-filter-form .details-wrapper {
+  overflow: hidden;
+}
+.locale-translate-filter-form .form-item-langcode,
+.locale-translate-filter-form .form-item-translation,
+.locale-translate-filter-form .form-item-customized {
+  float: left; /* LTR */
+  margin-right: 1em; /* LTR */
+  margin-bottom: 0;
+  /**
+   * In Opera 9, DOM elements with the property of "overflow: auto"
+   * will partially hide its contents with unnecessary scrollbars when
+   * its immediate child is floated without an explicit width set.
+   */
+  width: 15em;
+}
+[dir="rtl"] .locale-translate-filter-form .form-item-langcode,
+[dir="rtl"] .locale-translate-filter-form .form-item-translation,
+[dir="rtl"] .locale-translate-filter-form .form-item-customized {
+  float: right;
+  margin-left: 1em;
+  margin-right: 0;
+}
+.locale-translate-filter-form .form-type-select select {
+  width: 100%;
+}
+.locale-translate-filter-form .form-actions {
+  float: left; /* LTR */
+  padding: 3.8ex 0 0 0; /* LTR */
+}
+[dir="rtl"] .locale-translate-filter-form .form-actions {
+  float: right;
+  padding: 3.5ex 0 0 0;
+}
+.locale-translate-edit-form th {
+  width: 50%;
+  table-layout: fixed;
+}
+.locale-translate-edit-form td {
+  vertical-align: top
+}
+
+.locale-translate-edit-form tr.changed {
+  background: #ffb;
+}
+
+.locale-translate-edit-form tr .form-type-item .ajax-changed {
+  position: absolute;
+}
+
+.locale-translate-filter-form .form-wrapper {
+  margin-bottom:0;
+}
+
+.locale-translate-edit-form table.changed {
+  margin-top: 0;
+}
+
+/**
+ * Available translation updates page.
+ */
+#locale-translation-status-form table {
+  table-layout: fixed;
+}
+#locale-translation-status-form th.select-all {
+  width: 4%;
+}
+#locale-translation-status-form th.title {
+  width: 25%;
+}
+#locale-translation-status-form th.description {
+}
+#locale-translation-status-form td {
+  vertical-align: top;
+}
+.locale-translation-update__wrapper {
+  background: transparent url(../../../../misc/menu-collapsed.png) left .6em no-repeat;
+  margin-left: -12px;
+  padding-left: 12px;
+}
+.expanded .locale-translation-update__wrapper {
+  background: transparent url(../../../../misc/menu-expanded.png) left .6em no-repeat;
+}
+#locale-translation-status-form .description {
+  cursor: pointer;
+}
+.locale-translation-update__wrapper {
+  color: #5c5c5b;
+  line-height: 20px;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+.expanded .locale-translation-update__wrapper {
+  height: auto;
+  overflow: visible;
+  white-space: normal;
+}
+.expanded .locale-translation-update__message {
+  -webkit-hyphens: auto;
+  -moz-hyphens: auto;
+  -ms-hyphens: auto;
+  hyphens: auto;
+}
+.js .locale-translation-update__wrapper {
+  height: 20px;
+}
+.expanded .locale-translation-update__wrapper {
+  height: auto;
+  overflow: visible;
+  white-space: normal;
+}
+.locale-translation-update__details {
+  padding: 5px 0;
+  max-width: 490px;
+  white-space: normal;
+  font-size: 0.9em;
+  color: #666;
+}
+.locale-translation-update__details ul {
+  margin: 0;
+  padding: 0;
+}
+.locale-translation-update__details li {
+  margin: 0 0 0.25em 1.5em;
+  padding: 0;
+}
+@media screen and (max-width: 40em) {
+  #locale-translation-status-form th.title {
+    width: 20%;
+  }
+  #locale-translation-status-form th.status {
+    width: 40%;
+  }
+}
diff --git a/core/themes/stable/css/menu_ui/menu_ui.admin.css b/core/themes/stable/css/menu_ui/menu_ui.admin.css
new file mode 100644
index 0000000..efbfe75
--- /dev/null
+++ b/core/themes/stable/css/menu_ui/menu_ui.admin.css
@@ -0,0 +1,6 @@
+.menu-enabled {
+  width: 70px;
+}
+.menu-label {
+  font-weight: bold;
+}
diff --git a/core/themes/stable/css/node/node.admin.css b/core/themes/stable/css/node/node.admin.css
new file mode 100644
index 0000000..101a38d
--- /dev/null
+++ b/core/themes/stable/css/node/node.admin.css
@@ -0,0 +1,11 @@
+/**
+ * @file
+ * Styles for administration pages.
+ */
+
+/**
+ * Revisions overview screen.
+ */
+.revision-current {
+  background: #ffc;
+}
diff --git a/core/themes/stable/css/node/node.module.css b/core/themes/stable/css/node/node.module.css
new file mode 100644
index 0000000..397adb5
--- /dev/null
+++ b/core/themes/stable/css/node/node.module.css
@@ -0,0 +1,76 @@
+/**
+ * @file
+ * Styles for administration pages.
+ */
+
+/**
+ * Node add/edit form layout
+ */
+
+/* Narrow screens */
+.layout-region {
+  box-sizing:         border-box;
+}
+
+/* Wide screens */
+@media
+  screen and (min-width: 780px),
+  (orientation: landscape) and (min-device-height: 780px) {
+
+  .layout-region-node-main,
+  .layout-region-node-footer {
+    float: left; /* LTR */
+    width: 65%;
+    padding-right: 2em; /* LTR */
+    box-sizing: border-box;
+  }
+
+  [dir="rtl"] .layout-region-node-main,
+  [dir="rtl"] .layout-region-node-footer {
+    float: right;
+    padding-left: 2em;
+    padding-right: 0;
+  }
+
+  .layout-region-node-secondary {
+    float: right; /* LTR */
+    width: 35%;
+  }
+
+  [dir="rtl"] .layout-region-node-secondary {
+    float: left;
+  }
+
+  /* @todo File an issue to add a standard class to all text-like inputs */
+  .layout-region-node-secondary .form-autocomplete,
+  .layout-region-node-secondary .form-text,
+  .layout-region-node-secondary .form-tel,
+  .layout-region-node-secondary .form-email,
+  .layout-region-node-secondary .form-url,
+  .layout-region-node-secondary .form-search,
+  .layout-region-node-secondary .form-number,
+  .layout-region-node-secondary .form-color,
+  .layout-region-node-secondary textarea {
+    box-sizing:         border-box;
+    width: 100%;
+    max-width: 100%;
+  }
+}
+
+/**
+ * The vertical toolbar mode gets triggered for narrow screens, which throws off
+ * the intent of media queries written for the viewport width. When the vertical
+ * toolbar is on, we need to suppress layout for the original media width + the
+ * toolbar width (240px). In this case, 240px + 780px.
+ */
+@media
+  screen and (max-width: 1020px) {
+
+  .toolbar-vertical.toolbar-tray-open .layout-region-node-main,
+  .toolbar-vertical.toolbar-tray-open .layout-region-node-footer,
+  .toolbar-vertical.toolbar-tray-open .layout-region-node-secondary {
+    float: none;
+    width: auto;
+    padding-right: 0;
+  }
+}
diff --git a/core/themes/stable/css/node/node.preview.css b/core/themes/stable/css/node/node.preview.css
new file mode 100644
index 0000000..4847d3f
--- /dev/null
+++ b/core/themes/stable/css/node/node.preview.css
@@ -0,0 +1,22 @@
+/**
+ * @file
+ * Styles for node preview page.
+ */
+
+.node-preview-container {
+  position: fixed;
+  z-index: 499;
+  width: 100%;
+  padding: 10px;
+  box-sizing: border-box;
+}
+
+@media only screen and (min-width: 36em) {
+  .node-preview-container .form-type-select {
+    margin-left: 25%; /* LTR */
+  }
+  [dir="rtl"] .node-preview-container .form-type-select {
+    margin-right: 25%;
+    margin-left: 0;
+  }
+}
diff --git a/core/themes/stable/css/quickedit/quickedit.icons.theme.css b/core/themes/stable/css/quickedit/quickedit.icons.theme.css
new file mode 100644
index 0000000..0af01ba
--- /dev/null
+++ b/core/themes/stable/css/quickedit/quickedit.icons.theme.css
@@ -0,0 +1,74 @@
+/**
+ * @file
+ * Icons for Quick Edit module.
+ */
+
+.quickedit .icon {
+  min-height: 1em;
+  min-width: 2.5em;
+  position: relative;
+}
+.quickedit .icon.icon-only {
+  text-indent: -9999px;
+}
+.quickedit .icon.icon-end {
+  padding-right: 2.5em; /* LTR */
+}
+[dir="rtl"] .quickedit .icon.icon-end {
+  padding-left: 2.5em;
+  padding-right: 0;
+}
+.quickedit .icon:before {
+  background-attachment: scroll;
+  background-color: transparent;
+  background-position: center center;
+  background-repeat: no-repeat;
+  content: '';
+  display: block;
+  height: 100%;
+  left: 0; /* LTR */
+  position: absolute;
+  top: 0;
+  width: 100%;
+}
+[dir="rtl"] .quickedit .icon:before {
+  left: auto;
+  right: 0;
+}
+.quickedit .icon-end:before {
+  left: auto; /* LTR */
+  right: 0.5em; /* LTR */
+  width: 18px;
+}
+[dir="rtl"] .quickedit .icon-end:before {
+  left: 0.5em;
+  right: auto;
+}
+.quickedit button.icon {
+  font-size: 1em;
+}
+.quickedit .icon-pencil {
+  margin-left: .5em;
+  padding-left: 1.5em;
+}
+
+/**
+ * Images.
+ */
+.quickedit .icon-close:before {
+  background-image: url(../../../../misc/icons/787878/ex.svg);
+  height: 12px;
+  top: 10px;
+}
+.quickedit .icon-close:hover:before,
+.quickedit .icon-close:active:before {
+  background-image: url(../../../../misc/icons/000000/ex.svg);
+}
+.quickedit .icon-throbber:before {
+  background-image: url(../../../../modules/quickedit/images/icon-throbber.gif);
+}
+.quickedit .icon-pencil:before {
+  background-image: url(../../../../misc/icons/5181c6/pencil.svg);
+  background-position: left center;
+  background-size: 1.3em;
+}
diff --git a/core/themes/stable/css/quickedit/quickedit.module.css b/core/themes/stable/css/quickedit/quickedit.module.css
new file mode 100644
index 0000000..8ac9a55
--- /dev/null
+++ b/core/themes/stable/css/quickedit/quickedit.module.css
@@ -0,0 +1,123 @@
+/**
+ * @file
+ * Generic base styles for Quick Edit module.
+ *
+ * Note: every class is prefixed with "quickedit-" to prevent collisions with
+ * modules or themes. In Edit module-specific DOM subtrees, this is not
+ * necessary.
+ */
+
+/**
+ * Editable.
+ */
+.quickedit-editable {
+  z-index: 98;
+  position: relative;
+  cursor: pointer;
+}
+.quickedit-editable:focus {
+  outline: none;
+}
+
+/**
+ * Highlighted (hovered) editable.
+ */
+.quickedit-editable.quickedit-highlighted {
+  z-index: 99;
+}
+.quickedit-validation-errors > .messages {
+  margin-left: 0;
+  margin-right: 0;
+}
+.quickedit-validation-errors > .messages > ul {
+  list-style: none;
+  margin: 0;
+  padding: 0;
+}
+
+/**
+ * In-place editors that don't use a popup.
+ */
+.quickedit-validation-errors {
+  z-index: 300;
+  position: relative;
+}
+.quickedit-validation-errors .messages.error {
+  position: absolute;
+  top: 6px;
+  left: -5px; /* LTR */
+  margin: 0;
+  border: none;
+}
+[dir="rtl"] .quickedit-validation-errors .messages.error {
+  left: auto;
+  right: -5px;
+}
+
+/**
+ * Styling specific to the 'form' in-place editor.
+ */
+#quickedit_backstage {
+  display: none;
+}
+.quickedit-form {
+  position: absolute;
+  z-index: 300;
+  max-width: 35em;
+}
+.quickedit-form .placeholder {
+  min-height: 22px;
+}
+
+/**
+ * Default form styling overrides.
+ */
+.quickedit-form .form-wrapper .form-wrapper {
+  margin: inherit;
+}
+.quickedit-form .form-actions {
+  display: none;
+}
+.quickedit-form input {
+  max-width: 100%;
+}
+
+/**
+ * Entity toolbar.
+ */
+.quickedit-toolbar-container {
+  max-width: 100%;
+  position: absolute;
+  max-width: 320px;
+  width: 320px;
+  z-index: 100;
+}
+.quickedit-toolbar-container > .quickedit-toolbar-pointer,
+.quickedit-toolbar-container > .quickedit-toolbar-lining {
+  display: none;
+}
+.quickedit-form-container {
+  position: relative;
+  padding: 0;
+  border: 0;
+  margin: 0;
+  vertical-align: baseline;
+  z-index: 100;
+}
+.quickedit-toolgroup.ops {
+  float: right; /* LTR */
+}
+[dir="rtl"] .quickedit-toolgroup.ops {
+  float: left;
+}
+.quickedit-toolbar-label {
+  overflow: hidden;
+}
+#quickedit-toolbar-fence {
+  bottom: 0;
+  left: 0;
+  right: 0;
+  top: 0;
+  position: fixed;
+  z-index: -1;
+}
diff --git a/core/themes/stable/css/quickedit/quickedit.theme.css b/core/themes/stable/css/quickedit/quickedit.theme.css
new file mode 100644
index 0000000..399db7a
--- /dev/null
+++ b/core/themes/stable/css/quickedit/quickedit.theme.css
@@ -0,0 +1,254 @@
+/**
+ * @file
+ * Styling for Quick Edit module.
+ */
+
+/**
+ * Editable.
+ */
+.quickedit-field.quickedit-editable,
+.quickedit-field .quickedit-editable {
+  box-shadow: 0 0 0 2px #74b7ff;
+}
+
+/**
+ * Highlighted (hovered) editable.
+ */
+.quickedit-field.quickedit-highlighted,
+.quickedit-form.quickedit-highlighted,
+.quickedit-field .quickedit-highlighted {
+  box-shadow: 0 0 0 1px #74b7ff, 0 0 0 2px #007fff;
+}
+.quickedit-field.quickedit-changed,
+.quickedit-form.quickedit-changed,
+.quickedit-field .quickedit-changed {
+  box-shadow: 0 0 0 1px #fec17e, 0 0 0 2px #f7870a;
+}
+.quickedit-editing.quickedit-validation-error,
+.quickedit-form.quickedit-validation-error {
+  box-shadow: 0 0 0px 1px #ee8b74, 0 0 0 2px #fa2209;
+}
+.quickedit-editing.quickedit-editor-is-popup {
+  box-shadow: none;
+}
+.quickedit-form .form-item .error {
+  border: 1px solid #eea0a0;
+}
+
+/**
+ * Default form styling overrides.
+ */
+.quickedit-form form {
+  padding: 0.5em;
+}
+.quickedit-form .form-item {
+  margin: 0;
+}
+.quickedit-form .form-wrapper {
+  margin: .5em;
+}
+
+/**
+ * Animations.
+ */
+.quickedit-animate-invisible {
+  opacity: 0;
+}
+.quickedit-animate-default {
+  -webkit-transition: all .4s ease;
+  transition: all .4s ease;
+}
+.quickedit-animate-slow {
+  -webkit-transition: all .6s ease;
+  transition: all .6s ease;
+}
+.quickedit-animate-delay-veryfast {
+  -webkit-transition-delay: .05s;
+  transition-delay: .05s;
+}
+.quickedit-animate-delay-fast {
+  -webkit-transition-delay: .2s;
+  transition-delay: .2s;
+}
+.quickedit-animate-disable-width {
+  -webkit-transition: width 0s;
+  transition: width 0s;
+}
+.quickedit-animate-only-visibility {
+  -webkit-transition: opacity .2s ease;
+  transition: opacity .2s ease;
+}
+
+/**
+ * In-place editors that don't use a popup.
+ */
+.quickedit-validation-errors .messages.error {
+  box-shadow: 0 0 1px 1px red, 0 0 3px 3px rgba(153, 153, 153, .5);
+  background-color: white;
+}
+
+/**
+ * Styling specific to the 'form' in-place editor.
+ */
+.quickedit-form {
+  box-shadow: 0 0 30px 4px #4f4f4f;
+  background-color: white;
+}
+
+/**
+ * Toolbars.
+ */
+.quickedit-toolbar-container {
+  font-family: 'Source Sans Pro','Lucida Grande', sans-serif;
+  padding-bottom: 7px;
+  padding-top: 7px;
+  -webkit-transition: all 1s;
+  transition: all 1s;
+}
+.quickedit-toolbar-container > .quickedit-toolbar-content {
+  background-image: -webkit-linear-gradient(top, #fff, #e4e4e4);
+  background-image:   linear-gradient(to bottom, #fff, #e4e4e4);
+  box-sizing: border-box;
+  color: black;
+  padding: 0.1667em;
+  position: relative;
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  user-select: none;
+  z-index: 2;
+}
+.quickedit-toolbar-container > .quickedit-toolbar-pointer {
+  background-color: #e4e4e4;
+  bottom: 2px;
+  box-shadow: 0 0 0 1px #818181, 0px 0px 0 4px rgba(150, 150, 150, 0.5);
+  display: block;
+  height: 16px;
+  left: 18px; /* LTR */
+  position: absolute;
+  -webkit-transform: rotate(45deg);
+  -ms-transform: rotate(45deg);
+  transform: rotate(45deg);
+  width: 16px;
+  z-index: 1;
+}
+[dir="rtl"] .quickedit-toolbar-container > .quickedit-toolbar-pointer {
+  left: auto;
+  right: 18px;
+}
+.quickedit-toolbar-container.quickedit-toolbar-pointer-top > .quickedit-toolbar-pointer {
+  bottom: auto;
+  top: 2px;
+}
+.quickedit-toolbar-container > .quickedit-toolbar-lining {
+  bottom: 7px;
+  box-shadow: 0 0 0 1px #818181, 0px 3px 0px 1px rgba(150, 150, 150, 0.5);
+  display: block;
+  left: 0;
+  position: absolute;
+  right: 0;
+  top: 7px;
+  z-index: 0;
+}
+
+.quickedit-toolbar-label {
+  font-style: italic;
+  overflow: hidden;
+  padding: 0.333em 0.4em;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+.quickedit-toolbar-label .field:after {
+  content: ' → '; /* LTR */
+}
+
+[dir="rtl"] .quickedit-toolbar-label .field:after {
+  content: ' ← ';
+}
+
+/* The toolbar; these are not necessarily visible. */
+.quickedit-toolbar {
+  font-family: 'Droid sans', 'Lucida Grande', sans-serif;
+}
+.quickedit-toolbar-entity {
+  padding: 0.1667em 0.2em;
+}
+
+ /**
+  * Info toolgroup.
+  */
+.quickedit-toolbar-fullwidth {
+  width: 100%;
+}
+.quickedit-toolgroup.wysiwyg-floated {
+  float: right; /* LTR */
+}
+[dir="rtl"] .quickedit-toolgroup.wysiwyg-floated {
+  float: left;
+}
+.quickedit-toolgroup.wysiwyg-main {
+  clear: both;
+  width: 100%;
+  padding-left: 0; /* LTR */
+}
+[dir="rtl"] .quickedit-toolgroup.wysiwyg-main {
+  padding-left: 0;
+  padding-right: 0;
+}
+
+/**
+ * Buttons.
+ */
+.quickedit-button {
+  background-color: #e4e4e4;
+  border: 1px solid #d2d2d2;
+  color: #5a5a5a;
+  cursor: pointer;
+  display: inline-block;
+  margin: 0;
+  opacity: 1;
+  padding: 0.345em;
+  -webkit-transition: opacity .1s ease;
+  transition: opacity .1s ease;
+}
+.quickedit-button[aria-hidden="true"] {
+  visibility: hidden;
+  opacity: 0;
+}
+.quickedit-button + .quickedit-button {
+  margin-left: 0.2em; /* LTR */
+}
+[dir="rtl"] .quickedit-button + .quickedit-button {
+  margin-left: auto;
+  margin-right: 0.25em;
+}
+/* Button with icons. */
+.quickedit-button:hover,
+.quickedit-button:active {
+  background-color: #c8c8c8;
+  border: 1px solid #a0a0a0;
+  color: #2e2e2e;
+}
+.quickedit-toolbar-container .quickedit-button.action-cancel {
+  background-color: transparent;
+  border: 1px solid transparent;
+}
+.quickedit-button.action-save {
+  color: white;
+  background-color: #50a0e9;
+  background-image: -webkit-linear-gradient(top, #50a0e9, #4481dc);
+  background-image:   linear-gradient(to bottom, #50a0e9, #4481dc);
+  border: 1px solid transparent;
+}
+.quickedit-button.action-save:hover,
+.quickedit-button.action-save:active {
+  border: 1px solid #a0a0a0;
+}
+.quickedit-button.action-saving,
+.quickedit-button.action-saving:hover,
+.quickedit-button.action-saving:active {
+  background-color: #e4e4e4;
+  background-image: none;
+  border-color: #d2d2d2;
+  color: #5a5a5a;
+}
diff --git a/core/themes/stable/css/shortcut/shortcut.icons.theme.css b/core/themes/stable/css/shortcut/shortcut.icons.theme.css
new file mode 100644
index 0000000..975f6bf
--- /dev/null
+++ b/core/themes/stable/css/shortcut/shortcut.icons.theme.css
@@ -0,0 +1,40 @@
+/**
+ * @file
+ * Styling for the shortcut module icons.
+ */
+
+/**
+ * Toolbar tab icon.
+ */
+.toolbar-bar .toolbar-icon-shortcut:before {
+  background-image: url(../../../../misc/icons/bebebe/star.svg);
+}
+.toolbar-bar .toolbar-icon-shortcut:active:before,
+.toolbar-bar .toolbar-icon-shortcut.is-active:before {
+  background-image: url(../../../../misc/icons/ffffff/star.svg);
+}
+
+/**
+ * Add/remove links.
+ */
+.shortcut-action__icon {
+  background: transparent url(../../../../modules/shortcut/images/favstar.svg) no-repeat left top;
+  width: 20px;
+  height: 20px;
+  display: inline-block;
+  vertical-align: -2px;
+}
+[dir="rtl"] .shortcut-action__icon {
+  background-image: url(../../../../modules/shortcut/images/favstar-rtl.svg);
+}
+.shortcut-action--add:hover .shortcut-action__icon,
+.shortcut-action--add:focus .shortcut-action__icon {
+  background-position: -20px top;
+}
+.shortcut-action--remove .shortcut-action__icon {
+  background-position: -40px top;
+}
+.shortcut-action--remove:focus .shortcut-action__icon,
+.shortcut-action--remove:hover .shortcut-action__icon {
+  background-position: -60px top;
+}
diff --git a/core/themes/stable/css/shortcut/shortcut.theme.css b/core/themes/stable/css/shortcut/shortcut.theme.css
new file mode 100644
index 0000000..73d58c6
--- /dev/null
+++ b/core/themes/stable/css/shortcut/shortcut.theme.css
@@ -0,0 +1,62 @@
+/**
+ * @file
+ * Styling for the shortcut module.
+ */
+
+/**
+ * Toolbar.
+ */
+.toolbar .toolbar-tray-vertical .edit-shortcuts {
+  text-align: right; /* LTR */
+  padding: 1em;
+}
+[dir="rtl"] .toolbar .toolbar-tray-vertical .edit-shortcuts {
+  text-align: left;
+}
+.toolbar .toolbar-tray-horizontal .edit-shortcuts {
+  float: right; /* LTR */
+}
+[dir="rtl"] .toolbar .toolbar-tray-horizontal .edit-shortcuts {
+  float: left;
+}
+
+/**
+ * Add/remove links.
+ */
+.shortcut-action {
+  display: inline-block;
+  margin-left: 0.3em; /* LTR */
+}
+[dir="rtl"] .shortcut-action {
+  margin-left: 0;
+  margin-right: 0.3em;
+}
+.shortcut-action__message {
+  background: #000000;
+  background: rgba(0, 0, 0, 0.5);
+  border-radius: 5px;
+  padding: 0 5px;
+  color: #ffffff;
+  display: inline-block;
+  margin-left: 0.3em; /* LTR */
+  opacity: 0;
+  -ms-transform: translateY(-12px);
+  -webkit-transform: translateY(-12px);
+  transform: translateY(-12px);
+  -webkit-transition: all 200ms ease-out;
+  transition: all 200ms ease-out;
+  -ms-backface-visibility: hidden;
+  -webkit-backface-visibility: hidden;
+  backface-visibility: hidden;
+}
+[dir="rtl"] .shortcut-action__message {
+  margin-left: 0;
+  margin-right: 0.3em;
+}
+.shortcut-action:hover .shortcut-action__message,
+.shortcut-action:focus .shortcut-action__message {
+  opacity: 1;
+  -ms-transform: translateY(-2px);
+  -webkit-transform: translateY(-2px);
+  transform: translateY(-2px);
+}
diff --git a/core/themes/stable/css/simpletest/simpletest.module.css b/core/themes/stable/css/simpletest/simpletest.module.css
new file mode 100644
index 0000000..71f7eea
--- /dev/null
+++ b/core/themes/stable/css/simpletest/simpletest.module.css
@@ -0,0 +1,89 @@
+
+/* Test Table */
+#simpletest-form-table th.select-all {
+  width: 1em;
+}
+th.simpletest-test-label {
+  width: 40%;
+}
+
+.simpletest-image {
+  display: inline-block;
+  cursor: pointer;
+  width: 1em;
+}
+.simpletest-group-label label {
+  display: inline;
+  font-weight: bold;
+}
+.simpletest-test-label label {
+  margin-left: 1em; /* LTR */
+}
+.simpletest-test-description .description {
+  margin: 0;
+}
+#simpletest-form-table tr td {
+  background-color: white;
+  color: #494949;
+}
+#simpletest-form-table tr.simpletest-group td {
+  background-color: #edf5fa;
+  color: #494949;
+}
+
+table#simpletest-form-table tr.simpletest-group label {
+  display: inline;
+}
+
+div.message > div.item-list {
+  font-weight: normal;
+}
+
+div.simpletest-pass {
+  color: #33a333;
+}
+.simpletest-fail {
+  color: #981010;
+}
+
+tr.simpletest-pass.odd {
+  background-color: #b6ffb6;
+}
+tr.simpletest-pass.even {
+  background-color: #9bff9b;
+}
+tr.simpletest-fail.odd {
+  background-color: #ffc9c9;
+}
+tr.simpletest-fail.even {
+  background-color: #ffacac;
+}
+tr.simpletest-exception.odd {
+  background-color: #f4ea71;
+}
+tr.simpletest-exception.even {
+  background-color: #f5e742;
+}
+tr.simpletest-debug.odd {
+  background-color: #eee;
+}
+tr.simpletest-debug.even {
+  background-color: #fff;
+}
+
+a.simpletest-collapse {
+  height: 0;
+  width: 0;
+  top: -99em;
+  position: absolute;
+}
+a.simpletest-collapse:focus,
+a.simpletest-collapse:hover {
+  font-size: 80%;
+  top: 0px;
+  height: auto;
+  width: auto;
+  overflow: visible;
+  position: relative;
+  z-index: 1000;
+}
diff --git a/core/themes/stable/css/system/components/ajax-progress.module.css b/core/themes/stable/css/system/components/ajax-progress.module.css
new file mode 100644
index 0000000..507889e
--- /dev/null
+++ b/core/themes/stable/css/system/components/ajax-progress.module.css
@@ -0,0 +1,49 @@
+/**
+ * @file
+ * Throbber.
+ */
+
+.ajax-progress {
+  display: inline-block;
+  padding: 1px 5px 2px 5px;
+}
+[dir="rtl"] .ajax-progress {
+  float: right;
+}
+.ajax-progress-throbber .throbber {
+  background: transparent url(../../../../../misc/throbber-active.gif) no-repeat 0px center;
+  display: inline;
+  padding: 1px 5px 2px;
+}
+.ajax-progress-throbber .message {
+  display: inline;
+  padding: 1px 5px 2px;
+}
+tr .ajax-progress-throbber .throbber {
+  margin: 0 2px;
+}
+.ajax-progress-bar {
+  width: 16em;
+}
+
+/* Full screen throbber */
+.ajax-progress-fullscreen {
+  /* Can't do center:50% middle: 50%, so approximate it for a typical window size. */
+  left: 49%; /* LTR */
+  position: fixed;
+  top: 48.5%;
+  z-index: 1000;
+  background-color: #232323;
+  background-image: url(../../../../../misc/loading-small.gif);
+  background-position: center center;
+  background-repeat: no-repeat;
+  border-radius: 7px;
+  height: 24px;
+  opacity: 0.9;
+  padding: 4px;
+  width: 24px;
+}
+[dir="rtl"] .ajax-progress-fullscreen {
+  left: auto;
+  right: 49%;
+}
diff --git a/core/themes/stable/css/system/components/align.module.css b/core/themes/stable/css/system/components/align.module.css
new file mode 100644
index 0000000..a8fdf53
--- /dev/null
+++ b/core/themes/stable/css/system/components/align.module.css
@@ -0,0 +1,32 @@
+/**
+ * @file
+ * Alignment classes for text and block level elements.
+ */
+
+.text-align-left {
+  text-align: left;
+}
+.text-align-right {
+  text-align: right;
+}
+.text-align-center {
+  text-align: center;
+}
+.text-align-justify {
+  text-align: justify;
+}
+
+/**
+ * Alignment classes for block level elements (images, videos, blockquotes, etc.)
+ */
+.align-left {
+  float: left;
+}
+.align-right {
+  float: right;
+}
+.align-center {
+  display: block;
+  margin-left: auto;
+  margin-right: auto;
+}
diff --git a/core/themes/stable/css/system/components/autocomplete-loading.module.css b/core/themes/stable/css/system/components/autocomplete-loading.module.css
new file mode 100644
index 0000000..2645cbf
--- /dev/null
+++ b/core/themes/stable/css/system/components/autocomplete-loading.module.css
@@ -0,0 +1,22 @@
+/**
+ * @file
+ * Visual styles for animated throbber.
+ *
+ * @see autocomplete.js
+ */
+
+.js input.form-autocomplete {
+  background-image: url(../../../../../misc/throbber-inactive.png);
+  background-position: 100% center; /* LTR */
+  background-repeat: no-repeat;
+}
+.js[dir="rtl"] input.form-autocomplete {
+  background-position: 0% center;
+}
+.js input.form-autocomplete.ui-autocomplete-loading {
+  background-image: url(../../../../../misc/throbber-active.gif);
+  background-position: 100% center; /* LTR */
+}
+.js[dir="rtl"] input.form-autocomplete.ui-autocomplete-loading {
+  background-position: 0% center;
+}
diff --git a/core/themes/stable/css/system/components/clearfix.module.css b/core/themes/stable/css/system/components/clearfix.module.css
new file mode 100644
index 0000000..d68066e
--- /dev/null
+++ b/core/themes/stable/css/system/components/clearfix.module.css
@@ -0,0 +1,15 @@
+/**
+ * @file
+ * Float clearing.
+ *
+ * Based on the micro clearfix hack by Nicolas Gallagher, with the :before
+ * pseudo selector removed to allow normal top margin collapse.
+ *
+ * @see http://nicolasgallagher.com/micro-clearfix-hack
+ */
+
+.clearfix:after {
+  content: "";
+  display: table;
+  clear: both;
+}
diff --git a/core/themes/stable/css/system/components/container-inline.module.css b/core/themes/stable/css/system/components/container-inline.module.css
new file mode 100644
index 0000000..b68da65
--- /dev/null
+++ b/core/themes/stable/css/system/components/container-inline.module.css
@@ -0,0 +1,13 @@
+/**
+ * @file
+ * Inline items.
+ */
+
+.container-inline div,
+.container-inline label {
+  display: inline;
+}
+/* Details contents always need to be rendered as block. */
+.container-inline .details-wrapper {
+  display: block;
+}
diff --git a/core/themes/stable/css/system/components/details.module.css b/core/themes/stable/css/system/components/details.module.css
new file mode 100644
index 0000000..23ee3b4
--- /dev/null
+++ b/core/themes/stable/css/system/components/details.module.css
@@ -0,0 +1,10 @@
+/**
+ * @file
+ * Collapsible details.
+ *
+ * @see collapse.js
+ */
+
+.js details:not([open]) .details-wrapper {
+  display: none;
+}
diff --git a/core/themes/stable/css/system/components/fieldgroup.module.css b/core/themes/stable/css/system/components/fieldgroup.module.css
new file mode 100644
index 0000000..e779909
--- /dev/null
+++ b/core/themes/stable/css/system/components/fieldgroup.module.css
@@ -0,0 +1,9 @@
+/**
+ * @file
+ * Fieldgroup border reset.
+ */
+
+.fieldgroup {
+  border-width: 0;
+  padding: 0;
+}
diff --git a/core/themes/stable/css/system/components/hidden.module.css b/core/themes/stable/css/system/components/hidden.module.css
new file mode 100644
index 0000000..6501c27
--- /dev/null
+++ b/core/themes/stable/css/system/components/hidden.module.css
@@ -0,0 +1,53 @@
+/**
+ * @file
+ * Utility classes to hide elements in different ways.
+ */
+
+/**
+ * Hide elements from all users.
+ *
+ * Used for elements which should not be immediately displayed to any user. An
+ * example would be collapsible details that will be expanded with a click
+ * from a user. The effect of this class can be toggled with the jQuery show()
+ * and hide() functions.
+ */
+.hidden {
+  display: none;
+}
+
+/**
+ * Hide elements visually, but keep them available for screen readers.
+ *
+ * Used for information required for screen reader users to understand and use
+ * the site where visual display is undesirable. Information provided in this
+ * manner should be kept concise, to avoid unnecessary burden on the user.
+ * "!important" is used to prevent unintentional overrides.
+ */
+.visually-hidden {
+  position: absolute !important;
+  clip: rect(1px, 1px, 1px, 1px);
+  overflow: hidden;
+  height: 1px;
+  width: 1px;
+  word-wrap: normal;
+}
+
+/**
+ * The .focusable class extends the .visually-hidden class to allow
+ * the element to be focusable when navigated to via the keyboard.
+ */
+.visually-hidden.focusable:active,
+.visually-hidden.focusable:focus {
+  position: static !important;
+  clip: auto;
+  overflow: visible;
+  height: auto;
+  width: auto;
+}
+
+/**
+ * Hide visually and from screen readers, but maintain layout.
+ */
+.invisible {
+  visibility: hidden;
+}
diff --git a/core/themes/stable/css/system/components/js.module.css b/core/themes/stable/css/system/components/js.module.css
new file mode 100644
index 0000000..f827020
--- /dev/null
+++ b/core/themes/stable/css/system/components/js.module.css
@@ -0,0 +1,22 @@
+/**
+ * @file
+ * Utility classes to assist with Javascript functionality.
+ */
+
+/**
+ * For anything you want to hide on page load when JS is enabled, so
+ * that you can use the JS to control visibility and avoid flicker.
+ */
+.js .js-hide {
+  display: none;
+}
+
+/**
+ * For anything you want to show on page load only when JS is enabled.
+ */
+.js-show {
+  display: none;
+}
+.js .js-show {
+  display: block;
+}
diff --git a/core/themes/stable/css/system/components/nowrap.module.css b/core/themes/stable/css/system/components/nowrap.module.css
new file mode 100644
index 0000000..466d9fe
--- /dev/null
+++ b/core/themes/stable/css/system/components/nowrap.module.css
@@ -0,0 +1,8 @@
+/**
+ * @file
+ * Utility class to prevent text wrapping.
+ */
+
+.nowrap {
+  white-space: nowrap;
+}
diff --git a/core/themes/stable/css/system/components/position-container.module.css b/core/themes/stable/css/system/components/position-container.module.css
new file mode 100644
index 0000000..ae209f3
--- /dev/null
+++ b/core/themes/stable/css/system/components/position-container.module.css
@@ -0,0 +1,8 @@
+/*
+ * @file
+ * Contain positioned elements.
+ */
+
+.position-container {
+  position: relative;
+}
diff --git a/core/themes/stable/css/system/components/progress.module.css b/core/themes/stable/css/system/components/progress.module.css
new file mode 100644
index 0000000..80042af
--- /dev/null
+++ b/core/themes/stable/css/system/components/progress.module.css
@@ -0,0 +1,51 @@
+/**
+ * @file
+ * Progress behavior.
+ *
+ * @see progress.js
+ */
+
+.progress {
+  position: relative;
+}
+.progress__track {
+  background-color: #fff;
+  border: 1px solid;
+  margin-top: 5px;
+  max-width: 100%;
+  min-width: 100px;
+  height: 16px;
+}
+.progress__bar {
+  background-color: #000;
+  height: 1.5em;
+  width: 3%;
+  min-width: 3%;
+  max-width: 100%;
+}
+.progress__description,
+.progress__percentage {
+  color: #555;
+  overflow: hidden;
+  font-size: .875em;
+  margin-top: 0.2em;
+}
+.progress__description {
+  float: left; /* LTR */
+}
+[dir="rtl"] .progress__description {
+  float: right;
+}
+.progress__percentage {
+  float: right; /* LTR */
+}
+[dir="rtl"] .progress__percentage {
+  float: left;
+}
+.progress--small .progress__track {
+  height: 7px;
+}
+.progress--small .progress__bar {
+  height: 7px;
+  background-size: 20px 20px;
+}
diff --git a/core/themes/stable/css/system/components/reset-appearance.module.css b/core/themes/stable/css/system/components/reset-appearance.module.css
new file mode 100644
index 0000000..847f269
--- /dev/null
+++ b/core/themes/stable/css/system/components/reset-appearance.module.css
@@ -0,0 +1,15 @@
+/*
+ * @file
+ * Utility class to remove browser styles, especially for button.
+ */
+
+.reset-appearance {
+  -webkit-appearance: none;
+  -moz-appearance: none;
+  appearance: none;
+  border: 0 none;
+  background: transparent;
+  padding: 0;
+  margin: 0;
+  line-height: inherit;
+}
diff --git a/core/themes/stable/css/system/components/resize.module.css b/core/themes/stable/css/system/components/resize.module.css
new file mode 100644
index 0000000..d8d4559
--- /dev/null
+++ b/core/themes/stable/css/system/components/resize.module.css
@@ -0,0 +1,21 @@
+/**
+ * @file
+ * Resizable textareas.
+ */
+
+.resize-none {
+  resize: none;
+}
+.resize-vertical {
+  resize: vertical;
+  min-height: 2em;
+}
+.resize-horizontal {
+  resize: horizontal;
+  max-width: 100%;
+}
+.resize-both {
+  resize: both;
+  max-width: 100%;
+  min-height: 2em;
+}
diff --git a/core/themes/stable/css/system/components/sticky-header.module.css b/core/themes/stable/css/system/components/sticky-header.module.css
new file mode 100644
index 0000000..f3bdcea
--- /dev/null
+++ b/core/themes/stable/css/system/components/sticky-header.module.css
@@ -0,0 +1,13 @@
+/**
+ * @file
+ * Table header behavior.
+ *
+ * @see tableheader.js
+ */
+
+table.sticky-header {
+  background-color: #fff;
+  margin-top: 0;
+  z-index: 500;
+  top: 0;
+}
diff --git a/core/themes/stable/css/system/components/tabledrag.module.css b/core/themes/stable/css/system/components/tabledrag.module.css
new file mode 100644
index 0000000..9a6445c
--- /dev/null
+++ b/core/themes/stable/css/system/components/tabledrag.module.css
@@ -0,0 +1,88 @@
+/**
+ * @file
+ * Table drag behavior.
+ *
+ * @see tabledrag.js
+ */
+
+body.drag {
+  cursor: move;
+}
+tr.region-title {
+  font-weight: bold;
+}
+tr.region-message {
+  color: #999;
+}
+tr.region-populated {
+  display: none;
+}
+tr.add-new .tabledrag-changed {
+  display: none;
+}
+.draggable a.tabledrag-handle {
+  cursor: move;
+  float: left; /* LTR */
+  height: 1.7em;
+  margin-left: -1em; /* LTR */
+  overflow: hidden;
+  text-decoration: none;
+}
+[dir="rtl"] .draggable a.tabledrag-handle {
+  float: right;
+  margin-right: -1em;
+  margin-left: 0;
+}
+a.tabledrag-handle:hover {
+  text-decoration: none;
+}
+a.tabledrag-handle .handle {
+  background: url(../../../../../misc/icons/787878/move.svg) no-repeat 6px 7px;
+  height: 14px;
+  margin: -0.4em 0.5em 0;
+  padding: 0.42em 0.5em;
+  width: 14px;
+}
+a.tabledrag-handle:hover .handle,
+a.tabledrag-handle:focus .handle {
+  background-image: url(../../../../../misc/icons/000000/move.svg);
+}
+.touch .draggable td {
+  padding: 0 10px;
+}
+.touch .draggable .menu-item__link {
+  display: inline-block;
+  padding: 10px 0;
+}
+.touch a.tabledrag-handle {
+  height: 44px;
+  width: 40px;
+}
+.touch a.tabledrag-handle .handle {
+  background-position: 40% 19px; /* LTR */
+  height: 21px;
+}
+[dir="rtl"] .touch a.tabledrag-handle .handle {
+  background-position: right 40% top 19px;
+}
+.touch .draggable.drag a.tabledrag-handle .handle {
+  background-position: 50% -32px;
+}
+.tabledrag-toggle-weight-wrapper {
+  text-align: right; /* LTR */
+}
+[dir="rtl"] .tabledrag-toggle-weight-wrapper {
+  text-align: left;
+}
+.indentation {
+  float: left; /* LTR */
+  height: 1.7em;
+  margin: -0.4em 0.2em -0.4em -0.4em; /* LTR */
+  padding: 0.42em 0 0.42em 0.6em; /* LTR */
+  width: 20px;
+}
+[dir="rtl"] .indentation {
+  float: right;
+  margin: -0.4em -0.4em -0.4em 0.2em;
+  padding: 0.42em 0.6em 0.42em 0;
+}
diff --git a/core/themes/stable/css/system/components/tablesort.module.css b/core/themes/stable/css/system/components/tablesort.module.css
new file mode 100644
index 0000000..d4b6b48
--- /dev/null
+++ b/core/themes/stable/css/system/components/tablesort.module.css
@@ -0,0 +1,19 @@
+/**
+ * @file
+ * Table sort indicator.
+ *
+ * @see tablesort-indicator.html.twig
+ */
+
+.tablesort {
+  width: 16px;
+  height: 16px;
+  display: inline-block;
+  background-size: 100%;
+}
+.tablesort--asc {
+  background-image: url(../../../../../misc/icons/787878/twistie-down.svg);
+}
+.tablesort--desc {
+  background-image: url(../../../../../misc/icons/787878/twistie-up.svg);
+}
diff --git a/core/themes/stable/css/system/components/tree-child.module.css b/core/themes/stable/css/system/components/tree-child.module.css
new file mode 100644
index 0000000..8af02b9
--- /dev/null
+++ b/core/themes/stable/css/system/components/tree-child.module.css
@@ -0,0 +1,18 @@
+/**
+ * @file
+ * Visual styles for a nested tree child.
+ */
+
+div.tree-child {
+  background: url(../../../../../misc/tree.png) no-repeat 11px center; /* LTR */
+}
+div.tree-child-last {
+  background: url(../../../../../misc/tree-bottom.png) no-repeat 11px center; /* LTR */
+}
+[dir="rtl"] div.tree-child,
+[dir="rtl"] div.tree-child-last {
+  background-position: -65px center;
+}
+div.tree-child-horizontal {
+  background: url(../../../../../misc/tree.png) no-repeat -11px center;
+}
diff --git a/core/themes/stable/css/system/system.admin.css b/core/themes/stable/css/system/system.admin.css
new file mode 100644
index 0000000..93625ba
--- /dev/null
+++ b/core/themes/stable/css/system/system.admin.css
@@ -0,0 +1,380 @@
+/**
+ * @file
+ * Styles for administration pages.
+ */
+
+/**
+ * Reusable layout styles.
+ */
+.layout-container {
+  margin: 0 1.5em;
+}
+.layout-container:after {
+  content: "";
+  display: table;
+  clear: both;
+}
+
+@media screen and (min-width: 38em) {
+  .layout-container {
+    margin: 0 2.5em;
+  }
+  .layout-column {
+    float: left;  /* LTR */
+    box-sizing: border-box;
+  }
+  [dir="rtl"] .layout-column {
+    float: right;
+  }
+  .layout-column + .layout-column {
+    padding-left: 10px; /* LTR */
+  }
+  [dir="rtl"] .layout-column + .layout-column {
+    padding-right: 10px;
+    padding-left: 0;
+  }
+  .layout-column--half {
+    width: 50%;
+  }
+  .layout-column--quarter {
+    width: 25%;
+  }
+  .layout-column--three-quarter {
+    width: 75%;
+  }
+}
+
+/**
+ * Panel.
+ * Used to visually group items together.
+ */
+.panel {
+  padding: 5px 5px 15px;
+}
+.panel__description {
+  margin: 0 0 3px;
+  padding: 2px 0 3px 0;
+}
+
+/**
+ * System compact link: to toggle the display of description text.
+ */
+.compact-link {
+  margin: 0 0 0.5em 0;
+}
+
+/**
+ * Quick inline admin links.
+ */
+small .admin-link:before {
+  content: ' [';
+}
+small .admin-link:after {
+  content: ']';
+}
+
+/**
+ * Modules page.
+ */
+.system-modules thead > tr {
+  border: 0;
+}
+.system-modules div.incompatible {
+  font-weight: bold;
+}
+.system-modules td.checkbox {
+  min-width: 25px;
+  width: 4%;
+}
+.system-modules td.module {
+  width: 25%;
+}
+.system-modules td {
+  vertical-align: top;
+}
+.system-modules label,
+.system-modules-uninstall label {
+  color: #1d1d1d;
+  font-size: 1.15em;
+}
+.system-modules details {
+  color: #5c5c5b;
+  line-height: 20px;
+  overflow: hidden; /* truncates descriptions if too long */
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+.system-modules details[open] {
+  height: auto;
+  overflow: visible;
+  white-space: normal;
+}
+.system-modules details[open] summary .text {
+  -webkit-hyphens: auto;
+  -moz-hyphens: auto;
+  -ms-hyphens: auto;
+  hyphens: auto;
+  text-transform: none;
+}
+.system-modules td details a {
+  color: #5C5C5B;
+  border: 0px;
+}
+.system-modules td details {
+  border: 0;
+  margin: 0;
+  height: 20px;
+}
+.system-modules td details summary {
+  padding: 0;
+  text-transform: none;
+  font-weight: normal;
+  cursor: default;
+}
+.system-modules td {
+  padding-left: 0; /* LTR */
+}
+[dir="rtl"] .system-modules td {
+  padding-left: 12px;
+  padding-right: 0;
+}
+
+@media screen and (max-width: 40em) {
+  .system-modules td.name {
+    width: 20%;
+  }
+  .system-modules td.description {
+    width: 40%;
+  }
+}
+.system-modules .requirements {
+  padding: 5px 0;
+  max-width: 490px;
+}
+.system-modules .links {
+  overflow: hidden; /* prevents collapse */
+}
+.system-modules .checkbox {
+  margin: 0 5px;
+}
+.system-modules .checkbox .form-item {
+  margin-bottom: 0;
+}
+.admin-requirements,
+.admin-required {
+  font-size: 0.9em;
+  color: #666;
+}
+.admin-enabled {
+  color: #080;
+}
+.admin-missing {
+  color: #f00;
+}
+.module-link {
+  display: block;
+  padding: 2px 20px;
+  white-space: nowrap;
+  margin-top: 2px;
+  float: left; /* LTR */
+}
+[dir="rtl"] .module-link {
+  float: right;
+}
+.module-link-help {
+  background: url(../../../../misc/icons/787878/questionmark-disc.svg) 0 50% no-repeat;
+}
+.module-link-permissions {
+  background: url(../../../../misc/icons/787878/key.svg) 0 50% no-repeat;
+}
+.module-link-configure {
+  background: url(../../../../misc/icons/787878/cog.svg) 0 50% no-repeat;
+}
+
+/* Status report. */
+.system-status-report__status-title {
+  position: relative;
+  vertical-align: top;
+  width: 25%;
+  padding: 10px 6px 10px 40px; /* LTR */
+  box-sizing: border-box;
+  font-weight: normal;
+}
+[dir="rtl"] .system-status-report__status-title {
+    padding: 10px 40px 10px 6px;
+}
+.system-status-report__status-icon:before {
+  content: "";
+  background-repeat: no-repeat;
+  height: 16px;
+  width: 16px;
+  display: block;
+  position: absolute;
+  left: 12px; /* LTR */
+  top: 12px;
+}
+[dir="rtl"] .system-status-report__status-icon:before  {
+  left: auto;
+  right: 12px;
+}
+.system-status-report__status-icon--error:before {
+  background-image: url(../../../../misc/icons/e32700/error.svg);
+}
+.system-status-report__status-icon--warning:before {
+  background-image: url(../../../../misc/icons/e29700/warning.svg);
+}
+
+/**
+ * Appearance page.
+ */
+.theme-info__header {
+  margin-bottom: 0;
+  font-weight: normal;
+}
+.theme-default .theme-info__header {
+  font-weight: bold;
+}
+.theme-info__description {
+  margin-top: 0;
+}
+.system-themes-list {
+  margin-bottom: 20px;
+}
+.system-themes-list-uninstalled {
+  border-top: 1px solid #cdcdcd;
+  padding-top: 20px;
+}
+.system-themes-list__header {
+  margin: 0;
+}
+
+.theme-selector {
+  padding-top: 20px;
+}
+.theme-selector .screenshot,
+.theme-selector .no-screenshot {
+  border: 1px solid #e0e0d8;
+  padding: 2px;
+  vertical-align: bottom;
+  max-width: 100%;
+  height: auto;
+  text-align: center;
+}
+.theme-default .screenshot {
+  border: 1px solid #aaa;
+}
+.system-themes-list-uninstalled .screenshot,
+.system-themes-list-uninstalled .no-screenshot {
+  max-width: 194px;
+  height: auto;
+}
+
+/**
+ * Theme display without vertical toolbar.
+ */
+@media screen and (min-width: 45em) {
+  body:not(.toolbar-vertical) .system-themes-list-installed .screenshot,
+  body:not(.toolbar-vertical) .system-themes-list-installed .no-screenshot {
+    float: left; /* LTR */
+    margin: 0 20px 0 0; /* LTR */
+    width: 294px;
+  }
+  [dir="rtl"] body:not(.toolbar-vertical) .system-themes-list-installed .screenshot,
+  [dir="rtl"] body:not(.toolbar-vertical) .system-themes-list-installed .no-screenshot {
+    float: right;
+    margin: 0 0 0 20px;
+  }
+  body:not(.toolbar-vertical) .system-themes-list-installed .system-themes-list__header {
+    margin-top: 0;
+  }
+  body:not(.toolbar-vertical) .system-themes-list-uninstalled .theme-selector {
+    box-sizing: border-box;
+    width: 31.25%;
+    float: left; /* LTR */
+    padding: 20px 20px 20px 0; /* LTR */
+  }
+  [dir="rtl"] body:not(.toolbar-vertical) .system-themes-list-uninstalled .theme-selector {
+    float: right;
+    padding: 20px 0 20px 20px;
+  }
+  body:not(.toolbar-vertical) .system-themes-list-uninstalled .theme-info {
+    min-height: 170px;
+  }
+}
+
+/**
+ * Theme display with vertical toolbar.
+ */
+@media screen and (min-width: 60em) {
+  .toolbar-vertical .system-themes-list-installed .screenshot,
+  .toolbar-vertical .system-themes-list-installed .no-screenshot {
+    float: left; /* LTR */
+    margin: 0 20px 0 0; /* LTR */
+    width: 294px;
+  }
+  [dir="rtl"] .toolbar-vertical .system-themes-list-installed .screenshot,
+  [dir="rtl"] .toolbar-vertical .system-themes-list-installed .no-screenshot {
+    float: right;
+    margin: 0 0 0 20px;
+  }
+  .toolbar-vertical .system-themes-list-installed .theme-info__header {
+    margin-top: 0;
+  }
+  .toolbar-vertical .system-themes-list-uninstalled .theme-selector {
+    box-sizing: border-box;
+    width: 31.25%;
+    float: left; /* LTR */
+    padding: 20px 20px 20px 0; /* LTR */
+  }
+  [dir="rtl"] .toolbar-vertical .system-themes-list-uninstalled .theme-selector {
+    float: right;
+    padding: 20px 0 20px 20px;
+  }
+  .toolbar-vertical .system-themes-list-uninstalled .theme-info {
+    min-height: 170px;
+  }
+}
+.system-themes-list-installed .theme-info {
+  max-width: 940px;
+}
+
+.theme-selector .incompatible {
+  margin-top: 10px;
+  font-weight: bold;
+}
+.theme-selector .operations {
+  margin: 10px 0 0 0;
+  padding: 0;
+}
+.theme-selector .operations li {
+  float: left; /* LTR */
+  margin: 0;
+  padding: 0 0.7em;
+  list-style-type: none;
+  border-right: 1px solid #cdcdcd;  /* LTR */
+}
+[dir="rtl"] .theme-selector .operations li {
+  float: right;
+  border-left: 1px solid #cdcdcd;
+  border-right: none;
+}
+.theme-selector .operations li:last-child {
+  padding: 0 0 0 0.7em; /* LTR */
+  border-right: none; /* LTR */
+}
+[dir="rtl"] .theme-selector .operations li:last-child {
+  padding: 0 0.7em 0 0;
+  border-left: none;
+}
+.theme-selector .operations li:first-child {
+  padding: 0 0.7em 0 0; /* LTR */
+}
+[dir="rtl"] .theme-selector .operations li:first-child {
+  padding: 0 0 0 0.7em;
+}
+.system-themes-admin-form {
+  clear: left; /* LTR */
+}
+[dir="rtl"] .system-themes-admin-form {
+  clear: right;
+}
diff --git a/core/themes/stable/css/system/system.diff.css b/core/themes/stable/css/system/system.diff.css
new file mode 100644
index 0000000..e57e6eb
--- /dev/null
+++ b/core/themes/stable/css/system/system.diff.css
@@ -0,0 +1,42 @@
+/**
+ * Traditional split diff theming
+ */
+table.diff {
+  border-spacing: 4px;
+  margin-bottom: 20px;
+  table-layout: fixed;
+  width: 100%;
+}
+table.diff .diff-context {
+  background-color: #fafafa;
+}
+table.diff .diff-deletedline {
+  background-color: #ffa;
+  width: 50%;
+}
+table.diff .diff-addedline {
+  background-color: #afa;
+  width: 50%;
+}
+table.diff .diffchange {
+  color: #f00;
+  font-weight: bold;
+}
+table.diff .diff-marker {
+  width: 1.4em;
+}
+table.diff th {
+  padding-right: inherit; /* LTR */
+}
+[dir="rtl"] table.diff th {
+  padding-right: 0;
+  padding-left: inherit;
+}
+table.diff td div {
+  overflow: auto;
+  padding: 0.1ex 0.5em;
+  word-wrap: break-word;
+}
+table.diff td {
+  padding: 0.1ex 0.4em;
+}
diff --git a/core/themes/stable/css/system/system.maintenance.css b/core/themes/stable/css/system/system.maintenance.css
new file mode 100644
index 0000000..8b142cc
--- /dev/null
+++ b/core/themes/stable/css/system/system.maintenance.css
@@ -0,0 +1,56 @@
+/**
+ * Update styles
+ */
+.update-results {
+  margin-top: 3em;
+  padding: 0.25em;
+  border: 1px solid #ccc;
+  background: #eee;
+  font-size: smaller;
+}
+.update-results h2 {
+  margin-top: 0.25em;
+}
+.update-results h4 {
+  margin-bottom: 0.25em;
+}
+.update-results .none {
+  color: #888;
+  font-style: italic;
+}
+.update-results .failure strong {
+  color: #b63300;
+}
+
+/**
+ * Authorize.php styles
+ */
+#edit-submit-connection {
+  clear: both;
+}
+#edit-submit-process,
+.filetransfer {
+  display: none;
+  clear: both;
+}
+.js #edit-submit-connection {
+  display: none;
+}
+.js #edit-submit-process {
+  display: block;
+}
+
+#edit-connection-settings-change-connection-type {
+  margin: 2.6em 0.5em 0 1em; /* LTR */
+}
+[dir="rtl"] #edit-connection-settings-change-connection-type {
+  margin-left: 0.5em;
+  margin-right: 1em;
+}
+
+/**
+ * Theme maintenance styles
+ */
+.authorize-results__failure {
+  font-weight: bold;
+}
diff --git a/core/themes/stable/css/taxonomy/taxonomy.theme.css b/core/themes/stable/css/taxonomy/taxonomy.theme.css
new file mode 100644
index 0000000..543666a
--- /dev/null
+++ b/core/themes/stable/css/taxonomy/taxonomy.theme.css
@@ -0,0 +1,10 @@
+
+.taxonomy-term-preview {
+  background-color: #eee;
+}
+.taxonomy-term-divider-top {
+  border-bottom: none;
+}
+.taxonomy-term-divider-bottom {
+  border-top: 1px dotted #ccc;
+}
diff --git a/core/themes/stable/css/toolbar/toolbar.icons.theme.css b/core/themes/stable/css/toolbar/toolbar.icons.theme.css
new file mode 100644
index 0000000..7ba95ff
--- /dev/null
+++ b/core/themes/stable/css/toolbar/toolbar.icons.theme.css
@@ -0,0 +1,300 @@
+/**
+ * @file
+ * Styling for toolbar module icons.
+ */
+
+.toolbar .toolbar-icon {
+  padding-left: 2.75em; /* LTR */
+  position: relative;
+}
+[dir="rtl"] .toolbar .toolbar-icon {
+  padding-left: 1.3333em;
+  padding-right: 2.75em;
+}
+.toolbar .toolbar-icon:before {
+  background-attachment: scroll;
+  background-color: transparent;
+  background-position: center center;
+  background-repeat: no-repeat;
+  background-size: 100% auto;
+  content: '';
+  display: block;
+  height: 100%;
+  left: 0.6667em; /* LTR */
+  position: absolute;
+  top: 0;
+  width: 20px;
+}
+[dir="rtl"] .toolbar .toolbar-icon:before {
+  left: auto;
+  right: 0.6667em;
+}
+.toolbar button.toolbar-icon {
+  background-color: transparent;
+  border: 0;
+  font-size: 1em;
+}
+.toolbar .toolbar-menu ul .toolbar-icon {
+  padding-left: 1.3333em; /* LTR */
+}
+[dir="rtl"] .toolbar .toolbar-menu ul .toolbar-icon {
+  padding-left: 0;
+  padding-right: 1.3333em;
+}
+.toolbar .toolbar-menu ul a.toolbar-icon:before {
+  display: none;
+}
+.toolbar .toolbar-tray-vertical .toolbar-menu ul a {
+  padding-left: 2.75em; /* LTR */
+}
+[dir="rtl"] .toolbar .toolbar-tray-vertical .toolbar-menu ul a {
+  padding-left: 0;
+  padding-right: 2.75em;
+}
+.toolbar .toolbar-tray-vertical .toolbar-menu ul ul a {
+  padding-left: 3.75em; /* LTR */
+}
+[dir="rtl"] .toolbar .toolbar-tray-vertical .toolbar-menu ul ul a {
+  padding-left: 0;
+  padding-right: 3.75em;
+}
+
+.toolbar .toolbar-tray-vertical .toolbar-menu a {
+  padding-left: 2.75em; /* LTR */
+  padding-right: 4em; /* LTR */
+}
+[dir="rtl"] .toolbar .toolbar-tray-vertical .toolbar-menu a {
+  padding-left: 4em;
+  padding-right: 2.75em;
+}
+
+/**
+ * Top level icons.
+ */
+.toolbar-bar .toolbar-icon-menu:before {
+  background-image: url(../../../../misc/icons/bebebe/hamburger.svg);
+}
+.toolbar-bar .toolbar-icon-menu:active:before,
+.toolbar-bar .toolbar-icon-menu.is-active:before {
+  background-image: url(../../../../misc/icons/ffffff/hamburger.svg);
+}
+.toolbar-bar .toolbar-icon-help:before {
+  background-image: url(../../../../misc/icons/bebebe/questionmark-disc.svg);
+}
+.toolbar-bar .toolbar-icon-help:active:before,
+.toolbar-bar .toolbar-icon-help.is-active:before {
+  background-image: url(../../../../misc/icons/ffffff/questionmark-disc.svg);
+}
+
+/**
+ * Main menu icons.
+ */
+.toolbar-icon-system-admin-content:before {
+  background-image: url(../../../../misc/icons/787878/file.svg);
+}
+.toolbar-icon-system-admin-content:active:before,
+.toolbar-icon-system-admin-content.is-active:before {
+  background-image: url(../../../../misc/icons/000000/file.svg);
+}
+.toolbar-icon-system-admin-structure:before {
+  background-image: url(../../../../misc/icons/787878/orgchart.svg);
+}
+.toolbar-icon-system-admin-structure:active:before,
+.toolbar-icon-system-admin-structure.is-active:before {
+  background-image: url(../../../../misc/icons/000000/orgchart.svg);
+}
+.toolbar-icon-system-themes-page:before {
+  background-image: url(../../../../misc/icons/787878/paintbrush.svg);
+}
+.toolbar-icon-system-themes-page:active:before,
+.toolbar-icon-system-themes-page.is-active:before {
+  background-image: url(../../../../misc/icons/000000/paintbrush.svg);
+}
+.toolbar-icon-entity-user-collection:before {
+  background-image: url(../../../../misc/icons/787878/people.svg);
+}
+.toolbar-icon-entity-user-collection:active:before,
+.toolbar-icon-entity-user-collection.is-active:before {
+  background-image: url(../../../../misc/icons/000000/people.svg);
+}
+.toolbar-icon-system-modules-list:before {
+  background-image: url(../../../../misc/icons/787878/puzzlepiece.svg);
+}
+.toolbar-icon-system-modules-list:active:before,
+.toolbar-icon-system-modules-list.is-active:before {
+  background-image: url(../../../../misc/icons/000000/puzzlepiece.svg);
+}
+.toolbar-icon-system-admin-config:before {
+  background-image: url(../../../../misc/icons/787878/wrench.svg);
+}
+.toolbar-icon-system-admin-config:active:before,
+.toolbar-icon-system-admin-config.is-active:before {
+  background-image: url(../../../../misc/icons/000000/wrench.svg);
+}
+.toolbar-icon-system-admin-reports:before {
+  background-image: url(../../../../misc/icons/787878/barchart.svg);
+}
+.toolbar-icon-system-admin-reports:active:before,
+.toolbar-icon-system-admin-reports.is-active:before {
+  background-image: url(../../../../misc/icons/000000/barchart.svg);
+}
+.toolbar-icon-help-main:before {
+  background-image: url(../../../../misc/icons/787878/questionmark-disc.svg);
+}
+.toolbar-icon-help-main:active:before,
+.toolbar-icon-help-main.is-active:before {
+  background-image: url(../../../../misc/icons/000000/questionmark-disc.svg);
+}
+
+@media only screen and (min-width: 16.5em) {
+  .toolbar .toolbar-bar .toolbar-tab > .toolbar-icon {
+    margin-left: 0;
+    margin-right: 0;
+    padding-left: 0;
+    padding-right: 0;
+    text-indent: -9999px;
+    width: 4em;
+  }
+  .toolbar .toolbar-bar .toolbar-tab > .toolbar-icon:before {
+    background-size: 42% auto;
+    left: 0; /* LTR */
+    width: 100%;
+  }
+  .no-svg .toolbar .toolbar-bar .toolbar-tab > .toolbar-icon:before {
+    background-size: auto auto;
+  }
+  [dir="rtl"] .toolbar .toolbar-bar .toolbar-tab > .toolbar-icon:before {
+    left: auto;
+    right: 0;
+  }
+}
+
+@media only screen and (min-width: 36em) {
+  .toolbar .toolbar-bar .toolbar-tab > .toolbar-icon {
+    background-position: left center; /* LTR */
+    padding-left: 2.75em; /* LTR */
+    padding-right: 1.3333em; /* LTR */
+    text-indent: 0;
+    width: auto;
+  }
+  [dir="rtl"] .toolbar .toolbar-bar .toolbar-tab > .toolbar-icon {
+    background-position: right center;
+    padding-left: 1.3333em;
+    padding-right: 2.75em;
+  }
+  .toolbar .toolbar-bar .toolbar-tab > .toolbar-icon:before {
+    background-size: 100% auto;
+    left: 0.6667em; /* LTR */
+    width: 20px;
+  }
+  .no-svg .toolbar .toolbar-bar .toolbar-tab > .toolbar-icon:before {
+    background-size: auto auto;
+  }
+  [dir="rtl"] .toolbar .toolbar-bar .toolbar-tab > .toolbar-icon:before {
+    left: 0;
+    right: 0.6667em;
+  }
+}
+
+/**
+ *  Accessibility/focus
+ */
+.toolbar-tab a:focus {
+  outline: none;
+  text-decoration: underline;
+}
+.toolbar-lining button:focus {
+  outline: none;
+}
+.toolbar-tray-horizontal a:focus,
+.toolbar-box a:focus {
+  outline: none;
+  background-color: #f5f5f5;
+}
+.toolbar-box a:hover:focus {
+  text-decoration: underline;
+}
+.toolbar .toolbar-icon.toolbar-handle:focus {
+  outline: none;
+  background-color: #f5f5f5;
+}
+
+
+/**
+ * Handle.
+ */
+.toolbar .toolbar-icon.toolbar-handle {
+  width: 4em;
+  text-indent: -9999px;
+}
+.toolbar .toolbar-icon.toolbar-handle:before {
+  left: 1.6667em; /* LTR */
+}
+[dir="rtl"] .toolbar .toolbar-icon.toolbar-handle:before {
+  left: auto;
+  right: 1.6667em;
+}
+.toolbar .toolbar-icon.toolbar-handle:before {
+  background-image: url(../../../../misc/icons/5181c6/chevron-disc-down.svg);
+}
+.toolbar .toolbar-icon.toolbar-handle.open:before {
+  background-image: url(../../../../misc/icons/787878/chevron-disc-up.svg);
+}
+.toolbar .toolbar-menu .toolbar-menu .toolbar-icon.toolbar-handle:before {
+  background-image: url(../../../../misc/icons/5181c6/twistie-down.svg);
+  background-size: 75%;
+}
+.toolbar .toolbar-menu .toolbar-menu .toolbar-icon.toolbar-handle.open:before {
+  background-image: url(../../../../misc/icons/787878/twistie-up.svg);
+  background-size: 75%;
+}
+.toolbar .toolbar-icon-escape-admin:before {
+  background-image: url(../../../../misc/icons/bebebe/chevron-disc-left.svg);
+}
+[dir="rtl"] .toolbar .toolbar-icon-escape-admin:before {
+  background-image: url(../../../../misc/icons/bebebe/chevron-disc-right.svg);
+}
+/**
+ * Orientation toggle.
+ */
+.toolbar .toolbar-toggle-orientation button {
+  height: 39px;
+  padding: 0;
+  text-indent: -999em;
+  width: 39px;
+}
+.toolbar .toolbar-toggle-orientation button:before {
+  left: 0;
+  right: 0;
+  margin: 0 auto;
+}
+[dir="rtl"] .toolbar .toolbar-toggle-orientation .toolbar-icon {
+  padding: 0;
+}
+/**
+ * In order to support a hover effect on the SVG images, while also supporting
+ * RTL text direction and no SVG support, this little icon requires some very
+ * specific targeting, setting and unsetting.
+ */
+.toolbar .toolbar-toggle-orientation [value="vertical"]:before {
+  background-image: url(../../../../misc/icons/bebebe/push-left.svg); /* LTR */
+}
+.toolbar .toolbar-toggle-orientation [value="vertical"]:hover:before,
+.toolbar .toolbar-toggle-orientation [value="vertical"]:focus:before
+ {
+  background-image: url(../../../../misc/icons/787878/push-left.svg); /* LTR */
+}
+[dir="rtl"] .toolbar .toolbar-toggle-orientation [value="vertical"]:before {
+  background-image: url(../../../../misc/icons/bebebe/push-right.svg);
+}
+[dir="rtl"] .toolbar .toolbar-toggle-orientation [value="vertical"]:hover:before,
+[dir="rtl"] .toolbar .toolbar-toggle-orientation [value="vertical"]:focus:before {
+  background-image: url(../../../../misc/icons/787878/push-right.svg);
+}
+.toolbar .toolbar-toggle-orientation [value="horizontal"]:before {
+  background-image: url(../../../../misc/icons/bebebe/push-up.svg);
+}
+.toolbar .toolbar-toggle-orientation [value="horizontal"]:hover:before,
+.toolbar .toolbar-toggle-orientation [value="horizontal"]:focus:before {
+  background-image: url(../../../../misc/icons/787878/push-up.svg);
+}
diff --git a/core/themes/stable/css/toolbar/toolbar.menu.css b/core/themes/stable/css/toolbar/toolbar.menu.css
new file mode 100644
index 0000000..099fb08
--- /dev/null
+++ b/core/themes/stable/css/toolbar/toolbar.menu.css
@@ -0,0 +1,103 @@
+/**
+ * @file toolbar.menu.css
+ */
+.toolbar .toolbar-menu,
+[dir="rtl"] .toolbar .toolbar-menu {
+  list-style: none;
+  margin: 0;
+  padding: 0;
+}
+.toolbar .toolbar-box {
+  display: block;
+  line-height: 1em; /* this prevents the value "normal" from being returned as the line-height */
+  position: relative;
+  width: auto;
+}
+.toolbar .toolbar-tray-horizontal .toolbar-menu .toolbar-handle,
+.toolbar .toolbar-tray-horizontal .toolbar-menu ul,
+.toolbar .toolbar-tray-vertical .toolbar-menu ul {
+  display: none;
+}
+.toolbar .toolbar-tray-vertical li.open > ul {
+  display: block; /* Show the sub-menus */
+}
+.toolbar .toolbar-tray-vertical .toolbar-handle + a {
+  margin-right: 3em; /* LTR */
+}
+[dir="rtl"] .toolbar .toolbar-tray-vertical .toolbar-handle + a {
+  margin-left: 3em;
+  margin-right: 0;
+}
+.toolbar .toolbar-tray .menu-item--active-trail > .toolbar-box a,
+.toolbar .toolbar-tray a.is-active {
+  color: #000;
+  font-weight: bold;
+}
+
+/* ----- Toolbar menu tray for viewports less than 320px ------ */
+@media screen and (max-width: 319px) {
+  .toolbar .toolbar-tray-vertical.is-active {
+    width: 100%;
+  }
+}
+
+/**
+ * Items.
+ */
+.toolbar .level-2 > ul {
+  background-color: #fafafa;
+  border-bottom-color: #cccccc;
+  border-top-color: #e5e5e5;
+}
+.toolbar .level-3 > ul {
+  background-color: #f5f5f5;
+  border-bottom-color: #c5c5c5;
+  border-top-color: #dddddd;
+}
+.toolbar .level-4 > ul {
+  background-color: #eeeeee;
+  border-bottom-color: #bbbbbb;
+  border-top-color: #d5d5d5;
+}
+.toolbar .level-5 > ul {
+  background-color: #e5e5e5;
+  border-bottom-color: #b5b5b5;
+  border-top-color: #cccccc;
+}
+.toolbar .level-6 > ul {
+  background-color: #eeeeee;
+  border-bottom-color: #aaaaaa;
+  border-top-color: #c5c5c5;
+}
+.toolbar .level-7 > ul {
+  background-color: #fafafa;
+  border-bottom-color: #b5b5b5;
+  border-top-color: #cccccc;
+}
+.toolbar .level-8 > ul {
+  background-color: #dddddd;
+  border-bottom-color: #cccccc;
+  border-top-color: #dddddd;
+}
+
+/**
+ * Handle.
+ */
+.toolbar .toolbar-handle:hover {
+  cursor: pointer;
+}
+.toolbar .toolbar-icon.toolbar-handle {
+  bottom: 0;
+  display: block;
+  height: 100%;
+  padding: 0;
+  position: absolute;
+  right: 0; /* LTR */
+  top: 0;
+  z-index: 1;
+}
+[dir="rtl"] .toolbar .toolbar-icon.toolbar-handle {
+  left: 0;
+  padding: 0;
+  right: auto;
+}
diff --git a/core/themes/stable/css/toolbar/toolbar.module.css b/core/themes/stable/css/toolbar/toolbar.module.css
new file mode 100644
index 0000000..69eb7c2
--- /dev/null
+++ b/core/themes/stable/css/toolbar/toolbar.module.css
@@ -0,0 +1,260 @@
+/**
+ * @file toolbar.module.css
+ *
+ *
+ * Aggressive resets so we can achieve a consistent look in hostile CSS
+ * environments.
+ */
+#toolbar-administration,
+#toolbar-administration * {
+  box-sizing: border-box;
+}
+#toolbar-administration {
+  font-size: small;
+  line-height: 1;
+  margin: 0;
+  padding: 0;
+  vertical-align: baseline;
+}
+@media print {
+  #toolbar-administration {
+    display: none;
+  }
+}
+
+/**
+ * Very specific overrides for Drupal system CSS.
+ */
+.toolbar li,
+.toolbar .item-list,
+.toolbar .item-list li,
+.toolbar .menu-item,
+.toolbar .menu-item--expanded {
+  list-style-type: none;
+  list-style-image: none;
+}
+.toolbar .menu-item {
+  padding-top: 0;
+}
+.toolbar .toolbar-bar .toolbar-tab,
+.toolbar .menu-item {
+  display: block;
+}
+.toolbar .toolbar-bar .toolbar-tab.hidden {
+  display: none;
+}
+.toolbar a {
+  display: block;
+  line-height: 1;
+}
+
+/**
+ * Administration menu.
+ */
+.toolbar .toolbar-bar,
+.toolbar .toolbar-tray {
+  position: relative;
+  z-index: 1250;
+}
+/* Position the admin toolbar absolutely when the configured standard breakpoint
+ * is active. The toolbar container, that contains the bar and the trays, is
+ * position absolutely so that it scrolls with the page. Otherwise, on smaller
+ * screens, the components of the admin toolbar are positioned statically. */
+body.toolbar-fixed .toolbar-oriented,
+.toolbar-oriented .toolbar-bar,
+.toolbar-oriented .toolbar-tray {
+  left: 0;
+  position: absolute;
+  right: 0;
+  top: 0;
+}
+/* Layer the bar just above the trays and above contextual link triggers. */
+.toolbar-oriented .toolbar-bar {
+  z-index: 502;
+}
+/* Position the admin toolbar fixed when the configured standard breakpoint is
+ * active. */
+body.toolbar-fixed .toolbar-oriented .toolbar-bar {
+  position: fixed;
+}
+/* When the configured narrow breakpoint is active, the toolbar is sized to wrap
+ * around the trays in order to provide a context for scrolling tray content
+ * that is taller than the viewport. */
+body.toolbar-tray-open.toolbar-fixed.toolbar-vertical .toolbar-oriented {
+  bottom: 0;
+  width: 240px;
+  width: 15rem;
+}
+/* Present the admin toolbar tabs horizontally as a default on user agents that
+ * do not understand media queries or on user agents where JavaScript is
+ * disabled. */
+.toolbar .toolbar-bar .toolbar-tab,
+.toolbar .toolbar-tray-horizontal li {
+  float: left; /* LTR */
+}
+[dir="rtl"] .toolbar .toolbar-bar .toolbar-tab,
+[dir="rtl"] .toolbar .toolbar-tray-horizontal li {
+  float: right;
+}
+/* Present the admin toolbar tabs vertically by default on user agents that
+ * that understand media queries. This will be the small screen default. */
+@media only screen {
+  .toolbar .toolbar-bar .toolbar-tab,
+  .toolbar .toolbar-tray-horizontal li {
+    float: none; /* LTR */
+  }
+  [dir="rtl"] .toolbar .toolbar-bar .toolbar-tab,
+  [dir="rtl"] .toolbar .toolbar-tray-horizontal li {
+    float: none;
+  }
+}
+/* This min-width media query is meant to provide basic horizontal layout to
+ * the main menu tabs when JavaScript is disabled on user agents that understand
+ * media queries. */
+@media (min-width:16.5em) {
+  .toolbar .toolbar-bar .toolbar-tab,
+  .toolbar .toolbar-tray-horizontal li {
+    float: left; /* LTR */
+  }
+  [dir="rtl"] .toolbar .toolbar-bar .toolbar-tab,
+  [dir="rtl"] .toolbar .toolbar-tray-horizontal li {
+    float: right;
+  }
+}
+/* Present the admin toolbar tabs horizontally when the configured narrow
+ * breakpoint is active. */
+.toolbar-oriented .toolbar-bar .toolbar-tab,
+.toolbar-oriented .toolbar-tray-horizontal li {
+  float: left; /* LTR */
+}
+[dir="rtl"] .toolbar-oriented .toolbar-bar .toolbar-tab,
+[dir="rtl"] .toolbar-oriented .toolbar-tray-horizontal li {
+  float: right;
+}
+
+/**
+ * Toolbar tray.
+ */
+.toolbar .toolbar-tray {
+  display: none;
+  z-index: 501;
+}
+.toolbar-oriented .toolbar-tray-vertical {
+  left: -100%; /* LTR */
+  position: absolute;
+  width: 240px;
+  width: 15rem;
+}
+[dir="rtl"] .toolbar-oriented .toolbar-tray-vertical {
+  left: auto;
+  right: -100%;
+}
+.toolbar .toolbar-tray-vertical > .toolbar-lining {
+  min-height: 100%;
+}
+.toolbar .toolbar-tray-vertical > .toolbar-lining:before {
+  width: 100%;
+}
+.toolbar-oriented .toolbar-tray-vertical > .toolbar-lining:before {
+  bottom: 0;
+  content: '';
+  display: block;
+  left: 0; /* LTR */
+  position: fixed;
+  top: 0;
+  width: 240px;
+  width: 14rem;
+  z-index: -1;
+}
+[dir="rtl"] .toolbar .toolbar-tray-vertical > .toolbar-lining:before {
+  left: auto;
+  right: 0;
+}
+/* Layer the links just above the toolbar-tray. */
+.toolbar .toolbar-bar .toolbar-tab > .toolbar-icon{
+  position: relative;
+  z-index: 502;
+}
+/* Hide secondary menus when the tray is horizontal. */
+.toolbar-oriented .toolbar-tray-horizontal .menu-item ul {
+  display: none;
+}
+/* When the configured standard breakpoint is active and the tray is in a
+ * horizontal position, the tray is fixed to the top of the viewport and does
+ * not scroll with the page contents. */
+body.toolbar-fixed .toolbar .toolbar-tray-horizontal {
+  position: fixed;
+}
+/* When the configured standard breakpoint is active and the tray is in a
+ * vertical position, the tray does not scroll with the page. The contents of
+ * the tray scroll within the confines of the viewport. */
+.toolbar .toolbar-tray-vertical.is-active,
+body.toolbar-fixed .toolbar .toolbar-tray-vertical {
+  height: 100%;
+  overflow-x: hidden;
+  overflow-y: auto;
+  position: fixed;
+}
+.toolbar .toolbar-tray.is-active {
+  display: block;
+}
+/* Bring the tray into the viewport. By default it is just off-screen. */
+.toolbar-oriented .toolbar-tray-vertical.is-active {
+  left: 0; /* LTR */
+}
+[dir="rtl"] .toolbar-oriented .toolbar-tray-vertical.is-active {
+  left: auto;
+  right: 0;
+}
+/* When the configured standard breakpoint is active, the tray appears to push
+ * the page content away from the edge of the viewport. */
+body.toolbar-tray-open.toolbar-vertical.toolbar-fixed {
+  margin-left: 240px; /* LTR */
+  margin-left: 15rem; /* LTR */
+}
+@media print {
+  body.toolbar-tray-open.toolbar-vertical.toolbar-fixed {
+    margin-left: 0;
+  }
+}
+[dir="rtl"] body.toolbar-tray-open.toolbar-vertical.toolbar-fixed {
+  margin-left: auto;
+  margin-left: auto;
+  margin-right: 240px;
+  margin-right: 15rem;
+}
+@media print {
+  [dir="rtl"] body.toolbar-tray-open.toolbar-vertical.toolbar-fixed {
+    margin-right: 0;
+  }
+}
+/**
+ * ToolBar tray orientation toggle.
+ */
+/* Hide the orientation toggle when the configured narrow breakpoint is not
+ * active. */
+.toolbar .toolbar-tray .toolbar-toggle-orientation {
+  display: none;
+}
+/* Show the orientation toggle when the configured narrow breakpoint is
+ * active. */
+.toolbar-oriented .toolbar-tray .toolbar-toggle-orientation {
+  display: block;
+}
+.toolbar-oriented .toolbar-tray-horizontal .toolbar-toggle-orientation {
+  bottom: 0;
+  position: absolute;
+  right: 0; /* LTR */
+  top: auto;
+}
+[dir="rtl"] .toolbar-oriented .toolbar-tray-horizontal .toolbar-toggle-orientation {
+  left: 0;
+  right: auto;
+}
+.toolbar-oriented .toolbar-tray-vertical .toolbar-toggle-orientation {
+  float: right; /* LTR */
+  width: 100%;
+}
+[dir="rtl"] .toolbar-oriented .toolbar-tray-vertical .toolbar-toggle-orientation {
+  float: left;
+}
diff --git a/core/themes/stable/css/toolbar/toolbar.theme.css b/core/themes/stable/css/toolbar/toolbar.theme.css
new file mode 100644
index 0000000..981ee00
--- /dev/null
+++ b/core/themes/stable/css/toolbar/toolbar.theme.css
@@ -0,0 +1,168 @@
+/**
+ * @file toolbar.theme.css
+ */
+.toolbar {
+  font-family: "Source Sans Pro", "Lucida Grande", Verdana, sans-serif;
+  /* Set base font size to 13px based on root ems. */
+  font-size: 0.8125rem;
+  -moz-tap-highlight-color: rgba(0,0,0,0);
+  -o-tap-highlight-color: rgba(0,0,0,0);
+  -webkit-tap-highlight-color: rgba(0,0,0,0);
+  tap-highlight-color: rgba(0,0,0,0);
+  -moz-touch-callout: none;
+  -o-touch-callout: none;
+  -webkit-touch-callout: none;
+  touch-callout: none;
+}
+.toolbar .toolbar-item {
+  cursor: pointer;
+  padding: 1em 1.3333em;
+  line-height: 1em;
+  text-decoration: none;
+}
+.toolbar .toolbar-item:hover, .toolbar .toolbar-item:focus {
+  text-decoration: underline;
+}
+
+/**
+ * Toolbar bar.
+ */
+.toolbar .toolbar-bar {
+  background-color: #0f0f0f;
+  box-shadow: -1px 0 3px 1px rgba(0, 0, 0, 0.3333); /* LTR */
+  color: #dddddd;
+}
+[dir="rtl"] .toolbar .toolbar-bar {
+  box-shadow: 1px 0 3px 1px rgba(0, 0, 0, 0.3333);
+}
+.toolbar .toolbar-bar .toolbar-item {
+  color: #ffffff;
+}
+.toolbar .toolbar-bar .toolbar-tab > .toolbar-item {
+  font-weight: bold;
+}
+.toolbar .toolbar-bar .toolbar-tab > .toolbar-item:hover,
+.toolbar .toolbar-bar .toolbar-tab > .toolbar-item:focus {
+  background-image: -webkit-linear-gradient(rgba(255, 255, 255, 0.125) 20%, transparent 200%);
+  background-image: linear-gradient(rgba(255, 255, 255, 0.125) 20%, transparent 200%);
+}
+.toolbar .toolbar-bar .toolbar-tab > .toolbar-item.is-active {
+  background-image: -webkit-linear-gradient(rgba(255, 255, 255, 0.25) 20%, transparent 200%);
+  background-image: linear-gradient(rgba(255, 255, 255, 0.25) 20%, transparent 200%);
+}
+
+/**
+ * Toolbar tray.
+ */
+.toolbar .toolbar-tray {
+  background-color: #ffffff;
+}
+.toolbar .toolbar-tray-horizontal > .toolbar-lining {
+  padding-right: 5em; /* LTR */
+}
+[dir="rtl"] .toolbar .toolbar-tray-horizontal > .toolbar-lining {
+  padding-right: 0;
+  padding-left: 5em;
+}
+.toolbar .toolbar-tray-vertical {
+  background-color: #f5f5f5;
+  border-right: 1px solid #aaaaaa; /* LTR */
+  box-shadow: -1px 0 5px 2px rgba(0, 0, 0, 0.3333); /* LTR */
+}
+[dir="rtl"] .toolbar .toolbar-tray-vertical {
+  border-left: 1px solid #aaaaaa;
+  border-right: 0 none;
+  box-shadow: 1px 0 5px 2px rgba(0, 0, 0, 0.3333);
+}
+.toolbar .toolbar-tray-horizontal {
+  border-bottom: 1px solid #aaaaaa;
+  box-shadow: -2px 1px 3px 1px rgba(0, 0, 0, 0.3333); /* LTR */
+}
+[dir="rtl"] .toolbar .toolbar-tray-horizontal {
+  box-shadow: 2px 1px 3px 1px rgba(0, 0, 0, 0.3333);
+}
+.toolbar .toolbar-tray-horizontal .toolbar-tray {
+  background-color: #f5f5f5;
+}
+.toolbar-tray a {
+  color: #565656;
+  cursor: pointer;
+  padding: 1em 1.3333em;
+  text-decoration: none;
+}
+.toolbar-tray a:hover,
+.toolbar-tray a:active,
+.toolbar-tray a:focus,
+.toolbar-tray a.is-active
+ {
+  color: #000;
+  text-decoration: underline;
+}
+.toolbar .toolbar-menu {
+  background-color: #ffffff;
+}
+.toolbar .toolbar-tray-horizontal .menu-item + .menu-item {
+  border-left: 1px solid #dddddd; /* LTR */
+}
+[dir="rtl"] .toolbar .toolbar-tray-horizontal .menu-item + .menu-item {
+  border-left: 0 none ;
+  border-right: 1px solid #dddddd;
+}
+.toolbar .toolbar-tray-horizontal .menu-item:last-child {
+  border-right: 1px solid #dddddd; /* LTR */
+}
+[dir="rtl"] .toolbar .toolbar-tray-horizontal .menu-item:last-child {
+  border-left: 1px solid #dddddd;
+}
+.toolbar .toolbar-tray-vertical .menu-item + .menu-item {
+  border-top: 1px solid #dddddd;
+}
+.toolbar .toolbar-tray-vertical .menu-item:last-child {
+  border-bottom: 1px solid #dddddd;
+}
+.toolbar .toolbar-tray-vertical .menu-item .menu-item {
+  border: 0 none;
+}
+.toolbar .toolbar-tray-vertical .toolbar-menu ul ul {
+  border-bottom: 1px solid #dddddd;
+  border-top: 1px solid #dddddd;
+}
+.toolbar .toolbar-tray-vertical .menu-item:last-child > ul {
+  border-bottom: 0;
+}
+.toolbar .toolbar-tray-vertical .toolbar-menu .toolbar-menu .toolbar-menu .toolbar-menu {
+  margin-left: 0.25em; /* LTR */
+}
+[dir="rtl"] .toolbar .toolbar-tray-vertical .toolbar-menu .toolbar-menu .toolbar-menu .toolbar-menu {
+  margin-left: 0;
+  margin-right: 0.25em;
+}
+.toolbar .toolbar-menu .toolbar-menu a {
+  color: #434343;
+}
+
+/**
+ * Orientation toggle.
+ */
+.toolbar .toolbar-toggle-orientation {
+  background-color: #f5f5f5;
+  padding: 0;
+  height: 100%;
+}
+.toolbar .toolbar-tray-horizontal .toolbar-toggle-orientation {
+  border-left: 1px solid #c9c9c9; /* LTR */
+}
+[dir="rtl"] .toolbar .toolbar-tray-horizontal .toolbar-toggle-orientation {
+  border-left: 0 none;
+  border-right: 1px solid #c9c9c9;
+}
+.toolbar .toolbar-toggle-orientation > .toolbar-lining {
+  float: right; /* LTR */
+}
+[dir="rtl"] .toolbar .toolbar-toggle-orientation > .toolbar-lining {
+  float: left;
+}
+.toolbar .toolbar-toggle-orientation button {
+  cursor: pointer;
+  display: inline-block;
+}
diff --git a/core/themes/stable/css/tour/tour.module.css b/core/themes/stable/css/tour/tour.module.css
new file mode 100644
index 0000000..18d8aa0
--- /dev/null
+++ b/core/themes/stable/css/tour/tour.module.css
@@ -0,0 +1,142 @@
+/**
+ * @file
+ * Styling for tour module.
+ */
+
+/* Tab appearance. */
+.toolbar .toolbar-bar .tour-toolbar-tab.toolbar-tab {
+  float: right; /* LTR */
+}
+[dir="rtl"] .toolbar .toolbar-bar .tour-toolbar-tab.toolbar-tab {
+  float: left;
+}
+
+/* Override placement of the tour progress indicator. */
+.tour-progress {
+  position: absolute;
+  bottom: 20px;
+  right: 20px; /* LTR */
+}
+[dir="rtl"] .tour-progress {
+  right: auto;
+  left: 20px;
+}
+
+/* Default styles for the container */
+.joyride-tip-guide {
+  position: absolute;
+  display: none;
+  background: #fff;
+  width: 300px;
+  z-index: 101;
+  top: 0;
+  left: 0;
+}
+@media only screen and (max-width: 767px) {
+  .joyride-tip-guide {
+    width: 85%;
+    left: 2.5%;
+  }
+}
+
+.joyride-content-wrapper {
+  position: relative;
+  padding: 20px 50px 20px 20px;  /* LTR */
+}
+[dir="rtl"] .joyride-content-wrapper {
+  padding: 20px 20px 20px 50px;
+}
+
+/* Add a little css triangle pip, older browser just miss out on the fanciness of it. */
+.joyride-tip-guide .joyride-nub {
+  display: block;
+  position: absolute;
+  left: 22px;
+  width: 0;
+  height: 0;
+}
+
+.joyride-tip-guide .joyride-nub.top {
+  top: -28px;
+  bottom: auto;
+}
+
+.joyride-tip-guide .joyride-nub.bottom {
+  bottom: -28px;
+}
+
+.joyride-tip-guide .joyride-nub.right {
+  top: 22px;
+  bottom: auto;
+  left: auto;
+  right: -28px;
+}
+
+.joyride-tip-guide .joyride-nub.left {
+  top: 22px;
+  left: -28px;
+  right: auto;
+  bottom: auto;
+}
+
+.joyride-tip-guide .joyride-nub.top-right {
+  top: -28px;
+  bottom: auto;
+  left: auto;
+  right: 28px;
+}
+
+.joyride-tip-guide .tour-tip-label {
+  margin-top: 0;
+}
+
+.joyride-tip-guide p {
+  margin: 0 0 1.4em;
+}
+
+.joyride-timer-indicator-wrap {
+  width: 50px;
+  height: 3px;
+  position: absolute;
+  right: 17px;
+  bottom: 16px;
+}
+.joyride-timer-indicator {
+  display: block;
+  width: 0;
+  height: inherit;
+}
+
+.joyride-close-tip {
+  position: absolute;
+  line-height: 1em;
+  right: 20px; /* LTR */
+  top: 20px;
+}
+[dir="rtl"] .joyride-close-tip {
+  left: 20px;
+  right: auto;
+}
+
+.joyride-modal-bg {
+  position: fixed;
+  height: 100%;
+  width: 100%;
+  z-index: 100;
+  display: none;
+  top: 0;
+  left: 0;
+  cursor: pointer;
+}
+
+.joyride-expose-wrapper {
+  position: absolute;
+  z-index: 102;
+}
+
+.joyride-expose-cover {
+  position: absolute;
+  z-index: 10000;
+  top: 0;
+  left: 0;
+}
diff --git a/core/themes/stable/css/update/update.admin.theme.css b/core/themes/stable/css/update/update.admin.theme.css
new file mode 100644
index 0000000..abf0a88
--- /dev/null
+++ b/core/themes/stable/css/update/update.admin.theme.css
@@ -0,0 +1,63 @@
+/**
+ * @file
+ * Styles used by the Update Manager module.
+ */
+
+.project-update__title {
+  font-weight: bold;
+  font-size: 110%;
+}
+.project-update__status {
+  float: right; /* LTR */
+  font-size: 110%;
+}
+[dir="rtl"] .project-update__status {
+  float: left;
+}
+.project-update__status--not-supported {
+  float: left; /* LTR */
+}
+[dir="rtl"] .project-update__status--not-supported {
+  float: right;
+}
+.project-update__status--security-error {
+  font-weight: bold;
+  color: #970f00;
+}
+
+.project-update__status-icon {
+  padding-left: 0.5em; /* LTR */
+}
+[dir="rtl"] .project-update__status-icon {
+  padding-left: 0;
+  padding-right: 0.5em;
+}
+.project-update__details {
+  padding: 1em 1em 0.25em 1em;
+}
+
+.project-update__version {
+  padding: 1em 0;
+}
+.project-update__version-date {
+  white-space: nowrap;
+}
+.project-update__version-details {
+  padding-right: 0.5em; /* LTR */
+}
+[dir="rtl"] .project-update__version-details {
+  padding-left: 0.5em;
+  direction: ltr; /* Version numbers should always be LTR. */
+}
+.project-update__version-links {
+  text-align: right; /* LTR */
+  padding-right: 1em; /* LTR */
+  list-style-type: none;
+}
+[dir="rtl"] .project-update__version-links {
+  text-align: left;
+  padding-left: 1em;
+}
+.project-update__version--recommended-strong .project-update__version-title {
+  font-weight: bold;
+}
diff --git a/core/themes/stable/css/user/user.admin.css b/core/themes/stable/css/user/user.admin.css
new file mode 100644
index 0000000..10358c2
--- /dev/null
+++ b/core/themes/stable/css/user/user.admin.css
@@ -0,0 +1,22 @@
+/**
+ * @file
+ * Admin styling for the User module.
+ */
+
+/* Permissions page */
+.permissions .module {
+  font-weight: bold;
+}
+.permissions .permission {
+  padding-left: 1.5em; /* LTR */
+}
+[dir="rtl"] .permissions .permission {
+  padding-left: 0;
+  padding-right: 1.5em;
+}
+
+/* Account settings */
+.user-admin-settings .details-description {
+  font-size: 0.85em;
+  padding-bottom: .5em;
+}
diff --git a/core/themes/stable/css/user/user.icons.admin.css b/core/themes/stable/css/user/user.icons.admin.css
new file mode 100644
index 0000000..7df6ba2
--- /dev/null
+++ b/core/themes/stable/css/user/user.icons.admin.css
@@ -0,0 +1,15 @@
+/**
+ * @file
+ * Styling for the user module icons.
+ */
+
+/**
+ * Toolbar tab icon.
+ */
+.toolbar-bar .toolbar-icon-user:before {
+  background-image: url(../../../../misc/icons/bebebe/person.svg);
+}
+.toolbar-bar .toolbar-icon-user:active:before,
+.toolbar-bar .toolbar-icon-user.is-active:before {
+  background-image: url(../../../../misc/icons/ffffff/person.svg);
+}
diff --git a/core/themes/stable/css/user/user.module.css b/core/themes/stable/css/user/user.module.css
new file mode 100644
index 0000000..0b6afcf
--- /dev/null
+++ b/core/themes/stable/css/user/user.module.css
@@ -0,0 +1,21 @@
+/**
+ * @file
+ * Module styling for user module.
+ */
+.password-strength__title,
+.password-strength__text {
+  display: inline;
+}
+.password-strength__meter {
+  height: 0.75em;
+  margin-top: 0.5em;
+  background-color: lightgray;
+}
+.password-strength__indicator {
+  height: 100%;
+  width: 0;
+  background-color: gray;
+}
+.password-confirm-match {
+  visibility: hidden;
+}
diff --git a/core/themes/stable/css/views/views.module.css b/core/themes/stable/css/views/views.module.css
new file mode 100644
index 0000000..da7a342
--- /dev/null
+++ b/core/themes/stable/css/views/views.module.css
@@ -0,0 +1,19 @@
+/* table style column align */
+.views-align-left {
+  text-align: left;
+}
+.views-align-right {
+  text-align: right;
+}
+.views-align-center {
+  text-align: center;
+}
+/* Grid style column align. */
+.views-view-grid .views-col {
+  float: left;
+}
+.views-view-grid .views-row {
+  clear: both;
+  float: left;
+  width: 100%;
+}
diff --git a/core/themes/stable/css/views_ui/views_ui.admin.css b/core/themes/stable/css/views_ui/views_ui.admin.css
new file mode 100644
index 0000000..e932af1
--- /dev/null
+++ b/core/themes/stable/css/views_ui/views_ui.admin.css
@@ -0,0 +1,208 @@
+/**
+ * @file
+ * The .admin.css file is intended to only contain positioning and size
+ * declarations. For example: display, position, float, clear, and overflow.
+ */
+
+.views-admin ul,
+.views-admin menu,
+.views-admin dir {
+  padding: 0;
+}
+.views-admin pre {
+  margin-bottom: 0;
+  margin-top: 0;
+  white-space: pre-wrap;
+}
+.views-left-25 {
+  float: left; /* LTR */
+  width: 25%;
+}
+[dir="rtl"] .views-left-25 {
+  float: right;
+}
+.views-left-30 {
+  float: left; /* LTR */
+  width: 30%;
+}
+[dir="rtl"] .views-left-30 {
+  float: right;
+}
+.views-left-40 {
+  float: left; /* LTR */
+  width: 40%;
+}
+[dir="rtl"] .views-left-40 {
+  float: right;
+}
+.views-left-50 {
+  float: left; /* LTR */
+  width: 50%;
+}
+[dir="rtl"] .views-left-50 {
+  float: right;
+}
+.views-left-75 {
+  float: left; /* LTR */
+  width: 75%;
+}
+[dir="rtl"] .views-left-75 {
+  float: right;
+}
+.views-right-50 {
+  float: right; /* LTR */
+  width: 50%;
+}
+[dir="rtl"] .views-right-50 {
+  float: left;
+}
+.views-right-60 {
+  float: right; /* LTR */
+  width: 60%;
+}
+[dir="rtl"] .views-right-60 {
+  float: left;
+}
+.views-right-70 {
+  float: right; /* LTR */
+  width: 70%;
+}
+[dir="rtl"] .views-right-70 {
+  float: left;
+}
+.views-group-box .form-item {
+  margin-left: 3px;
+  margin-right: 3px;
+}
+
+/*
+ * The attachment details section, its tabs for each section and the buttons
+ * to add a new section
+ */
+.views-displays {
+  clear: both;
+}
+
+/* The tabs that switch between sections */
+.views-displays .tabs {
+  border-bottom: 0 none;
+  margin: 0;
+  overflow: visible;
+  padding: 0;
+}
+.views-displays .tabs > li {
+  border-right: 0 none; /* LTR */
+  float: left; /* LTR */
+  padding: 0;
+}
+[dir="rtl"] .views-displays .tabs > li {
+  float: right;
+  border-left: 0 none;
+  border-right: 1px solid #bfbfbf;
+}
+.views-displays .tabs .open > a {
+  position: relative;
+  z-index: 51;
+}
+.views-displays .tabs .views-display-deleted-link {
+  text-decoration: line-through;
+}
+.views-display-deleted > details > summary,
+.views-display-deleted .details-wrapper > .views-ui-display-tab-bucket > *,
+.views-display-deleted .views-display-columns {
+  opacity: 0.25;
+}
+.views-display-disabled > details > summary,
+.views-display-disabled .details-wrapper > .views-ui-display-tab-bucket > *,
+.views-display-disabled .views-display-columns {
+  opacity: 0.5;
+}
+.views-display-tab .details-wrapper > .views-ui-display-tab-bucket .actions {
+  opacity: 1.0;
+}
+.views-displays .tabs .add {
+  position: relative;
+}
+.views-displays .tabs .action-list {
+  left: 0; /* LTR */
+  margin: 0;
+  position: absolute;
+  top: 23px;
+  z-index: 50;
+}
+[dir="rtl"] .views-displays .tabs .action-list {
+  left: auto;
+  right: 0;
+}
+.views-displays .tabs .action-list li {
+  display: block;
+}
+.views-display-columns .details-wrapper {
+  padding: 0;
+}
+.views-display-column {
+  box-sizing: border-box;
+}
+.views-display-columns > * {
+  margin-bottom: 2em;
+}
+
+@media screen and (min-width:45em) { /* 720px */
+  .views-display-columns > * {
+    float: left; /* LTR */
+    margin-left: 2%; /* LTR */
+    margin-bottom: 0;
+    width: 32%;
+  }
+  [dir="rtl"] .views-display-columns > * {
+    float: right;
+    margin-left: 0;
+    margin-right: 2%;
+  }
+  .views-display-columns > *:first-child {
+    margin-left: 0; /* LTR */
+  }
+  [dir="rtl"] .views-display-columns > *:first-child {
+    margin-right: 0;
+  }
+}
+
+.views-ui-dialog .scroll {
+  overflow: auto;
+  padding: 1em;
+}
+.views-filterable-options-controls {
+  display: none;
+}
+.views-ui-dialog .views-filterable-options-controls {
+  display: inline;
+}
+
+/* Don't let the messages overwhelm the modal */
+.views-ui-dialog .views-messages {
+  max-height: 200px;
+  overflow: auto;
+}
+.views-display-setting .label,
+.views-display-setting .views-ajax-link {
+  float: left; /* LTR */
+}
+[dir="rtl"] .views-display-setting .label,
+[dir="rtl"] .views-display-setting .views-ajax-link {
+  float: right;
+}
+.form-item-options-value-all {
+  display: none;
+}
+.js-only {
+  display: none;
+}
+html.js .js-only {
+  display: inherit;
+}
+html.js span.js-only {
+  display: inline;
+}
+.js .views-edit-view .dropbutton-wrapper {
+  width: auto;
+}
diff --git a/core/themes/stable/css/views_ui/views_ui.admin.theme.css b/core/themes/stable/css/views_ui/views_ui.admin.theme.css
new file mode 100644
index 0000000..0744f04
--- /dev/null
+++ b/core/themes/stable/css/views_ui/views_ui.admin.theme.css
@@ -0,0 +1,839 @@
+/**
+ * @file
+ * The .admin.theme.css file is intended to contain presentation declarations
+ * including images, borders, colors, and fonts.
+ */
+
+.views-admin .links {
+  list-style: none outside none;
+  margin: 0;
+}
+.views-admin a:hover {
+  text-decoration: none;
+}
+.box-padding {
+  padding-left: 12px;
+  padding-right: 12px;
+}
+.box-margin {
+  margin: 12px 12px 0 12px;
+}
+.views-admin .icon {
+  height: 16px;
+  width: 16px;
+}
+.views-admin .icon,
+.views-admin .icon-text {
+  background-attachment: scroll;
+  background-image: url(../../../../modules/views_ui/images/sprites.png);
+  background-position: left top; /* LTR */
+  background-repeat: no-repeat;
+}
+[dir="rtl"] .views-admin .icon,
+[dir="rtl"] .views-admin .icon-text {
+  background-position: right top;
+}
+.views-admin a.icon {
+  background: linear-gradient(-90deg, #fff 0, #e8e8e8 100%) no-repeat, repeat-y;
+  border: 1px solid #ddd;
+  border-radius: 4px;
+  box-shadow: 0 0 0 rgba(0,0,0,0.3333) inset;
+}
+.views-admin a.icon:hover {
+  border-color: #d0d0d0;
+  box-shadow: 0 0 1px rgba(0,0,0,0.3333) inset;
+}
+.views-admin a.icon:active {
+  border-color: #c0c0c0;
+}
+.views-admin span.icon {
+  float: left; /* LTR */
+  position: relative;
+}
+[dir="rtl"] .views-admin span.icon {
+  float: right;
+}
+.views-admin .icon.compact {
+  display: block;
+  overflow: hidden;
+  direction: ltr;
+  text-indent: -9999px;
+}
+
+/* Targets any element with an icon -> text combo */
+.views-admin .icon-text {
+  padding-left: 19px; /* LTR */
+}
+[dir="rtl"] .views-admin .icon-text {
+  padding-left: 0;
+  padding-right: 19px;
+}
+.views-admin .icon.linked {
+  background-position: center -153px;
+}
+.views-admin .icon.unlinked {
+  background-position: center -195px;
+}
+.views-admin .icon.add {
+  background-position: center 3px;
+}
+.views-admin a.icon.add {
+  background-position: center 3px, left top; /* LTR */
+}
+[dir="rtl"] .views-admin a.icon.add {
+  background-position: center 3px, right top;
+}
+.views-admin .icon.delete {
+  background-position: center -52px;
+}
+.views-admin a.icon.delete {
+  background-position: center -52px, left top; /* LTR */
+}
+[dir="rtl"] .views-admin a.icon.delete {
+  background-position: center -52px, right top;
+}
+.views-admin .icon.rearrange {
+  background-position: center -111px;
+}
+.views-admin a.icon.rearrange {
+  background-position: center -111px, left top; /* LTR */
+}
+[dir="rtl"] .views-admin a.icon.rearrange {
+  background-position: center -111px, right top;
+}
+.views-displays .tabs a:hover > .icon.add {
+  background-position: center -25px;
+}
+.views-displays .tabs .open a:hover > .icon.add {
+  background-position: center 3px;
+}
+details.box-padding {
+  border: none;
+}
+.views-admin details details {
+  margin-bottom: 0;
+}
+.form-item {
+  margin-top: 9px;
+  padding-bottom: 0;
+  padding-top: 0;
+}
+.form-type-checkbox {
+  margin-top: 6px;
+}
+.form-checkbox,
+.form-radio {
+  vertical-align: baseline;
+}
+
+.container-inline {
+  padding-top: 15px;
+  padding-bottom: 15px;
+}
+.container-inline > * + *,
+.container-inline .details-wrapper > * + * {
+  padding-left: 4px; /* LTR */
+}
+[dir="rtl"] .container-inline > * + *,
+[dir="rtl"] .container-inline .details-wrapper > * + * {
+  padding-left: 0;
+  padding-right: 4px;
+}
+.views-admin details details.container-inline {
+  margin-bottom: 1em;
+  margin-top: 1em;
+  padding-top: 0;
+}
+.views-admin details details.container-inline > .details-wrapper {
+  padding-bottom: 0;
+}
+/* Indent form elements so they're directly underneath the label of the checkbox that reveals them */
+.views-admin .form-type-checkbox + .form-wrapper {
+  margin-left: 16px; /* LTR */
+}
+[dir="rtl"] .views-admin .form-type-checkbox + .form-wrapper {
+  margin-left: 0;
+  margin-right: 16px;
+}
+
+/* Hide 'remove' checkboxes. */
+.views-remove-checkbox {
+    display: none;
+}
+
+/* sizes the labels of checkboxes and radio button to the height of the text */
+.views-admin .form-type-checkbox label,
+.views-admin .form-type-radio label {
+  line-height: 2;
+}
+.views-admin-dependent .form-item {
+  margin-bottom: 6px;
+  margin-top: 6px;
+}
+.views-ui-view-title {
+  font-weight: bold;
+  margin-top: 0;
+}
+.view-changed {
+  margin-bottom: 21px;
+}
+.views-admin .unit-title {
+  font-size: 15px;
+  line-height: 1.6154;
+  margin-bottom: 0;
+  margin-top: 18px;
+}
+
+/* These header classes are ambiguous and should be scoped to th elements */
+.views-ui-name {
+  width: 18%;
+}
+.views-ui-description {
+  width: 26%;
+}
+.views-ui-tag {
+  width: 8%;
+}
+.views-ui-path {
+  width: auto;
+}
+.views-ui-operations {
+  width: 24%;
+}
+
+/**
+ * I wish this didn't have to be so specific
+ */
+.form-item-description-enable + .form-item-description {
+  margin-top: 0;
+}
+.form-item-description-enable label {
+  font-weight: bold;
+}
+.form-item-page-create,
+.form-item-block-create {
+  margin-top: 13px;
+}
+.form-item-page-create label,
+.form-item-block-create label,
+.form-item-rest-export-create label {
+  font-weight: bold;
+}
+
+/* This makes the form elements after the "Display Format" label flow underneath the label */
+.form-item-page-style-style-plugin > label,
+.form-item-block-style-style-plugin > label {
+  display: block;
+}
+.views-attachment .options-set label {
+  font-weight: normal;
+}
+
+/* Styling for the form that allows views filters to be rearranged. */
+.group-populated {
+  display: none;
+}
+td.group-title {
+  font-weight: bold;
+}
+.views-ui-dialog td.group-title {
+  margin: 0;
+  padding: 0;
+}
+.views-ui-dialog td.group-title span {
+  display: block;
+  height: 1px;
+  overflow: hidden;
+}
+.group-message .form-submit,
+.views-remove-group-link,
+.views-add-group {
+  float: right; /* LTR */
+  clear: both;
+}
+[dir="rtl"] .group-message .form-submit,
+[dir="rtl"] .views-remove-group-link,
+[dir="rtl"] .views-add-group {
+  float: left;
+}
+.views-operator-label {
+  font-style: italic;
+  font-weight: bold;
+  padding-left: 0.5em; /* LTR */
+  text-transform: uppercase;
+}
+[dir="rtl"] .views-operator-label {
+  padding-left: 0;
+  padding-right: 0.5em;
+}
+.grouped-description,
+.exposed-description {
+  float: left; /* LTR */
+  padding-top: 3px;
+  padding-right: 10px; /* LTR */
+}
+[dir="rtl"] .grouped-description,
+[dir="rtl"] .exposed-description {
+  float: right;
+  padding-left: 10px;
+  padding-right: 0;
+}
+.views-displays {
+  border: 1px solid #ccc;
+  padding-bottom: 36px;
+}
+.views-display-top {
+  background-color: #e1e2dc;
+  border-bottom: 1px solid #ccc;
+  padding: 8px 8px 3px;
+  position: relative;
+}
+.views-display-top .tabs {
+  margin-right: 18em; /* LTR */
+}
+[dir="rtl"] .views-display-top .tabs {
+  margin-left: 18em;
+  margin-right: 0;
+}
+.views-display-top .tabs > li {
+  margin-right: 6px; /* LTR */
+  padding-left: 0; /* LTR */
+}
+[dir="rtl"] .views-display-top .tabs > li {
+  margin-left: 6px;
+  margin-right: 0.3em;
+  padding-right: 0;
+}
+.views-display-top .tabs > li:last-child {
+  margin-right: 0; /* LTR */
+}
+[dir="rtl"] .views-display-top .tabs > li:last-child {
+  margin-left: 0;
+  margin-right: 0.3em;
+}
+.form-edit .form-actions {
+  background-color: #e1e2dc;
+  border-right: 1px solid #ccc;
+  border-bottom: 1px solid #ccc;
+  border-left: 1px solid #ccc;
+  margin-top: 0;
+  padding: 8px 12px;
+}
+.views-displays .tabs.secondary {
+  margin-right: 200px; /* LTR */
+  border: 0;
+}
+[dir="rtl"] .views-displays .tabs.secondary {
+  margin-left: 200px;
+  margin-right: 0;
+}
+.views-displays .tabs.secondary li,
+.views-displays .tabs.secondary li.is-active {
+  background: transparent;
+  border: 0;
+  padding: 0;
+  width: auto;
+}
+.views-displays .tabs li.add ul.action-list li{
+  margin: 0;
+}
+.views-displays .tabs.secondary li {
+  margin: 0 5px 5px 6px; /* LTR */
+}
+[dir="rtl"] .views-displays .tabs.secondary li {
+  margin-left: 5px;
+  margin-right: 6px;
+}
+.views-displays .tabs.secondary .tabs__tab + .tabs__tab {
+  border-top: 0;
+}
+.views-displays .tabs li.tabs__tab:hover {
+  border: 0;
+  padding-left: 0; /* LTR */
+}
+[dir="rtl"] .views-displays .tabs li.tabs__tab:hover {
+  padding-left: 15px;
+  padding-right: 0;
+}
+.views-displays .tabs.secondary a {
+  border: 1px solid #cbcbcb;
+  border-radius: 7px;
+  display: inline-block;
+  font-size: small;
+  line-height: 1.3333;
+  padding: 3px 7px;
+}
+
+/* Display a red border if the display doesn't validate. */
+.views-displays .tabs li.is-active a.is-active.error,
+.views-displays .tabs .error {
+  border: 2px solid #ed541d;
+  padding: 1px 6px;
+}
+.views-displays .tabs a:focus {
+  outline: none;
+  text-decoration: underline;
+}
+.views-displays .tabs.secondary li a {
+  background-color: #fff;
+}
+.views-displays .tabs li a:hover,
+.views-displays .tabs li.is-active a,
+.views-displays .tabs li.is-active a.is-active {
+  background-color: #555;
+  color: #fff;
+}
+.views-displays .tabs .open > a {
+  background-color: #f1f1f1;
+  border-bottom: 1px solid transparent;
+  position: relative;
+}
+.views-displays .tabs .open > a:hover {
+  color: #0074bd;
+  background-color: #f1f1f1;
+}
+.views-displays .tabs .action-list  li {
+  background-color: #f1f1f1;
+  border-color: #cbcbcb;
+  border-style: solid;
+  border-width: 0 1px;
+  padding: 2px 9px;
+}
+.views-displays .tabs .action-list li:first-child {
+  border-width: 1px 1px 0;
+}
+.views-displays .action-list  li:last-child {
+  border-width: 0 1px 1px;
+}
+.views-displays .tabs .action-list  li:last-child {
+  border-width: 0 1px 1px;
+}
+.views-displays .tabs .action-list input.form-submit {
+  background: none repeat scroll 0 0 transparent;
+  border: medium none;
+  margin: 0;
+  padding: 0;
+}
+.views-displays .tabs .action-list input.form-submit:hover {
+  box-shadow: none;
+}
+.views-displays .tabs .action-list li:hover {
+  background-color: #ddd;
+}
+.edit-display-settings {
+  margin: 12px 12px 0 12px
+}
+.edit-display-settings-top.views-ui-display-tab-bucket {
+  border: 1px solid #f3f3f3;
+  line-height: 20px;
+  margin: 0 0 15px 0;
+  padding-top: 4px;
+  padding-bottom: 4px;
+  position: relative;
+}
+.views-display-column {
+  border: 1px solid #f3f3f3;
+}
+.views-display-column + .views-display-column {
+  margin-top: 0;
+}
+.view-preview-form .form-item-view-args,
+.view-preview-form .form-actions {
+  margin-top: 5px;
+}
+.view-preview-form .arguments-preview {
+  font-size: 1em;
+}
+.view-preview-form .arguments-preview,
+.view-preview-form .form-item-view-args {
+  margin-left: 10px; /* LTR */
+}
+[dir="rtl"] .view-preview-form .arguments-preview,
+[dir="rtl"] .view-preview-form .form-item-view-args {
+  margin-left: 0;
+  margin-right: 10px;
+}
+.view-preview-form .form-item-view-args label {
+  float: left; /* LTR */
+  font-weight: normal;
+  height: 6ex;
+  margin-right: 0.75em; /* LTR */
+}
+[dir="rtl"] .view-preview-form .form-item-view-args label {
+  float: right;
+  margin-left: 0.75em;
+  margin-right: 0.2em;
+}
+.form-item-live-preview,
+.form-item-view-args,
+.preview-submit-wrapper {
+  display: inline-block;
+}
+.form-item-live-preview,
+.view-preview-form .form-actions {
+  vertical-align: top;
+}
+@media screen and (min-width:45em) { /* 720px */
+  .view-preview-form .form-type-textfield .description {
+    white-space: nowrap;
+  }
+}
+
+/* These are the individual "buckets," or boxes, inside the display settings area */
+.views-ui-display-tab-bucket {
+  border-bottom: 1px solid #f3f3f3;
+  line-height: 20px;
+  margin: 0;
+  padding-top: 4px;
+  position: relative;
+}
+.views-ui-display-tab-bucket:last-of-type {
+  border-bottom: none;
+}
+.views-ui-display-tab-bucket + .views-ui-display-tab-bucket {
+  border-top: medium none;
+}
+.views-ui-display-tab-bucket__title,
+.views-ui-display-tab-bucket > .views-display-setting {
+  padding: 2px 6px 4px;
+}
+.views-ui-display-tab-bucket__title {
+  font-size: small;
+  margin: 0;
+}
+.views-ui-display-tab-bucket.access {
+  padding-top: 0;
+}
+.views-ui-display-tab-bucket.page-settings {
+  border-bottom: medium none;
+}
+.views-display-setting .views-ajax-link {
+  margin-left: 0.2083em;
+  margin-right: 0.2083em;
+}
+
+.views-ui-display-tab-setting > span {
+  margin-left: 0.5em; /* LTR */
+}
+[dir="rtl"] .views-ui-display-tab-setting > span {
+  margin-left: 0;
+  margin-right: 0.5em;
+}
+
+/** Applies an overridden(italics) font style to overridden buckets.
+ * The better way to implement this would be to add the overridden class
+ * to the bucket header when the bucket is overridden and style it as a
+ * generic icon classed element. For the moment, we'll style the bucket
+ * header specifically with the overridden font style.
+ */
+.views-ui-display-tab-setting.overridden,
+.views-ui-display-tab-bucket.overridden .views-ui-display-tab-bucket__title {
+  font-style: italic;
+}
+
+/* This is each row within one of the "boxes." */
+.views-ui-display-tab-bucket .views-display-setting {
+  color: #666;
+  font-size: 12px;
+  padding-bottom: 2px;
+}
+.views-ui-display-tab-bucket .views-display-setting:nth-of-type(even) {
+  background-color: #f3f5ee;
+}
+.views-ui-display-tab-actions.views-ui-display-tab-bucket .views-display-setting {
+  background-color: transparent;
+}
+.views-ui-display-tab-bucket .views-group-text {
+  margin-top: 6px;
+  margin-bottom: 6px;
+}
+.views-display-setting .label {
+  margin-right: 3px; /* LTR */
+}
+[dir="rtl"] .views-display-setting .label {
+  margin-left: 3px;
+  margin-right: 0;
+}
+.views-edit-view {
+  margin-bottom: 15px;
+}
+
+/* The contents of the popup dialog on the views edit form. */
+.views-filterable-options .form-type-checkbox {
+  border: 1px solid #ccc;
+  padding: 5px 8px;
+  border-top: none;
+}
+.views-filterable-options {
+  border-top:  1px solid #ccc;
+}
+.views-filterable-options .filterable-option.odd .form-type-checkbox {
+  background-color: #f3f4ee;
+}
+.filterable-option .form-item {
+  margin-bottom: 0;
+  margin-top: 0;
+}
+.views-filterable-options .form-type-checkbox .description {
+  margin-top: 0;
+  margin-bottom: 0;
+}
+.views-filterable-options-controls .form-item {
+  width: 30%;
+  margin: 0 0 0 2%; /* LTR */
+}
+[dir="rtl"] .views-filterable-options-controls .form-item {
+  margin: 0 2% 0 0;
+}
+.views-filterable-options-controls input,
+.views-filterable-options-controls select {
+  width: 100%;
+}
+.views-ui-dialog .ui-dialog-content {
+  padding: 0;
+}
+.views-ui-dialog .views-filterable-options {
+  margin-bottom: 10px;
+}
+.views-ui-dialog .views-add-form-selected.container-inline {
+  padding: 0;
+}
+.views-ui-dialog .views-add-form-selected.container-inline > div {
+  display: block;
+}
+.views-ui-dialog .form-item-selected {
+  margin: 0;
+  padding: 6px 16px;
+}
+.views-ui-dialog .views-override {
+  background-color: #f3f4ee;
+  padding: 8px 13px;
+}
+.views-ui-dialog.views-ui-dialog-scroll .ui-dialog-titlebar {
+  border: none;
+}
+.views-ui-dialog .views-offset-top {
+  border-bottom: 1px solid #CCC;
+}
+.views-ui-dialog .views-offset-bottom {
+  border-top: 1px solid #CCC;
+}
+.views-ui-dialog .views-override > * {
+  margin: 0;
+}
+.views-ui-dialog .views-progress-indicator {
+  color: #fff;
+  font-size: 11px;
+  position: absolute;
+  right: 10px; /* LTR */
+  top: 32px;
+}
+[dir="rtl"] .views-ui-dialog .views-progress-indicator {
+  left: 10px;
+  right: auto;
+}
+.views-ui-dialog .views-progress-indicator:before {
+  content: "\003C\00A0";
+}
+.views-ui-dialog .views-progress-indicator:after {
+  content: "\00A0\003E";
+}
+.views-ui-dialog details .item-list {
+  padding-left: 2em; /* LTR */
+}
+[dir="rtl"] .views-ui-dialog details .item-list {
+  padding-left: 0;
+  padding-right: 2em;
+}
+.views-ui-rearrange-filter-form table {
+  border-collapse: collapse;
+}
+.views-ui-rearrange-filter-form tr td[rowspan] {
+  border-color: #cdcdcd;
+  border-style: solid;
+  border-width: 0 1px 1px 1px;
+}
+.views-ui-rearrange-filter-form tr[id^="views-row"] {
+  border-right: 1px solid #cdcdcd; /* LTR */
+}
+[dir="rtl"] .views-ui-rearrange-filter-form tr[id^="views-row"] {
+  border-left: 1px solid #cdcdcd;
+  border-right: 0;
+}
+.views-ui-rearrange-filter-form .even td {
+  background-color: #f3f4ed;
+}
+.views-ui-rearrange-filter-form .views-group-title {
+  border-top: 1px solid #cdcdcd;
+}
+.views-ui-rearrange-filter-form .group-empty {
+  border-bottom: 1px solid #cdcdcd;
+}
+.form-item-options-expose-required,
+.form-item-options-expose-label,
+.form-item-options-expose-description {
+  margin-bottom: 6px;
+  margin-left: 18px; /* LTR */
+  margin-top: 6px;
+}
+[dir="rtl"] .form-item-options-expose-required,
+[dir="rtl"] .form-item-options-expose-label,
+[dir="rtl"] .form-item-options-expose-description {
+  margin-left: 0;
+  margin-right: 18px;
+}
+.views-preview-wrapper {
+  border: 1px solid #ccc;
+}
+.view-preview-form {
+  position: relative;
+}
+.view-preview-form__title {
+  background-color: #e1e2dc;
+  border-bottom: 1px solid #ccc;
+  margin-top: 0;
+  padding: 8px 12px;
+}
+.view-preview-form .form-item-live-preview {
+  position: absolute;
+  right: 12px;
+  top: 3px;
+  margin-top: 2px;
+  margin-left: 2px; /* LTR */
+}
+[dir="rtl"] .view-preview-form .form-item-live-preview {
+  right: auto;
+  left: 12px;
+  margin-left: 0;
+  margin-right: 2px;
+}
+.views-live-preview {
+  padding: 12px;
+}
+.views-live-preview .views-query-info {
+  overflow: auto;
+}
+.views-live-preview .section-title {
+  color: #818181;
+  display: inline-block;
+  font-size: 13px;
+  font-weight: normal;
+  line-height: 1.6154;
+  margin-bottom: 0;
+  margin-top: 0;
+}
+.views-live-preview .view > * {
+  margin-top: 18px;
+}
+.views-live-preview .preview-section {
+  border: 1px dashed #dedede;
+  margin: 0 -5px;
+  padding: 3px 5px;
+}
+.views-live-preview li.views-row + li.views-row {
+  margin-top: 18px;
+}
+
+/* The div.views-row is intentional and excludes li.views-row, for example */
+.views-live-preview div.views-row + div.views-row {
+  margin-top: 36px;
+}
+.views-query-info table {
+  border-collapse: separate;
+  border-color: #ddd;
+  border-spacing: 0;
+  margin: 10px 0;
+}
+.views-query-info table tr {
+  background-color: #f9f9f9;
+}
+.views-query-info table th,
+.views-query-info table td {
+  color: #666;
+  padding: 4px 10px;
+}
+.messages {
+  margin-bottom: 18px;
+  line-height: 1.4555;
+}
+.dropbutton-multiple {
+  position: absolute;
+}
+.dropbutton-widget {
+  position: relative;
+}
+.js .views-edit-view .dropbutton-wrapper .dropbutton .dropbutton-action > * {
+  font-size: 10px;
+}
+.js .dropbutton-wrapper .dropbutton .dropbutton-action > .ajax-progress-throbber {
+  position: absolute;
+  right: -5px; /* LTR */
+  top: -1px;
+  z-index: 2;
+}
+[dir="rtl"].js .dropbutton-wrapper .dropbutton .dropbutton-action > .ajax-progress-throbber {
+  left: -5px;
+  right: auto;
+}
+.js .dropbutton-wrapper.dropbutton-multiple.open .dropbutton-action:first-child a {
+  border-radius: 1.1em 0 0 0; /* LTR */
+}
+[dir="rtl"].js .dropbutton-wrapper.dropbutton-multiple.open .dropbutton-action:first-child a {
+  border-radius: 0 1.1em 0 0;
+}
+.js .dropbutton-wrapper.dropbutton-multiple.open .dropbutton-action:last-child a {
+  border-radius: 0 0 0 1.1em; /* LTR */
+}
+[dir="rtl"].js .dropbutton-wrapper.dropbutton-multiple.open .dropbutton-action:last-child a {
+  border-radius: 0 0 1.1em 0;
+}
+.views-display-top .dropbutton-wrapper {
+  position: absolute;
+  right: 12px; /* LTR */
+  top: 7px;
+}
+[dir="rtl"] .views-display-top .dropbutton-wrapper {
+  left: 12px;
+  right: auto;
+}
+.views-display-top .dropbutton-wrapper .dropbutton-widget .dropbutton-action a {
+  width: auto;
+}
+
+.views-ui-display-tab-bucket .dropbutton-wrapper {
+  position: absolute;
+  right: 5px; /* LTR */
+  top: 4px;
+}
+[dir="rtl"] .views-ui-display-tab-bucket .dropbutton-wrapper {
+  left: 5px;
+  right: auto;
+}
+.views-ui-display-tab-bucket .dropbutton-wrapper .dropbutton-widget .dropbutton-action a {
+  width: auto;
+}
+.views-ui-display-tab-actions .dropbutton-wrapper li a,
+.views-ui-display-tab-actions .dropbutton-wrapper input {
+  background: none;
+  border: medium;
+  font-family: inherit;
+  font-size: 12px;
+  padding-left: 12px; /* LTR */
+  margin-bottom: 0;
+}
+[dir="rtl"] .views-ui-display-tab-actions .dropbutton-wrapper li a,
+[dir="rtl"] .views-ui-display-tab-actions .dropbutton-wrapper input {
+  padding-left: 0.5em;
+  padding-right: 12px;
+}
+.views-ui-display-tab-actions .dropbutton-wrapper input:hover {
+  background: none;
+  border: none;
+}
+.views-list-section {
+  margin-bottom: 2em;
+}
+.form-textarea-wrapper,
+.form-item-options-content {
+  width: 100%;
+}
diff --git a/core/themes/stable/css/views_ui/views_ui.contextual.css b/core/themes/stable/css/views_ui/views_ui.contextual.css
new file mode 100644
index 0000000..13db2d2
--- /dev/null
+++ b/core/themes/stable/css/views_ui/views_ui.contextual.css
@@ -0,0 +1,57 @@
+/**
+ * @file
+ * The .contextual.css file is intended to contain styles that override declarations
+ * in the Contextual module.
+ */
+
+.views-live-preview .contextual-region-active {
+  outline: medium none;
+}
+.views-live-preview .contextual {
+  right: auto; /* LTR */
+  top: auto;
+}
+[dir="rtl"] .views-live-preview .contextual {
+  left: auto;
+}
+.js .views-live-preview .contextual {
+  display: inline;
+}
+.views-live-preview .contextual-links-trigger {
+  display: block;
+}
+.contextual .contextual-links {
+  border-radius: 0 4px 4px 4px; /* LTR */
+  min-width: 10em;
+  padding: 6px 6px 9px 6px;
+  right: auto; /* LTR */
+}
+[dir="rtl"] .contextual .contextual-links {
+  border-radius: 4px 0 4px 4px;
+  left: auto;
+}
+.contextual-links li a,
+.contextual-links li span {
+  padding-bottom: 0.25em;
+  padding-right: 0.1667em; /* LTR */
+  padding-top: 0.25em;
+}
+[dir="rtl"] .contextual-links li a,
+[dir="rtl"] .contextual-links li span {
+  padding-left: 0.1667em;
+  padding-right: 0;
+}
+.contextual-links li span {
+  font-weight: bold;
+}
+.contextual-links li a {
+  margin: 0.25em 0;
+  padding-left: 1em; /* LTR */
+}
+[dir="rtl"] .contextual-links li a {
+  padding-left: 0.1667em;
+  padding-right: 1em;
+}
+.contextual-links li a:hover {
+  background-color: #badbec;
+}
diff --git a/core/themes/stable/js/block/block.admin.js b/core/themes/stable/js/block/block.admin.js
new file mode 100644
index 0000000..20be7fd
--- /dev/null
+++ b/core/themes/stable/js/block/block.admin.js
@@ -0,0 +1,97 @@
+/**
+ * @file
+ * Block admin behaviors.
+ */
+
+(function ($, Drupal) {
+
+  "use strict";
+
+  /**
+   * Filters the block list by a text input search string.
+   *
+   * The text input will have the selector `input.block-filter-text`.
+   *
+   * The target element to do searching in will be in the selector
+   * `input.block-filter-text[data-element]`
+   *
+   * The text source where the text should be found will have the selector
+   * `.block-filter-text-source`
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches the behavior for the block filtering.
+   */
+  Drupal.behaviors.blockFilterByText = {
+    attach: function (context, settings) {
+      var $input = $('input.block-filter-text').once('block-filter-text');
+      var $table = $($input.attr('data-element'));
+      var $filter_rows;
+
+      /**
+       * Filters the block list.
+       *
+       * @param {jQuery.Event} e
+       *   The jQuery event for the keyup event that triggered the filter.
+       */
+      function filterBlockList(e) {
+        var query = $(e.target).val().toLowerCase();
+
+        /**
+         * Shows or hides the block entry based on the query.
+         *
+         * @param {number} index
+         *   The index in the loop, as provided by `jQuery.each`
+         * @param {HTMLElement} label
+         *   The label of the block.
+         */
+        function toggleBlockEntry(index, label) {
+          var $label = $(label);
+          var $row = $label.parent().parent();
+          var textMatch = $label.text().toLowerCase().indexOf(query) !== -1;
+          $row.toggle(textMatch);
+        }
+
+        // Filter if the length of the query is at least 2 characters.
+        if (query.length >= 2) {
+          $filter_rows.each(toggleBlockEntry);
+        }
+        else {
+          $filter_rows.each(function (index) {
+            $(this).parent().parent().show();
+          });
+        }
+      }
+
+      if ($table.length) {
+        $filter_rows = $table.find('div.block-filter-text-source');
+        $input.on('keyup', filterBlockList);
+      }
+    }
+  };
+
+  /**
+   * Highlights the block that was just placed into the block listing.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches the behavior for the block placement highlighting.
+   */
+  Drupal.behaviors.blockHighlightPlacement = {
+    attach: function (context, settings) {
+      if (settings.blockPlacement) {
+        $(context).find('[data-drupal-selector="edit-blocks"]').once('block-highlight').each(function () {
+          var $container = $(this);
+          // Just scrolling the document.body will not work in Firefox. The html
+          // element is needed as well.
+          $('html, body').animate({
+            scrollTop: $('.js-block-placed').offset().top - $container.offset().top + $container.scrollTop()
+          }, 500);
+        });
+      }
+    }
+  };
+
+}(jQuery, Drupal));
diff --git a/core/themes/stable/js/block/block.js b/core/themes/stable/js/block/block.js
new file mode 100644
index 0000000..5607b02
--- /dev/null
+++ b/core/themes/stable/js/block/block.js
@@ -0,0 +1,213 @@
+/**
+ * @file
+ * Block behaviors.
+ */
+
+(function ($, window) {
+
+  "use strict";
+
+  /**
+   * Provide the summary information for the block settings vertical tabs.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches the behavior for the block settings summaries.
+   */
+  Drupal.behaviors.blockSettingsSummary = {
+    attach: function () {
+      // The drupalSetSummary method required for this behavior is not available
+      // on the Blocks administration page, so we need to make sure this
+      // behavior is processed only if drupalSetSummary is defined.
+      if (typeof jQuery.fn.drupalSetSummary === 'undefined') {
+        return;
+      }
+
+      /**
+       * Create a summary for checkboxes in the provided context.
+       *
+       * @param {HTMLDocument|HTMLElement} context
+       *   A context where one would find checkboxes to summarize.
+       *
+       * @return {string}
+       *   A string with the summary.
+       */
+      function checkboxesSummary(context) {
+        var vals = [];
+        var $checkboxes = $(context).find('input[type="checkbox"]:checked + label');
+        var il = $checkboxes.length;
+        for (var i = 0; i < il; i++) {
+          vals.push($($checkboxes[i]).html());
+        }
+        if (!vals.length) {
+          vals.push(Drupal.t('Not restricted'));
+        }
+        return vals.join(', ');
+      }
+
+      $('[data-drupal-selector="edit-visibility-node-type"], [data-drupal-selector="edit-visibility-language"], [data-drupal-selector="edit-visibility-user-role"]').drupalSetSummary(checkboxesSummary);
+
+      $('[data-drupal-selector="edit-visibility-request-path"]').drupalSetSummary(function (context) {
+        var $pages = $(context).find('textarea[name="visibility[request_path][pages]"]');
+        if (!$pages.val()) {
+          return Drupal.t('Not restricted');
+        }
+        else {
+          return Drupal.t('Restricted to certain pages');
+        }
+      });
+    }
+  };
+
+  /**
+   * Move a block in the blocks table between regions via select list.
+   *
+   * This behavior is dependent on the tableDrag behavior, since it uses the
+   * objects initialized in that behavior to update the row.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches the tableDrag behaviour for blocks in block administration.
+   */
+  Drupal.behaviors.blockDrag = {
+    attach: function (context, settings) {
+      // tableDrag is required and we should be on the blocks admin page.
+      if (typeof Drupal.tableDrag === 'undefined' || typeof Drupal.tableDrag.blocks === 'undefined') {
+        return;
+      }
+
+      var table = $('#blocks');
+      // Get the blocks tableDrag object.
+      var tableDrag = Drupal.tableDrag.blocks;
+      // Add a handler for when a row is swapped, update empty regions.
+      tableDrag.row.prototype.onSwap = function (swappedRow) {
+        checkEmptyRegions(table, this);
+        updateLastPlaced(table, this);
+      };
+
+      // Add a handler so when a row is dropped, update fields dropped into
+      // new regions.
+      tableDrag.onDrop = function () {
+        var dragObject = this;
+        var $rowElement = $(dragObject.rowObject.element);
+        // Use "region-message" row instead of "region" row because
+        // "region-{region_name}-message" is less prone to regexp match errors.
+        var regionRow = $rowElement.prevAll('tr.region-message').get(0);
+        var regionName = regionRow.className.replace(/([^ ]+[ ]+)*region-([^ ]+)-message([ ]+[^ ]+)*/, '$2');
+        var regionField = $rowElement.find('select.block-region-select');
+        // Check whether the newly picked region is available for this block.
+        if (regionField.find('option[value=' + regionName + ']').length === 0) {
+          // If not, alert the user and keep the block in its old region
+          // setting.
+          window.alert(Drupal.t('The block cannot be placed in this region.'));
+          // Simulate that there was a selected element change, so the row is
+          // put back to from where the user tried to drag it.
+          regionField.trigger('change');
+        }
+
+        // Update region and weight fields if the region has been changed.
+        if (!regionField.is('.block-region-' + regionName)) {
+          var weightField = $rowElement.find('select.block-weight');
+          var oldRegionName = weightField[0].className.replace(/([^ ]+[ ]+)*block-weight-([^ ]+)([ ]+[^ ]+)*/, '$2');
+          regionField.removeClass('block-region-' + oldRegionName).addClass('block-region-' + regionName);
+          weightField.removeClass('block-weight-' + oldRegionName).addClass('block-weight-' + regionName);
+          regionField.val(regionName);
+        }
+
+        updateBlockWeights(table, regionName);
+      };
+
+      // Add the behavior to each region select list.
+      $(context).find('select.block-region-select').once('block-region-select')
+        .on('change', function (event) {
+          // Make our new row and select field.
+          var row = $(this).closest('tr');
+          var select = $(this);
+
+          // Find the correct region and insert the row as the last in the
+          // region.
+          table.find('.region-' + select[0].value + '-message').nextUntil('.region-message').eq(-1).before(row);
+
+          updateBlockWeights(table, select[0].value);
+          // Modify empty regions with added or removed fields.
+          checkEmptyRegions(table, row);
+          // Update last placed block indication.
+          updateLastPlaced(table, row);
+          // Show unsaved changes warning.
+          if (!tableDrag.changed) {
+            $(Drupal.theme('tableDragChangedWarning')).insertBefore(tableDrag.table).hide().fadeIn('slow');
+            tableDrag.changed = true;
+          }
+          // Remove focus from selectbox.
+          select.trigger('blur');
+        });
+
+      var updateLastPlaced = function ($table, rowObject) {
+        // Remove the color-success class from new block if applicable.
+        $table.find('.color-success').removeClass('color-success');
+
+        var $rowObject = $(rowObject);
+        if (!$rowObject.is('.drag-previous')) {
+          $table.find('.drag-previous').removeClass('drag-previous');
+          $rowObject.addClass('drag-previous');
+        }
+      };
+
+      /**
+       * Update block weights in the given region.
+       *
+       * @param {jQuery} $table
+       *   Table with draggable items.
+       * @param {string} region
+       *   Machine name of region containing blocks to update.
+       */
+      var updateBlockWeights = function ($table, region) {
+        // Calculate minimum weight.
+        var weight = -Math.round($table.find('.draggable').length / 2);
+        // Update the block weights.
+        $table.find('.region-' + region + '-message').nextUntil('.region-title')
+          .find('select.block-weight').val(function () {
+            // Increment the weight before assigning it to prevent using the
+            // absolute minimum available weight. This way we always have an
+            // unused upper and lower bound, which makes manually setting the
+            // weights easier for users who prefer to do it that way.
+            return ++weight;
+          });
+      };
+
+      /**
+       * Checks empty regions and toggles classes based on this.
+       *
+       * @param {jQuery} table
+       *   The jQuery object representing the table to inspect.
+       * @param {jQuery} rowObject
+       *   The jQuery object representing the table row.
+       */
+      var checkEmptyRegions = function (table, rowObject) {
+        table.find('tr.region-message').each(function () {
+          var $this = $(this);
+          // If the dragged row is in this region, but above the message row,
+          // swap it down one space.
+          if ($this.prev('tr').get(0) === rowObject.element) {
+            // Prevent a recursion problem when using the keyboard to move rows
+            // up.
+            if ((rowObject.method !== 'keyboard' || rowObject.direction === 'down')) {
+              rowObject.swap('after', this);
+            }
+          }
+          // This region has become empty.
+          if ($this.next('tr').is(':not(.draggable)') || $this.next('tr').length === 0) {
+            $this.removeClass('region-populated').addClass('region-empty');
+          }
+          // This region has become populated.
+          else if ($this.is('.region-empty')) {
+            $this.removeClass('region-empty').addClass('region-populated');
+          }
+        });
+      };
+    }
+  };
+
+})(jQuery, window);
diff --git a/core/themes/stable/js/block_content/block_content.js b/core/themes/stable/js/block_content/block_content.js
new file mode 100644
index 0000000..8ce1e65
--- /dev/null
+++ b/core/themes/stable/js/block_content/block_content.js
@@ -0,0 +1,57 @@
+/**
+ * @file
+ * Defines Javascript behaviors for the block_content module.
+ */
+
+(function ($) {
+
+  "use strict";
+
+  /**
+   * Sets summaries about revision and translation of block content.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches summary behaviour block content form tabs.
+   *
+   *   Specifically, it updates summaries to the revision information and the
+   *   translation options.
+   */
+  Drupal.behaviors.blockContentDetailsSummaries = {
+    attach: function (context) {
+      var $context = $(context);
+      $context.find('.block-content-form-revision-information').drupalSetSummary(function (context) {
+        var $revisionContext = $(context);
+        var revisionCheckbox = $revisionContext.find('.js-form-item-revision input');
+
+        // Return 'New revision' if the 'Create new revision' checkbox is checked,
+        // or if the checkbox doesn't exist, but the revision log does. For users
+        // without the "Administer content" permission the checkbox won't appear,
+        // but the revision log will if the content type is set to auto-revision.
+        if (revisionCheckbox.is(':checked') || (!revisionCheckbox.length && $revisionContext.find('.js-form-item-revision-log textarea').length)) {
+          return Drupal.t('New revision');
+        }
+
+        return Drupal.t('No revision');
+      });
+
+      $context.find('fieldset.block-content-translation-options').drupalSetSummary(function (context) {
+        var $translationContext = $(context);
+        var translate;
+        var $checkbox = $translationContext.find('.js-form-item-translation-translate input');
+
+        if ($checkbox.size()) {
+          translate = $checkbox.is(':checked') ? Drupal.t('Needs to be updated') : Drupal.t('Does not need to be updated');
+        }
+        else {
+          $checkbox = $translationContext.find('.js-form-item-translation-retranslate input');
+          translate = $checkbox.is(':checked') ? Drupal.t('Flag other translations as outdated') : Drupal.t('Do not flag other translations as outdated');
+        }
+
+        return translate;
+      });
+    }
+  };
+
+})(jQuery);
diff --git a/core/themes/stable/js/book/book.js b/core/themes/stable/js/book/book.js
new file mode 100644
index 0000000..249f5a6
--- /dev/null
+++ b/core/themes/stable/js/book/book.js
@@ -0,0 +1,37 @@
+/**
+ * @file
+ * Javascript behaviors for the Book module.
+ */
+
+(function ($) {
+
+  "use strict";
+
+  /**
+   * Adds summaries to the book outline form.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches summary behavior to book outline forms.
+   */
+  Drupal.behaviors.bookDetailsSummaries = {
+    attach: function (context) {
+      $(context).find('.book-outline-form').drupalSetSummary(function (context) {
+        var $select = $(context).find('.book-title-select');
+        var val = $select.val();
+
+        if (val === '0') {
+          return Drupal.t('Not in book');
+        }
+        else if (val === 'new') {
+          return Drupal.t('New book');
+        }
+        else {
+          return Drupal.checkPlain($select.find(':selected').text());
+        }
+      });
+    }
+  };
+
+})(jQuery);
diff --git a/core/themes/stable/js/ckeditor/ckeditor.admin.js b/core/themes/stable/js/ckeditor/ckeditor.admin.js
new file mode 100644
index 0000000..30b3dc2
--- /dev/null
+++ b/core/themes/stable/js/ckeditor/ckeditor.admin.js
@@ -0,0 +1,499 @@
+/**
+ * @file
+ * CKEditor button and group configuration user interface.
+ */
+
+(function ($, Drupal, _, CKEDITOR) {
+
+  "use strict";
+
+  Drupal.ckeditor = Drupal.ckeditor || {};
+
+  /**
+   * Sets config behaviour and creates config views for the CKEditor toolbar.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches admin behaviour to the CKEditor buttons.
+   * @prop {Drupal~behaviorDetach} detach
+   *   Detaches admin behaviour from the CKEditor buttons on 'unload'.
+   */
+  Drupal.behaviors.ckeditorAdmin = {
+    attach: function (context) {
+      // Process the CKEditor configuration fragment once.
+      var $configurationForm = $(context).find('.ckeditor-toolbar-configuration').once('ckeditor-configuration');
+      if ($configurationForm.length) {
+        var $textarea = $configurationForm
+          // Hide the textarea that contains the serialized representation of the
+          // CKEditor configuration.
+          .find('.js-form-item-editor-settings-toolbar-button-groups')
+          .hide()
+          // Return the textarea child node from this expression.
+          .find('textarea');
+
+        // The HTML for the CKEditor configuration is assembled on the server
+        // and sent to the client as a serialized DOM fragment.
+        $configurationForm.append(drupalSettings.ckeditor.toolbarAdmin);
+
+        // Create a configuration model.
+        var model = Drupal.ckeditor.models.Model = new Drupal.ckeditor.Model({
+          $textarea: $textarea,
+          activeEditorConfig: JSON.parse($textarea.val()),
+          hiddenEditorConfig: drupalSettings.ckeditor.hiddenCKEditorConfig
+        });
+
+        // Create the configuration Views.
+        var viewDefaults = {
+          model: model,
+          el: $('.ckeditor-toolbar-configuration')
+        };
+        Drupal.ckeditor.views = {
+          controller: new Drupal.ckeditor.ControllerView(viewDefaults),
+          visualView: new Drupal.ckeditor.VisualView(viewDefaults),
+          keyboardView: new Drupal.ckeditor.KeyboardView(viewDefaults),
+          auralView: new Drupal.ckeditor.AuralView(viewDefaults)
+        };
+      }
+    },
+    detach: function (context, settings, trigger) {
+      // Early-return if the trigger for detachment is something else than
+      // unload.
+      if (trigger !== 'unload') {
+        return;
+      }
+
+      // We're detaching because CKEditor as text editor has been disabled; this
+      // really means that all CKEditor toolbar buttons have been removed.
+      // Hence,all editor features will be removed, so any reactions from
+      // filters will be undone.
+      var $configurationForm = $(context).find('.ckeditor-toolbar-configuration').findOnce('ckeditor-configuration');
+      if ($configurationForm.length && Drupal.ckeditor.models && Drupal.ckeditor.models.Model) {
+        var config = Drupal.ckeditor.models.Model.toJSON().activeEditorConfig;
+        var buttons = Drupal.ckeditor.views.controller.getButtonList(config);
+        var $activeToolbar = $('.ckeditor-toolbar-configuration').find('.ckeditor-toolbar-active');
+        for (var i = 0; i < buttons.length; i++) {
+          $activeToolbar.trigger('CKEditorToolbarChanged', ['removed', buttons[i]]);
+        }
+      }
+    }
+  };
+
+  /**
+   * CKEditor configuration UI methods of Backbone objects.
+   *
+   * @namespace
+   */
+  Drupal.ckeditor = {
+
+    /**
+     * A hash of View instances.
+     *
+     * @type {object}
+     */
+    views: {},
+
+    /**
+     * A hash of Model instances.
+     *
+     * @type {object}
+     */
+    models: {},
+
+    /**
+     * Translates changes in CKEditor config DOM structure to the config model.
+     *
+     * If the button is moved within an existing group, the DOM structure is
+     * simply translated to a configuration model. If the button is moved into a
+     * new group placeholder, then a process is launched to name that group
+     * before the button move is translated into configuration.
+     *
+     * @param {Backbone.View} view
+     *   The Backbone View that invoked this function.
+     * @param {jQuery} $button
+     *   A jQuery set that contains an li element that wraps a button element.
+     * @param {function} callback
+     *   A callback to invoke after the button group naming modal dialog has
+     *   been closed.
+     *
+     */
+    registerButtonMove: function (view, $button, callback) {
+      var $group = $button.closest('.ckeditor-toolbar-group');
+
+      // If dropped in a placeholder button group, the user must name it.
+      if ($group.hasClass('placeholder')) {
+        if (view.isProcessing) {
+          return;
+        }
+        view.isProcessing = true;
+
+        Drupal.ckeditor.openGroupNameDialog(view, $group, callback);
+      }
+      else {
+        view.model.set('isDirty', true);
+        callback(true);
+      }
+    },
+
+    /**
+     * Translates changes in CKEditor config DOM structure to the config model.
+     *
+     * Each row has a placeholder group at the end of the row. A user may not
+     * move an existing button group past the placeholder group at the end of a
+     * row.
+     *
+     * @param {Backbone.View} view
+     *   The Backbone View that invoked this function.
+     * @param {jQuery} $group
+     *   A jQuery set that contains an li element that wraps a group of buttons.
+     */
+    registerGroupMove: function (view, $group) {
+      // Remove placeholder classes if necessary.
+      var $row = $group.closest('.ckeditor-row');
+      if ($row.hasClass('placeholder')) {
+        $row.removeClass('placeholder');
+      }
+      // If there are any rows with just a placeholder group, mark the row as a
+      // placeholder.
+      $row.parent().children().each(function () {
+        $row = $(this);
+        if ($row.find('.ckeditor-toolbar-group').not('.placeholder').length === 0) {
+          $row.addClass('placeholder');
+        }
+      });
+      view.model.set('isDirty', true);
+    },
+
+    /**
+     * Opens a dialog with a form for changing the title of a button group.
+     *
+     * @param {Backbone.View} view
+     *   The Backbone View that invoked this function.
+     * @param {jQuery} $group
+     *   A jQuery set that contains an li element that wraps a group of buttons.
+     * @param {function} callback
+     *   A callback to invoke after the button group naming modal dialog has
+     *   been closed.
+     */
+    openGroupNameDialog: function (view, $group, callback) {
+      callback = callback || function () {};
+
+      /**
+       * Validates the string provided as a button group title.
+       *
+       * @param {HTMLElement} form
+       *   The form DOM element that contains the input with the new button
+       *   group title string.
+       *
+       * @return {bool}
+       *   Returns true when an error exists, otherwise returns false.
+       */
+      function validateForm(form) {
+        if (form.elements[0].value.length === 0) {
+          var $form = $(form);
+          if (!$form.hasClass('errors')) {
+            $form
+              .addClass('errors')
+              .find('input')
+              .addClass('error')
+              .attr('aria-invalid', 'true');
+            $('<div class=\"description\" >' + Drupal.t('Please provide a name for the button group.') + '</div>').insertAfter(form.elements[0]);
+          }
+          return true;
+        }
+        return false;
+      }
+
+      /**
+       * Attempts to close the dialog; Validates user input.
+       *
+       * @param {string} action
+       *   The dialog action chosen by the user: 'apply' or 'cancel'.
+       * @param {HTMLElement} form
+       *   The form DOM element that contains the input with the new button
+       *   group title string.
+       */
+      function closeDialog(action, form) {
+
+        /**
+         * Closes the dialog when the user cancels or supplies valid data.
+         */
+        function shutdown() {
+          dialog.close(action);
+
+          // The processing marker can be deleted since the dialog has been
+          // closed.
+          delete view.isProcessing;
+        }
+
+        /**
+         * Applies a string as the name of a CKEditor button group.
+         *
+         * @param {jQuery} $group
+         *   A jQuery set that contains an li element that wraps a group of
+         *   buttons.
+         * @param {string} name
+         *   The new name of the CKEditor button group.
+         */
+        function namePlaceholderGroup($group, name) {
+          // If it's currently still a placeholder, then that means we're
+          // creating a new group, and we must do some extra work.
+          if ($group.hasClass('placeholder')) {
+            // Remove all whitespace from the name, lowercase it and ensure
+            // HTML-safe encoding, then use this as the group ID for CKEditor
+            // configuration UI accessibility purposes only.
+            var groupID = 'ckeditor-toolbar-group-aria-label-for-' + Drupal.checkPlain(name.toLowerCase().replace(/\s/g, '-'));
+            $group
+              // Update the group container.
+              .removeAttr('aria-label')
+              .attr('data-drupal-ckeditor-type', 'group')
+              .attr('tabindex', 0)
+              // Update the group heading.
+              .children('.ckeditor-toolbar-group-name')
+              .attr('id', groupID)
+              .end()
+              // Update the group items.
+              .children('.ckeditor-toolbar-group-buttons')
+              .attr('aria-labelledby', groupID);
+          }
+
+          $group
+            .attr('data-drupal-ckeditor-toolbar-group-name', name)
+            .children('.ckeditor-toolbar-group-name')
+            .text(name);
+        }
+
+        // Invoke a user-provided callback and indicate failure.
+        if (action === 'cancel') {
+          shutdown();
+          callback(false, $group);
+          return;
+        }
+
+        // Validate that a group name was provided.
+        if (form && validateForm(form)) {
+          return;
+        }
+
+        // React to application of a valid group name.
+        if (action === 'apply') {
+          shutdown();
+          // Apply the provided name to the button group label.
+          namePlaceholderGroup($group, Drupal.checkPlain(form.elements[0].value));
+          // Remove placeholder classes so that new placeholders will be
+          // inserted.
+          $group.closest('.ckeditor-row.placeholder').addBack().removeClass('placeholder');
+
+          // Invoke a user-provided callback and indicate success.
+          callback(true, $group);
+
+          // Signal that the active toolbar DOM structure has changed.
+          view.model.set('isDirty', true);
+        }
+      }
+
+      // Create a Drupal dialog that will get a button group name from the user.
+      var $ckeditorButtonGroupNameForm = $(Drupal.theme('ckeditorButtonGroupNameForm'));
+      var dialog = Drupal.dialog($ckeditorButtonGroupNameForm.get(0), {
+        title: Drupal.t('Button group name'),
+        dialogClass: 'ckeditor-name-toolbar-group',
+        resizable: false,
+        buttons: [
+          {
+            text: Drupal.t('Apply'),
+            click: function () {
+              closeDialog('apply', this);
+            },
+            primary: true
+          },
+          {
+            text: Drupal.t('Cancel'),
+            click: function () {
+              closeDialog('cancel');
+            }
+          }
+        ],
+        open: function () {
+          var form = this;
+          var $form = $(this);
+          var $widget = $form.parent();
+          $widget.find('.ui-dialog-titlebar-close').remove();
+          // Set a click handler on the input and button in the form.
+          $widget.on('keypress.ckeditor', 'input, button', function (event) {
+            // React to enter key press.
+            if (event.keyCode === 13) {
+              var $target = $(event.currentTarget);
+              var data = $target.data('ui-button');
+              var action = 'apply';
+              // Assume 'apply', but take into account that the user might have
+              // pressed the enter key on the dialog buttons.
+              if (data && data.options && data.options.label) {
+                action = data.options.label.toLowerCase();
+              }
+              closeDialog(action, form);
+              event.stopPropagation();
+              event.stopImmediatePropagation();
+              event.preventDefault();
+            }
+          });
+          // Announce to the user that a modal dialog is open.
+          var text = Drupal.t('Editing the name of the new button group in a dialog.');
+          if (typeof $group.attr('data-drupal-ckeditor-toolbar-group-name') !== 'undefined') {
+            text = Drupal.t('Editing the name of the "@groupName" button group in a dialog.', {
+              '@groupName': $group.attr('data-drupal-ckeditor-toolbar-group-name')
+            });
+          }
+          Drupal.announce(text);
+        },
+        close: function (event) {
+          // Automatically destroy the DOM element that was used for the dialog.
+          $(event.target).remove();
+        }
+      });
+      // A modal dialog is used because the user must provide a button group
+      // name or cancel the button placement before taking any other action.
+      dialog.showModal();
+
+      $(document.querySelector('.ckeditor-name-toolbar-group').querySelector('input'))
+        // When editing, set the "group name" input in the form to the current
+        // value.
+        .attr('value', $group.attr('data-drupal-ckeditor-toolbar-group-name'))
+        // Focus on the "group name" input in the form.
+        .trigger('focus');
+    }
+
+  };
+
+  /**
+   * Automatically shows/hides settings of buttons-only CKEditor plugins.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches show/hide behaviour to Plugin Settings buttons.
+   */
+  Drupal.behaviors.ckeditorAdminButtonPluginSettings = {
+    attach: function (context) {
+      var $context = $(context);
+      var $ckeditorPluginSettings = $context.find('#ckeditor-plugin-settings').once('ckeditor-plugin-settings');
+      if ($ckeditorPluginSettings.length) {
+        // Hide all button-dependent plugin settings initially.
+        $ckeditorPluginSettings.find('[data-ckeditor-buttons]').each(function () {
+          var $this = $(this);
+          if ($this.data('verticalTab')) {
+            $this.data('verticalTab').tabHide();
+          }
+          else {
+            // On very narrow viewports, Vertical Tabs are disabled.
+            $this.hide();
+          }
+          $this.data('ckeditorButtonPluginSettingsActiveButtons', []);
+        });
+
+        // Whenever a button is added or removed, check if we should show or
+        // hide the corresponding plugin settings. (Note that upon
+        // initialization, each button that already is part of the toolbar still
+        // is considered "added", hence it also works correctly for buttons that
+        // were added previously.)
+        $context
+          .find('.ckeditor-toolbar-active')
+          .off('CKEditorToolbarChanged.ckeditorAdminPluginSettings')
+          .on('CKEditorToolbarChanged.ckeditorAdminPluginSettings', function (event, action, button) {
+            var $pluginSettings = $ckeditorPluginSettings
+              .find('[data-ckeditor-buttons~=' + button + ']');
+
+            // No settings for this button.
+            if ($pluginSettings.length === 0) {
+              return;
+            }
+
+            var verticalTab = $pluginSettings.data('verticalTab');
+            var activeButtons = $pluginSettings.data('ckeditorButtonPluginSettingsActiveButtons');
+            if (action === 'added') {
+              activeButtons.push(button);
+              // Show this plugin's settings if >=1 of its buttons are active.
+              if (verticalTab) {
+                verticalTab.tabShow();
+              }
+              else {
+                // On very narrow viewports, Vertical Tabs remain fieldsets.
+                $pluginSettings.show();
+              }
+
+            }
+            else {
+              // Remove this button from the list of active buttons.
+              activeButtons.splice(activeButtons.indexOf(button), 1);
+              // Show this plugin's settings 0 of its buttons are active.
+              if (activeButtons.length === 0) {
+                if (verticalTab) {
+                  verticalTab.tabHide();
+                }
+                else {
+                  // On very narrow viewports, Vertical Tabs are disabled.
+                  $pluginSettings.hide();
+                }
+              }
+            }
+            $pluginSettings.data('ckeditorButtonPluginSettingsActiveButtons', activeButtons);
+          });
+      }
+    }
+  };
+
+  /**
+   * Themes a blank CKEditor row.
+   *
+   * @return {string}
+   *   A HTML string for a CKEditor row.
+   */
+  Drupal.theme.ckeditorRow = function () {
+    return '<li class="ckeditor-row placeholder" role="group"><ul class="ckeditor-toolbar-groups clearfix"></ul></li>';
+  };
+
+  /**
+   * Themes a blank CKEditor button group.
+   *
+   * @return {string}
+   *   A HTML string for a CKEditor button group.
+   */
+  Drupal.theme.ckeditorToolbarGroup = function () {
+    var group = '';
+    group += '<li class="ckeditor-toolbar-group placeholder" role="presentation" aria-label="' + Drupal.t('Place a button to create a new button group.') + '">';
+    group += '<h3 class="ckeditor-toolbar-group-name">' + Drupal.t('New group') + '</h3>';
+    group += '<ul class="ckeditor-buttons ckeditor-toolbar-group-buttons" role="toolbar" data-drupal-ckeditor-button-sorting="target"></ul>';
+    group += '</li>';
+    return group;
+  };
+
+  /**
+   * Themes a form for changing the title of a CKEditor button group.
+   *
+   * @return {string}
+   *   A HTML string for the form for the title of a CKEditor button group.
+   */
+  Drupal.theme.ckeditorButtonGroupNameForm = function () {
+    return '<form><input name="group-name" required="required"></form>';
+  };
+
+  /**
+   * Themes a button that will toggle the button group names in active config.
+   *
+   * @return {string}
+   *   A HTML string for the button to toggle group names.
+   */
+  Drupal.theme.ckeditorButtonGroupNamesToggle = function () {
+    return '<a class="ckeditor-groupnames-toggle" role="button" aria-pressed="false"></a>';
+  };
+
+  /**
+   * Themes a button that will prompt the user to name a new button group.
+   *
+   * @return {string}
+   *   A HTML string for the button to create a name for a new button group.
+   */
+  Drupal.theme.ckeditorNewButtonGroup = function () {
+    return '<li class="ckeditor-add-new-group"><button role="button" aria-label="' + Drupal.t('Add a CKEditor button group to the end of this row.') + '">' + Drupal.t('Add group') + '</button></li>';
+  };
+
+})(jQuery, Drupal, _, CKEDITOR);
diff --git a/core/themes/stable/js/ckeditor/ckeditor.drupalimage.admin.js b/core/themes/stable/js/ckeditor/ckeditor.drupalimage.admin.js
new file mode 100644
index 0000000..05a2315
--- /dev/null
+++ b/core/themes/stable/js/ckeditor/ckeditor.drupalimage.admin.js
@@ -0,0 +1,45 @@
+/**
+ * @file
+ * CKEditor 'drupalimage' plugin admin behavior.
+ */
+
+(function ($, Drupal, drupalSettings) {
+
+  "use strict";
+
+  /**
+   * Provides the summary for the "drupalimage" plugin settings vertical tab.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches summary behaviour to the "drupalimage" settings vertical tab.
+   */
+  Drupal.behaviors.ckeditorDrupalImageSettingsSummary = {
+    attach: function () {
+      $('[data-ckeditor-plugin-id="drupalimage"]').drupalSetSummary(function (context) {
+        var root = 'input[name="editor[settings][plugins][drupalimage][image_upload]';
+        var $status = $(root + '[status]"]');
+        var $maxFileSize = $(root + '[max_size]"]');
+        var $maxWidth = $(root + '[max_dimensions][width]"]');
+        var $maxHeight = $(root + '[max_dimensions][height]"]');
+        var $scheme = $(root + '[scheme]"]:checked');
+
+        var maxFileSize = $maxFileSize.val() ? $maxFileSize.val() : $maxFileSize.attr('placeholder');
+        var maxDimensions = ($maxWidth.val() && $maxHeight.val()) ? '(' + $maxWidth.val() + 'x' + $maxHeight.val() + ')' : '';
+
+        if (!$status.is(':checked')) {
+          return Drupal.t('Uploads disabled');
+        }
+
+        var output = '';
+        output += Drupal.t('Uploads enabled, max size: @size @dimensions', {'@size': maxFileSize, '@dimensions': maxDimensions});
+        if ($scheme.length) {
+          output += '<br />' + $scheme.attr('data-label');
+        }
+        return output;
+      });
+    }
+  };
+
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/themes/stable/js/ckeditor/ckeditor.js b/core/themes/stable/js/ckeditor/ckeditor.js
new file mode 100644
index 0000000..d621576
--- /dev/null
+++ b/core/themes/stable/js/ckeditor/ckeditor.js
@@ -0,0 +1,275 @@
+/**
+ * @file
+ * CKEditor implementation of {@link Drupal.editors} API.
+ */
+
+(function (Drupal, debounce, CKEDITOR, $) {
+
+  "use strict";
+
+  /**
+   * @namespace
+   */
+  Drupal.editors.ckeditor = {
+
+    /**
+     * Editor attach callback.
+     *
+     * @param {HTMLElement} element
+     *   The element to attach the editor to.
+     * @param {string} format
+     *   The text format for the editor.
+     *
+     * @return {bool}
+     *   Whether the call to `CKEDITOR.replace()` created an editor or not.
+     */
+    attach: function (element, format) {
+      this._loadExternalPlugins(format);
+      // Also pass settings that are Drupal-specific.
+      format.editorSettings.drupal = {
+        format: format.format
+      };
+
+      // Set a title on the CKEditor instance that includes the text field's
+      // label so that screen readers say something that is understandable
+      // for end users.
+      var label = $('label[for=' + element.getAttribute('id') + ']').html();
+      format.editorSettings.title = Drupal.t("Rich Text Editor, !label field", {'!label': label});
+
+      return !!CKEDITOR.replace(element, format.editorSettings);
+    },
+
+    /**
+     * Editor detach callback.
+     *
+     * @param {HTMLElement} element
+     *   The element to detach the editor from.
+     * @param {string} format
+     *   The text format used for the editor.
+     * @param {string} trigger
+     *   The event trigger for the detach.
+     *
+     * @return {bool}
+     *   Whether the call to `CKEDITOR.dom.element.get(element).getEditor()`
+     *   found an editor or not.
+     */
+    detach: function (element, format, trigger) {
+      var editor = CKEDITOR.dom.element.get(element).getEditor();
+      if (editor) {
+        if (trigger === 'serialize') {
+          editor.updateElement();
+        }
+        else {
+          editor.destroy();
+          element.removeAttribute('contentEditable');
+        }
+      }
+      return !!editor;
+    },
+
+    /**
+     * Reacts on a change in the editor element.
+     *
+     * @param {HTMLElement} element
+     *   The element where the change occured.
+     * @param {function} callback
+     *   Callback called with the value of the editor.
+     *
+     * @return {bool}
+     *   Whether the call to `CKEDITOR.dom.element.get(element).getEditor()`
+     *   found an editor or not.
+     */
+    onChange: function (element, callback) {
+      var editor = CKEDITOR.dom.element.get(element).getEditor();
+      if (editor) {
+        editor.on('change', debounce(function () {
+          callback(editor.getData());
+        }, 400));
+      }
+      return !!editor;
+    },
+
+    /**
+     * Attaches an inline editor to a DOM element.
+     *
+     * @param {HTMLElement} element
+     *   The element to attach the editor to.
+     * @param {object} format
+     *   The text format used in the editor.
+     * @param {string} [mainToolbarId]
+     *   The id attribute for the main editor toolbar, if any.
+     * @param {string} [floatedToolbarId]
+     *   The id attribute for the floated editor toolbar, if any.
+     *
+     * @return {bool}
+     *   Whether the call to `CKEDITOR.replace()` created an editor or not.
+     */
+    attachInlineEditor: function (element, format, mainToolbarId, floatedToolbarId) {
+      this._loadExternalPlugins(format);
+      // Also pass settings that are Drupal-specific.
+      format.editorSettings.drupal = {
+        format: format.format
+      };
+
+      var settings = $.extend(true, {}, format.editorSettings);
+
+      // If a toolbar is already provided for "true WYSIWYG" (in-place editing),
+      // then use that toolbar instead: override the default settings to render
+      // CKEditor UI's top toolbar into mainToolbar, and don't render the bottom
+      // toolbar at all. (CKEditor doesn't need a floated toolbar.)
+      if (mainToolbarId) {
+        var settingsOverride = {
+          extraPlugins: 'sharedspace',
+          removePlugins: 'floatingspace,elementspath',
+          sharedSpaces: {
+            top: mainToolbarId
+          }
+        };
+
+        // Find the "Source" button, if any, and replace it with "Sourcedialog".
+        // (The 'sourcearea' plugin only works in CKEditor's iframe mode.)
+        var sourceButtonFound = false;
+        for (var i = 0; !sourceButtonFound && i < settings.toolbar.length; i++) {
+          if (settings.toolbar[i] !== '/') {
+            for (var j = 0; !sourceButtonFound && j < settings.toolbar[i].items.length; j++) {
+              if (settings.toolbar[i].items[j] === 'Source') {
+                sourceButtonFound = true;
+                // Swap sourcearea's "Source" button for sourcedialog's.
+                settings.toolbar[i].items[j] = 'Sourcedialog';
+                settingsOverride.extraPlugins += ',sourcedialog';
+                settingsOverride.removePlugins += ',sourcearea';
+              }
+            }
+          }
+        }
+
+        settings.extraPlugins += ',' + settingsOverride.extraPlugins;
+        settings.removePlugins += ',' + settingsOverride.removePlugins;
+        settings.sharedSpaces = settingsOverride.sharedSpaces;
+      }
+
+      // CKEditor requires an element to already have the contentEditable
+      // attribute set to "true", otherwise it won't attach an inline editor.
+      element.setAttribute('contentEditable', 'true');
+
+      return !!CKEDITOR.inline(element, settings);
+    },
+
+    /**
+     * Loads the required external plugins for the editor.
+     *
+     * @param {object} format
+     *   The text format used in the editor.
+     */
+    _loadExternalPlugins: function (format) {
+      var externalPlugins = format.editorSettings.drupalExternalPlugins;
+      // Register and load additional CKEditor plugins as necessary.
+      if (externalPlugins) {
+        for (var pluginName in externalPlugins) {
+          if (externalPlugins.hasOwnProperty(pluginName)) {
+            CKEDITOR.plugins.addExternal(pluginName, externalPlugins[pluginName], '');
+          }
+        }
+        delete format.editorSettings.drupalExternalPlugins;
+      }
+    }
+
+  };
+
+  Drupal.ckeditor = {
+
+    /**
+     * Variable storing the current dialog's save callback.
+     *
+     * @type {?function}
+     */
+    saveCallback: null,
+
+    /**
+     * Open a dialog for a Drupal-based plugin.
+     *
+     * This dynamically loads jQuery UI (if necessary) using the Drupal AJAX
+     * framework, then opens a dialog at the specified Drupal path.
+     *
+     * @param {CKEditor} editor
+     *   The CKEditor instance that is opening the dialog.
+     * @param {string} url
+     *   The URL that contains the contents of the dialog.
+     * @param {object} existingValues
+     *   Existing values that will be sent via POST to the url for the dialog
+     *   contents.
+     * @param {function} saveCallback
+     *   A function to be called upon saving the dialog.
+     * @param {object} dialogSettings
+     *   An object containing settings to be passed to the jQuery UI.
+     */
+    openDialog: function (editor, url, existingValues, saveCallback, dialogSettings) {
+      // Locate a suitable place to display our loading indicator.
+      var $target = $(editor.container.$);
+      if (editor.elementMode === CKEDITOR.ELEMENT_MODE_REPLACE) {
+        $target = $target.find('.cke_contents');
+      }
+
+      // Remove any previous loading indicator.
+      $target.css('position', 'relative').find('.ckeditor-dialog-loading').remove();
+
+      // Add a consistent dialog class.
+      var classes = dialogSettings.dialogClass ? dialogSettings.dialogClass.split(' ') : [];
+      classes.push('editor-dialog');
+      dialogSettings.dialogClass = classes.join(' ');
+      dialogSettings.autoResize = Drupal.checkWidthBreakpoint(600);
+
+      // Add a "Loading…" message, hide it underneath the CKEditor toolbar,
+      // create a Drupal.Ajax instance to load the dialog and trigger it.
+      var $content = $('<div class="ckeditor-dialog-loading"><span style="top: -40px;" class="ckeditor-dialog-loading-link">' + Drupal.t('Loading...') + '</span></div>');
+      $content.appendTo($target);
+
+      var ckeditorAjaxDialog = Drupal.ajax({
+        dialog: dialogSettings,
+        dialogType: 'modal',
+        selector: '.ckeditor-dialog-loading-link',
+        url: url,
+        progress: {type: 'throbber'},
+        submit: {
+          editor_object: existingValues
+        }
+      });
+      ckeditorAjaxDialog.execute();
+
+      // After a short delay, show "Loading…" message.
+      window.setTimeout(function () {
+        $content.find('span').animate({top: '0px'});
+      }, 1000);
+
+      // Store the save callback to be executed when this dialog is closed.
+      Drupal.ckeditor.saveCallback = saveCallback;
+    }
+  };
+
+  // Moves the dialog to the top of the CKEDITOR stack.
+  $(window).on('dialogcreate', function (e, dialog, $element, settings) {
+    $('.editor-dialog').css("zIndex", CKEDITOR.config.baseFloatZIndex + 1);
+  });
+
+  // Respond to new dialogs that are opened by CKEditor, closing the AJAX loader.
+  $(window).on('dialog:beforecreate', function (e, dialog, $element, settings) {
+    $('.ckeditor-dialog-loading').animate({top: '-40px'}, function () {
+      $(this).remove();
+    });
+  });
+
+  // Respond to dialogs that are saved, sending data back to CKEditor.
+  $(window).on('editor:dialogsave', function (e, values) {
+    if (Drupal.ckeditor.saveCallback) {
+      Drupal.ckeditor.saveCallback(values);
+    }
+  });
+
+  // Respond to dialogs that are closed, removing the current save handler.
+  $(window).on('dialog:afterclose', function (e, dialog, $element) {
+    if (Drupal.ckeditor.saveCallback) {
+      Drupal.ckeditor.saveCallback = null;
+    }
+  });
+
+})(Drupal, Drupal.debounce, CKEDITOR, jQuery);
diff --git a/core/themes/stable/js/ckeditor/ckeditor.stylescombo.admin.js b/core/themes/stable/js/ckeditor/ckeditor.stylescombo.admin.js
new file mode 100644
index 0000000..c616113
--- /dev/null
+++ b/core/themes/stable/js/ckeditor/ckeditor.stylescombo.admin.js
@@ -0,0 +1,128 @@
+/**
+ * @file
+ * CKEditor StylesCombo admin behavior.
+ */
+
+(function ($, Drupal, drupalSettings) {
+
+  "use strict";
+
+  /**
+   * Ensures that the "stylescombo" button's metadata remains up-to-date.
+   *
+   * Triggers the CKEditorPluginSettingsChanged event whenever the "stylescombo"
+   * plugin settings change, to ensure that the corresponding feature metadata
+   * is immediately updated — i.e. ensure that HTML tags and classes entered
+   * here are known to be "required", which may affect filter settings.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches admin behaviour to the "stylescombo" button.
+   */
+  Drupal.behaviors.ckeditorStylesComboSettings = {
+    attach: function (context) {
+      var $context = $(context);
+
+      // React to changes in the list of user-defined styles: calculate the new
+      // stylesSet setting up to 2 times per second, and if it is different,
+      // fire the CKEditorPluginSettingsChanged event with the updated parts of
+      // the CKEditor configuration. (This will, in turn, cause the hidden
+      // CKEditor instance to be updated and a drupalEditorFeatureModified event
+      // to fire.)
+      var $ckeditorActiveToolbar = $context
+        .find('.ckeditor-toolbar-configuration')
+        .find('.ckeditor-toolbar-active');
+      var previousStylesSet = drupalSettings.ckeditor.hiddenCKEditorConfig.stylesSet;
+      var that = this;
+      $context.find('[name="editor[settings][plugins][stylescombo][styles]"]')
+        .on('blur.ckeditorStylesComboSettings', function () {
+          var styles = $.trim($(this).val());
+          var stylesSet = that._generateStylesSetSetting(styles);
+          if (!_.isEqual(previousStylesSet, stylesSet)) {
+            previousStylesSet = stylesSet;
+            $ckeditorActiveToolbar.trigger('CKEditorPluginSettingsChanged', [
+              {stylesSet: stylesSet}
+            ]);
+          }
+        });
+    },
+
+    /**
+     * Builds the "stylesSet" configuration part of the CKEditor JS settings.
+     *
+     * @see \Drupal\ckeditor\Plugin\ckeditor\plugin\StylesCombo::generateStylesSetSetting()
+     *
+     * Note that this is a more forgiving implementation than the PHP version:
+     * the parsing works identically, but instead of failing on invalid styles,
+     * we just ignore those.
+     *
+     * @param {string} styles
+     *   The "styles" setting.
+     *
+     * @return {Array}
+     *   An array containing the "stylesSet" configuration.
+     */
+    _generateStylesSetSetting: function (styles) {
+      var stylesSet = [];
+
+      styles = styles.replace(/\r/g, "\n");
+      var lines = styles.split("\n");
+      for (var i = 0; i < lines.length; i++) {
+        var style = $.trim(lines[i]);
+
+        // Ignore empty lines in between non-empty lines.
+        if (style.length === 0) {
+          continue;
+        }
+
+        // Validate syntax: element[.class...]|label pattern expected.
+        if (style.match(/^ *[a-zA-Z0-9]+ *(\.[a-zA-Z0-9_-]+ *)*\| *.+ *$/) === null) {
+          // Instead of failing, we just ignore any invalid styles.
+          continue;
+        }
+
+        // Parse.
+        var parts = style.split('|');
+        var selector = parts[0];
+        var label = parts[1];
+        var classes = selector.split('.');
+        var element = classes.shift();
+
+        // Build the data structure CKEditor's stylescombo plugin expects.
+        // @see http://docs.cksource.com/CKEditor_3.x/Developers_Guide/Styles
+        stylesSet.push({
+          attributes: {class: classes.join(' ')},
+          element: element,
+          name: label
+        });
+      }
+
+      return stylesSet;
+    }
+  };
+
+  /**
+   * Provides the summary for the "stylescombo" plugin settings vertical tab.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches summary behaviour to the plugin settings vertical tab.
+   */
+  Drupal.behaviors.ckeditorStylesComboSettingsSummary = {
+    attach: function () {
+      $('[data-ckeditor-plugin-id="stylescombo"]').drupalSetSummary(function (context) {
+        var styles = $.trim($('[data-drupal-selector="edit-editor-settings-plugins-stylescombo-styles"]').val());
+        if (styles.length === 0) {
+          return Drupal.t('No styles configured');
+        }
+        else {
+          var count = $.trim(styles).split("\n").length;
+          return Drupal.t('@count styles configured', {'@count': count});
+        }
+      });
+    }
+  };
+
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/themes/stable/js/ckeditor/models/Model.js b/core/themes/stable/js/ckeditor/models/Model.js
new file mode 100644
index 0000000..201351b
--- /dev/null
+++ b/core/themes/stable/js/ckeditor/models/Model.js
@@ -0,0 +1,75 @@
+/**
+ * @file
+ * A Backbone Model for the state of a CKEditor toolbar configuration .
+ */
+
+(function (Drupal, Backbone) {
+
+  "use strict";
+
+  /**
+   * Backbone model for the CKEditor toolbar configuration state.
+   *
+   * @constructor
+   *
+   * @augments Backbone.Model
+   */
+  Drupal.ckeditor.Model = Backbone.Model.extend(/** @lends Drupal.ckeditor.Model# */{
+
+    /**
+     * Default values.
+     *
+     * @type {object}
+     */
+    defaults: /** @lends Drupal.ckeditor.Model# */{
+
+      /**
+       * The CKEditor configuration that is being manipulated through the UI.
+       */
+      activeEditorConfig: null,
+
+      /**
+       * The textarea that contains the serialized representation of the active
+       * CKEditor configuration.
+       */
+      $textarea: null,
+
+      /**
+       * Tracks whether the active toolbar DOM structure has been changed. When
+       * true, activeEditorConfig needs to be updated, and when that is updated,
+       * $textarea will also be updated.
+       */
+      isDirty: false,
+
+      /**
+       * The configuration for the hidden CKEditor instance that is used to
+       * build the features metadata.
+       */
+      hiddenEditorConfig: null,
+
+      /**
+       * A hash that maps buttons to features.
+       */
+      buttonsToFeatures: null,
+
+      /**
+       * A hash, keyed by a feature name, that details CKEditor plugin features.
+       */
+      featuresMetadata: null,
+
+      /**
+       * Whether the button group names are currently visible.
+       */
+      groupNamesVisible: false
+    },
+
+    /**
+     * @method
+     */
+    sync: function () {
+      // Push the settings into the textarea.
+      this.get('$textarea').val(JSON.stringify(this.get('activeEditorConfig')));
+    }
+  });
+
+})(Drupal, Backbone);
diff --git a/core/themes/stable/js/ckeditor/plugins/drupalimage/image.png b/core/themes/stable/js/ckeditor/plugins/drupalimage/image.png
new file mode 100644
index 0000000..07ddc2e
--- /dev/null
+++ b/core/themes/stable/js/ckeditor/plugins/drupalimage/image.png
@@ -0,0 +1,7 @@
+PNG
+
+   IHDR         (-S   PLTE                                          ZZZ```
+
+
+      			&&&   """###%%%)))***---666888999;;;<<<>>>@@@AAABBBDDDGGGIIIJJJKKKLLLNNNOOOPPPQQQRRRUUUYYYw    tRNS 
+!*LPU`tv~=K   IDATW` W*2KVG]yO HjZ e3 bOxt@+&0RՊX{]Ofbpٻyt<og۸u۶WC@C׾=t )7OSw:z    IENDB`
\ No newline at end of file
diff --git a/core/themes/stable/js/ckeditor/plugins/drupalimage/plugin.js b/core/themes/stable/js/ckeditor/plugins/drupalimage/plugin.js
new file mode 100644
index 0000000..2cd6254
--- /dev/null
+++ b/core/themes/stable/js/ckeditor/plugins/drupalimage/plugin.js
@@ -0,0 +1,230 @@
+/**
+ * @file
+ * Drupal Image plugin.
+ *
+ * This alters the existing CKEditor image2 widget plugin to:
+ * - require a data-entity-type and a data-entity-uuid attribute (which Drupal
+ *   uses to track where images are being used)
+ * - use a Drupal-native dialog (that is in fact just an alterable Drupal form
+ *   like any other) instead of CKEditor's own dialogs.
+ *
+ * @see \Drupal\editor\Form\EditorImageDialog
+ *
+ * @ignore
+ */
+
+(function ($, Drupal, CKEDITOR) {
+
+  "use strict";
+
+  CKEDITOR.plugins.add('drupalimage', {
+    requires: 'image2',
+
+    beforeInit: function (editor) {
+      // Override the image2 widget definition to require and handle the
+      // additional data-entity-type and data-entity-uuid attributes.
+      editor.on('widgetDefinition', function (event) {
+        var widgetDefinition = event.data;
+        if (widgetDefinition.name !== 'image') {
+          return;
+        }
+
+        // Override requiredContent & allowedContent.
+        widgetDefinition.requiredContent = 'img[alt,src,width,height,data-entity-type,data-entity-uuid]';
+        widgetDefinition.allowedContent.img.attributes += ',!data-entity-type,!data-entity-uuid';
+        // We don't allow <figure>, <figcaption>, <div> or <p>  in our downcast.
+        delete widgetDefinition.allowedContent.figure;
+        delete widgetDefinition.allowedContent.figcaption;
+        delete widgetDefinition.allowedContent.div;
+        delete widgetDefinition.allowedContent.p;
+
+        // Override the 'link' part, to completely disable image2's link
+        // support: http://dev.ckeditor.com/ticket/11341.
+        widgetDefinition.parts.link = 'This is a nonsensical selector to disable this functionality completely';
+
+        // Override downcast(): since we only accept <img> in our upcast method,
+        // the element is already correct. We only need to update the element's
+        // data-entity-uuid attribute.
+        widgetDefinition.downcast = function (element) {
+          element.attributes['data-entity-uuid'] = this.data['data-entity-uuid'];
+        };
+
+        // We want to upcast <img> elements to a DOM structure required by the
+        // image2 widget; we only accept an <img> tag, and that <img> tag MAY
+        // have a data-entity-type and a data-entity-uuid attribute.
+        widgetDefinition.upcast = function (element, data) {
+          if (element.name !== 'img') {
+            return;
+          }
+          // Don't initialize on pasted fake objects.
+          else if (element.attributes['data-cke-realelement']) {
+            return;
+          }
+
+          // Parse the data-entity-type attribute.
+          data['data-entity-type'] = element.attributes['data-entity-type'];
+          // Parse the data-entity-uuid attribute.
+          data['data-entity-uuid'] = element.attributes['data-entity-uuid'];
+
+          return element;
+        };
+
+        // Protected; keys of the widget data to be sent to the Drupal dialog.
+        // Keys in the hash are the keys for image2's data, values are the keys
+        // that the Drupal dialog uses.
+        widgetDefinition._mapDataToDialog = {
+          'src': 'src',
+          'alt': 'alt',
+          'width': 'width',
+          'height': 'height',
+          'data-entity-type': 'data-entity-type',
+          'data-entity-uuid': 'data-entity-uuid'
+        };
+
+        // Protected; transforms widget's data object to the format used by the
+        // \Drupal\editor\Form\EditorImageDialog dialog, keeping only the data
+        // listed in widgetDefinition._dataForDialog.
+        widgetDefinition._dataToDialogValues = function (data) {
+          var dialogValues = {};
+          var map = widgetDefinition._mapDataToDialog;
+          Object.keys(widgetDefinition._mapDataToDialog).forEach(function (key) {
+            dialogValues[map[key]] = data[key];
+          });
+          return dialogValues;
+        };
+
+        // Protected; the inverse of _dataToDialogValues.
+        widgetDefinition._dialogValuesToData = function (dialogReturnValues) {
+          var data = {};
+          var map = widgetDefinition._mapDataToDialog;
+          Object.keys(widgetDefinition._mapDataToDialog).forEach(function (key) {
+            if (dialogReturnValues.hasOwnProperty(map[key])) {
+              data[key] = dialogReturnValues[map[key]];
+            }
+          });
+          return data;
+        };
+
+        // Protected; creates Drupal dialog save callback.
+        widgetDefinition._createDialogSaveCallback = function (editor, widget) {
+          return function (dialogReturnValues) {
+            var firstEdit = !widget.ready;
+
+            // Dialog may have blurred the widget. Re-focus it first.
+            if (!firstEdit) {
+              widget.focus();
+            }
+
+            editor.fire('saveSnapshot');
+
+            // Pass `true` so DocumentFragment will also be returned.
+            var container = widget.wrapper.getParent(true);
+            var image = widget.parts.image;
+
+            // Set the updated widget data, after the necessary conversions from
+            // the dialog's return values.
+            // Note: on widget#setData this widget instance might be destroyed.
+            var data = widgetDefinition._dialogValuesToData(dialogReturnValues.attributes);
+            widget.setData(data);
+
+            // Retrieve the widget once again. It could've been destroyed
+            // when shifting state, so might deal with a new instance.
+            widget = editor.widgets.getByElement(image);
+
+            // It's first edit, just after widget instance creation, but before
+            // it was inserted into DOM. So we need to retrieve the widget
+            // wrapper from inside the DocumentFragment which we cached above
+            // and finalize other things (like ready event and flag).
+            if (firstEdit) {
+              editor.widgets.finalizeCreation(container);
+            }
+
+            setTimeout(function () {
+              // (Re-)focus the widget.
+              widget.focus();
+              // Save snapshot for undo support.
+              editor.fire('saveSnapshot');
+            });
+
+            return widget;
+          };
+        };
+      });
+
+      // Add a widget#edit listener to every instance of image2 widget in order
+      // to handle its editing with a Drupal-native dialog.
+      // This includes also a case just after the image was created
+      // and dialog should be opened for it for the first time.
+      editor.widgets.on('instanceCreated', function (event) {
+        var widget = event.data;
+
+        if (widget.name !== 'image') {
+          return;
+        }
+
+        widget.on('edit', function (event) {
+          // Cancel edit event to break image2's dialog binding
+          // (and also to prevent automatic insertion before opening dialog).
+          event.cancel();
+
+          // Open drupalimage dialog.
+          editor.execCommand('editdrupalimage', {
+            existingValues: widget.definition._dataToDialogValues(widget.data),
+            saveCallback: widget.definition._createDialogSaveCallback(editor, widget),
+            // Drupal.t() will not work inside CKEditor plugins because CKEditor
+            // loads the JavaScript file instead of Drupal. Pull translated
+            // strings from the plugin settings that are translated server-side.
+            dialogTitle: widget.data.src ? editor.config.drupalImage_dialogTitleEdit : editor.config.drupalImage_dialogTitleAdd
+          });
+        });
+      });
+
+      // Register the "editdrupalimage" command, which essentially just replaces
+      // the "image" command's CKEditor dialog with a Drupal-native dialog.
+      editor.addCommand('editdrupalimage', {
+        allowedContent: 'img[alt,!src,width,height,!data-entity-type,!data-entity-uuid]',
+        requiredContent: 'img[alt,src,width,height,data-entity-type,data-entity-uuid]',
+        modes: {wysiwyg: 1},
+        canUndo: true,
+        exec: function (editor, data) {
+          var dialogSettings = {
+            title: data.dialogTitle,
+            dialogClass: 'editor-image-dialog'
+          };
+          Drupal.ckeditor.openDialog(editor, Drupal.url('editor/dialog/image/' + editor.config.drupal.format), data.existingValues, data.saveCallback, dialogSettings);
+        }
+      });
+
+      // Register the toolbar button.
+      if (editor.ui.addButton) {
+        editor.ui.addButton('DrupalImage', {
+          label: Drupal.t('Image'),
+          // Note that we use the original image2 command!
+          command: 'image',
+          icon: this.path + '/image.png'
+        });
+      }
+    },
+
+    // Disable image2's integration with the link/drupallink plugins: don't
+    // allow the widget itself to become a link. Support for that may be added
+    // by an text filter that adds a data- attribute specifically for that.
+    afterInit: function (editor) {
+      if (editor.plugins.drupallink) {
+        var cmd = editor.getCommand('drupallink');
+        // Needs to be refreshed on selection changes.
+        cmd.contextSensitive = 1;
+        // Disable command and cancel event when the image widget is selected.
+        cmd.on('refresh', function (evt) {
+          var widget = editor.widgets.focused;
+          if (widget && widget.name === 'image') {
+            this.setState(CKEDITOR.TRISTATE_DISABLED);
+            evt.cancel();
+          }
+        });
+      }
+    }
+
+  });
+
+})(jQuery, Drupal, CKEDITOR);
diff --git a/core/themes/stable/js/ckeditor/plugins/drupalimagecaption/plugin.js b/core/themes/stable/js/ckeditor/plugins/drupalimagecaption/plugin.js
new file mode 100644
index 0000000..e68c809
--- /dev/null
+++ b/core/themes/stable/js/ckeditor/plugins/drupalimagecaption/plugin.js
@@ -0,0 +1,260 @@
+/**
+ * @file
+ * Drupal Image Caption plugin.
+ *
+ * This alters the existing CKEditor image2 widget plugin, which is already
+ * altered by the Drupal Image plugin, to:
+ * - allow for the data-caption and data-align attributes to be set
+ * - mimic the upcasting behavior of the caption_filter filter.
+ *
+ * @ignore
+ */
+
+(function (CKEDITOR) {
+
+  "use strict";
+
+  CKEDITOR.plugins.add('drupalimagecaption', {
+    requires: 'drupalimage',
+
+    beforeInit: function (editor) {
+      // Disable default placeholder text that comes with CKEditor's image2
+      // plugin: it has an inferior UX (it requires the user to manually delete
+      // the place holder text).
+      editor.lang.image2.captionPlaceholder = '';
+
+      // Drupal.t() will not work inside CKEditor plugins because CKEditor loads
+      // the JavaScript file instead of Drupal. Pull translated strings from the
+      // plugin settings that are translated server-side.
+      var placeholderText = editor.config.drupalImageCaption_captionPlaceholderText;
+
+      // Override the image2 widget definition to handle the additional
+      // data-align and data-caption attributes.
+      editor.on('widgetDefinition', function (event) {
+        var widgetDefinition = event.data;
+        if (widgetDefinition.name !== 'image') {
+          return;
+        }
+
+        // Only perform the downcasting/upcasting for to the enabled filters.
+        var captionFilterEnabled = editor.config.drupalImageCaption_captionFilterEnabled;
+        var alignFilterEnabled = editor.config.drupalImageCaption_alignFilterEnabled;
+
+        // Override default features definitions for drupalimagecaption.
+        CKEDITOR.tools.extend(widgetDefinition.features, {
+          caption: {
+            requiredContent: 'img[data-caption]'
+          },
+          align: {
+            requiredContent: 'img[data-align]'
+          }
+        }, true);
+
+        // Override requiredContent & allowedContent.
+        widgetDefinition.requiredContent = 'img[alt,src,width,height,data-entity-type,data-entity-uuid,data-align,data-caption]';
+        widgetDefinition.allowedContent.img.attributes += ',!data-align,!data-caption';
+
+        // Override allowedContent setting for the 'caption' nested editable.
+        // This must match what caption_filter enforces.
+        // @see \Drupal\filter\Plugin\Filter\FilterCaption::process()
+        // @see \Drupal\Component\Utility\Xss::filter()
+        widgetDefinition.editables.caption.allowedContent = 'a[!href]; em strong cite code br';
+
+        // Override downcast(): ensure we *only* output <img>, but also ensure
+        // we include the data-entity-type, data-entity-uuid, data-align and
+        // data-caption attributes.
+        widgetDefinition.downcast = function (element) {
+          // Find an image element in the one being downcasted (can be itself).
+          var img = findElementByName(element, 'img');
+          var caption = this.editables.caption;
+          var captionHtml = caption && caption.getData();
+          var attrs = img.attributes;
+
+          if (captionFilterEnabled) {
+            // If image contains a non-empty caption, serialize caption to the
+            // data-caption attribute.
+            if (captionHtml) {
+              attrs['data-caption'] = captionHtml;
+            }
+          }
+          if (alignFilterEnabled) {
+            if (this.data.align !== 'none') {
+              attrs['data-align'] = this.data.align;
+            }
+          }
+          attrs['data-entity-type'] = this.data['data-entity-type'];
+          attrs['data-entity-uuid'] = this.data['data-entity-uuid'];
+
+          return img;
+        };
+
+        // We want to upcast <img> elements to a DOM structure required by the
+        // image2 widget. Depending on a case it may be:
+        //   - just an <img> tag (non-captioned, not-centered image),
+        //   - <img> tag in a paragraph (non-captioned, centered image),
+        //   - <figure> tag (captioned image).
+        // We take the same attributes into account as downcast() does.
+        widgetDefinition.upcast = function (element, data) {
+          if (element.name !== 'img' || !element.attributes['data-entity-type'] || !element.attributes['data-entity-uuid']) {
+            return;
+          }
+          // Don't initialize on pasted fake objects.
+          else if (element.attributes['data-cke-realelement']) {
+            return;
+          }
+
+          var attrs = element.attributes;
+          var retElement = element;
+          var caption;
+
+          // We won't need the attributes during editing: we'll use widget.data
+          // to store them (except the caption, which is stored in the DOM).
+          if (captionFilterEnabled) {
+            caption = attrs['data-caption'];
+            delete attrs['data-caption'];
+          }
+          if (alignFilterEnabled) {
+            data.align = attrs['data-align'];
+            delete attrs['data-align'];
+          }
+          data['data-entity-type'] = attrs['data-entity-type'];
+          delete attrs['data-entity-type'];
+          data['data-entity-uuid'] = attrs['data-entity-uuid'];
+          delete attrs['data-entity-uuid'];
+
+          if (captionFilterEnabled) {
+            // Unwrap from <p> wrapper created by HTML parser for a captioned
+            // image. The captioned image will be transformed to <figure>, so we
+            // don't want the <p> anymore.
+            if (element.parent.name === 'p' && caption) {
+              var index = element.getIndex();
+              var splitBefore = index > 0;
+              var splitAfter = index + 1 < element.parent.children.length;
+
+              if (splitBefore) {
+                element.parent.split(index);
+              }
+              index = element.getIndex();
+              if (splitAfter) {
+                element.parent.split(index + 1);
+              }
+
+              element.parent.replaceWith(element);
+              retElement = element;
+            }
+
+            // If this image has a caption, create a full <figure> structure.
+            if (caption) {
+              var figure = new CKEDITOR.htmlParser.element('figure');
+              caption = new CKEDITOR.htmlParser.fragment.fromHtml(caption, 'figcaption');
+
+              // Use Drupal's data-placeholder attribute to insert a CSS-based,
+              // translation-ready placeholder for empty captions. Note that it
+              // also must to be done for new instances (see
+              // widgetDefinition._createDialogSaveCallback).
+              caption.attributes['data-placeholder'] = placeholderText;
+
+              element.replaceWith(figure);
+              figure.add(element);
+              figure.add(caption);
+              figure.attributes['class'] = editor.config.image2_captionedClass;
+              retElement = figure;
+            }
+          }
+
+          if (alignFilterEnabled) {
+            // If this image doesn't have a caption (or the caption filter is
+            // disabled), but it is centered, make sure that it's wrapped with
+            // <p>, which will become a part of the widget.
+            if (data.align === 'center' && (!captionFilterEnabled || !caption)) {
+              var p = new CKEDITOR.htmlParser.element('p');
+              element.replaceWith(p);
+              p.add(element);
+              // Apply the class for centered images.
+              p.addClass(editor.config.image2_alignClasses[1]);
+              retElement = p;
+            }
+          }
+
+          // Return the upcasted element (<img>, <figure> or <p>).
+          return retElement;
+        };
+
+        // Protected; keys of the widget data to be sent to the Drupal dialog.
+        // Append to the values defined by the drupalimage plugin.
+        // @see core/modules/ckeditor/js/plugins/drupalimage/plugin.js
+        CKEDITOR.tools.extend(widgetDefinition._mapDataToDialog, {
+          'align': 'data-align',
+          'data-caption': 'data-caption',
+          'hasCaption': 'hasCaption'
+        });
+
+        // Override Drupal dialog save callback.
+        var originalCreateDialogSaveCallback = widgetDefinition._createDialogSaveCallback;
+        widgetDefinition._createDialogSaveCallback = function (editor, widget) {
+          var saveCallback = originalCreateDialogSaveCallback.call(this, editor, widget);
+
+          return function (dialogReturnValues) {
+            // Ensure hasCaption is a boolean. image2 assumes it always works
+            // with booleans; if this is not the case, then
+            // CKEDITOR.plugins.image2.stateShifter() will incorrectly mark
+            // widget.data.hasCaption as "changed" (e.g. when hasCaption === 0
+            // instead of hasCaption === false). This causes image2's "state
+            // shifter" to enter the wrong branch of the algorithm and blow up.
+            dialogReturnValues.attributes.hasCaption = !!dialogReturnValues.attributes.hasCaption;
+
+            var actualWidget = saveCallback(dialogReturnValues);
+
+            // By default, the template of captioned widget has no
+            // data-placeholder attribute. Note that it also must be done when
+            // upcasting existing elements (see widgetDefinition.upcast).
+            if (dialogReturnValues.attributes.hasCaption) {
+              actualWidget.editables.caption.setAttribute('data-placeholder', placeholderText);
+
+              // Some browsers will add a <br> tag to a newly created DOM
+              // element with no content. Remove this <br> if it is the only
+              // thing in the caption. Our placeholder support requires the
+              // element be entirely empty. See filter-caption.css.
+              var captionElement = actualWidget.editables.caption.$;
+              if (captionElement.childNodes.length === 1 && captionElement.childNodes.item(0).nodeName === 'BR') {
+                captionElement.removeChild(captionElement.childNodes.item(0));
+              }
+            }
+          };
+        };
+      // Low priority to ensure drupalimage's event handler runs first.
+      }, null, null, 20);
+    }
+  });
+
+  /**
+   * Finds an element by its name.
+   *
+   * Function will check first the passed element itself and then all its
+   * children in DFS order.
+   *
+   * @param {CKEDITOR.htmlParser.element} element
+   *   The element to search.
+   * @param {string} name
+   *   The element name to search for.
+   *
+   * @return {?CKEDITOR.htmlParser.element}
+   *   The found element, or null.
+   */
+  function findElementByName(element, name) {
+    if (element.name === name) {
+      return element;
+    }
+
+    var found = null;
+    element.forEach(function (el) {
+      if (el.name === name) {
+        found = el;
+        // Stop here.
+        return false;
+      }
+    }, CKEDITOR.NODE_ELEMENT);
+    return found;
+  }
+
+})(CKEDITOR);
diff --git a/core/themes/stable/js/ckeditor/plugins/drupallink/link.png b/core/themes/stable/js/ckeditor/plugins/drupallink/link.png
new file mode 100644
index 0000000..b49cfde
--- /dev/null
+++ b/core/themes/stable/js/ckeditor/plugins/drupallink/link.png
@@ -0,0 +1,3 @@
+PNG
+
+   IHDR         (-S   lPLTE                  XXX]]]PPPPPP                  #tRNS 68abcdg   hIDATxڭG0 {ZdD kWi ILi2uԱ*]x y2lބ{m6Ǫ:`ᘦR	2$:WYz    IENDB`
\ No newline at end of file
diff --git a/core/themes/stable/js/ckeditor/plugins/drupallink/plugin.js b/core/themes/stable/js/ckeditor/plugins/drupallink/plugin.js
new file mode 100644
index 0000000..4d0832a
--- /dev/null
+++ b/core/themes/stable/js/ckeditor/plugins/drupallink/plugin.js
@@ -0,0 +1,235 @@
+/**
+ * @file
+ * Drupal Link plugin.
+ *
+ * @ignore
+ */
+
+(function ($, Drupal, drupalSettings, CKEDITOR) {
+
+  "use strict";
+
+  CKEDITOR.plugins.add('drupallink', {
+    init: function (editor) {
+      // Add the commands for link and unlink.
+      editor.addCommand('drupallink', {
+        allowedContent: 'a[!href,target]',
+        requiredContent: 'a[href]',
+        modes: {wysiwyg: 1},
+        canUndo: true,
+        exec: function (editor) {
+          var linkElement = getSelectedLink(editor);
+          var linkDOMElement = null;
+
+          // Set existing values based on selected element.
+          var existingValues = {};
+          if (linkElement && linkElement.$) {
+            linkDOMElement = linkElement.$;
+
+            // Populate an array with the link's current attributes.
+            var attribute = null;
+            var attributeName;
+            for (var attrIndex = 0; attrIndex < linkDOMElement.attributes.length; attrIndex++) {
+              attribute = linkDOMElement.attributes.item(attrIndex);
+              attributeName = attribute.nodeName.toLowerCase();
+              // Don't consider data-cke-saved- attributes; they're just there
+              // to work around browser quirks.
+              if (attributeName.substring(0, 15) === 'data-cke-saved-') {
+                continue;
+              }
+              // Store the value for this attribute, unless there's a
+              // data-cke-saved- alternative for it, which will contain the
+              // quirk-free, original value.
+              existingValues[attributeName] = linkElement.data('cke-saved-' + attributeName) || attribute.nodeValue;
+            }
+          }
+
+          // Prepare a save callback to be used upon saving the dialog.
+          var saveCallback = function (returnValues) {
+            editor.fire('saveSnapshot');
+
+            // Create a new link element if needed.
+            if (!linkElement && returnValues.attributes.href) {
+              var selection = editor.getSelection();
+              var range = selection.getRanges(1)[0];
+
+              // Use link URL as text with a collapsed cursor.
+              if (range.collapsed) {
+                // Shorten mailto URLs to just the email address.
+                var text = new CKEDITOR.dom.text(returnValues.attributes.href.replace(/^mailto:/, ''), editor.document);
+                range.insertNode(text);
+                range.selectNodeContents(text);
+              }
+
+              // Ignore a disabled target attribute.
+              if (returnValues.attributes.target === 0) {
+                delete returnValues.attributes.target;
+              }
+
+              // Create the new link by applying a style to the new text.
+              var style = new CKEDITOR.style({element: 'a', attributes: returnValues.attributes});
+              style.type = CKEDITOR.STYLE_INLINE;
+              style.applyToRange(range);
+              range.select();
+
+              // Set the link so individual properties may be set below.
+              linkElement = getSelectedLink(editor);
+            }
+            // Update the link properties.
+            else if (linkElement) {
+              for (var attrName in returnValues.attributes) {
+                if (returnValues.attributes.hasOwnProperty(attrName)) {
+                  // Update the property if a value is specified.
+                  if (returnValues.attributes[attrName].length > 0) {
+                    var value = returnValues.attributes[attrName];
+                    linkElement.data('cke-saved-' + attrName, value);
+                    linkElement.setAttribute(attrName, value);
+                  }
+                  // Delete the property if set to an empty string.
+                  else {
+                    linkElement.removeAttribute(attrName);
+                  }
+                }
+              }
+            }
+
+            // Save snapshot for undo support.
+            editor.fire('saveSnapshot');
+          };
+          // Drupal.t() will not work inside CKEditor plugins because CKEditor
+          // loads the JavaScript file instead of Drupal. Pull translated
+          // strings from the plugin settings that are translated server-side.
+          var dialogSettings = {
+            title: linkElement ? editor.config.drupalLink_dialogTitleEdit : editor.config.drupalLink_dialogTitleAdd,
+            dialogClass: 'editor-link-dialog'
+          };
+
+          // Open the dialog for the edit form.
+          Drupal.ckeditor.openDialog(editor, Drupal.url('editor/dialog/link/' + editor.config.drupal.format), existingValues, saveCallback, dialogSettings);
+        }
+      });
+      editor.addCommand('drupalunlink', {
+        contextSensitive: 1,
+        startDisabled: 1,
+        allowedContent: 'a[!href]',
+        requiredContent: 'a[href]',
+        exec: function (editor) {
+          var style = new CKEDITOR.style({element: 'a', type: CKEDITOR.STYLE_INLINE, alwaysRemoveElement: 1});
+          editor.removeStyle(style);
+        },
+        refresh: function (editor, path) {
+          var element = path.lastElement && path.lastElement.getAscendant('a', true);
+          if (element && element.getName() === 'a' && element.getAttribute('href') && element.getChildCount()) {
+            this.setState(CKEDITOR.TRISTATE_OFF);
+          }
+          else {
+            this.setState(CKEDITOR.TRISTATE_DISABLED);
+          }
+        }
+      });
+
+      // CTRL + K.
+      editor.setKeystroke(CKEDITOR.CTRL + 75, 'drupallink');
+
+      // Add buttons for link and unlink.
+      if (editor.ui.addButton) {
+        editor.ui.addButton('DrupalLink', {
+          label: Drupal.t('Link'),
+          command: 'drupallink',
+          icon: this.path + '/link.png'
+        });
+        editor.ui.addButton('DrupalUnlink', {
+          label: Drupal.t('Unlink'),
+          command: 'drupalunlink',
+          icon: this.path + '/unlink.png'
+        });
+      }
+
+      editor.on('doubleclick', function (evt) {
+        var element = getSelectedLink(editor) || evt.data.element;
+
+        if (!element.isReadOnly()) {
+          if (element.is('a')) {
+            editor.getSelection().selectElement(element);
+            editor.getCommand('drupallink').exec();
+          }
+        }
+      });
+
+      // If the "menu" plugin is loaded, register the menu items.
+      if (editor.addMenuItems) {
+        editor.addMenuItems({
+          link: {
+            label: Drupal.t('Edit Link'),
+            command: 'drupallink',
+            group: 'link',
+            order: 1
+          },
+
+          unlink: {
+            label: Drupal.t('Unlink'),
+            command: 'drupalunlink',
+            group: 'link',
+            order: 5
+          }
+        });
+      }
+
+      // If the "contextmenu" plugin is loaded, register the listeners.
+      if (editor.contextMenu) {
+        editor.contextMenu.addListener(function (element, selection) {
+          if (!element || element.isReadOnly()) {
+            return null;
+          }
+          var anchor = getSelectedLink(editor);
+          if (!anchor) {
+            return null;
+          }
+
+          var menu = {};
+          if (anchor.getAttribute('href') && anchor.getChildCount()) {
+            menu = {link: CKEDITOR.TRISTATE_OFF, unlink: CKEDITOR.TRISTATE_OFF};
+          }
+          return menu;
+        });
+      }
+    }
+  });
+
+  /**
+   * Get the surrounding link element of current selection.
+   *
+   * The following selection will all return the link element.
+   *
+   * @example
+   *  <a href="#">li^nk</a>
+   *  <a href="#">[link]</a>
+   *  text[<a href="#">link]</a>
+   *  <a href="#">li[nk</a>]
+   *  [<b><a href="#">li]nk</a></b>]
+   *  [<a href="#"><b>li]nk</b></a>
+   *
+   * @param {CKEDITOR.editor} editor
+   *   The CKEditor editor object
+   *
+   * @return {?HTMLElement}
+   *   The selected link element, or null.
+   *
+   */
+  function getSelectedLink(editor) {
+    var selection = editor.getSelection();
+    var selectedElement = selection.getSelectedElement();
+    if (selectedElement && selectedElement.is('a')) {
+      return selectedElement;
+    }
+
+    var range = selection.getRanges(true)[0];
+
+    if (range) {
+      range.shrink(CKEDITOR.SHRINK_TEXT);
+      return editor.elementPath(range.getCommonAncestor()).contains('a', 1);
+    }
+    return null;
+  }
+
+})(jQuery, Drupal, drupalSettings, CKEDITOR);
diff --git a/core/themes/stable/js/ckeditor/plugins/drupallink/unlink.png b/core/themes/stable/js/ckeditor/plugins/drupallink/unlink.png
new file mode 100644
index 0000000..de912d1
--- /dev/null
+++ b/core/themes/stable/js/ckeditor/plugins/drupallink/unlink.png
@@ -0,0 +1,7 @@
+PNG
+
+   IHDR         7   IDAT(c`!40l`NEY!
+/ /韫N@!贈ïgw!
+0Iw/c`9s{`2<f	!	M͏chfx,B#D硶ggE
+ ma6e/_;
+0,C37FW-%t˺ ASL2    IENDB`
\ No newline at end of file
diff --git a/core/themes/stable/js/ckeditor/views/AuralView.js b/core/themes/stable/js/ckeditor/views/AuralView.js
new file mode 100644
index 0000000..6acfb91
--- /dev/null
+++ b/core/themes/stable/js/ckeditor/views/AuralView.js
@@ -0,0 +1,233 @@
+/**
+ * @file
+ * A Backbone View that provides the aural view of CKEditor toolbar
+ * configuration.
+ */
+
+(function (Drupal, Backbone, $) {
+
+  "use strict";
+
+  Drupal.ckeditor.AuralView = Backbone.View.extend(/** @lends Drupal.ckeditor.AuralView# */{
+
+    /**
+     * @type {object}
+     */
+    events: {
+      'click .ckeditor-buttons a': 'announceButtonHelp',
+      'click .ckeditor-multiple-buttons a': 'announceSeparatorHelp',
+      'focus .ckeditor-button a': 'onFocus',
+      'focus .ckeditor-button-separator a': 'onFocus',
+      'focus .ckeditor-toolbar-group': 'onFocus'
+    },
+
+    /**
+     * Backbone View for CKEditor toolbar configuration; aural UX (output only).
+     *
+     * @constructs
+     *
+     * @augments Backbone.View
+     */
+    initialize: function () {
+      // Announce the button and group positions when the model is no longer
+      // dirty.
+      this.listenTo(this.model, 'change:isDirty', this.announceMove);
+    },
+
+    /**
+     * Calls announce on buttons and groups when their position is changed.
+     *
+     * @param {Drupal.ckeditor.ConfigurationModel} model
+     *   The ckeditor configuration model.
+     * @param {bool} isDirty
+     *   A model attribute that indicates if the changed toolbar configuration
+     *   has been stored or not.
+     */
+    announceMove: function (model, isDirty) {
+      // Announce the position of a button or group after the model has been
+      // updated.
+      if (!isDirty) {
+        var item = document.activeElement || null;
+        if (item) {
+          var $item = $(item);
+          if ($item.hasClass('ckeditor-toolbar-group')) {
+            this.announceButtonGroupPosition($item);
+          }
+          else if ($item.parent().hasClass('ckeditor-button')) {
+            this.announceButtonPosition($item.parent());
+          }
+        }
+      }
+    },
+
+    /**
+     * Handles the focus event of elements in the active and available toolbars.
+     *
+     * @param {jQuery.Event} event
+     *   The focus event that was triggered.
+     */
+    onFocus: function (event) {
+      event.stopPropagation();
+
+      var $originalTarget = $(event.target);
+      var $currentTarget = $(event.currentTarget);
+      var $parent = $currentTarget.parent();
+      if ($parent.hasClass('ckeditor-button') || $parent.hasClass('ckeditor-button-separator')) {
+        this.announceButtonPosition($currentTarget.parent());
+      }
+      else if ($originalTarget.attr('role') !== 'button' && $currentTarget.hasClass('ckeditor-toolbar-group')) {
+        this.announceButtonGroupPosition($currentTarget);
+      }
+    },
+
+    /**
+     * Announces the current position of a button group.
+     *
+     * @param {jQuery} $group
+     *   A jQuery set that contains an li element that wraps a group of buttons.
+     */
+    announceButtonGroupPosition: function ($group) {
+      var $groups = $group.parent().children();
+      var $row = $group.closest('.ckeditor-row');
+      var $rows = $row.parent().children();
+      var position = $groups.index($group) + 1;
+      var positionCount = $groups.not('.placeholder').length;
+      var row = $rows.index($row) + 1;
+      var rowCount = $rows.not('.placeholder').length;
+      var text = Drupal.t('@groupName button group in position @position of @positionCount in row @row of @rowCount.', {
+        '@groupName': $group.attr('data-drupal-ckeditor-toolbar-group-name'),
+        '@position': position,
+        '@positionCount': positionCount,
+        '@row': row,
+        '@rowCount': rowCount
+      });
+      // If this position is the first in the last row then tell the user that
+      // pressing the down arrow key will create a new row.
+      if (position === 1 && row === rowCount) {
+        text += "\n";
+        text += Drupal.t("Press the down arrow key to create a new row.");
+      }
+      Drupal.announce(text, 'assertive');
+    },
+
+    /**
+     * Announces current button position.
+     *
+     * @param {jQuery} $button
+     *   A jQuery set that contains an li element that wraps a button.
+     */
+    announceButtonPosition: function ($button) {
+      var $row = $button.closest('.ckeditor-row');
+      var $rows = $row.parent().children();
+      var $buttons = $button.closest('.ckeditor-buttons').children();
+      var $group = $button.closest('.ckeditor-toolbar-group');
+      var $groups = $group.parent().children();
+      var groupPosition = $groups.index($group) + 1;
+      var groupPositionCount = $groups.not('.placeholder').length;
+      var position = $buttons.index($button) + 1;
+      var positionCount = $buttons.length;
+      var row = $rows.index($row) + 1;
+      var rowCount = $rows.not('.placeholder').length;
+      // The name of the button separator is 'button separator' and its type
+      // is 'separator', so we do not want to print the type of this item,
+      // otherwise the UA will speak 'button separator separator'.
+      var type = ($button.attr('data-drupal-ckeditor-type') === 'separator') ? '' : Drupal.t('button');
+      var text;
+      // The button is located in the available button set.
+      if ($button.closest('.ckeditor-toolbar-disabled').length > 0) {
+        text = Drupal.t('@name @type.', {
+          '@name': $button.children().attr('aria-label'),
+          '@type': type
+        });
+        text += "\n" + Drupal.t('Press the down arrow key to activate.');
+
+        Drupal.announce(text, 'assertive');
+      }
+      // The button is in the active toolbar.
+      else if ($group.not('.placeholder').length === 1) {
+        text = Drupal.t('@name @type in position @position of @positionCount in @groupName button group in row @row of @rowCount.', {
+          '@name': $button.children().attr('aria-label'),
+          '@type': type,
+          '@position': position,
+          '@positionCount': positionCount,
+          '@groupName': $group.attr('data-drupal-ckeditor-toolbar-group-name'),
+          '@row': row,
+          '@rowCount': rowCount
+        });
+        // If this position is the first in the last row then tell the user that
+        // pressing the down arrow key will create a new row.
+        if (groupPosition === 1 && position === 1 && row === rowCount) {
+          text += "\n";
+          text += Drupal.t("Press the down arrow key to create a new button group in a new row.");
+        }
+        // If this position is the last one in this row then tell the user that
+        // moving the button to the next group will create a new group.
+        if (groupPosition === groupPositionCount && position === positionCount) {
+          text += "\n";
+          text += Drupal.t("This is the last group. Move the button forward to create a new group.");
+        }
+        Drupal.announce(text, 'assertive');
+      }
+    },
+
+    /**
+     * Provides help information when a button is clicked.
+     *
+     * @param {jQuery.Event} event
+     *   The click event for the button click.
+     */
+    announceButtonHelp: function (event) {
+      var $link = $(event.currentTarget);
+      var $button = $link.parent();
+      var enabled = $button.closest('.ckeditor-toolbar-active').length > 0;
+      var message;
+
+      if (enabled) {
+        message = Drupal.t('The "@name" button is currently enabled.', {
+          '@name': $link.attr('aria-label')
+        });
+        message += "\n" + Drupal.t('Use the keyboard arrow keys to change the position of this button.');
+        message += "\n" + Drupal.t('Press the up arrow key on the top row to disable the button.');
+      }
+      else {
+        message = Drupal.t('The "@name" button is currently disabled.', {
+          '@name': $link.attr('aria-label')
+        });
+        message += "\n" + Drupal.t('Use the down arrow key to move this button into the active toolbar.');
+      }
+      Drupal.announce(message);
+      event.preventDefault();
+    },
+
+    /**
+     * Provides help information when a separator is clicked.
+     *
+     * @param {jQuery.Event} event
+     *   The click event for the separator click.
+     */
+    announceSeparatorHelp: function (event) {
+      var $link = $(event.currentTarget);
+      var $button = $link.parent();
+      var enabled = $button.closest('.ckeditor-toolbar-active').length > 0;
+      var message;
+
+      if (enabled) {
+        message = Drupal.t('This @name is currently enabled.', {
+          '@name': $link.attr('aria-label')
+        });
+        message += "\n" + Drupal.t('Use the keyboard arrow keys to change the position of this separator.');
+      }
+      else {
+        message = Drupal.t('Separators are used to visually split individual buttons.');
+        message += "\n" + Drupal.t('This @name is currently disabled.', {
+          '@name': $link.attr('aria-label')
+        });
+        message += "\n" + Drupal.t('Use the down arrow key to move this separator into the active toolbar.');
+        message += "\n" + Drupal.t('You may add multiple separators to each button group.');
+      }
+      Drupal.announce(message);
+      event.preventDefault();
+    }
+  });
+
+})(Drupal, Backbone, jQuery);
diff --git a/core/themes/stable/js/ckeditor/views/ControllerView.js b/core/themes/stable/js/ckeditor/views/ControllerView.js
new file mode 100644
index 0000000..7cfaeee
--- /dev/null
+++ b/core/themes/stable/js/ckeditor/views/ControllerView.js
@@ -0,0 +1,383 @@
+/**
+ * @file
+ * A Backbone View acting as a controller for CKEditor toolbar configuration.
+ */
+
+(function (Drupal, Backbone, $) {
+
+  "use strict";
+
+  Drupal.ckeditor.ControllerView = Backbone.View.extend(/** @lends Drupal.ckeditor.ControllerView# */{
+
+    /**
+     * @type {object}
+     */
+    events: {},
+
+    /**
+     * Backbone View acting as a controller for CKEditor toolbar configuration.
+     *
+     * @constructs
+     *
+     * @augments Backbone.View
+     */
+    initialize: function () {
+      this.getCKEditorFeatures(this.model.get('hiddenEditorConfig'), this.disableFeaturesDisallowedByFilters.bind(this));
+
+      // Push the active editor configuration to the textarea.
+      this.model.listenTo(this.model, 'change:activeEditorConfig', this.model.sync);
+      this.listenTo(this.model, 'change:isDirty', this.parseEditorDOM);
+    },
+
+    /**
+     * Converts the active toolbar DOM structure to an object representation.
+     *
+     * @param {Drupal.ckeditor.ConfigurationModel} model
+     *   The state model for the CKEditor configuration.
+     * @param {bool} isDirty
+     *   Tracks whether the active toolbar DOM structure has been changed.
+     *   isDirty is toggled back to false in this method.
+     * @param {object} options
+     *   An object that includes:
+     * @param {bool} [options.broadcast]
+     *   A flag that controls whether a CKEditorToolbarChanged event should be
+     *   fired for configuration changes.
+     *
+     * @fires event:CKEditorToolbarChanged
+     */
+    parseEditorDOM: function (model, isDirty, options) {
+      if (isDirty) {
+        var currentConfig = this.model.get('activeEditorConfig');
+
+        // Process the rows.
+        var rows = [];
+        this.$el
+          .find('.ckeditor-active-toolbar-configuration')
+          .children('.ckeditor-row').each(function () {
+            var groups = [];
+            // Process the button groups.
+            $(this).find('.ckeditor-toolbar-group').each(function () {
+              var $group = $(this);
+              var $buttons = $group.find('.ckeditor-button');
+              if ($buttons.length) {
+                var group = {
+                  name: $group.attr('data-drupal-ckeditor-toolbar-group-name'),
+                  items: []
+                };
+                $group.find('.ckeditor-button, .ckeditor-multiple-button').each(function () {
+                  group.items.push($(this).attr('data-drupal-ckeditor-button-name'));
+                });
+                groups.push(group);
+              }
+            });
+            if (groups.length) {
+              rows.push(groups);
+            }
+          });
+        this.model.set('activeEditorConfig', rows);
+        // Mark the model as clean. Whether or not the sync to the textfield
+        // occurs depends on the activeEditorConfig attribute firing a change
+        // event. The DOM has at least been processed and posted, so as far as
+        // the model is concerned, it is clean.
+        this.model.set('isDirty', false);
+
+        // Determine whether we should trigger an event.
+        if (options.broadcast !== false) {
+          var prev = this.getButtonList(currentConfig);
+          var next = this.getButtonList(rows);
+          if (prev.length !== next.length) {
+            this.$el
+              .find('.ckeditor-toolbar-active')
+              .trigger('CKEditorToolbarChanged', [
+                (prev.length < next.length) ? 'added' : 'removed',
+                _.difference(_.union(prev, next), _.intersection(prev, next))[0]
+              ]);
+          }
+        }
+      }
+    },
+
+    /**
+     * Asynchronously retrieve the metadata for all available CKEditor features.
+     *
+     * In order to get a list of all features needed by CKEditor, we create a
+     * hidden CKEditor instance, then check the CKEditor's "allowedContent"
+     * filter settings. Because creating an instance is expensive, a callback
+     * must be provided that will receive a hash of {@link Drupal.EditorFeature}
+     * features keyed by feature (button) name.
+     *
+     * @param {object} CKEditorConfig
+     *   An object that represents the configuration settings for a CKEditor
+     *   editor component.
+     * @param {function} callback
+     *   A function to invoke when the instanceReady event is fired by the
+     *   CKEditor object.
+     */
+    getCKEditorFeatures: function (CKEditorConfig, callback) {
+      var getProperties = function (CKEPropertiesList) {
+        return (_.isObject(CKEPropertiesList)) ? _.keys(CKEPropertiesList) : [];
+      };
+
+      var convertCKERulesToEditorFeature = function (feature, CKEFeatureRules) {
+        for (var i = 0; i < CKEFeatureRules.length; i++) {
+          var CKERule = CKEFeatureRules[i];
+          var rule = new Drupal.EditorFeatureHTMLRule();
+
+          // Tags.
+          var tags = getProperties(CKERule.elements);
+          rule.required.tags = (CKERule.propertiesOnly) ? [] : tags;
+          rule.allowed.tags = tags;
+          // Attributes.
+          rule.required.attributes = getProperties(CKERule.requiredAttributes);
+          rule.allowed.attributes = getProperties(CKERule.attributes);
+          // Styles.
+          rule.required.styles = getProperties(CKERule.requiredStyles);
+          rule.allowed.styles = getProperties(CKERule.styles);
+          // Classes.
+          rule.required.classes = getProperties(CKERule.requiredClasses);
+          rule.allowed.classes = getProperties(CKERule.classes);
+          // Raw.
+          rule.raw = CKERule;
+
+          feature.addHTMLRule(rule);
+        }
+      };
+
+      // Create hidden CKEditor with all features enabled, retrieve metadata.
+      // @see \Drupal\ckeditor\Plugin\Editor\CKEditor::settingsForm.
+      var hiddenCKEditorID = 'ckeditor-hidden';
+      if (CKEDITOR.instances[hiddenCKEditorID]) {
+        CKEDITOR.instances[hiddenCKEditorID].destroy(true);
+      }
+      // Load external plugins, if any.
+      var hiddenEditorConfig = this.model.get('hiddenEditorConfig');
+      if (hiddenEditorConfig.drupalExternalPlugins) {
+        var externalPlugins = hiddenEditorConfig.drupalExternalPlugins;
+        for (var pluginName in externalPlugins) {
+          if (externalPlugins.hasOwnProperty(pluginName)) {
+            CKEDITOR.plugins.addExternal(pluginName, externalPlugins[pluginName], '');
+          }
+        }
+      }
+      CKEDITOR.inline($('#' + hiddenCKEditorID).get(0), CKEditorConfig);
+
+      // Once the instance is ready, retrieve the allowedContent filter rules
+      // and convert them to Drupal.EditorFeature objects.
+      CKEDITOR.once('instanceReady', function (e) {
+        if (e.editor.name === hiddenCKEditorID) {
+          // First collect all CKEditor allowedContent rules.
+          var CKEFeatureRulesMap = {};
+          var rules = e.editor.filter.allowedContent;
+          var rule;
+          var name;
+          for (var i = 0; i < rules.length; i++) {
+            rule = rules[i];
+            name = rule.featureName || ':(';
+            if (!CKEFeatureRulesMap[name]) {
+              CKEFeatureRulesMap[name] = [];
+            }
+            CKEFeatureRulesMap[name].push(rule);
+          }
+
+          // Now convert these to Drupal.EditorFeature objects. And track which
+          // buttons are mapped to which features.
+          // @see getFeatureForButton()
+          var features = {};
+          var buttonsToFeatures = {};
+          for (var featureName in CKEFeatureRulesMap) {
+            if (CKEFeatureRulesMap.hasOwnProperty(featureName)) {
+              var feature = new Drupal.EditorFeature(featureName);
+              convertCKERulesToEditorFeature(feature, CKEFeatureRulesMap[featureName]);
+              features[featureName] = feature;
+              var command = e.editor.getCommand(featureName);
+              if (command) {
+                buttonsToFeatures[command.uiItems[0].name] = featureName;
+              }
+            }
+          }
+
+          callback(features, buttonsToFeatures);
+        }
+      });
+    },
+
+    /**
+     * Retrieves the feature for a given button from featuresMetadata. Returns
+     * false if the given button is in fact a divider.
+     *
+     * @param {string} button
+     *   The name of a CKEditor button.
+     *
+     * @return {object}
+     *   The feature metadata object for a button.
+     */
+    getFeatureForButton: function (button) {
+      // Return false if the button being added is a divider.
+      if (button === '-') {
+        return false;
+      }
+
+      // Get a Drupal.editorFeature object that contains all metadata for
+      // the feature that was just added or removed. Not every feature has
+      // such metadata.
+      var featureName = this.model.get('buttonsToFeatures')[button.toLowerCase()];
+      // Features without an associated command do not have a 'feature name' by
+      // default, so we use the lowercased button name instead.
+      if (!featureName) {
+        featureName = button.toLowerCase();
+      }
+      var featuresMetadata = this.model.get('featuresMetadata');
+      if (!featuresMetadata[featureName]) {
+        featuresMetadata[featureName] = new Drupal.EditorFeature(featureName);
+        this.model.set('featuresMetadata', featuresMetadata);
+      }
+      return featuresMetadata[featureName];
+    },
+
+    /**
+     * Checks buttons against filter settings; disables disallowed buttons.
+     *
+     * @param {object} features
+     *   A map of {@link Drupal.EditorFeature} objects.
+     * @param {object} buttonsToFeatures
+     *   Object containing the button-to-feature mapping.
+     *
+     * @see Drupal.ckeditor.ControllerView#getFeatureForButton
+     */
+    disableFeaturesDisallowedByFilters: function (features, buttonsToFeatures) {
+      this.model.set('featuresMetadata', features);
+      // Store the button-to-feature mapping. Needs to happen only once, because
+      // the same buttons continue to have the same features; only the rules for
+      // specific features may change.
+      // @see getFeatureForButton()
+      this.model.set('buttonsToFeatures', buttonsToFeatures);
+
+      // Ensure that toolbar configuration changes are broadcast.
+      this.broadcastConfigurationChanges(this.$el);
+
+      // Initialization: not all of the default toolbar buttons may be allowed
+      // by the current filter settings. Remove any of the default toolbar
+      // buttons that require more permissive filter settings. The remaining
+      // default toolbar buttons are marked as "added".
+      var existingButtons = [];
+      // Loop through each button group after flattening the groups from the
+      // toolbar row arrays.
+      var buttonGroups = _.flatten(this.model.get('activeEditorConfig'));
+      for (var i = 0; i < buttonGroups.length; i++) {
+        // Pull the button names from each toolbar button group.
+        var buttons = buttonGroups[i].items;
+        for (var k = 0; k < buttons.length; k++) {
+          existingButtons.push(buttons[k]);
+        }
+      }
+      // Remove duplicate buttons.
+      existingButtons = _.unique(existingButtons);
+      // Prepare the active toolbar and available-button toolbars.
+      for (var n = 0; n < existingButtons.length; n++) {
+        var button = existingButtons[n];
+        var feature = this.getFeatureForButton(button);
+        // Skip dividers.
+        if (feature === false) {
+          continue;
+        }
+
+        if (Drupal.editorConfiguration.featureIsAllowedByFilters(feature)) {
+          // Existing toolbar buttons are in fact "added features".
+          this.$el.find('.ckeditor-toolbar-active').trigger('CKEditorToolbarChanged', ['added', existingButtons[n]]);
+        }
+        else {
+          // Move the button element from the active the active toolbar to the
+          // list of available buttons.
+          $('.ckeditor-toolbar-active li[data-drupal-ckeditor-button-name="' + button + '"]')
+            .detach()
+            .appendTo('.ckeditor-toolbar-disabled > .ckeditor-toolbar-available > ul');
+          // Update the toolbar value field.
+          this.model.set({isDirty: true}, {broadcast: false});
+        }
+      }
+    },
+
+    /**
+     * Sets up broadcasting of CKEditor toolbar configuration changes.
+     *
+     * @param {jQuery} $ckeditorToolbar
+     *   The active toolbar DOM element wrapped in jQuery.
+     */
+    broadcastConfigurationChanges: function ($ckeditorToolbar) {
+      var view = this;
+      var hiddenEditorConfig = this.model.get('hiddenEditorConfig');
+      var getFeatureForButton = this.getFeatureForButton.bind(this);
+      var getCKEditorFeatures = this.getCKEditorFeatures.bind(this);
+      $ckeditorToolbar
+        .find('.ckeditor-toolbar-active')
+        // Listen for CKEditor toolbar configuration changes. When a button is
+        // added/removed, call an appropriate Drupal.editorConfiguration method.
+        .on('CKEditorToolbarChanged.ckeditorAdmin', function (event, action, button) {
+          var feature = getFeatureForButton(button);
+
+          // Early-return if the button being added is a divider.
+          if (feature === false) {
+            return;
+          }
+
+          // Trigger a standardized text editor configuration event to indicate
+          // whether a feature was added or removed, so that filters can react.
+          var configEvent = (action === 'added') ? 'addedFeature' : 'removedFeature';
+          Drupal.editorConfiguration[configEvent](feature);
+        })
+        // Listen for CKEditor plugin settings changes. When a plugin setting is
+        // changed, rebuild the CKEditor features metadata.
+        .on('CKEditorPluginSettingsChanged.ckeditorAdmin', function (event, settingsChanges) {
+          // Update hidden CKEditor configuration.
+          for (var key in settingsChanges) {
+            if (settingsChanges.hasOwnProperty(key)) {
+              hiddenEditorConfig[key] = settingsChanges[key];
+            }
+          }
+
+          // Retrieve features for the updated hidden CKEditor configuration.
+          getCKEditorFeatures(hiddenEditorConfig, function (features) {
+            // Trigger a standardized text editor configuration event for each
+            // feature that was modified by the configuration changes.
+            var featuresMetadata = view.model.get('featuresMetadata');
+            for (var name in features) {
+              if (features.hasOwnProperty(name)) {
+                var feature = features[name];
+                if (featuresMetadata.hasOwnProperty(name) && !_.isEqual(featuresMetadata[name], feature)) {
+                  Drupal.editorConfiguration.modifiedFeature(feature);
+                }
+              }
+            }
+            // Update the CKEditor features metadata.
+            view.model.set('featuresMetadata', features);
+          });
+        });
+    },
+
+    /**
+     * Returns the list of buttons from an editor configuration.
+     *
+     * @param {object} config
+     *   A CKEditor configuration object.
+     *
+     * @return {Array}
+     *   A list of buttons in the CKEditor configuration.
+     */
+    getButtonList: function (config) {
+      var buttons = [];
+      // Remove the rows.
+      config = _.flatten(config);
+
+      // Loop through the button groups and pull out the buttons.
+      config.forEach(function (group) {
+        group.items.forEach(function (button) {
+          buttons.push(button);
+        });
+      });
+
+      // Remove the dividing elements if any.
+      return _.without(buttons, '-');
+    }
+  });
+
+})(Drupal, Backbone, jQuery);
diff --git a/core/themes/stable/js/ckeditor/views/KeyboardView.js b/core/themes/stable/js/ckeditor/views/KeyboardView.js
new file mode 100644
index 0000000..6425980
--- /dev/null
+++ b/core/themes/stable/js/ckeditor/views/KeyboardView.js
@@ -0,0 +1,266 @@
+/**
+ * @file
+ * Backbone View providing the aural view of CKEditor keyboard UX configuration.
+ */
+
+(function (Drupal, Backbone, $) {
+
+  "use strict";
+
+  Drupal.ckeditor.KeyboardView = Backbone.View.extend(/** @lends Drupal.ckeditor.KeyboardView# */{
+
+    /**
+     * Backbone View for CKEditor toolbar configuration; keyboard UX.
+     *
+     * @constructs
+     *
+     * @augments Backbone.View
+     */
+    initialize: function () {
+      // Add keyboard arrow support.
+      this.$el.on('keydown.ckeditor', '.ckeditor-buttons a, .ckeditor-multiple-buttons a', this.onPressButton.bind(this));
+      this.$el.on('keydown.ckeditor', '[data-drupal-ckeditor-type="group"]', this.onPressGroup.bind(this));
+    },
+
+    /**
+     * @inheritdoc
+     */
+    render: function () {
+    },
+
+    /**
+     * Handles keypresses on a CKEditor configuration button.
+     *
+     * @param {jQuery.Event} event
+     *   The keypress event triggered.
+     */
+    onPressButton: function (event) {
+      var upDownKeys = [
+        38, // Up arrow.
+        63232, // Safari up arrow.
+        40, // Down arrow.
+        63233 // Safari down arrow.
+      ];
+      var leftRightKeys = [
+        37, // Left arrow.
+        63234, // Safari left arrow.
+        39, // Right arrow.
+        63235 // Safari right arrow.
+      ];
+
+      // Respond to an enter key press. Prevent the bubbling of the enter key
+      // press to the button group parent element.
+      if (event.keyCode === 13) {
+        event.stopPropagation();
+      }
+
+      // Only take action when a direction key is pressed.
+      if (_.indexOf(_.union(upDownKeys, leftRightKeys), event.keyCode) > -1) {
+        var view = this;
+        var $target = $(event.currentTarget);
+        var $button = $target.parent();
+        var $container = $button.parent();
+        var $group = $button.closest('.ckeditor-toolbar-group');
+        var $row = $button.closest('.ckeditor-row');
+        var containerType = $container.data('drupal-ckeditor-button-sorting');
+        var $availableButtons = this.$el.find('[data-drupal-ckeditor-button-sorting="source"]');
+        var $activeButtons = this.$el.find('.ckeditor-toolbar-active');
+        // The current location of the button, just in case it needs to be put
+        // back.
+        var $originalGroup = $group;
+        var dir;
+
+        // Move available buttons between their container and the active
+        // toolbar.
+        if (containerType === 'source') {
+          // Move the button to the active toolbar configuration when the down
+          // or up keys are pressed.
+          if (_.indexOf([40, 63233], event.keyCode) > -1) {
+            // Move the button to the first row, first button group index
+            // position.
+            $activeButtons.find('.ckeditor-toolbar-group-buttons').eq(0).prepend($button);
+          }
+        }
+        else if (containerType === 'target') {
+          // Move buttons between sibling buttons in a group and between groups.
+          if (_.indexOf(leftRightKeys, event.keyCode) > -1) {
+            // Move left.
+            var $siblings = $container.children();
+            var index = $siblings.index($button);
+            if (_.indexOf([37, 63234], event.keyCode) > -1) {
+              // Move between sibling buttons.
+              if (index > 0) {
+                $button.insertBefore($container.children().eq(index - 1));
+              }
+              // Move between button groups and rows.
+              else {
+                // Move between button groups.
+                $group = $container.parent().prev();
+                if ($group.length > 0) {
+                  $group.find('.ckeditor-toolbar-group-buttons').append($button);
+                }
+                // Wrap between rows.
+                else {
+                  $container.closest('.ckeditor-row').prev().find('.ckeditor-toolbar-group').not('.placeholder').find('.ckeditor-toolbar-group-buttons').eq(-1).append($button);
+                }
+              }
+            }
+            // Move right.
+            else if (_.indexOf([39, 63235], event.keyCode) > -1) {
+              // Move between sibling buttons.
+              if (index < ($siblings.length - 1)) {
+                $button.insertAfter($container.children().eq(index + 1));
+              }
+              // Move between button groups. Moving right at the end of a row
+              // will create a new group.
+              else {
+                $container.parent().next().find('.ckeditor-toolbar-group-buttons').prepend($button);
+              }
+            }
+          }
+          // Move buttons between rows and the available button set.
+          else if (_.indexOf(upDownKeys, event.keyCode) > -1) {
+            dir = (_.indexOf([38, 63232], event.keyCode) > -1) ? 'prev' : 'next';
+            $row = $container.closest('.ckeditor-row')[dir]();
+            // Move the button back into the available button set.
+            if (dir === 'prev' && $row.length === 0) {
+              // If this is a divider, just destroy it.
+              if ($button.data('drupal-ckeditor-type') === 'separator') {
+                $button
+                  .off()
+                  .remove();
+                // Focus on the first button in the active toolbar.
+                $activeButtons.find('.ckeditor-toolbar-group-buttons').eq(0).children().eq(0).children().trigger('focus');
+              }
+              // Otherwise, move it.
+              else {
+                $availableButtons.prepend($button);
+              }
+            }
+            else {
+              $row.find('.ckeditor-toolbar-group-buttons').eq(0).prepend($button);
+            }
+          }
+        }
+        // Move dividers between their container and the active toolbar.
+        else if (containerType === 'dividers') {
+          // Move the button to the active toolbar configuration when the down
+          // or up keys are pressed.
+          if (_.indexOf([40, 63233], event.keyCode) > -1) {
+            // Move the button to the first row, first button group index
+            // position.
+            $button = $button.clone(true);
+            $activeButtons.find('.ckeditor-toolbar-group-buttons').eq(0).prepend($button);
+            $target = $button.children();
+          }
+        }
+
+        view = this;
+        // Attempt to move the button to the new toolbar position.
+        Drupal.ckeditor.registerButtonMove(this, $button, function (result) {
+
+          // Put the button back if the registration failed.
+          // If the button was in a row, then it was in the active toolbar
+          // configuration. The button was probably placed in a new group, but
+          // that action was canceled.
+          if (!result && $originalGroup) {
+            $originalGroup.find('.ckeditor-buttons').append($button);
+          }
+          // Otherwise refresh the sortables to acknowledge the new button
+          // positions.
+          else {
+            view.$el.find('.ui-sortable').sortable('refresh');
+          }
+          // Refocus the target button so that the user can continue from a
+          // known place.
+          $target.trigger('focus');
+        });
+
+        event.preventDefault();
+        event.stopPropagation();
+      }
+    },
+
+    /**
+     * Handles keypresses on a CKEditor configuration group.
+     *
+     * @param {jQuery.Event} event
+     *   The keypress event triggered.
+     */
+    onPressGroup: function (event) {
+      var upDownKeys = [
+        38, // Up arrow.
+        63232, // Safari up arrow.
+        40, // Down arrow.
+        63233 // Safari down arrow.
+      ];
+      var leftRightKeys = [
+        37, // Left arrow.
+        63234, // Safari left arrow.
+        39, // Right arrow.
+        63235 // Safari right arrow.
+      ];
+
+      // Respond to an enter key press.
+      if (event.keyCode === 13) {
+        var view = this;
+        // Open the group renaming dialog in the next evaluation cycle so that
+        // this event can be cancelled and the bubbling wiped out. Otherwise,
+        // Firefox has issues because the page focus is shifted to the dialog
+        // along with the keydown event.
+        window.setTimeout(function () {
+          Drupal.ckeditor.openGroupNameDialog(view, $(event.currentTarget));
+        }, 0);
+        event.preventDefault();
+        event.stopPropagation();
+      }
+
+      // Respond to direction key presses.
+      if (_.indexOf(_.union(upDownKeys, leftRightKeys), event.keyCode) > -1) {
+        var $group = $(event.currentTarget);
+        var $container = $group.parent();
+        var $siblings = $container.children();
+        var index;
+        var dir;
+        // Move groups between sibling groups.
+        if (_.indexOf(leftRightKeys, event.keyCode) > -1) {
+          index = $siblings.index($group);
+          // Move left between sibling groups.
+          if ((_.indexOf([37, 63234], event.keyCode) > -1)) {
+            if (index > 0) {
+              $group.insertBefore($siblings.eq(index - 1));
+            }
+            // Wrap between rows. Insert the group before the placeholder group
+            // at the end of the previous row.
+            else {
+              $group.insertBefore($container.closest('.ckeditor-row').prev().find('.ckeditor-toolbar-groups').children().eq(-1));
+            }
+          }
+          // Move right between sibling groups.
+          else if (_.indexOf([39, 63235], event.keyCode) > -1) {
+            // Move to the right if the next group is not a placeholder.
+            if (!$siblings.eq(index + 1).hasClass('placeholder')) {
+              $group.insertAfter($container.children().eq(index + 1));
+            }
+            // Wrap group between rows.
+            else {
+              $container.closest('.ckeditor-row').next().find('.ckeditor-toolbar-groups').prepend($group);
+            }
+          }
+
+        }
+        // Move groups between rows.
+        else if (_.indexOf(upDownKeys, event.keyCode) > -1) {
+          dir = (_.indexOf([38, 63232], event.keyCode) > -1) ? 'prev' : 'next';
+          $group.closest('.ckeditor-row')[dir]().find('.ckeditor-toolbar-groups').eq(0).prepend($group);
+        }
+
+        Drupal.ckeditor.registerGroupMove(this, $group);
+        $group.trigger('focus');
+        event.preventDefault();
+        event.stopPropagation();
+      }
+    }
+  });
+
+})(Drupal, Backbone, jQuery);
diff --git a/core/themes/stable/js/ckeditor/views/VisualView.js b/core/themes/stable/js/ckeditor/views/VisualView.js
new file mode 100644
index 0000000..c165152
--- /dev/null
+++ b/core/themes/stable/js/ckeditor/views/VisualView.js
@@ -0,0 +1,273 @@
+/**
+ * @file
+ * A Backbone View that provides the visual UX view of CKEditor toolbar
+ *   configuration.
+ */
+
+(function (Drupal, Backbone, $) {
+
+  "use strict";
+
+  Drupal.ckeditor.VisualView = Backbone.View.extend(/** @lends Drupal.ckeditor.VisualView# */{
+
+    events: {
+      'click .ckeditor-toolbar-group-name': 'onGroupNameClick',
+      'click .ckeditor-groupnames-toggle': 'onGroupNamesToggleClick',
+      'click .ckeditor-add-new-group button': 'onAddGroupButtonClick'
+    },
+
+    /**
+     * Backbone View for CKEditor toolbar configuration; visual UX.
+     *
+     * @constructs
+     *
+     * @augments Backbone.View
+     */
+    initialize: function () {
+      this.listenTo(this.model, 'change:isDirty change:groupNamesVisible', this.render);
+
+      // Add a toggle for the button group names.
+      $(Drupal.theme('ckeditorButtonGroupNamesToggle'))
+        .prependTo(this.$el.find('#ckeditor-active-toolbar').parent());
+
+      this.render();
+    },
+
+    /**
+     * Render function for rendering the toolbar configuration.
+     *
+     * @param {*} model
+     *   Model used for the view.
+     * @param {string} [value]
+     *   The value that was changed.
+     * @param {object} changedAttributes
+     *   The attributes that was changed.
+     *
+     * @return {Drupal.ckeditor.VisualView}
+     *   The {@link Drupal.ckeditor.VisualView} object.
+     */
+    render: function (model, value, changedAttributes) {
+      this.insertPlaceholders();
+      this.applySorting();
+
+      // Toggle button group names.
+      var groupNamesVisible = this.model.get('groupNamesVisible');
+      // If a button was just placed in the active toolbar, ensure that the
+      // button group names are visible.
+      if (changedAttributes && changedAttributes.changes && changedAttributes.changes.isDirty) {
+        this.model.set({groupNamesVisible: true}, {silent: true});
+        groupNamesVisible = true;
+      }
+      this.$el.find('[data-toolbar="active"]').toggleClass('ckeditor-group-names-are-visible', groupNamesVisible);
+      this.$el.find('.ckeditor-groupnames-toggle')
+        .text((groupNamesVisible) ? Drupal.t('Hide group names') : Drupal.t('Show group names'))
+        .attr('aria-pressed', groupNamesVisible);
+
+      return this;
+    },
+
+    /**
+     * Handles clicks to a button group name.
+     *
+     * @param {jQuery.Event} event
+     *   The click event on the button group.
+     */
+    onGroupNameClick: function (event) {
+      var $group = $(event.currentTarget).closest('.ckeditor-toolbar-group');
+      Drupal.ckeditor.openGroupNameDialog(this, $group);
+
+      event.stopPropagation();
+      event.preventDefault();
+    },
+
+    /**
+     * Handles clicks on the button group names toggle button.
+     *
+     * @param {jQuery.Event} event
+     *   The click event on the toggle button.
+     */
+    onGroupNamesToggleClick: function (event) {
+      this.model.set('groupNamesVisible', !this.model.get('groupNamesVisible'));
+      event.preventDefault();
+    },
+
+    /**
+     * Prompts the user to provide a name for a new button group; inserts it.
+     *
+     * @param {jQuery.Event} event
+     *   The event of the button click.
+     */
+    onAddGroupButtonClick: function (event) {
+
+      /**
+       * Inserts a new button if the openGroupNameDialog function returns true.
+       *
+       * @param {bool} success
+       *   A flag that indicates if the user created a new group (true) or
+       *   canceled out of the dialog (false).
+       * @param {jQuery} $group
+       *   A jQuery DOM fragment that represents the new button group. It has
+       *   not been added to the DOM yet.
+       */
+      function insertNewGroup(success, $group) {
+        if (success) {
+          $group.appendTo($(event.currentTarget).closest('.ckeditor-row').children('.ckeditor-toolbar-groups'));
+          // Focus on the new group.
+          $group.trigger('focus');
+        }
+      }
+
+      // Pass in a DOM fragment of a placeholder group so that the new group
+      // name can be applied to it.
+      Drupal.ckeditor.openGroupNameDialog(this, $(Drupal.theme('ckeditorToolbarGroup')), insertNewGroup);
+
+      event.preventDefault();
+    },
+
+    /**
+     * Handles jQuery Sortable stop sort of a button group.
+     *
+     * @param {jQuery.Event} event
+     *   The event triggered on the group drag.
+     * @param {object} ui
+     *   A jQuery.ui.sortable argument that contains information about the
+     *   elements involved in the sort action.
+     */
+    endGroupDrag: function (event, ui) {
+      var view = this;
+      Drupal.ckeditor.registerGroupMove(this, ui.item, function (success) {
+        if (!success) {
+          // Cancel any sorting in the configuration area.
+          view.$el.find('.ckeditor-toolbar-configuration').find('.ui-sortable').sortable('cancel');
+        }
+      });
+    },
+
+    /**
+     * Handles jQuery Sortable start sort of a button.
+     *
+     * @param {jQuery.Event} event
+     *   The event triggered on the group drag.
+     * @param {object} ui
+     *   A jQuery.ui.sortable argument that contains information about the
+     *   elements involved in the sort action.
+     */
+    startButtonDrag: function (event, ui) {
+      this.$el.find('a:focus').trigger('blur');
+
+      // Show the button group names as soon as the user starts dragging.
+      this.model.set('groupNamesVisible', true);
+    },
+
+    /**
+     * Handles jQuery Sortable stop sort of a button.
+     *
+     * @param {jQuery.Event} event
+     *   The event triggered on the button drag.
+     * @param {object} ui
+     *   A jQuery.ui.sortable argument that contains information about the
+     *   elements involved in the sort action.
+     */
+    endButtonDrag: function (event, ui) {
+      var view = this;
+      Drupal.ckeditor.registerButtonMove(this, ui.item, function (success) {
+        if (!success) {
+          // Cancel any sorting in the configuration area.
+          view.$el.find('.ui-sortable').sortable('cancel');
+        }
+        // Refocus the target button so that the user can continue from a known
+        // place.
+        ui.item.find('a').trigger('focus');
+      });
+    },
+
+    /**
+     * Invokes jQuery.sortable() on new buttons and groups in a CKEditor config.
+     */
+    applySorting: function () {
+      // Make the buttons sortable.
+      this.$el.find('.ckeditor-buttons').not('.ui-sortable').sortable({
+        // Change this to .ckeditor-toolbar-group-buttons.
+        connectWith: '.ckeditor-buttons',
+        placeholder: 'ckeditor-button-placeholder',
+        forcePlaceholderSize: true,
+        tolerance: 'pointer',
+        cursor: 'move',
+        start: this.startButtonDrag.bind(this),
+        // Sorting within a sortable.
+        stop: this.endButtonDrag.bind(this)
+      }).disableSelection();
+
+      // Add the drag and drop functionality to button groups.
+      this.$el.find('.ckeditor-toolbar-groups').not('.ui-sortable').sortable({
+        connectWith: '.ckeditor-toolbar-groups',
+        cancel: '.ckeditor-add-new-group',
+        placeholder: 'ckeditor-toolbar-group-placeholder',
+        forcePlaceholderSize: true,
+        cursor: 'move',
+        stop: this.endGroupDrag.bind(this)
+      });
+
+      // Add the drag and drop functionality to buttons.
+      this.$el.find('.ckeditor-multiple-buttons li').draggable({
+        connectToSortable: '.ckeditor-toolbar-active .ckeditor-buttons',
+        helper: 'clone'
+      });
+    },
+
+    /**
+     * Wraps the invocation of methods to insert blank groups and rows.
+     */
+    insertPlaceholders: function () {
+      this.insertPlaceholderRow();
+      this.insertNewGroupButtons();
+    },
+
+    /**
+     * Inserts a blank row at the bottom of the CKEditor configuration.
+     */
+    insertPlaceholderRow: function () {
+      var $rows = this.$el.find('.ckeditor-row');
+      // Add a placeholder row. to the end of the list if one does not exist.
+      if (!$rows.eq(-1).hasClass('placeholder')) {
+        this.$el
+          .find('.ckeditor-toolbar-active')
+          .children('.ckeditor-active-toolbar-configuration')
+          .append(Drupal.theme('ckeditorRow'));
+      }
+      // Update the $rows variable to include the new row.
+      $rows = this.$el.find('.ckeditor-row');
+      // Remove blank rows except the last one.
+      var len = $rows.length;
+      $rows.filter(function (index, row) {
+        // Do not remove the last row.
+        if (index + 1 === len) {
+          return false;
+        }
+        return $(row).find('.ckeditor-toolbar-group').not('.placeholder').length === 0;
+      })
+        // Then get all rows that are placeholders and remove them.
+        .remove();
+    },
+
+    /**
+     * Inserts a button in each row that will add a new CKEditor button group.
+     */
+    insertNewGroupButtons: function () {
+      // Insert an add group button to each row.
+      this.$el.find('.ckeditor-row').each(function () {
+        var $row = $(this);
+        var $groups = $row.find('.ckeditor-toolbar-group');
+        var $button = $row.find('.ckeditor-add-new-group');
+        if ($button.length === 0) {
+          $row.children('.ckeditor-toolbar-groups').append(Drupal.theme('ckeditorNewButtonGroup'));
+        }
+        // If a placeholder group exists, make sure it's at the end of the row.
+        else if (!$groups.eq(-1).hasClass('ckeditor-add-new-group')) {
+          $button.appendTo($row.children('.ckeditor-toolbar-groups'));
+        }
+      });
+    }
+  });
+
+})(Drupal, Backbone, jQuery);
diff --git a/core/themes/stable/js/color/color.js b/core/themes/stable/js/color/color.js
new file mode 100644
index 0000000..437dffe
--- /dev/null
+++ b/core/themes/stable/js/color/color.js
@@ -0,0 +1,297 @@
+/**
+ * @file
+ * Attaches the behaviors for the Color module.
+ */
+
+(function ($) {
+
+  "use strict";
+
+  /**
+   * Displays farbtastic color selector and initialize color administration UI.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attach color selection behavior to relevant context.
+   */
+  Drupal.behaviors.color = {
+    attach: function (context, settings) {
+      var i;
+      var j;
+      var colors;
+      // This behavior attaches by ID, so is only valid once on a page.
+      var form = $(context).find('#system-theme-settings .color-form').once('color');
+      if (form.length === 0) {
+        return;
+      }
+      var inputs = [];
+      var hooks = [];
+      var locks = [];
+      var focused = null;
+
+      // Add Farbtastic.
+      $('<div class="color-placeholder"></div>').once('color').prependTo(form);
+      var farb = $.farbtastic('.color-placeholder');
+
+      // Decode reference colors to HSL.
+      var reference = settings.color.reference;
+      for (i in reference) {
+        if (reference.hasOwnProperty(i)) {
+          reference[i] = farb.RGBToHSL(farb.unpack(reference[i]));
+        }
+      }
+
+      // Build a preview.
+      var height = [];
+      var width = [];
+      // Loop through all defined gradients.
+      for (i in settings.gradients) {
+        if (settings.gradients.hasOwnProperty(i)) {
+          // Add element to display the gradient.
+          $('.color-preview').once('color').append('<div id="gradient-' + i + '"></div>');
+          var gradient = $('.color-preview #gradient-' + i);
+          // Add height of current gradient to the list (divided by 10).
+          height.push(parseInt(gradient.css('height'), 10) / 10);
+          // Add width of current gradient to the list (divided by 10).
+          width.push(parseInt(gradient.css('width'), 10) / 10);
+          // Add rows (or columns for horizontal gradients).
+          // Each gradient line should have a height (or width for horizontal
+          // gradients) of 10px (because we divided the height/width by 10
+          // above).
+          for (j = 0; j < (settings.gradients[i].direction === 'vertical' ? height[i] : width[i]); ++j) {
+            gradient.append('<div class="gradient-line"></div>');
+          }
+        }
+      }
+
+      // Set up colorScheme selector.
+      form.find('#edit-scheme').on('change', function () {
+        var schemes = settings.color.schemes;
+        var colorScheme = this.options[this.selectedIndex].value;
+        if (colorScheme !== '' && schemes[colorScheme]) {
+          // Get colors of active scheme.
+          colors = schemes[colorScheme];
+          for (var fieldName in colors) {
+            if (colors.hasOwnProperty(fieldName)) {
+              callback($('#edit-palette-' + fieldName), colors[fieldName], false, true);
+            }
+          }
+          preview();
+        }
+      });
+
+      /**
+       * Renders the preview.
+       */
+      function preview() {
+        Drupal.color.callback(context, settings, form, farb, height, width);
+      }
+
+      /**
+       * Shifts a given color, using a reference pair (ref in HSL).
+       *
+       * This algorithm ensures relative ordering on the saturation and
+       * luminance axes is preserved, and performs a simple hue shift.
+       *
+       * It is also symmetrical. If: shift_color(c, a, b) === d, then
+       * shift_color(d, b, a) === c.
+       *
+       * @function Drupal.color~shift_color
+       *
+       * @param {string} given
+       *   A hex color code to shift.
+       * @param {Array.<number>} ref1
+       *   First HSL color reference.
+       * @param {Array.<number>} ref2
+       *   Second HSL color reference.
+       *
+       * @return {string}
+       *   A hex color, shifted.
+       */
+      function shift_color(given, ref1, ref2) {
+        var d;
+        // Convert to HSL.
+        given = farb.RGBToHSL(farb.unpack(given));
+
+        // Hue: apply delta.
+        given[0] += ref2[0] - ref1[0];
+
+        // Saturation: interpolate.
+        if (ref1[1] === 0 || ref2[1] === 0) {
+          given[1] = ref2[1];
+        }
+        else {
+          d = ref1[1] / ref2[1];
+          if (d > 1) {
+            given[1] /= d;
+          }
+          else {
+            given[1] = 1 - (1 - given[1]) * d;
+          }
+        }
+
+        // Luminance: interpolate.
+        if (ref1[2] === 0 || ref2[2] === 0) {
+          given[2] = ref2[2];
+        }
+        else {
+          d = ref1[2] / ref2[2];
+          if (d > 1) {
+            given[2] /= d;
+          }
+          else {
+            given[2] = 1 - (1 - given[2]) * d;
+          }
+        }
+
+        return farb.pack(farb.HSLToRGB(given));
+      }
+
+      /**
+       * Callback for Farbtastic when a new color is chosen.
+       *
+       * @param {HTMLElement} input
+       *   The input element where the color is chosen.
+       * @param {string} color
+       *   The color that was chosen through the input.
+       * @param {bool} propagate
+       *   Whether or not to propagate the color to a locked pair value
+       * @param {bool} colorScheme
+       *   Flag to indicate if the user is using a color scheme when changing
+       *   the color.
+       */
+      function callback(input, color, propagate, colorScheme) {
+        var matched;
+        // Set background/foreground colors.
+        $(input).css({
+          backgroundColor: color,
+          color: farb.RGBToHSL(farb.unpack(color))[2] > 0.5 ? '#000' : '#fff'
+        });
+
+        // Change input value.
+        if ($(input).val() && $(input).val() !== color) {
+          $(input).val(color);
+
+          // Update locked values.
+          if (propagate) {
+            i = input.i;
+            for (j = i + 1; ; ++j) {
+              if (!locks[j - 1] || $(locks[j - 1]).is('.is-unlocked')) {
+                break;
+              }
+              matched = shift_color(color, reference[input.key], reference[inputs[j].key]);
+              callback(inputs[j], matched, false);
+            }
+            for (j = i - 1; ; --j) {
+              if (!locks[j] || $(locks[j]).is('.is-unlocked')) {
+                break;
+              }
+              matched = shift_color(color, reference[input.key], reference[inputs[j].key]);
+              callback(inputs[j], matched, false);
+            }
+
+            // Update preview.
+            preview();
+          }
+
+          // Reset colorScheme selector.
+          if (!colorScheme) {
+            resetScheme();
+          }
+        }
+      }
+
+      /**
+       * Resets the color scheme selector.
+       */
+      function resetScheme() {
+        form.find('#edit-scheme').each(function () {
+          this.selectedIndex = this.options.length - 1;
+        });
+      }
+
+      /**
+       * Focuses Farbtastic on a particular field.
+       *
+       * @param {jQuery.Event} e
+       *   The focus event on the field.
+       */
+      function focus(e) {
+        var input = e.target;
+        // Remove old bindings.
+        if (focused) {
+          $(focused).off('keyup', farb.updateValue)
+            .off('keyup', preview).off('keyup', resetScheme)
+            .parent().removeClass('item-selected');
+        }
+
+        // Add new bindings.
+        focused = input;
+        farb.linkTo(function (color) { callback(input, color, true, false); });
+        farb.setColor(input.value);
+        $(focused).on('keyup', farb.updateValue).on('keyup', preview).on('keyup', resetScheme)
+          .parent().addClass('item-selected');
+      }
+
+      // Initialize color fields.
+      form.find('.js-color-palette input.form-text')
+        .each(function () {
+          // Extract palette field name.
+          this.key = this.id.substring(13);
+
+          // Link to color picker temporarily to initialize.
+          farb.linkTo(function () {}).setColor('#000').linkTo(this);
+
+          // Add lock.
+          var i = inputs.length;
+          if (inputs.length) {
+            var toggleClick = true;
+            var lock = $('<button class="color-palette__lock link">' + Drupal.t('Unlock') + '</button>').on('click', function (e) {
+              e.preventDefault();
+              if (toggleClick) {
+                $(this).addClass('is-unlocked').html(Drupal.t('Lock'));
+                $(hooks[i - 1]).attr('class',
+                  locks[i - 2] && $(locks[i - 2]).is(':not(.is-unlocked)') ? 'color-palette__hook is-up' : 'color-palette__hook'
+                );
+                $(hooks[i]).attr('class',
+                  locks[i] && $(locks[i]).is(':not(.is-unlocked)') ? 'color-palette__hook is-down' : 'color-palette__hook'
+                );
+              }
+              else {
+                $(this).removeClass('is-unlocked').html(Drupal.t('Unlock'));
+                $(hooks[i - 1]).attr('class',
+                  locks[i - 2] && $(locks[i - 2]).is(':not(.is-unlocked)') ? 'color-palette__hook is-both' : 'color-palette__hook is-down'
+                );
+                $(hooks[i]).attr('class',
+                  locks[i] && $(locks[i]).is(':not(.is-unlocked)') ? 'color-palette__hook is-both' : 'color-palette__hook is-up'
+                );
+              }
+              toggleClick = !toggleClick;
+            });
+            $(this).after(lock);
+            locks.push(lock);
+          }
+
+          // Add hook.
+          var hook = $('<div class="color-palette__hook"></div>');
+          $(this).after(hook);
+          hooks.push(hook);
+
+          $(this).parent().find('.color-palette__lock').trigger('click');
+          this.i = i;
+          inputs.push(this);
+        })
+        .on('focus', focus);
+
+      form.find('.js-color-palette label');
+
+      // Focus first color.
+      inputs[0].focus();
+
+      // Render preview.
+      preview();
+    }
+  };
+
+})(jQuery);
diff --git a/core/themes/stable/js/color/preview.js b/core/themes/stable/js/color/preview.js
new file mode 100644
index 0000000..551cfd7
--- /dev/null
+++ b/core/themes/stable/js/color/preview.js
@@ -0,0 +1,74 @@
+/**
+ * @file
+ * Attaches preview-related behavior for the Color module.
+ */
+
+(function ($) {
+
+  "use strict";
+
+  /**
+   * Namespace for color-related functionality for Drupal.
+   *
+   * @namespace
+   */
+  Drupal.color = {
+
+    /**
+     * The callback for when the color preview has been attached.
+     *
+     * @param {Element} context
+     *   The context to initiate the color behaviour.
+     * @param {object} settings
+     *   Settings for the color functionality.
+     * @param {HTMLFormElement} form
+     *   The form to initiate the color behaviour on.
+     * @param {object} farb
+     *   The farbtastic object.
+     * @param {number} height
+     *   Height of gradient.
+     * @param {number} width
+     *   Width of gradient.
+     */
+    callback: function (context, settings, form, farb, height, width) {
+      var accum;
+      var delta;
+      // Solid background.
+      form.find('.color-preview').css('backgroundColor', form.find('.color-palette input[name="palette[base]"]').val());
+
+      // Text preview.
+      form.find('#text').css('color', form.find('.color-palette input[name="palette[text]"]').val());
+      form.find('#text a, #text h2').css('color', form.find('.color-palette input[name="palette[link]"]').val());
+
+      function gradientLineColor(i, element) {
+        for (var k in accum) {
+          if (accum.hasOwnProperty(k)) {
+            accum[k] += delta[k];
+          }
+        }
+        element.style.backgroundColor = farb.pack(accum);
+      }
+
+      // Set up gradients if there are some.
+      var color_start;
+      var color_end;
+      for (var i in settings.gradients) {
+        if (settings.gradients.hasOwnProperty(i)) {
+          color_start = farb.unpack(form.find('.color-palette input[name="palette[' + settings.gradients[i].colors[0] + ']"]').val());
+          color_end = farb.unpack(form.find('.color-palette input[name="palette[' + settings.gradients[i].colors[1] + ']"]').val());
+          if (color_start && color_end) {
+            delta = [];
+            for (var j in color_start) {
+              if (color_start.hasOwnProperty(j)) {
+                delta[j] = (color_end[j] - color_start[j]) / (settings.gradients[i].vertical ? height[i] : width[i]);
+              }
+            }
+            accum = color_start;
+            // Render gradient lines.
+            form.find('#gradient-' + i + ' > div').each(gradientLineColor);
+          }
+        }
+      }
+    }
+  };
+})(jQuery);
diff --git a/core/themes/stable/js/comment/comment-by-viewer.js b/core/themes/stable/js/comment/comment-by-viewer.js
new file mode 100644
index 0000000..4f597e0
--- /dev/null
+++ b/core/themes/stable/js/comment/comment-by-viewer.js
@@ -0,0 +1,26 @@
+/**
+ * @file
+ * Attaches behaviors for the Comment module's "by-viewer" class.
+ */
+
+(function ($, Drupal, drupalSettings) {
+
+  "use strict";
+
+  /**
+   * Add 'by-viewer' class to comments written by the current user.
+   *
+   * @type {Drupal~behavior}
+   */
+  Drupal.behaviors.commentByViewer = {
+    attach: function (context) {
+      var currentUserID = parseInt(drupalSettings.user.uid, 10);
+      $('[data-comment-user-id]')
+        .filter(function () {
+          return parseInt(this.getAttribute('data-comment-user-id'), 10) === currentUserID;
+        })
+        .addClass('by-viewer');
+    }
+  };
+
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/themes/stable/js/comment/comment-entity-form.js b/core/themes/stable/js/comment/comment-entity-form.js
new file mode 100644
index 0000000..3cdf08c
--- /dev/null
+++ b/core/themes/stable/js/comment/comment-entity-form.js
@@ -0,0 +1,23 @@
+/**
+ * @file
+ * Attaches comment behaviors to the entity form.
+ */
+
+(function ($) {
+
+  "use strict";
+
+  /**
+   *
+   * @type {Drupal~behavior}
+   */
+  Drupal.behaviors.commentFieldsetSummaries = {
+    attach: function (context) {
+      var $context = $(context);
+      $context.find('fieldset.comment-entity-settings-form').drupalSetSummary(function (context) {
+        return Drupal.checkPlain($(context).find('.js-form-item-comment input:checked').next('label').text());
+      });
+    }
+  };
+
+})(jQuery);
diff --git a/core/themes/stable/js/comment/comment-new-indicator.js b/core/themes/stable/js/comment/comment-new-indicator.js
new file mode 100644
index 0000000..d426c0d
--- /dev/null
+++ b/core/themes/stable/js/comment/comment-new-indicator.js
@@ -0,0 +1,96 @@
+/**
+ * @file
+ * Attaches behaviors for the Comment module's "new" indicator.
+ *
+ * May only be loaded for authenticated users, with the History module
+ * installed.
+ */
+
+(function ($, Drupal, window) {
+
+  "use strict";
+
+  /**
+   * Renders "new" comment indicators wherever necessary.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches "new" comment indicators behavior.
+   */
+  Drupal.behaviors.commentNewIndicator = {
+    attach: function (context) {
+      // Collect all "new" comment indicator placeholders (and their
+      // corresponding node IDs) newer than 30 days ago that have not already
+      // been read after their last comment timestamp.
+      var nodeIDs = [];
+      var $placeholders = $(context)
+        .find('[data-comment-timestamp]')
+        .once('history')
+        .filter(function () {
+          var $placeholder = $(this);
+          var commentTimestamp = parseInt($placeholder.attr('data-comment-timestamp'), 10);
+          var nodeID = $placeholder.closest('[data-history-node-id]').attr('data-history-node-id');
+          if (Drupal.history.needsServerCheck(nodeID, commentTimestamp)) {
+            nodeIDs.push(nodeID);
+            return true;
+          }
+          else {
+            return false;
+          }
+        });
+
+      if ($placeholders.length === 0) {
+        return;
+      }
+
+      // Fetch the node read timestamps from the server.
+      Drupal.history.fetchTimestamps(nodeIDs, function () {
+        processCommentNewIndicators($placeholders);
+      });
+    }
+  };
+
+  /**
+   * Processes the markup for "new comment" indicators.
+   *
+   * @param {jQuery} $placeholders
+   *   The elements that should be processed.
+   */
+  function processCommentNewIndicators($placeholders) {
+    var isFirstNewComment = true;
+    var newCommentString = Drupal.t('new');
+    var $placeholder;
+
+    $placeholders.each(function (index, placeholder) {
+      $placeholder = $(placeholder);
+      var timestamp = parseInt($placeholder.attr('data-comment-timestamp'), 10);
+      var $node = $placeholder.closest('[data-history-node-id]');
+      var nodeID = $node.attr('data-history-node-id');
+      var lastViewTimestamp = Drupal.history.getLastRead(nodeID);
+
+      if (timestamp > lastViewTimestamp) {
+        // Turn the placeholder into an actual "new" indicator.
+        var $comment = $(placeholder)
+          .removeClass('hidden')
+          .text(newCommentString)
+          .closest('.js-comment')
+          // Add 'new' class to the comment, so it can be styled.
+          .addClass('new');
+
+        // Insert "new" anchor just before the "comment-<cid>" anchor if
+        // this is the first new comment in the DOM.
+        if (isFirstNewComment) {
+          isFirstNewComment = false;
+          $comment.prev().before('<a id="new" />');
+          // If the URL points to the first new comment, then scroll to that
+          // comment.
+          if (window.location.hash === '#new') {
+            window.scrollTo(0, $comment.offset().top - Drupal.displace.offsets.top);
+          }
+        }
+      }
+    });
+  }
+
+})(jQuery, Drupal, window);
diff --git a/core/themes/stable/js/comment/node-new-comments-link.js b/core/themes/stable/js/comment/node-new-comments-link.js
new file mode 100644
index 0000000..de757ec
--- /dev/null
+++ b/core/themes/stable/js/comment/node-new-comments-link.js
@@ -0,0 +1,177 @@
+/**
+ * @file
+ * Attaches behaviors for the Comment module's "X new comments" link.
+ *
+ * May only be loaded for authenticated users, with the History module
+ * installed.
+ */
+
+(function ($, Drupal) {
+
+  "use strict";
+
+  /**
+   * Render "X new comments" links wherever necessary.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches new comment links behavior.
+   */
+  Drupal.behaviors.nodeNewCommentsLink = {
+    attach: function (context) {
+      // Collect all "X new comments" node link placeholders (and their
+      // corresponding node IDs) newer than 30 days ago that have not already
+      // been read after their last comment timestamp.
+      var nodeIDs = [];
+      var $placeholders = $(context)
+        .find('[data-history-node-last-comment-timestamp]')
+        .once('history')
+        .filter(function () {
+          var $placeholder = $(this);
+          var lastCommentTimestamp = parseInt($placeholder.attr('data-history-node-last-comment-timestamp'), 10);
+          var nodeID = $placeholder.closest('[data-history-node-id]').attr('data-history-node-id');
+          if (Drupal.history.needsServerCheck(nodeID, lastCommentTimestamp)) {
+            nodeIDs.push(nodeID);
+            // Hide this placeholder link until it is certain we'll need it.
+            hide($placeholder);
+            return true;
+          }
+          else {
+            // Remove this placeholder link from the DOM because we won't need
+            // it.
+            remove($placeholder);
+            return false;
+          }
+        });
+
+      if ($placeholders.length === 0) {
+        return;
+      }
+
+      // Perform an AJAX request to retrieve node read timestamps.
+      Drupal.history.fetchTimestamps(nodeIDs, function () {
+        processNodeNewCommentLinks($placeholders);
+      });
+    }
+  };
+
+  /**
+   * Hides a "new comment" element.
+   *
+   * @param {jQuery} $placeholder
+   *   The placeholder element of the new comment link.
+   *
+   * @return {jQuery}
+   *   The placeholder element passed in as a parameter.
+   */
+  function hide($placeholder) {
+    return $placeholder
+      // Find the parent <li>.
+      .closest('.comment-new-comments')
+      // Find the preceding <li>, if any, and give it the 'last' class.
+      .prev().addClass('last')
+      // Go back to the parent <li> and hide it.
+      .end().hide();
+  }
+
+  /**
+   * Removes a "new comment" element.
+   *
+   * @param {jQuery} $placeholder
+   *   The placeholder element of the new comment link.
+   */
+  function remove($placeholder) {
+    hide($placeholder).remove();
+  }
+
+  /**
+   * Shows a "new comment" element.
+   *
+   * @param {jQuery} $placeholder
+   *   The placeholder element of the new comment link.
+   *
+   * @return {jQuery}
+   *   The placeholder element passed in as a parameter.
+   */
+  function show($placeholder) {
+    return $placeholder
+      // Find the parent <li>.
+      .closest('.comment-new-comments')
+      // Find the preceding <li>, if any, and remove its 'last' class, if any.
+      .prev().removeClass('last')
+      // Go back to the parent <li> and show it.
+      .end().show();
+  }
+
+  /**
+   * Processes new comment links and adds appropriate text in relevant cases.
+   *
+   * @param {jQuery} $placeholders
+   *   The placeholder elements of the current page.
+   */
+  function processNodeNewCommentLinks($placeholders) {
+    // Figure out which placeholders need the "x new comments" links.
+    var $placeholdersToUpdate = {};
+    var fieldName = 'comment';
+    var $placeholder;
+    $placeholders.each(function (index, placeholder) {
+      $placeholder = $(placeholder);
+      var timestamp = parseInt($placeholder.attr('data-history-node-last-comment-timestamp'), 10);
+      fieldName = $placeholder.attr('data-history-node-field-name');
+      var nodeID = $placeholder.closest('[data-history-node-id]').attr('data-history-node-id');
+      var lastViewTimestamp = Drupal.history.getLastRead(nodeID);
+
+      // Queue this placeholder's "X new comments" link to be downloaded from
+      // the server.
+      if (timestamp > lastViewTimestamp) {
+        $placeholdersToUpdate[nodeID] = $placeholder;
+      }
+      // No "X new comments" link necessary; remove it from the DOM.
+      else {
+        remove($placeholder);
+      }
+    });
+
+    // Perform an AJAX request to retrieve node view timestamps.
+    var nodeIDs = Object.keys($placeholdersToUpdate);
+    if (nodeIDs.length === 0) {
+      return;
+    }
+
+    /**
+     * Renders the "X new comments" links.
+     *
+     * Either use the data embedded in the page or perform an AJAX request to
+     * retrieve the same data.
+     *
+     * @param {object} results
+     *   Data about new comment links indexed by nodeID.
+     */
+    function render(results) {
+      for (var nodeID in results) {
+        if (results.hasOwnProperty(nodeID) && $placeholdersToUpdate.hasOwnProperty(nodeID)) {
+          $placeholdersToUpdate[nodeID]
+            .attr('href', results[nodeID].first_new_comment_link)
+            .text(Drupal.formatPlural(results[nodeID].new_comment_count, '1 new comment', '@count new comments'))
+            .removeClass('hidden');
+          show($placeholdersToUpdate[nodeID]);
+        }
+      }
+    }
+
+    if (drupalSettings.comment && drupalSettings.comment.newCommentsLinks) {
+      render(drupalSettings.comment.newCommentsLinks.node[fieldName]);
+    }
+    else {
+      $.ajax({
+        url: Drupal.url('comments/render_new_comments_node_links'),
+        type: 'POST',
+        data: {'node_ids[]': nodeIDs, 'field_name': fieldName},
+        dataType: 'json',
+        success: render
+      });
+    }
+  }
+
+})(jQuery, Drupal);
diff --git a/core/themes/stable/js/content_translation/content_translation.admin.js b/core/themes/stable/js/content_translation/content_translation.admin.js
new file mode 100644
index 0000000..2ca7496
--- /dev/null
+++ b/core/themes/stable/js/content_translation/content_translation.admin.js
@@ -0,0 +1,131 @@
+/**
+ * @file
+ * Content Translation admin behaviors.
+ */
+
+(function ($, Drupal, drupalSettings) {
+
+  "use strict";
+
+  /**
+   * Forces applicable options to be checked as translatable.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches content translation dependent options to the UI.
+   */
+  Drupal.behaviors.contentTranslationDependentOptions = {
+    attach: function (context) {
+      var $context = $(context);
+      var options = drupalSettings.contentTranslationDependentOptions;
+      var $fields;
+      var dependent_columns;
+
+      function fieldsChangeHandler($fields, dependent_columns) {
+        return function (e) {
+          Drupal.behaviors.contentTranslationDependentOptions.check($fields, dependent_columns, $(e.target));
+        };
+      }
+
+      // We're given a generic name to look for so we find all inputs containing
+      // that name and copy over the input values that require all columns to be
+      // translatable.
+      if (options.dependent_selectors) {
+        for (var field in options.dependent_selectors) {
+          if (options.dependent_selectors.hasOwnProperty(field)) {
+            $fields = $context.find('input[name^="' + field + '"]');
+            dependent_columns = options.dependent_selectors[field];
+
+            $fields.on('change', fieldsChangeHandler($fields, dependent_columns));
+            Drupal.behaviors.contentTranslationDependentOptions.check($fields, dependent_columns);
+          }
+        }
+      }
+    },
+    check: function ($fields, dependent_columns, $changed) {
+      var $element = $changed;
+      var column;
+
+      function filterFieldsList(index, field) {
+        return $(field).val() === column;
+      }
+
+      // A field that has many different translatable parts can also define one
+      // or more columns that require all columns to be translatable.
+      for (var index in dependent_columns) {
+        if (dependent_columns.hasOwnProperty(index)) {
+          column = dependent_columns[index];
+
+          if (!$changed) {
+            $element = $fields.filter(filterFieldsList);
+          }
+
+          if ($element.is('input[value="' + column + '"]:checked')) {
+            $fields.prop('checked', true)
+              .not($element).prop('disabled', true);
+          }
+          else {
+            $fields.prop('disabled', false);
+          }
+
+        }
+      }
+    }
+  };
+
+  /**
+   * Makes field translatability inherit bundle translatability.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches content translation behavior.
+   */
+  Drupal.behaviors.contentTranslation = {
+    attach: function (context) {
+      // Initially hide all field rows for non translatable bundles and all
+      // column rows for non translatable fields.
+      $(context).find('table .bundle-settings .translatable :input').once('translation-entity-admin-hide').each(function () {
+        var $input = $(this);
+        var $bundleSettings = $input.closest('.bundle-settings');
+        if (!$input.is(':checked')) {
+          $bundleSettings.nextUntil('.bundle-settings').hide();
+        }
+        else {
+          $bundleSettings.nextUntil('.bundle-settings', '.field-settings').find('.translatable :input:not(:checked)').closest('.field-settings').nextUntil(':not(.column-settings)').hide();
+        }
+      });
+
+      // When a bundle is made translatable all of its fields should inherit
+      // this setting. Instead when it is made non translatable its fields are
+      // hidden, since their translatability no longer matters.
+      $('body').once('translation-entity-admin-bind').on('click', 'table .bundle-settings .translatable :input', function (e) {
+        var $target = $(e.target);
+        var $bundleSettings = $target.closest('.bundle-settings');
+        var $settings = $bundleSettings.nextUntil('.bundle-settings');
+        var $fieldSettings = $settings.filter('.field-settings');
+        if ($target.is(':checked')) {
+          $bundleSettings.find('.operations :input[name$="[language_alterable]"]').prop('checked', true);
+          $fieldSettings.find('.translatable :input').prop('checked', true);
+          $settings.show();
+        }
+        else {
+          $settings.hide();
+        }
+      })
+        .on('click', 'table .field-settings .translatable :input', function (e) {
+          var $target = $(e.target);
+          var $fieldSettings = $target.closest('.field-settings');
+          var $columnSettings = $fieldSettings.nextUntil('.field-settings, .bundle-settings');
+          if ($target.is(':checked')) {
+            $columnSettings.show();
+          }
+          else {
+            $columnSettings.hide();
+          }
+        });
+    }
+  };
+
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/themes/stable/js/contextual/contextual.js b/core/themes/stable/js/contextual/contextual.js
new file mode 100644
index 0000000..c90911c
--- /dev/null
+++ b/core/themes/stable/js/contextual/contextual.js
@@ -0,0 +1,256 @@
+/**
+ * @file
+ * Attaches behaviors for the Contextual module.
+ */
+
+(function ($, Drupal, drupalSettings, _, Backbone, JSON, storage) {
+
+  "use strict";
+
+  var options = $.extend(drupalSettings.contextual,
+    // Merge strings on top of drupalSettings so that they are not mutable.
+    {
+      strings: {
+        open: Drupal.t('Open'),
+        close: Drupal.t('Close')
+      }
+    }
+  );
+
+  // Clear the cached contextual links whenever the current user's set of
+  // permissions changes.
+  var cachedPermissionsHash = storage.getItem('Drupal.contextual.permissionsHash');
+  var permissionsHash = drupalSettings.user.permissionsHash;
+  if (cachedPermissionsHash !== permissionsHash) {
+    if (typeof permissionsHash === 'string') {
+      _.chain(storage).keys().each(function (key) {
+        if (key.substring(0, 18) === 'Drupal.contextual.') {
+          storage.removeItem(key);
+        }
+      });
+    }
+    storage.setItem('Drupal.contextual.permissionsHash', permissionsHash);
+  }
+
+  /**
+   * Initializes a contextual link: updates its DOM, sets up model and views.
+   *
+   * @param {jQuery} $contextual
+   *   A contextual links placeholder DOM element, containing the actual
+   *   contextual links as rendered by the server.
+   * @param {string} html
+   *   The server-side rendered HTML for this contextual link.
+   */
+  function initContextual($contextual, html) {
+    var $region = $contextual.closest('.contextual-region');
+    var contextual = Drupal.contextual;
+
+    $contextual
+      // Update the placeholder to contain its rendered contextual links.
+      .html(html)
+      // Use the placeholder as a wrapper with a specific class to provide
+      // positioning and behavior attachment context.
+      .addClass('contextual')
+      // Ensure a trigger element exists before the actual contextual links.
+      .prepend(Drupal.theme('contextualTrigger'));
+
+    // Set the destination parameter on each of the contextual links.
+    var destination = 'destination=' + Drupal.encodePath(drupalSettings.path.currentPath);
+    $contextual.find('.contextual-links a').each(function () {
+      var url = this.getAttribute('href');
+      var glue = (url.indexOf('?') === -1) ? '?' : '&';
+      this.setAttribute('href', url + glue + destination);
+    });
+
+    // Create a model and the appropriate views.
+    var model = new contextual.StateModel({
+      title: $region.find('h2').eq(0).text().trim()
+    });
+    var viewOptions = $.extend({el: $contextual, model: model}, options);
+    contextual.views.push({
+      visual: new contextual.VisualView(viewOptions),
+      aural: new contextual.AuralView(viewOptions),
+      keyboard: new contextual.KeyboardView(viewOptions)
+    });
+    contextual.regionViews.push(new contextual.RegionView(
+      $.extend({el: $region, model: model}, options))
+    );
+
+    // Add the model to the collection. This must happen after the views have
+    // been associated with it, otherwise collection change event handlers can't
+    // trigger the model change event handler in its views.
+    contextual.collection.add(model);
+
+    // Let other JavaScript react to the adding of a new contextual link.
+    $(document).trigger('drupalContextualLinkAdded', {
+      $el: $contextual,
+      $region: $region,
+      model: model
+    });
+
+    // Fix visual collisions between contextual link triggers.
+    adjustIfNestedAndOverlapping($contextual);
+  }
+
+  /**
+   * Determines if a contextual link is nested & overlapping, if so: adjusts it.
+   *
+   * This only deals with two levels of nesting; deeper levels are not touched.
+   *
+   * @param {jQuery} $contextual
+   *   A contextual links placeholder DOM element, containing the actual
+   *   contextual links as rendered by the server.
+   */
+  function adjustIfNestedAndOverlapping($contextual) {
+    var $contextuals = $contextual
+      // @todo confirm that .closest() is not sufficient
+      .parents('.contextual-region').eq(-1)
+      .find('.contextual');
+
+    // Early-return when there's no nesting.
+    if ($contextuals.length === 1) {
+      return;
+    }
+
+    // If the two contextual links overlap, then we move the second one.
+    var firstTop = $contextuals.eq(0).offset().top;
+    var secondTop = $contextuals.eq(1).offset().top;
+    if (firstTop === secondTop) {
+      var $nestedContextual = $contextuals.eq(1);
+
+      // Retrieve height of nested contextual link.
+      var height = 0;
+      var $trigger = $nestedContextual.find('.trigger');
+      // Elements with the .visually-hidden class have no dimensions, so this
+      // class must be temporarily removed to the calculate the height.
+      $trigger.removeClass('visually-hidden');
+      height = $nestedContextual.height();
+      $trigger.addClass('visually-hidden');
+
+      // Adjust nested contextual link's position.
+      $nestedContextual.css({top: $nestedContextual.position().top + height});
+    }
+  }
+
+  /**
+   * Attaches outline behavior for regions associated with contextual links.
+   *
+   * Events
+   *   Contextual triggers an event that can be used by other scripts.
+   *   - drupalContextualLinkAdded: Triggered when a contextual link is added.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *  Attaches the outline behavior to the right context.
+   */
+  Drupal.behaviors.contextual = {
+    attach: function (context) {
+      var $context = $(context);
+
+      // Find all contextual links placeholders, if any.
+      var $placeholders = $context.find('[data-contextual-id]').once('contextual-render');
+      if ($placeholders.length === 0) {
+        return;
+      }
+
+      // Collect the IDs for all contextual links placeholders.
+      var ids = [];
+      $placeholders.each(function () {
+        ids.push($(this).attr('data-contextual-id'));
+      });
+
+      // Update all contextual links placeholders whose HTML is cached.
+      var uncachedIDs = _.filter(ids, function initIfCached(contextualID) {
+        var html = storage.getItem('Drupal.contextual.' + contextualID);
+        if (html !== null) {
+          // Initialize after the current execution cycle, to make the AJAX
+          // request for retrieving the uncached contextual links as soon as
+          // possible, but also to ensure that other Drupal behaviors have had
+          // the chance to set up an event listener on the Backbone collection
+          // Drupal.contextual.collection.
+          window.setTimeout(function () {
+            initContextual($context.find('[data-contextual-id="' + contextualID + '"]'), html);
+          });
+          return false;
+        }
+        return true;
+      });
+
+      // Perform an AJAX request to let the server render the contextual links
+      // for each of the placeholders.
+      if (uncachedIDs.length > 0) {
+        $.ajax({
+          url: Drupal.url('contextual/render'),
+          type: 'POST',
+          data: {'ids[]': uncachedIDs},
+          dataType: 'json',
+          success: function (results) {
+            _.each(results, function (html, contextualID) {
+              // Store the metadata.
+              storage.setItem('Drupal.contextual.' + contextualID, html);
+              // If the rendered contextual links are empty, then the current
+              // user does not have permission to access the associated links:
+              // don't render anything.
+              if (html.length > 0) {
+                // Update the placeholders to contain its rendered contextual
+                // links. Usually there will only be one placeholder, but it's
+                // possible for multiple identical placeholders exist on the
+                // page (probably because the same content appears more than
+                // once).
+                $placeholders = $context.find('[data-contextual-id="' + contextualID + '"]');
+
+                // Initialize the contextual links.
+                for (var i = 0; i < $placeholders.length; i++) {
+                  initContextual($placeholders.eq(i), html);
+                }
+              }
+            });
+          }
+        });
+      }
+    }
+  };
+
+  /**
+   * Namespace for contextual related functionality.
+   *
+   * @namespace
+   */
+  Drupal.contextual = {
+
+    /**
+     * The {@link Drupal.contextual.View} instances associated with each list
+     * element of contextual links.
+     *
+     * @type {Array}
+     */
+    views: [],
+
+    /**
+     * The {@link Drupal.contextual.RegionView} instances associated with each
+     * contextual region element.
+     *
+     * @type {Array}
+     */
+    regionViews: []
+  };
+
+  /**
+   * A Backbone.Collection of {@link Drupal.contextual.StateModel} instances.
+   *
+   * @type {Backbone.Collection}
+   */
+  Drupal.contextual.collection = new Backbone.Collection([], {model: Drupal.contextual.StateModel});
+
+  /**
+   * A trigger is an interactive element often bound to a click handler.
+   *
+   * @return {string}
+   *   A string representing a DOM fragment.
+   */
+  Drupal.theme.contextualTrigger = function () {
+    return '<button class="trigger visually-hidden focusable" type="button"></button>';
+  };
+
+})(jQuery, Drupal, drupalSettings, _, Backbone, window.JSON, window.sessionStorage);
diff --git a/core/themes/stable/js/contextual/contextual.toolbar.js b/core/themes/stable/js/contextual/contextual.toolbar.js
new file mode 100644
index 0000000..839d1b0
--- /dev/null
+++ b/core/themes/stable/js/contextual/contextual.toolbar.js
@@ -0,0 +1,77 @@
+/**
+ * @file
+ * Attaches behaviors for the Contextual module's edit toolbar tab.
+ */
+
+(function ($, Drupal, Backbone) {
+
+  "use strict";
+
+  var strings = {
+    tabbingReleased: Drupal.t('Tabbing is no longer constrained by the Contextual module.'),
+    tabbingConstrained: Drupal.t('Tabbing is constrained to a set of @contextualsCount and the edit mode toggle.'),
+    pressEsc: Drupal.t('Press the esc key to exit.')
+  };
+
+  /**
+   * Initializes a contextual link: updates its DOM, sets up model and views.
+   *
+   * @param {HTMLElement} context
+   *   A contextual links DOM element as rendered by the server.
+   */
+  function initContextualToolbar(context) {
+    if (!Drupal.contextual || !Drupal.contextual.collection) {
+      return;
+    }
+
+    var contextualToolbar = Drupal.contextualToolbar;
+    var model = contextualToolbar.model = new contextualToolbar.StateModel({
+      // Checks whether localStorage indicates we should start in edit mode
+      // rather than view mode.
+      // @see Drupal.contextualToolbar.VisualView.persist
+      isViewing: localStorage.getItem('Drupal.contextualToolbar.isViewing') !== 'false'
+    }, {
+      contextualCollection: Drupal.contextual.collection
+    });
+
+    var viewOptions = {
+      el: $('.toolbar .toolbar-bar .contextual-toolbar-tab'),
+      model: model,
+      strings: strings
+    };
+    new contextualToolbar.VisualView(viewOptions);
+    new contextualToolbar.AuralView(viewOptions);
+  }
+
+  /**
+   * Attaches contextual's edit toolbar tab behavior.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches contextual toolbar behavior on a contextualToolbar-init event.
+   */
+  Drupal.behaviors.contextualToolbar = {
+    attach: function (context) {
+      if ($('body').once('contextualToolbar-init').length) {
+        initContextualToolbar(context);
+      }
+    }
+  };
+
+  /**
+   * Namespace for the contextual toolbar.
+   *
+   * @namespace
+   */
+  Drupal.contextualToolbar = {
+
+    /**
+     * The {@link Drupal.contextualToolbar.StateModel} instance.
+     *
+     * @type {?Drupal.contextualToolbar.StateModel}
+     */
+    model: null
+  };
+
+})(jQuery, Drupal, Backbone);
diff --git a/core/themes/stable/js/contextual/models/StateModel.js b/core/themes/stable/js/contextual/models/StateModel.js
new file mode 100644
index 0000000..154013a
--- /dev/null
+++ b/core/themes/stable/js/contextual/models/StateModel.js
@@ -0,0 +1,132 @@
+/**
+ * @file
+ * A Backbone Model for the state of a contextual link's trigger, list & region.
+ */
+
+(function (Drupal, Backbone) {
+
+  "use strict";
+
+  /**
+   * Models the state of a contextual link's trigger, list & region.
+   *
+   * @constructor
+   *
+   * @augments Backbone.Model
+   */
+  Drupal.contextual.StateModel = Backbone.Model.extend(/** @lends Drupal.contextual.StateModel# */{
+
+    /**
+     * @type {object}
+     *
+     * @prop {string} title
+     * @prop {bool} regionIsHovered
+     * @prop {bool} hasFocus
+     * @prop {bool} isOpen
+     * @prop {bool} isLocked
+     */
+    defaults: /** @lends Drupal.contextual.StateModel# */{
+
+      /**
+       * The title of the entity to which these contextual links apply.
+       *
+       * @type {string}
+       */
+      title: '',
+
+      /**
+       * Represents if the contextual region is being hovered.
+       *
+       * @type {bool}
+       */
+      regionIsHovered: false,
+
+      /**
+       * Represents if the contextual trigger or options have focus.
+       *
+       * @type {bool}
+       */
+      hasFocus: false,
+
+      /**
+       * Represents if the contextual options for an entity are available to
+       * be selected (i.e. whether the list of options is visible).
+       *
+       * @type {bool}
+       */
+      isOpen: false,
+
+      /**
+       * When the model is locked, the trigger remains active.
+       *
+       * @type {bool}
+       */
+      isLocked: false
+    },
+
+    /**
+     * Opens or closes the contextual link.
+     *
+     * If it is opened, then also give focus.
+     *
+     * @return {Drupal.contextual.StateModel}
+     *   The current contextual state model.
+     */
+    toggleOpen: function () {
+      var newIsOpen = !this.get('isOpen');
+      this.set('isOpen', newIsOpen);
+      if (newIsOpen) {
+        this.focus();
+      }
+      return this;
+    },
+
+    /**
+     * Closes this contextual link.
+     *
+     * Does not call blur() because we want to allow a contextual link to have
+     * focus, yet be closed for example when hovering.
+     *
+     * @return {Drupal.contextual.StateModel}
+     *   The current contextual state model.
+     */
+    close: function () {
+      this.set('isOpen', false);
+      return this;
+    },
+
+    /**
+     * Gives focus to this contextual link.
+     *
+     * Also closes + removes focus from every other contextual link.
+     *
+     * @return {Drupal.contextual.StateModel}
+     *   The current contextual state model.
+     */
+    focus: function () {
+      this.set('hasFocus', true);
+      var cid = this.cid;
+      this.collection.each(function (model) {
+        if (model.cid !== cid) {
+          model.close().blur();
+        }
+      });
+      return this;
+    },
+
+    /**
+     * Removes focus from this contextual link, unless it is open.
+     *
+     * @return {Drupal.contextual.StateModel}
+     *   The current contextual state model.
+     */
+    blur: function () {
+      if (!this.get('isOpen')) {
+        this.set('hasFocus', false);
+      }
+      return this;
+    }
+
+  });
+
+})(Drupal, Backbone);
diff --git a/core/themes/stable/js/contextual/toolbar/models/StateModel.js b/core/themes/stable/js/contextual/toolbar/models/StateModel.js
new file mode 100644
index 0000000..bca190f
--- /dev/null
+++ b/core/themes/stable/js/contextual/toolbar/models/StateModel.js
@@ -0,0 +1,119 @@
+/**
+ * @file
+ * A Backbone Model for the state of Contextual module's edit toolbar tab.
+ */
+
+(function (Drupal, Backbone) {
+
+  "use strict";
+
+  Drupal.contextualToolbar.StateModel = Backbone.Model.extend(/** @lends Drupal.contextualToolbar.StateModel# */{
+
+    /**
+     * @type {object}
+     *
+     * @prop {bool} isViewing
+     * @prop {bool} isVisible
+     * @prop {number} contextualCount
+     * @prop {Drupal~TabbingContext} tabbingContext
+     */
+    defaults: /** @lends Drupal.contextualToolbar.StateModel# */{
+
+      /**
+       * Indicates whether the toggle is currently in "view" or "edit" mode.
+       *
+       * @type {bool}
+       */
+      isViewing: true,
+
+      /**
+       * Indicates whether the toggle should be visible or hidden. Automatically
+       * calculated, depends on contextualCount.
+       *
+       * @type {bool}
+       */
+      isVisible: false,
+
+      /**
+       * Tracks how many contextual links exist on the page.
+       *
+       * @type {number}
+       */
+      contextualCount: 0,
+
+      /**
+       * A TabbingContext object as returned by {@link Drupal~TabbingManager}:
+       * the set of tabbable elements when edit mode is enabled.
+       *
+       * @type {?Drupal~TabbingContext}
+       */
+      tabbingContext: null
+    },
+
+    /**
+     * Models the state of the edit mode toggle.
+     *
+     * @constructs
+     *
+     * @augments Backbone.Model
+     *
+     * @param {object} attrs
+     *   Attributes for the backbone model.
+     * @param {object} options
+     *   An object with the following option:
+     * @param {Backbone.collection} options.contextualCollection
+     *   The collection of {@link Drupal.contextual.StateModel} models that
+     *   represent the contextual links on the page.
+     */
+    initialize: function (attrs, options) {
+      // Respond to new/removed contextual links.
+      this.listenTo(options.contextualCollection, 'reset remove add', this.countContextualLinks);
+      this.listenTo(options.contextualCollection, 'add', this.lockNewContextualLinks);
+
+      // Automatically determine visibility.
+      this.listenTo(this, 'change:contextualCount', this.updateVisibility);
+
+      // Whenever edit mode is toggled, lock all contextual links.
+      this.listenTo(this, 'change:isViewing', function (model, isViewing) {
+        options.contextualCollection.each(function (contextualModel) {
+          contextualModel.set('isLocked', !isViewing);
+        });
+      });
+    },
+
+    /**
+     * Tracks the number of contextual link models in the collection.
+     *
+     * @param {Drupal.contextual.StateModel} contextualModel
+     *   The contextual links model that was added or removed.
+     * @param {Backbone.Collection} contextualCollection
+     *    The collection of contextual link models.
+     */
+    countContextualLinks: function (contextualModel, contextualCollection) {
+      this.set('contextualCount', contextualCollection.length);
+    },
+
+    /**
+     * Lock newly added contextual links if edit mode is enabled.
+     *
+     * @param {Drupal.contextual.StateModel} contextualModel
+     *   The contextual links model that was added.
+     * @param {Backbone.Collection} [contextualCollection]
+     *    The collection of contextual link models.
+     */
+    lockNewContextualLinks: function (contextualModel, contextualCollection) {
+      if (!this.get('isViewing')) {
+        contextualModel.set('isLocked', true);
+      }
+    },
+
+    /**
+     * Automatically updates visibility of the view/edit mode toggle.
+     */
+    updateVisibility: function () {
+      this.set('isVisible', this.get('contextualCount') > 0);
+    }
+
+  });
+
+})(Drupal, Backbone);
diff --git a/core/themes/stable/js/contextual/toolbar/views/AuralView.js b/core/themes/stable/js/contextual/toolbar/views/AuralView.js
new file mode 100644
index 0000000..1bb99b4
--- /dev/null
+++ b/core/themes/stable/js/contextual/toolbar/views/AuralView.js
@@ -0,0 +1,104 @@
+/**
+ * @file
+ * A Backbone View that provides the aural view of the edit mode toggle.
+ */
+
+(function ($, Drupal, Backbone, _) {
+
+  "use strict";
+
+  Drupal.contextualToolbar.AuralView = Backbone.View.extend(/** @lends Drupal.contextualToolbar.AuralView# */{
+
+    /**
+     * Tracks whether the tabbing constraint announcement has been read once.
+     *
+     * @type {bool}
+     */
+    announcedOnce: false,
+
+    /**
+     * Renders the aural view of the edit mode toggle (screen reader support).
+     *
+     * @constructs
+     *
+     * @augments Backbone.View
+     *
+     * @param {object} options
+     *   Options for the view.
+     */
+    initialize: function (options) {
+      this.options = options;
+
+      this.listenTo(this.model, 'change', this.render);
+      this.listenTo(this.model, 'change:isViewing', this.manageTabbing);
+
+      $(document).on('keyup', _.bind(this.onKeypress, this));
+    },
+
+    /**
+     * @inheritdoc
+     *
+     * @return {Drupal.contextualToolbar.AuralView}
+     *   The current contextual toolbar aural view.
+     */
+    render: function () {
+      // Render the state.
+      this.$el.find('button').attr('aria-pressed', !this.model.get('isViewing'));
+
+      return this;
+    },
+
+    /**
+     * Limits tabbing to the contextual links and edit mode toolbar tab.
+     */
+    manageTabbing: function () {
+      var tabbingContext = this.model.get('tabbingContext');
+      // Always release an existing tabbing context.
+      if (tabbingContext) {
+        tabbingContext.release();
+        Drupal.announce(this.options.strings.tabbingReleased);
+      }
+      // Create a new tabbing context when edit mode is enabled.
+      if (!this.model.get('isViewing')) {
+        tabbingContext = Drupal.tabbingManager.constrain($('.contextual-toolbar-tab, .contextual'));
+        this.model.set('tabbingContext', tabbingContext);
+        this.announceTabbingConstraint();
+        this.announcedOnce = true;
+      }
+    },
+
+    /**
+     * Announces the current tabbing constraint.
+     */
+    announceTabbingConstraint: function () {
+      var strings = this.options.strings;
+      Drupal.announce(Drupal.formatString(strings.tabbingConstrained, {
+        '@contextualsCount': Drupal.formatPlural(Drupal.contextual.collection.length, '@count contextual link', '@count contextual links')
+      }));
+      Drupal.announce(strings.pressEsc);
+    },
+
+    /**
+     * Responds to esc and tab key press events.
+     *
+     * @param {jQuery.Event} event
+     *   The keypress event.
+     */
+    onKeypress: function (event) {
+      // The first tab key press is tracked so that an annoucement about tabbing
+      // constraints can be raised if edit mode is enabled when the page is
+      // loaded.
+      if (!this.announcedOnce && event.keyCode === 9 && !this.model.get('isViewing')) {
+        this.announceTabbingConstraint();
+        // Set announce to true so that this conditional block won't run again.
+        this.announcedOnce = true;
+      }
+      // Respond to the ESC key. Exit out of edit mode.
+      if (event.keyCode === 27) {
+        this.model.set('isViewing', true);
+      }
+    }
+
+  });
+
+})(jQuery, Drupal, Backbone, _);
diff --git a/core/themes/stable/js/contextual/toolbar/views/VisualView.js b/core/themes/stable/js/contextual/toolbar/views/VisualView.js
new file mode 100644
index 0000000..e76f65e
--- /dev/null
+++ b/core/themes/stable/js/contextual/toolbar/views/VisualView.js
@@ -0,0 +1,84 @@
+/**
+ * @file
+ * A Backbone View that provides the visual view of the edit mode toggle.
+ */
+
+(function (Drupal, Backbone) {
+
+  "use strict";
+
+  Drupal.contextualToolbar.VisualView = Backbone.View.extend(/** @lends Drupal.contextualToolbar.VisualView# */{
+
+    /**
+     * Events for the Backbone view.
+     *
+     * @return {object}
+     *   A mapping of events to be used in the view.
+     */
+    events: function () {
+      // Prevents delay and simulated mouse events.
+      var touchEndToClick = function (event) {
+        event.preventDefault();
+        event.target.click();
+      };
+
+      return {
+        click: function () {
+          this.model.set('isViewing', !this.model.get('isViewing'));
+        },
+        touchend: touchEndToClick
+      };
+    },
+
+    /**
+     * Renders the visual view of the edit mode toggle.
+     *
+     * Listens to mouse & touch and handles edit mode toggle interactions.
+     *
+     * @constructs
+     *
+     * @augments Backbone.View
+     */
+    initialize: function () {
+      this.listenTo(this.model, 'change', this.render);
+      this.listenTo(this.model, 'change:isViewing', this.persist);
+    },
+
+    /**
+     * @inheritdoc
+     *
+     * @return {Drupal.contextualToolbar.VisualView}
+     *   The current contextual toolbar visual view.
+     */
+    render: function () {
+      // Render the visibility.
+      this.$el.toggleClass('hidden', !this.model.get('isVisible'));
+      // Render the state.
+      this.$el.find('button').toggleClass('is-active', !this.model.get('isViewing'));
+
+      return this;
+    },
+
+    /**
+     * Model change handler; persists the isViewing value to localStorage.
+     *
+     * `isViewing === true` is the default, so only stores in localStorage when
+     * it's not the default value (i.e. false).
+     *
+     * @param {Drupal.contextualToolbar.StateModel} model
+     *   A {@link Drupal.contextualToolbar.StateModel} model.
+     * @param {bool} isViewing
+     *   The value of the isViewing attribute in the model.
+     */
+    persist: function (model, isViewing) {
+      if (!isViewing) {
+        localStorage.setItem('Drupal.contextualToolbar.isViewing', 'false');
+      }
+      else {
+        localStorage.removeItem('Drupal.contextualToolbar.isViewing');
+      }
+    }
+
+  });
+
+})(Drupal, Backbone);
diff --git a/core/themes/stable/js/contextual/views/AuralView.js b/core/themes/stable/js/contextual/views/AuralView.js
new file mode 100644
index 0000000..70466d7
--- /dev/null
+++ b/core/themes/stable/js/contextual/views/AuralView.js
@@ -0,0 +1,55 @@
+/**
+ * @file
+ * A Backbone View that provides the aural view of a contextual link.
+ */
+
+(function (Drupal, Backbone) {
+
+  "use strict";
+
+  Drupal.contextual.AuralView = Backbone.View.extend(/** @lends Drupal.contextual.AuralView# */{
+
+    /**
+     * Renders the aural view of a contextual link (i.e. screen reader support).
+     *
+     * @constructs
+     *
+     * @augments Backbone.View
+     *
+     * @param {object} options
+     *   Options for the view.
+     */
+    initialize: function (options) {
+      this.options = options;
+
+      this.listenTo(this.model, 'change', this.render);
+
+      // Use aria-role form so that the number of items in the list is spoken.
+      this.$el.attr('role', 'form');
+
+      // Initial render.
+      this.render();
+    },
+
+    /**
+     * @inheritdoc
+     */
+    render: function () {
+      var isOpen = this.model.get('isOpen');
+
+      // Set the hidden property of the links.
+      this.$el.find('.contextual-links')
+        .prop('hidden', !isOpen);
+
+      // Update the view of the trigger.
+      this.$el.find('.trigger')
+        .text(Drupal.t('@action @title configuration options', {
+          '@action': (!isOpen) ? this.options.strings.open : this.options.strings.close,
+          '@title': this.model.get('title')
+        }))
+        .attr('aria-pressed', isOpen);
+    }
+
+  });
+
+})(Drupal, Backbone);
diff --git a/core/themes/stable/js/contextual/views/KeyboardView.js b/core/themes/stable/js/contextual/views/KeyboardView.js
new file mode 100644
index 0000000..e57ccad
--- /dev/null
+++ b/core/themes/stable/js/contextual/views/KeyboardView.js
@@ -0,0 +1,61 @@
+/**
+ * @file
+ * A Backbone View that provides keyboard interaction for a contextual link.
+ */
+
+(function (Drupal, Backbone) {
+
+  "use strict";
+
+  Drupal.contextual.KeyboardView = Backbone.View.extend(/** @lends Drupal.contextual.KeyboardView# */{
+
+    /**
+     * @type {object}
+     */
+    events: {
+      'focus .trigger': 'focus',
+      'focus .contextual-links a': 'focus',
+      'blur .trigger': function () { this.model.blur(); },
+      'blur .contextual-links a': function () {
+        // Set up a timeout to allow a user to tab between the trigger and the
+        // contextual links without the menu dismissing.
+        var that = this;
+        this.timer = window.setTimeout(function () {
+          that.model.close().blur();
+        }, 150);
+      }
+    },
+
+    /**
+     * Provides keyboard interaction for a contextual link.
+     *
+     * @constructs
+     *
+     * @augments Backbone.View
+     */
+    initialize: function () {
+
+      /**
+       * The timer is used to create a delay before dismissing the contextual
+       * links on blur. This is only necessary when keyboard users tab into
+       * contextual links without edit mode (i.e. without TabbingManager).
+       * That means that if we decide to disable tabbing of contextual links
+       * without edit mode, all this timer logic can go away.
+       *
+       * @type {NaN|number}
+       */
+      this.timer = NaN;
+    },
+
+    /**
+     * Sets focus on the model; Clears the timer that dismisses the links.
+     */
+    focus: function () {
+      // Clear the timeout that might have been set by blurring a link.
+      window.clearTimeout(this.timer);
+      this.model.focus();
+    }
+
+  });
+
+})(Drupal, Backbone);
diff --git a/core/themes/stable/js/contextual/views/RegionView.js b/core/themes/stable/js/contextual/views/RegionView.js
new file mode 100644
index 0000000..624bb18
--- /dev/null
+++ b/core/themes/stable/js/contextual/views/RegionView.js
@@ -0,0 +1,57 @@
+/**
+ * @file
+ * A Backbone View that renders the visual view of a contextual region element.
+ */
+
+(function (Drupal, Backbone, Modernizr) {
+
+  "use strict";
+
+  Drupal.contextual.RegionView = Backbone.View.extend(/** @lends Drupal.contextual.RegionView# */{
+
+    /**
+     * Events for the Backbone view.
+     *
+     * @return {object}
+     *   A mapping of events to be used in the view.
+     */
+    events: function () {
+      var mapping = {
+        mouseenter: function () { this.model.set('regionIsHovered', true); },
+        mouseleave: function () {
+          this.model.close().blur().set('regionIsHovered', false);
+        }
+      };
+      // We don't want mouse hover events on touch.
+      if (Modernizr.touch) {
+        mapping = {};
+      }
+      return mapping;
+    },
+
+    /**
+     * Renders the visual view of a contextual region element.
+     *
+     * @constructs
+     *
+     * @augments Backbone.View
+     */
+    initialize: function () {
+      this.listenTo(this.model, 'change:hasFocus', this.render);
+    },
+
+    /**
+     * @inheritdoc
+     *
+     * @return {Drupal.contextual.RegionView}
+     *   The current contextual region view.
+     */
+    render: function () {
+      this.$el.toggleClass('focus', this.model.get('hasFocus'));
+
+      return this;
+    }
+
+  });
+
+})(Drupal, Backbone, Modernizr);
diff --git a/core/themes/stable/js/contextual/views/VisualView.js b/core/themes/stable/js/contextual/views/VisualView.js
new file mode 100644
index 0000000..7c8e3ef
--- /dev/null
+++ b/core/themes/stable/js/contextual/views/VisualView.js
@@ -0,0 +1,80 @@
+/**
+ * @file
+ * A Backbone View that provides the visual view of a contextual link.
+ */
+
+(function (Drupal, Backbone, Modernizr) {
+
+  "use strict";
+
+  Drupal.contextual.VisualView = Backbone.View.extend(/** @lends Drupal.contextual.VisualView# */{
+
+    /**
+     * Events for the Backbone view.
+     *
+     * @return {object}
+     *   A mapping of events to be used in the view.
+     */
+    events: function () {
+      // Prevents delay and simulated mouse events.
+      var touchEndToClick = function (event) {
+        event.preventDefault();
+        event.target.click();
+      };
+      var mapping = {
+        'click .trigger': function () { this.model.toggleOpen(); },
+        'touchend .trigger': touchEndToClick,
+        'click .contextual-links a': function () { this.model.close().blur(); },
+        'touchend .contextual-links a': touchEndToClick
+      };
+      // We only want mouse hover events on non-touch.
+      if (!Modernizr.touch) {
+        mapping.mouseenter = function () { this.model.focus(); };
+      }
+      return mapping;
+    },
+
+    /**
+     * Renders the visual view of a contextual link. Listens to mouse & touch.
+     *
+     * @constructs
+     *
+     * @augments Backbone.View
+     */
+    initialize: function () {
+      this.listenTo(this.model, 'change', this.render);
+    },
+
+    /**
+     * @inheritdoc
+     *
+     * @return {Drupal.contextual.VisualView}
+     *   The current contextual visual view.
+     */
+    render: function () {
+      var isOpen = this.model.get('isOpen');
+      // The trigger should be visible when:
+      //  - the mouse hovered over the region,
+      //  - the trigger is locked,
+      //  - and for as long as the contextual menu is open.
+      var isVisible = this.model.get('isLocked') || this.model.get('regionIsHovered') || isOpen;
+
+      this.$el
+        // The open state determines if the links are visible.
+        .toggleClass('open', isOpen)
+        // Update the visibility of the trigger.
+        .find('.trigger').toggleClass('visually-hidden', !isVisible);
+
+      // Nested contextual region handling: hide any nested contextual triggers.
+      if ('isOpen' in this.model.changed) {
+        this.$el.closest('.contextual-region')
+          .find('.contextual .trigger:not(:first)')
+          .toggle(!isOpen);
+      }
+
+      return this;
+    }
+
+  });
+
+})(Drupal, Backbone, Modernizr);
diff --git a/core/themes/stable/js/core/active-link.js b/core/themes/stable/js/core/active-link.js
new file mode 100644
index 0000000..97c9966
--- /dev/null
+++ b/core/themes/stable/js/core/active-link.js
@@ -0,0 +1,68 @@
+/**
+ * @file
+ * Attaches behaviors for Drupal's active link marking.
+ */
+
+(function (Drupal, drupalSettings) {
+
+  "use strict";
+
+  /**
+   * Append is-active class.
+   *
+   * The link is only active if its path corresponds to the current path, the
+   * language of the linked path is equal to the current language, and if the
+   * query parameters of the link equal those of the current request, since the
+   * same request with different query parameters may yield a different page
+   * (e.g. pagers, exposed View filters).
+   *
+   * Does not discriminate based on element type, so allows you to set the
+   * is-active class on any element: a, li…
+   *
+   * @type {Drupal~behavior}
+   */
+  Drupal.behaviors.activeLinks = {
+    attach: function (context) {
+      // Start by finding all potentially active links.
+      var path = drupalSettings.path;
+      var queryString = JSON.stringify(path.currentQuery);
+      var querySelector = path.currentQuery ? "[data-drupal-link-query='" + queryString + "']" : ':not([data-drupal-link-query])';
+      var originalSelectors = ['[data-drupal-link-system-path="' + path.currentPath + '"]'];
+      var selectors;
+
+      // If this is the front page, we have to check for the <front> path as
+      // well.
+      if (path.isFront) {
+        originalSelectors.push('[data-drupal-link-system-path="<front>"]');
+      }
+
+      // Add language filtering.
+      selectors = [].concat(
+        // Links without any hreflang attributes (most of them).
+        originalSelectors.map(function (selector) { return selector + ':not([hreflang])'; }),
+        // Links with hreflang equals to the current language.
+        originalSelectors.map(function (selector) { return selector + '[hreflang="' + path.currentLanguage + '"]'; })
+      );
+
+      // Add query string selector for pagers, exposed filters.
+      selectors = selectors.map(function (current) { return current + querySelector; });
+
+      // Query the DOM.
+      var activeLinks = context.querySelectorAll(selectors.join(','));
+      var il = activeLinks.length;
+      for (var i = 0; i < il; i++) {
+        activeLinks[i].classList.add('is-active');
+      }
+    },
+    detach: function (context, settings, trigger) {
+      if (trigger === 'unload') {
+        var activeLinks = context.querySelectorAll('[data-drupal-link-system-path].is-active');
+        var il = activeLinks.length;
+        for (var i = 0; i < il; i++) {
+          activeLinks[i].classList.remove('is-active');
+        }
+      }
+    }
+  };
+
+})(Drupal, drupalSettings);
diff --git a/core/themes/stable/js/core/ajax.js b/core/themes/stable/js/core/ajax.js
new file mode 100644
index 0000000..4dba53e
--- /dev/null
+++ b/core/themes/stable/js/core/ajax.js
@@ -0,0 +1,1109 @@
+/**
+ * @file
+ * Provides Ajax page updating via jQuery $.ajax.
+ *
+ * Ajax is a method of making a request via JavaScript while viewing an HTML
+ * page. The request returns an array of commands encoded in JSON, which is
+ * then executed to make any changes that are necessary to the page.
+ *
+ * Drupal uses this file to enhance form elements with `#ajax['url']` and
+ * `#ajax['wrapper']` properties. If set, this file will automatically be
+ * included to provide Ajax capabilities.
+ */
+
+(function ($, window, Drupal, drupalSettings) {
+
+  "use strict";
+
+  /**
+   * Attaches the Ajax behavior to each Ajax form element.
+   *
+   * @type {Drupal~behavior}
+   */
+  Drupal.behaviors.AJAX = {
+    attach: function (context, settings) {
+
+      function loadAjaxBehavior(base) {
+        var element_settings = settings.ajax[base];
+        if (typeof element_settings.selector === 'undefined') {
+          element_settings.selector = '#' + base;
+        }
+        $(element_settings.selector).once('drupal-ajax').each(function () {
+          element_settings.element = this;
+          element_settings.base = base;
+          Drupal.ajax(element_settings);
+        });
+      }
+
+      // Load all Ajax behaviors specified in the settings.
+      for (var base in settings.ajax) {
+        if (settings.ajax.hasOwnProperty(base)) {
+          loadAjaxBehavior(base);
+        }
+      }
+
+      // Bind Ajax behaviors to all items showing the class.
+      $('.use-ajax').once('ajax').each(function () {
+        var element_settings = {};
+        // Clicked links look better with the throbber than the progress bar.
+        element_settings.progress = {type: 'throbber'};
+
+        // For anchor tags, these will go to the target of the anchor rather
+        // than the usual location.
+        if ($(this).attr('href')) {
+          element_settings.url = $(this).attr('href');
+          element_settings.event = 'click';
+        }
+        element_settings.dialogType = $(this).data('dialog-type');
+        element_settings.dialog = $(this).data('dialog-options');
+        element_settings.base = $(this).attr('id');
+        element_settings.element = this;
+        Drupal.ajax(element_settings);
+      });
+
+      // This class means to submit the form to the action using Ajax.
+      $('.use-ajax-submit').once('ajax').each(function () {
+        var element_settings = {};
+
+        // Ajax submits specified in this manner automatically submit to the
+        // normal form action.
+        element_settings.url = $(this.form).attr('action');
+        // Form submit button clicks need to tell the form what was clicked so
+        // it gets passed in the POST request.
+        element_settings.setClick = true;
+        // Form buttons use the 'click' event rather than mousedown.
+        element_settings.event = 'click';
+        // Clicked form buttons look better with the throbber than the progress
+        // bar.
+        element_settings.progress = {type: 'throbber'};
+        element_settings.base = $(this).attr('id');
+        element_settings.element = this;
+
+        Drupal.ajax(element_settings);
+      });
+    }
+  };
+
+  /**
+   * Extends Error to provide handling for Errors in Ajax.
+   *
+   * @constructor
+   *
+   * @augments Error
+   *
+   * @param {XMLHttpRequest} xmlhttp
+   *   XMLHttpRequest object used for the failed request.
+   * @param {string} uri
+   *   The URI where the error occurred.
+   * @param {string} customMessage
+   *   The custom message.
+   */
+  Drupal.AjaxError = function (xmlhttp, uri, customMessage) {
+
+    var statusCode;
+    var statusText;
+    var pathText;
+    var responseText;
+    var readyStateText;
+    if (xmlhttp.status) {
+      statusCode = "\n" + Drupal.t("An AJAX HTTP error occurred.") + "\n" + Drupal.t("HTTP Result Code: !status", {'!status': xmlhttp.status});
+    }
+    else {
+      statusCode = "\n" + Drupal.t("An AJAX HTTP request terminated abnormally.");
+    }
+    statusCode += "\n" + Drupal.t("Debugging information follows.");
+    pathText = "\n" + Drupal.t("Path: !uri", {'!uri': uri});
+    statusText = '';
+    // In some cases, when statusCode === 0, xmlhttp.statusText may not be
+    // defined. Unfortunately, testing for it with typeof, etc, doesn't seem to
+    // catch that and the test causes an exception. So we need to catch the
+    // exception here.
+    try {
+      statusText = "\n" + Drupal.t("StatusText: !statusText", {'!statusText': $.trim(xmlhttp.statusText)});
+    }
+    catch (e) {
+      // Empty.
+    }
+
+    responseText = '';
+    // Again, we don't have a way to know for sure whether accessing
+    // xmlhttp.responseText is going to throw an exception. So we'll catch it.
+    try {
+      responseText = "\n" + Drupal.t("ResponseText: !responseText", {'!responseText': $.trim(xmlhttp.responseText)});
+    }
+    catch (e) {
+      // Empty.
+    }
+
+    // Make the responseText more readable by stripping HTML tags and newlines.
+    responseText = responseText.replace(/<("[^"]*"|'[^']*'|[^'">])*>/gi, "");
+    responseText = responseText.replace(/[\n]+\s+/g, "\n");
+
+    // We don't need readyState except for status == 0.
+    readyStateText = xmlhttp.status === 0 ? ("\n" + Drupal.t("ReadyState: !readyState", {'!readyState': xmlhttp.readyState})) : "";
+
+    customMessage = customMessage ? ("\n" + Drupal.t("CustomMessage: !customMessage", {'!customMessage': customMessage})) : "";
+
+    /**
+     * Formatted and translated error message.
+     *
+     * @type {string}
+     */
+    this.message = statusCode + pathText + statusText + customMessage + responseText + readyStateText;
+
+    /**
+     * Used by some browsers to display a more accurate stack trace.
+     *
+     * @type {string}
+     */
+    this.name = 'AjaxError';
+  };
+
+  Drupal.AjaxError.prototype = new Error();
+  Drupal.AjaxError.prototype.constructor = Drupal.AjaxError;
+
+  /**
+   * Provides Ajax page updating via jQuery $.ajax.
+   *
+   * This function is designed to improve developer experience by wrapping the
+   * initialization of {@link Drupal.Ajax} objects and storing all created
+   * objects in the {@link Drupal.ajax.instances} array.
+   *
+   * @example
+   * Drupal.behaviors.myCustomAJAXStuff = {
+   *   attach: function (context, settings) {
+   *
+   *     var ajaxSettings = {
+   *       url: 'my/url/path',
+   *       // If the old version of Drupal.ajax() needs to be used those
+   *       // properties can be added
+   *       base: 'myBase',
+   *       element: $(context).find('.someElement')
+   *     };
+   *
+   *     var myAjaxObject = Drupal.ajax(ajaxSettings);
+   *
+   *     // Declare a new Ajax command specifically for this Ajax object.
+   *     myAjaxObject.commands.insert = function (ajax, response, status) {
+   *       $('#my-wrapper').append(response.data);
+   *       alert('New content was appended to #my-wrapper');
+   *     };
+   *
+   *     // This command will remove this Ajax object from the page.
+   *     myAjaxObject.commands.destroyObject = function (ajax, response, status) {
+   *       Drupal.ajax.instances[this.instanceIndex] = null;
+   *     };
+   *
+   *     // Programmatically trigger the Ajax request.
+   *     myAjaxObject.execute();
+   *   }
+   * };
+   *
+   * @param {object} settings
+   *   The settings object passed to {@link Drupal.Ajax} constructor.
+   * @param {string} [settings.base]
+   *   Base is passed to {@link Drupal.Ajax} constructor as the 'base'
+   *   parameter.
+   * @param {HTMLElement} [settings.element]
+   *   Element parameter of {@link Drupal.Ajax} constructor, element on which
+   *   event listeners will be bound.
+   *
+   * @return {Drupal.Ajax}
+   *
+   * @see Drupal.AjaxCommands
+   */
+  Drupal.ajax = function (settings) {
+    if (arguments.length !== 1) {
+      throw new Error('Drupal.ajax() function must be called with one configuration object only');
+    }
+    // Map those config keys to variables for the old Drupal.ajax function.
+    var base = settings.base || false;
+    var element = settings.element || false;
+    delete settings.base;
+    delete settings.element;
+
+    // By default do not display progress for ajax calls without an element.
+    if (!settings.progress && !element) {
+      settings.progress = false;
+    }
+
+    var ajax = new Drupal.Ajax(base, element, settings);
+    ajax.instanceIndex = Drupal.ajax.instances.length;
+    Drupal.ajax.instances.push(ajax);
+
+    return ajax;
+  };
+
+  /**
+   * Contains all created Ajax objects.
+   *
+   * @type {Array.<Drupal.Ajax>}
+   */
+  Drupal.ajax.instances = [];
+
+  /**
+   * Ajax constructor.
+   *
+   * The Ajax request returns an array of commands encoded in JSON, which is
+   * then executed to make any changes that are necessary to the page.
+   *
+   * Drupal uses this file to enhance form elements with `#ajax['url']` and
+   * `#ajax['wrapper']` properties. If set, this file will automatically be
+   * included to provide Ajax capabilities.
+   *
+   * @constructor
+   *
+   * @param {string} [base]
+   *   Base parameter of {@link Drupal.Ajax} constructor
+   * @param {HTMLElement} [element]
+   *   Element parameter of {@link Drupal.Ajax} constructor, element on which
+   *   event listeners will be bound.
+   * @param {object} element_settings
+   * @param {string} element_settings.url
+   *   Target of the Ajax request.
+   * @param {string} [element_settings.event]
+   *   Event bound to settings.element which will trigger the Ajax request.
+   * @param {string} [element_settings.method]
+   *   Name of the jQuery method used to insert new content in the targeted
+   *   element.
+   */
+  Drupal.Ajax = function (base, element, element_settings) {
+    var defaults = {
+      event: element ? 'mousedown' : null,
+      keypress: true,
+      selector: base ? '#' + base : null,
+      effect: 'none',
+      speed: 'none',
+      method: 'replaceWith',
+      progress: {
+        type: 'throbber',
+        message: Drupal.t('Please wait...')
+      },
+      submit: {
+        js: true
+      }
+    };
+
+    $.extend(this, defaults, element_settings);
+
+    /**
+     * @type {Drupal.AjaxCommands}
+     */
+    this.commands = new Drupal.AjaxCommands();
+    this.instanceIndex = false;
+
+    // @todo Remove this after refactoring the PHP code to:
+    //   - Call this 'selector'.
+    //   - Include the '#' for ID-based selectors.
+    //   - Support non-ID-based selectors.
+    if (this.wrapper) {
+
+      /**
+       * @type {string}
+       */
+      this.wrapper = '#' + this.wrapper;
+    }
+
+    /**
+     * @type {HTMLElement}
+     */
+    this.element = element;
+
+    /**
+     * @type {object}
+     */
+    this.element_settings = element_settings;
+
+    // If there isn't a form, jQuery.ajax() will be used instead, allowing us to
+    // bind Ajax to links as well.
+    if (this.element && this.element.form) {
+
+      /**
+       * @type {jQuery}
+       */
+      this.$form = $(this.element.form);
+    }
+
+    // If no Ajax callback URL was given, use the link href or form action.
+    if (!this.url) {
+      var $element = $(this.element);
+      if ($element.is('a')) {
+        this.url = $element.attr('href');
+      }
+      else if (this.element && element.form) {
+        this.url = this.$form.attr('action');
+      }
+    }
+
+    // Replacing 'nojs' with 'ajax' in the URL allows for an easy method to let
+    // the server detect when it needs to degrade gracefully.
+    // There are four scenarios to check for:
+    // 1. /nojs/
+    // 2. /nojs$ - The end of a URL string.
+    // 3. /nojs? - Followed by a query (e.g. path/nojs?destination=foobar).
+    // 4. /nojs# - Followed by a fragment (e.g.: path/nojs#myfragment).
+    var originalUrl = this.url;
+    this.url = this.url.replace(/\/nojs(\/|$|\?|#)/g, '/ajax$1');
+    // If the 'nojs' version of the URL is trusted, also trust the 'ajax'
+    // version.
+    if (drupalSettings.ajaxTrustedUrl[originalUrl]) {
+      drupalSettings.ajaxTrustedUrl[this.url] = true;
+    }
+
+    // Set the options for the ajaxSubmit function.
+    // The 'this' variable will not persist inside of the options object.
+    var ajax = this;
+
+    /**
+     * Options for the ajaxSubmit function.
+     *
+     * @name Drupal.Ajax#options
+     *
+     * @type {object}
+     *
+     * @prop {string} url
+     * @prop {object} data
+     * @prop {function} beforeSerialize
+     * @prop {function} beforeSubmit
+     * @prop {function} beforeSend
+     * @prop {function} success
+     * @prop {function} complete
+     * @prop {string} dataType
+     * @prop {object} accepts
+     * @prop {string} accepts.json
+     * @prop {string} type
+     */
+    ajax.options = {
+      url: ajax.url,
+      data: ajax.submit,
+      beforeSerialize: function (element_settings, options) {
+        return ajax.beforeSerialize(element_settings, options);
+      },
+      beforeSubmit: function (form_values, element_settings, options) {
+        ajax.ajaxing = true;
+        return ajax.beforeSubmit(form_values, element_settings, options);
+      },
+      beforeSend: function (xmlhttprequest, options) {
+        ajax.ajaxing = true;
+        return ajax.beforeSend(xmlhttprequest, options);
+      },
+      success: function (response, status, xmlhttprequest) {
+        // Sanity check for browser support (object expected).
+        // When using iFrame uploads, responses must be returned as a string.
+        if (typeof response === 'string') {
+          response = $.parseJSON(response);
+        }
+
+        // Prior to invoking the response's commands, verify that they can be
+        // trusted by checking for a response header. See
+        // \Drupal\Core\EventSubscriber\AjaxResponseSubscriber for details.
+        // - Empty responses are harmless so can bypass verification. This
+        //   avoids an alert message for server-generated no-op responses that
+        //   skip Ajax rendering.
+        // - Ajax objects with trusted URLs (e.g., ones defined server-side via
+        //   #ajax) can bypass header verification. This is especially useful
+        //   for Ajax with multipart forms. Because IFRAME transport is used,
+        //   the response headers cannot be accessed for verification.
+        if (response !== null && !drupalSettings.ajaxTrustedUrl[ajax.url]) {
+          if (xmlhttprequest.getResponseHeader('X-Drupal-Ajax-Token') !== '1') {
+            var customMessage = Drupal.t("The response failed verification so will not be processed.");
+            return ajax.error(xmlhttprequest, ajax.url, customMessage);
+          }
+        }
+
+        return ajax.success(response, status);
+      },
+      complete: function (xmlhttprequest, status) {
+        ajax.ajaxing = false;
+        if (status === 'error' || status === 'parsererror') {
+          return ajax.error(xmlhttprequest, ajax.url);
+        }
+      },
+      dataType: 'json',
+      type: 'POST'
+    };
+
+    if (element_settings.dialog) {
+      ajax.options.data.dialogOptions = element_settings.dialog;
+    }
+
+    // Ensure that we have a valid URL by adding ? when no query parameter is
+    // yet available, otherwise append using &.
+    if (ajax.options.url.indexOf('?') === -1) {
+      ajax.options.url += '?';
+    }
+    else {
+      ajax.options.url += '&';
+    }
+    ajax.options.url += Drupal.ajax.WRAPPER_FORMAT + '=drupal_' + (element_settings.dialogType || 'ajax');
+
+    // Bind the ajaxSubmit function to the element event.
+    $(ajax.element).on(element_settings.event, function (event) {
+      if (!drupalSettings.ajaxTrustedUrl[ajax.url] && !Drupal.url.isLocal(ajax.url)) {
+        throw new Error(Drupal.t('The callback URL is not local and not trusted: !url', {'!url': ajax.url}));
+      }
+      return ajax.eventResponse(this, event);
+    });
+
+    // If necessary, enable keyboard submission so that Ajax behaviors
+    // can be triggered through keyboard input as well as e.g. a mousedown
+    // action.
+    if (element_settings.keypress) {
+      $(ajax.element).on('keypress', function (event) {
+        return ajax.keypressResponse(this, event);
+      });
+    }
+
+    // If necessary, prevent the browser default action of an additional event.
+    // For example, prevent the browser default action of a click, even if the
+    // Ajax behavior binds to mousedown.
+    if (element_settings.prevent) {
+      $(ajax.element).on(element_settings.prevent, false);
+    }
+  };
+
+  /**
+   * URL query attribute to indicate the wrapper used to render a request.
+   *
+   * The wrapper format determines how the HTML is wrapped, for example in a
+   * modal dialog.
+   *
+   * @const {string}
+   */
+  Drupal.ajax.WRAPPER_FORMAT = '_wrapper_format';
+
+  /**
+   * Request parameter to indicate that a request is a Drupal Ajax request.
+   *
+   * @const
+   */
+  Drupal.Ajax.AJAX_REQUEST_PARAMETER = '_drupal_ajax';
+
+  /**
+   * Execute the ajax request.
+   *
+   * Allows developers to execute an Ajax request manually without specifying
+   * an event to respond to.
+   */
+  Drupal.Ajax.prototype.execute = function () {
+    // Do not perform another ajax command if one is already in progress.
+    if (this.ajaxing) {
+      return;
+    }
+
+    try {
+      this.beforeSerialize(this.element, this.options);
+      $.ajax(this.options);
+    }
+    catch (e) {
+      // Unset the ajax.ajaxing flag here because it won't be unset during
+      // the complete response.
+      this.ajaxing = false;
+      window.alert("An error occurred while attempting to process " + this.options.url + ": " + e.message);
+    }
+  };
+
+  /**
+   * Handle a key press.
+   *
+   * The Ajax object will, if instructed, bind to a key press response. This
+   * will test to see if the key press is valid to trigger this event and
+   * if it is, trigger it for us and prevent other keypresses from triggering.
+   * In this case we're handling RETURN and SPACEBAR keypresses (event codes 13
+   * and 32. RETURN is often used to submit a form when in a textfield, and
+   * SPACE is often used to activate an element without submitting.
+   *
+   * @param {HTMLElement} element
+   * @param {jQuery.Event} event
+   */
+  Drupal.Ajax.prototype.keypressResponse = function (element, event) {
+    // Create a synonym for this to reduce code confusion.
+    var ajax = this;
+
+    // Detect enter key and space bar and allow the standard response for them,
+    // except for form elements of type 'text', 'tel', 'number' and 'textarea',
+    // where the spacebar activation causes inappropriate activation if
+    // #ajax['keypress'] is TRUE. On a text-type widget a space should always
+    // be a space.
+    if (event.which === 13 || (event.which === 32 && element.type !== 'text' &&
+      element.type !== 'textarea' && element.type !== 'tel' && element.type !== 'number')) {
+      event.preventDefault();
+      event.stopPropagation();
+      $(ajax.element_settings.element).trigger(ajax.element_settings.event);
+    }
+  };
+
+  /**
+   * Handle an event that triggers an Ajax response.
+   *
+   * When an event that triggers an Ajax response happens, this method will
+   * perform the actual Ajax call. It is bound to the event using
+   * bind() in the constructor, and it uses the options specified on the
+   * Ajax object.
+   *
+   * @param {HTMLElement} element
+   * @param {jQuery.Event} event
+   */
+  Drupal.Ajax.prototype.eventResponse = function (element, event) {
+    event.preventDefault();
+    event.stopPropagation();
+
+    // Create a synonym for this to reduce code confusion.
+    var ajax = this;
+
+    // Do not perform another Ajax command if one is already in progress.
+    if (ajax.ajaxing) {
+      return;
+    }
+
+    try {
+      if (ajax.$form) {
+        // If setClick is set, we must set this to ensure that the button's
+        // value is passed.
+        if (ajax.setClick) {
+          // Mark the clicked button. 'form.clk' is a special variable for
+          // ajaxSubmit that tells the system which element got clicked to
+          // trigger the submit. Without it there would be no 'op' or
+          // equivalent.
+          element.form.clk = element;
+        }
+
+        ajax.$form.ajaxSubmit(ajax.options);
+      }
+      else {
+        ajax.beforeSerialize(ajax.element, ajax.options);
+        $.ajax(ajax.options);
+      }
+    }
+    catch (e) {
+      // Unset the ajax.ajaxing flag here because it won't be unset during
+      // the complete response.
+      ajax.ajaxing = false;
+      window.alert("An error occurred while attempting to process " + ajax.options.url + ": " + e.message);
+    }
+  };
+
+  /**
+   * Handler for the form serialization.
+   *
+   * Runs before the beforeSend() handler (see below), and unlike that one, runs
+   * before field data is collected.
+   *
+   * @param {HTMLElement} element
+   * @param {object} options
+   * @param {object} options.data
+   */
+  Drupal.Ajax.prototype.beforeSerialize = function (element, options) {
+    // Allow detaching behaviors to update field values before collecting them.
+    // This is only needed when field values are added to the POST data, so only
+    // when there is a form such that this.$form.ajaxSubmit() is used instead of
+    // $.ajax(). When there is no form and $.ajax() is used, beforeSerialize()
+    // isn't called, but don't rely on that: explicitly check this.$form.
+    if (this.$form) {
+      var settings = this.settings || drupalSettings;
+      Drupal.detachBehaviors(this.$form.get(0), settings, 'serialize');
+    }
+
+    // Inform Drupal that this is an AJAX request.
+    options.data[Drupal.Ajax.AJAX_REQUEST_PARAMETER] = 1;
+
+    // Allow Drupal to return new JavaScript and CSS files to load without
+    // returning the ones already loaded.
+    // @see \Drupal\Core\Theme\AjaxBasePageNegotiator
+    // @see \Drupal\Core\Asset\LibraryDependencyResolverInterface::getMinimalRepresentativeSubset()
+    // @see system_js_settings_alter()
+    var pageState = drupalSettings.ajaxPageState;
+    options.data['ajax_page_state[theme]'] = pageState.theme;
+    options.data['ajax_page_state[theme_token]'] = pageState.theme_token;
+    options.data['ajax_page_state[libraries]'] = pageState.libraries;
+  };
+
+  /**
+   * Modify form values prior to form submission.
+   *
+   * @param {object} form_values
+   * @param {HTMLElement} element
+   * @param {object} options
+   */
+  Drupal.Ajax.prototype.beforeSubmit = function (form_values, element, options) {
+    // This function is left empty to make it simple to override for modules
+    // that wish to add functionality here.
+  };
+
+  /**
+   * Prepare the Ajax request before it is sent.
+   *
+   * @param {XMLHttpRequest} xmlhttprequest
+   * @param {object} options
+   * @param {object} options.extraData
+   */
+  Drupal.Ajax.prototype.beforeSend = function (xmlhttprequest, options) {
+    // For forms without file inputs, the jQuery Form plugin serializes the
+    // form values, and then calls jQuery's $.ajax() function, which invokes
+    // this handler. In this circumstance, options.extraData is never used. For
+    // forms with file inputs, the jQuery Form plugin uses the browser's normal
+    // form submission mechanism, but captures the response in a hidden IFRAME.
+    // In this circumstance, it calls this handler first, and then appends
+    // hidden fields to the form to submit the values in options.extraData.
+    // There is no simple way to know which submission mechanism will be used,
+    // so we add to extraData regardless, and allow it to be ignored in the
+    // former case.
+    if (this.$form) {
+      options.extraData = options.extraData || {};
+
+      // Let the server know when the IFRAME submission mechanism is used. The
+      // server can use this information to wrap the JSON response in a
+      // TEXTAREA, as per http://jquery.malsup.com/form/#file-upload.
+      options.extraData.ajax_iframe_upload = '1';
+
+      // The triggering element is about to be disabled (see below), but if it
+      // contains a value (e.g., a checkbox, textfield, select, etc.), ensure
+      // that value is included in the submission. As per above, submissions
+      // that use $.ajax() are already serialized prior to the element being
+      // disabled, so this is only needed for IFRAME submissions.
+      var v = $.fieldValue(this.element);
+      if (v !== null) {
+        options.extraData[this.element.name] = v;
+      }
+    }
+
+    // Disable the element that received the change to prevent user interface
+    // interaction while the Ajax request is in progress. ajax.ajaxing prevents
+    // the element from triggering a new request, but does not prevent the user
+    // from changing its value.
+    $(this.element).prop('disabled', true);
+
+    if (!this.progress || !this.progress.type) {
+      return;
+    }
+
+    // Insert progress indicator.
+    var progressIndicatorMethod = 'setProgressIndicator' + this.progress.type.slice(0, 1).toUpperCase() + this.progress.type.slice(1).toLowerCase();
+    if (progressIndicatorMethod in this && typeof this[progressIndicatorMethod] === 'function') {
+      this[progressIndicatorMethod].call(this);
+    }
+  };
+
+  /**
+   * Sets the progress bar progress indicator.
+   */
+  Drupal.Ajax.prototype.setProgressIndicatorBar = function () {
+    var progressBar = new Drupal.ProgressBar('ajax-progress-' + this.element.id, $.noop, this.progress.method, $.noop);
+    if (this.progress.message) {
+      progressBar.setProgress(-1, this.progress.message);
+    }
+    if (this.progress.url) {
+      progressBar.startMonitoring(this.progress.url, this.progress.interval || 1500);
+    }
+    this.progress.element = $(progressBar.element).addClass('ajax-progress ajax-progress-bar');
+    this.progress.object = progressBar;
+    $(this.element).after(this.progress.element);
+  };
+
+  /**
+   * Sets the throbber progress indicator.
+   */
+  Drupal.Ajax.prototype.setProgressIndicatorThrobber = function () {
+    this.progress.element = $('<div class="ajax-progress ajax-progress-throbber"><div class="throbber">&nbsp;</div></div>');
+    if (this.progress.message) {
+      this.progress.element.find('.throbber').after('<div class="message">' + this.progress.message + '</div>');
+    }
+    $(this.element).after(this.progress.element);
+  };
+
+  /**
+   * Sets the fullscreen progress indicator.
+   */
+  Drupal.Ajax.prototype.setProgressIndicatorFullscreen = function () {
+    this.progress.element = $('<div class="ajax-progress ajax-progress-fullscreen">&nbsp;</div>');
+    $('body').after(this.progress.element);
+  };
+
+  /**
+   * Handler for the form redirection completion.
+   *
+   * @param {Array.<Drupal.AjaxCommands~commandDefinition>} response
+   * @param {number} status
+   */
+  Drupal.Ajax.prototype.success = function (response, status) {
+    // Remove the progress element.
+    if (this.progress.element) {
+      $(this.progress.element).remove();
+    }
+    if (this.progress.object) {
+      this.progress.object.stopMonitoring();
+    }
+    $(this.element).prop('disabled', false);
+
+    for (var i in response) {
+      if (response.hasOwnProperty(i) && response[i].command && this.commands[response[i].command]) {
+        this.commands[response[i].command](this, response[i], status);
+      }
+    }
+
+    // Reattach behaviors, if they were detached in beforeSerialize(). The
+    // attachBehaviors() called on the new content from processing the response
+    // commands is not sufficient, because behaviors from the entire form need
+    // to be reattached.
+    if (this.$form) {
+      var settings = this.settings || drupalSettings;
+      Drupal.attachBehaviors(this.$form.get(0), settings);
+    }
+
+    // Remove any response-specific settings so they don't get used on the next
+    // call by mistake.
+    this.settings = null;
+  };
+
+  /**
+   * Build an effect object to apply an effect when adding new HTML.
+   *
+   * @param {object} response
+   * @param {string} [response.effect]
+   * @param {string|number} [response.speed]
+   *
+   * @return {object}
+   */
+  Drupal.Ajax.prototype.getEffect = function (response) {
+    var type = response.effect || this.effect;
+    var speed = response.speed || this.speed;
+
+    var effect = {};
+    if (type === 'none') {
+      effect.showEffect = 'show';
+      effect.hideEffect = 'hide';
+      effect.showSpeed = '';
+    }
+    else if (type === 'fade') {
+      effect.showEffect = 'fadeIn';
+      effect.hideEffect = 'fadeOut';
+      effect.showSpeed = speed;
+    }
+    else {
+      effect.showEffect = type + 'Toggle';
+      effect.hideEffect = type + 'Toggle';
+      effect.showSpeed = speed;
+    }
+
+    return effect;
+  };
+
+  /**
+   * Handler for the form redirection error.
+   *
+   * @param {object} xmlhttprequest
+   * @param {string} uri
+   * @param {string} customMessage
+   */
+  Drupal.Ajax.prototype.error = function (xmlhttprequest, uri, customMessage) {
+    // Remove the progress element.
+    if (this.progress.element) {
+      $(this.progress.element).remove();
+    }
+    if (this.progress.object) {
+      this.progress.object.stopMonitoring();
+    }
+    // Undo hide.
+    $(this.wrapper).show();
+    // Re-enable the element.
+    $(this.element).prop('disabled', false);
+    // Reattach behaviors, if they were detached in beforeSerialize().
+    if (this.$form) {
+      var settings = this.settings || drupalSettings;
+      Drupal.attachBehaviors(this.$form.get(0), settings);
+    }
+    throw new Drupal.AjaxError(xmlhttprequest, uri, customMessage);
+  };
+
+  /**
+   * @typedef {object} Drupal.AjaxCommands~commandDefinition
+   *
+   * @prop {string} command
+   * @prop {string} [method]
+   * @prop {string} [selector]
+   * @prop {string} [data]
+   * @prop {object} [settings]
+   * @prop {bool} [asterisk]
+   * @prop {string} [text]
+   * @prop {string} [title]
+   * @prop {string} [url]
+   * @prop {object} [argument]
+   * @prop {string} [name]
+   * @prop {string} [value]
+   * @prop {string} [old]
+   * @prop {string} [new]
+   * @prop {bool} [merge]
+   * @prop {Array} [args]
+   *
+   * @see Drupal.AjaxCommands
+   */
+
+  /**
+   * Provide a series of commands that the client will perform.
+   *
+   * @constructor
+   */
+  Drupal.AjaxCommands = function () {};
+  Drupal.AjaxCommands.prototype = {
+
+    /**
+     * Command to insert new content into the DOM.
+     *
+     * @param {Drupal.Ajax} ajax
+     * @param {object} response
+     * @param {string} response.data
+     * @param {string} [response.method]
+     * @param {string} [response.selector]
+     * @param {object} [response.settings]
+     * @param {number} [status]
+     */
+    insert: function (ajax, response, status) {
+      // Get information from the response. If it is not there, default to
+      // our presets.
+      var wrapper = response.selector ? $(response.selector) : $(ajax.wrapper);
+      var method = response.method || ajax.method;
+      var effect = ajax.getEffect(response);
+      var settings;
+
+      // We don't know what response.data contains: it might be a string of text
+      // without HTML, so don't rely on jQuery correctly interpreting
+      // $(response.data) as new HTML rather than a CSS selector. Also, if
+      // response.data contains top-level text nodes, they get lost with either
+      // $(response.data) or $('<div></div>').replaceWith(response.data).
+      var new_content_wrapped = $('<div></div>').html(response.data);
+      var new_content = new_content_wrapped.contents();
+
+      // For legacy reasons, the effects processing code assumes that
+      // new_content consists of a single top-level element. Also, it has not
+      // been sufficiently tested whether attachBehaviors() can be successfully
+      // called with a context object that includes top-level text nodes.
+      // However, to give developers full control of the HTML appearing in the
+      // page, and to enable Ajax content to be inserted in places where DIV
+      // elements are not allowed (e.g., within TABLE, TR, and SPAN parents),
+      // we check if the new content satisfies the requirement of a single
+      // top-level element, and only use the container DIV created above when
+      // it doesn't. For more information, please see
+      // https://www.drupal.org/node/736066.
+      if (new_content.length !== 1 || new_content.get(0).nodeType !== 1) {
+        new_content = new_content_wrapped;
+      }
+
+      // If removing content from the wrapper, detach behaviors first.
+      switch (method) {
+        case 'html':
+        case 'replaceWith':
+        case 'replaceAll':
+        case 'empty':
+        case 'remove':
+          settings = response.settings || ajax.settings || drupalSettings;
+          Drupal.detachBehaviors(wrapper.get(0), settings);
+      }
+
+      // Add the new content to the page.
+      wrapper[method](new_content);
+
+      // Immediately hide the new content if we're using any effects.
+      if (effect.showEffect !== 'show') {
+        new_content.hide();
+      }
+
+      // Determine which effect to use and what content will receive the
+      // effect, then show the new content.
+      if (new_content.find('.ajax-new-content').length > 0) {
+        new_content.find('.ajax-new-content').hide();
+        new_content.show();
+        new_content.find('.ajax-new-content')[effect.showEffect](effect.showSpeed);
+      }
+      else if (effect.showEffect !== 'show') {
+        new_content[effect.showEffect](effect.showSpeed);
+      }
+
+      // Attach all JavaScript behaviors to the new content, if it was
+      // successfully added to the page, this if statement allows
+      // `#ajax['wrapper']` to be optional.
+      if (new_content.parents('html').length > 0) {
+        // Apply any settings from the returned JSON if available.
+        settings = response.settings || ajax.settings || drupalSettings;
+        Drupal.attachBehaviors(new_content.get(0), settings);
+      }
+    },
+
+    /**
+     * Command to remove a chunk from the page.
+     *
+     * @param {Drupal.Ajax} [ajax]
+     * @param {object} response
+     * @param {string} response.selector
+     * @param {object} [response.settings]
+     * @param {number} [status]
+     */
+    remove: function (ajax, response, status) {
+      var settings = response.settings || ajax.settings || drupalSettings;
+      $(response.selector).each(function () {
+        Drupal.detachBehaviors(this, settings);
+      })
+        .remove();
+    },
+
+    /**
+     * Command to mark a chunk changed.
+     *
+     * @param {Drupal.Ajax} [ajax]
+     * @param {object} response
+     * @param {string} response.selector
+     * @param {bool} [response.asterisk]
+     * @param {number} [status]
+     */
+    changed: function (ajax, response, status) {
+      if (!$(response.selector).hasClass('ajax-changed')) {
+        $(response.selector).addClass('ajax-changed');
+        if (response.asterisk) {
+          $(response.selector).find(response.asterisk).append(' <abbr class="ajax-changed" title="' + Drupal.t('Changed') + '">*</abbr> ');
+        }
+      }
+    },
+
+    /**
+     * Command to provide an alert.
+     *
+     * @param {Drupal.Ajax} [ajax]
+     * @param {object} response
+     * @param {string} response.text
+     * @param {string} response.title
+     * @param {number} [status]
+     */
+    alert: function (ajax, response, status) {
+      window.alert(response.text, response.title);
+    },
+
+    /**
+     * Command to set the window.location, redirecting the browser.
+     *
+     * @param {Drupal.Ajax} [ajax]
+     * @param {object} response
+     * @param {string} response.url
+     * @param {number} [status]
+     */
+    redirect: function (ajax, response, status) {
+      window.location = response.url;
+    },
+
+    /**
+     * Command to provide the jQuery css() function.
+     *
+     * @param {Drupal.Ajax} [ajax]
+     * @param {object} response
+     * @param {object} response.argument
+     * @param {number} [status]
+     */
+    css: function (ajax, response, status) {
+      $(response.selector).css(response.argument);
+    },
+
+    /**
+     * Command to set the settings used for other commands in this response.
+     *
+     * @param {Drupal.Ajax} [ajax]
+     * @param {object} response
+     * @param {bool} response.merge
+     * @param {object} response.settings
+     * @param {number} [status]
+     */
+    settings: function (ajax, response, status) {
+      if (response.merge) {
+        $.extend(true, drupalSettings, response.settings);
+      }
+      else {
+        ajax.settings = response.settings;
+      }
+    },
+
+    /**
+     * Command to attach data using jQuery's data API.
+     *
+     * @param {Drupal.Ajax} [ajax]
+     * @param {object} response
+     * @param {string} response.name
+     * @param {string} response.selector
+     * @param {string|object} response.value
+     * @param {number} [status]
+     */
+    data: function (ajax, response, status) {
+      $(response.selector).data(response.name, response.value);
+    },
+
+    /**
+     * Command to apply a jQuery method.
+     *
+     * @param {Drupal.Ajax} [ajax]
+     * @param {object} response
+     * @param {Array} response.args
+     * @param {string} response.method
+     * @param {string} response.selector
+     * @param {number} [status]
+     */
+    invoke: function (ajax, response, status) {
+      var $element = $(response.selector);
+      $element[response.method].apply($element, response.args);
+    },
+
+    /**
+     * Command to restripe a table.
+     *
+     * @param {Drupal.Ajax} [ajax]
+     * @param {object} response
+     * @param {string} response.selector
+     * @param {number} [status]
+     */
+    restripe: function (ajax, response, status) {
+      // :even and :odd are reversed because jQuery counts from 0 and
+      // we count from 1, so we're out of sync.
+      // Match immediate children of the parent element to allow nesting.
+      $(response.selector).find('> tbody > tr:visible, > tr:visible')
+        .removeClass('odd even')
+        .filter(':even').addClass('odd').end()
+        .filter(':odd').addClass('even');
+    },
+
+    /**
+     * Command to update a form's build ID.
+     *
+     * @param {Drupal.Ajax} [ajax]
+     * @param {object} response
+     * @param {string} response.old
+     * @param {string} response.new
+     * @param {number} [status]
+     */
+    update_build_id: function (ajax, response, status) {
+      $('input[name="form_build_id"][value="' + response.old + '"]').val(response.new);
+    },
+
+    /**
+     * Command to add css.
+     *
+     * Uses the proprietary addImport method if available as browsers which
+     * support that method ignore @import statements in dynamically added
+     * stylesheets.
+     *
+     * @param {Drupal.Ajax} [ajax]
+     * @param {object} response
+     * @param {string} response.data
+     * @param {number} [status]
+     */
+    add_css: function (ajax, response, status) {
+      // Add the styles in the normal way.
+      $('head').prepend(response.data);
+      // Add imports in the styles using the addImport method if available.
+      var match;
+      var importMatch = /^@import url\("(.*)"\);$/igm;
+      if (document.styleSheets[0].addImport && importMatch.test(response.data)) {
+        importMatch.lastIndex = 0;
+        do {
+          match = importMatch.exec(response.data);
+          document.styleSheets[0].addImport(match[1]);
+        } while (match);
+      }
+    }
+  };
+
+})(jQuery, this, Drupal, drupalSettings);
diff --git a/core/themes/stable/js/core/announce.js b/core/themes/stable/js/core/announce.js
new file mode 100644
index 0000000..0a0abeb
--- /dev/null
+++ b/core/themes/stable/js/core/announce.js
@@ -0,0 +1,116 @@
+/**
+ * @file
+ * Adds an HTML element and method to trigger audio UAs to read system messages.
+ *
+ * Use {@link Drupal.announce} to indicate to screen reader users that an
+ * element on the page has changed state. For instance, if clicking a link
+ * loads 10 more items into a list, one might announce the change like this.
+ *
+ * @example
+ * $('#search-list')
+ *   .on('itemInsert', function (event, data) {
+ *     // Insert the new items.
+ *     $(data.container.el).append(data.items.el);
+ *     // Announce the change to the page contents.
+ *     Drupal.announce(Drupal.t('@count items added to @container',
+ *       {'@count': data.items.length, '@container': data.container.title}
+ *     ));
+ *   });
+ */
+
+(function (Drupal, debounce) {
+
+  "use strict";
+
+  var liveElement;
+  var announcements = [];
+
+  /**
+   * Builds a div element with the aria-live attribute and add it to the DOM.
+   *
+   * @type {Drupal~behavior}
+   */
+  Drupal.behaviors.drupalAnnounce = {
+    attach: function (context) {
+      // Create only one aria-live element.
+      if (!liveElement) {
+        liveElement = document.createElement('div');
+        liveElement.id = 'drupal-live-announce';
+        liveElement.className = 'visually-hidden';
+        liveElement.setAttribute('aria-live', 'polite');
+        liveElement.setAttribute('aria-busy', 'false');
+        document.body.appendChild(liveElement);
+      }
+    }
+  };
+
+  /**
+   * Concatenates announcements to a single string; appends to the live region.
+   */
+  function announce() {
+    var text = [];
+    var priority = 'polite';
+    var announcement;
+
+    // Create an array of announcement strings to be joined and appended to the
+    // aria live region.
+    var il = announcements.length;
+    for (var i = 0; i < il; i++) {
+      announcement = announcements.pop();
+      text.unshift(announcement.text);
+      // If any of the announcements has a priority of assertive then the group
+      // of joined announcements will have this priority.
+      if (announcement.priority === 'assertive') {
+        priority = 'assertive';
+      }
+    }
+
+    if (text.length) {
+      // Clear the liveElement so that repeated strings will be read.
+      liveElement.innerHTML = '';
+      // Set the busy state to true until the node changes are complete.
+      liveElement.setAttribute('aria-busy', 'true');
+      // Set the priority to assertive, or default to polite.
+      liveElement.setAttribute('aria-live', priority);
+      // Print the text to the live region. Text should be run through
+      // Drupal.t() before being passed to Drupal.announce().
+      liveElement.innerHTML = text.join('\n');
+      // The live text area is updated. Allow the AT to announce the text.
+      liveElement.setAttribute('aria-busy', 'false');
+    }
+  }
+
+  /**
+   * Triggers audio UAs to read the supplied text.
+   *
+   * The aria-live region will only read the text that currently populates its
+   * text node. Replacing text quickly in rapid calls to announce results in
+   * only the text from the most recent call to {@link Drupal.announce} being
+   * read. By wrapping the call to announce in a debounce function, we allow for
+   * time for multiple calls to {@link Drupal.announce} to queue up their
+   * messages. These messages are then joined and append to the aria-live region
+   * as one text node.
+   *
+   * @param {string} text
+   *   A string to be read by the UA.
+   * @param {string} [priority='polite']
+   *   A string to indicate the priority of the message. Can be either
+   *   'polite' or 'assertive'.
+   *
+   * @return {function}
+   *
+   * @see http://www.w3.org/WAI/PF/aria-practices/#liveprops
+   */
+  Drupal.announce = function (text, priority) {
+    // Save the text and priority into a closure variable. Multiple simultaneous
+    // announcements will be concatenated and read in sequence.
+    announcements.push({
+      text: text,
+      priority: priority
+    });
+    // Immediately invoke the function that debounce returns. 200 ms is right at
+    // the cusp where humans notice a pause, so we will wait
+    // at most this much time before the set of queued announcements is read.
+    return (debounce(announce, 200)());
+  };
+}(Drupal, Drupal.debounce));
diff --git a/core/themes/stable/js/core/autocomplete.js b/core/themes/stable/js/core/autocomplete.js
new file mode 100644
index 0000000..f826281
--- /dev/null
+++ b/core/themes/stable/js/core/autocomplete.js
@@ -0,0 +1,250 @@
+/**
+ * @file
+ * Autocomplete based on jQuery UI.
+ */
+
+(function ($, Drupal) {
+
+  "use strict";
+
+  var autocomplete;
+
+  /**
+   * Helper splitting terms from the autocomplete value.
+   *
+   * @function Drupal.autocomplete.splitValues
+   *
+   * @param {string} value
+   *
+   * @return {Array}
+   */
+  function autocompleteSplitValues(value) {
+    // We will match the value against comma-separated terms.
+    var result = [];
+    var quote = false;
+    var current = '';
+    var valueLength = value.length;
+    var character;
+
+    for (var i = 0; i < valueLength; i++) {
+      character = value.charAt(i);
+      if (character === '"') {
+        current += character;
+        quote = !quote;
+      }
+      else if (character === ',' && !quote) {
+        result.push(current.trim());
+        current = '';
+      }
+      else {
+        current += character;
+      }
+    }
+    if (value.length > 0) {
+      result.push($.trim(current));
+    }
+
+    return result;
+  }
+
+  /**
+   * Returns the last value of an multi-value textfield.
+   *
+   * @function Drupal.autocomplete.extractLastTerm
+   *
+   * @param {string} terms
+   *
+   * @return {string}
+   */
+  function extractLastTerm(terms) {
+    return autocomplete.splitValues(terms).pop();
+  }
+
+  /**
+   * The search handler is called before a search is performed.
+   *
+   * @function Drupal.autocomplete.options.search
+   *
+   * @param {object} event
+   *
+   * @return {bool}
+   */
+  function searchHandler(event) {
+    var options = autocomplete.options;
+    var term = autocomplete.extractLastTerm(event.target.value);
+    // Abort search if the first character is in firstCharacterBlacklist.
+    if (term.length > 0 && options.firstCharacterBlacklist.indexOf(term[0]) !== -1) {
+      return false;
+    }
+    // Only search when the term is at least the minimum length.
+    return term.length >= options.minLength;
+  }
+
+  /**
+   * JQuery UI autocomplete source callback.
+   *
+   * @param {object} request
+   * @param {function} response
+   */
+  function sourceData(request, response) {
+    var elementId = this.element.attr('id');
+
+    if (!(elementId in autocomplete.cache)) {
+      autocomplete.cache[elementId] = {};
+    }
+
+    /**
+     * Filter through the suggestions removing all terms already tagged and
+     * display the available terms to the user.
+     *
+     * @param {object} suggestions
+     */
+    function showSuggestions(suggestions) {
+      var tagged = autocomplete.splitValues(request.term);
+      var il = tagged.length;
+      for (var i = 0; i < il; i++) {
+        var index = suggestions.indexOf(tagged[i]);
+        if (index >= 0) {
+          suggestions.splice(index, 1);
+        }
+      }
+      response(suggestions);
+    }
+
+    /**
+     * Transforms the data object into an array and update autocomplete results.
+     *
+     * @param {object} data
+     */
+    function sourceCallbackHandler(data) {
+      autocomplete.cache[elementId][term] = data;
+
+      // Send the new string array of terms to the jQuery UI list.
+      showSuggestions(data);
+    }
+
+    // Get the desired term and construct the autocomplete URL for it.
+    var term = autocomplete.extractLastTerm(request.term);
+
+    // Check if the term is already cached.
+    if (autocomplete.cache[elementId].hasOwnProperty(term)) {
+      showSuggestions(autocomplete.cache[elementId][term]);
+    }
+    else {
+      var options = $.extend({success: sourceCallbackHandler, data: {q: term}}, autocomplete.ajax);
+      $.ajax(this.element.attr('data-autocomplete-path'), options);
+    }
+  }
+
+  /**
+   * Handles an autocompletefocus event.
+   *
+   * @return {bool}
+   */
+  function focusHandler() {
+    return false;
+  }
+
+  /**
+   * Handles an autocompleteselect event.
+   *
+   * @param {jQuery.Event} event
+   * @param {object} ui
+   *
+   * @return {bool}
+   */
+  function selectHandler(event, ui) {
+    var terms = autocomplete.splitValues(event.target.value);
+    // Remove the current input.
+    terms.pop();
+    // Add the selected item.
+    if (ui.item.value.search(",") > 0) {
+      terms.push('"' + ui.item.value + '"');
+    }
+    else {
+      terms.push(ui.item.value);
+    }
+    event.target.value = terms.join(', ');
+    // Return false to tell jQuery UI that we've filled in the value already.
+    return false;
+  }
+
+  /**
+   * Override jQuery UI _renderItem function to output HTML by default.
+   *
+   * @param {object} ul
+   * @param {object} item
+   *
+   * @return {object}
+   */
+  function renderItem(ul, item) {
+    return $("<li>")
+      .append($("<a>").html(item.label))
+      .appendTo(ul);
+  }
+
+  /**
+   * Attaches the autocomplete behavior to all required fields.
+   *
+   * @type {Drupal~behavior}
+   */
+  Drupal.behaviors.autocomplete = {
+    attach: function (context) {
+      // Act on textfields with the "form-autocomplete" class.
+      var $autocomplete = $(context).find('input.form-autocomplete').once('autocomplete');
+      if ($autocomplete.length) {
+        // Allow options to be overriden per instance.
+        var blacklist = $autocomplete.attr('data-autocomplete-first-character-blacklist');
+        $.extend(autocomplete.options, {
+          firstCharacterBlacklist: (blacklist) ? blacklist : ''
+        });
+        // Use jQuery UI Autocomplete on the textfield.
+        $autocomplete.autocomplete(autocomplete.options)
+          .data("ui-autocomplete")
+          ._renderItem = autocomplete.options.renderItem;
+      }
+    },
+    detach: function (context, settings, trigger) {
+      if (trigger === 'unload') {
+        $(context).find('input.form-autocomplete')
+          .removeOnce('autocomplete')
+          .autocomplete('destroy');
+      }
+    }
+  };
+
+  /**
+   * Autocomplete object implementation.
+   *
+   * @namespace Drupal.autocomplete
+   */
+  autocomplete = {
+    cache: {},
+    // Exposes options to allow overriding by contrib.
+    splitValues: autocompleteSplitValues,
+    extractLastTerm: extractLastTerm,
+    // jQuery UI autocomplete options.
+
+    /**
+     * JQuery UI option object.
+     *
+     * @name Drupal.autocomplete.options
+     */
+    options: {
+      source: sourceData,
+      focus: focusHandler,
+      search: searchHandler,
+      select: selectHandler,
+      renderItem: renderItem,
+      minLength: 1,
+      // Custom options, used by Drupal.autocomplete.
+      firstCharacterBlacklist: ''
+    },
+    ajax: {
+      dataType: 'json'
+    }
+  };
+
+  Drupal.autocomplete = autocomplete;
+
+})(jQuery, Drupal);
diff --git a/core/themes/stable/js/core/batch.js b/core/themes/stable/js/core/batch.js
new file mode 100644
index 0000000..43242ea
--- /dev/null
+++ b/core/themes/stable/js/core/batch.js
@@ -0,0 +1,46 @@
+/**
+ * @file
+ * Drupal's batch API.
+ */
+
+(function ($, Drupal) {
+
+  "use strict";
+
+  /**
+   * Attaches the batch behavior to progress bars.
+   *
+   * @type {Drupal~behavior}
+   */
+  Drupal.behaviors.batch = {
+    attach: function (context, settings) {
+      var batch = settings.batch;
+      var $progress = $('[data-drupal-progress]').once('batch');
+      var progressBar;
+
+      // Success: redirect to the summary.
+      function updateCallback(progress, status, pb) {
+        if (progress === '100') {
+          pb.stopMonitoring();
+          window.location = batch.uri + '&op=finished';
+        }
+      }
+
+      function errorCallback(pb) {
+        $progress.prepend($('<p class="error"></p>').html(batch.errorMessage));
+        $('#wait').hide();
+      }
+
+      if ($progress.length) {
+        progressBar = new Drupal.ProgressBar('updateprogress', updateCallback, 'POST', errorCallback);
+        progressBar.setProgress(-1, batch.initMessage);
+        progressBar.startMonitoring(batch.uri + '&op=do', 10);
+        // Remove HTML from no-js progress bar.
+        $progress.empty();
+        // Append the JS progressbar element.
+        $progress.append(progressBar.element);
+      }
+    }
+  };
+
+})(jQuery, Drupal);
diff --git a/core/themes/stable/js/core/collapse.js b/core/themes/stable/js/core/collapse.js
new file mode 100644
index 0000000..1ad89f7
--- /dev/null
+++ b/core/themes/stable/js/core/collapse.js
@@ -0,0 +1,141 @@
+/**
+ * @file
+ * Polyfill for HTML5 details elements.
+ */
+
+(function ($, Modernizr, Drupal) {
+
+  "use strict";
+
+  /**
+   * The collapsible details object represents a single details element.
+   *
+   * @constructor Drupal.CollapsibleDetails
+   *
+   * @param {HTMLElement} node
+   */
+  function CollapsibleDetails(node) {
+    this.$node = $(node);
+    this.$node.data('details', this);
+    // Expand details if there are errors inside, or if it contains an
+    // element that is targeted by the URI fragment identifier.
+    var anchor = location.hash && location.hash !== '#' ? ', ' + location.hash : '';
+    if (this.$node.find('.error' + anchor).length) {
+      this.$node.attr('open', true);
+    }
+    // Initialize and setup the summary,
+    this.setupSummary();
+    // Initialize and setup the legend.
+    this.setupLegend();
+  }
+
+  $.extend(CollapsibleDetails, /** @lends Drupal.CollapsibleDetails */{
+
+    /**
+     * Holds references to instantiated CollapsibleDetails objects.
+     *
+     * @type {Array.<Drupal.CollapsibleDetails>}
+     */
+    instances: []
+  });
+
+  $.extend(CollapsibleDetails.prototype, /** @lends Drupal.CollapsibleDetails# */{
+
+    /**
+     * Initialize and setup summary events and markup.
+     *
+     * @fires event:summaryUpdated
+     *
+     * @listens event:summaryUpdated
+     */
+    setupSummary: function () {
+      this.$summary = $('<span class="summary"></span>');
+      this.$node
+        .on('summaryUpdated', $.proxy(this.onSummaryUpdated, this))
+        .trigger('summaryUpdated');
+    },
+
+    /**
+     * Initialize and setup legend markup.
+     */
+    setupLegend: function () {
+      // Turn the summary into a clickable link.
+      var $legend = this.$node.find('> summary');
+
+      $('<span class="details-summary-prefix visually-hidden"></span>')
+        .append(this.$node.attr('open') ? Drupal.t('Hide') : Drupal.t('Show'))
+        .prependTo($legend)
+        .after(document.createTextNode(' '));
+
+      // .wrapInner() does not retain bound events.
+      $('<a class="details-title"></a>')
+        .attr('href', '#' + this.$node.attr('id'))
+        .prepend($legend.contents())
+        .appendTo($legend);
+
+      $legend
+        .append(this.$summary)
+        .on('click', $.proxy(this.onLegendClick, this));
+    },
+
+    /**
+     * Handle legend clicks.
+     *
+     * @param {jQuery.Event} e
+     */
+    onLegendClick: function (e) {
+      this.toggle();
+      e.preventDefault();
+    },
+
+    /**
+     * Update summary.
+     */
+    onSummaryUpdated: function () {
+      var text = $.trim(this.$node.drupalGetSummary());
+      this.$summary.html(text ? ' (' + text + ')' : '');
+    },
+
+    /**
+     * Toggle the visibility of a details element using smooth animations.
+     */
+    toggle: function () {
+      var isOpen = !!this.$node.attr('open');
+      var $summaryPrefix = this.$node.find('> summary span.details-summary-prefix');
+      if (isOpen) {
+        $summaryPrefix.html(Drupal.t('Show'));
+      }
+      else {
+        $summaryPrefix.html(Drupal.t('Hide'));
+      }
+      // Delay setting the attribute to emulate chrome behavior and make
+      // details-aria.js work as expected with this polyfill.
+      setTimeout(function () {
+        this.$node.attr('open', !isOpen);
+      }.bind(this), 0);
+    }
+  });
+
+  /**
+   * Polyfill HTML5 details element.
+   *
+   * @type {Drupal~behavior}
+   */
+  Drupal.behaviors.collapse = {
+    attach: function (context) {
+      if (Modernizr.details) {
+        return;
+      }
+      var $collapsibleDetails = $(context).find('details').once('collapse').addClass('collapse-processed');
+      if ($collapsibleDetails.length) {
+        for (var i = 0; i < $collapsibleDetails.length; i++) {
+          CollapsibleDetails.instances.push(new CollapsibleDetails($collapsibleDetails[i]));
+        }
+      }
+    }
+  };
+
+  // Expose constructor in the public space.
+  Drupal.CollapsibleDetails = CollapsibleDetails;
+
+})(jQuery, Modernizr, Drupal);
diff --git a/core/themes/stable/js/core/date.js b/core/themes/stable/js/core/date.js
new file mode 100644
index 0000000..fe2a239
--- /dev/null
+++ b/core/themes/stable/js/core/date.js
@@ -0,0 +1,56 @@
+/**
+ * @file
+ * Polyfill for HTML5 date input.
+ */
+
+(function ($, Modernizr, Drupal) {
+
+  "use strict";
+
+  /**
+   * Attach datepicker fallback on date elements.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches the behavior. Accepts in `settings.date` an object listing
+   *   elements to process, keyed by the HTML ID of the form element containing
+   *   the human-readable value. Each element is an datepicker settings object.
+   * @prop {Drupal~behaviorDetach} detach
+   *   Detach the behavior destroying datepickers on effected elements.
+   */
+  Drupal.behaviors.date = {
+    attach: function (context, settings) {
+      var $context = $(context);
+      // Skip if date are supported by the browser.
+      if (Modernizr.inputtypes.date === true) {
+        return;
+      }
+      $context.find('input[data-drupal-date-format]').once('datePicker').each(function () {
+        var $input = $(this);
+        var datepickerSettings = {};
+        var dateFormat = $input.data('drupalDateFormat');
+        // The date format is saved in PHP style, we need to convert to jQuery
+        // datepicker.
+        datepickerSettings.dateFormat = dateFormat
+          .replace('Y', 'yy')
+          .replace('m', 'mm')
+          .replace('d', 'dd');
+        // Add min and max date if set on the input.
+        if ($input.attr('min')) {
+          datepickerSettings.minDate = $input.attr('min');
+        }
+        if ($input.attr('max')) {
+          datepickerSettings.maxDate = $input.attr('max');
+        }
+        $input.datepicker(datepickerSettings);
+      });
+    },
+    detach: function (context, settings, trigger) {
+      if (trigger === 'unload') {
+        $(context).find('input[data-drupal-date-format]').findOnce('datePicker').datepicker('destroy');
+      }
+    }
+  };
+
+})(jQuery, Modernizr, Drupal);
diff --git a/core/themes/stable/js/core/debounce.js b/core/themes/stable/js/core/debounce.js
new file mode 100644
index 0000000..0239ce6
--- /dev/null
+++ b/core/themes/stable/js/core/debounce.js
@@ -0,0 +1,52 @@
+/**
+ * @file
+ * Adapted from underscore.js with the addition Drupal namespace.
+ */
+
+/**
+ * Limits the invocations of a function in a given time frame.
+ *
+ * The debounce function wrapper should be used sparingly. One clear use case
+ * is limiting the invocation of a callback attached to the window resize event.
+ *
+ * Before using the debounce function wrapper, consider first whether the
+ * callback could be attached to an event that fires less frequently or if the
+ * function can be written in such a way that it is only invoked under specific
+ * conditions.
+ *
+ * @param {function} func
+ *   The function to be invoked.
+ * @param {number} wait
+ *   The time period within which the callback function should only be
+ *   invoked once. For example if the wait period is 250ms, then the callback
+ *   will only be called at most 4 times per second.
+ * @param {bool} immediate
+ *   Whether we wait at the beginning or end to execute the function.
+ *
+ * @return {function}
+ *   The debounced function.
+ */
+Drupal.debounce = function (func, wait, immediate) {
+
+  "use strict";
+
+  var timeout;
+  var result;
+  return function () {
+    var context = this;
+    var args = arguments;
+    var later = function () {
+      timeout = null;
+      if (!immediate) {
+        result = func.apply(context, args);
+      }
+    };
+    var callNow = immediate && !timeout;
+    clearTimeout(timeout);
+    timeout = setTimeout(later, wait);
+    if (callNow) {
+      result = func.apply(context, args);
+    }
+    return result;
+  };
+};
diff --git a/core/themes/stable/js/core/details-aria.js b/core/themes/stable/js/core/details-aria.js
new file mode 100644
index 0000000..4de6bdf
--- /dev/null
+++ b/core/themes/stable/js/core/details-aria.js
@@ -0,0 +1,29 @@
+/**
+ * @file
+ * Add aria attribute handling for details and summary elements.
+ */
+
+(function ($, Drupal) {
+
+  "use strict";
+
+  /**
+   * Handles `aria-expanded` and `aria-pressed` attributes on details elements.
+   *
+   * @type {Drupal~behavior}
+   */
+  Drupal.behaviors.detailsAria = {
+    attach: function () {
+      $('body').once('detailsAria').on('click.detailsAria', 'summary', function (event) {
+        var $summary = $(event.currentTarget);
+        var open = $(event.currentTarget.parentNode).attr('open') === 'open' ? 'false' : 'true';
+
+        $summary.attr({
+          'aria-expanded': open,
+          'aria-pressed': open
+        });
+      });
+    }
+  };
+
+})(jQuery, Drupal);
diff --git a/core/themes/stable/js/core/dialog/dialog.ajax.js b/core/themes/stable/js/core/dialog/dialog.ajax.js
new file mode 100644
index 0000000..7f56672
--- /dev/null
+++ b/core/themes/stable/js/core/dialog/dialog.ajax.js
@@ -0,0 +1,213 @@
+/**
+ * @file
+ * Extends the Drupal AJAX functionality to integrate the dialog API.
+ */
+
+(function ($, Drupal) {
+
+  "use strict";
+
+  /**
+   * Initialize dialogs for Ajax purposes.
+   *
+   * @type {Drupal~behavior}
+   */
+  Drupal.behaviors.dialog = {
+    attach: function (context, settings) {
+      var $context = $(context);
+
+      // Provide a known 'drupal-modal' DOM element for Drupal-based modal
+      // dialogs. Non-modal dialogs are responsible for creating their own
+      // elements, since there can be multiple non-modal dialogs at a time.
+      if (!$('#drupal-modal').length) {
+        // Add 'ui-front' jQuery UI class so jQuery UI widgets like autocomplete
+        // sit on top of dialogs. For more information see
+        // http://api.jqueryui.com/theming/stacking-elements/.
+        $('<div id="drupal-modal" class="ui-front"/>').hide().appendTo('body');
+      }
+
+      // Special behaviors specific when attaching content within a dialog.
+      // These behaviors usually fire after a validation error inside a dialog.
+      var $dialog = $context.closest('.ui-dialog-content');
+      if ($dialog.length) {
+        // Remove and replace the dialog buttons with those from the new form.
+        if ($dialog.dialog('option', 'drupalAutoButtons')) {
+          // Trigger an event to detect/sync changes to buttons.
+          $dialog.trigger('dialogButtonsChange');
+        }
+
+        // Force focus on the modal when the behavior is run.
+        $dialog.dialog('widget').trigger('focus');
+      }
+
+      var originalClose = settings.dialog.close;
+      // Overwrite the close method to remove the dialog on closing.
+      settings.dialog.close = function (event) {
+        originalClose.apply(settings.dialog, arguments);
+        $(event.target).remove();
+      };
+    },
+
+    /**
+     * Scan a dialog for any primary buttons and move them to the button area.
+     *
+     * @param {jQuery} $dialog
+     *   An jQuery object containing the element that is the dialog target.
+     *
+     * @return {Array}
+     *   An array of buttons that need to be added to the button area.
+     */
+    prepareDialogButtons: function ($dialog) {
+      var buttons = [];
+      var $buttons = $dialog.find('.form-actions input[type=submit]');
+      $buttons.each(function () {
+        // Hidden form buttons need special attention. For browser consistency,
+        // the button needs to be "visible" in order to have the enter key fire
+        // the form submit event. So instead of a simple "hide" or
+        // "display: none", we set its dimensions to zero.
+        // See http://mattsnider.com/how-forms-submit-when-pressing-enter/
+        var $originalButton = $(this).css({
+          display: 'block',
+          width: 0,
+          height: 0,
+          padding: 0,
+          border: 0
+        });
+        buttons.push({
+          text: $originalButton.html() || $originalButton.attr('value'),
+          class: $originalButton.attr('class'),
+          click: function (e) {
+            $originalButton.trigger('mousedown').trigger('mouseup').trigger('click');
+            e.preventDefault();
+          }
+        });
+      });
+      return buttons;
+    }
+  };
+
+  /**
+   * Command to open a dialog.
+   *
+   * @param {Drupal.Ajax} ajax
+   * @param {object} response
+   * @param {number} [status]
+   *
+   * @return {bool|undefined}
+   */
+  Drupal.AjaxCommands.prototype.openDialog = function (ajax, response, status) {
+    if (!response.selector) {
+      return false;
+    }
+    var $dialog = $(response.selector);
+    if (!$dialog.length) {
+      // Create the element if needed.
+      $dialog = $('<div id="' + response.selector.replace(/^#/, '') + '" class="ui-front"/>').appendTo('body');
+    }
+    // Set up the wrapper, if there isn't one.
+    if (!ajax.wrapper) {
+      ajax.wrapper = $dialog.attr('id');
+    }
+
+    // Use the ajax.js insert command to populate the dialog contents.
+    response.command = 'insert';
+    response.method = 'html';
+    ajax.commands.insert(ajax, response, status);
+
+    // Move the buttons to the jQuery UI dialog buttons area.
+    if (!response.dialogOptions.buttons) {
+      response.dialogOptions.drupalAutoButtons = true;
+      response.dialogOptions.buttons = Drupal.behaviors.dialog.prepareDialogButtons($dialog);
+    }
+
+    // Bind dialogButtonsChange.
+    $dialog.on('dialogButtonsChange', function () {
+      var buttons = Drupal.behaviors.dialog.prepareDialogButtons($dialog);
+      $dialog.dialog('option', 'buttons', buttons);
+    });
+
+    // Open the dialog itself.
+    response.dialogOptions = response.dialogOptions || {};
+    var dialog = Drupal.dialog($dialog.get(0), response.dialogOptions);
+    if (response.dialogOptions.modal) {
+      dialog.showModal();
+    }
+    else {
+      dialog.show();
+    }
+
+    // Add the standard Drupal class for buttons for style consistency.
+    $dialog.parent().find('.ui-dialog-buttonset').addClass('form-actions');
+  };
+
+  /**
+   * Command to close a dialog.
+   *
+   * If no selector is given, it defaults to trying to close the modal.
+   *
+   * @param {Drupal.Ajax} [ajax]
+   * @param {object} response
+   * @param {string} response.selector
+   * @param {bool} response.persist
+   * @param {number} [status]
+   */
+  Drupal.AjaxCommands.prototype.closeDialog = function (ajax, response, status) {
+    var $dialog = $(response.selector);
+    if ($dialog.length) {
+      Drupal.dialog($dialog.get(0)).close();
+      if (!response.persist) {
+        $dialog.remove();
+      }
+    }
+
+    // Unbind dialogButtonsChange.
+    $dialog.off('dialogButtonsChange');
+  };
+
+  /**
+   * Command to set a dialog property.
+   *
+   * JQuery UI specific way of setting dialog options.
+   *
+   * @param {Drupal.Ajax} [ajax]
+   * @param {object} response
+   * @param {string} response.selector
+   * @param {string} response.optionsName
+   * @param {string} response.optionValue
+   * @param {number} [status]
+   */
+  Drupal.AjaxCommands.prototype.setDialogOption = function (ajax, response, status) {
+    var $dialog = $(response.selector);
+    if ($dialog.length) {
+      $dialog.dialog('option', response.optionName, response.optionValue);
+    }
+  };
+
+  /**
+   * Binds a listener on dialog creation to handle the cancel link.
+   *
+   * @param {jQuery.Event} e
+   * @param {Drupal.dialog~dialogDefinition} dialog
+   * @param {jQuery} $element
+   * @param {object} settings
+   */
+  $(window).on('dialog:aftercreate', function (e, dialog, $element, settings) {
+    $element.on('click.dialog', '.dialog-cancel', function (e) {
+      dialog.close('cancel');
+      e.preventDefault();
+      e.stopPropagation();
+    });
+  });
+
+  /**
+   * Removes all 'dialog' listeners.
+   *
+   * @param {jQuery.Event} e
+   * @param {Drupal.dialog~dialogDefinition} dialog
+   * @param {jQuery} $element
+   */
+  $(window).on('dialog:beforeclose', function (e, dialog, $element) {
+    $element.off('.dialog');
+  });
+
+})(jQuery, Drupal);
diff --git a/core/themes/stable/js/core/dialog/dialog.jquery-ui.js b/core/themes/stable/js/core/dialog/dialog.jquery-ui.js
new file mode 100644
index 0000000..27fff75
--- /dev/null
+++ b/core/themes/stable/js/core/dialog/dialog.jquery-ui.js
@@ -0,0 +1,36 @@
+/**
+ * @file
+ * Adds default classes to buttons for styling purposes.
+ */
+
+(function ($) {
+
+  "use strict";
+
+  $.widget('ui.dialog', $.ui.dialog, {
+    options: {
+      buttonClass: 'button',
+      buttonPrimaryClass: 'button--primary'
+    },
+    _createButtons: function () {
+      var opts = this.options;
+      var primaryIndex;
+      var $buttons;
+      var index;
+      var il = opts.buttons.length;
+      for (index = 0; index < il; index++) {
+        if (opts.buttons[index].primary && opts.buttons[index].primary === true) {
+          primaryIndex = index;
+          delete opts.buttons[index].primary;
+          break;
+        }
+      }
+      this._super();
+      $buttons = this.uiButtonSet.children().addClass(opts.buttonClass);
+      if (typeof primaryIndex !== 'undefined') {
+        $buttons.eq(index).addClass(opts.buttonPrimaryClass);
+      }
+    }
+  });
+
+})(jQuery);
diff --git a/core/themes/stable/js/core/dialog/dialog.js b/core/themes/stable/js/core/dialog/dialog.js
new file mode 100644
index 0000000..cb7fb92
--- /dev/null
+++ b/core/themes/stable/js/core/dialog/dialog.js
@@ -0,0 +1,98 @@
+/**
+ * @file
+ * Dialog API inspired by HTML5 dialog element.
+ *
+ * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#the-dialog-element
+ */
+
+(function ($, Drupal, drupalSettings) {
+
+  "use strict";
+
+  /**
+   * Default dialog options.
+   *
+   * @type {object}
+   *
+   * @prop {bool} [autoOpen=true]
+   * @prop {string} [dialogClass='']
+   * @prop {string} [buttonClass='button']
+   * @prop {string} [buttonPrimaryClass='button--primary']
+   * @prop {function} close
+   */
+  drupalSettings.dialog = {
+    autoOpen: true,
+    dialogClass: '',
+    // Drupal-specific extensions: see dialog.jquery-ui.js.
+    buttonClass: 'button',
+    buttonPrimaryClass: 'button--primary',
+    // When using this API directly (when generating dialogs on the client
+    // side), you may want to override this method and do
+    // `jQuery(event.target).remove()` as well, to remove the dialog on
+    // closing.
+    close: function (event) {
+      Drupal.detachBehaviors(event.target, null, 'unload');
+    }
+  };
+
+  /**
+   * @typedef {object} Drupal.dialog~dialogDefinition
+   *
+   * @prop {boolean} open
+   *   Is the dialog open or not.
+   * @prop {*} returnValue
+   *   Return value of the dialog.
+   * @prop {function} show
+   *   Method to display the dialog on the page.
+   * @prop {function} showModal
+   *   Method to display the dialog as a modal on the page.
+   * @prop {function} close
+   *   Method to hide the dialog from the page.
+   */
+
+  /**
+   * Polyfill HTML5 dialog element with jQueryUI.
+   *
+   * @param {HTMLElement} element
+   * @param {object} options
+   *   jQuery UI options to be passed to the dialog.
+   *
+   * @return {Drupal.dialog~dialogDefinition}
+   */
+  Drupal.dialog = function (element, options) {
+
+    function openDialog(settings) {
+      settings = $.extend({}, drupalSettings.dialog, options, settings);
+      // Trigger a global event to allow scripts to bind events to the dialog.
+      $(window).trigger('dialog:beforecreate', [dialog, $element, settings]);
+      $element.dialog(settings);
+      dialog.open = true;
+      $(window).trigger('dialog:aftercreate', [dialog, $element, settings]);
+    }
+
+    function closeDialog(value) {
+      $(window).trigger('dialog:beforeclose', [dialog, $element]);
+      $element.dialog('close');
+      dialog.returnValue = value;
+      dialog.open = false;
+      $(window).trigger('dialog:afterclose', [dialog, $element]);
+    }
+
+    var undef;
+    var $element = $(element);
+    var dialog = {
+      open: false,
+      returnValue: undef,
+      show: function () {
+        openDialog({modal: false});
+      },
+      showModal: function () {
+        openDialog({modal: true});
+      },
+      close: closeDialog
+    };
+
+    return dialog;
+  };
+
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/themes/stable/js/core/dialog/dialog.position.js b/core/themes/stable/js/core/dialog/dialog.position.js
new file mode 100644
index 0000000..a356477
--- /dev/null
+++ b/core/themes/stable/js/core/dialog/dialog.position.js
@@ -0,0 +1,106 @@
+/**
+ * @file
+ * Positioning extensions for dialogs.
+ */
+
+/**
+ * Triggers when content inside a dialog changes.
+ *
+ * @event dialogContentResize
+ */
+
+(function ($, Drupal, drupalSettings, debounce, displace) {
+
+  "use strict";
+
+  // autoResize option will turn off resizable and draggable.
+  drupalSettings.dialog = $.extend({autoResize: true, maxHeight: '95%'}, drupalSettings.dialog);
+
+  /**
+   * Resets the current options for positioning.
+   *
+   * This is used as a window resize and scroll callback to reposition the
+   * jQuery UI dialog. Although not a built-in jQuery UI option, this can
+   * be disabled by setting autoResize: false in the options array when creating
+   * a new {@link Drupal.dialog}.
+   *
+   * @function Drupal.dialog~resetSize
+   *
+   * @param {jQuery.Event} event
+   *
+   * @fires event:dialogContentResize
+   */
+  function resetSize(event) {
+    var positionOptions = ['width', 'height', 'minWidth', 'minHeight', 'maxHeight', 'maxWidth', 'position'];
+    var adjustedOptions = {};
+    var windowHeight = $(window).height();
+    var option;
+    var optionValue;
+    var adjustedValue;
+    for (var n = 0; n < positionOptions.length; n++) {
+      option = positionOptions[n];
+      optionValue = event.data.settings[option];
+      if (optionValue) {
+        // jQuery UI does not support percentages on heights, convert to pixels.
+        if (typeof optionValue === 'string' && /%$/.test(optionValue) && /height/i.test(option)) {
+          // Take offsets in account.
+          windowHeight -= displace.offsets.top + displace.offsets.bottom;
+          adjustedValue = parseInt(0.01 * parseInt(optionValue, 10) * windowHeight, 10);
+          // Don't force the dialog to be bigger vertically than needed.
+          if (option === 'height' && event.data.$element.parent().outerHeight() < adjustedValue) {
+            adjustedValue = 'auto';
+          }
+          adjustedOptions[option] = adjustedValue;
+        }
+      }
+    }
+    // Offset the dialog center to be at the center of Drupal.displace.offsets.
+    adjustedOptions = resetPosition(adjustedOptions);
+    event.data.$element
+      .dialog('option', adjustedOptions)
+      .trigger('dialogContentResize');
+  }
+
+  /**
+   * Position the dialog's center at the center of displace.offsets boundaries.
+   *
+   * @function Drupal.dialog~resetPosition
+   *
+   * @param {object} options
+   *
+   * @return {object}
+   */
+  function resetPosition(options) {
+    var offsets = displace.offsets;
+    var left = offsets.left - offsets.right;
+    var top = offsets.top - offsets.bottom;
+
+    var leftString = (left > 0 ? '+' : '-') + Math.abs(Math.round(left / 2)) + 'px';
+    var topString = (top > 0 ? '+' : '-') + Math.abs(Math.round(top / 2)) + 'px';
+    options.position = {
+      my: 'center' + (left !== 0 ? leftString : '') + ' center' + (top !== 0 ? topString : ''),
+      of: window
+    };
+    return options;
+  }
+
+  $(window).on({
+    'dialog:aftercreate': function (event, dialog, $element, settings) {
+      var autoResize = debounce(resetSize, 20);
+      var eventData = {settings: settings, $element: $element};
+      if (settings.autoResize === true || settings.autoResize === 'true') {
+        $element
+          .dialog('option', {resizable: false, draggable: false})
+          .dialog('widget').css('position', 'fixed');
+        $(window)
+          .on('resize.dialogResize scroll.dialogResize', eventData, autoResize)
+          .trigger('resize.dialogResize');
+        $(document).on('drupalViewportOffsetChange', eventData, autoResize);
+      }
+    },
+    'dialog:beforeclose': function (event, dialog, $element) {
+      $(window).off('.dialogResize');
+    }
+  });
+
+})(jQuery, Drupal, drupalSettings, Drupal.debounce, Drupal.displace);
diff --git a/core/themes/stable/js/core/displace.js b/core/themes/stable/js/core/displace.js
new file mode 100644
index 0000000..5e21ca1
--- /dev/null
+++ b/core/themes/stable/js/core/displace.js
@@ -0,0 +1,222 @@
+/**
+ * @file
+ * Manages elements that can offset the size of the viewport.
+ *
+ * Measures and reports viewport offset dimensions from elements like the
+ * toolbar that can potentially displace the positioning of other elements.
+ */
+
+/**
+ * @typedef {object} Drupal~displaceOffset
+ *
+ * @prop {number} top
+ * @prop {number} left
+ * @prop {number} right
+ * @prop {number} bottom
+ */
+
+/**
+ * Triggers when layout of the page changes.
+ *
+ * This is used to position fixed element on the page during page resize and
+ * Toolbar toggling.
+ *
+ * @event drupalViewportOffsetChange
+ */
+
+(function ($, Drupal, debounce) {
+
+  "use strict";
+
+  /**
+   * @name Drupal.displace.offsets
+   *
+   * @type {Drupal~displaceOffset}
+   */
+  var offsets = {
+    top: 0,
+    right: 0,
+    bottom: 0,
+    left: 0
+  };
+
+  /**
+   * Registers a resize handler on the window.
+   *
+   * @type {Drupal~behavior}
+   */
+  Drupal.behaviors.drupalDisplace = {
+    attach: function () {
+      // Mark this behavior as processed on the first pass.
+      if (this.displaceProcessed) {
+        return;
+      }
+      this.displaceProcessed = true;
+
+      $(window).on('resize.drupalDisplace', debounce(displace, 200));
+    }
+  };
+
+  /**
+   * Informs listeners of the current offset dimensions.
+   *
+   * @function Drupal.displace
+   *
+   * @prop {Drupal~displaceOffset} offsets
+   *
+   * @param {bool} [broadcast]
+   *   When true or undefined, causes the recalculated offsets values to be
+   *   broadcast to listeners.
+   *
+   * @return {Drupal~displaceOffset}
+   *   An object whose keys are the for sides an element -- top, right, bottom
+   *   and left. The value of each key is the viewport displacement distance for
+   *   that edge.
+   *
+   * @fires event:drupalViewportOffsetChange
+   */
+  function displace(broadcast) {
+    offsets = Drupal.displace.offsets = calculateOffsets();
+    if (typeof broadcast === 'undefined' || broadcast) {
+      $(document).trigger('drupalViewportOffsetChange', offsets);
+    }
+    return offsets;
+  }
+
+  /**
+   * Determines the viewport offsets.
+   *
+   * @return {Drupal~displaceOffset}
+   *   An object whose keys are the for sides an element -- top, right, bottom
+   *   and left. The value of each key is the viewport displacement distance for
+   *   that edge.
+   */
+  function calculateOffsets() {
+    return {
+      top: calculateOffset('top'),
+      right: calculateOffset('right'),
+      bottom: calculateOffset('bottom'),
+      left: calculateOffset('left')
+    };
+  }
+
+  /**
+   * Gets a specific edge's offset.
+   *
+   * Any element with the attribute data-offset-{edge} e.g. data-offset-top will
+   * be considered in the viewport offset calculations. If the attribute has a
+   * numeric value, that value will be used. If no value is provided, one will
+   * be calculated using the element's dimensions and placement.
+   *
+   * @function Drupal.displace.calculateOffset
+   *
+   * @param {string} edge
+   *   The name of the edge to calculate. Can be 'top', 'right',
+   *   'bottom' or 'left'.
+   *
+   * @return {number}
+   *   The viewport displacement distance for the requested edge.
+   */
+  function calculateOffset(edge) {
+    var edgeOffset = 0;
+    var displacingElements = document.querySelectorAll('[data-offset-' + edge + ']');
+    var n = displacingElements.length;
+    for (var i = 0; i < n; i++) {
+      var el = displacingElements[i];
+      // If the element is not visible, do consider its dimensions.
+      if (el.style.display === 'none') {
+        continue;
+      }
+      // If the offset data attribute contains a displacing value, use it.
+      var displacement = parseInt(el.getAttribute('data-offset-' + edge), 10);
+      // If the element's offset data attribute exits
+      // but is not a valid number then get the displacement
+      // dimensions directly from the element.
+      if (isNaN(displacement)) {
+        displacement = getRawOffset(el, edge);
+      }
+      // If the displacement value is larger than the current value for this
+      // edge, use the displacement value.
+      edgeOffset = Math.max(edgeOffset, displacement);
+    }
+
+    return edgeOffset;
+  }
+
+  /**
+   * Calculates displacement for element based on its dimensions and placement.
+   *
+   * @param {HTMLElement} el
+   *   The jQuery element whose dimensions and placement will be measured.
+   *
+   * @param {string} edge
+   *   The name of the edge of the viewport that the element is associated
+   *   with.
+   *
+   * @return {number}
+   *   The viewport displacement distance for the requested edge.
+   */
+  function getRawOffset(el, edge) {
+    var $el = $(el);
+    var documentElement = document.documentElement;
+    var displacement = 0;
+    var horizontal = (edge === 'left' || edge === 'right');
+    // Get the offset of the element itself.
+    var placement = $el.offset()[horizontal ? 'left' : 'top'];
+    // Subtract scroll distance from placement to get the distance
+    // to the edge of the viewport.
+    placement -= window['scroll' + (horizontal ? 'X' : 'Y')] || document.documentElement['scroll' + (horizontal) ? 'Left' : 'Top'] || 0;
+    // Find the displacement value according to the edge.
+    switch (edge) {
+      // Left and top elements displace as a sum of their own offset value
+      // plus their size.
+      case 'top':
+        // Total displacement is the sum of the elements placement and size.
+        displacement = placement + $el.outerHeight();
+        break;
+
+      case 'left':
+        // Total displacement is the sum of the elements placement and size.
+        displacement = placement + $el.outerWidth();
+        break;
+
+      // Right and bottom elements displace according to their left and
+      // top offset. Their size isn't important.
+      case 'bottom':
+        displacement = documentElement.clientHeight - placement;
+        break;
+
+      case 'right':
+        displacement = documentElement.clientWidth - placement;
+        break;
+
+      default:
+        displacement = 0;
+    }
+    return displacement;
+  }
+
+  /**
+   * Assign the displace function to a property of the Drupal global object.
+   *
+   * @ignore
+   */
+  Drupal.displace = displace;
+  $.extend(Drupal.displace, {
+
+    /**
+     * Expose offsets to other scripts to avoid having to recalculate offsets.
+     *
+     * @ignore
+     */
+    offsets: offsets,
+
+    /**
+     * Expose method to compute a single edge offsets.
+     *
+     * @ignore
+     */
+    calculateOffset: calculateOffset
+  });
+
+})(jQuery, Drupal, Drupal.debounce);
diff --git a/core/themes/stable/js/core/dropbutton/dropbutton.js b/core/themes/stable/js/core/dropbutton/dropbutton.js
new file mode 100644
index 0000000..4f7645c
--- /dev/null
+++ b/core/themes/stable/js/core/dropbutton/dropbutton.js
@@ -0,0 +1,226 @@
+/**
+ * @file
+ * Dropbutton feature.
+ */
+
+(function ($, Drupal) {
+
+  "use strict";
+
+  /**
+   * Process elements with the .dropbutton class on page load.
+   *
+   * @type {Drupal~behavior}
+   */
+  Drupal.behaviors.dropButton = {
+    attach: function (context, settings) {
+      var $dropbuttons = $(context).find('.dropbutton-wrapper').once('dropbutton');
+      if ($dropbuttons.length) {
+        // Adds the delegated handler that will toggle dropdowns on click.
+        var $body = $('body').once('dropbutton-click');
+        if ($body.length) {
+          $body.on('click', '.dropbutton-toggle', dropbuttonClickHandler);
+        }
+        // Initialize all buttons.
+        var il = $dropbuttons.length;
+        for (var i = 0; i < il; i++) {
+          DropButton.dropbuttons.push(new DropButton($dropbuttons[i], settings.dropbutton));
+        }
+      }
+    }
+  };
+
+  /**
+   * Delegated callback for opening and closing dropbutton secondary actions.
+   *
+   * @function Drupal.DropButton~dropbuttonClickHandler
+   *
+   * @param {jQuery.Event} e
+   */
+  function dropbuttonClickHandler(e) {
+    e.preventDefault();
+    $(e.target).closest('.dropbutton-wrapper').toggleClass('open');
+  }
+
+  /**
+   * A DropButton presents an HTML list as a button with a primary action.
+   *
+   * All secondary actions beyond the first in the list are presented in a
+   * dropdown list accessible through a toggle arrow associated with the button.
+   *
+   * @constructor Drupal.DropButton
+   *
+   * @param {HTMLElement} dropbutton
+   *   A DOM element.
+   * @param {object} settings
+   *   A list of options including:
+   * @param {string} settings.title
+   *   The text inside the toggle link element. This text is hidden
+   *   from visual UAs.
+   */
+  function DropButton(dropbutton, settings) {
+    // Merge defaults with settings.
+    var options = $.extend({title: Drupal.t('List additional actions')}, settings);
+    var $dropbutton = $(dropbutton);
+
+    /**
+     * @type {jQuery}
+     */
+    this.$dropbutton = $dropbutton;
+
+    /**
+     * @type {jQuery}
+     */
+    this.$list = $dropbutton.find('.dropbutton');
+
+    /**
+     * Find actions and mark them.
+     *
+     * @type {jQuery}
+     */
+    this.$actions = this.$list.find('li').addClass('dropbutton-action');
+
+    // Add the special dropdown only if there are hidden actions.
+    if (this.$actions.length > 1) {
+      // Identify the first element of the collection.
+      var $primary = this.$actions.slice(0, 1);
+      // Identify the secondary actions.
+      var $secondary = this.$actions.slice(1);
+      $secondary.addClass('secondary-action');
+      // Add toggle link.
+      $primary.after(Drupal.theme('dropbuttonToggle', options));
+      // Bind mouse events.
+      this.$dropbutton
+        .addClass('dropbutton-multiple')
+        .on({
+
+          /**
+           * Adds a timeout to close the dropdown on mouseleave.
+           *
+           * @ignore
+           */
+          'mouseleave.dropbutton': $.proxy(this.hoverOut, this),
+
+          /**
+           * Clears timeout when mouseout of the dropdown.
+           *
+           * @ignore
+           */
+          'mouseenter.dropbutton': $.proxy(this.hoverIn, this),
+
+          /**
+           * Similar to mouseleave/mouseenter, but for keyboard navigation.
+           *
+           * @ignore
+           */
+          'focusout.dropbutton': $.proxy(this.focusOut, this),
+
+          /**
+           * @ignore
+           */
+          'focusin.dropbutton': $.proxy(this.focusIn, this)
+        });
+    }
+    else {
+      this.$dropbutton.addClass('dropbutton-single');
+    }
+  }
+
+  /**
+   * Extend the DropButton constructor.
+   */
+  $.extend(DropButton, /** @lends Drupal.DropButton */{
+    /**
+     * Store all processed DropButtons.
+     *
+     * @type {Array.<Drupal.DropButton>}
+     */
+    dropbuttons: []
+  });
+
+  /**
+   * Extend the DropButton prototype.
+   */
+  $.extend(DropButton.prototype, /** @lends Drupal.DropButton# */{
+
+    /**
+     * Toggle the dropbutton open and closed.
+     *
+     * @param {bool} [show]
+     *   Force the dropbutton to open by passing true or to close by
+     *   passing false.
+     */
+    toggle: function (show) {
+      var isBool = typeof show === 'boolean';
+      show = isBool ? show : !this.$dropbutton.hasClass('open');
+      this.$dropbutton.toggleClass('open', show);
+    },
+
+    /**
+     * @method
+     */
+    hoverIn: function () {
+      // Clear any previous timer we were using.
+      if (this.timerID) {
+        window.clearTimeout(this.timerID);
+      }
+    },
+
+    /**
+     * @method
+     */
+    hoverOut: function () {
+      // Wait half a second before closing.
+      this.timerID = window.setTimeout($.proxy(this, 'close'), 500);
+    },
+
+    /**
+     * @method
+     */
+    open: function () {
+      this.toggle(true);
+    },
+
+    /**
+     * @method
+     */
+    close: function () {
+      this.toggle(false);
+    },
+
+    /**
+     * @param {jQuery.Event} e
+     */
+    focusOut: function (e) {
+      this.hoverOut.call(this, e);
+    },
+
+    /**
+     * @param {jQuery.Event} e
+     */
+    focusIn: function (e) {
+      this.hoverIn.call(this, e);
+    }
+  });
+
+  $.extend(Drupal.theme, /** @lends Drupal.theme */{
+
+    /**
+     * A toggle is an interactive element often bound to a click handler.
+     *
+     * @param {object} options
+     * @param {string} [options.title]
+     *   The HTML anchor title attribute and text for the inner span element.
+     *
+     * @return {string}
+     *   A string representing a DOM fragment.
+     */
+    dropbuttonToggle: function (options) {
+      return '<li class="dropbutton-toggle"><button type="button"><span class="dropbutton-arrow"><span class="visually-hidden">' + options.title + '</span></span></button></li>';
+    }
+  });
+
+  // Expose constructor in the public space.
+  Drupal.DropButton = DropButton;
+
+})(jQuery, Drupal);
diff --git a/core/themes/stable/js/core/drupal.js b/core/themes/stable/js/core/drupal.js
new file mode 100644
index 0000000..befb9d8
--- /dev/null
+++ b/core/themes/stable/js/core/drupal.js
@@ -0,0 +1,610 @@
+/**
+ * @file
+ * Defines the Drupal JavaScript API.
+ */
+
+/**
+ * A jQuery object, typically the return value from a `$(selector)` call.
+ *
+ * Holds an HTMLElement or a collection of HTMLElements.
+ *
+ * @typedef {object} jQuery
+ *
+ * @prop {number} length=0
+ *   Number of elements contained in the jQuery object.
+ */
+
+/**
+ * Variable generated by Drupal that holds all translated strings from PHP.
+ *
+ * Content of this variable is automatically created by Drupal when using the
+ * Interface Translation module. It holds the translation of strings used on
+ * the page.
+ *
+ * This variable is used to pass data from the backend to the frontend. Data
+ * contained in `drupalSettings` is used during behavior initialization.
+ *
+ * @global
+ *
+ * @var {object} drupalTranslations
+ */
+
+/**
+ * Global Drupal object.
+ *
+ * All Drupal JavaScript APIs are contained in this namespace.
+ *
+ * @global
+ *
+ * @namespace
+ */
+window.Drupal = {behaviors: {}, locale: {}};
+
+// Class indicating that JavaScript is enabled; used for styling purpose.
+document.documentElement.className += ' js';
+
+// Allow other JavaScript libraries to use $.
+if (window.jQuery) {
+  jQuery.noConflict();
+}
+
+// JavaScript should be made compatible with libraries other than jQuery by
+// wrapping it in an anonymous closure.
+(function (domready, Drupal, drupalSettings, drupalTranslations) {
+
+  "use strict";
+
+  /**
+   * Helper to rethrow errors asynchronously.
+   *
+   * This way Errors bubbles up outside of the original callstack, making it
+   * easier to debug errors in the browser.
+   *
+   * @param {Error|string} error
+   *   The error to be thrown.
+   */
+  Drupal.throwError = function (error) {
+    setTimeout(function () { throw error; }, 0);
+  };
+
+  /**
+   * Custom error thrown after attach/detach if one or more behaviors failed.
+   * Initializes the JavaScript behaviors for page loads and Ajax requests.
+   *
+   * @callback Drupal~behaviorAttach
+   *
+   * @param {HTMLDocument|HTMLElement} context
+   *   An element to detach behaviors from.
+   * @param {?object} settings
+   *   An object containing settings for the current context. It is rarely used.
+   *
+   * @see Drupal.attachBehaviors
+   */
+
+  /**
+   * Reverts and cleans up JavaScript behavior initialization.
+   *
+   * @callback Drupal~behaviorDetach
+   *
+   * @param {HTMLDocument|HTMLElement} context
+   *   An element to attach behaviors to.
+   * @param {object} settings
+   *   An object containing settings for the current context.
+   * @param {string} trigger
+   *   One of `'unload'`, `'move'`, or `'serialize'`.
+   *
+   * @see Drupal.detachBehaviors
+   */
+
+  /**
+   * @typedef {object} Drupal~behavior
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Function run on page load and after an Ajax call.
+   * @prop {Drupal~behaviorDetach} detach
+   *   Function run when content is serialized or removed from the page.
+   */
+
+  /**
+   * Holds all initialization methods.
+   *
+   * @namespace Drupal.behaviors
+   *
+   * @type {Object.<string, Drupal~behavior>}
+   */
+
+  /**
+   * Defines a behavior to be run during attach and detach phases.
+   *
+   * Attaches all registered behaviors to a page element.
+   *
+   * Behaviors are event-triggered actions that attach to page elements,
+   * enhancing default non-JavaScript UIs. Behaviors are registered in the
+   * {@link Drupal.behaviors} object using the method 'attach' and optionally
+   * also 'detach'.
+   *
+   * {@link Drupal.attachBehaviors} is added below to the `jQuery.ready` event
+   * and therefore runs on initial page load. Developers implementing Ajax in
+   * their solutions should also call this function after new page content has
+   * been loaded, feeding in an element to be processed, in order to attach all
+   * behaviors to the new content.
+   *
+   * Behaviors should use `var elements =
+   * $(context).find(selector).once('behavior-name');` to ensure the behavior is
+   * attached only once to a given element. (Doing so enables the reprocessing
+   * of given elements, which may be needed on occasion despite the ability to
+   * limit behavior attachment to a particular element.)
+   *
+   * @example
+   * Drupal.behaviors.behaviorName = {
+   *   attach: function (context, settings) {
+   *     // ...
+   *   },
+   *   detach: function (context, settings, trigger) {
+   *     // ...
+   *   }
+   * };
+   *
+   * @param {HTMLDocument|HTMLElement} [context=document]
+   *   An element to attach behaviors to.
+   * @param {object} [settings=drupalSettings]
+   *   An object containing settings for the current context. If none is given,
+   *   the global {@link drupalSettings} object is used.
+   *
+   * @see Drupal~behaviorAttach
+   * @see Drupal.detachBehaviors
+   *
+   * @throws {Drupal~DrupalBehaviorError}
+   */
+  Drupal.attachBehaviors = function (context, settings) {
+    context = context || document;
+    settings = settings || drupalSettings;
+    var behaviors = Drupal.behaviors;
+    // Execute all of them.
+    for (var i in behaviors) {
+      if (behaviors.hasOwnProperty(i) && typeof behaviors[i].attach === 'function') {
+        // Don't stop the execution of behaviors in case of an error.
+        try {
+          behaviors[i].attach(context, settings);
+        }
+        catch (e) {
+          Drupal.throwError(e);
+        }
+      }
+    }
+  };
+
+  // Attach all behaviors.
+  domready(function () { Drupal.attachBehaviors(document, drupalSettings); });
+
+  /**
+   * Detaches registered behaviors from a page element.
+   *
+   * Developers implementing Ajax in their solutions should call this function
+   * before page content is about to be removed, feeding in an element to be
+   * processed, in order to allow special behaviors to detach from the content.
+   *
+   * Such implementations should use `.findOnce()` and `.removeOnce()` to find
+   * elements with their corresponding `Drupal.behaviors.behaviorName.attach`
+   * implementation, i.e. `.removeOnce('behaviorName')`, to ensure the behavior
+   * is detached only from previously processed elements.
+   *
+   * @param {HTMLDocument|HTMLElement} [context=document]
+   *   An element to detach behaviors from.
+   * @param {object} [settings=drupalSettings]
+   *   An object containing settings for the current context. If none given,
+   *   the global {@link drupalSettings} object is used.
+   * @param {string} [trigger='unload']
+   *   A string containing what's causing the behaviors to be detached. The
+   *   possible triggers are:
+   *   - `'unload'`: The context element is being removed from the DOM.
+   *   - `'move'`: The element is about to be moved within the DOM (for example,
+   *     during a tabledrag row swap). After the move is completed,
+   *     {@link Drupal.attachBehaviors} is called, so that the behavior can undo
+   *     whatever it did in response to the move. Many behaviors won't need to
+   *     do anything simply in response to the element being moved, but because
+   *     IFRAME elements reload their "src" when being moved within the DOM,
+   *     behaviors bound to IFRAME elements (like WYSIWYG editors) may need to
+   *     take some action.
+   *   - `'serialize'`: When an Ajax form is submitted, this is called with the
+   *     form as the context. This provides every behavior within the form an
+   *     opportunity to ensure that the field elements have correct content
+   *     in them before the form is serialized. The canonical use-case is so
+   *     that WYSIWYG editors can update the hidden textarea to which they are
+   *     bound.
+   *
+   * @throws {Drupal~DrupalBehaviorError}
+   *
+   * @see Drupal~behaviorDetach
+   * @see Drupal.attachBehaviors
+   */
+  Drupal.detachBehaviors = function (context, settings, trigger) {
+    context = context || document;
+    settings = settings || drupalSettings;
+    trigger = trigger || 'unload';
+    var behaviors = Drupal.behaviors;
+    // Execute all of them.
+    for (var i in behaviors) {
+      if (behaviors.hasOwnProperty(i) && typeof behaviors[i].detach === 'function') {
+        // Don't stop the execution of behaviors in case of an error.
+        try {
+          behaviors[i].detach(context, settings, trigger);
+        }
+        catch (e) {
+          Drupal.throwError(e);
+        }
+      }
+    }
+  };
+
+  /**
+   * Tests the document width for mobile configurations.
+   *
+   * @param {number} [width=640]
+   *   Value of the width to check for.
+   *
+   * @return {bool}
+   *   true if the document's `clientWidth` is bigger than `width`, returns
+   *   false otherwise.
+   *
+   * @deprecated Temporary solution for the mobile initiative.
+   */
+  Drupal.checkWidthBreakpoint = function (width) {
+    width = width || drupalSettings.widthBreakpoint || 640;
+    return (document.documentElement.clientWidth > width);
+  };
+
+  /**
+   * Encodes special characters in a plain-text string for display as HTML.
+   *
+   * @param {string} str
+   *   The string to be encoded.
+   *
+   * @return {string}
+   *   The encoded string.
+   *
+   * @ingroup sanitization
+   */
+  Drupal.checkPlain = function (str) {
+    str = str.toString()
+      .replace(/&/g, '&amp;')
+      .replace(/"/g, '&quot;')
+      .replace(/</g, '&lt;')
+      .replace(/>/g, '&gt;');
+    return str;
+  };
+
+  /**
+   * Replaces placeholders with sanitized values in a string.
+   *
+   * @param {string} str
+   *   A string with placeholders.
+   * @param {object} args
+   *   An object of replacements pairs to make. Incidences of any key in this
+   *   array are replaced with the corresponding value. Based on the first
+   *   character of the key, the value is escaped and/or themed:
+   *    - `'!variable'`: inserted as is.
+   *    - `'@variable'`: escape plain text to HTML ({@link Drupal.checkPlain}).
+   *    - `'%variable'`: escape text and theme as a placeholder for user-
+   *      submitted content ({@link Drupal.checkPlain} +
+   *      `{@link Drupal.theme}('placeholder')`).
+   *
+   * @return {string}
+   *
+   * @see Drupal.t
+   */
+  Drupal.formatString = function (str, args) {
+    // Keep args intact.
+    var processedArgs = {};
+    // Transform arguments before inserting them.
+    for (var key in args) {
+      if (args.hasOwnProperty(key)) {
+        switch (key.charAt(0)) {
+          // Escaped only.
+          case '@':
+            processedArgs[key] = Drupal.checkPlain(args[key]);
+            break;
+
+          // Pass-through.
+          case '!':
+            processedArgs[key] = args[key];
+            break;
+
+          // Escaped and placeholder.
+          default:
+            processedArgs[key] = Drupal.theme('placeholder', args[key]);
+            break;
+        }
+      }
+    }
+
+    return Drupal.stringReplace(str, processedArgs, null);
+  };
+
+  /**
+   * Replaces substring.
+   *
+   * The longest keys will be tried first. Once a substring has been replaced,
+   * its new value will not be searched again.
+   *
+   * @param {string} str
+   *   A string with placeholders.
+   * @param {object} args
+   *   Key-value pairs.
+   * @param {Array|null} keys
+   *   Array of keys from `args`. Internal use only.
+   *
+   * @return {string}
+   *   The replaced string.
+   */
+  Drupal.stringReplace = function (str, args, keys) {
+    if (str.length === 0) {
+      return str;
+    }
+
+    // If the array of keys is not passed then collect the keys from the args.
+    if (!Array.isArray(keys)) {
+      keys = [];
+      for (var k in args) {
+        if (args.hasOwnProperty(k)) {
+          keys.push(k);
+        }
+      }
+
+      // Order the keys by the character length. The shortest one is the first.
+      keys.sort(function (a, b) { return a.length - b.length; });
+    }
+
+    if (keys.length === 0) {
+      return str;
+    }
+
+    // Take next longest one from the end.
+    var key = keys.pop();
+    var fragments = str.split(key);
+
+    if (keys.length) {
+      for (var i = 0; i < fragments.length; i++) {
+        // Process each fragment with a copy of remaining keys.
+        fragments[i] = Drupal.stringReplace(fragments[i], args, keys.slice(0));
+      }
+    }
+
+    return fragments.join(args[key]);
+  };
+
+  /**
+   * Translates strings to the page language, or a given language.
+   *
+   * See the documentation of the server-side t() function for further details.
+   *
+   * @param {string} str
+   *   A string containing the English string to translate.
+   * @param {Object.<string, string>} [args]
+   *   An object of replacements pairs to make after translation. Incidences
+   *   of any key in this array are replaced with the corresponding value.
+   *   See {@link Drupal.formatString}.
+   * @param {object} [options]
+   *   Additional options for translation.
+   * @param {string} [options.context='']
+   *   The context the source string belongs to.
+   *
+   * @return {string}
+   *   The formatted string.
+   *   The translated string.
+   */
+  Drupal.t = function (str, args, options) {
+    options = options || {};
+    options.context = options.context || '';
+
+    // Fetch the localized version of the string.
+    if (typeof drupalTranslations !== 'undefined' && drupalTranslations.strings && drupalTranslations.strings[options.context] && drupalTranslations.strings[options.context][str]) {
+      str = drupalTranslations.strings[options.context][str];
+    }
+
+    if (args) {
+      str = Drupal.formatString(str, args);
+    }
+    return str;
+  };
+
+  /**
+   * Returns the URL to a Drupal page.
+   *
+   * @param {string} path
+   *   Drupal path to transform to URL.
+   *
+   * @return {string}
+   *   The full URL.
+   */
+  Drupal.url = function (path) {
+    return drupalSettings.path.baseUrl + drupalSettings.path.pathPrefix + path;
+  };
+
+  /**
+   * Returns the passed in URL as an absolute URL.
+   *
+   * @param {string} url
+   *   The URL string to be normalized to an absolute URL.
+   *
+   * @return {string}
+   *   The normalized, absolute URL.
+   *
+   * @see https://github.com/angular/angular.js/blob/v1.4.4/src/ng/urlUtils.js
+   * @see https://grack.com/blog/2009/11/17/absolutizing-url-in-javascript
+   * @see https://github.com/jquery/jquery-ui/blob/1.11.4/ui/tabs.js#L53
+   */
+  Drupal.url.toAbsolute = function (url) {
+    var urlParsingNode = document.createElement('a');
+
+    // Decode the URL first; this is required by IE <= 6. Decoding non-UTF-8
+    // strings may throw an exception.
+    try {
+      url = decodeURIComponent(url);
+    }
+    catch (e) {
+      // Empty.
+    }
+
+    urlParsingNode.setAttribute('href', url);
+
+    // IE <= 7 normalizes the URL when assigned to the anchor node similar to
+    // the other browsers.
+    return urlParsingNode.cloneNode(false).href;
+  };
+
+  /**
+   * Returns true if the URL is within Drupal's base path.
+   *
+   * @param {string} url
+   *   The URL string to be tested.
+   *
+   * @return {bool}
+   *   `true` if local.
+   *
+   * @see https://github.com/jquery/jquery-ui/blob/1.11.4/ui/tabs.js#L58
+   */
+  Drupal.url.isLocal = function (url) {
+    // Always use browser-derived absolute URLs in the comparison, to avoid
+    // attempts to break out of the base path using directory traversal.
+    var absoluteUrl = Drupal.url.toAbsolute(url);
+    var protocol = location.protocol;
+
+    // Consider URLs that match this site's base URL but use HTTPS instead of HTTP
+    // as local as well.
+    if (protocol === 'http:' && absoluteUrl.indexOf('https:') === 0) {
+      protocol = 'https:';
+    }
+    var baseUrl = protocol + '//' + location.host + drupalSettings.path.baseUrl.slice(0, -1);
+
+    // Decoding non-UTF-8 strings may throw an exception.
+    try {
+      absoluteUrl = decodeURIComponent(absoluteUrl);
+    }
+    catch (e) {
+      // Empty.
+    }
+    try {
+      baseUrl = decodeURIComponent(baseUrl);
+    }
+    catch (e) {
+      // Empty.
+    }
+
+    // The given URL matches the site's base URL, or has a path under the site's
+    // base URL.
+    return absoluteUrl === baseUrl || absoluteUrl.indexOf(baseUrl + '/') === 0;
+  };
+
+  /**
+   * Formats a string containing a count of items.
+   *
+   * This function ensures that the string is pluralized correctly. Since
+   * {@link Drupal.t} is called by this function, make sure not to pass
+   * already-localized strings to it.
+   *
+   * See the documentation of the server-side
+   * \Drupal\Core\StringTranslation\TranslationInterface::formatPlural()
+   * function for more details.
+   *
+   * @param {number} count
+   *   The item count to display.
+   * @param {string} singular
+   *   The string for the singular case. Please make sure it is clear this is
+   *   singular, to ease translation (e.g. use "1 new comment" instead of "1
+   *   new"). Do not use @count in the singular string.
+   * @param {string} plural
+   *   The string for the plural case. Please make sure it is clear this is
+   *   plural, to ease translation. Use @count in place of the item count, as in
+   *   "@count new comments".
+   * @param {object} [args]
+   *   An object of replacements pairs to make after translation. Incidences
+   *   of any key in this array are replaced with the corresponding value.
+   *   See {@link Drupal.formatString}.
+   *   Note that you do not need to include @count in this array.
+   *   This replacement is done automatically for the plural case.
+   * @param {object} [options]
+   *   The options to pass to the {@link Drupal.t} function.
+   *
+   * @return {string}
+   *   A translated string.
+   */
+  Drupal.formatPlural = function (count, singular, plural, args, options) {
+    args = args || {};
+    args['@count'] = count;
+
+    var pluralDelimiter = drupalSettings.pluralDelimiter;
+    var translations = Drupal.t(singular + pluralDelimiter + plural, args, options).split(pluralDelimiter);
+    var index = 0;
+
+    // Determine the index of the plural form.
+    if (typeof drupalTranslations !== 'undefined' && drupalTranslations.pluralFormula) {
+      index = count in drupalTranslations.pluralFormula ? drupalTranslations.pluralFormula[count] : drupalTranslations.pluralFormula['default'];
+    }
+    else if (args['@count'] !== 1) {
+      index = 1;
+    }
+
+    return translations[index];
+  };
+
+  /**
+   * Encodes a Drupal path for use in a URL.
+   *
+   * For aesthetic reasons slashes are not escaped.
+   *
+   * @param {string} item
+   *   Unencoded path.
+   *
+   * @return {string}
+   *   The encoded path.
+   */
+  Drupal.encodePath = function (item) {
+    return window.encodeURIComponent(item).replace(/%2F/g, '/');
+  };
+
+  /**
+   * Generates the themed representation of a Drupal object.
+   *
+   * All requests for themed output must go through this function. It examines
+   * the request and routes it to the appropriate theme function. If the current
+   * theme does not provide an override function, the generic theme function is
+   * called.
+   *
+   * @example
+   * <caption>To retrieve the HTML for text that should be emphasized and
+   * displayed as a placeholder inside a sentence.</caption>
+   * Drupal.theme('placeholder', text);
+   *
+   * @namespace
+   *
+   * @param {function} func
+   *   The name of the theme function to call.
+   * @param {...args}
+   *   Additional arguments to pass along to the theme function.
+   *
+   * @return {string|object|HTMLElement|jQuery}
+   *   Any data the theme function returns. This could be a plain HTML string,
+   *   but also a complex object.
+   */
+  Drupal.theme = function (func) {
+    var args = Array.prototype.slice.apply(arguments, [1]);
+    if (func in Drupal.theme) {
+      return Drupal.theme[func].apply(this, args);
+    }
+  };
+
+  /**
+   * Formats text for emphasized display in a placeholder inside a sentence.
+   *
+   * @param {string} str
+   *   The text to format (plain-text).
+   *
+   * @return {string}
+   *   The formatted text (html).
+   */
+  Drupal.theme.placeholder = function (str) {
+    return '<em class="placeholder">' + Drupal.checkPlain(str) + '</em>';
+  };
+
+})(domready, Drupal, window.drupalSettings, window.drupalTranslations);
diff --git a/core/themes/stable/js/core/drupalSettingsLoader.js b/core/themes/stable/js/core/drupalSettingsLoader.js
new file mode 100644
index 0000000..ab3911d
--- /dev/null
+++ b/core/themes/stable/js/core/drupalSettingsLoader.js
@@ -0,0 +1,24 @@
+/**
+ * @file
+ * Parse inline JSON and initialize the drupalSettings global object.
+ */
+
+(function () {
+
+  "use strict";
+
+  var settingsElement = document.querySelector('script[type="application/json"][data-drupal-selector="drupal-settings-json"]');
+
+  /**
+   * Variable generated by Drupal with all the configuration created from PHP.
+   *
+   * @global
+   *
+   * @type {object}
+   */
+  window.drupalSettings = {};
+
+  if (settingsElement !== null) {
+    window.drupalSettings = JSON.parse(settingsElement.textContent);
+  }
+})();
diff --git a/core/themes/stable/js/core/form.js b/core/themes/stable/js/core/form.js
new file mode 100644
index 0000000..7210be5
--- /dev/null
+++ b/core/themes/stable/js/core/form.js
@@ -0,0 +1,237 @@
+/**
+ * @file
+ * Form features.
+ */
+
+/**
+ * Triggers when a value in the form changed.
+ *
+ * The event triggers when content is typed or pasted in a text field, before
+ * the change event triggers.
+ *
+ * @event formUpdated
+ */
+
+(function ($, Drupal, debounce) {
+
+  "use strict";
+
+  /**
+   * Retrieves the summary for the first element.
+   *
+   * @return {string}
+   */
+  $.fn.drupalGetSummary = function () {
+    var callback = this.data('summaryCallback');
+    return (this[0] && callback) ? $.trim(callback(this[0])) : '';
+  };
+
+  /**
+   * Sets the summary for all matched elements.
+   *
+   * @param {function} callback
+   *   Either a function that will be called each time the summary is
+   *   retrieved or a string (which is returned each time).
+   *
+   * @return {jQuery}
+   *
+   * @fires event:summaryUpdated
+   *
+   * @listens event:formUpdated
+   */
+  $.fn.drupalSetSummary = function (callback) {
+    var self = this;
+
+    // To facilitate things, the callback should always be a function. If it's
+    // not, we wrap it into an anonymous function which just returns the value.
+    if (typeof callback !== 'function') {
+      var val = callback;
+      callback = function () { return val; };
+    }
+
+    return this
+      .data('summaryCallback', callback)
+      // To prevent duplicate events, the handlers are first removed and then
+      // (re-)added.
+      .off('formUpdated.summary')
+      .on('formUpdated.summary', function () {
+        self.trigger('summaryUpdated');
+      })
+      // The actual summaryUpdated handler doesn't fire when the callback is
+      // changed, so we have to do this manually.
+      .trigger('summaryUpdated');
+  };
+
+  /**
+   * Prevents consecutive form submissions of identical form values.
+   *
+   * Repetitive form submissions that would submit the identical form values
+   * are prevented, unless the form values are different to the previously
+   * submitted values.
+   *
+   * This is a simplified re-implementation of a user-agent behavior that
+   * should be natively supported by major web browsers, but at this time, only
+   * Firefox has a built-in protection.
+   *
+   * A form value-based approach ensures that the constraint is triggered for
+   * consecutive, identical form submissions only. Compared to that, a form
+   * button-based approach would (1) rely on [visible] buttons to exist where
+   * technically not required and (2) require more complex state management if
+   * there are multiple buttons in a form.
+   *
+   * This implementation is based on form-level submit events only and relies
+   * on jQuery's serialize() method to determine submitted form values. As such,
+   * the following limitations exist:
+   *
+   * - Event handlers on form buttons that preventDefault() do not receive a
+   *   double-submit protection. That is deemed to be fine, since such button
+   *   events typically trigger reversible client-side or server-side
+   *   operations that are local to the context of a form only.
+   * - Changed values in advanced form controls, such as file inputs, are not
+   *   part of the form values being compared between consecutive form submits
+   *   (due to limitations of jQuery.serialize()). That is deemed to be
+   *   acceptable, because if the user forgot to attach a file, then the size of
+   *   HTTP payload will most likely be small enough to be fully passed to the
+   *   server endpoint within (milli)seconds. If a user mistakenly attached a
+   *   wrong file and is technically versed enough to cancel the form submission
+   *   (and HTTP payload) in order to attach a different file, then that
+   *   edge-case is not supported here.
+   *
+   * Lastly, all forms submitted via HTTP GET are idempotent by definition of
+   * HTTP standards, so excluded in this implementation.
+   *
+   * @type {Drupal~behavior}
+   */
+  Drupal.behaviors.formSingleSubmit = {
+    attach: function () {
+      function onFormSubmit(e) {
+        var $form = $(e.currentTarget);
+        var formValues = $form.serialize();
+        var previousValues = $form.attr('data-drupal-form-submit-last');
+        if (previousValues === formValues) {
+          e.preventDefault();
+        }
+        else {
+          $form.attr('data-drupal-form-submit-last', formValues);
+        }
+      }
+
+      $('body').once('form-single-submit')
+        .on('submit.singleSubmit', 'form:not([method~="GET"])', onFormSubmit);
+    }
+  };
+
+  /**
+   * Sends a 'formUpdated' event each time a form element is modified.
+   *
+   * @param {HTMLElement} element
+   *
+   * @fires event:formUpdated
+   */
+  function triggerFormUpdated(element) {
+    $(element).trigger('formUpdated');
+  }
+
+  /**
+   * Collects the IDs of all form fields in the given form.
+   *
+   * @param {HTMLFormElement} form
+   *
+   * @return {Array}
+   */
+  function fieldsList(form) {
+    var $fieldList = $(form).find('[name]').map(function (index, element) {
+      // We use id to avoid name duplicates on radio fields and filter out
+      // elements with a name but no id.
+      return element.getAttribute('id');
+    });
+    // Return a true array.
+    return $.makeArray($fieldList);
+  }
+
+  /**
+   * Triggers the 'formUpdated' event on form elements when they are modified.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @fires event:formUpdated
+   */
+  Drupal.behaviors.formUpdated = {
+    attach: function (context) {
+      var $context = $(context);
+      var contextIsForm = $context.is('form');
+      var $forms = (contextIsForm ? $context : $context.find('form')).once('form-updated');
+      var formFields;
+
+      if ($forms.length) {
+        // Initialize form behaviors, use $.makeArray to be able to use native
+        // forEach array method and have the callback parameters in the right
+        // order.
+        $.makeArray($forms).forEach(function (form) {
+          var events = 'change.formUpdated input.formUpdated ';
+          var eventHandler = debounce(function (event) { triggerFormUpdated(event.target); }, 300);
+          formFields = fieldsList(form).join(',');
+
+          form.setAttribute('data-drupal-form-fields', formFields);
+          $(form).on(events, eventHandler);
+        });
+      }
+      // On ajax requests context is the form element.
+      if (contextIsForm) {
+        formFields = fieldsList(context).join(',');
+        // @todo replace with form.getAttribute() when #1979468 is in.
+        var currentFields = $(context).attr('data-drupal-form-fields');
+        // If there has been a change in the fields or their order, trigger
+        // formUpdated.
+        if (formFields !== currentFields) {
+          triggerFormUpdated(context);
+        }
+      }
+
+    },
+    detach: function (context, settings, trigger) {
+      var $context = $(context);
+      var contextIsForm = $context.is('form');
+      if (trigger === 'unload') {
+        var $forms = (contextIsForm ? $context : $context.find('form')).removeOnce('form-updated');
+        if ($forms.length) {
+          $.makeArray($forms).forEach(function (form) {
+            form.removeAttribute('data-drupal-form-fields');
+            $(form).off('.formUpdated');
+          });
+        }
+      }
+    }
+  };
+
+  /**
+   * Prepopulate form fields with information from the visitor browser.
+   *
+   * @type {Drupal~behavior}
+   */
+  Drupal.behaviors.fillUserInfoFromBrowser = {
+    attach: function (context, settings) {
+      var userInfo = ['name', 'mail', 'homepage'];
+      var $forms = $('[data-user-info-from-browser]').once('user-info-from-browser');
+      if ($forms.length) {
+        userInfo.map(function (info) {
+          var $element = $forms.find('[name=' + info + ']');
+          var browserData = localStorage.getItem('Drupal.visitor.' + info);
+          var emptyOrDefault = ($element.val() === '' || ($element.attr('data-drupal-default-value') === $element.val()));
+          if ($element.length && emptyOrDefault && browserData) {
+            $element.val(browserData);
+          }
+        });
+      }
+      $forms.on('submit', function () {
+        userInfo.map(function (info) {
+          var $element = $forms.find('[name=' + info + ']');
+          if ($element.length) {
+            localStorage.setItem('Drupal.visitor.' + info, $element.val());
+          }
+        });
+      });
+    }
+  };
+
+})(jQuery, Drupal, Drupal.debounce);
diff --git a/core/themes/stable/js/core/machine-name.js b/core/themes/stable/js/core/machine-name.js
new file mode 100644
index 0000000..688c4b6
--- /dev/null
+++ b/core/themes/stable/js/core/machine-name.js
@@ -0,0 +1,203 @@
+/**
+ * @file
+ * Machine name functionality.
+ */
+
+(function ($, Drupal, drupalSettings) {
+
+  "use strict";
+
+  /**
+   * Attach the machine-readable name form element behavior.
+   *
+   * @type {Drupal~behavior}
+   */
+  Drupal.behaviors.machineName = {
+
+    /**
+     * Attaches the behavior.
+     *
+     * @param {Element} context
+     * @param {object} settings
+     * @param {object} settings.machineName
+     *   A list of elements to process, keyed by the HTML ID of the form
+     *   element containing the human-readable value. Each element is an object
+     *   defining the following properties:
+     *   - target: The HTML ID of the machine name form element.
+     *   - suffix: The HTML ID of a container to show the machine name preview
+     *     in (usually a field suffix after the human-readable name
+     *     form element).
+     *   - label: The label to show for the machine name preview.
+     *   - replace_pattern: A regular expression (without modifiers) matching
+     *     disallowed characters in the machine name; e.g., '[^a-z0-9]+'.
+     *   - replace: A character to replace disallowed characters with; e.g.,
+     *     '_' or '-'.
+     *   - standalone: Whether the preview should stay in its own element
+     *     rather than the suffix of the source element.
+     *   - field_prefix: The #field_prefix of the form element.
+     *   - field_suffix: The #field_suffix of the form element.
+     */
+    attach: function (context, settings) {
+      var self = this;
+      var $context = $(context);
+      var timeout = null;
+      var xhr = null;
+
+      function clickEditHandler(e) {
+        var data = e.data;
+        data.$wrapper.removeClass('visually-hidden');
+        data.$target.trigger('focus');
+        data.$suffix.hide();
+        data.$source.off('.machineName');
+      }
+
+      function machineNameHandler(e) {
+        var data = e.data;
+        var options = data.options;
+        var baseValue = $(e.target).val();
+
+        var rx = new RegExp(options.replace_pattern, 'g');
+        var expected = baseValue.toLowerCase().replace(rx, options.replace).substr(0, options.maxlength);
+
+        // Abort the last pending request because the label has changed and it
+        // is no longer valid.
+        if (xhr && xhr.readystate !== 4) {
+          xhr.abort();
+          xhr = null;
+        }
+
+        // Wait 300 milliseconds since the last event to update the machine name
+        // i.e., after the user has stopped typing.
+        if (timeout) {
+          clearTimeout(timeout);
+          timeout = null;
+        }
+        timeout = setTimeout(function () {
+          if (baseValue.toLowerCase() !== expected) {
+            xhr = self.transliterate(baseValue, options).done(function (machine) {
+              self.showMachineName(machine.substr(0, options.maxlength), data);
+            });
+          }
+          else {
+            self.showMachineName(expected, data);
+          }
+        }, 300);
+      }
+
+      Object.keys(settings.machineName).forEach(function (source_id) {
+        var machine = '';
+        var eventData;
+        var options = settings.machineName[source_id];
+
+        var $source = $context.find(source_id).addClass('machine-name-source').once('machine-name');
+        var $target = $context.find(options.target).addClass('machine-name-target');
+        var $suffix = $context.find(options.suffix);
+        var $wrapper = $target.closest('.js-form-item');
+        // All elements have to exist.
+        if (!$source.length || !$target.length || !$suffix.length || !$wrapper.length) {
+          return;
+        }
+        // Skip processing upon a form validation error on the machine name.
+        if ($target.hasClass('error')) {
+          return;
+        }
+        // Figure out the maximum length for the machine name.
+        options.maxlength = $target.attr('maxlength');
+        // Hide the form item container of the machine name form element.
+        $wrapper.addClass('visually-hidden');
+        // Determine the initial machine name value. Unless the machine name
+        // form element is disabled or not empty, the initial default value is
+        // based on the human-readable form element value.
+        if ($target.is(':disabled') || $target.val() !== '') {
+          machine = $target.val();
+        }
+        else if ($source.val() !== '') {
+          machine = self.transliterate($source.val(), options);
+        }
+        // Append the machine name preview to the source field.
+        var $preview = $('<span class="machine-name-value">' + options.field_prefix + Drupal.checkPlain(machine) + options.field_suffix + '</span>');
+        $suffix.empty();
+        if (options.label) {
+          $suffix.append('<span class="machine-name-label">' + options.label + ': </span>');
+        }
+        $suffix.append($preview);
+
+        // If the machine name cannot be edited, stop further processing.
+        if ($target.is(':disabled')) {
+          return;
+        }
+
+        eventData = {
+          $source: $source,
+          $target: $target,
+          $suffix: $suffix,
+          $wrapper: $wrapper,
+          $preview: $preview,
+          options: options
+        };
+        // If it is editable, append an edit link.
+        var $link = $('<span class="admin-link"><button type="button" class="link">' + Drupal.t('Edit') + '</button></span>').on('click', eventData, clickEditHandler);
+        $suffix.append($link);
+
+        // Preview the machine name in realtime when the human-readable name
+        // changes, but only if there is no machine name yet; i.e., only upon
+        // initial creation, not when editing.
+        if ($target.val() === '') {
+          $source.on('formUpdated.machineName', eventData, machineNameHandler)
+            // Initialize machine name preview.
+            .trigger('formUpdated.machineName');
+        }
+
+        // Add a listener for an invalid event on the machine name input
+        // to show its container and focus it.
+        $target.on('invalid', eventData, clickEditHandler);
+      });
+    },
+
+    showMachineName: function (machine, data) {
+      var settings = data.options;
+      // Set the machine name to the transliterated value.
+      if (machine !== '') {
+        if (machine !== settings.replace) {
+          data.$target.val(machine);
+          data.$preview.html(settings.field_prefix + Drupal.checkPlain(machine) + settings.field_suffix);
+        }
+        data.$suffix.show();
+      }
+      else {
+        data.$suffix.hide();
+        data.$target.val(machine);
+        data.$preview.empty();
+      }
+    },
+
+    /**
+     * Transliterate a human-readable name to a machine name.
+     *
+     * @param {string} source
+     *   A string to transliterate.
+     * @param {object} settings
+     *   The machine name settings for the corresponding field.
+     * @param {string} settings.replace_pattern
+     *   A regular expression (without modifiers) matching disallowed characters
+     *   in the machine name; e.g., '[^a-z0-9]+'.
+     * @param {string} settings.replace
+     *   A character to replace disallowed characters with; e.g., '_' or '-'.
+     * @param {number} settings.maxlength
+     *   The maximum length of the machine name.
+     *
+     * @return {jQuery}
+     *   The transliterated source string.
+     */
+    transliterate: function (source, settings) {
+      return $.get(Drupal.url('machine_name/transliterate'), {
+        text: source,
+        langcode: drupalSettings.langcode,
+        replace_pattern: settings.replace_pattern,
+        replace: settings.replace,
+        lowercase: true
+      });
+    }
+  };
+
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/themes/stable/js/core/progress.js b/core/themes/stable/js/core/progress.js
new file mode 100644
index 0000000..fdfd3b6
--- /dev/null
+++ b/core/themes/stable/js/core/progress.js
@@ -0,0 +1,157 @@
+/**
+ * @file
+ * Progress bar.
+ */
+
+(function ($, Drupal) {
+
+  "use strict";
+
+  /**
+   * Theme function for the progress bar.
+   *
+   * @param {string} id
+   *
+   * @return {string}
+   *   The HTML for the progress bar.
+   */
+  Drupal.theme.progressBar = function (id) {
+    return '<div id="' + id + '" class="progress" aria-live="polite">' +
+      '<div class="progress__label">&nbsp;</div>' +
+      '<div class="progress__track"><div class="progress__bar"></div></div>' +
+      '<div class="progress__percentage"></div>' +
+      '<div class="progress__description">&nbsp;</div>' +
+      '</div>';
+  };
+
+  /**
+   * A progressbar object. Initialized with the given id. Must be inserted into
+   * the DOM afterwards through progressBar.element.
+   *
+   * Method is the function which will perform the HTTP request to get the
+   * progress bar state. Either "GET" or "POST".
+   *
+   * @example
+   * pb = new Drupal.ProgressBar('myProgressBar');
+   * some_element.appendChild(pb.element);
+   *
+   * @constructor
+   *
+   * @param {string} id
+   * @param {function} updateCallback
+   * @param {string} method
+   * @param {function} errorCallback
+   */
+  Drupal.ProgressBar = function (id, updateCallback, method, errorCallback) {
+    this.id = id;
+    this.method = method || 'GET';
+    this.updateCallback = updateCallback;
+    this.errorCallback = errorCallback;
+
+    // The WAI-ARIA setting aria-live="polite" will announce changes after
+    // users
+    // have completed their current activity and not interrupt the screen
+    // reader.
+    this.element = $(Drupal.theme('progressBar', id));
+  };
+
+  $.extend(Drupal.ProgressBar.prototype, /** @lends Drupal.ProgressBar# */{
+
+    /**
+     * Set the percentage and status message for the progressbar.
+     *
+     * @param {number} percentage
+     * @param {string} message
+     * @param {string} label
+     */
+    setProgress: function (percentage, message, label) {
+      if (percentage >= 0 && percentage <= 100) {
+        $(this.element).find('div.progress__bar').css('width', percentage + '%');
+        $(this.element).find('div.progress__percentage').html(percentage + '%');
+      }
+      $('div.progress__description', this.element).html(message);
+      $('div.progress__label', this.element).html(label);
+      if (this.updateCallback) {
+        this.updateCallback(percentage, message, this);
+      }
+    },
+
+    /**
+     * Start monitoring progress via Ajax.
+     *
+     * @param {string} uri
+     * @param {number} delay
+     */
+    startMonitoring: function (uri, delay) {
+      this.delay = delay;
+      this.uri = uri;
+      this.sendPing();
+    },
+
+    /**
+     * Stop monitoring progress via Ajax.
+     */
+    stopMonitoring: function () {
+      clearTimeout(this.timer);
+      // This allows monitoring to be stopped from within the callback.
+      this.uri = null;
+    },
+
+    /**
+     * Request progress data from server.
+     */
+    sendPing: function () {
+      if (this.timer) {
+        clearTimeout(this.timer);
+      }
+      if (this.uri) {
+        var pb = this;
+        // When doing a post request, you need non-null data. Otherwise a
+        // HTTP 411 or HTTP 406 (with Apache mod_security) error may result.
+        var uri = this.uri;
+        if (uri.indexOf('?') === -1) {
+          uri += '?';
+        }
+        else {
+          uri += '&';
+        }
+        uri += '_format=json';
+        $.ajax({
+          type: this.method,
+          url: uri,
+          data: '',
+          success: function (progress) {
+            // Display errors.
+            if (progress.status === 0) {
+              pb.displayError(progress.data);
+              return;
+            }
+            // Update display.
+            pb.setProgress(progress.percentage, progress.message, progress.label);
+            // Schedule next timer.
+            pb.timer = setTimeout(function () { pb.sendPing(); }, pb.delay);
+          },
+          error: function (xmlhttp) {
+            var e = new Drupal.AjaxError(xmlhttp, pb.uri);
+            pb.displayError('<pre>' + e.message + '</pre>');
+          }
+        });
+      }
+    },
+
+    /**
+     * Display errors on the page.
+     *
+     * @param {string} string
+     */
+    displayError: function (string) {
+      var error = $('<div class="messages messages--error"></div>').html(string);
+      $(this.element).before(error).hide();
+
+      if (this.errorCallback) {
+        this.errorCallback(this);
+      }
+    }
+  });
+
+})(jQuery, Drupal);
diff --git a/core/themes/stable/js/core/states.js b/core/themes/stable/js/core/states.js
new file mode 100644
index 0000000..589a370
--- /dev/null
+++ b/core/themes/stable/js/core/states.js
@@ -0,0 +1,703 @@
+/**
+ * @file
+ * Drupal's states library.
+ */
+
+(function ($) {
+
+  "use strict";
+
+  /**
+   * The base States namespace.
+   *
+   * Having the local states variable allows us to use the States namespace
+   * without having to always declare "Drupal.states".
+   *
+   * @namespace Drupal.states
+   */
+  var states = Drupal.states = {
+
+    /**
+     * An array of functions that should be postponed.
+     */
+    postponed: []
+  };
+
+  /**
+   * Attaches the states.
+   *
+   * @type {Drupal~behavior}
+   */
+  Drupal.behaviors.states = {
+    attach: function (context, settings) {
+      var $states = $(context).find('[data-drupal-states]');
+      var config;
+      var state;
+      var il = $states.length;
+      for (var i = 0; i < il; i++) {
+        config = JSON.parse($states[i].getAttribute('data-drupal-states'));
+        for (state in config) {
+          if (config.hasOwnProperty(state)) {
+            new states.Dependent({
+              element: $($states[i]),
+              state: states.State.sanitize(state),
+              constraints: config[state]
+            });
+          }
+        }
+      }
+
+      // Execute all postponed functions now.
+      while (states.postponed.length) {
+        (states.postponed.shift())();
+      }
+    }
+  };
+
+  /**
+   * Object representing an element that depends on other elements.
+   *
+   * @constructor Drupal.states.Dependent
+   *
+   * @param {object} args
+   *   Object with the following keys (all of which are required)
+   * @param {jQuery} args.element
+   *   A jQuery object of the dependent element
+   * @param {Drupal.states.State} args.state
+   *   A State object describing the state that is dependent
+   * @param {object} args.constraints
+   *   An object with dependency specifications. Lists all elements that this
+   *   element depends on. It can be nested and can contain
+   *   arbitrary AND and OR clauses.
+   */
+  states.Dependent = function (args) {
+    $.extend(this, {values: {}, oldValue: null}, args);
+
+    this.dependees = this.getDependees();
+    for (var selector in this.dependees) {
+      if (this.dependees.hasOwnProperty(selector)) {
+        this.initializeDependee(selector, this.dependees[selector]);
+      }
+    }
+  };
+
+  /**
+   * Comparison functions for comparing the value of an element with the
+   * specification from the dependency settings. If the object type can't be
+   * found in this list, the === operator is used by default.
+   *
+   * @name Drupal.states.Dependent.comparisons
+   *
+   * @prop {function} RegExp
+   * @prop {function} Function
+   * @prop {function} Number
+   */
+  states.Dependent.comparisons = {
+    RegExp: function (reference, value) {
+      return reference.test(value);
+    },
+    Function: function (reference, value) {
+      // The "reference" variable is a comparison function.
+      return reference(value);
+    },
+    Number: function (reference, value) {
+      // If "reference" is a number and "value" is a string, then cast
+      // reference as a string before applying the strict comparison in
+      // compare().
+      // Otherwise numeric keys in the form's #states array fail to match
+      // string values returned from jQuery's val().
+      return (typeof value === 'string') ? compare(reference.toString(), value) : compare(reference, value);
+    }
+  };
+
+  states.Dependent.prototype = {
+
+    /**
+     * Initializes one of the elements this dependent depends on.
+     *
+     * @memberof Drupal.states.Dependent#
+     *
+     * @param {string} selector
+     *   The CSS selector describing the dependee.
+     * @param {object} dependeeStates
+     *   The list of states that have to be monitored for tracking the
+     *   dependee's compliance status.
+     */
+    initializeDependee: function (selector, dependeeStates) {
+      var state;
+      var self = this;
+
+      function stateEventHandler(e) {
+        self.update(e.data.selector, e.data.state, e.value);
+      }
+
+      // Cache for the states of this dependee.
+      this.values[selector] = {};
+
+      for (var i in dependeeStates) {
+        if (dependeeStates.hasOwnProperty(i)) {
+          state = dependeeStates[i];
+          // Make sure we're not initializing this selector/state combination
+          // twice.
+          if ($.inArray(state, dependeeStates) === -1) {
+            continue;
+          }
+
+          state = states.State.sanitize(state);
+
+          // Initialize the value of this state.
+          this.values[selector][state.name] = null;
+
+          // Monitor state changes of the specified state for this dependee.
+          $(selector).on('state:' + state, {selector: selector, state: state}, stateEventHandler);
+
+          // Make sure the event we just bound ourselves to is actually fired.
+          new states.Trigger({selector: selector, state: state});
+        }
+      }
+    },
+
+    /**
+     * Compares a value with a reference value.
+     *
+     * @memberof Drupal.states.Dependent#
+     *
+     * @param {object} reference
+     *   The value used for reference.
+     * @param {string} selector
+     *   CSS selector describing the dependee.
+     * @param {Drupal.states.State} state
+     *   A State object describing the dependee's updated state.
+     *
+     * @return {bool}
+     *   true or false.
+     */
+    compare: function (reference, selector, state) {
+      var value = this.values[selector][state.name];
+      if (reference.constructor.name in states.Dependent.comparisons) {
+        // Use a custom compare function for certain reference value types.
+        return states.Dependent.comparisons[reference.constructor.name](reference, value);
+      }
+      else {
+        // Do a plain comparison otherwise.
+        return compare(reference, value);
+      }
+    },
+
+    /**
+     * Update the value of a dependee's state.
+     *
+     * @memberof Drupal.states.Dependent#
+     *
+     * @param {string} selector
+     *   CSS selector describing the dependee.
+     * @param {Drupal.states.state} state
+     *   A State object describing the dependee's updated state.
+     * @param {string} value
+     *   The new value for the dependee's updated state.
+     */
+    update: function (selector, state, value) {
+      // Only act when the 'new' value is actually new.
+      if (value !== this.values[selector][state.name]) {
+        this.values[selector][state.name] = value;
+        this.reevaluate();
+      }
+    },
+
+    /**
+     * Triggers change events in case a state changed.
+     *
+     * @memberof Drupal.states.Dependent#
+     */
+    reevaluate: function () {
+      // Check whether any constraint for this dependent state is satisfied.
+      var value = this.verifyConstraints(this.constraints);
+
+      // Only invoke a state change event when the value actually changed.
+      if (value !== this.oldValue) {
+        // Store the new value so that we can compare later whether the value
+        // actually changed.
+        this.oldValue = value;
+
+        // Normalize the value to match the normalized state name.
+        value = invert(value, this.state.invert);
+
+        // By adding "trigger: true", we ensure that state changes don't go into
+        // infinite loops.
+        this.element.trigger({type: 'state:' + this.state, value: value, trigger: true});
+      }
+    },
+
+    /**
+     * Evaluates child constraints to determine if a constraint is satisfied.
+     *
+     * @memberof Drupal.states.Dependent#
+     *
+     * @param {object|Array} constraints
+     *   A constraint object or an array of constraints.
+     * @param {string} selector
+     *   The selector for these constraints. If undefined, there isn't yet a
+     *   selector that these constraints apply to. In that case, the keys of the
+     *   object are interpreted as the selector if encountered.
+     *
+     * @return {bool}
+     *   true or false, depending on whether these constraints are satisfied.
+     */
+    verifyConstraints: function (constraints, selector) {
+      var result;
+      if ($.isArray(constraints)) {
+        // This constraint is an array (OR or XOR).
+        var hasXor = $.inArray('xor', constraints) === -1;
+        var len = constraints.length;
+        for (var i = 0; i < len; i++) {
+          if (constraints[i] !== 'xor') {
+            var constraint = this.checkConstraints(constraints[i], selector, i);
+            // Return if this is OR and we have a satisfied constraint or if
+            // this is XOR and we have a second satisfied constraint.
+            if (constraint && (hasXor || result)) {
+              return hasXor;
+            }
+            result = result || constraint;
+          }
+        }
+      }
+      // Make sure we don't try to iterate over things other than objects. This
+      // shouldn't normally occur, but in case the condition definition is
+      // bogus, we don't want to end up with an infinite loop.
+      else if ($.isPlainObject(constraints)) {
+        // This constraint is an object (AND).
+        for (var n in constraints) {
+          if (constraints.hasOwnProperty(n)) {
+            result = ternary(result, this.checkConstraints(constraints[n], selector, n));
+            // False and anything else will evaluate to false, so return when
+            // any false condition is found.
+            if (result === false) { return false; }
+          }
+        }
+      }
+      return result;
+    },
+
+    /**
+     * Checks whether the value matches the requirements for this constraint.
+     *
+     * @memberof Drupal.states.Dependent#
+     *
+     * @param {string|Array|object} value
+     *   Either the value of a state or an array/object of constraints. In the
+     *   latter case, resolving the constraint continues.
+     * @param {string} [selector]
+     *   The selector for this constraint. If undefined, there isn't yet a
+     *   selector that this constraint applies to. In that case, the state key
+     *   is propagates to a selector and resolving continues.
+     * @param {Drupal.states.State} [state]
+     *   The state to check for this constraint. If undefined, resolving
+     *   continues. If both selector and state aren't undefined and valid
+     *   non-numeric strings, a lookup for the actual value of that selector's
+     *   state is performed. This parameter is not a State object but a pristine
+     *   state string.
+     *
+     * @return {bool}
+     *   true or false, depending on whether this constraint is satisfied.
+     */
+    checkConstraints: function (value, selector, state) {
+      // Normalize the last parameter. If it's non-numeric, we treat it either
+      // as a selector (in case there isn't one yet) or as a trigger/state.
+      if (typeof state !== 'string' || (/[0-9]/).test(state[0])) {
+        state = null;
+      }
+      else if (typeof selector === 'undefined') {
+        // Propagate the state to the selector when there isn't one yet.
+        selector = state;
+        state = null;
+      }
+
+      if (state !== null) {
+        // Constraints is the actual constraints of an element to check for.
+        state = states.State.sanitize(state);
+        return invert(this.compare(value, selector, state), state.invert);
+      }
+      else {
+        // Resolve this constraint as an AND/OR operator.
+        return this.verifyConstraints(value, selector);
+      }
+    },
+
+    /**
+     * Gathers information about all required triggers.
+     *
+     * @memberof Drupal.states.Dependent#
+     *
+     * @return {object}
+     */
+    getDependees: function () {
+      var cache = {};
+      // Swivel the lookup function so that we can record all available
+      // selector- state combinations for initialization.
+      var _compare = this.compare;
+      this.compare = function (reference, selector, state) {
+        (cache[selector] || (cache[selector] = [])).push(state.name);
+        // Return nothing (=== undefined) so that the constraint loops are not
+        // broken.
+      };
+
+      // This call doesn't actually verify anything but uses the resolving
+      // mechanism to go through the constraints array, trying to look up each
+      // value. Since we swivelled the compare function, this comparison returns
+      // undefined and lookup continues until the very end. Instead of lookup up
+      // the value, we record that combination of selector and state so that we
+      // can initialize all triggers.
+      this.verifyConstraints(this.constraints);
+      // Restore the original function.
+      this.compare = _compare;
+
+      return cache;
+    }
+  };
+
+  /**
+   * @constructor Drupal.states.Trigger
+   *
+   * @param {object} args
+   */
+  states.Trigger = function (args) {
+    $.extend(this, args);
+
+    if (this.state in states.Trigger.states) {
+      this.element = $(this.selector);
+
+      // Only call the trigger initializer when it wasn't yet attached to this
+      // element. Otherwise we'd end up with duplicate events.
+      if (!this.element.data('trigger:' + this.state)) {
+        this.initialize();
+      }
+    }
+  };
+
+  states.Trigger.prototype = {
+
+    /**
+     * @memberof Drupal.states.Trigger#
+     */
+    initialize: function () {
+      var trigger = states.Trigger.states[this.state];
+
+      if (typeof trigger === 'function') {
+        // We have a custom trigger initialization function.
+        trigger.call(window, this.element);
+      }
+      else {
+        for (var event in trigger) {
+          if (trigger.hasOwnProperty(event)) {
+            this.defaultTrigger(event, trigger[event]);
+          }
+        }
+      }
+
+      // Mark this trigger as initialized for this element.
+      this.element.data('trigger:' + this.state, true);
+    },
+
+    /**
+     * @memberof Drupal.states.Trigger#
+     *
+     * @param {jQuery.Event} event
+     * @param {function} valueFn
+     */
+    defaultTrigger: function (event, valueFn) {
+      var oldValue = valueFn.call(this.element);
+
+      // Attach the event callback.
+      this.element.on(event, $.proxy(function (e) {
+        var value = valueFn.call(this.element, e);
+        // Only trigger the event if the value has actually changed.
+        if (oldValue !== value) {
+          this.element.trigger({type: 'state:' + this.state, value: value, oldValue: oldValue});
+          oldValue = value;
+        }
+      }, this));
+
+      states.postponed.push($.proxy(function () {
+        // Trigger the event once for initialization purposes.
+        this.element.trigger({type: 'state:' + this.state, value: oldValue, oldValue: null});
+      }, this));
+    }
+  };
+
+  /**
+   * This list of states contains functions that are used to monitor the state
+   * of an element. Whenever an element depends on the state of another element,
+   * one of these trigger functions is added to the dependee so that the
+   * dependent element can be updated.
+   *
+   * @name Drupal.states.Trigger.states
+   *
+   * @prop empty
+   * @prop checked
+   * @prop value
+   * @prop collapsed
+   */
+  states.Trigger.states = {
+    // 'empty' describes the state to be monitored.
+    empty: {
+      // 'keyup' is the (native DOM) event that we watch for.
+      keyup: function () {
+        // The function associated with that trigger returns the new value for
+        // the state.
+        return this.val() === '';
+      }
+    },
+
+    checked: {
+      change: function () {
+        // prop() and attr() only takes the first element into account. To
+        // support selectors matching multiple checkboxes, iterate over all and
+        // return whether any is checked.
+        var checked = false;
+        this.each(function () {
+          // Use prop() here as we want a boolean of the checkbox state.
+          // @see http://api.jquery.com/prop/
+          checked = $(this).prop('checked');
+          // Break the each() loop if this is checked.
+          return !checked;
+        });
+        return checked;
+      }
+    },
+
+    // For radio buttons, only return the value if the radio button is selected.
+    value: {
+      keyup: function () {
+        // Radio buttons share the same :input[name="key"] selector.
+        if (this.length > 1) {
+          // Initial checked value of radios is undefined, so we return false.
+          return this.filter(':checked').val() || false;
+        }
+        return this.val();
+      },
+      change: function () {
+        // Radio buttons share the same :input[name="key"] selector.
+        if (this.length > 1) {
+          // Initial checked value of radios is undefined, so we return false.
+          return this.filter(':checked').val() || false;
+        }
+        return this.val();
+      }
+    },
+
+    collapsed: {
+      collapsed: function (e) {
+        return (typeof e !== 'undefined' && 'value' in e) ? e.value : !this.is('[open]');
+      }
+    }
+  };
+
+  /**
+   * A state object is used for describing the state and performing aliasing.
+   *
+   * @constructor Drupal.states.State
+   *
+   * @param {string} state
+   */
+  states.State = function (state) {
+
+    /**
+     * Original unresolved name.
+     */
+    this.pristine = this.name = state;
+
+    // Normalize the state name.
+    var process = true;
+    do {
+      // Iteratively remove exclamation marks and invert the value.
+      while (this.name.charAt(0) === '!') {
+        this.name = this.name.substring(1);
+        this.invert = !this.invert;
+      }
+
+      // Replace the state with its normalized name.
+      if (this.name in states.State.aliases) {
+        this.name = states.State.aliases[this.name];
+      }
+      else {
+        process = false;
+      }
+    } while (process);
+  };
+
+  /**
+   * Creates a new State object by sanitizing the passed value.
+   *
+   * @name Drupal.states.State.sanitize
+   *
+   * @param {string|Drupal.states.State} state
+   *
+   * @return {Drupal.states.state}
+   */
+  states.State.sanitize = function (state) {
+    if (state instanceof states.State) {
+      return state;
+    }
+    else {
+      return new states.State(state);
+    }
+  };
+
+  /**
+   * This list of aliases is used to normalize states and associates negated
+   * names with their respective inverse state.
+   *
+   * @name Drupal.states.State.aliases
+   */
+  states.State.aliases = {
+    enabled: '!disabled',
+    invisible: '!visible',
+    invalid: '!valid',
+    untouched: '!touched',
+    optional: '!required',
+    filled: '!empty',
+    unchecked: '!checked',
+    irrelevant: '!relevant',
+    expanded: '!collapsed',
+    open: '!collapsed',
+    closed: 'collapsed',
+    readwrite: '!readonly'
+  };
+
+  states.State.prototype = {
+
+    /**
+     * @memberof Drupal.states.State#
+     */
+    invert: false,
+
+    /**
+     * Ensures that just using the state object returns the name.
+     *
+     * @memberof Drupal.states.State#
+     *
+     * @return {string}
+     */
+    toString: function () {
+      return this.name;
+    }
+  };
+
+  /**
+   * Global state change handlers. These are bound to "document" to cover all
+   * elements whose state changes. Events sent to elements within the page
+   * bubble up to these handlers. We use this system so that themes and modules
+   * can override these state change handlers for particular parts of a page.
+   */
+
+  $(document).on('state:disabled', function (e) {
+    // Only act when this change was triggered by a dependency and not by the
+    // element monitoring itself.
+    if (e.trigger) {
+      $(e.target)
+        .prop('disabled', e.value)
+        .closest('.js-form-item, .js-form-submit, .js-form-wrapper').toggleClass('form-disabled', e.value)
+        .find('select, input, textarea').prop('disabled', e.value);
+
+      // Note: WebKit nightlies don't reflect that change correctly.
+      // See https://bugs.webkit.org/show_bug.cgi?id=23789
+    }
+  });
+
+  $(document).on('state:required', function (e) {
+    if (e.trigger) {
+      if (e.value) {
+        var label = 'label' + (e.target.id ? '[for=' + e.target.id + ']' : '');
+        var $label = $(e.target).attr({'required': 'required', 'aria-required': 'aria-required'}).closest('.js-form-item, .js-form-wrapper').find(label);
+        // Avoids duplicate required markers on initialization.
+        if (!$label.hasClass('js-form-required').length) {
+          $label.addClass('js-form-required form-required');
+        }
+      }
+      else {
+        $(e.target).removeAttr('required aria-required').closest('.js-form-item, .js-form-wrapper').find('label.js-form-required').removeClass('js-form-required form-required');
+      }
+    }
+  });
+
+  $(document).on('state:visible', function (e) {
+    if (e.trigger) {
+      $(e.target).closest('.js-form-item, .js-form-submit, .js-form-wrapper').toggle(e.value);
+    }
+  });
+
+  $(document).on('state:checked', function (e) {
+    if (e.trigger) {
+      $(e.target).prop('checked', e.value);
+    }
+  });
+
+  $(document).on('state:collapsed', function (e) {
+    if (e.trigger) {
+      if ($(e.target).is('[open]') === e.value) {
+        $(e.target).find('> summary a').trigger('click');
+      }
+    }
+  });
+
+  /**
+   * These are helper functions implementing addition "operators" and don't
+   * implement any logic that is particular to states.
+   */
+
+  /**
+   * Bitwise AND with a third undefined state.
+   *
+   * @function Drupal.states~ternary
+   *
+   * @param {*} a
+   * @param {*} b
+   *
+   * @return {bool}
+   */
+  function ternary(a, b) {
+    if (typeof a === 'undefined') {
+      return b;
+    }
+    else if (typeof b === 'undefined') {
+      return a;
+    }
+    else {
+      return a && b;
+    }
+  }
+
+  /**
+   * Inverts a (if it's not undefined) when invertState is true.
+   *
+   * @function Drupal.states~invert
+   *
+   * @param {*} a
+   * @param {bool} invertState
+   *
+   * @return {bool}
+   */
+  function invert(a, invertState) {
+    return (invertState && typeof a !== 'undefined') ? !a : a;
+  }
+
+  /**
+   * Compares two values while ignoring undefined values.
+   *
+   * @function Drupal.states~compare
+   *
+   * @param {*} a
+   * @param {*} b
+   *
+   * @return {bool}
+   */
+  function compare(a, b) {
+    if (a === b) {
+      return typeof a === 'undefined' ? a : true;
+    }
+    else {
+      return typeof a === 'undefined' || typeof b === 'undefined';
+    }
+  }
+
+})(jQuery);
diff --git a/core/themes/stable/js/core/tabbingmanager.js b/core/themes/stable/js/core/tabbingmanager.js
new file mode 100644
index 0000000..4593ec4
--- /dev/null
+++ b/core/themes/stable/js/core/tabbingmanager.js
@@ -0,0 +1,368 @@
+/**
+ * @file
+ * Manages page tabbing modifications made by modules.
+ */
+
+/**
+ * Allow modules to respond to the constrain event.
+ *
+ * @event drupalTabbingConstrained
+ */
+
+/**
+ * Allow modules to respond to the tabbingContext release event.
+ *
+ * @event drupalTabbingContextReleased
+ */
+
+/**
+ * Allow modules to respond to the constrain event.
+ *
+ * @event drupalTabbingContextActivated
+ */
+
+/**
+ * Allow modules to respond to the constrain event.
+ *
+ * @event drupalTabbingContextDeactivated
+ */
+
+(function ($, Drupal) {
+
+  "use strict";
+
+  /**
+   * Provides an API for managing page tabbing order modifications.
+   *
+   * @constructor Drupal~TabbingManager
+   */
+  function TabbingManager() {
+
+    /**
+     * Tabbing sets are stored as a stack. The active set is at the top of the
+     * stack. We use a JavaScript array as if it were a stack; we consider the
+     * first element to be the bottom and the last element to be the top. This
+     * allows us to use JavaScript's built-in Array.push() and Array.pop()
+     * methods.
+     *
+     * @type {Array.<Drupal~TabbingContext>}
+     */
+    this.stack = [];
+  }
+
+  /**
+   * Add public methods to the TabbingManager class.
+   */
+  $.extend(TabbingManager.prototype, /** @lends Drupal~TabbingManager# */{
+
+    /**
+     * Constrain tabbing to the specified set of elements only.
+     *
+     * Makes elements outside of the specified set of elements unreachable via
+     * the tab key.
+     *
+     * @param {jQuery} elements
+     *   The set of elements to which tabbing should be constrained. Can also
+     *   be a jQuery-compatible selector string.
+     *
+     * @return {Drupal~TabbingContext}
+     *
+     * @fires event:drupalTabbingConstrained
+     */
+    constrain: function (elements) {
+      // Deactivate all tabbingContexts to prepare for the new constraint. A
+      // tabbingContext instance will only be reactivated if the stack is
+      // unwound to it in the _unwindStack() method.
+      var il = this.stack.length;
+      for (var i = 0; i < il; i++) {
+        this.stack[i].deactivate();
+      }
+
+      // The "active tabbing set" are the elements tabbing should be constrained
+      // to.
+      var $elements = $(elements).find(':tabbable').addBack(':tabbable');
+
+      var tabbingContext = new TabbingContext({
+        // The level is the current height of the stack before this new
+        // tabbingContext is pushed on top of the stack.
+        level: this.stack.length,
+        $tabbableElements: $elements
+      });
+
+      this.stack.push(tabbingContext);
+
+      // Activates the tabbingContext; this will manipulate the DOM to constrain
+      // tabbing.
+      tabbingContext.activate();
+
+      // Allow modules to respond to the constrain event.
+      $(document).trigger('drupalTabbingConstrained', tabbingContext);
+
+      return tabbingContext;
+    },
+
+    /**
+     * Restores a former tabbingContext when an active one is released.
+     *
+     * The TabbingManager stack of tabbingContext instances will be unwound
+     * from the top-most released tabbingContext down to the first non-released
+     * tabbingContext instance. This non-released instance is then activated.
+     */
+    release: function () {
+      // Unwind as far as possible: find the topmost non-released
+      // tabbingContext.
+      var toActivate = this.stack.length - 1;
+      while (toActivate >= 0 && this.stack[toActivate].released) {
+        toActivate--;
+      }
+
+      // Delete all tabbingContexts after the to be activated one. They have
+      // already been deactivated, so their effect on the DOM has been reversed.
+      this.stack.splice(toActivate + 1);
+
+      // Get topmost tabbingContext, if one exists, and activate it.
+      if (toActivate >= 0) {
+        this.stack[toActivate].activate();
+      }
+    },
+
+    /**
+     * Makes all elements outside the of the tabbingContext's set untabbable.
+     *
+     * Elements made untabbable have their original tabindex and autofocus
+     * values stored so that they might be restored later when this
+     * tabbingContext is deactivated.
+     *
+     * @param {Drupal~TabbingContext} tabbingContext
+     *   The TabbingContext instance that has been activated.
+     */
+    activate: function (tabbingContext) {
+      var $set = tabbingContext.$tabbableElements;
+      var level = tabbingContext.level;
+      // Determine which elements are reachable via tabbing by default.
+      var $disabledSet = $(':tabbable')
+        // Exclude elements of the active tabbing set.
+        .not($set);
+      // Set the disabled set on the tabbingContext.
+      tabbingContext.$disabledElements = $disabledSet;
+      // Record the tabindex for each element, so we can restore it later.
+      var il = $disabledSet.length;
+      for (var i = 0; i < il; i++) {
+        this.recordTabindex($disabledSet.eq(i), level);
+      }
+      // Make all tabbable elements outside of the active tabbing set
+      // unreachable.
+      $disabledSet
+        .prop('tabindex', -1)
+        .prop('autofocus', false);
+
+      // Set focus on an element in the tabbingContext's set of tabbable
+      // elements. First, check if there is an element with an autofocus
+      // attribute. Select the last one from the DOM order.
+      var $hasFocus = $set.filter('[autofocus]').eq(-1);
+      // If no element in the tabbable set has an autofocus attribute, select
+      // the first element in the set.
+      if ($hasFocus.length === 0) {
+        $hasFocus = $set.eq(0);
+      }
+      $hasFocus.trigger('focus');
+    },
+
+    /**
+     * Restores that tabbable state of a tabbingContext's disabled elements.
+     *
+     * Elements that were made untabbable have their original tabindex and
+     * autofocus values restored.
+     *
+     * @param {Drupal~TabbingContext} tabbingContext
+     *   The TabbingContext instance that has been deactivated.
+     */
+    deactivate: function (tabbingContext) {
+      var $set = tabbingContext.$disabledElements;
+      var level = tabbingContext.level;
+      var il = $set.length;
+      for (var i = 0; i < il; i++) {
+        this.restoreTabindex($set.eq(i), level);
+      }
+    },
+
+    /**
+     * Records the tabindex and autofocus values of an untabbable element.
+     *
+     * @param {jQuery} $el
+     *   The set of elements that have been disabled.
+     * @param {number} level
+     *   The stack level for which the tabindex attribute should be recorded.
+     */
+    recordTabindex: function ($el, level) {
+      var tabInfo = $el.data('drupalOriginalTabIndices') || {};
+      tabInfo[level] = {
+        tabindex: $el[0].getAttribute('tabindex'),
+        autofocus: $el[0].hasAttribute('autofocus')
+      };
+      $el.data('drupalOriginalTabIndices', tabInfo);
+    },
+
+    /**
+     * Restores the tabindex and autofocus values of a reactivated element.
+     *
+     * @param {jQuery} $el
+     *   The element that is being reactivated.
+     * @param {number} level
+     *   The stack level for which the tabindex attribute should be restored.
+     */
+    restoreTabindex: function ($el, level) {
+      var tabInfo = $el.data('drupalOriginalTabIndices');
+      if (tabInfo && tabInfo[level]) {
+        var data = tabInfo[level];
+        if (data.tabindex) {
+          $el[0].setAttribute('tabindex', data.tabindex);
+        }
+        // If the element did not have a tabindex at this stack level then
+        // remove it.
+        else {
+          $el[0].removeAttribute('tabindex');
+        }
+        if (data.autofocus) {
+          $el[0].setAttribute('autofocus', 'autofocus');
+        }
+
+        // Clean up $.data.
+        if (level === 0) {
+          // Remove all data.
+          $el.removeData('drupalOriginalTabIndices');
+        }
+        else {
+          // Remove the data for this stack level and higher.
+          var levelToDelete = level;
+          while (tabInfo.hasOwnProperty(levelToDelete)) {
+            delete tabInfo[levelToDelete];
+            levelToDelete++;
+          }
+          $el.data('drupalOriginalTabIndices', tabInfo);
+        }
+      }
+    }
+  });
+
+  /**
+   * Stores a set of tabbable elements.
+   *
+   * This constraint can be removed with the release() method.
+   *
+   * @constructor Drupal~TabbingContext
+   *
+   * @param {object} options
+   *   A set of initiating values
+   * @param {number} options.level
+   *   The level in the TabbingManager's stack of this tabbingContext.
+   * @param {jQuery} options.$tabbableElements
+   *   The DOM elements that should be reachable via the tab key when this
+   *   tabbingContext is active.
+   * @param {jQuery} options.$disabledElements
+   *   The DOM elements that should not be reachable via the tab key when this
+   *   tabbingContext is active.
+   * @param {bool} options.released
+   *   A released tabbingContext can never be activated again. It will be
+   *   cleaned up when the TabbingManager unwinds its stack.
+   * @param {bool} options.active
+   *   When true, the tabbable elements of this tabbingContext will be reachable
+   *   via the tab key and the disabled elements will not. Only one
+   *   tabbingContext can be active at a time.
+   */
+  function TabbingContext(options) {
+
+    $.extend(this, /** @lends Drupal~TabbingContext# */{
+
+      /**
+       * @type {?number}
+       */
+      level: null,
+
+      /**
+       * @type {jQuery}
+       */
+      $tabbableElements: $(),
+
+      /**
+       * @type {jQuery}
+       */
+      $disabledElements: $(),
+
+      /**
+       * @type {bool}
+       */
+      released: false,
+
+      /**
+       * @type {bool}
+       */
+      active: false
+    }, options);
+  }
+
+  /**
+   * Add public methods to the TabbingContext class.
+   */
+  $.extend(TabbingContext.prototype, /** @lends Drupal~TabbingContext# */{
+
+    /**
+     * Releases this TabbingContext.
+     *
+     * Once a TabbingContext object is released, it can never be activated
+     * again.
+     *
+     * @fires event:drupalTabbingContextReleased
+     */
+    release: function () {
+      if (!this.released) {
+        this.deactivate();
+        this.released = true;
+        Drupal.tabbingManager.release(this);
+        // Allow modules to respond to the tabbingContext release event.
+        $(document).trigger('drupalTabbingContextReleased', this);
+      }
+    },
+
+    /**
+     * Activates this TabbingContext.
+     *
+     * @fires event:drupalTabbingContextActivated
+     */
+    activate: function () {
+      // A released TabbingContext object can never be activated again.
+      if (!this.active && !this.released) {
+        this.active = true;
+        Drupal.tabbingManager.activate(this);
+        // Allow modules to respond to the constrain event.
+        $(document).trigger('drupalTabbingContextActivated', this);
+      }
+    },
+
+    /**
+     * Deactivates this TabbingContext.
+     *
+     * @fires event:drupalTabbingContextDeactivated
+     */
+    deactivate: function () {
+      if (this.active) {
+        this.active = false;
+        Drupal.tabbingManager.deactivate(this);
+        // Allow modules to respond to the constrain event.
+        $(document).trigger('drupalTabbingContextDeactivated', this);
+      }
+    }
+  });
+
+  // Mark this behavior as processed on the first pass and return if it is
+  // already processed.
+  if (Drupal.tabbingManager) {
+    return;
+  }
+
+  /**
+   * @type {Drupal~TabbingManager}
+   */
+  Drupal.tabbingManager = new TabbingManager();
+
+}(jQuery, Drupal));
diff --git a/core/themes/stable/js/core/tabledrag.js b/core/themes/stable/js/core/tabledrag.js
new file mode 100644
index 0000000..8754b98
--- /dev/null
+++ b/core/themes/stable/js/core/tabledrag.js
@@ -0,0 +1,1513 @@
+/**
+ * @file
+ * Provide dragging capabilities to admin uis.
+ */
+
+/**
+ * Triggers when weights columns are toggled.
+ *
+ * @event columnschange
+ */
+
+(function ($, Drupal, drupalSettings) {
+
+  "use strict";
+
+  /**
+   * Store the state of weight columns display for all tables.
+   *
+   * Default value is to hide weight columns.
+   */
+  var showWeight = JSON.parse(localStorage.getItem('Drupal.tableDrag.showWeight'));
+
+  /**
+   * Drag and drop table rows with field manipulation.
+   *
+   * Using the drupal_attach_tabledrag() function, any table with weights or
+   * parent relationships may be made into draggable tables. Columns containing
+   * a field may optionally be hidden, providing a better user experience.
+   *
+   * Created tableDrag instances may be modified with custom behaviors by
+   * overriding the .onDrag, .onDrop, .row.onSwap, and .row.onIndent methods.
+   * See blocks.js for an example of adding additional functionality to
+   * tableDrag.
+   *
+   * @type {Drupal~behavior}
+   */
+  Drupal.behaviors.tableDrag = {
+    attach: function (context, settings) {
+      function initTableDrag(table, base) {
+        if (table.length) {
+          // Create the new tableDrag instance. Save in the Drupal variable
+          // to allow other scripts access to the object.
+          Drupal.tableDrag[base] = new Drupal.tableDrag(table[0], settings.tableDrag[base]);
+        }
+      }
+
+      for (var base in settings.tableDrag) {
+        if (settings.tableDrag.hasOwnProperty(base)) {
+          initTableDrag($(context).find('#' + base).once('tabledrag'), base);
+        }
+      }
+    }
+  };
+
+  /**
+   * Provides table and field manipulation.
+   *
+   * @constructor
+   *
+   * @param {HTMLElement} table
+   *   DOM object for the table to be made draggable.
+   * @param {object} tableSettings
+   *   Settings for the table added via drupal_add_dragtable().
+   */
+  Drupal.tableDrag = function (table, tableSettings) {
+    var self = this;
+    var $table = $(table);
+
+    /**
+     * @type {jQuery}
+     */
+    this.$table = $(table);
+
+    /**
+     *
+     * @type {HTMLElement}
+     */
+    this.table = table;
+
+    /**
+     * @type {object}
+     */
+    this.tableSettings = tableSettings;
+
+    /**
+     * Used to hold information about a current drag operation.
+     *
+     * @type {?HTMLElement}
+     */
+    this.dragObject = null;
+
+    /**
+     * Provides operations for row manipulation.
+     *
+     * @type {?HTMLElement}
+     */
+    this.rowObject = null;
+
+    /**
+     * Remember the previous element.
+     *
+     * @type {?HTMLElement}
+     */
+    this.oldRowElement = null;
+
+    /**
+     * Used to determine up or down direction from last mouse move.
+     *
+     * @type {number}
+     */
+    this.oldY = 0;
+
+    /**
+     * Whether anything in the entire table has changed.
+     *
+     * @type {bool}
+     */
+    this.changed = false;
+
+    /**
+     * Maximum amount of allowed parenting.
+     *
+     * @type {number}
+     */
+    this.maxDepth = 0;
+
+    /**
+     * Direction of the table.
+     *
+     * @type {number}
+     */
+    this.rtl = $(this.table).css('direction') === 'rtl' ? -1 : 1;
+
+    /**
+     *
+     * @type {bool}
+     */
+    this.striping = $(this.table).data('striping') === 1;
+
+    /**
+     * Configure the scroll settings.
+     *
+     * @type {object}
+     *
+     * @prop {number} amount
+     * @prop {number} interval
+     * @prop {number} trigger
+     */
+    this.scrollSettings = {amount: 4, interval: 50, trigger: 70};
+
+    /**
+     *
+     * @type {?number}
+     */
+    this.scrollInterval = null;
+
+    /**
+     *
+     * @type {number}
+     */
+    this.scrollY = 0;
+
+    /**
+     *
+     * @type {number}
+     */
+    this.windowHeight = 0;
+
+    /**
+     * Check this table's settings to see if there are parent relationships in
+     * this table. For efficiency, large sections of code can be skipped if we
+     * don't need to track horizontal movement and indentations.
+     *
+     * @type {bool}
+     */
+    this.indentEnabled = false;
+    for (var group in tableSettings) {
+      if (tableSettings.hasOwnProperty(group)) {
+        for (var n in tableSettings[group]) {
+          if (tableSettings[group].hasOwnProperty(n)) {
+            if (tableSettings[group][n].relationship === 'parent') {
+              this.indentEnabled = true;
+            }
+            if (tableSettings[group][n].limit > 0) {
+              this.maxDepth = tableSettings[group][n].limit;
+            }
+          }
+        }
+      }
+    }
+    if (this.indentEnabled) {
+
+      /**
+       * Total width of indents, set in makeDraggable.
+       *
+       * @type {number}
+       */
+      this.indentCount = 1;
+      // Find the width of indentations to measure mouse movements against.
+      // Because the table doesn't need to start with any indentations, we
+      // manually append 2 indentations in the first draggable row, measure
+      // the offset, then remove.
+      var indent = Drupal.theme('tableDragIndentation');
+      var testRow = $('<tr/>').addClass('draggable').appendTo(table);
+      var testCell = $('<td/>').appendTo(testRow).prepend(indent).prepend(indent);
+      var $indentation = testCell.find('.js-indentation');
+
+      /**
+       *
+       * @type {number}
+       */
+      this.indentAmount = $indentation.get(1).offsetLeft - $indentation.get(0).offsetLeft;
+      testRow.remove();
+    }
+
+    // Make each applicable row draggable.
+    // Match immediate children of the parent element to allow nesting.
+    $table.find('> tr.draggable, > tbody > tr.draggable').each(function () { self.makeDraggable(this); });
+
+    // Add a link before the table for users to show or hide weight columns.
+    $table.before($('<button type="button" class="link tabledrag-toggle-weight"></button>')
+      .attr('title', Drupal.t('Re-order rows by numerical weight instead of dragging.'))
+      .on('click', $.proxy(function (e) {
+        e.preventDefault();
+        this.toggleColumns();
+      }, this))
+      .wrap('<div class="tabledrag-toggle-weight-wrapper"></div>')
+      .parent()
+    );
+
+    // Initialize the specified columns (for example, weight or parent columns)
+    // to show or hide according to user preference. This aids accessibility
+    // so that, e.g., screen reader users can choose to enter weight values and
+    // manipulate form elements directly, rather than using drag-and-drop..
+    self.initColumns();
+
+    // Add event bindings to the document. The self variable is passed along
+    // as event handlers do not have direct access to the tableDrag object.
+    if (Modernizr.touch) {
+      $(document).on('touchmove', function (event) { return self.dragRow(event.originalEvent.touches[0], self); });
+      $(document).on('touchend', function (event) { return self.dropRow(event.originalEvent.touches[0], self); });
+    }
+    else {
+      $(document).on('mousemove', function (event) { return self.dragRow(event, self); });
+      $(document).on('mouseup', function (event) { return self.dropRow(event, self); });
+    }
+
+    // React to localStorage event showing or hiding weight columns.
+    $(window).on('storage', $.proxy(function (e) {
+      // Only react to 'Drupal.tableDrag.showWeight' value change.
+      if (e.originalEvent.key === 'Drupal.tableDrag.showWeight') {
+        // This was changed in another window, get the new value for this
+        // window.
+        showWeight = JSON.parse(e.originalEvent.newValue);
+        this.displayColumns(showWeight);
+      }
+    }, this));
+  };
+
+  /**
+   * Initialize columns containing form elements to be hidden by default.
+   *
+   * Identify and mark each cell with a CSS class so we can easily toggle
+   * show/hide it. Finally, hide columns if user does not have a
+   * 'Drupal.tableDrag.showWeight' localStorage value.
+   */
+  Drupal.tableDrag.prototype.initColumns = function () {
+    var $table = this.$table;
+    var hidden;
+    var cell;
+    var columnIndex;
+    for (var group in this.tableSettings) {
+      if (this.tableSettings.hasOwnProperty(group)) {
+
+        // Find the first field in this group.
+        for (var d in this.tableSettings[group]) {
+          if (this.tableSettings[group].hasOwnProperty(d)) {
+            var field = $table.find('.' + this.tableSettings[group][d].target).eq(0);
+            if (field.length && this.tableSettings[group][d].hidden) {
+              hidden = this.tableSettings[group][d].hidden;
+              cell = field.closest('td');
+              break;
+            }
+          }
+        }
+
+        // Mark the column containing this field so it can be hidden.
+        if (hidden && cell[0]) {
+          // Add 1 to our indexes. The nth-child selector is 1 based, not 0
+          // based. Match immediate children of the parent element to allow
+          // nesting.
+          columnIndex = cell.parent().find('> td').index(cell.get(0)) + 1;
+          $table.find('> thead > tr, > tbody > tr, > tr').each(this.addColspanClass(columnIndex));
+        }
+      }
+    }
+    this.displayColumns(showWeight);
+  };
+
+  /**
+   * Mark cells that have colspan.
+   *
+   * In order to adjust the colspan instead of hiding them altogether.
+   *
+   * @param {number} columnIndex
+   *
+   * @return {function}
+   */
+  Drupal.tableDrag.prototype.addColspanClass = function (columnIndex) {
+    return function () {
+      // Get the columnIndex and adjust for any colspans in this row.
+      var $row = $(this);
+      var index = columnIndex;
+      var cells = $row.children();
+      var cell;
+      cells.each(function (n) {
+        if (n < index && this.colSpan && this.colSpan > 1) {
+          index -= this.colSpan - 1;
+        }
+      });
+      if (index > 0) {
+        cell = cells.filter(':nth-child(' + index + ')');
+        if (cell[0].colSpan && cell[0].colSpan > 1) {
+          // If this cell has a colspan, mark it so we can reduce the colspan.
+          cell.addClass('tabledrag-has-colspan');
+        }
+        else {
+          // Mark this cell so we can hide it.
+          cell.addClass('tabledrag-hide');
+        }
+      }
+    };
+  };
+
+  /**
+   * Hide or display weight columns. Triggers an event on change.
+   *
+   * @fires event:columnschange
+   *
+   * @param {bool} displayWeight
+   *   'true' will show weight columns.
+   */
+  Drupal.tableDrag.prototype.displayColumns = function (displayWeight) {
+    if (displayWeight) {
+      this.showColumns();
+    }
+    // Default action is to hide columns.
+    else {
+      this.hideColumns();
+    }
+    // Trigger an event to allow other scripts to react to this display change.
+    // Force the extra parameter as a bool.
+    $('table').findOnce('tabledrag').trigger('columnschange', !!displayWeight);
+  };
+
+  /**
+   * Toggle the weight column depending on 'showWeight' value.
+   *
+   * Store only default override.
+   */
+  Drupal.tableDrag.prototype.toggleColumns = function () {
+    showWeight = !showWeight;
+    this.displayColumns(showWeight);
+    if (showWeight) {
+      // Save default override.
+      localStorage.setItem('Drupal.tableDrag.showWeight', showWeight);
+    }
+    else {
+      // Reset the value to its default.
+      localStorage.removeItem('Drupal.tableDrag.showWeight');
+    }
+  };
+
+  /**
+   * Hide the columns containing weight/parent form elements.
+   *
+   * Undo showColumns().
+   */
+  Drupal.tableDrag.prototype.hideColumns = function () {
+    var $tables = $('table').findOnce('tabledrag');
+    // Hide weight/parent cells and headers.
+    $tables.find('.tabledrag-hide').css('display', 'none');
+    // Show TableDrag handles.
+    $tables.find('.tabledrag-handle').css('display', '');
+    // Reduce the colspan of any effected multi-span columns.
+    $tables.find('.tabledrag-has-colspan').each(function () {
+      this.colSpan = this.colSpan - 1;
+    });
+    // Change link text.
+    $('.tabledrag-toggle-weight').text(Drupal.t('Show row weights'));
+  };
+
+  /**
+   * Show the columns containing weight/parent form elements.
+   *
+   * Undo hideColumns().
+   */
+  Drupal.tableDrag.prototype.showColumns = function () {
+    var $tables = $('table').findOnce('tabledrag');
+    // Show weight/parent cells and headers.
+    $tables.find('.tabledrag-hide').css('display', '');
+    // Hide TableDrag handles.
+    $tables.find('.tabledrag-handle').css('display', 'none');
+    // Increase the colspan for any columns where it was previously reduced.
+    $tables.find('.tabledrag-has-colspan').each(function () {
+      this.colSpan = this.colSpan + 1;
+    });
+    // Change link text.
+    $('.tabledrag-toggle-weight').text(Drupal.t('Hide row weights'));
+  };
+
+  /**
+   * Find the target used within a particular row and group.
+   *
+   * @param {string} group
+   * @param {HTMLElement} row
+   *
+   * @return {object}
+   */
+  Drupal.tableDrag.prototype.rowSettings = function (group, row) {
+    var field = $(row).find('.' + group);
+    var tableSettingsGroup = this.tableSettings[group];
+    for (var delta in tableSettingsGroup) {
+      if (tableSettingsGroup.hasOwnProperty(delta)) {
+        var targetClass = tableSettingsGroup[delta].target;
+        if (field.is('.' + targetClass)) {
+          // Return a copy of the row settings.
+          var rowSettings = {};
+          for (var n in tableSettingsGroup[delta]) {
+            if (tableSettingsGroup[delta].hasOwnProperty(n)) {
+              rowSettings[n] = tableSettingsGroup[delta][n];
+            }
+          }
+          return rowSettings;
+        }
+      }
+    }
+  };
+
+  /**
+   * Take an item and add event handlers to make it become draggable.
+   *
+   * @param {HTMLElement} item
+   */
+  Drupal.tableDrag.prototype.makeDraggable = function (item) {
+    var self = this;
+    var $item = $(item);
+    // Add a class to the title link
+    $item.find('td:first-of-type').find('a').addClass('menu-item__link');
+    // Create the handle.
+    var handle = $('<a href="#" class="tabledrag-handle"><div class="handle">&nbsp;</div></a>').attr('title', Drupal.t('Drag to re-order'));
+    // Insert the handle after indentations (if any).
+    var $indentationLast = $item.find('td:first-of-type').find('.js-indentation').eq(-1);
+    if ($indentationLast.length) {
+      $indentationLast.after(handle);
+      // Update the total width of indentation in this entire table.
+      self.indentCount = Math.max($item.find('.js-indentation').length, self.indentCount);
+    }
+    else {
+      $item.find('td').eq(0).prepend(handle);
+    }
+
+    if (Modernizr.touch) {
+      handle.on('touchstart', function (event) {
+        event.preventDefault();
+        event = event.originalEvent.touches[0];
+        self.dragStart(event, self, item);
+      });
+    }
+    else {
+      handle.on('mousedown', function (event) {
+        event.preventDefault();
+        self.dragStart(event, self, item);
+      });
+    }
+
+    // Prevent the anchor tag from jumping us to the top of the page.
+    handle.on('click', function (e) {
+      e.preventDefault();
+    });
+
+    // Set blur cleanup when a handle is focused.
+    handle.on('focus', function () {
+      self.safeBlur = true;
+    });
+
+    // On blur, fire the same function as a touchend/mouseup. This is used to
+    // update values after a row has been moved through the keyboard support.
+    handle.on('blur', function (event) {
+      if (self.rowObject && self.safeBlur) {
+        self.dropRow(event, self);
+      }
+    });
+
+    // Add arrow-key support to the handle.
+    handle.on('keydown', function (event) {
+      // If a rowObject doesn't yet exist and this isn't the tab key.
+      if (event.keyCode !== 9 && !self.rowObject) {
+        self.rowObject = new self.row(item, 'keyboard', self.indentEnabled, self.maxDepth, true);
+      }
+
+      var keyChange = false;
+      var groupHeight;
+      switch (event.keyCode) {
+        // Left arrow.
+        case 37:
+        // Safari left arrow.
+        case 63234:
+          keyChange = true;
+          self.rowObject.indent(-1 * self.rtl);
+          break;
+
+        // Up arrow.
+        case 38:
+        // Safari up arrow.
+        case 63232:
+          var $previousRow = $(self.rowObject.element).prev('tr:first-of-type');
+          var previousRow = $previousRow.get(0);
+          while (previousRow && $previousRow.is(':hidden')) {
+            $previousRow = $(previousRow).prev('tr:first-of-type');
+            previousRow = $previousRow.get(0);
+          }
+          if (previousRow) {
+            // Do not allow the onBlur cleanup.
+            self.safeBlur = false;
+            self.rowObject.direction = 'up';
+            keyChange = true;
+
+            if ($(item).is('.tabledrag-root')) {
+              // Swap with the previous top-level row.
+              groupHeight = 0;
+              while (previousRow && $previousRow.find('.js-indentation').length) {
+                $previousRow = $(previousRow).prev('tr:first-of-type');
+                previousRow = $previousRow.get(0);
+                groupHeight += $previousRow.is(':hidden') ? 0 : previousRow.offsetHeight;
+              }
+              if (previousRow) {
+                self.rowObject.swap('before', previousRow);
+                // No need to check for indentation, 0 is the only valid one.
+                window.scrollBy(0, -groupHeight);
+              }
+            }
+            else if (self.table.tBodies[0].rows[0] !== previousRow || $previousRow.is('.draggable')) {
+              // Swap with the previous row (unless previous row is the first
+              // one and undraggable).
+              self.rowObject.swap('before', previousRow);
+              self.rowObject.interval = null;
+              self.rowObject.indent(0);
+              window.scrollBy(0, -parseInt(item.offsetHeight, 10));
+            }
+            // Regain focus after the DOM manipulation.
+            handle.trigger('focus');
+          }
+          break;
+
+        // Right arrow.
+        case 39:
+        // Safari right arrow.
+        case 63235:
+          keyChange = true;
+          self.rowObject.indent(self.rtl);
+          break;
+
+        // Down arrow.
+        case 40:
+        // Safari down arrow.
+        case 63233:
+          var $nextRow = $(self.rowObject.group).eq(-1).next('tr:first-of-type');
+          var nextRow = $nextRow.get(0);
+          while (nextRow && $nextRow.is(':hidden')) {
+            $nextRow = $(nextRow).next('tr:first-of-type');
+            nextRow = $nextRow.get(0);
+          }
+          if (nextRow) {
+            // Do not allow the onBlur cleanup.
+            self.safeBlur = false;
+            self.rowObject.direction = 'down';
+            keyChange = true;
+
+            if ($(item).is('.tabledrag-root')) {
+              // Swap with the next group (necessarily a top-level one).
+              groupHeight = 0;
+              var nextGroup = new self.row(nextRow, 'keyboard', self.indentEnabled, self.maxDepth, false);
+              if (nextGroup) {
+                $(nextGroup.group).each(function () {
+                  groupHeight += $(this).is(':hidden') ? 0 : this.offsetHeight;
+                });
+                var nextGroupRow = $(nextGroup.group).eq(-1).get(0);
+                self.rowObject.swap('after', nextGroupRow);
+                // No need to check for indentation, 0 is the only valid one.
+                window.scrollBy(0, parseInt(groupHeight, 10));
+              }
+            }
+            else {
+              // Swap with the next row.
+              self.rowObject.swap('after', nextRow);
+              self.rowObject.interval = null;
+              self.rowObject.indent(0);
+              window.scrollBy(0, parseInt(item.offsetHeight, 10));
+            }
+            // Regain focus after the DOM manipulation.
+            handle.trigger('focus');
+          }
+          break;
+      }
+
+      if (self.rowObject && self.rowObject.changed === true) {
+        $(item).addClass('drag');
+        if (self.oldRowElement) {
+          $(self.oldRowElement).removeClass('drag-previous');
+        }
+        self.oldRowElement = item;
+        if (self.striping === true) {
+          self.restripeTable();
+        }
+        self.onDrag();
+      }
+
+      // Returning false if we have an arrow key to prevent scrolling.
+      if (keyChange) {
+        return false;
+      }
+    });
+
+    // Compatibility addition, return false on keypress to prevent unwanted
+    // scrolling. IE and Safari will suppress scrolling on keydown, but all
+    // other browsers need to return false on keypress.
+    // http://www.quirksmode.org/js/keys.html
+    handle.on('keypress', function (event) {
+      switch (event.keyCode) {
+        // Left arrow.
+        case 37:
+        // Up arrow.
+        case 38:
+        // Right arrow.
+        case 39:
+        // Down arrow.
+        case 40:
+          return false;
+      }
+    });
+  };
+
+  /**
+   * Pointer event initiator, creates drag object and information.
+   *
+   * @param {jQuery.Event} event
+   *   The event object that trigger the drag.
+   * @param {Drupal.tableDrag} self
+   *   The drag handle.
+   * @param {HTMLElement} item
+   *   The item that that is being dragged.
+   */
+  Drupal.tableDrag.prototype.dragStart = function (event, self, item) {
+    // Create a new dragObject recording the pointer information.
+    self.dragObject = {};
+    self.dragObject.initOffset = self.getPointerOffset(item, event);
+    self.dragObject.initPointerCoords = self.pointerCoords(event);
+    if (self.indentEnabled) {
+      self.dragObject.indentPointerPos = self.dragObject.initPointerCoords;
+    }
+
+    // If there's a lingering row object from the keyboard, remove its focus.
+    if (self.rowObject) {
+      $(self.rowObject.element).find('a.tabledrag-handle').trigger('blur');
+    }
+
+    // Create a new rowObject for manipulation of this row.
+    self.rowObject = new self.row(item, 'pointer', self.indentEnabled, self.maxDepth, true);
+
+    // Save the position of the table.
+    self.table.topY = $(self.table).offset().top;
+    self.table.bottomY = self.table.topY + self.table.offsetHeight;
+
+    // Add classes to the handle and row.
+    $(item).addClass('drag');
+
+    // Set the document to use the move cursor during drag.
+    $('body').addClass('drag');
+    if (self.oldRowElement) {
+      $(self.oldRowElement).removeClass('drag-previous');
+    }
+  };
+
+  /**
+   * Pointer movement handler, bound to document.
+   *
+   * @param {jQuery.Event} event
+   * @param {Drupal.tableDrag} self
+   *
+   * @return {bool|undefined}
+   */
+  Drupal.tableDrag.prototype.dragRow = function (event, self) {
+    if (self.dragObject) {
+      self.currentPointerCoords = self.pointerCoords(event);
+      var y = self.currentPointerCoords.y - self.dragObject.initOffset.y;
+      var x = self.currentPointerCoords.x - self.dragObject.initOffset.x;
+
+      // Check for row swapping and vertical scrolling.
+      if (y !== self.oldY) {
+        self.rowObject.direction = y > self.oldY ? 'down' : 'up';
+        // Update the old value.
+        self.oldY = y;
+        // Check if the window should be scrolled (and how fast).
+        var scrollAmount = self.checkScroll(self.currentPointerCoords.y);
+        // Stop any current scrolling.
+        clearInterval(self.scrollInterval);
+        // Continue scrolling if the mouse has moved in the scroll direction.
+        if (scrollAmount > 0 && self.rowObject.direction === 'down' || scrollAmount < 0 && self.rowObject.direction === 'up') {
+          self.setScroll(scrollAmount);
+        }
+
+        // If we have a valid target, perform the swap and restripe the table.
+        var currentRow = self.findDropTargetRow(x, y);
+        if (currentRow) {
+          if (self.rowObject.direction === 'down') {
+            self.rowObject.swap('after', currentRow, self);
+          }
+          else {
+            self.rowObject.swap('before', currentRow, self);
+          }
+          if (self.striping === true) {
+            self.restripeTable();
+          }
+        }
+      }
+
+      // Similar to row swapping, handle indentations.
+      if (self.indentEnabled) {
+        var xDiff = self.currentPointerCoords.x - self.dragObject.indentPointerPos.x;
+        // Set the number of indentations the pointer has been moved left or
+        // right.
+        var indentDiff = Math.round(xDiff / self.indentAmount);
+        // Indent the row with our estimated diff, which may be further
+        // restricted according to the rows around this row.
+        var indentChange = self.rowObject.indent(indentDiff);
+        // Update table and pointer indentations.
+        self.dragObject.indentPointerPos.x += self.indentAmount * indentChange * self.rtl;
+        self.indentCount = Math.max(self.indentCount, self.rowObject.indents);
+      }
+
+      return false;
+    }
+  };
+
+  /**
+   * Pointerup behavior.
+   *
+   * @param {jQuery.Event} event
+   * @param {Drupal.tableDrag} self
+   */
+  Drupal.tableDrag.prototype.dropRow = function (event, self) {
+    var droppedRow;
+    var $droppedRow;
+
+    // Drop row functionality.
+    if (self.rowObject !== null) {
+      droppedRow = self.rowObject.element;
+      $droppedRow = $(droppedRow);
+      // The row is already in the right place so we just release it.
+      if (self.rowObject.changed === true) {
+        // Update the fields in the dropped row.
+        self.updateFields(droppedRow);
+
+        // If a setting exists for affecting the entire group, update all the
+        // fields in the entire dragged group.
+        for (var group in self.tableSettings) {
+          if (self.tableSettings.hasOwnProperty(group)) {
+            var rowSettings = self.rowSettings(group, droppedRow);
+            if (rowSettings.relationship === 'group') {
+              for (var n in self.rowObject.children) {
+                if (self.rowObject.children.hasOwnProperty(n)) {
+                  self.updateField(self.rowObject.children[n], group);
+                }
+              }
+            }
+          }
+        }
+
+        self.rowObject.markChanged();
+        if (self.changed === false) {
+          $(Drupal.theme('tableDragChangedWarning')).insertBefore(self.table).hide().fadeIn('slow');
+          self.changed = true;
+        }
+      }
+
+      if (self.indentEnabled) {
+        self.rowObject.removeIndentClasses();
+      }
+      if (self.oldRowElement) {
+        $(self.oldRowElement).removeClass('drag-previous');
+      }
+      $droppedRow.removeClass('drag').addClass('drag-previous');
+      self.oldRowElement = droppedRow;
+      self.onDrop();
+      self.rowObject = null;
+    }
+
+    // Functionality specific only to pointerup events.
+    if (self.dragObject !== null) {
+      self.dragObject = null;
+      $('body').removeClass('drag');
+      clearInterval(self.scrollInterval);
+    }
+  };
+
+  /**
+   * Get the coordinates from the event (allowing for browser differences).
+   *
+   * @param {jQuery.Event} event
+   *
+   * @return {{x: number, y: number}}
+   */
+  Drupal.tableDrag.prototype.pointerCoords = function (event) {
+    if (event.pageX || event.pageY) {
+      return {x: event.pageX, y: event.pageY};
+    }
+    return {
+      x: event.clientX + document.body.scrollLeft - document.body.clientLeft,
+      y: event.clientY + document.body.scrollTop - document.body.clientTop
+    };
+  };
+
+  /**
+   * Get the event offset from the target element.
+   *
+   * Given a target element and a pointer event, get the event offset from that
+   * element. To do this we need the element's position and the target position.
+   *
+   * @param {HTMLElement} target
+   * @param {jQuery.Event} event
+   *
+   * @return {{x: number, y: number}}
+   */
+  Drupal.tableDrag.prototype.getPointerOffset = function (target, event) {
+    var docPos = $(target).offset();
+    var pointerPos = this.pointerCoords(event);
+    return {x: pointerPos.x - docPos.left, y: pointerPos.y - docPos.top};
+  };
+
+  /**
+   * Find the row the mouse is currently over.
+   *
+   * This row is then taken and swapped with the one being dragged.
+   *
+   * @param {number} x
+   *   The x coordinate of the mouse on the page (not the screen).
+   * @param {number} y
+   *   The y coordinate of the mouse on the page (not the screen).
+   *
+   * @return {*}
+   */
+  Drupal.tableDrag.prototype.findDropTargetRow = function (x, y) {
+    var rows = $(this.table.tBodies[0].rows).not(':hidden');
+    for (var n = 0; n < rows.length; n++) {
+      var row = rows[n];
+      var $row = $(row);
+      var rowY = $row.offset().top;
+      var rowHeight;
+      // Because Safari does not report offsetHeight on table rows, but does on
+      // table cells, grab the firstChild of the row and use that instead.
+      // http://jacob.peargrove.com/blog/2006/technical/table-row-offsettop-bug-in-safari.
+      if (row.offsetHeight === 0) {
+        rowHeight = parseInt(row.firstChild.offsetHeight, 10) / 2;
+      }
+      // Other browsers.
+      else {
+        rowHeight = parseInt(row.offsetHeight, 10) / 2;
+      }
+
+      // Because we always insert before, we need to offset the height a bit.
+      if ((y > (rowY - rowHeight)) && (y < (rowY + rowHeight))) {
+        if (this.indentEnabled) {
+          // Check that this row is not a child of the row being dragged.
+          for (n in this.rowObject.group) {
+            if (this.rowObject.group[n] === row) {
+              return null;
+            }
+          }
+        }
+        else {
+          // Do not allow a row to be swapped with itself.
+          if (row === this.rowObject.element) {
+            return null;
+          }
+        }
+
+        // Check that swapping with this row is allowed.
+        if (!this.rowObject.isValidSwap(row)) {
+          return null;
+        }
+
+        // We may have found the row the mouse just passed over, but it doesn't
+        // take into account hidden rows. Skip backwards until we find a
+        // draggable row.
+        while ($row.is(':hidden') && $row.prev('tr').is(':hidden')) {
+          $row = $row.prev('tr:first-of-type');
+          row = $row.get(0);
+        }
+        return row;
+      }
+    }
+    return null;
+  };
+
+  /**
+   * After the row is dropped, update the table fields.
+   *
+   * @param {HTMLElement} changedRow
+   *   DOM object for the row that was just dropped.
+   */
+  Drupal.tableDrag.prototype.updateFields = function (changedRow) {
+    for (var group in this.tableSettings) {
+      if (this.tableSettings.hasOwnProperty(group)) {
+        // Each group may have a different setting for relationship, so we find
+        // the source rows for each separately.
+        this.updateField(changedRow, group);
+      }
+    }
+  };
+
+  /**
+   * After the row is dropped, update a single table field.
+   *
+   * @param {HTMLElement} changedRow
+   *   DOM object for the row that was just dropped.
+   * @param {string} group
+   *   The settings group on which field updates will occur.
+   */
+  Drupal.tableDrag.prototype.updateField = function (changedRow, group) {
+    var rowSettings = this.rowSettings(group, changedRow);
+    var $changedRow = $(changedRow);
+    var sourceRow;
+    var $previousRow;
+    var previousRow;
+    var useSibling;
+    // Set the row as its own target.
+    if (rowSettings.relationship === 'self' || rowSettings.relationship === 'group') {
+      sourceRow = changedRow;
+    }
+    // Siblings are easy, check previous and next rows.
+    else if (rowSettings.relationship === 'sibling') {
+      $previousRow = $changedRow.prev('tr:first-of-type');
+      previousRow = $previousRow.get(0);
+      var $nextRow = $changedRow.next('tr:first-of-type');
+      var nextRow = $nextRow.get(0);
+      sourceRow = changedRow;
+      if ($previousRow.is('.draggable') && $previousRow.find('.' + group).length) {
+        if (this.indentEnabled) {
+          if ($previousRow.find('.js-indentations').length === $changedRow.find('.js-indentations').length) {
+            sourceRow = previousRow;
+          }
+        }
+        else {
+          sourceRow = previousRow;
+        }
+      }
+      else if ($nextRow.is('.draggable') && $nextRow.find('.' + group).length) {
+        if (this.indentEnabled) {
+          if ($nextRow.find('.js-indentations').length === $changedRow.find('.js-indentations').length) {
+            sourceRow = nextRow;
+          }
+        }
+        else {
+          sourceRow = nextRow;
+        }
+      }
+    }
+    // Parents, look up the tree until we find a field not in this group.
+    // Go up as many parents as indentations in the changed row.
+    else if (rowSettings.relationship === 'parent') {
+      $previousRow = $changedRow.prev('tr');
+      previousRow = $previousRow;
+      while ($previousRow.length && $previousRow.find('.js-indentation').length >= this.rowObject.indents) {
+        $previousRow = $previousRow.prev('tr');
+        previousRow = $previousRow;
+      }
+      // If we found a row.
+      if ($previousRow.length) {
+        sourceRow = $previousRow.get(0);
+      }
+      // Otherwise we went all the way to the left of the table without finding
+      // a parent, meaning this item has been placed at the root level.
+      else {
+        // Use the first row in the table as source, because it's guaranteed to
+        // be at the root level. Find the first item, then compare this row
+        // against it as a sibling.
+        sourceRow = $(this.table).find('tr.draggable:first-of-type').get(0);
+        if (sourceRow === this.rowObject.element) {
+          sourceRow = $(this.rowObject.group[this.rowObject.group.length - 1]).next('tr.draggable').get(0);
+        }
+        useSibling = true;
+      }
+    }
+
+    // Because we may have moved the row from one category to another,
+    // take a look at our sibling and borrow its sources and targets.
+    this.copyDragClasses(sourceRow, changedRow, group);
+    rowSettings = this.rowSettings(group, changedRow);
+
+    // In the case that we're looking for a parent, but the row is at the top
+    // of the tree, copy our sibling's values.
+    if (useSibling) {
+      rowSettings.relationship = 'sibling';
+      rowSettings.source = rowSettings.target;
+    }
+
+    var targetClass = '.' + rowSettings.target;
+    var targetElement = $changedRow.find(targetClass).get(0);
+
+    // Check if a target element exists in this row.
+    if (targetElement) {
+      var sourceClass = '.' + rowSettings.source;
+      var sourceElement = $(sourceClass, sourceRow).get(0);
+      switch (rowSettings.action) {
+        case 'depth':
+          // Get the depth of the target row.
+          targetElement.value = $(sourceElement).closest('tr').find('.js-indentation').length;
+          break;
+
+        case 'match':
+          // Update the value.
+          targetElement.value = sourceElement.value;
+          break;
+
+        case 'order':
+          var siblings = this.rowObject.findSiblings(rowSettings);
+          if ($(targetElement).is('select')) {
+            // Get a list of acceptable values.
+            var values = [];
+            $(targetElement).find('option').each(function () {
+              values.push(this.value);
+            });
+            var maxVal = values[values.length - 1];
+            // Populate the values in the siblings.
+            $(siblings).find(targetClass).each(function () {
+              // If there are more items than possible values, assign the
+              // maximum value to the row.
+              if (values.length > 0) {
+                this.value = values.shift();
+              }
+              else {
+                this.value = maxVal;
+              }
+            });
+          }
+          else {
+            // Assume a numeric input field.
+            var weight = parseInt($(siblings[0]).find(targetClass).val(), 10) || 0;
+            $(siblings).find(targetClass).each(function () {
+              this.value = weight;
+              weight++;
+            });
+          }
+          break;
+      }
+    }
+  };
+
+  /**
+   * Copy all tableDrag related classes from one row to another.
+   *
+   * Copy all special tableDrag classes from one row's form elements to a
+   * different one, removing any special classes that the destination row
+   * may have had.
+   *
+   * @param {HTMLElement} sourceRow
+   * @param {HTMLElement} targetRow
+   * @param {string} group
+   */
+  Drupal.tableDrag.prototype.copyDragClasses = function (sourceRow, targetRow, group) {
+    var sourceElement = $(sourceRow).find('.' + group);
+    var targetElement = $(targetRow).find('.' + group);
+    if (sourceElement.length && targetElement.length) {
+      targetElement[0].className = sourceElement[0].className;
+    }
+  };
+
+  /**
+   * @param {number} cursorY
+   * @return {number}
+   */
+  Drupal.tableDrag.prototype.checkScroll = function (cursorY) {
+    var de = document.documentElement;
+    var b = document.body;
+
+    var windowHeight = this.windowHeight = window.innerHeight || (de.clientHeight && de.clientWidth !== 0 ? de.clientHeight : b.offsetHeight);
+    var scrollY;
+    if (document.all) {
+      scrollY = this.scrollY = !de.scrollTop ? b.scrollTop : de.scrollTop;
+    }
+    else {
+      scrollY = this.scrollY = window.pageYOffset ? window.pageYOffset : window.scrollY;
+    }
+    var trigger = this.scrollSettings.trigger;
+    var delta = 0;
+
+    // Return a scroll speed relative to the edge of the screen.
+    if (cursorY - scrollY > windowHeight - trigger) {
+      delta = trigger / (windowHeight + scrollY - cursorY);
+      delta = (delta > 0 && delta < trigger) ? delta : trigger;
+      return delta * this.scrollSettings.amount;
+    }
+    else if (cursorY - scrollY < trigger) {
+      delta = trigger / (cursorY - scrollY);
+      delta = (delta > 0 && delta < trigger) ? delta : trigger;
+      return -delta * this.scrollSettings.amount;
+    }
+  };
+
+  /**
+   * @param {number} scrollAmount
+   */
+  Drupal.tableDrag.prototype.setScroll = function (scrollAmount) {
+    var self = this;
+
+    this.scrollInterval = setInterval(function () {
+      // Update the scroll values stored in the object.
+      self.checkScroll(self.currentPointerCoords.y);
+      var aboveTable = self.scrollY > self.table.topY;
+      var belowTable = self.scrollY + self.windowHeight < self.table.bottomY;
+      if (scrollAmount > 0 && belowTable || scrollAmount < 0 && aboveTable) {
+        window.scrollBy(0, scrollAmount);
+      }
+    }, this.scrollSettings.interval);
+  };
+
+  /**
+   * Command to restripe table properly.
+   */
+  Drupal.tableDrag.prototype.restripeTable = function () {
+    // :even and :odd are reversed because jQuery counts from 0 and
+    // we count from 1, so we're out of sync.
+    // Match immediate children of the parent element to allow nesting.
+    $(this.table).find('> tbody > tr.draggable:visible, > tr.draggable:visible')
+      .removeClass('odd even')
+      .filter(':odd').addClass('even').end()
+      .filter(':even').addClass('odd');
+  };
+
+  /**
+   * Stub function. Allows a custom handler when a row begins dragging.
+   *
+   * @return {?bool}
+   */
+  Drupal.tableDrag.prototype.onDrag = function () {
+    return null;
+  };
+
+  /**
+   * Stub function. Allows a custom handler when a row is dropped.
+   *
+   * @return {?bool}
+   */
+  Drupal.tableDrag.prototype.onDrop = function () {
+    return null;
+  };
+
+  /**
+   * Constructor to make a new object to manipulate a table row.
+   *
+   * @param {HTMLElement} tableRow
+   *   The DOM element for the table row we will be manipulating.
+   * @param {string} method
+   *   The method in which this row is being moved. Either 'keyboard' or
+   *   'mouse'.
+   * @param {bool} indentEnabled
+   *   Whether the containing table uses indentations. Used for optimizations.
+   * @param {number} maxDepth
+   *   The maximum amount of indentations this row may contain.
+   * @param {bool} addClasses
+   *   Whether we want to add classes to this row to indicate child
+   *   relationships.
+   */
+  Drupal.tableDrag.prototype.row = function (tableRow, method, indentEnabled, maxDepth, addClasses) {
+    var $tableRow = $(tableRow);
+
+    this.element = tableRow;
+    this.method = method;
+    this.group = [tableRow];
+    this.groupDepth = $tableRow.find('.js-indentation').length;
+    this.changed = false;
+    this.table = $tableRow.closest('table')[0];
+    this.indentEnabled = indentEnabled;
+    this.maxDepth = maxDepth;
+    // Direction the row is being moved.
+    this.direction = '';
+    if (this.indentEnabled) {
+      this.indents = $tableRow.find('.js-indentation').length;
+      this.children = this.findChildren(addClasses);
+      this.group = $.merge(this.group, this.children);
+      // Find the depth of this entire group.
+      for (var n = 0; n < this.group.length; n++) {
+        this.groupDepth = Math.max($(this.group[n]).find('.js-indentation').length, this.groupDepth);
+      }
+    }
+  };
+
+  /**
+   * Find all children of rowObject by indentation.
+   *
+   * @param {bool} addClasses
+   *   Whether we want to add classes to this row to indicate child
+   *   relationships.
+   *
+   * @return {Array}
+   */
+  Drupal.tableDrag.prototype.row.prototype.findChildren = function (addClasses) {
+    var parentIndentation = this.indents;
+    var currentRow = $(this.element, this.table).next('tr.draggable');
+    var rows = [];
+    var child = 0;
+
+    function rowIndentation(indentNum, el) {
+      var self = $(el);
+      if (child === 1 && (indentNum === parentIndentation)) {
+        self.addClass('tree-child-first');
+      }
+      if (indentNum === parentIndentation) {
+        self.addClass('tree-child');
+      }
+      else if (indentNum > parentIndentation) {
+        self.addClass('tree-child-horizontal');
+      }
+    }
+
+    while (currentRow.length) {
+      // A greater indentation indicates this is a child.
+      if (currentRow.find('.js-indentation').length > parentIndentation) {
+        child++;
+        rows.push(currentRow[0]);
+        if (addClasses) {
+          currentRow.find('.js-indentation').each(rowIndentation);
+        }
+      }
+      else {
+        break;
+      }
+      currentRow = currentRow.next('tr.draggable');
+    }
+    if (addClasses && rows.length) {
+      $(rows[rows.length - 1]).find('.js-indentation:nth-child(' + (parentIndentation + 1) + ')').addClass('tree-child-last');
+    }
+    return rows;
+  };
+
+  /**
+   * Ensure that two rows are allowed to be swapped.
+   *
+   * @param {HTMLElement} row
+   *   DOM object for the row being considered for swapping.
+   *
+   * @return {bool}
+   */
+  Drupal.tableDrag.prototype.row.prototype.isValidSwap = function (row) {
+    var $row = $(row);
+    if (this.indentEnabled) {
+      var prevRow;
+      var nextRow;
+      if (this.direction === 'down') {
+        prevRow = row;
+        nextRow = $row.next('tr').get(0);
+      }
+      else {
+        prevRow = $row.prev('tr').get(0);
+        nextRow = row;
+      }
+      this.interval = this.validIndentInterval(prevRow, nextRow);
+
+      // We have an invalid swap if the valid indentations interval is empty.
+      if (this.interval.min > this.interval.max) {
+        return false;
+      }
+    }
+
+    // Do not let an un-draggable first row have anything put before it.
+    if (this.table.tBodies[0].rows[0] === row && $row.is(':not(.draggable)')) {
+      return false;
+    }
+
+    return true;
+  };
+
+  /**
+   * Perform the swap between two rows.
+   *
+   * @param {string} position
+   *   Whether the swap will occur 'before' or 'after' the given row.
+   * @param {HTMLElement} row
+   *   DOM element what will be swapped with the row group.
+   */
+  Drupal.tableDrag.prototype.row.prototype.swap = function (position, row) {
+    // Makes sure only DOM object are passed to Drupal.detachBehaviors().
+    this.group.forEach(function (row) {
+      Drupal.detachBehaviors(row, drupalSettings, 'move');
+    });
+    $(row)[position](this.group);
+    // Makes sure only DOM object are passed to Drupal.attachBehaviors()s.
+    this.group.forEach(function (row) {
+      Drupal.attachBehaviors(row, drupalSettings);
+    });
+    this.changed = true;
+    this.onSwap(row);
+  };
+
+  /**
+   * Determine the valid indentations interval for the row at a given position.
+   *
+   * @param {?HTMLElement} prevRow
+   *   DOM object for the row before the tested position
+   *   (or null for first position in the table).
+   * @param {?HTMLElement} nextRow
+   *   DOM object for the row after the tested position
+   *   (or null for last position in the table).
+   *
+   * @return {{min: number, max: number}}
+   */
+  Drupal.tableDrag.prototype.row.prototype.validIndentInterval = function (prevRow, nextRow) {
+    var $prevRow = $(prevRow);
+    var minIndent;
+    var maxIndent;
+
+    // Minimum indentation:
+    // Do not orphan the next row.
+    minIndent = nextRow ? $(nextRow).find('.js-indentation').length : 0;
+
+    // Maximum indentation:
+    if (!prevRow || $prevRow.is(':not(.draggable)') || $(this.element).is('.tabledrag-root')) {
+      // Do not indent:
+      // - the first row in the table,
+      // - rows dragged below a non-draggable row,
+      // - 'root' rows.
+      maxIndent = 0;
+    }
+    else {
+      // Do not go deeper than as a child of the previous row.
+      maxIndent = $prevRow.find('.js-indentation').length + ($prevRow.is('.tabledrag-leaf') ? 0 : 1);
+      // Limit by the maximum allowed depth for the table.
+      if (this.maxDepth) {
+        maxIndent = Math.min(maxIndent, this.maxDepth - (this.groupDepth - this.indents));
+      }
+    }
+
+    return {min: minIndent, max: maxIndent};
+  };
+
+  /**
+   * Indent a row within the legal bounds of the table.
+   *
+   * @param {number} indentDiff
+   *   The number of additional indentations proposed for the row (can be
+   *   positive or negative). This number will be adjusted to nearest valid
+   *   indentation level for the row.
+   *
+   * @return {number}
+   */
+  Drupal.tableDrag.prototype.row.prototype.indent = function (indentDiff) {
+    var $group = $(this.group);
+    // Determine the valid indentations interval if not available yet.
+    if (!this.interval) {
+      var prevRow = $(this.element).prev('tr').get(0);
+      var nextRow = $group.eq(-1).next('tr').get(0);
+      this.interval = this.validIndentInterval(prevRow, nextRow);
+    }
+
+    // Adjust to the nearest valid indentation.
+    var indent = this.indents + indentDiff;
+    indent = Math.max(indent, this.interval.min);
+    indent = Math.min(indent, this.interval.max);
+    indentDiff = indent - this.indents;
+
+    for (var n = 1; n <= Math.abs(indentDiff); n++) {
+      // Add or remove indentations.
+      if (indentDiff < 0) {
+        $group.find('.js-indentation:first-of-type').remove();
+        this.indents--;
+      }
+      else {
+        $group.find('td:first-of-type').prepend(Drupal.theme('tableDragIndentation'));
+        this.indents++;
+      }
+    }
+    if (indentDiff) {
+      // Update indentation for this row.
+      this.changed = true;
+      this.groupDepth += indentDiff;
+      this.onIndent();
+    }
+
+    return indentDiff;
+  };
+
+  /**
+   * Find all siblings for a row.
+   *
+   * According to its subgroup or indentation. Note that the passed-in row is
+   * included in the list of siblings.
+   *
+   * @param {object} rowSettings
+   *   The field settings we're using to identify what constitutes a sibling.
+   *
+   * @return {Array}
+   */
+  Drupal.tableDrag.prototype.row.prototype.findSiblings = function (rowSettings) {
+    var siblings = [];
+    var directions = ['prev', 'next'];
+    var rowIndentation = this.indents;
+    var checkRowIndentation;
+    for (var d = 0; d < directions.length; d++) {
+      var checkRow = $(this.element)[directions[d]]();
+      while (checkRow.length) {
+        // Check that the sibling contains a similar target field.
+        if (checkRow.find('.' + rowSettings.target)) {
+          // Either add immediately if this is a flat table, or check to ensure
+          // that this row has the same level of indentation.
+          if (this.indentEnabled) {
+            checkRowIndentation = checkRow.find('.js-indentation').length;
+          }
+
+          if (!(this.indentEnabled) || (checkRowIndentation === rowIndentation)) {
+            siblings.push(checkRow[0]);
+          }
+          else if (checkRowIndentation < rowIndentation) {
+            // No need to keep looking for siblings when we get to a parent.
+            break;
+          }
+        }
+        else {
+          break;
+        }
+        checkRow = checkRow[directions[d]]();
+      }
+      // Since siblings are added in reverse order for previous, reverse the
+      // completed list of previous siblings. Add the current row and continue.
+      if (directions[d] === 'prev') {
+        siblings.reverse();
+        siblings.push(this.element);
+      }
+    }
+    return siblings;
+  };
+
+  /**
+   * Remove indentation helper classes from the current row group.
+   */
+  Drupal.tableDrag.prototype.row.prototype.removeIndentClasses = function () {
+    for (var n in this.children) {
+      if (this.children.hasOwnProperty(n)) {
+        $(this.children[n]).find('.js-indentation')
+          .removeClass('tree-child')
+          .removeClass('tree-child-first')
+          .removeClass('tree-child-last')
+          .removeClass('tree-child-horizontal');
+      }
+    }
+  };
+
+  /**
+   * Add an asterisk or other marker to the changed row.
+   */
+  Drupal.tableDrag.prototype.row.prototype.markChanged = function () {
+    var marker = Drupal.theme('tableDragChangedMarker');
+    var cell = $(this.element).find('td:first-of-type');
+    if (cell.find('abbr.tabledrag-changed').length === 0) {
+      cell.append(marker);
+    }
+  };
+
+  /**
+   * Stub function. Allows a custom handler when a row is indented.
+   *
+   * @return {?bool}
+   */
+  Drupal.tableDrag.prototype.row.prototype.onIndent = function () {
+    return null;
+  };
+
+  /**
+   * Stub function. Allows a custom handler when a row is swapped.
+   *
+   * @param {HTMLElement} swappedRow
+   *
+   * @return {?bool}
+   */
+  Drupal.tableDrag.prototype.row.prototype.onSwap = function (swappedRow) {
+    return null;
+  };
+
+  $.extend(Drupal.theme, /** @lends Drupal.theme */{
+
+    /**
+     * @return {string}
+     */
+    tableDragChangedMarker: function () {
+      return '<abbr class="warning tabledrag-changed" title="' + Drupal.t('Changed') + '">*</abbr>';
+    },
+
+    /**
+     * @return {string}
+     */
+    tableDragIndentation: function () {
+      return '<div class="js-indentation indentation">&nbsp;</div>';
+    },
+
+    /**
+     * @return {string}
+     */
+    tableDragChangedWarning: function () {
+      return '<div class="tabledrag-changed-warning messages messages--warning" role="alert">' + Drupal.theme('tableDragChangedMarker') + ' ' + Drupal.t('You have unsaved changes.') + '</div>';
+    }
+  });
+
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/themes/stable/js/core/tableheader.js b/core/themes/stable/js/core/tableheader.js
new file mode 100644
index 0000000..7a21290
--- /dev/null
+++ b/core/themes/stable/js/core/tableheader.js
@@ -0,0 +1,308 @@
+/**
+ * @file
+ * Sticky table headers.
+ */
+
+(function ($, Drupal, displace) {
+
+  "use strict";
+
+  /**
+   * Attaches sticky table headers.
+   *
+   * @type {Drupal~behavior}
+   */
+  Drupal.behaviors.tableHeader = {
+    attach: function (context) {
+      $(window).one('scroll.TableHeaderInit', {context: context}, tableHeaderInitHandler);
+    }
+  };
+
+  function scrollValue(position) {
+    return document.documentElement[position] || document.body[position];
+  }
+
+  // Select and initialize sticky table headers.
+  function tableHeaderInitHandler(e) {
+    var $tables = $(e.data.context).find('table.sticky-enabled').once('tableheader');
+    var il = $tables.length;
+    for (var i = 0; i < il; i++) {
+      TableHeader.tables.push(new TableHeader($tables[i]));
+    }
+    forTables('onScroll');
+  }
+
+  // Helper method to loop through tables and execute a method.
+  function forTables(method, arg) {
+    var tables = TableHeader.tables;
+    var il = tables.length;
+    for (var i = 0; i < il; i++) {
+      tables[i][method](arg);
+    }
+  }
+
+  function tableHeaderResizeHandler(e) {
+    forTables('recalculateSticky');
+  }
+
+  function tableHeaderOnScrollHandler(e) {
+    forTables('onScroll');
+  }
+
+  function tableHeaderOffsetChangeHandler(e, offsets) {
+    forTables('stickyPosition', offsets.top);
+  }
+
+  // Bind event that need to change all tables.
+  $(window).on({
+
+    /**
+     * When resizing table width can change, recalculate everything.
+     *
+     * @ignore
+     */
+    'resize.TableHeader': tableHeaderResizeHandler,
+
+    /**
+     * Bind only one event to take care of calling all scroll callbacks.
+     *
+     * @ignore
+     */
+    'scroll.TableHeader': tableHeaderOnScrollHandler
+  });
+  // Bind to custom Drupal events.
+  $(document).on({
+
+    /**
+     * Recalculate columns width when window is resized and when show/hide
+     * weight is triggered.
+     *
+     * @ignore
+     */
+    'columnschange.TableHeader': tableHeaderResizeHandler,
+
+    /**
+     * Recalculate TableHeader.topOffset when viewport is resized.
+     *
+     * @ignore
+     */
+    'drupalViewportOffsetChange.TableHeader': tableHeaderOffsetChangeHandler
+  });
+
+  /**
+   * Constructor for the tableHeader object. Provides sticky table headers.
+   *
+   * TableHeader will make the current table header stick to the top of the page
+   * if the table is very long.
+   *
+   * @constructor Drupal.TableHeader
+   *
+   * @param {HTMLElement} table
+   *   DOM object for the table to add a sticky header to.
+   *
+   * @listens event:columnschange
+   */
+  function TableHeader(table) {
+    var $table = $(table);
+
+    /**
+     * @name Drupal.TableHeader#$originalTable
+     *
+     * @type {HTMLElement}
+     */
+    this.$originalTable = $table;
+
+    /**
+     * @type {jQuery}
+     */
+    this.$originalHeader = $table.children('thead');
+
+    /**
+     * @type {jQuery}
+     */
+    this.$originalHeaderCells = this.$originalHeader.find('> tr > th');
+
+    /**
+     * @type {null|bool}
+     */
+    this.displayWeight = null;
+    this.$originalTable.addClass('sticky-table');
+    this.tableHeight = $table[0].clientHeight;
+    this.tableOffset = this.$originalTable.offset();
+
+    // React to columns change to avoid making checks in the scroll callback.
+    this.$originalTable.on('columnschange', {tableHeader: this}, function (e, display) {
+      var tableHeader = e.data.tableHeader;
+      if (tableHeader.displayWeight === null || tableHeader.displayWeight !== display) {
+        tableHeader.recalculateSticky();
+      }
+      tableHeader.displayWeight = display;
+    });
+
+    // Create and display sticky header.
+    this.createSticky();
+  }
+
+  /**
+   * Store the state of TableHeader.
+   */
+  $.extend(TableHeader, /** @lends Drupal.TableHeader */{
+
+    /**
+     * This will store the state of all processed tables.
+     *
+     * @type {Array.<Drupal.TableHeader>}
+     */
+    tables: []
+  });
+
+  /**
+   * Extend TableHeader prototype.
+   */
+  $.extend(TableHeader.prototype, /** @lends Drupal.TableHeader# */{
+
+    /**
+     * Minimum height in pixels for the table to have a sticky header.
+     *
+     * @type {number}
+     */
+    minHeight: 100,
+
+    /**
+     * Absolute position of the table on the page.
+     *
+     * @type {?Drupal~displaceOffset}
+     */
+    tableOffset: null,
+
+    /**
+     * Absolute position of the table on the page.
+     *
+     * @type {?number}
+     */
+    tableHeight: null,
+
+    /**
+     * Boolean storing the sticky header visibility state.
+     *
+     * @type {bool}
+     */
+    stickyVisible: false,
+
+    /**
+     * Create the duplicate header.
+     */
+    createSticky: function () {
+      // Clone the table header so it inherits original jQuery properties.
+      var $stickyHeader = this.$originalHeader.clone(true);
+      // Hide the table to avoid a flash of the header clone upon page load.
+      this.$stickyTable = $('<table class="sticky-header"/>')
+        .css({
+          visibility: 'hidden',
+          position: 'fixed',
+          top: '0px'
+        })
+        .append($stickyHeader)
+        .insertBefore(this.$originalTable);
+
+      this.$stickyHeaderCells = $stickyHeader.find('> tr > th');
+
+      // Initialize all computations.
+      this.recalculateSticky();
+    },
+
+    /**
+     * Set absolute position of sticky.
+     *
+     * @param {number} offsetTop
+     * @param {number} offsetLeft
+     *
+     * @return {jQuery}
+     */
+    stickyPosition: function (offsetTop, offsetLeft) {
+      var css = {};
+      if (typeof offsetTop === 'number') {
+        css.top = offsetTop + 'px';
+      }
+      if (typeof offsetLeft === 'number') {
+        css.left = (this.tableOffset.left - offsetLeft) + 'px';
+      }
+      return this.$stickyTable.css(css);
+    },
+
+    /**
+     * Returns true if sticky is currently visible.
+     *
+     * @return {bool}
+     */
+    checkStickyVisible: function () {
+      var scrollTop = scrollValue('scrollTop');
+      var tableTop = this.tableOffset.top - displace.offsets.top;
+      var tableBottom = tableTop + this.tableHeight;
+      var visible = false;
+
+      if (tableTop < scrollTop && scrollTop < (tableBottom - this.minHeight)) {
+        visible = true;
+      }
+
+      this.stickyVisible = visible;
+      return visible;
+    },
+
+    /**
+     * Check if sticky header should be displayed.
+     *
+     * This function is throttled to once every 250ms to avoid unnecessary
+     * calls.
+     *
+     * @param {jQuery.Event} e
+     */
+    onScroll: function (e) {
+      this.checkStickyVisible();
+      // Track horizontal positioning relative to the viewport.
+      this.stickyPosition(null, scrollValue('scrollLeft'));
+      this.$stickyTable.css('visibility', this.stickyVisible ? 'visible' : 'hidden');
+    },
+
+    /**
+     * Event handler: recalculates position of the sticky table header.
+     *
+     * @param {jQuery.Event} event
+     *   Event being triggered.
+     */
+    recalculateSticky: function (event) {
+      // Update table size.
+      this.tableHeight = this.$originalTable[0].clientHeight;
+
+      // Update offset top.
+      displace.offsets.top = displace.calculateOffset('top');
+      this.tableOffset = this.$originalTable.offset();
+      this.stickyPosition(displace.offsets.top, scrollValue('scrollLeft'));
+
+      // Update columns width.
+      var $that = null;
+      var $stickyCell = null;
+      var display = null;
+      // Resize header and its cell widths.
+      // Only apply width to visible table cells. This prevents the header from
+      // displaying incorrectly when the sticky header is no longer visible.
+      var il = this.$originalHeaderCells.length;
+      for (var i = 0; i < il; i++) {
+        $that = $(this.$originalHeaderCells[i]);
+        $stickyCell = this.$stickyHeaderCells.eq($that.index());
+        display = $that.css('display');
+        if (display !== 'none') {
+          $stickyCell.css({width: $that.css('width'), display: display});
+        }
+        else {
+          $stickyCell.css('display', 'none');
+        }
+      }
+      this.$stickyTable.css('width', this.$originalTable.outerWidth());
+    }
+  });
+
+  // Expose constructor in the public space.
+  Drupal.TableHeader = TableHeader;
+
+}(jQuery, Drupal, window.parent.Drupal.displace));
diff --git a/core/themes/stable/js/core/tableresponsive.js b/core/themes/stable/js/core/tableresponsive.js
new file mode 100644
index 0000000..f2ebd18
--- /dev/null
+++ b/core/themes/stable/js/core/tableresponsive.js
@@ -0,0 +1,168 @@
+/**
+ * @file
+ * Responsive table functionality.
+ */
+
+(function ($, Drupal, window) {
+
+  "use strict";
+
+  /**
+   * Attach the tableResponsive function to {@link Drupal.behaviors}.
+   *
+   * @type {Drupal~behavior}
+   */
+  Drupal.behaviors.tableResponsive = {
+    attach: function (context, settings) {
+      var $tables = $(context).find('table.responsive-enabled').once('tableresponsive');
+      if ($tables.length) {
+        var il = $tables.length;
+        for (var i = 0; i < il; i++) {
+          TableResponsive.tables.push(new TableResponsive($tables[i]));
+        }
+      }
+    }
+  };
+
+  /**
+   * The TableResponsive object optimizes table presentation for screen size.
+   *
+   * A responsive table hides columns at small screen sizes, leaving the most
+   * important columns visible to the end user. Users should not be prevented
+   * from accessing all columns, however. This class adds a toggle to a table
+   * with hidden columns that exposes the columns. Exposing the columns will
+   * likely break layouts, but it provides the user with a means to access
+   * data, which is a guiding principle of responsive design.
+   *
+   * @constructor Drupal.TableResponsive
+   *
+   * @param {HTMLElement} table
+   */
+  function TableResponsive(table) {
+    this.table = table;
+    this.$table = $(table);
+    this.showText = Drupal.t('Show all columns');
+    this.hideText = Drupal.t('Hide lower priority columns');
+    // Store a reference to the header elements of the table so that the DOM is
+    // traversed only once to find them.
+    this.$headers = this.$table.find('th');
+    // Add a link before the table for users to show or hide weight columns.
+    this.$link = $('<button type="button" class="link tableresponsive-toggle"></button>')
+      .attr('title', Drupal.t('Show table cells that were hidden to make the table fit within a small screen.'))
+      .on('click', $.proxy(this, 'eventhandlerToggleColumns'));
+
+    this.$table.before($('<div class="tableresponsive-toggle-columns"></div>').append(this.$link));
+
+    // Attach a resize handler to the window.
+    $(window)
+      .on('resize.tableresponsive', $.proxy(this, 'eventhandlerEvaluateColumnVisibility'))
+      .trigger('resize.tableresponsive');
+  }
+
+  /**
+   * Extend the TableResponsive function with a list of managed tables.
+   */
+  $.extend(TableResponsive, /** @lends Drupal.TableResponsive */{
+
+    /**
+     * Store all created instances.
+     *
+     * @type {Array.<Drupal.TableResponsive>}
+     */
+    tables: []
+  });
+
+  /**
+   * Associates an action link with the table that will show hidden columns.
+   *
+   * Columns are assumed to be hidden if their header has the class priority-low
+   * or priority-medium.
+   */
+  $.extend(TableResponsive.prototype, /** @lends Drupal.TableResponsive# */{
+
+    /**
+     * @param {jQuery.Event} e
+     */
+    eventhandlerEvaluateColumnVisibility: function (e) {
+      var pegged = parseInt(this.$link.data('pegged'), 10);
+      var hiddenLength = this.$headers.filter('.priority-medium:hidden, .priority-low:hidden').length;
+      // If the table has hidden columns, associate an action link with the
+      // table to show the columns.
+      if (hiddenLength > 0) {
+        this.$link.show().text(this.showText);
+      }
+      // When the toggle is pegged, its presence is maintained because the user
+      // has interacted with it. This is necessary to keep the link visible if
+      // the user adjusts screen size and changes the visibility of columns.
+      if (!pegged && hiddenLength === 0) {
+        this.$link.hide().text(this.hideText);
+      }
+    },
+
+    /**
+     * Toggle the visibility of columns based on their priority.
+     *
+     * Columns are classed with either 'priority-low' or 'priority-medium'.
+     *
+     * @param {jQuery.Event} e
+     */
+    eventhandlerToggleColumns: function (e) {
+      e.preventDefault();
+      var self = this;
+      var $hiddenHeaders = this.$headers.filter('.priority-medium:hidden, .priority-low:hidden');
+      this.$revealedCells = this.$revealedCells || $();
+      // Reveal hidden columns.
+      if ($hiddenHeaders.length > 0) {
+        $hiddenHeaders.each(function (index, element) {
+          var $header = $(this);
+          var position = $header.prevAll('th').length;
+          self.$table.find('tbody tr').each(function () {
+            var $cells = $(this).find('td').eq(position);
+            $cells.show();
+            // Keep track of the revealed cells, so they can be hidden later.
+            self.$revealedCells = $().add(self.$revealedCells).add($cells);
+          });
+          $header.show();
+          // Keep track of the revealed headers, so they can be hidden later.
+          self.$revealedCells = $().add(self.$revealedCells).add($header);
+        });
+        this.$link.text(this.hideText).data('pegged', 1);
+      }
+      // Hide revealed columns.
+      else {
+        this.$revealedCells.hide();
+        // Strip the 'display:none' declaration from the style attributes of
+        // the table cells that .hide() added.
+        this.$revealedCells.each(function (index, element) {
+          var $cell = $(this);
+          var properties = $cell.attr('style').split(';');
+          var newProps = [];
+          // The hide method adds display none to the element. The element
+          // should be returned to the same state it was in before the columns
+          // were revealed, so it is necessary to remove the display none value
+          // from the style attribute.
+          var match = /^display\s*\:\s*none$/;
+          for (var i = 0; i < properties.length; i++) {
+            var prop = properties[i];
+            prop.trim();
+            // Find the display:none property and remove it.
+            var isDisplayNone = match.exec(prop);
+            if (isDisplayNone) {
+              continue;
+            }
+            newProps.push(prop);
+          }
+          // Return the rest of the style attribute values to the element.
+          $cell.attr('style', newProps.join(';'));
+        });
+        this.$link.text(this.showText).data('pegged', 0);
+        // Refresh the toggle link.
+        $(window).trigger('resize.tableresponsive');
+      }
+    }
+  });
+
+  // Make the TableResponsive object available in the Drupal namespace.
+  Drupal.TableResponsive = TableResponsive;
+
+})(jQuery, Drupal, window);
diff --git a/core/themes/stable/js/core/tableselect.js b/core/themes/stable/js/core/tableselect.js
new file mode 100644
index 0000000..8a77695
--- /dev/null
+++ b/core/themes/stable/js/core/tableselect.js
@@ -0,0 +1,144 @@
+/**
+ * @file
+ * Table select functionality.
+ */
+
+(function ($, Drupal) {
+
+  "use strict";
+
+  /**
+   * Initialize tableSelects.
+   *
+   * @type {Drupal~behavior}
+   */
+  Drupal.behaviors.tableSelect = {
+    attach: function (context, settings) {
+      // Select the inner-most table in case of nested tables.
+      $(context).find('th.select-all').closest('table').once('table-select').each(Drupal.tableSelect);
+    }
+  };
+
+  /**
+   * Callback used in {@link Drupal.behaviors.tableSelect}.
+   */
+  Drupal.tableSelect = function () {
+    // Do not add a "Select all" checkbox if there are no rows with checkboxes
+    // in the table.
+    if ($(this).find('td input[type="checkbox"]').length === 0) {
+      return;
+    }
+
+    // Keep track of the table, which checkbox is checked and alias the
+    // settings.
+    var table = this;
+    var checkboxes;
+    var lastChecked;
+    var $table = $(table);
+    var strings = {
+      selectAll: Drupal.t('Select all rows in this table'),
+      selectNone: Drupal.t('Deselect all rows in this table')
+    };
+    var updateSelectAll = function (state) {
+      // Update table's select-all checkbox (and sticky header's if available).
+      $table.prev('table.sticky-header').addBack().find('th.select-all input[type="checkbox"]').each(function () {
+        $(this).attr('title', state ? strings.selectNone : strings.selectAll);
+
+        /**
+         * @this {HTMLElement}
+         */
+        this.checked = state;
+      });
+    };
+
+    // Find all <th> with class select-all, and insert the check all checkbox.
+    $table.find('th.select-all').prepend($('<input type="checkbox" class="form-checkbox" />').attr('title', strings.selectAll)).on('click', function (event) {
+      if ($(event.target).is('input[type="checkbox"]')) {
+        // Loop through all checkboxes and set their state to the select all
+        // checkbox' state.
+        checkboxes.each(function () {
+
+          /**
+           * @this {HTMLElement}
+           */
+          this.checked = event.target.checked;
+          // Either add or remove the selected class based on the state of the
+          // check all checkbox.
+
+          /**
+           * @this {HTMLElement}
+           */
+          $(this).closest('tr').toggleClass('selected', this.checked);
+        });
+        // Update the title and the state of the check all box.
+        updateSelectAll(event.target.checked);
+      }
+    });
+
+    // For each of the checkboxes within the table that are not disabled.
+    checkboxes = $table.find('td input[type="checkbox"]:enabled').on('click', function (e) {
+      // Either add or remove the selected class based on the state of the
+      // check all checkbox.
+
+      /**
+       * @this {HTMLElement}
+       */
+      $(this).closest('tr').toggleClass('selected', this.checked);
+
+      // If this is a shift click, we need to highlight everything in the
+      // range. Also make sure that we are actually checking checkboxes
+      // over a range and that a checkbox has been checked or unchecked before.
+      if (e.shiftKey && lastChecked && lastChecked !== e.target) {
+        // We use the checkbox's parent TR to do our range searching.
+        Drupal.tableSelectRange($(e.target).closest('tr')[0], $(lastChecked).closest('tr')[0], e.target.checked);
+      }
+
+      // If all checkboxes are checked, make sure the select-all one is checked
+      // too, otherwise keep unchecked.
+      updateSelectAll((checkboxes.length === checkboxes.filter(':checked').length));
+
+      // Keep track of the last checked checkbox.
+      lastChecked = e.target;
+    });
+
+    // If all checkboxes are checked on page load, make sure the select-all one
+    // is checked too, otherwise keep unchecked.
+    updateSelectAll((checkboxes.length === checkboxes.filter(':checked').length));
+  };
+
+  /**
+   * @param {HTMLElement} from
+   * @param {HTMLElement} to
+   * @param {bool} state
+   */
+  Drupal.tableSelectRange = function (from, to, state) {
+    // We determine the looping mode based on the order of from and to.
+    var mode = from.rowIndex > to.rowIndex ? 'previousSibling' : 'nextSibling';
+
+    // Traverse through the sibling nodes.
+    for (var i = from[mode]; i; i = i[mode]) {
+      var $i;
+      // Make sure that we're only dealing with elements.
+      if (i.nodeType !== 1) {
+        continue;
+      }
+      $i = $(i);
+      // Either add or remove the selected class based on the state of the
+      // target checkbox.
+      $i.toggleClass('selected', state);
+      $i.find('input[type="checkbox"]').prop('checked', state);
+
+      if (to.nodeType) {
+        // If we are at the end of the range, stop.
+        if (i === to) {
+          break;
+        }
+      }
+      // A faster alternative to doing $(i).filter(to).length.
+      else if ($.filter(to, [i]).r.length) {
+        break;
+      }
+    }
+  };
+
+})(jQuery, Drupal);
diff --git a/core/themes/stable/js/core/timezone.js b/core/themes/stable/js/core/timezone.js
new file mode 100644
index 0000000..801d728
--- /dev/null
+++ b/core/themes/stable/js/core/timezone.js
@@ -0,0 +1,76 @@
+/**
+ * @file
+ * Timezone detection.
+ */
+
+(function ($) {
+
+  "use strict";
+
+  /**
+   * Set the client's system time zone as default values of form fields.
+   *
+   * @type {Drupal~behavior}
+   */
+  Drupal.behaviors.setTimezone = {
+    attach: function (context, settings) {
+      var $timezone = $(context).find('.timezone-detect').once('timezone');
+      if ($timezone.length) {
+        var dateString = Date();
+        // In some client environments, date strings include a time zone
+        // abbreviation, between 3 and 5 letters enclosed in parentheses,
+        // which can be interpreted by PHP.
+        var matches = dateString.match(/\(([A-Z]{3,5})\)/);
+        var abbreviation = matches ? matches[1] : 0;
+
+        // For all other client environments, the abbreviation is set to "0"
+        // and the current offset from UTC and daylight saving time status are
+        // used to guess the time zone.
+        var dateNow = new Date();
+        var offsetNow = dateNow.getTimezoneOffset() * -60;
+
+        // Use January 1 and July 1 as test dates for determining daylight
+        // saving time status by comparing their offsets.
+        var dateJan = new Date(dateNow.getFullYear(), 0, 1, 12, 0, 0, 0);
+        var dateJul = new Date(dateNow.getFullYear(), 6, 1, 12, 0, 0, 0);
+        var offsetJan = dateJan.getTimezoneOffset() * -60;
+        var offsetJul = dateJul.getTimezoneOffset() * -60;
+
+        var isDaylightSavingTime;
+        // If the offset from UTC is identical on January 1 and July 1,
+        // assume daylight saving time is not used in this time zone.
+        if (offsetJan === offsetJul) {
+          isDaylightSavingTime = '';
+        }
+        // If the maximum annual offset is equivalent to the current offset,
+        // assume daylight saving time is in effect.
+        else if (Math.max(offsetJan, offsetJul) === offsetNow) {
+          isDaylightSavingTime = 1;
+        }
+        // Otherwise, assume daylight saving time is not in effect.
+        else {
+          isDaylightSavingTime = 0;
+        }
+
+        // Submit request to the system/timezone callback and set the form
+        // field to the response time zone. The client date is passed to the
+        // callback for debugging purposes. Submit a synchronous request to
+        // avoid database errors associated with concurrent requests
+        // during install.
+        var path = 'system/timezone/' + abbreviation + '/' + offsetNow + '/' + isDaylightSavingTime;
+        $.ajax({
+          async: false,
+          url: Drupal.url(path),
+          data: {date: dateString},
+          dataType: 'json',
+          success: function (data) {
+            if (data) {
+              $timezone.val(data);
+            }
+          }
+        });
+      }
+    }
+  };
+
+})(jQuery);
diff --git a/core/themes/stable/js/core/vertical-tabs.js b/core/themes/stable/js/core/vertical-tabs.js
new file mode 100644
index 0000000..3f53cd8
--- /dev/null
+++ b/core/themes/stable/js/core/vertical-tabs.js
@@ -0,0 +1,244 @@
+/**
+ * @file
+ * Define vertical tabs functionality.
+ */
+
+/**
+ * Triggers when form values inside a vertical tab changes.
+ *
+ * This is used to update the summary in vertical tabs in order to know what
+ * are the important fields' values.
+ *
+ * @event summaryUpdated
+ */
+
+(function ($) {
+
+  "use strict";
+
+  /**
+   * This script transforms a set of details into a stack of vertical tabs.
+   *
+   * Each tab may have a summary which can be updated by another
+   * script. For that to work, each details element has an associated
+   * 'verticalTabCallback' (with jQuery.data() attached to the details),
+   * which is called every time the user performs an update to a form
+   * element inside the tab pane.
+   *
+   * @type {Drupal~behavior}
+   */
+  Drupal.behaviors.verticalTabs = {
+    attach: function (context) {
+
+      if (!Drupal.checkWidthBreakpoint()) {
+        return;
+      }
+
+      $(context).find('[data-vertical-tabs-panes]').once('vertical-tabs').each(function () {
+        var $this = $(this).addClass('vertical-tabs__panes');
+        var focusID = $this.find(':hidden.vertical-tabs__active-tab').val();
+        var tab_focus;
+
+        // Check if there are some details that can be converted to
+        // vertical-tabs.
+        var $details = $this.find('> details');
+        if ($details.length === 0) {
+          return;
+        }
+
+        // Create the tab column.
+        var tab_list = $('<ul class="vertical-tabs__menu"></ul>');
+        $this.wrap('<div class="vertical-tabs clearfix"></div>').before(tab_list);
+
+        // Transform each details into a tab.
+        $details.each(function () {
+          var $that = $(this);
+          var vertical_tab = new Drupal.verticalTab({
+            title: $that.find('> summary').text(),
+            details: $that
+          });
+          tab_list.append(vertical_tab.item);
+          $that
+            .removeClass('collapsed')
+            // prop() can't be used on browsers not supporting details element,
+            // the style won't apply to them if prop() is used.
+            .attr('open', true)
+            .addClass('vertical-tabs__pane')
+            .data('verticalTab', vertical_tab);
+          if (this.id === focusID) {
+            tab_focus = $that;
+          }
+        });
+
+        $(tab_list).find('> li').eq(0).addClass('first');
+        $(tab_list).find('> li').eq(-1).addClass('last');
+
+        if (!tab_focus) {
+          // If the current URL has a fragment and one of the tabs contains an
+          // element that matches the URL fragment, activate that tab.
+          var $locationHash = $this.find(window.location.hash);
+          if (window.location.hash && $locationHash.length) {
+            tab_focus = $locationHash.closest('.vertical-tabs__pane');
+          }
+          else {
+            tab_focus = $this.find('> .vertical-tabs__pane').eq(0);
+          }
+        }
+        if (tab_focus.length) {
+          tab_focus.data('verticalTab').focus();
+        }
+      });
+    }
+  };
+
+  /**
+   * The vertical tab object represents a single tab within a tab group.
+   *
+   * @constructor
+   *
+   * @param {object} settings
+   * @param {string} settings.title
+   *   The name of the tab.
+   * @param {jQuery} settings.details
+   *   The jQuery object of the details element that is the tab pane.
+   *
+   * @fires event:summaryUpdated
+   *
+   * @listens event:summaryUpdated
+   */
+  Drupal.verticalTab = function (settings) {
+    var self = this;
+    $.extend(this, settings, Drupal.theme('verticalTab', settings));
+
+    this.link.attr('href', '#' + settings.details.attr('id'));
+
+    this.link.on('click', function (e) {
+      e.preventDefault();
+      self.focus();
+    });
+
+    // Keyboard events added:
+    // Pressing the Enter key will open the tab pane.
+    this.link.on('keydown', function (event) {
+      if (event.keyCode === 13) {
+        event.preventDefault();
+        self.focus();
+        // Set focus on the first input field of the visible details/tab pane.
+        $(".vertical-tabs__pane :input:visible:enabled").eq(0).trigger('focus');
+      }
+    });
+
+    this.details
+      .on('summaryUpdated', function () {
+        self.updateSummary();
+      })
+      .trigger('summaryUpdated');
+  };
+
+  Drupal.verticalTab.prototype = {
+
+    /**
+     * Displays the tab's content pane.
+     */
+    focus: function () {
+      this.details
+        .siblings('.vertical-tabs__pane')
+        .each(function () {
+          var tab = $(this).data('verticalTab');
+          tab.details.hide();
+          tab.item.removeClass('is-selected');
+        })
+        .end()
+        .show()
+        .siblings(':hidden.vertical-tabs__active-tab')
+        .val(this.details.attr('id'));
+      this.item.addClass('is-selected');
+      // Mark the active tab for screen readers.
+      $('#active-vertical-tab').remove();
+      this.link.append('<span id="active-vertical-tab" class="visually-hidden">' + Drupal.t('(active tab)') + '</span>');
+    },
+
+    /**
+     * Updates the tab's summary.
+     */
+    updateSummary: function () {
+      this.summary.html(this.details.drupalGetSummary());
+    },
+
+    /**
+     * Shows a vertical tab pane.
+     *
+     * @return {Drupal.verticalTab}
+     */
+    tabShow: function () {
+      // Display the tab.
+      this.item.show();
+      // Show the vertical tabs.
+      this.item.closest('.js-form-type-vertical-tabs').show();
+      // Update .first marker for items. We need recurse from parent to retain
+      // the actual DOM element order as jQuery implements sortOrder, but not
+      // as public method.
+      this.item.parent().children('.vertical-tabs__menu-item').removeClass('first')
+        .filter(':visible').eq(0).addClass('first');
+      // Display the details element.
+      this.details.removeClass('vertical-tab--hidden').show();
+      // Focus this tab.
+      this.focus();
+      return this;
+    },
+
+    /**
+     * Hides a vertical tab pane.
+     *
+     * @return {Drupal.verticalTab}
+     */
+    tabHide: function () {
+      // Hide this tab.
+      this.item.hide();
+      // Update .first marker for items. We need recurse from parent to retain
+      // the actual DOM element order as jQuery implements sortOrder, but not
+      // as public method.
+      this.item.parent().children('.vertical-tabs__menu-item').removeClass('first')
+        .filter(':visible').eq(0).addClass('first');
+      // Hide the details element.
+      this.details.addClass('vertical-tab--hidden').hide();
+      // Focus the first visible tab (if there is one).
+      var $firstTab = this.details.siblings('.vertical-tabs__pane:not(.vertical-tab--hidden)').eq(0);
+      if ($firstTab.length) {
+        $firstTab.data('verticalTab').focus();
+      }
+      // Hide the vertical tabs (if no tabs remain).
+      else {
+        this.item.closest('.js-form-type-vertical-tabs').hide();
+      }
+      return this;
+    }
+  };
+
+  /**
+   * Theme function for a vertical tab.
+   *
+   * @param {object} settings
+   *   An object with the following keys:
+   * @param {string} settings.title
+   *   The name of the tab.
+   *
+   * @return {object}
+   *   This function has to return an object with at least these keys:
+   *   - item: The root tab jQuery element
+   *   - link: The anchor tag that acts as the clickable area of the tab
+   *       (jQuery version)
+   *   - summary: The jQuery element that contains the tab summary
+   */
+  Drupal.theme.verticalTab = function (settings) {
+    var tab = {};
+    tab.item = $('<li class="vertical-tabs__menu-item" tabindex="-1"></li>')
+      .append(tab.link = $('<a href="#"></a>')
+        .append(tab.title = $('<strong class="vertical-tabs__menu-item-title"></strong>').text(settings.title))
+        .append(tab.summary = $('<span class="vertical-tabs__menu-item-summary"></span>')
+        )
+      );
+    return tab;
+  };
+
+})(jQuery);
diff --git a/core/themes/stable/js/editor/editor.admin.js b/core/themes/stable/js/editor/editor.admin.js
new file mode 100644
index 0000000..bc7ebcc
--- /dev/null
+++ b/core/themes/stable/js/editor/editor.admin.js
@@ -0,0 +1,935 @@
+/**
+ * @file
+ * Provides a JavaScript API to broadcast text editor configuration changes.
+ *
+ * Filter implementations may listen to the drupalEditorFeatureAdded,
+ * drupalEditorFeatureRemoved, and drupalEditorFeatureRemoved events on document
+ * to automatically adjust their settings based on the editor configuration.
+ */
+
+(function ($, _, Drupal, document) {
+
+  "use strict";
+
+  /**
+   * Editor configuration namespace.
+   *
+   * @namespace
+   */
+  Drupal.editorConfiguration = {
+
+    /**
+     * Must be called by a specific text editor's configuration whenever a
+     * feature is added by the user.
+     *
+     * Triggers the drupalEditorFeatureAdded event on the document, which
+     * receives a {@link Drupal.EditorFeature} object.
+     *
+     * @param {Drupal.EditorFeature} feature
+     *   A text editor feature object.
+     *
+     * @fires event:drupalEditorFeatureAdded
+     */
+    addedFeature: function (feature) {
+      $(document).trigger('drupalEditorFeatureAdded', feature);
+    },
+
+    /**
+     * Must be called by a specific text editor's configuration whenever a
+     * feature is removed by the user.
+     *
+     * Triggers the drupalEditorFeatureRemoved event on the document, which
+     * receives a {@link Drupal.EditorFeature} object.
+     *
+     * @param {Drupal.EditorFeature} feature
+     *   A text editor feature object.
+     *
+     * @fires event:drupalEditorFeatureRemoved
+     */
+    removedFeature: function (feature) {
+      $(document).trigger('drupalEditorFeatureRemoved', feature);
+    },
+
+    /**
+     * Must be called by a specific text editor's configuration whenever a
+     * feature is modified, i.e. has different rules.
+     *
+     * For example when the "Bold" button is configured to use the `<b>` tag
+     * instead of the `<strong>` tag.
+     *
+     * Triggers the drupalEditorFeatureModified event on the document, which
+     * receives a {@link Drupal.EditorFeature} object.
+     *
+     * @param {Drupal.EditorFeature} feature
+     *   A text editor feature object.
+     *
+     * @fires event:drupalEditorFeatureModified
+     */
+    modifiedFeature: function (feature) {
+      $(document).trigger('drupalEditorFeatureModified', feature);
+    },
+
+    /**
+     * May be called by a specific text editor's configuration whenever a
+     * feature is being added, to check whether it would require the filter
+     * settings to be updated.
+     *
+     * The canonical use case is when a text editor is being enabled:
+     * preferably
+     * this would not cause the filter settings to be changed; rather, the
+     * default set of buttons (features) for the text editor should adjust
+     * itself to not cause filter setting changes.
+     *
+     * Note: for filters to integrate with this functionality, it is necessary
+     * that they implement
+     * `Drupal.filterSettingsForEditors[filterID].getRules()`.
+     *
+     * @param {Drupal.EditorFeature} feature
+     *   A text editor feature object.
+     *
+     * @return {bool}
+     *   Whether the given feature is allowed by the current filters.
+     */
+    featureIsAllowedByFilters: function (feature) {
+
+      /**
+       * Generate the universe U of possible values that can result from the
+       * feature's rules' requirements.
+       *
+       * This generates an object of this form:
+       *   var universe = {
+       *     a: {
+       *       'touchedByAllowedPropertyRule': false,
+       *       'tag': false,
+       *       'attributes:href': false,
+       *       'classes:external': false,
+       *     },
+       *     strong: {
+       *       'touchedByAllowedPropertyRule': false,
+       *       'tag': false,
+       *     },
+       *     img: {
+       *       'touchedByAllowedPropertyRule': false,
+       *       'tag': false,
+       *       'attributes:src': false
+       *     }
+       *   };
+       *
+       * In this example, the given text editor feature resulted in the above
+       * universe, which shows that it must be allowed to generate the a,
+       * strong and img tags. For the a tag, it must be able to set the "href"
+       * attribute and the "external" class. For the strong tag, no further
+       * properties are required. For the img tag, the "src" attribute is
+       * required. The "tag" key is used to track whether that tag was
+       * explicitly allowed by one of the filter's rules. The
+       * "touchedByAllowedPropertyRule" key is used for state tracking that is
+       * essential for filterStatusAllowsFeature() to be able to reason: when
+       * all of a filter's rules have been applied, and none of the forbidden
+       * rules matched (which would have resulted in early termination) yet the
+       * universe has not been made empty (which would be the end result if
+       * everything in the universe were explicitly allowed), then this piece
+       * of state data enables us to determine whether a tag whose properties
+       * were not all explicitly allowed are in fact still allowed, because its
+       * tag was explicitly allowed and there were no filter rules applying
+       * "allowed tag property value" restrictions for this particular tag.
+       *
+       * @param {object} feature
+       *   The feature in question.
+       *
+       * @return {object}
+       *   The universe generated.
+       *
+       * @see findPropertyValueOnTag()
+       * @see filterStatusAllowsFeature()
+       */
+      function generateUniverseFromFeatureRequirements(feature) {
+        var properties = ['attributes', 'styles', 'classes'];
+        var universe = {};
+
+        for (var r = 0; r < feature.rules.length; r++) {
+          var featureRule = feature.rules[r];
+
+          // For each tag required by this feature rule, create a basic entry in
+          // the universe.
+          var requiredTags = featureRule.required.tags;
+          for (var t = 0; t < requiredTags.length; t++) {
+            universe[requiredTags[t]] = {
+              // Whether this tag was allowed or not.
+              tag: false,
+              // Whether any filter rule that applies to this tag had an allowed
+              // property rule. i.e. will become true if >=1 filter rule has >=1
+              // allowed property rule.
+              touchedByAllowedPropertyRule: false,
+              // Analogous, but for forbidden property rule.
+              touchedBytouchedByForbiddenPropertyRule: false
+            };
+          }
+
+          // If no required properties are defined for this rule, we can move on
+          // to the next feature.
+          if (emptyProperties(featureRule.required)) {
+            continue;
+          }
+
+          // Expand the existing universe, assume that each tags' property
+          // value is disallowed. If the filter rules allow everything in the
+          // feature's universe, then the feature is allowed.
+          for (var p = 0; p < properties.length; p++) {
+            var property = properties[p];
+            for (var pv = 0; pv < featureRule.required[property].length; pv++) {
+              var propertyValue = featureRule.required[property];
+              universe[requiredTags][property + ':' + propertyValue] = false;
+            }
+          }
+        }
+
+        return universe;
+      }
+
+      /**
+       * Provided a section of a feature or filter rule, checks if no property
+       * values are defined for all properties: attributes, classes and styles.
+       *
+       * @param {object} section
+       *   The section to check.
+       *
+       * @return {bool}
+       *   Returns true if the section has empty properties, false otherwise.
+       */
+      function emptyProperties(section) {
+        return section.attributes.length === 0 && section.classes.length === 0 && section.styles.length === 0;
+      }
+
+      /**
+       * Calls findPropertyValueOnTag on the given tag for every property value
+       * that is listed in the "propertyValues" parameter. Supports the wildcard
+       * tag.
+       *
+       * @param {object} universe
+       *   The universe to check.
+       * @param {string} tag
+       *   The tag to look for.
+       * @param {string} property
+       *   The property to check.
+       * @param {Array} propertyValues
+       *   Values of the property to check.
+       * @param {bool} allowing
+       *   Whether to update the universe or not.
+       *
+       * @return {bool}
+       *   Returns true if found, false otherwise.
+       */
+      function findPropertyValuesOnTag(universe, tag, property, propertyValues, allowing) {
+        // Detect the wildcard case.
+        if (tag === '*') {
+          return findPropertyValuesOnAllTags(universe, property, propertyValues, allowing);
+        }
+
+        var atLeastOneFound = false;
+        _.each(propertyValues, function (propertyValue) {
+          if (findPropertyValueOnTag(universe, tag, property, propertyValue, allowing)) {
+            atLeastOneFound = true;
+          }
+        });
+        return atLeastOneFound;
+      }
+
+      /**
+       * Calls findPropertyValuesOnAllTags for all tags in the universe.
+       *
+       * @param {object} universe
+       *   The universe to check.
+       * @param {string} property
+       *   The property to check.
+       * @param {Array} propertyValues
+       *   Values of the property to check.
+       * @param {bool} allowing
+       *   Whether to update the universe or not.
+       *
+       * @return {bool}
+       *   Returns true if found, false otherwise.
+       */
+      function findPropertyValuesOnAllTags(universe, property, propertyValues, allowing) {
+        var atLeastOneFound = false;
+        _.each(_.keys(universe), function (tag) {
+          if (findPropertyValuesOnTag(universe, tag, property, propertyValues, allowing)) {
+            atLeastOneFound = true;
+          }
+        });
+        return atLeastOneFound;
+      }
+
+      /**
+       * Finds out if a specific property value (potentially containing
+       * wildcards) exists on the given tag. When the "allowing" parameter
+       * equals true, the universe will be updated if that specific property
+       * value exists. Returns true if found, false otherwise.
+       *
+       * @param {object} universe
+       *   The universe to check.
+       * @param {string} tag
+       *   The tag to look for.
+       * @param {string} property
+       *   The property to check.
+       * @param {string} propertyValue
+       *   The property value to check.
+       * @param {bool} allowing
+       *   Whether to update the universe or not.
+       *
+       * @return {bool}
+       *   Returns true if found, false otherwise.
+       */
+      function findPropertyValueOnTag(universe, tag, property, propertyValue, allowing) {
+        // If the tag does not exist in the universe, then it definitely can't
+        // have this specific property value.
+        if (!_.has(universe, tag)) {
+          return false;
+        }
+
+        var key = property + ':' + propertyValue;
+
+        // Track whether a tag was touched by a filter rule that allows specific
+        // property values on this particular tag.
+        // @see generateUniverseFromFeatureRequirements
+        if (allowing) {
+          universe[tag].touchedByAllowedPropertyRule = true;
+        }
+
+        // The simple case: no wildcard in property value.
+        if (_.indexOf(propertyValue, '*') === -1) {
+          if (_.has(universe, tag) && _.has(universe[tag], key)) {
+            if (allowing) {
+              universe[tag][key] = true;
+            }
+            return true;
+          }
+          return false;
+        }
+        // The complex case: wildcard in property value.
+        else {
+          var atLeastOneFound = false;
+          var regex = key.replace(/\*/g, "[^ ]*");
+          _.each(_.keys(universe[tag]), function (key) {
+            if (key.match(regex)) {
+              atLeastOneFound = true;
+              if (allowing) {
+                universe[tag][key] = true;
+              }
+            }
+          });
+          return atLeastOneFound;
+        }
+      }
+
+      /**
+       * Deletes a tag from the universe if the tag itself and each of its
+       * properties are marked as allowed.
+       *
+       * @param {object} universe
+       *   The universe to delete from.
+       * @param {string} tag
+       *   The tag to check.
+       *
+       * @return {bool}
+       *   Whether something was deleted from the universe.
+       */
+      function deleteFromUniverseIfAllowed(universe, tag) {
+        // Detect the wildcard case.
+        if (tag === '*') {
+          return deleteAllTagsFromUniverseIfAllowed(universe);
+        }
+        if (_.has(universe, tag) && _.every(_.omit(universe[tag], 'touchedByAllowedPropertyRule'))) {
+          delete universe[tag];
+          return true;
+        }
+        return false;
+      }
+
+      /**
+       * Calls deleteFromUniverseIfAllowed for all tags in the universe.
+       *
+       * @param {object} universe
+       *   The universe to delete from.
+       *
+       * @return {bool}
+       *   Whether something was deleted from the universe.
+       */
+      function deleteAllTagsFromUniverseIfAllowed(universe) {
+        var atLeastOneDeleted = false;
+        _.each(_.keys(universe), function (tag) {
+          if (deleteFromUniverseIfAllowed(universe, tag)) {
+            atLeastOneDeleted = true;
+          }
+        });
+        return atLeastOneDeleted;
+      }
+
+      /**
+       * Checks if any filter rule forbids either a tag or a tag property value
+       * that exists in the universe.
+       *
+       * @param {object} universe
+       *   Universe to check.
+       * @param {object} filterStatus
+       *   Filter status to use for check.
+       *
+       * @return {bool}
+       *   Whether any filter rule forbids something in the universe.
+       */
+      function anyForbiddenFilterRuleMatches(universe, filterStatus) {
+        var properties = ['attributes', 'styles', 'classes'];
+
+        // Check if a tag in the universe is forbidden.
+        var allRequiredTags = _.keys(universe);
+        var filterRule;
+        for (var i = 0; i < filterStatus.rules.length; i++) {
+          filterRule = filterStatus.rules[i];
+          if (filterRule.allow === false) {
+            if (_.intersection(allRequiredTags, filterRule.tags).length > 0) {
+              return true;
+            }
+          }
+        }
+
+        // Check if a property value of a tag in the universe is forbidden.
+        // For all filter rules…
+        for (var n = 0; n < filterStatus.rules.length; n++) {
+          filterRule = filterStatus.rules[n];
+          // … if there are tags with restricted property values …
+          if (filterRule.restrictedTags.tags.length && !emptyProperties(filterRule.restrictedTags.forbidden)) {
+            // … for all those tags …
+            for (var j = 0; j < filterRule.restrictedTags.tags.length; j++) {
+              var tag = filterRule.restrictedTags.tags[j];
+              // … then iterate over all properties …
+              for (var k = 0; k < properties.length; k++) {
+                var property = properties[k];
+                // … and return true if just one of the forbidden property
+                // values for this tag and property is listed in the universe.
+                if (findPropertyValuesOnTag(universe, tag, property, filterRule.restrictedTags.forbidden[property], false)) {
+                  return true;
+                }
+              }
+            }
+          }
+        }
+
+        return false;
+      }
+
+      /**
+       * Applies every filter rule's explicit allowing of a tag or a tag
+       * property value to the universe. Whenever both the tag and all of its
+       * required property values are marked as explicitly allowed, they are
+       * deleted from the universe.
+       *
+       * @param {object} universe
+       *   Universe to delete from.
+       * @param {object} filterStatus
+       *   The filter status in question.
+       */
+      function markAllowedTagsAndPropertyValues(universe, filterStatus) {
+        var properties = ['attributes', 'styles', 'classes'];
+
+        // Check if a tag in the universe is allowed.
+        var filterRule;
+        var tag;
+        for (var l = 0; !_.isEmpty(universe) && l < filterStatus.rules.length; l++) {
+          filterRule = filterStatus.rules[l];
+          if (filterRule.allow === true) {
+            for (var m = 0; !_.isEmpty(universe) && m < filterRule.tags.length; m++) {
+              tag = filterRule.tags[m];
+              if (_.has(universe, tag)) {
+                universe[tag].tag = true;
+                deleteFromUniverseIfAllowed(universe, tag);
+              }
+            }
+          }
+        }
+
+        // Check if a property value of a tag in the universe is allowed.
+        // For all filter rules…
+        for (var i = 0; !_.isEmpty(universe) && i < filterStatus.rules.length; i++) {
+          filterRule = filterStatus.rules[i];
+          // … if there are tags with restricted property values …
+          if (filterRule.restrictedTags.tags.length && !emptyProperties(filterRule.restrictedTags.allowed)) {
+            // … for all those tags …
+            for (var j = 0; !_.isEmpty(universe) && j < filterRule.restrictedTags.tags.length; j++) {
+              tag = filterRule.restrictedTags.tags[j];
+              // … then iterate over all properties …
+              for (var k = 0; k < properties.length; k++) {
+                var property = properties[k];
+                // … and try to delete this tag from the universe if just one
+                // of the allowed property values for this tag and property is
+                // listed in the universe. (Because everything might be allowed
+                // now.)
+                if (findPropertyValuesOnTag(universe, tag, property, filterRule.restrictedTags.allowed[property], true)) {
+                  deleteFromUniverseIfAllowed(universe, tag);
+                }
+              }
+            }
+          }
+        }
+      }
+
+      /**
+       * Checks whether the current status of a filter allows a specific feature
+       * by building the universe of potential values from the feature's
+       * requirements and then checking whether anything in the filter prevents
+       * that.
+       *
+       * @param {object} filterStatus
+       *   The filter status in question.
+       * @param {object} feature
+       *   The feature requested.
+       *
+       * @return {bool}
+       *   Whether the current status of the filter allows specified feature.
+       *
+       * @see generateUniverseFromFeatureRequirements()
+       */
+      function filterStatusAllowsFeature(filterStatus, feature) {
+        // An inactive filter by definition allows the feature.
+        if (!filterStatus.active) {
+          return true;
+        }
+
+        // A feature that specifies no rules has no HTML requirements and is
+        // hence allowed by definition.
+        if (feature.rules.length === 0) {
+          return true;
+        }
+
+        // Analogously for a filter that specifies no rules.
+        if (filterStatus.rules.length === 0) {
+          return true;
+        }
+
+        // Generate the universe U of possible values that can result from the
+        // feature's rules' requirements.
+        var universe = generateUniverseFromFeatureRequirements(feature);
+
+        // If anything that is in the universe (and is thus required by the
+        // feature) is forbidden by any of the filter's rules, then this filter
+        // does not allow this feature.
+        if (anyForbiddenFilterRuleMatches(universe, filterStatus)) {
+          return false;
+        }
+
+        // Mark anything in the universe that is allowed by any of the filter's
+        // rules as allowed. If everything is explicitly allowed, then the
+        // universe will become empty.
+        markAllowedTagsAndPropertyValues(universe, filterStatus);
+
+        // If there was at least one filter rule allowing tags, then everything
+        // in the universe must be allowed for this feature to be allowed, and
+        // thus by now it must be empty. However, it is still possible that the
+        // filter allows the feature, due to no rules for allowing tag property
+        // values and/or rules for forbidding tag property values. For details:
+        // see the comments below.
+        // @see generateUniverseFromFeatureRequirements()
+        if (_.some(_.pluck(filterStatus.rules, 'allow'))) {
+          // If the universe is empty, then everything was explicitly allowed
+          // and our job is done: this filter allows this feature!
+          if (_.isEmpty(universe)) {
+            return true;
+          }
+          // Otherwise, it is still possible that this feature is allowed.
+          else {
+            // Every tag must be explicitly allowed if there are filter rules
+            // doing tag whitelisting.
+            if (!_.every(_.pluck(universe, 'tag'))) {
+              return false;
+            }
+            // Every tag was explicitly allowed, but since the universe is not
+            // empty, one or more tag properties are disallowed. However, if
+            // only blacklisting of tag properties was applied to these tags,
+            // and no whitelisting was ever applied, then it's still fine:
+            // since none of the tag properties were blacklisted, we got to
+            // this point, and since no whitelisting was applied, it doesn't
+            // matter that the properties: this could never have happened
+            // anyway. It's only this late that we can know this for certain.
+            else {
+              var tags = _.keys(universe);
+              // Figure out if there was any rule applying whitelisting tag
+              // restrictions to each of the remaining tags.
+              for (var i = 0; i < tags.length; i++) {
+                var tag = tags[i];
+                if (_.has(universe, tag)) {
+                  if (universe[tag].touchedByAllowedPropertyRule === false) {
+                    delete universe[tag];
+                  }
+                }
+              }
+              return _.isEmpty(universe);
+            }
+          }
+        }
+        // Otherwise, if all filter rules were doing blacklisting, then the sole
+        // fact that we got to this point indicates that this filter allows for
+        // everything that is required for this feature.
+        else {
+          return true;
+        }
+      }
+
+      // If any filter's current status forbids the editor feature, return
+      // false.
+      Drupal.filterConfiguration.update();
+      for (var filterID in Drupal.filterConfiguration.statuses) {
+        if (Drupal.filterConfiguration.statuses.hasOwnProperty(filterID)) {
+          var filterStatus = Drupal.filterConfiguration.statuses[filterID];
+          if (!(filterStatusAllowsFeature(filterStatus, feature))) {
+            return false;
+          }
+        }
+      }
+
+      return true;
+    }
+  };
+
+  /**
+   * Constructor for an editor feature HTML rule.
+   *
+   * Intended to be used in combination with {@link Drupal.EditorFeature}.
+   *
+   * A text editor feature rule object describes both:
+   *  - required HTML tags, attributes, styles and classes: without these, the
+   *    text editor feature is unable to function. It's possible that a
+   *  - allowed HTML tags, attributes, styles and classes: these are optional
+   *    in the strictest sense, but it is possible that the feature generates
+   *    them.
+   *
+   * The structure can be very clearly seen below: there's a "required" and an
+   * "allowed" key. For each of those, there are objects with the "tags",
+   * "attributes", "styles" and "classes" keys. For all these keys the values
+   * are initialized to the empty array. List each possible value as an array
+   * value. Besides the "required" and "allowed" keys, there's an optional
+   * "raw" key: it allows text editor implementations to optionally pass in
+   * their raw representation instead of the Drupal-defined representation for
+   * HTML rules.
+   *
+   * @example
+   * tags: ['<a>']
+   * attributes: ['href', 'alt']
+   * styles: ['color', 'text-decoration']
+   * classes: ['external', 'internal']
+   *
+   * @constructor
+   *
+   * @see Drupal.EditorFeature
+   */
+  Drupal.EditorFeatureHTMLRule = function () {
+
+    /**
+     *
+     * @type {object}
+     *
+     * @prop {Array} tags
+     * @prop {Array} attributes
+     * @prop {Array} styles
+     * @prop {Array} classes
+     */
+    this.required = {tags: [], attributes: [], styles: [], classes: []};
+
+    /**
+     *
+     * @type {object}
+     *
+     * @prop {Array} tags
+     * @prop {Array} attributes
+     * @prop {Array} styles
+     * @prop {Array} classes
+     */
+    this.allowed = {tags: [], attributes: [], styles: [], classes: []};
+
+    /**
+     *
+     * @type {null}
+     */
+    this.raw = null;
+  };
+
+  /**
+   * A text editor feature object. Initialized with the feature name.
+   *
+   * Contains a set of HTML rules ({@link Drupal.EditorFeatureHTMLRule} objects)
+   * that describe which HTML tags, attributes, styles and classes are required
+   * (i.e. essential for the feature to function at all) and which are allowed
+   * (i.e. the feature may generate this, but they're not essential).
+   *
+   * It is necessary to allow for multiple HTML rules per feature: with just
+   * one HTML rule per feature, there is not enough expressiveness to describe
+   * certain cases. For example: a "table" feature would probably require the
+   * `<table>` tag, and might allow e.g. the "summary" attribute on that tag.
+   * However, the table feature would also require the `<tr>` and `<td>` tags,
+   * but it doesn't make sense to allow for a "summary" attribute on these tags.
+   * Hence these would need to be split in two separate rules.
+   *
+   * HTML rules must be added with the `addHTMLRule()` method. A feature that
+   * has zero HTML rules does not create or modify HTML.
+   *
+   * @constructor
+   *
+   * @param {string} name
+   *   The name of the feature.
+   *
+   * @see Drupal.EditorFeatureHTMLRule
+   */
+  Drupal.EditorFeature = function (name) {
+    this.name = name;
+    this.rules = [];
+  };
+
+  /**
+   * Adds a HTML rule to the list of HTML rules for this feature.
+   *
+   * @param {Drupal.EditorFeatureHTMLRule} rule
+   *   A text editor feature HTML rule.
+   */
+  Drupal.EditorFeature.prototype.addHTMLRule = function (rule) {
+    this.rules.push(rule);
+  };
+
+  /**
+   * Text filter status object. Initialized with the filter ID.
+   *
+   * Indicates whether the text filter is currently active (enabled) or not.
+   *
+   * Contains a set of HTML rules ({@link Drupal.FilterHTMLRule} objects) that
+   * describe which HTML tags are allowed or forbidden. They can also describe
+   * for a set of tags (or all tags) which attributes, styles and classes are
+   * allowed and which are forbidden.
+   *
+   * It is necessary to allow for multiple HTML rules per feature, for
+   * analogous reasons as {@link Drupal.EditorFeature}.
+   *
+   * HTML rules must be added with the `addHTMLRule()` method. A filter that has
+   * zero HTML rules does not disallow any HTML.
+   *
+   * @constructor
+   *
+   * @param {string} name
+   *   The name of the feature.
+   *
+   * @see Drupal.FilterHTMLRule
+   */
+  Drupal.FilterStatus = function (name) {
+
+    /**
+     *
+     * @type {string}
+     */
+    this.name = name;
+
+    /**
+     *
+     * @type {bool}
+     */
+    this.active = false;
+
+    /**
+     *
+     * @type {Array.<Drupal.FilterHTMLRule>}
+     */
+    this.rules = [];
+  };
+
+  /**
+   * Adds a HTML rule to the list of HTML rules for this filter.
+   *
+   * @param {Drupal.FilterHTMLRule} rule
+   *   A text filter HTML rule.
+   */
+  Drupal.FilterStatus.prototype.addHTMLRule = function (rule) {
+    this.rules.push(rule);
+  };
+
+  /**
+   * A text filter HTML rule object.
+   *
+   * Intended to be used in combination with {@link Drupal.FilterStatus}.
+   *
+   * A text filter rule object describes:
+   *  1. allowed or forbidden tags: (optional) whitelist or blacklist HTML tags
+   *  2. restricted tag properties: (optional) whitelist or blacklist
+   *     attributes, styles and classes on a set of HTML tags.
+   *
+   * Typically, each text filter rule object does either 1 or 2, not both.
+   *
+   * The structure can be very clearly seen below:
+   *  1. use the "tags" key to list HTML tags, and set the "allow" key to
+   *     either true (to allow these HTML tags) or false (to forbid these HTML
+   *     tags). If you leave the "tags" key's default value (the empty array),
+   *     no restrictions are applied.
+   *  2. all nested within the "restrictedTags" key: use the "tags" subkey to
+   *     list HTML tags to which you want to apply property restrictions, then
+   *     use the "allowed" subkey to whitelist specific property values, and
+   *     similarly use the "forbidden" subkey to blacklist specific property
+   *     values.
+   *
+   * @example
+   * <caption>Whitelist the "p", "strong" and "a" HTML tags.</caption>
+   * {
+   *   tags: ['p', 'strong', 'a'],
+   *   allow: true,
+   *   restrictedTags: {
+   *     tags: [],
+   *     allowed: { attributes: [], styles: [], classes: [] },
+   *     forbidden: { attributes: [], styles: [], classes: [] }
+   *   }
+   * }
+   * @example
+   * <caption>For the "a" HTML tag, only allow the "href" attribute
+   * and the "external" class and disallow the "target" attribute.</caption>
+   * {
+   *   tags: [],
+   *   allow: null,
+   *   restrictedTags: {
+   *     tags: ['a'],
+   *     allowed: { attributes: ['href'], styles: [], classes: ['external'] },
+   *     forbidden: { attributes: ['target'], styles: [], classes: [] }
+   *   }
+   * }
+   * @example
+   * <caption>For all tags, allow the "data-*" attribute (that is, any
+   * attribute that begins with "data-").</caption>
+   * {
+   *   tags: [],
+   *   allow: null,
+   *   restrictedTags: {
+   *     tags: ['*'],
+   *     allowed: { attributes: ['data-*'], styles: [], classes: [] },
+   *     forbidden: { attributes: [], styles: [], classes: [] }
+   *   }
+   * }
+   *
+   * @return {object}
+   *   An object with the following structure:
+   * ```
+   * {
+   *   tags: Array,
+   *   allow: null,
+   *   restrictedTags: {
+   *     tags: Array,
+   *     allowed: {attributes: Array, styles: Array, classes: Array},
+   *     forbidden: {attributes: Array, styles: Array, classes: Array}
+   *   }
+   * }
+   * ```
+   *
+   * @see Drupal.FilterStatus
+   */
+  Drupal.FilterHTMLRule = function () {
+    // Allow or forbid tags.
+    this.tags = [];
+    this.allow = null;
+
+    // Apply restrictions to properties set on tags.
+    this.restrictedTags = {
+      tags: [],
+      allowed: {attributes: [], styles: [], classes: []},
+      forbidden: {attributes: [], styles: [], classes: []}
+    };
+
+    return this;
+  };
+
+  Drupal.FilterHTMLRule.prototype.clone = function () {
+    var clone = new Drupal.FilterHTMLRule();
+    clone.tags = this.tags.slice(0);
+    clone.allow = this.allow;
+    clone.restrictedTags.tags = this.restrictedTags.tags.slice(0);
+    clone.restrictedTags.allowed.attributes = this.restrictedTags.allowed.attributes.slice(0);
+    clone.restrictedTags.allowed.styles = this.restrictedTags.allowed.styles.slice(0);
+    clone.restrictedTags.allowed.classes = this.restrictedTags.allowed.classes.slice(0);
+    clone.restrictedTags.forbidden.attributes = this.restrictedTags.forbidden.attributes.slice(0);
+    clone.restrictedTags.forbidden.styles = this.restrictedTags.forbidden.styles.slice(0);
+    clone.restrictedTags.forbidden.classes = this.restrictedTags.forbidden.classes.slice(0);
+    return clone;
+  };
+
+  /**
+   * Tracks the configuration of all text filters in {@link Drupal.FilterStatus}
+   * objects for {@link Drupal.editorConfiguration.featureIsAllowedByFilters}.
+   *
+   * @namespace
+   */
+  Drupal.filterConfiguration = {
+
+    /**
+     * Drupal.FilterStatus objects, keyed by filter ID.
+     *
+     * @type {Object.<string, Drupal.FilterStatus>}
+     */
+    statuses: {},
+
+    /**
+     * Live filter setting parsers.
+     *
+     * Object keyed by filter ID, for those filters that implement it.
+     *
+     * Filters should load the implementing JavaScript on the filter
+     * configuration form and implement
+     * `Drupal.filterSettings[filterID].getRules()`, which should return an
+     * array of {@link Drupal.FilterHTMLRule} objects.
+     *
+     * @namespace
+     */
+    liveSettingParsers: {},
+
+    /**
+     * Updates all {@link Drupal.FilterStatus} objects to reflect current state.
+     *
+     * Automatically checks whether a filter is currently enabled or not. To
+     * support more finegrained.
+     *
+     * If a filter implements a live setting parser, then that will be used to
+     * keep the HTML rules for the {@link Drupal.FilterStatus} object
+     * up-to-date.
+     */
+    update: function () {
+      for (var filterID in Drupal.filterConfiguration.statuses) {
+        if (Drupal.filterConfiguration.statuses.hasOwnProperty(filterID)) {
+          // Update status.
+          Drupal.filterConfiguration.statuses[filterID].active = $('[name="filters[' + filterID + '][status]"]').is(':checked');
+
+          // Update current rules.
+          if (Drupal.filterConfiguration.liveSettingParsers[filterID]) {
+            Drupal.filterConfiguration.statuses[filterID].rules = Drupal.filterConfiguration.liveSettingParsers[filterID].getRules();
+          }
+        }
+      }
+    }
+
+  };
+
+  /**
+   * Initializes {@link Drupal.filterConfiguration}.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Gets filter configuration from filter form input.
+   */
+  Drupal.behaviors.initializeFilterConfiguration = {
+    attach: function (context, settings) {
+      var $context = $(context);
+
+      $context.find('#filters-status-wrapper input.form-checkbox').once('filter-editor-status').each(function () {
+        var $checkbox = $(this);
+        var nameAttribute = $checkbox.attr('name');
+
+        // The filter's checkbox has a name attribute of the form
+        // "filters[<name of filter>][status]", parse "<name of filter>"
+        // from it.
+        var filterID = nameAttribute.substring(8, nameAttribute.indexOf(']'));
+
+        // Create a Drupal.FilterStatus object to track the state (whether it's
+        // active or not and its current settings, if any) of each filter.
+        Drupal.filterConfiguration.statuses[filterID] = new Drupal.FilterStatus(filterID);
+      });
+    }
+  };
+
+})(jQuery, _, Drupal, document);
diff --git a/core/themes/stable/js/editor/editor.dialog.js b/core/themes/stable/js/editor/editor.dialog.js
new file mode 100644
index 0000000..5b0ad50
--- /dev/null
+++ b/core/themes/stable/js/editor/editor.dialog.js
@@ -0,0 +1,34 @@
+/**
+ * @file
+ * AJAX commands used by Editor module.
+ */
+
+(function ($, Drupal) {
+
+  "use strict";
+
+  /**
+   * Command to save the contents of an editor-provided modal.
+   *
+   * This command does not close the open modal. It should be followed by a
+   * call to `Drupal.AjaxCommands.prototype.closeDialog`. Editors that are
+   * integrated with dialogs must independently listen for an
+   * `editor:dialogsave` event to save the changes into the contents of their
+   * interface.
+   *
+   * @param {Drupal.Ajax} [ajax]
+   *   The Drupal.Ajax object.
+   * @param {object} response
+   *   The server response from the ajax request.
+   * @param {Array} response.values
+   *   The values that were saved.
+   * @param {number} [status]
+   *   The status code from the ajax request.
+   *
+   * @fires event:editor:dialogsave
+   */
+  Drupal.AjaxCommands.prototype.editorDialogSave = function (ajax, response, status) {
+    $(window).trigger('editor:dialogsave', [response.values]);
+  };
+
+})(jQuery, Drupal);
diff --git a/core/themes/stable/js/editor/editor.formattedTextEditor.js b/core/themes/stable/js/editor/editor.formattedTextEditor.js
new file mode 100644
index 0000000..0c0419f
--- /dev/null
+++ b/core/themes/stable/js/editor/editor.formattedTextEditor.js
@@ -0,0 +1,231 @@
+/**
+ * @file
+ * Text editor-based in-place editor for formatted text content in Drupal.
+ *
+ * Depends on editor.module. Works with any (WYSIWYG) editor that implements the
+ * editor.js API, including the optional attachInlineEditor() and onChange()
+ * methods.
+ * For example, assuming that a hypothetical editor's name was "Magical Editor"
+ * and its editor.js API implementation lived at Drupal.editors.magical, this
+ * JavaScript would use:
+ *  - Drupal.editors.magical.attachInlineEditor()
+ */
+
+(function ($, Drupal, drupalSettings) {
+
+  "use strict";
+
+  Drupal.quickedit.editors.editor = Drupal.quickedit.EditorView.extend(/** @lends Drupal.quickedit.editors.editor# */{
+
+    /**
+     * The text format for this field.
+     *
+     * @type {string}
+     */
+    textFormat: null,
+
+    /**
+     * Indicates whether this text format has transformations.
+     *
+     * @type {bool}
+     */
+    textFormatHasTransformations: null,
+
+    /**
+     * Stores a reference to the text editor object for this field.
+     *
+     * @type {Drupal.quickedit.EditorModel}
+     */
+    textEditor: null,
+
+    /**
+     * Stores the textual DOM element that is being in-place edited.
+     *
+     * @type {jQuery}
+     */
+    $textElement: null,
+
+    /**
+     * @constructs
+     *
+     * @augments Drupal.quickedit.EditorView
+     *
+     * @param {object} options
+     *   Options for the editor view.
+     */
+    initialize: function (options) {
+      Drupal.quickedit.EditorView.prototype.initialize.call(this, options);
+
+      var metadata = Drupal.quickedit.metadata.get(this.fieldModel.get('fieldID'), 'custom');
+      this.textFormat = drupalSettings.editor.formats[metadata.format];
+      this.textFormatHasTransformations = metadata.formatHasTransformations;
+      this.textEditor = Drupal.editors[this.textFormat.editor];
+
+      // Store the actual value of this field. We'll need this to restore the
+      // original value when the user discards his modifications.
+      var $fieldItems = this.$el.find('.field__item');
+      if ($fieldItems.length) {
+        this.$textElement = $fieldItems.eq(0);
+      }
+      else {
+        this.$textElement = this.$el;
+      }
+      this.model.set('originalValue', this.$textElement.html());
+    },
+
+    /**
+     * @inheritdoc
+     *
+     * @return {jQuery}
+     *   The text element edited.
+     */
+    getEditedElement: function () {
+      return this.$textElement;
+    },
+
+    /**
+     * @inheritdoc
+     *
+     * @param {object} fieldModel
+     *   The field model.
+     * @param {string} state
+     *   The current state.
+     */
+    stateChange: function (fieldModel, state) {
+      var editorModel = this.model;
+      var from = fieldModel.previous('state');
+      var to = state;
+      switch (to) {
+        case 'inactive':
+          break;
+
+        case 'candidate':
+          // Detach the text editor when entering the 'candidate' state from one
+          // of the states where it could have been attached.
+          if (from !== 'inactive' && from !== 'highlighted') {
+            this.textEditor.detach(this.$textElement.get(0), this.textFormat);
+          }
+          // A field model's editor view revert() method is invoked when an
+          // 'active' field becomes a 'candidate' field. But, in the case of
+          // this in-place editor, the content will have been *replaced* if the
+          // text format has transformation filters. Therefore, if we stop
+          // in-place editing this entity, revert explicitly.
+          if (from === 'active' && this.textFormatHasTransformations) {
+            this.revert();
+          }
+          if (from === 'invalid') {
+            this.removeValidationErrors();
+          }
+          break;
+
+        case 'highlighted':
+          break;
+
+        case 'activating':
+          // When transformation filters have been been applied to the formatted
+          // text of this field, then we'll need to load a re-formatted version
+          // of it without the transformation filters.
+          if (this.textFormatHasTransformations) {
+            var $textElement = this.$textElement;
+            this._getUntransformedText(function (untransformedText) {
+              $textElement.html(untransformedText);
+              fieldModel.set('state', 'active');
+            });
+          }
+          // When no transformation filters have been applied: start WYSIWYG
+          // editing immediately!
+          else {
+            // Defer updating the model until the current state change has
+            // propagated, to not trigger a nested state change event.
+            _.defer(function () {
+              fieldModel.set('state', 'active');
+            });
+          }
+          break;
+
+        case 'active':
+          var textElement = this.$textElement.get(0);
+          var toolbarView = fieldModel.toolbarView;
+          this.textEditor.attachInlineEditor(
+            textElement,
+            this.textFormat,
+            toolbarView.getMainWysiwygToolgroupId(),
+            toolbarView.getFloatedWysiwygToolgroupId()
+          );
+          // Set the state to 'changed' whenever the content has changed.
+          this.textEditor.onChange(textElement, function (htmlText) {
+            editorModel.set('currentValue', htmlText);
+            fieldModel.set('state', 'changed');
+          });
+          break;
+
+        case 'changed':
+          break;
+
+        case 'saving':
+          if (from === 'invalid') {
+            this.removeValidationErrors();
+          }
+          this.save();
+          break;
+
+        case 'saved':
+          break;
+
+        case 'invalid':
+          this.showValidationErrors();
+          break;
+      }
+    },
+
+    /**
+     * @inheritdoc
+     *
+     * @return {object}
+     *   The sttings for the quick edit UI.
+     */
+    getQuickEditUISettings: function () {
+      return {padding: true, unifiedToolbar: true, fullWidthToolbar: true, popup: false};
+    },
+
+    /**
+     * @inheritdoc
+     */
+    revert: function () {
+      this.$textElement.html(this.model.get('originalValue'));
+    },
+
+    /**
+     * Loads untransformed text for this field.
+     *
+     * More accurately: it re-filters formatted text to exclude transformation
+     * filters used by the text format.
+     *
+     * @param {function} callback
+     *   A callback function that will receive the untransformed text.
+     *
+     * @see \Drupal\editor\Ajax\GetUntransformedTextCommand
+     */
+    _getUntransformedText: function (callback) {
+      var fieldID = this.fieldModel.get('fieldID');
+
+      // Create a Drupal.ajax instance to load the form.
+      var textLoaderAjax = Drupal.ajax({
+        url: Drupal.quickedit.util.buildUrl(fieldID, Drupal.url('editor/!entity_type/!id/!field_name/!langcode/!view_mode')),
+        submit: {nocssjs: true}
+      });
+
+      // Implement a scoped editorGetUntransformedText AJAX command: calls the
+      // callback.
+      textLoaderAjax.commands.editorGetUntransformedText = function (ajax, response, status) {
+        callback(response.data);
+      };
+
+      // This will ensure our scoped editorGetUntransformedText AJAX command
+      // gets called.
+      textLoaderAjax.execute();
+    }
+
+  });
+
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/themes/stable/js/editor/editor.js b/core/themes/stable/js/editor/editor.js
new file mode 100644
index 0000000..20731b4
--- /dev/null
+++ b/core/themes/stable/js/editor/editor.js
@@ -0,0 +1,318 @@
+/**
+ * @file
+ * Attaches behavior for the Editor module.
+ */
+
+(function ($, Drupal, drupalSettings) {
+
+  "use strict";
+
+  /**
+   * Finds the text area field associated with the given text format selector.
+   *
+   * @param {jQuery} $formatSelector
+   *   A text format selector DOM element.
+   *
+   * @return {HTMLElement}
+   *   The text area DOM element, if it was found.
+   */
+  function findFieldForFormatSelector($formatSelector) {
+    var field_id = $formatSelector.attr('data-editor-for');
+    // This selector will only find text areas in the top-level document. We do
+    // not support attaching editors on text areas within iframes.
+    return $('#' + field_id).get(0);
+  }
+
+  /**
+   * Changes the text editor on a text area.
+   *
+   * @param {HTMLElement} field
+   *   The text area DOM element.
+   * @param {string} newFormatID
+   *   The text format we're changing to; the text editor for the currently
+   *   active text format will be detached, and the text editor for the new text
+   *   format will be attached.
+   */
+  function changeTextEditor(field, newFormatID) {
+    var previousFormatID = field.getAttribute('data-editor-active-text-format');
+
+    // Detach the current editor (if any) and attach a new editor.
+    if (drupalSettings.editor.formats[previousFormatID]) {
+      Drupal.editorDetach(field, drupalSettings.editor.formats[previousFormatID]);
+    }
+    // When no text editor is currently active, stop tracking changes.
+    else {
+      $(field).off('.editor');
+    }
+
+    // Attach the new text editor (if any).
+    if (drupalSettings.editor.formats[newFormatID]) {
+      var format = drupalSettings.editor.formats[newFormatID];
+      filterXssWhenSwitching(field, format, previousFormatID, Drupal.editorAttach);
+    }
+
+    // Store the new active format.
+    field.setAttribute('data-editor-active-text-format', newFormatID);
+  }
+
+  /**
+   * Handles changes in text format.
+   *
+   * @param {jQuery.Event} event
+   *   The text format change event.
+   */
+  function onTextFormatChange(event) {
+    var $select = $(event.target);
+    var field = event.data.field;
+    var activeFormatID = field.getAttribute('data-editor-active-text-format');
+    var newFormatID = $select.val();
+
+    // Prevent double-attaching if the change event is triggered manually.
+    if (newFormatID === activeFormatID) {
+      return;
+    }
+
+    // When changing to a text format that has a text editor associated
+    // with it that supports content filtering, then first ask for
+    // confirmation, because switching text formats might cause certain
+    // markup to be stripped away.
+    var supportContentFiltering = drupalSettings.editor.formats[newFormatID] && drupalSettings.editor.formats[newFormatID].editorSupportsContentFiltering;
+    // If there is no content yet, it's always safe to change the text format.
+    var hasContent = field.value !== '';
+    if (hasContent && supportContentFiltering) {
+      var message = Drupal.t('Changing the text format to %text_format will permanently remove content that is not allowed in that text format.<br><br>Save your changes before switching the text format to avoid losing data.', {
+        '%text_format': $select.find('option:selected').text()
+      });
+      var confirmationDialog = Drupal.dialog('<div>' + message + '</div>', {
+        title: Drupal.t('Change text format?'),
+        dialogClass: 'editor-change-text-format-modal',
+        resizable: false,
+        buttons: [
+          {
+            text: Drupal.t('Continue'),
+            class: 'button button--primary',
+            click: function () {
+              changeTextEditor(field, newFormatID);
+              confirmationDialog.close();
+            }
+          },
+          {
+            text: Drupal.t('Cancel'),
+            class: 'button',
+            click: function () {
+              // Restore the active format ID: cancel changing text format. We
+              // cannot simply call event.preventDefault() because jQuery's
+              // change event is only triggered after the change has already
+              // been accepted.
+              $select.val(activeFormatID);
+              confirmationDialog.close();
+            }
+          }
+        ],
+        // Prevent this modal from being closed without the user making a choice
+        // as per http://stackoverflow.com/a/5438771.
+        closeOnEscape: false,
+        create: function () {
+          $(this).parent().find('.ui-dialog-titlebar-close').remove();
+        },
+        beforeClose: false,
+        close: function (event) {
+          // Automatically destroy the DOM element that was used for the dialog.
+          $(event.target).remove();
+        }
+      });
+
+      confirmationDialog.showModal();
+    }
+    else {
+      changeTextEditor(field, newFormatID);
+    }
+  }
+
+  /**
+   * Initialize an empty object for editors to place their attachment code.
+   *
+   * @namespace
+   */
+  Drupal.editors = {};
+
+  /**
+   * Enables editors on text_format elements.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches an editor to an input element.
+   * @prop {Drupal~behaviorDetach} detach
+   *   Detaches an editor from an input element.
+   */
+  Drupal.behaviors.editor = {
+    attach: function (context, settings) {
+      // If there are no editor settings, there are no editors to enable.
+      if (!settings.editor) {
+        return;
+      }
+
+      $(context).find('[data-editor-for]').once('editor').each(function () {
+        var $this = $(this);
+        var field = findFieldForFormatSelector($this);
+
+        // Opt-out if no supported text area was found.
+        if (!field) {
+          return;
+        }
+
+        // Store the current active format.
+        var activeFormatID = $this.val();
+        field.setAttribute('data-editor-active-text-format', activeFormatID);
+
+        // Directly attach this text editor, if the text format is enabled.
+        if (settings.editor.formats[activeFormatID]) {
+          // XSS protection for the current text format/editor is performed on
+          // the server side, so we don't need to do anything special here.
+          Drupal.editorAttach(field, settings.editor.formats[activeFormatID]);
+        }
+        // When there is no text editor for this text format, still track
+        // changes, because the user has the ability to switch to some text
+        // editor, otherwise this code would not be executed.
+        $(field).on('change.editor keypress.editor', function () {
+          field.setAttribute('data-editor-value-is-changed', 'true');
+          // Just knowing that the value was changed is enough, stop tracking.
+          $(field).off('.editor');
+        });
+
+        // Attach onChange handler to text format selector element.
+        if ($this.is('select')) {
+          $this.on('change.editorAttach', {field: field}, onTextFormatChange);
+        }
+        // Detach any editor when the containing form is submitted.
+        $this.parents('form').on('submit', function (event) {
+          // Do not detach if the event was canceled.
+          if (event.isDefaultPrevented()) {
+            return;
+          }
+          // Detach the current editor (if any).
+          if (settings.editor.formats[activeFormatID]) {
+            Drupal.editorDetach(field, settings.editor.formats[activeFormatID], 'serialize');
+          }
+        });
+      });
+    },
+
+    detach: function (context, settings, trigger) {
+      var editors;
+      // The 'serialize' trigger indicates that we should simply update the
+      // underlying element with the new text, without destroying the editor.
+      if (trigger === 'serialize') {
+        // Removing the editor-processed class guarantees that the editor will
+        // be reattached. Only do this if we're planning to destroy the editor.
+        editors = $(context).find('[data-editor-for]').findOnce('editor');
+      }
+      else {
+        editors = $(context).find('[data-editor-for]').removeOnce('editor');
+      }
+
+      editors.each(function () {
+        var $this = $(this);
+        var activeFormatID = $this.val();
+        var field = findFieldForFormatSelector($this);
+        if (field && activeFormatID in settings.editor.formats) {
+          Drupal.editorDetach(field, settings.editor.formats[activeFormatID], trigger);
+        }
+      });
+    }
+  };
+
+  /**
+   * Attaches editor behaviors to the field.
+   *
+   * @param {HTMLElement} field
+   *   The textarea DOM element.
+   * @param {object} format
+   *   The text format that's being activated, from
+   *   drupalSettings.editor.formats.
+   *
+   * @listens event:change
+   *
+   * @fires event:formUpdated
+   */
+  Drupal.editorAttach = function (field, format) {
+    if (format.editor) {
+      // Attach the text editor.
+      Drupal.editors[format.editor].attach(field, format);
+
+      // Ensures form.js' 'formUpdated' event is triggered even for changes that
+      // happen within the text editor.
+      Drupal.editors[format.editor].onChange(field, function () {
+        $(field).trigger('formUpdated');
+
+        // Keep track of changes, so we know what to do when switching text
+        // formats and guaranteeing XSS protection.
+        field.setAttribute('data-editor-value-is-changed', 'true');
+      });
+    }
+  };
+
+  /**
+   * Detaches editor behaviors from the field.
+   *
+   * @param {HTMLElement} field
+   *   The textarea DOM element.
+   * @param {object} format
+   *   The text format that's being activated, from
+   *   drupalSettings.editor.formats.
+   * @param {string} trigger
+   *   Trigger value from the detach behavior.
+   */
+  Drupal.editorDetach = function (field, format, trigger) {
+    if (format.editor) {
+      Drupal.editors[format.editor].detach(field, format, trigger);
+
+      // Restore the original value if the user didn't make any changes yet.
+      if (field.getAttribute('data-editor-value-is-changed') === 'false') {
+        field.value = field.getAttribute('data-editor-value-original');
+      }
+    }
+  };
+
+  /**
+   * Filter away XSS attack vectors when switching text formats.
+   *
+   * @param {HTMLElement} field
+   *   The textarea DOM element.
+   * @param {object} format
+   *   The text format that's being activated, from
+   *   drupalSettings.editor.formats.
+   * @param {string} originalFormatID
+   *   The text format ID of the original text format.
+   * @param {function} callback
+   *   A callback to be called (with no parameters) after the field's value has
+   *   been XSS filtered.
+   */
+  function filterXssWhenSwitching(field, format, originalFormatID, callback) {
+    // A text editor that already is XSS-safe needs no additional measures.
+    if (format.editor.isXssSafe) {
+      callback(field, format);
+    }
+    // Otherwise, ensure XSS safety: let the server XSS filter this value.
+    else {
+      $.ajax({
+        url: Drupal.url('editor/filter_xss/' + format.format),
+        type: 'POST',
+        data: {
+          value: field.value,
+          original_format_id: originalFormatID
+        },
+        dataType: 'json',
+        success: function (xssFilteredValue) {
+          // If the server returns false, then no XSS filtering is needed.
+          if (xssFilteredValue !== false) {
+            field.value = xssFilteredValue;
+          }
+          callback(field, format);
+        }
+      });
+    }
+  }
+
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/themes/stable/js/field_ui/field_ui.js b/core/themes/stable/js/field_ui/field_ui.js
new file mode 100644
index 0000000..9f1afc6
--- /dev/null
+++ b/core/themes/stable/js/field_ui/field_ui.js
@@ -0,0 +1,338 @@
+/**
+ * @file
+ * Attaches the behaviors for the Field UI module.
+ */
+
+(function ($, Drupal, drupalSettings) {
+
+  "use strict";
+
+  /**
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Adds behaviors to the field storage add form.
+   */
+  Drupal.behaviors.fieldUIFieldStorageAddForm = {
+    attach: function (context) {
+      var $form = $(context).find('[data-drupal-selector="field-ui-field-storage-add-form"]').once('field_ui_add');
+      if ($form.length) {
+        // Add a few 'js-form-required' and 'form-required' css classes here.
+        // We can not use the Form API '#required' property because both label
+        // elements for "add new" and "re-use existing" can never be filled and
+        // submitted at the same time. The actual validation will happen
+        // server-side.
+        $form.find(
+          '.js-form-item-label label,' +
+          '.js-form-item-field-name label,' +
+          '.js-form-item-existing-storage-label label')
+          .addClass('js-form-required form-required');
+
+        var $newFieldType = $form.find('select[name="new_storage_type"]');
+        var $existingStorageName = $form.find('select[name="existing_storage_name"]');
+        var $existingStorageLabel = $form.find('input[name="existing_storage_label"]');
+
+        // When the user selects a new field type, clear the "existing field"
+        // selection.
+        $newFieldType.on('change', function () {
+          if ($(this).val() !== '') {
+            // Reset the "existing storage name" selection.
+            $existingStorageName.val('').trigger('change');
+          }
+        });
+
+        // When the user selects an existing storage name, clear the "new field
+        // type" selection and populate the 'existing_storage_label' element.
+        $existingStorageName.on('change', function () {
+          var value = $(this).val();
+          if (value !== '') {
+            // Reset the "new field type" selection.
+            $newFieldType.val('').trigger('change');
+
+            // Pre-populate the "existing storage label" element.
+            if (typeof drupalSettings.existingFieldLabels[value] !== 'undefined') {
+              $existingStorageLabel.val(drupalSettings.existingFieldLabels[value]);
+            }
+          }
+        });
+      }
+    }
+  };
+
+  /**
+   * Attaches the fieldUIOverview behavior.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches the fieldUIOverview behavior.
+   *
+   * @see Drupal.fieldUIOverview.attach
+   */
+  Drupal.behaviors.fieldUIDisplayOverview = {
+    attach: function (context, settings) {
+      $(context).find('table#field-display-overview').once('field-display-overview').each(function () {
+        Drupal.fieldUIOverview.attach(this, settings.fieldUIRowsData, Drupal.fieldUIDisplayOverview);
+      });
+    }
+  };
+
+  /**
+   * Namespace for the field UI overview.
+   *
+   * @namespace
+   */
+  Drupal.fieldUIOverview = {
+
+    /**
+     * Attaches the fieldUIOverview behavior.
+     *
+     * @param {HTMLTableElement} table
+     *   The table element for the overview.
+     * @param {object} rowsData
+     *   The data of the rows in the table.
+     * @param {object} rowHandlers
+     *   Handlers to be added to the rows.
+     */
+    attach: function (table, rowsData, rowHandlers) {
+      var tableDrag = Drupal.tableDrag[table.id];
+
+      // Add custom tabledrag callbacks.
+      tableDrag.onDrop = this.onDrop;
+      tableDrag.row.prototype.onSwap = this.onSwap;
+
+      // Create row handlers.
+      $(table).find('tr.draggable').each(function () {
+        // Extract server-side data for the row.
+        var row = this;
+        if (row.id in rowsData) {
+          var data = rowsData[row.id];
+          data.tableDrag = tableDrag;
+
+          // Create the row handler, make it accessible from the DOM row
+          // element.
+          var rowHandler = new rowHandlers[data.rowHandler](row, data);
+          $(row).data('fieldUIRowHandler', rowHandler);
+        }
+      });
+    },
+
+    /**
+     * Event handler to be attached to form inputs triggering a region change.
+     */
+    onChange: function () {
+      var $trigger = $(this);
+      var $row = $trigger.closest('tr');
+      var rowHandler = $row.data('fieldUIRowHandler');
+
+      var refreshRows = {};
+      refreshRows[rowHandler.name] = $trigger.get(0);
+
+      // Handle region change.
+      var region = rowHandler.getRegion();
+      if (region !== rowHandler.region) {
+        // Remove parenting.
+        $row.find('select.js-field-parent').val('');
+        // Let the row handler deal with the region change.
+        $.extend(refreshRows, rowHandler.regionChange(region));
+        // Update the row region.
+        rowHandler.region = region;
+      }
+
+      // Ajax-update the rows.
+      Drupal.fieldUIOverview.AJAXRefreshRows(refreshRows);
+    },
+
+    /**
+     * Lets row handlers react when a row is dropped into a new region.
+     */
+    onDrop: function () {
+      var dragObject = this;
+      var row = dragObject.rowObject.element;
+      var $row = $(row);
+      var rowHandler = $row.data('fieldUIRowHandler');
+      if (typeof rowHandler !== 'undefined') {
+        var regionRow = $row.prevAll('tr.region-message').get(0);
+        var region = regionRow.className.replace(/([^ ]+[ ]+)*region-([^ ]+)-message([ ]+[^ ]+)*/, '$2');
+
+        if (region !== rowHandler.region) {
+          // Let the row handler deal with the region change.
+          var refreshRows = rowHandler.regionChange(region);
+          // Update the row region.
+          rowHandler.region = region;
+          // Ajax-update the rows.
+          Drupal.fieldUIOverview.AJAXRefreshRows(refreshRows);
+        }
+      }
+    },
+
+    /**
+     * Refreshes placeholder rows in empty regions while a row is being dragged.
+     *
+     * Copied from block.js.
+     *
+     * @param {HTMLElement} draggedRow
+     *   The tableDrag rowObject for the row being dragged.
+     */
+    onSwap: function (draggedRow) {
+      var rowObject = this;
+      $(rowObject.table).find('tr.region-message').each(function () {
+        var $this = $(this);
+        // If the dragged row is in this region, but above the message row, swap
+        // it down one space.
+        if ($this.prev('tr').get(0) === rowObject.group[rowObject.group.length - 1]) {
+          // Prevent a recursion problem when using the keyboard to move rows
+          // up.
+          if ((rowObject.method !== 'keyboard' || rowObject.direction === 'down')) {
+            rowObject.swap('after', this);
+          }
+        }
+        // This region has become empty.
+        if ($this.next('tr').is(':not(.draggable)') || $this.next('tr').length === 0) {
+          $this.removeClass('region-populated').addClass('region-empty');
+        }
+        // This region has become populated.
+        else if ($this.is('.region-empty')) {
+          $this.removeClass('region-empty').addClass('region-populated');
+        }
+      });
+    },
+
+    /**
+     * Triggers Ajax refresh of selected rows.
+     *
+     * The 'format type' selects can trigger a series of changes in child rows.
+     * The #ajax behavior is therefore not attached directly to the selects, but
+     * triggered manually through a hidden #ajax 'Refresh' button.
+     *
+     * @param {object} rows
+     *   A hash object, whose keys are the names of the rows to refresh (they
+     *   will receive the 'ajax-new-content' effect on the server side), and
+     *   whose values are the DOM element in the row that should get an Ajax
+     *   throbber.
+     */
+    AJAXRefreshRows: function (rows) {
+      // Separate keys and values.
+      var rowNames = [];
+      var ajaxElements = [];
+      var rowName;
+      for (rowName in rows) {
+        if (rows.hasOwnProperty(rowName)) {
+          rowNames.push(rowName);
+          ajaxElements.push(rows[rowName]);
+        }
+      }
+
+      if (rowNames.length) {
+        // Add a throbber next each of the ajaxElements.
+        $(ajaxElements).after('<div class="ajax-progress ajax-progress-throbber"><div class="throbber">&nbsp;</div></div>');
+
+        // Fire the Ajax update.
+        $('input[name=refresh_rows]').val(rowNames.join(' '));
+        $('input[data-drupal-selector="edit-refresh"]').trigger('mousedown');
+
+        // Disabled elements do not appear in POST ajax data, so we mark the
+        // elements disabled only after firing the request.
+        $(ajaxElements).prop('disabled', true);
+      }
+    }
+  };
+
+  /**
+   * Row handlers for the 'Manage display' screen.
+   *
+   * @namespace
+   */
+  Drupal.fieldUIDisplayOverview = {};
+
+  /**
+   * Constructor for a 'field' row handler.
+   *
+   * This handler is used for both fields and 'extra fields' rows.
+   *
+   * @constructor
+   *
+   * @param {HTMLTableRowElement} row
+   *   The row DOM element.
+   * @param {object} data
+   *   Additional data to be populated in the constructed object.
+   *
+   * @return {Drupal.fieldUIDisplayOverview.field}
+   *   The field row handler constructed.
+   */
+  Drupal.fieldUIDisplayOverview.field = function (row, data) {
+    this.row = row;
+    this.name = data.name;
+    this.region = data.region;
+    this.tableDrag = data.tableDrag;
+
+    // Attach change listener to the 'plugin type' select.
+    this.$pluginSelect = $(row).find('select.field-plugin-type');
+    this.$pluginSelect.on('change', Drupal.fieldUIOverview.onChange);
+
+    return this;
+  };
+
+  Drupal.fieldUIDisplayOverview.field.prototype = {
+
+    /**
+     * Returns the region corresponding to the current form values of the row.
+     *
+     * @return {string}
+     *   Either 'hidden' or 'content'.
+     */
+    getRegion: function () {
+      return (this.$pluginSelect.val() === 'hidden') ? 'hidden' : 'content';
+    },
+
+    /**
+     * Reacts to a row being changed regions.
+     *
+     * This function is called when the row is moved to a different region, as
+     * a
+     * result of either :
+     * - a drag-and-drop action (the row's form elements then probably need to
+     * be updated accordingly)
+     * - user input in one of the form elements watched by the
+     *   {@link Drupal.fieldUIOverview.onChange} change listener.
+     *
+     * @param {string} region
+     *   The name of the new region for the row.
+     *
+     * @return {object}
+     *   A hash object indicating which rows should be Ajax-updated as a result
+     *   of the change, in the format expected by
+     *   {@link Drupal.fieldUIOverview.AJAXRefreshRows}.
+     */
+    regionChange: function (region) {
+
+      // When triggered by a row drag, the 'format' select needs to be adjusted
+      // to the new region.
+      var currentValue = this.$pluginSelect.val();
+      var value;
+      // @TODO Check if this couldn't just be like
+      // if (region !== 'hidden') {
+      if (region === 'content') {
+        if (currentValue === 'hidden') {
+          // Restore the formatter back to the default formatter. Pseudo-fields
+          // do not have default formatters, we just return to 'visible' for
+          // those.
+          value = (typeof this.defaultPlugin !== 'undefined') ? this.defaultPlugin : this.$pluginSelect.find('option').val();
+        }
+      }
+      else {
+        value = 'hidden';
+      }
+
+      if (typeof value !== 'undefined') {
+        this.$pluginSelect.val(value);
+      }
+
+      var refreshRows = {};
+      refreshRows[this.name] = this.$pluginSelect.get(0);
+
+      return refreshRows;
+    }
+  };
+
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/themes/stable/js/file/file.js b/core/themes/stable/js/file/file.js
new file mode 100644
index 0000000..7d5d919
--- /dev/null
+++ b/core/themes/stable/js/file/file.js
@@ -0,0 +1,257 @@
+/**
+ * @file
+ * Provides JavaScript additions to the managed file field type.
+ *
+ * This file provides progress bar support (if available), popup windows for
+ * file previews, and disabling of other file fields during Ajax uploads (which
+ * prevents separate file fields from accidentally uploading files).
+ */
+
+(function ($, Drupal) {
+
+  "use strict";
+
+  /**
+   * Attach behaviors to the file fields passed in the settings.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches validation for file extensions.
+   * @prop {Drupal~behaviorDetach} detach
+   *   Detaches validation for file extensions.
+   */
+  Drupal.behaviors.fileValidateAutoAttach = {
+    attach: function (context, settings) {
+      var $context = $(context);
+      var elements;
+
+      function initFileValidation(selector) {
+        $context.find(selector)
+          .once('fileValidate')
+          .on('change.fileValidate', {extensions: elements[selector]}, Drupal.file.validateExtension);
+      }
+
+      if (settings.file && settings.file.elements) {
+        elements = settings.file.elements;
+        Object.keys(elements).forEach(initFileValidation);
+      }
+    },
+    detach: function (context, settings, trigger) {
+      var $context = $(context);
+      var elements;
+
+      function removeFileValidation(selector) {
+        $context.find(selector)
+          .removeOnce('fileValidate')
+          .off('change.fileValidate', Drupal.file.validateExtension);
+      }
+
+      if (trigger === 'unload' && settings.file && settings.file.elements) {
+        elements = settings.file.elements;
+        Object.keys(elements).forEach(removeFileValidation);
+      }
+    }
+  };
+
+  /**
+   * Attach behaviors to file element auto upload.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches triggers for the upload button.
+   * @prop {Drupal~behaviorDetach} detach
+   *   Detaches auto file upload trigger.
+   */
+  Drupal.behaviors.fileAutoUpload = {
+    attach: function (context) {
+      $(context).find('input[type="file"]').once('auto-file-upload').on('change.autoFileUpload', Drupal.file.triggerUploadButton);
+    },
+    detach: function (context, setting, trigger) {
+      if (trigger === 'unload') {
+        $(context).find('input[type="file"]').removeOnce('auto-file-upload').off('.autoFileUpload');
+      }
+    }
+  };
+
+  /**
+   * Attach behaviors to the file upload and remove buttons.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches form submit events.
+   * @prop {Drupal~behaviorDetach} detach
+   *   Detaches form submit events.
+   */
+  Drupal.behaviors.fileButtons = {
+    attach: function (context) {
+      var $context = $(context);
+      $context.find('.js-form-submit').on('mousedown', Drupal.file.disableFields);
+      $context.find('.js-form-managed-file .js-form-submit').on('mousedown', Drupal.file.progressBar);
+    },
+    detach: function (context) {
+      var $context = $(context);
+      $context.find('.js-form-submit').off('mousedown', Drupal.file.disableFields);
+      $context.find('.js-form-managed-file .js-form-submit').off('mousedown', Drupal.file.progressBar);
+    }
+  };
+
+  /**
+   * Attach behaviors to links within managed file elements for preview windows.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches triggers.
+   * @prop {Drupal~behaviorDetach} detach
+   *   Detaches triggers.
+   */
+  Drupal.behaviors.filePreviewLinks = {
+    attach: function (context) {
+      $(context).find('div.js-form-managed-file .file a, .file-widget .file a').on('click', Drupal.file.openInNewWindow);
+    },
+    detach: function (context) {
+      $(context).find('div.js-form-managed-file .file a, .file-widget .file a').off('click', Drupal.file.openInNewWindow);
+    }
+  };
+
+  /**
+   * File upload utility functions.
+   *
+   * @namespace
+   */
+  Drupal.file = Drupal.file || {
+
+    /**
+     * Client-side file input validation of file extensions.
+     *
+     * @name Drupal.file.validateExtension
+     *
+     * @param {jQuery.Event} event
+     *   The event triggered. For example `change.fileValidate`.
+     */
+    validateExtension: function (event) {
+      event.preventDefault();
+      // Remove any previous errors.
+      $('.file-upload-js-error').remove();
+
+      // Add client side validation for the input[type=file].
+      var extensionPattern = event.data.extensions.replace(/,\s*/g, '|');
+      if (extensionPattern.length > 1 && this.value.length > 0) {
+        var acceptableMatch = new RegExp('\\.(' + extensionPattern + ')$', 'gi');
+        if (!acceptableMatch.test(this.value)) {
+          var error = Drupal.t("The selected file %filename cannot be uploaded. Only files with the following extensions are allowed: %extensions.", {
+            // According to the specifications of HTML5, a file upload control
+            // should not reveal the real local path to the file that a user
+            // has selected. Some web browsers implement this restriction by
+            // replacing the local path with "C:\fakepath\", which can cause
+            // confusion by leaving the user thinking perhaps Drupal could not
+            // find the file because it messed up the file path. To avoid this
+            // confusion, therefore, we strip out the bogus fakepath string.
+            '%filename': this.value.replace('C:\\fakepath\\', ''),
+            '%extensions': extensionPattern.replace(/\|/g, ', ')
+          });
+          $(this).closest('div.js-form-managed-file').prepend('<div class="messages messages--error file-upload-js-error" aria-live="polite">' + error + '</div>');
+          this.value = '';
+          // Cancel all other change event handlers.
+          event.stopImmediatePropagation();
+        }
+      }
+    },
+
+    /**
+     * Trigger the upload_button mouse event to auto-upload as a managed file.
+     *
+     * @name Drupal.file.triggerUploadButton
+     *
+     * @param {jQuery.Event} event
+     *   The event triggered. For example `change.autoFileUpload`.
+     */
+    triggerUploadButton: function (event) {
+      $(event.target).closest('.js-form-managed-file').find('.js-form-submit').trigger('mousedown');
+    },
+
+    /**
+     * Prevent file uploads when using buttons not intended to upload.
+     *
+     * @name Drupal.file.disableFields
+     *
+     * @param {jQuery.Event} event
+     *   The event triggered, most likely a `mousedown` event.
+     */
+    disableFields: function (event) {
+      var $clickedButton = $(this).findOnce('ajax');
+
+      // Only disable upload fields for Ajax buttons.
+      if (!$clickedButton.length) {
+        return;
+      }
+
+      // Check if we're working with an "Upload" button.
+      var $enabledFields = [];
+      if ($clickedButton.closest('div.js-form-managed-file').length > 0) {
+        $enabledFields = $clickedButton.closest('div.js-form-managed-file').find('input.js-form-file');
+      }
+
+      // Temporarily disable upload fields other than the one we're currently
+      // working with. Filter out fields that are already disabled so that they
+      // do not get enabled when we re-enable these fields at the end of
+      // behavior processing. Re-enable in a setTimeout set to a relatively
+      // short amount of time (1 second). All the other mousedown handlers
+      // (like Drupal's Ajax behaviors) are executed before any timeout
+      // functions are called, so we don't have to worry about the fields being
+      // re-enabled too soon. @todo If the previous sentence is true, why not
+      // set the timeout to 0?
+      var $fieldsToTemporarilyDisable = $('div.js-form-managed-file input.js-form-file').not($enabledFields).not(':disabled');
+      $fieldsToTemporarilyDisable.prop('disabled', true);
+      setTimeout(function () {
+        $fieldsToTemporarilyDisable.prop('disabled', false);
+      }, 1000);
+    },
+
+    /**
+     * Add progress bar support if possible.
+     *
+     * @name Drupal.file.progressBar
+     *
+     * @param {jQuery.Event} event
+     *   The event triggered, most likely a `mousedown` event.
+     */
+    progressBar: function (event) {
+      var $clickedButton = $(this);
+      var $progressId = $clickedButton.closest('div.js-form-managed-file').find('input.file-progress');
+      if ($progressId.length) {
+        var originalName = $progressId.attr('name');
+
+        // Replace the name with the required identifier.
+        $progressId.attr('name', originalName.match(/APC_UPLOAD_PROGRESS|UPLOAD_IDENTIFIER/)[0]);
+
+        // Restore the original name after the upload begins.
+        setTimeout(function () {
+          $progressId.attr('name', originalName);
+        }, 1000);
+      }
+      // Show the progress bar if the upload takes longer than half a second.
+      setTimeout(function () {
+        $clickedButton.closest('div.js-form-managed-file').find('div.ajax-progress-bar').slideDown();
+      }, 500);
+    },
+
+    /**
+     * Open links to files within forms in a new window.
+     *
+     * @name Drupal.file.openInNewWindow
+     *
+     * @param {jQuery.Event} event
+     *   The event triggered, most likely a `click` event.
+     */
+    openInNewWindow: function (event) {
+      event.preventDefault();
+      $(this).attr('target', '_blank');
+      window.open(this.href, 'filePreview', 'toolbar=0,scrollbars=1,location=1,statusbar=1,menubar=0,resizable=1,width=500,height=550');
+    }
+  };
+
+})(jQuery, Drupal);
diff --git a/core/themes/stable/js/filter/filter.admin.js b/core/themes/stable/js/filter/filter.admin.js
new file mode 100644
index 0000000..3af5624
--- /dev/null
+++ b/core/themes/stable/js/filter/filter.admin.js
@@ -0,0 +1,69 @@
+/**
+ * @file
+ * Attaches administration-specific behavior for the Filter module.
+ */
+
+(function ($) {
+
+  "use strict";
+
+  /**
+   * Displays and updates the status of filters on the admin page.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches behaviors to the filter admin view.
+   */
+  Drupal.behaviors.filterStatus = {
+    attach: function (context, settings) {
+      var $context = $(context);
+      $context.find('#filters-status-wrapper input.form-checkbox').once('filter-status').each(function () {
+        var $checkbox = $(this);
+        // Retrieve the tabledrag row belonging to this filter.
+        var $row = $context.find('#' + $checkbox.attr('id').replace(/-status$/, '-weight')).closest('tr');
+        // Retrieve the vertical tab belonging to this filter.
+        var $filterSettings = $context.find('#' + $checkbox.attr('id').replace(/-status$/, '-settings'));
+        var filterSettingsTab = $filterSettings.data('verticalTab');
+
+        // Bind click handler to this checkbox to conditionally show and hide
+        // the filter's tableDrag row and vertical tab pane.
+        $checkbox.on('click.filterUpdate', function () {
+          if ($checkbox.is(':checked')) {
+            $row.show();
+            if (filterSettingsTab) {
+              filterSettingsTab.tabShow().updateSummary();
+            }
+            else {
+              // On very narrow viewports, Vertical Tabs are disabled.
+              $filterSettings.show();
+            }
+          }
+          else {
+            $row.hide();
+            if (filterSettingsTab) {
+              filterSettingsTab.tabHide().updateSummary();
+            }
+            else {
+              // On very narrow viewports, Vertical Tabs are disabled.
+              $filterSettings.hide();
+            }
+          }
+          // Restripe table after toggling visibility of table row.
+          Drupal.tableDrag['filter-order'].restripeTable();
+        });
+
+        // Attach summary for configurable filters (only for screen readers).
+        if (filterSettingsTab) {
+          filterSettingsTab.details.drupalSetSummary(function (tabContext) {
+            return $checkbox.is(':checked') ? Drupal.t('Enabled') : Drupal.t('Disabled');
+          });
+        }
+
+        // Trigger our bound click handler to update elements to initial state.
+        $checkbox.triggerHandler('click.filterUpdate');
+      });
+    }
+  };
+
+})(jQuery);
diff --git a/core/themes/stable/js/filter/filter.api.php b/core/themes/stable/js/filter/filter.api.php
new file mode 100644
index 0000000..a2ba9b0
--- /dev/null
+++ b/core/themes/stable/js/filter/filter.api.php
@@ -0,0 +1,60 @@
+<?php
+
+/**
+ * @file
+ * Hooks provided by the Filter module.
+ */
+
+/**
+ * @addtogroup hooks
+ * @{
+ */
+
+/**
+ * Perform alterations on filter definitions.
+ *
+ * @param $info
+ *   Array of information on filters exposed by filter plugins.
+ */
+function hook_filter_info_alter(&$info) {
+  // Alter the default settings of the URL filter provided by core.
+  $info['filter_url']['default_settings'] = array(
+    'filter_url_length' => 100,
+  );
+}
+
+/**
+ * Alters images with an invalid source.
+ *
+ * When the 'Restrict images to this site' filter is enabled, any images that
+ * are not hosted on the site will be passed through this hook, most commonly to
+ * replace the invalid image with an error indicator.
+ *
+ * @param DOMElement $image
+ *   An IMG node to format, parsed from the filtered text.
+ */
+function hook_filter_secure_image_alter(&$image) {
+  // Turn an invalid image into an error indicator.
+  $image->setAttribute('src', base_path() . 'core/misc/icons/e32700/error.svg');
+  $image->setAttribute('alt', t('Image removed.'));
+  $image->setAttribute('title', t('This image has been removed. For security reasons, only images from the local domain are allowed.'));
+
+  // Add a CSS class to aid in styling.
+  $class = ($image->getAttribute('class') ? trim($image->getAttribute('class')) . ' ' : '');
+  $class .= 'filter-image-invalid';
+  $image->setAttribute('class', $class);
+}
+
+/**
+ * Perform actions when a text format has been disabled.
+ *
+ * @param $format
+ *   The format object of the format being disabled.
+ */
+function hook_filter_format_disable($format) {
+  mymodule_cache_rebuild();
+}
+
+/**
+ * @} End of "addtogroup hooks".
+ */
diff --git a/core/themes/stable/js/filter/filter.filter_html.admin.js b/core/themes/stable/js/filter/filter.filter_html.admin.js
new file mode 100644
index 0000000..5de2d11
--- /dev/null
+++ b/core/themes/stable/js/filter/filter.filter_html.admin.js
@@ -0,0 +1,328 @@
+/**
+ * @file
+ * Attaches behavior for updating filter_html's settings automatically.
+ */
+
+(function ($, _, document, window) {
+
+  "use strict";
+
+  if (Drupal.filterConfiguration) {
+
+    /**
+     * Implement a live setting parser to prevent text editors from automatically
+     * enabling buttons that are not allowed by this filter's configuration.
+     *
+     * @namespace
+     */
+    Drupal.filterConfiguration.liveSettingParsers.filter_html = {
+
+      /**
+       * @return {Array}
+       *   An array of filter rules.
+       */
+      getRules: function () {
+        var currentValue = $('#edit-filters-filter-html-settings-allowed-html').val();
+        var rules = Drupal.behaviors.filterFilterHtmlUpdating._parseSetting(currentValue);
+
+        // Build a FilterHTMLRule that reflects the hard-coded behavior that
+        // strips all "style" attribute and all "on*" attributes.
+        var rule = new Drupal.FilterHTMLRule();
+        rule.restrictedTags.tags = ['*'];
+        rule.restrictedTags.forbidden.attributes = ['style', 'on*'];
+        rules.push(rule);
+
+        return rules;
+      }
+    };
+  }
+
+  /**
+   * Displays and updates what HTML tags are allowed to use in a filter.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @todo Remove everything but 'attach' and 'detach' and make a proper object.
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches behavior for updating allowed HTML tags.
+   */
+  Drupal.behaviors.filterFilterHtmlUpdating = {
+
+    // The form item contains the "Allowed HTML tags" setting.
+    $allowedHTMLFormItem: null,
+
+    // The description for the "Allowed HTML tags" field.
+    $allowedHTMLDescription: null,
+
+    /**
+     * The parsed, user-entered tag list of $allowedHTMLFormItem
+     *
+     * @var {Object.<string, Drupal.FilterHTMLRule>}
+     */
+    userTags: {},
+
+    // The auto-created tag list thus far added.
+    autoTags: null,
+
+    // Track which new features have been added to the text editor.
+    newFeatures: {},
+
+    attach: function (context, settings) {
+      var that = this;
+      $(context).find('[name="filters[filter_html][settings][allowed_html]"]').once('filter-filter_html-updating').each(function () {
+        that.$allowedHTMLFormItem = $(this);
+        that.$allowedHTMLDescription = that.$allowedHTMLFormItem.closest('.js-form-item').find('.description');
+        that.userTags = that._parseSetting(this.value);
+
+        // Update the new allowed tags based on added text editor features.
+        $(document)
+          .on('drupalEditorFeatureAdded', function (e, feature) {
+            that.newFeatures[feature.name] = feature.rules;
+            that._updateAllowedTags();
+          })
+          .on('drupalEditorFeatureModified', function (e, feature) {
+            if (that.newFeatures.hasOwnProperty(feature.name)) {
+              that.newFeatures[feature.name] = feature.rules;
+              that._updateAllowedTags();
+            }
+          })
+          .on('drupalEditorFeatureRemoved', function (e, feature) {
+            if (that.newFeatures.hasOwnProperty(feature.name)) {
+              delete that.newFeatures[feature.name];
+              that._updateAllowedTags();
+            }
+          });
+
+        // When the allowed tags list is manually changed, update userTags.
+        that.$allowedHTMLFormItem.on('change.updateUserTags', function () {
+          that.userTags = _.difference(that._parseSetting(this.value), that.autoTags);
+        });
+      });
+    },
+
+    /**
+     * Updates the "Allowed HTML tags" setting and shows an informative message.
+     */
+    _updateAllowedTags: function () {
+      // Update the list of auto-created tags.
+      this.autoTags = this._calculateAutoAllowedTags(this.userTags, this.newFeatures);
+
+      // Remove any previous auto-created tag message.
+      this.$allowedHTMLDescription.find('.editor-update-message').remove();
+
+      // If any auto-created tags: insert message and update form item.
+      if (!_.isEmpty(this.autoTags)) {
+        this.$allowedHTMLDescription.append(Drupal.theme('filterFilterHTMLUpdateMessage', this.autoTags));
+        var userTagsWithoutOverrides = _.omit(this.userTags, _.keys(this.autoTags));
+        this.$allowedHTMLFormItem.val(this._generateSetting(userTagsWithoutOverrides) + ' ' + this._generateSetting(this.autoTags));
+      }
+      // Restore to original state.
+      else {
+        this.$allowedHTMLFormItem.val(this._generateSetting(this.userTags));
+      }
+    },
+
+    /**
+     * Calculates which HTML tags the added text editor buttons need to work.
+     *
+     * The filter_html filter is only concerned with the required tags, not with
+     * any properties, nor with each feature's "allowed" tags.
+     *
+     * @param {Array} userAllowedTags
+     *   The list of user-defined allowed tags.
+     * @param {object} newFeatures
+     *   A list of {@link Drupal.EditorFeature} objects' rules, keyed by
+     *   their name.
+     *
+     * @return {Array}
+     *   A list of new allowed tags.
+     */
+    _calculateAutoAllowedTags: function (userAllowedTags, newFeatures) {
+      var featureName;
+      var feature;
+      var featureRule;
+      var filterRule;
+      var tag;
+      var editorRequiredTags = {};
+      // Map the newly added Text Editor features to Drupal.FilterHtmlRule
+      // objects (to allow comparing userTags with autoTags).
+      for (featureName in newFeatures) {
+        if (newFeatures.hasOwnProperty(featureName)) {
+          feature = newFeatures[featureName];
+          for (var f = 0; f < feature.length; f++) {
+            featureRule = feature[f];
+            for (var t = 0; t < featureRule.required.tags.length; t++) {
+              tag = featureRule.required.tags[t];
+              if (!_.has(editorRequiredTags, tag)) {
+                filterRule = new Drupal.FilterHTMLRule();
+                filterRule.restrictedTags.tags = [tag];
+                // @todo Neither Drupal.FilterHtmlRule nor
+                //   Drupal.EditorFeatureHTMLRule allow for generic attribute
+                //   value restrictions, only for the "class" and "style"
+                //   attribute's values to be restricted. The filter_html filter
+                //   always disallows the "style" attribute, so we only need to
+                //   support "class" attribute value restrictions. Fix once
+                //   https://www.drupal.org/node/2567801 lands.
+                filterRule.restrictedTags.allowed.attributes = featureRule.required.attributes.slice(0);
+                filterRule.restrictedTags.allowed.classes = featureRule.required.classes.slice(0);
+                editorRequiredTags[tag] = filterRule;
+              }
+              // The tag is already allowed, add any additionally allowed
+              // attributes.
+              else {
+                filterRule = editorRequiredTags[tag];
+                filterRule.restrictedTags.allowed.attributes = _.union(filterRule.restrictedTags.allowed.attributes, featureRule.required.attributes);
+                filterRule.restrictedTags.allowed.classes = _.union(filterRule.restrictedTags.allowed.classes, featureRule.required.classes);
+              }
+            }
+          }
+        }
+      }
+
+      // Now compare userAllowedTags with editorRequiredTags, and build
+      // autoAllowedTags, which contains:
+      // - any tags in editorRequiredTags but not in userAllowedTags (i.e. tags
+      //   that are additionally going to be allowed)
+      // - any tags in editorRequiredTags that already exists in userAllowedTags
+      //   but does not allow all attributes or attribute values
+      var autoAllowedTags = {};
+      for (tag in editorRequiredTags) {
+        // If userAllowedTags does not contain a rule for this editor-required
+        // tag, then add it to the list of automatically allowed tags.
+        if (!_.has(userAllowedTags, tag)) {
+          autoAllowedTags[tag] = editorRequiredTags[tag];
+        }
+        // Otherwise, if userAllowedTags already allows this tag, then check if
+        // additional attributes and classes on this tag are required by the
+        // editor.
+        else {
+          var requiredAttributes = editorRequiredTags[tag].restrictedTags.allowed.attributes;
+          var allowedAttributes = userAllowedTags[tag].restrictedTags.allowed.attributes;
+          var needsAdditionalAttributes = requiredAttributes.length && _.difference(requiredAttributes, allowedAttributes).length;
+          var requiredClasses = editorRequiredTags[tag].restrictedTags.allowed.classes;
+          var allowedClasses = userAllowedTags[tag].restrictedTags.allowed.classes;
+          var needsAdditionalClasses = requiredClasses.length && _.difference(requiredClasses, allowedClasses).length;
+          if (needsAdditionalAttributes || needsAdditionalClasses) {
+            autoAllowedTags[tag] = userAllowedTags[tag].clone();
+          }
+          if (needsAdditionalAttributes) {
+            autoAllowedTags[tag].restrictedTags.allowed.attributes = _.union(allowedAttributes, requiredAttributes);
+          }
+          if (needsAdditionalClasses) {
+            autoAllowedTags[tag].restrictedTags.allowed.classes = _.union(allowedClasses, requiredClasses);
+          }
+        }
+      }
+
+      return autoAllowedTags;
+    },
+
+    /**
+     * Parses the value of this.$allowedHTMLFormItem.
+     *
+     * @param {string} setting
+     *   The string representation of the setting. For example:
+     *     <p class="callout"> <br> <a href hreflang>
+     *
+     * @return {Object.<string, Drupal.FilterHTMLRule>}
+     *   The corresponding text filter HTML rule objects, one per tag, keyed by
+     *   tag name.
+     */
+    _parseSetting: function (setting) {
+      var node;
+      var tag;
+      var rule;
+      var attributes;
+      var attribute;
+      var allowedTags = setting.match(/(<[^>]+>)/g);
+      var sandbox = document.createElement('div');
+      var rules = {};
+      for (var t = 0; t < allowedTags.length; t++) {
+        // Let the browser do the parsing work for us.
+        sandbox.innerHTML = allowedTags[t];
+        node = sandbox.firstChild;
+        tag = node.tagName.toLowerCase();
+
+        // Build the Drupal.FilterHtmlRule object.
+        rule = new Drupal.FilterHTMLRule();
+        // We create one rule per allowed tag, so always one tag.
+        rule.restrictedTags.tags = [tag];
+        // Add the attribute restrictions.
+        attributes = node.attributes;
+        for (var i = 0; i < attributes.length; i++) {
+          attribute = attributes.item(i);
+          var attributeName = attribute.nodeName;
+          // @todo Drupal.FilterHtmlRule does not allow for generic attribute
+          //   value restrictions, only for the "class" and "style" attribute's
+          //   values. The filter_html filter always disallows the "style"
+          //   attribute, so we only need to support "class" attribute value
+          //   restrictions. Fix once https://www.drupal.org/node/2567801 lands.
+          if (attributeName === 'class') {
+            var attributeValue = attribute.textContent;
+            rule.restrictedTags.allowed.classes = attributeValue.split(' ');
+          }
+          else {
+            rule.restrictedTags.allowed.attributes.push(attributeName);
+          }
+        }
+
+        rules[tag] = rule;
+      }
+      return rules;
+    },
+
+    /**
+     * Generates the value of this.$allowedHTMLFormItem.
+     *
+     * @param {Object.<string, Drupal.FilterHTMLRule>} tags
+     *   The parsed representation of the setting.
+     *
+     * @return {Array}
+     *   The string representation of the setting. e.g. "<p> <br> <a>"
+     */
+    _generateSetting: function (tags) {
+      return _.reduce(tags, function (setting, rule, tag) {
+        if (setting.length) {
+          setting += ' ';
+        }
+
+        setting += '<' + tag;
+        if (rule.restrictedTags.allowed.attributes.length) {
+          setting += ' ' + rule.restrictedTags.allowed.attributes.join(' ');
+        }
+        // @todo Drupal.FilterHtmlRule does not allow for generic attribute
+        //   value restrictions, only for the "class" and "style" attribute's
+        //   values. The filter_html filter always disallows the "style"
+        //   attribute, so we only need to support "class" attribute value
+        //   restrictions. Fix once https://www.drupal.org/node/2567801 lands.
+        if (rule.restrictedTags.allowed.classes.length) {
+          setting += ' class="' + rule.restrictedTags.allowed.classes.join(' ') + '"';
+        }
+
+        setting += '>';
+        return setting;
+      }, '');
+    }
+
+  };
+
+  /**
+   * Theme function for the filter_html update message.
+   *
+   * @param {Array} tags
+   *   An array of the new tags that are to be allowed.
+   *
+   * @return {string}
+   *   The corresponding HTML.
+   */
+  Drupal.theme.filterFilterHTMLUpdateMessage = function (tags) {
+    var html = '';
+    var tagList = Drupal.behaviors.filterFilterHtmlUpdating._generateSetting(tags);
+    html += '<p class="editor-update-message">';
+    html += Drupal.t('Based on the text editor configuration, these tags have automatically been added: <strong>@tag-list</strong>.', {'@tag-list': tagList});
+    html += '</p>';
+    return html;
+  };
+
+})(jQuery, _, document, window);
diff --git a/core/themes/stable/js/filter/filter.js b/core/themes/stable/js/filter/filter.js
new file mode 100644
index 0000000..cdb0795
--- /dev/null
+++ b/core/themes/stable/js/filter/filter.js
@@ -0,0 +1,39 @@
+/**
+ * @file
+ * Attaches behavior for the Filter module.
+ */
+
+(function ($) {
+
+  "use strict";
+
+  /**
+   * Displays the guidelines of the selected text format automatically.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches behavior for updating filter guidelines.
+   */
+  Drupal.behaviors.filterGuidelines = {
+    attach: function (context) {
+
+      function updateFilterGuidelines(event) {
+        var $this = $(event.target);
+        var value = $this.val();
+        $this.closest('.filter-wrapper')
+          .find('.filter-guidelines-item').hide()
+          .filter('.filter-guidelines-' + value).show();
+      }
+
+      $(context).find('.filter-guidelines').once('filter-guidelines')
+        .find(':header').hide()
+        .closest('.filter-wrapper').find('select.filter-list')
+        .on('change.filterGuidelines', updateFilterGuidelines)
+        // Need to trigger the namespaced event to avoid triggering formUpdated
+        // when initializing the select.
+        .trigger('change.filterGuidelines');
+    }
+  };
+
+})(jQuery);
diff --git a/core/themes/stable/js/history/history.js b/core/themes/stable/js/history/history.js
new file mode 100644
index 0000000..789f085
--- /dev/null
+++ b/core/themes/stable/js/history/history.js
@@ -0,0 +1,134 @@
+/**
+ * @file
+ * JavaScript API for the History module, with client-side caching.
+ *
+ * May only be loaded for authenticated users, with the History module enabled.
+ */
+
+(function ($, Drupal, drupalSettings, storage) {
+
+  "use strict";
+
+  var currentUserID = parseInt(drupalSettings.user.uid, 10);
+
+  // Any comment that is older than 30 days is automatically considered read,
+  // so for these we don't need to perform a request at all!
+  var thirtyDaysAgo = Math.round(new Date().getTime() / 1000) - 30 * 24 * 60 * 60;
+
+  // Use the data embedded in the page, if available.
+  var embeddedLastReadTimestamps = false;
+  if (drupalSettings.history && drupalSettings.history.lastReadTimestamps) {
+    embeddedLastReadTimestamps = drupalSettings.history.lastReadTimestamps;
+  }
+
+  /**
+   * @namespace
+   */
+  Drupal.history = {
+
+    /**
+     * Fetch "last read" timestamps for the given nodes.
+     *
+     * @param {Array} nodeIDs
+     *   An array of node IDs.
+     * @param {function} callback
+     *   A callback that is called after the requested timestamps were fetched.
+     */
+    fetchTimestamps: function (nodeIDs, callback) {
+      // Use the data embedded in the page, if available.
+      if (embeddedLastReadTimestamps) {
+        callback();
+        return;
+      }
+
+      $.ajax({
+        url: Drupal.url('history/get_node_read_timestamps'),
+        type: 'POST',
+        data: {'node_ids[]': nodeIDs},
+        dataType: 'json',
+        success: function (results) {
+          for (var nodeID in results) {
+            if (results.hasOwnProperty(nodeID)) {
+              storage.setItem('Drupal.history.' + currentUserID + '.' + nodeID, results[nodeID]);
+            }
+          }
+          callback();
+        }
+      });
+    },
+
+    /**
+     * Get the last read timestamp for the given node.
+     *
+     * @param {number|string} nodeID
+     *   A node ID.
+     *
+     * @return {number}
+     *   A UNIX timestamp.
+     */
+    getLastRead: function (nodeID) {
+      // Use the data embedded in the page, if available.
+      if (embeddedLastReadTimestamps && embeddedLastReadTimestamps[nodeID]) {
+        return parseInt(embeddedLastReadTimestamps[nodeID], 10);
+      }
+      return parseInt(storage.getItem('Drupal.history.' + currentUserID + '.' + nodeID) || 0, 10);
+    },
+
+    /**
+     * Marks a node as read, store the last read timestamp client-side.
+     *
+     * @param {number|string} nodeID
+     *   A node ID.
+     */
+    markAsRead: function (nodeID) {
+      $.ajax({
+        url: Drupal.url('history/' + nodeID + '/read'),
+        type: 'POST',
+        dataType: 'json',
+        success: function (timestamp) {
+          // If the data is embedded in the page, don't store on the client
+          // side.
+          if (embeddedLastReadTimestamps && embeddedLastReadTimestamps[nodeID]) {
+            return;
+          }
+
+          storage.setItem('Drupal.history.' + currentUserID + '.' + nodeID, timestamp);
+        }
+      });
+    },
+
+    /**
+     * Determines whether a server check is necessary.
+     *
+     * Any content that is >30 days old never gets a "new" or "updated"
+     * indicator. Any content that was published before the oldest known reading
+     * also never gets a "new" or "updated" indicator, because it must've been
+     * read already.
+     *
+     * @param {number|string} nodeID
+     *   A node ID.
+     * @param {number} contentTimestamp
+     *   The time at which some content (e.g. a comment) was published.
+     *
+     * @return {bool}
+     *   Whether a server check is necessary for the given node and its
+     *   timestamp.
+     */
+    needsServerCheck: function (nodeID, contentTimestamp) {
+      // First check if the content is older than 30 days, then we can bail
+      // early.
+      if (contentTimestamp < thirtyDaysAgo) {
+        return false;
+      }
+
+      // Use the data embedded in the page, if available.
+      if (embeddedLastReadTimestamps && embeddedLastReadTimestamps[nodeID]) {
+        return contentTimestamp > parseInt(embeddedLastReadTimestamps[nodeID], 10);
+      }
+
+      var minLastReadTimestamp = parseInt(storage.getItem('Drupal.history.' + currentUserID + '.' + nodeID) || 0, 10);
+      return contentTimestamp > minLastReadTimestamp;
+    }
+  };
+
+})(jQuery, Drupal, drupalSettings, window.localStorage);
diff --git a/core/themes/stable/js/history/mark-as-read.js b/core/themes/stable/js/history/mark-as-read.js
new file mode 100644
index 0000000..848d351
--- /dev/null
+++ b/core/themes/stable/js/history/mark-as-read.js
@@ -0,0 +1,23 @@
+/**
+ * @file
+ * Marks the nodes listed in drupalSettings.history.nodesToMarkAsRead as read.
+ *
+ * Uses the History module JavaScript API.
+ *
+ * @see Drupal.history
+ */
+
+(function (window, Drupal, drupalSettings) {
+
+  "use strict";
+
+  // When the window's "load" event is triggered, mark all enumerated nodes as
+  // read. This still allows for Drupal behaviors (which are triggered on the
+  // "DOMContentReady" event) to add "new" and "updated" indicators.
+  window.addEventListener('load', function () {
+    if (drupalSettings.history && drupalSettings.history.nodesToMarkAsRead) {
+      Object.keys(drupalSettings.history.nodesToMarkAsRead).forEach(Drupal.history.markAsRead);
+    }
+  });
+
+})(window, Drupal, drupalSettings);
diff --git a/core/themes/stable/js/language/language.admin.js b/core/themes/stable/js/language/language.admin.js
new file mode 100644
index 0000000..cedd78d
--- /dev/null
+++ b/core/themes/stable/js/language/language.admin.js
@@ -0,0 +1,43 @@
+/**
+ * @file
+ * Language admin behavior.
+ */
+
+(function ($, Drupal) {
+
+  "use strict";
+
+  /**
+   * Makes language negotiation inherit user interface negotiation.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attach behavior to language negotiation admin user interface.
+   */
+  Drupal.behaviors.negotiationLanguage = {
+    attach: function () {
+      var $configForm = $('#language-negotiation-configure-form');
+      var inputSelector = 'input[name$="[configurable]"]';
+      // Given a customization checkbox derive the language type being changed.
+      function toggleTable(checkbox) {
+        var $checkbox = $(checkbox);
+        // Get the language detection type such as Interface text language
+        // detection or Content language detection.
+        $checkbox.closest('.table-language-group')
+          .find('table, .tabledrag-toggle-weight')
+          .toggle($checkbox.prop('checked'));
+      }
+
+      // Bind hide/show and rearrange customization checkboxes.
+      $configForm.once('negotiation-language-admin-bind').on('change', inputSelector, function (event) {
+        toggleTable(event.target);
+      });
+      // Initially, hide language detection types that are not customized.
+      $configForm.find(inputSelector + ':not(:checked)').each(function (index, element) {
+        toggleTable(element);
+      });
+    }
+  };
+
+})(jQuery, Drupal);
diff --git a/core/themes/stable/js/locale/locale.admin.js b/core/themes/stable/js/locale/locale.admin.js
new file mode 100644
index 0000000..be54349
--- /dev/null
+++ b/core/themes/stable/js/locale/locale.admin.js
@@ -0,0 +1,116 @@
+/**
+ * @file
+ * Locale admin behavior.
+ */
+
+(function ($, Drupal) {
+
+  "use strict";
+
+  /**
+   * Marks changes of translations.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches behavior to show the user if translations has changed.
+   * @prop {Drupal~behaviorDetach} detach
+   *   Detach behavior to show the user if translations has changed.
+   */
+  Drupal.behaviors.localeTranslateDirty = {
+    attach: function () {
+      var $form = $("#locale-translate-edit-form").once('localetranslatedirty');
+      if ($form.length) {
+        // Display a notice if any row changed.
+        $form.one('formUpdated.localeTranslateDirty', 'table', function () {
+          var $marker = $(Drupal.theme('localeTranslateChangedWarning')).hide();
+          $(this).addClass('changed').before($marker);
+          $marker.fadeIn('slow');
+        });
+        // Highlight changed row.
+        $form.on('formUpdated.localeTranslateDirty', 'tr', function () {
+          var $row = $(this);
+          var $rowToMark = $row.once('localemark');
+          var marker = Drupal.theme('localeTranslateChangedMarker');
+
+          $row.addClass('changed');
+          // Add an asterisk only once if row changed.
+          if ($rowToMark.length) {
+            $rowToMark.find('td:first-child .js-form-item').append(marker);
+          }
+        });
+      }
+    },
+    detach: function (context, settings, trigger) {
+      if (trigger === 'unload') {
+        var $form = $("#locale-translate-edit-form").removeOnce('localetranslatedirty');
+        if ($form.length) {
+          $form.off('formUpdated.localeTranslateDirty');
+        }
+      }
+    }
+  };
+
+  /**
+   * Show/hide the description details on Available translation updates page.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches behavior for toggling details on the translation update page.
+   */
+  Drupal.behaviors.hideUpdateInformation = {
+    attach: function (context, settings) {
+      var $table = $('#locale-translation-status-form').once('expand-updates');
+      if ($table.length) {
+        var $tbodies = $table.find('tbody');
+
+        // Open/close the description details by toggling a tr class.
+        $tbodies.on('click keydown', '.description', function (e) {
+          if (e.keyCode && (e.keyCode !== 13 && e.keyCode !== 32)) {
+            return;
+          }
+          e.preventDefault();
+          var $tr = $(this).closest('tr');
+
+          $tr.toggleClass('expanded');
+
+          // Change screen reader text.
+          $tr.find('.locale-translation-update__prefix').text(function () {
+            if ($tr.hasClass('expanded')) {
+              return Drupal.t('Hide description');
+            }
+            else {
+              return Drupal.t('Show description');
+            }
+          });
+        });
+        $table.find('.requirements, .links').hide();
+      }
+    }
+  };
+
+  $.extend(Drupal.theme, /** @lends Drupal.theme */{
+
+    /**
+     * Creates markup for a changed translation marker.
+     *
+     * @return {string}
+     *   Markup for the marker.
+     */
+    localeTranslateChangedMarker: function () {
+      return '<abbr class="warning ajax-changed" title="' + Drupal.t('Changed') + '">*</abbr>';
+    },
+
+    /**
+     * Creates markup for the translation changed warning.
+     *
+     * @return {string}
+     *   Markup for the warning.
+     */
+    localeTranslateChangedWarning: function () {
+      return '<div class="clearfix messages messages--warning">' + Drupal.theme('localeTranslateChangedMarker') + ' ' + Drupal.t('Changes made in this table will not be saved until the form is submitted.') + '</div>';
+    }
+  });
+
+})(jQuery, Drupal);
diff --git a/core/themes/stable/js/locale/locale.bulk.js b/core/themes/stable/js/locale/locale.bulk.js
new file mode 100644
index 0000000..c6c4daf
--- /dev/null
+++ b/core/themes/stable/js/locale/locale.bulk.js
@@ -0,0 +1,38 @@
+/**
+ * @file
+ * Locale behavior.
+ */
+
+(function ($, Drupal) {
+
+  "use strict";
+
+  /**
+   * Select the language code of an imported file based on its filename.
+   *
+   * This only works if the file name ends with "LANGCODE.po".
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches behavior for preselecting language code based on filename.
+   */
+  Drupal.behaviors.importLanguageCodeSelector = {
+    attach: function (context, settings) {
+      var $form = $('#locale-translate-import-form').once('autodetect-lang');
+      if ($form.length) {
+        var $langcode = $form.find('.langcode-input');
+        $form.find('.file-import-input')
+          .on('change', function () {
+            // If the filename is fully the language code or the filename
+            // ends with a language code, pre-select that one.
+            var matches = $(this).val().match(/([^.][\.]*)([\w-]+)\.po$/);
+            if (matches && $langcode.find('option[value="' + matches[2] + '"]').length) {
+              $langcode.val(matches[2]);
+            }
+          });
+      }
+    }
+  };
+
+})(jQuery, Drupal);
diff --git a/core/themes/stable/js/locale/locale.datepicker.js b/core/themes/stable/js/locale/locale.datepicker.js
new file mode 100644
index 0000000..197fc42
--- /dev/null
+++ b/core/themes/stable/js/locale/locale.datepicker.js
@@ -0,0 +1,88 @@
+/**
+ * @file
+ * Datepicker JavaScript for the Locale module.
+ */
+
+(function ($, Drupal, drupalSettings) {
+
+  "use strict";
+
+  /**
+   * Attaches language support to the jQuery UI datepicker component.
+   *
+   * @type {Drupal~behavior}
+   */
+  Drupal.behaviors.localeDatepicker = {
+    attach: function (context, settings) {
+      // This code accesses drupalSettings and localized strings via Drupal.t().
+      // So this code should run after these are initialized. By placing it in an
+      // attach behavior this is assured.
+      $.datepicker.regional['drupal-locale'] = $.extend({
+        closeText: Drupal.t('Done'),
+        prevText: Drupal.t('Prev'),
+        nextText: Drupal.t('Next'),
+        currentText: Drupal.t('Today'),
+        monthNames: [
+          Drupal.t('January', {}, {context: "Long month name"}),
+          Drupal.t('February', {}, {context: "Long month name"}),
+          Drupal.t('March', {}, {context: "Long month name"}),
+          Drupal.t('April', {}, {context: "Long month name"}),
+          Drupal.t('May', {}, {context: "Long month name"}),
+          Drupal.t('June', {}, {context: "Long month name"}),
+          Drupal.t('July', {}, {context: "Long month name"}),
+          Drupal.t('August', {}, {context: "Long month name"}),
+          Drupal.t('September', {}, {context: "Long month name"}),
+          Drupal.t('October', {}, {context: "Long month name"}),
+          Drupal.t('November', {}, {context: "Long month name"}),
+          Drupal.t('December', {}, {context: "Long month name"})
+        ],
+        monthNamesShort: [
+          Drupal.t('Jan'),
+          Drupal.t('Feb'),
+          Drupal.t('Mar'),
+          Drupal.t('Apr'),
+          Drupal.t('May'),
+          Drupal.t('Jun'),
+          Drupal.t('Jul'),
+          Drupal.t('Aug'),
+          Drupal.t('Sep'),
+          Drupal.t('Oct'),
+          Drupal.t('Nov'),
+          Drupal.t('Dec')
+        ],
+        dayNames: [
+          Drupal.t('Sunday'),
+          Drupal.t('Monday'),
+          Drupal.t('Tuesday'),
+          Drupal.t('Wednesday'),
+          Drupal.t('Thursday'),
+          Drupal.t('Friday'),
+          Drupal.t('Saturday')
+        ],
+        dayNamesShort: [
+          Drupal.t('Sun'),
+          Drupal.t('Mon'),
+          Drupal.t('Tue'),
+          Drupal.t('Wed'),
+          Drupal.t('Thu'),
+          Drupal.t('Fri'),
+          Drupal.t('Sat')
+        ],
+        dayNamesMin: [
+          Drupal.t('Su'),
+          Drupal.t('Mo'),
+          Drupal.t('Tu'),
+          Drupal.t('We'),
+          Drupal.t('Th'),
+          Drupal.t('Fr'),
+          Drupal.t('Sa')
+        ],
+        dateFormat: Drupal.t('mm/dd/yy'),
+        firstDay: 0,
+        isRTL: 0
+      }, drupalSettings.jquery.ui.datepicker);
+      $.datepicker.setDefaults($.datepicker.regional['drupal-locale']);
+    }
+  };
+
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/themes/stable/js/menu_ui/menu_ui.admin.js b/core/themes/stable/js/menu_ui/menu_ui.admin.js
new file mode 100644
index 0000000..5120d6e
--- /dev/null
+++ b/core/themes/stable/js/menu_ui/menu_ui.admin.js
@@ -0,0 +1,68 @@
+/**
+ * @file
+ * Menu UI admin behaviors.
+ */
+
+(function ($) {
+
+  "use strict";
+
+  /**
+   *
+   * @type {Drupal~behavior}
+   */
+  Drupal.behaviors.menuUiChangeParentItems = {
+    attach: function (context, settings) {
+      var $menu = $('#edit-menu').once('menu-parent');
+      if ($menu.length) {
+        // Update the list of available parent menu items to match the initial
+        // available menus.
+        Drupal.menuUiUpdateParentList();
+
+        // Update list of available parent menu items.
+        $menu.on('change', 'input', Drupal.menuUiUpdateParentList);
+      }
+    }
+  };
+
+  /**
+   * Function to set the options of the menu parent item dropdown.
+   */
+  Drupal.menuUiUpdateParentList = function () {
+    var $menu = $('#edit-menu');
+    var values = [];
+
+    $menu.find('input:checked').each(function () {
+      // Get the names of all checked menus.
+      values.push(Drupal.checkPlain($.trim($(this).val())));
+    });
+
+    $.ajax({
+      url: location.protocol + '//' + location.host + Drupal.url('admin/structure/menu/parents'),
+      type: 'POST',
+      data: {'menus[]': values},
+      dataType: 'json',
+      success: function (options) {
+        var $select = $('#edit-menu-parent');
+        // Save key of last selected element.
+        var selected = $select.val();
+        // Remove all existing options from dropdown.
+        $select.children().remove();
+        // Add new options to dropdown. Keep a count of options for testing later.
+        var totalOptions = 0;
+        for (var machineName in options) {
+          if (options.hasOwnProperty(machineName)) {
+            $select.append(
+              $('<option ' + (machineName === selected ? ' selected="selected"' : '') + '></option>').val(machineName).text(options[machineName])
+            );
+            totalOptions++;
+          }
+        }
+
+        // Hide the parent options if there are no options for it.
+        $select.closest('div').toggle(totalOptions > 0).attr('hidden', totalOptions === 0);
+      }
+    });
+  };
+
+})(jQuery);
diff --git a/core/themes/stable/js/menu_ui/menu_ui.js b/core/themes/stable/js/menu_ui/menu_ui.js
new file mode 100644
index 0000000..935d1a3
--- /dev/null
+++ b/core/themes/stable/js/menu_ui/menu_ui.js
@@ -0,0 +1,91 @@
+/**
+ * @file
+ * Menu UI behaviors.
+ */
+
+(function ($) {
+
+  "use strict";
+
+  /**
+   * Set a summary on the menu link form.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Find the form and call `drupalSetSummary` on it.
+   */
+  Drupal.behaviors.menuUiDetailsSummaries = {
+    attach: function (context) {
+      $(context).find('.menu-link-form').drupalSetSummary(function (context) {
+        var $context = $(context);
+        if ($context.find('.js-form-item-menu-enabled input').is(':checked')) {
+          return Drupal.checkPlain($context.find('.js-form-item-menu-title input').val());
+        }
+        else {
+          return Drupal.t('Not in menu');
+        }
+      });
+    }
+  };
+
+  /**
+   * Automatically fill in a menu link title, if possible.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches change and keyup behavior for automatically filling out menu
+   *   link titles.
+   */
+  Drupal.behaviors.menuUiLinkAutomaticTitle = {
+    attach: function (context) {
+      var $context = $(context);
+      $context.find('.menu-link-form').each(function () {
+        var $this = $(this);
+        // Try to find menu settings widget elements as well as a 'title' field
+        // in the form, but play nicely with user permissions and form
+        // alterations.
+        var $checkbox = $this.find('.js-form-item-menu-enabled input');
+        var $link_title = $context.find('.js-form-item-menu-title input');
+        var $title = $this.closest('form').find('.js-form-item-title-0-value input');
+        // Bail out if we do not have all required fields.
+        if (!($checkbox.length && $link_title.length && $title.length)) {
+          return;
+        }
+        // If there is a link title already, mark it as overridden. The user
+        // expects that toggling the checkbox twice will take over the node's
+        // title.
+        if ($checkbox.is(':checked') && $link_title.val().length) {
+          $link_title.data('menuLinkAutomaticTitleOverridden', true);
+        }
+        // Whenever the value is changed manually, disable this behavior.
+        $link_title.on('keyup', function () {
+          $link_title.data('menuLinkAutomaticTitleOverridden', true);
+        });
+        // Global trigger on checkbox (do not fill-in a value when disabled).
+        $checkbox.on('change', function () {
+          if ($checkbox.is(':checked')) {
+            if (!$link_title.data('menuLinkAutomaticTitleOverridden')) {
+              $link_title.val($title.val());
+            }
+          }
+          else {
+            $link_title.val('');
+            $link_title.removeData('menuLinkAutomaticTitleOverridden');
+          }
+          $checkbox.closest('.vertical-tabs-pane').trigger('summaryUpdated');
+          $checkbox.trigger('formUpdated');
+        });
+        // Take over any title change.
+        $title.on('keyup', function () {
+          if (!$link_title.data('menuLinkAutomaticTitleOverridden') && $checkbox.is(':checked')) {
+            $link_title.val($title.val());
+            $link_title.val($title.val()).trigger('formUpdated');
+          }
+        });
+      });
+    }
+  };
+
+})(jQuery);
diff --git a/core/themes/stable/js/node/content_types.js b/core/themes/stable/js/node/content_types.js
new file mode 100644
index 0000000..912bdc9
--- /dev/null
+++ b/core/themes/stable/js/node/content_types.js
@@ -0,0 +1,62 @@
+/**
+ * @file
+ * Javascript for the node content editing form.
+ */
+
+(function ($) {
+
+  "use strict";
+
+  /**
+   * Behaviors for setting summaries on content type form.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches summary behaviors on content type edit forms.
+   */
+  Drupal.behaviors.contentTypes = {
+    attach: function (context) {
+      var $context = $(context);
+      // Provide the vertical tab summaries.
+      $context.find('#edit-submission').drupalSetSummary(function (context) {
+        var vals = [];
+        vals.push(Drupal.checkPlain($(context).find('#edit-title-label').val()) || Drupal.t('Requires a title'));
+        return vals.join(', ');
+      });
+      $context.find('#edit-workflow').drupalSetSummary(function (context) {
+        var vals = [];
+        $(context).find("input[name^='options']:checked").parent().each(function () {
+          vals.push(Drupal.checkPlain($(this).text()));
+        });
+        if (!$(context).find('#edit-options-status').is(':checked')) {
+          vals.unshift(Drupal.t('Not published'));
+        }
+        return vals.join(', ');
+      });
+      $('#edit-language', context).drupalSetSummary(function (context) {
+        var vals = [];
+
+        vals.push($(".js-form-item-language-configuration-langcode select option:selected", context).text());
+
+        $('input:checked', context).next('label').each(function () {
+          vals.push(Drupal.checkPlain($(this).text()));
+        });
+
+        return vals.join(', ');
+      });
+      $context.find('#edit-display').drupalSetSummary(function (context) {
+        var vals = [];
+        var $editContext = $(context);
+        $editContext.find('input:checked').next('label').each(function () {
+          vals.push(Drupal.checkPlain($(this).text()));
+        });
+        if (!$editContext.find('#edit-display-submitted').is(':checked')) {
+          vals.unshift(Drupal.t("Don't display post information"));
+        }
+        return vals.join(', ');
+      });
+    }
+  };
+
+})(jQuery);
diff --git a/core/themes/stable/js/node/node.js b/core/themes/stable/js/node/node.js
new file mode 100644
index 0000000..c1244c8
--- /dev/null
+++ b/core/themes/stable/js/node/node.js
@@ -0,0 +1,79 @@
+/**
+ * @file
+ * Defines Javascript behaviors for the node module.
+ */
+
+(function ($, Drupal, drupalSettings) {
+
+  "use strict";
+
+  /**
+   * Behaviors for tabs in the node edit form.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches summary behavior for tabs in the node edit form.
+   */
+  Drupal.behaviors.nodeDetailsSummaries = {
+    attach: function (context) {
+      var $context = $(context);
+      $context.find('.node-form-revision-information').drupalSetSummary(function (context) {
+        var $revisionContext = $(context);
+        var revisionCheckbox = $revisionContext.find('.js-form-item-revision input');
+
+          // Return 'New revision' if the 'Create new revision' checkbox is
+          // checked, or if the checkbox doesn't exist, but the revision log does.
+          // For users without the "Administer content" permission the checkbox
+          // won't appear, but the revision log will if the content type is set to
+          // auto-revision.
+        if (revisionCheckbox.is(':checked') || (!revisionCheckbox.length && $revisionContext.find('.js-form-item-revision-log textarea').length)) {
+          return Drupal.t('New revision');
+        }
+
+        return Drupal.t('No revision');
+      });
+
+      $context.find('.node-form-author').drupalSetSummary(function (context) {
+        var $authorContext = $(context);
+        var name = $authorContext.find('.field--name-uid input').val();
+        var date = $authorContext.find('.field--name-created input').val();
+        return date ?
+          Drupal.t('By @name on @date', {'@name': name, '@date': date}) :
+          Drupal.t('By @name', {'@name': name});
+      });
+
+      $context.find('.node-form-options').drupalSetSummary(function (context) {
+        var $optionsContext = $(context);
+        var vals = [];
+
+        if ($optionsContext.find('input').is(':checked')) {
+          $optionsContext.find('input:checked').next('label').each(function () {
+            vals.push(Drupal.checkPlain($.trim($(this).text())));
+          });
+          return vals.join(', ');
+        }
+        else {
+          return Drupal.t('Not promoted');
+        }
+      });
+
+      $context.find('fieldset.node-translation-options').drupalSetSummary(function (context) {
+        var $translationContext = $(context);
+        var translate;
+        var $checkbox = $translationContext.find('.js-form-item-translation-translate input');
+
+        if ($checkbox.size()) {
+          translate = $checkbox.is(':checked') ? Drupal.t('Needs to be updated') : Drupal.t('Does not need to be updated');
+        }
+        else {
+          $checkbox = $translationContext.find('.js-form-item-translation-retranslate input');
+          translate = $checkbox.is(':checked') ? Drupal.t('Flag other translations as outdated') : Drupal.t('Do not flag other translations as outdated');
+        }
+
+        return translate;
+      });
+    }
+  };
+
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/themes/stable/js/node/node.preview.js b/core/themes/stable/js/node/node.preview.js
new file mode 100644
index 0000000..ec48832
--- /dev/null
+++ b/core/themes/stable/js/node/node.preview.js
@@ -0,0 +1,99 @@
+/**
+ * @file
+ * Preview behaviors.
+ */
+
+(function ($, Drupal) {
+
+  "use strict";
+
+  /**
+   * Disables all non-relevant links in node previews.
+   *
+   * Destroys links (except local fragment identifiers such as href="#frag") in
+   * node previews to prevent users from leaving the page.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches confirmation prompt for clicking links in node preview mode.
+   * @prop {Drupal~behaviorDetach} detach
+   *   Detaches confirmation prompt for clicking links in node preview mode.
+   */
+  Drupal.behaviors.nodePreviewDestroyLinks = {
+    attach: function (context) {
+
+      function clickPreviewModal(event) {
+        // Only confirm leaving previews when left-clicking and user is not
+        // pressing the ALT, CTRL, META (Command key on the Macintosh keyboard)
+        // or SHIFT key.
+        if (event.button === 0 && !event.altKey && !event.ctrlKey && !event.metaKey && !event.shiftKey) {
+          event.preventDefault();
+          var $previewDialog = $('<div>' + Drupal.theme('nodePreviewModal') + '</div>').appendTo('body');
+          Drupal.dialog($previewDialog, {
+            title: Drupal.t('Leave preview?'),
+            buttons: [
+              {
+                text: Drupal.t('Cancel'),
+                click: function () {
+                  $(this).dialog('close');
+                }
+              }, {
+                text: Drupal.t('Leave preview'),
+                click: function () {
+                  window.top.location.href = event.target.href;
+                }
+              }
+            ]
+          }).showModal();
+        }
+      }
+
+      var $preview = $(context).find('.content').once('node-preview');
+      if ($(context).find('.node-preview-container').length) {
+        $preview.on('click.preview', 'a:not([href^=#], #edit-backlink, #toolbar-administration a)', clickPreviewModal);
+      }
+    },
+    detach: function (context, settings, trigger) {
+      if (trigger === 'unload') {
+        var $preview = $(context).find('.content').removeOnce('node-preview');
+        if ($preview.length) {
+          $preview.off('click.preview');
+        }
+      }
+    }
+  };
+
+  /**
+   * Switch view mode.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches automatic submit on `formUpdated.preview` events.
+   */
+  Drupal.behaviors.nodePreviewSwitchViewMode = {
+    attach: function (context) {
+      var $autosubmit = $(context).find('[data-drupal-autosubmit]').once('autosubmit');
+      if ($autosubmit.length) {
+        $autosubmit.on('formUpdated.preview', function () {
+          $(this.form).trigger('submit');
+        });
+      }
+    }
+  };
+
+  /**
+   * Theme function for node preview modal.
+   *
+   * @return {string}
+   *   Markup for the node preview modal.
+   */
+  Drupal.theme.nodePreviewModal = function () {
+    return '<p>' +
+      Drupal.t('Leaving the preview will cause unsaved changes to be lost. Are you sure you want to leave the preview?') +
+      '</p><small class="description">' +
+      Drupal.t('CTRL+Left click will prevent this dialog from showing and proceed to the clicked link.') + '</small>';
+  };
+
+})(jQuery, Drupal);
diff --git a/core/themes/stable/js/path/path.js b/core/themes/stable/js/path/path.js
new file mode 100644
index 0000000..2a7f48c
--- /dev/null
+++ b/core/themes/stable/js/path/path.js
@@ -0,0 +1,29 @@
+/**
+ * @file
+ * Attaches behaviors for the Path module.
+ */
+(function ($) {
+
+  "use strict";
+
+  /**
+   * Behaviors for settings summaries on path edit forms.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches summary behavior on path edit forms.
+   */
+  Drupal.behaviors.pathDetailsSummaries = {
+    attach: function (context) {
+      $(context).find('.path-form').drupalSetSummary(function (context) {
+        var path = $('.js-form-item-path-0-alias input').val();
+
+        return path ?
+          Drupal.t('Alias: @alias', {'@alias': path}) :
+          Drupal.t('No alias');
+      });
+    }
+  };
+
+})(jQuery);
diff --git a/core/themes/stable/js/quickedit/editors/formEditor.js b/core/themes/stable/js/quickedit/editors/formEditor.js
new file mode 100644
index 0000000..c612a28
--- /dev/null
+++ b/core/themes/stable/js/quickedit/editors/formEditor.js
@@ -0,0 +1,255 @@
+/**
+ * @file
+ * Form-based in-place editor. Works for any field type.
+ */
+
+(function ($, Drupal) {
+
+  "use strict";
+
+  /**
+   * @constructor
+   *
+   * @augments Drupal.quickedit.EditorView
+   */
+  Drupal.quickedit.editors.form = Drupal.quickedit.EditorView.extend(/** @lends Drupal.quickedit.editors.form# */{
+
+    /**
+     * Tracks form container DOM element that is used while in-place editing.
+     *
+     * @type {jQuery}
+     */
+    $formContainer: null,
+
+    /**
+     * Holds the {@link Drupal.Ajax} object.
+     *
+     * @type {Drupal.Ajax}
+     */
+    formSaveAjax: null,
+
+    /**
+     * @inheritdoc
+     *
+     * @param {object} fieldModel
+     *   The field model that holds the state.
+     * @param {string} state
+     *   The state to change to.
+     */
+    stateChange: function (fieldModel, state) {
+      var from = fieldModel.previous('state');
+      var to = state;
+      switch (to) {
+        case 'inactive':
+          break;
+
+        case 'candidate':
+          if (from !== 'inactive') {
+            this.removeForm();
+          }
+          break;
+
+        case 'highlighted':
+          break;
+
+        case 'activating':
+          // If coming from an invalid state, then the form is already loaded.
+          if (from !== 'invalid') {
+            this.loadForm();
+          }
+          break;
+
+        case 'active':
+          break;
+
+        case 'changed':
+          break;
+
+        case 'saving':
+          this.save();
+          break;
+
+        case 'saved':
+          break;
+
+        case 'invalid':
+          this.showValidationErrors();
+          break;
+      }
+    },
+
+    /**
+     * @inheritdoc
+     *
+     * @return {object}
+     *   A settings object for the quick edit UI.
+     */
+    getQuickEditUISettings: function () {
+      return {padding: true, unifiedToolbar: true, fullWidthToolbar: true, popup: true};
+    },
+
+    /**
+     * Loads the form for this field, displays it on top of the actual field.
+     */
+    loadForm: function () {
+      var fieldModel = this.fieldModel;
+
+      // Generate a DOM-compatible ID for the form container DOM element.
+      var id = 'quickedit-form-for-' + fieldModel.id.replace(/[\/\[\]]/g, '_');
+
+      // Render form container.
+      var $formContainer = this.$formContainer = $(Drupal.theme('quickeditFormContainer', {
+        id: id,
+        loadingMsg: Drupal.t('Loading…')
+      }));
+      $formContainer
+        .find('.quickedit-form')
+        .addClass('quickedit-editable quickedit-highlighted quickedit-editing')
+        .attr('role', 'dialog');
+
+      // Insert form container in DOM.
+      if (this.$el.css('display') === 'inline') {
+        $formContainer.prependTo(this.$el.offsetParent());
+        // Position the form container to render on top of the field's element.
+        var pos = this.$el.position();
+        $formContainer.css('left', pos.left).css('top', pos.top);
+      }
+      else {
+        $formContainer.insertBefore(this.$el);
+      }
+
+      // Load form, insert it into the form container and attach event handlers.
+      var formOptions = {
+        fieldID: fieldModel.get('fieldID'),
+        $el: this.$el,
+        nocssjs: false,
+        // Reset an existing entry for this entity in the PrivateTempStore (if
+        // any) when loading the field. Logically speaking, this should happen
+        // in a separate request because this is an entity-level operation, not
+        // a field-level operation. But that would require an additional
+        // request, that might not even be necessary: it is only when a user
+        // loads a first changed field for an entity that this needs to happen:
+        // precisely now!
+        reset: !fieldModel.get('entity').get('inTempStore')
+      };
+      Drupal.quickedit.util.form.load(formOptions, function (form, ajax) {
+        Drupal.AjaxCommands.prototype.insert(ajax, {
+          data: form,
+          selector: '#' + id + ' .placeholder'
+        });
+
+        $formContainer
+          .on('formUpdated.quickedit', ':input', function (event) {
+            var state = fieldModel.get('state');
+            // If the form is in an invalid state, it will persist on the page.
+            // Set the field to activating so that the user can correct the
+            // invalid value.
+            if (state === 'invalid') {
+              fieldModel.set('state', 'activating');
+            }
+            // Otherwise assume that the fieldModel is in a candidate state and
+            // set it to changed on formUpdate.
+            else {
+              fieldModel.set('state', 'changed');
+            }
+          })
+          .on('keypress.quickedit', 'input', function (event) {
+            if (event.keyCode === 13) {
+              return false;
+            }
+          });
+
+        // The in-place editor has loaded; change state to 'active'.
+        fieldModel.set('state', 'active');
+      });
+    },
+
+    /**
+     * Removes the form for this field, detaches behaviors and event handlers.
+     */
+    removeForm: function () {
+      if (this.$formContainer === null) {
+        return;
+      }
+
+      delete this.formSaveAjax;
+      // Allow form widgets to detach properly.
+      Drupal.detachBehaviors(this.$formContainer.get(0), null, 'unload');
+      this.$formContainer
+        .off('change.quickedit', ':input')
+        .off('keypress.quickedit', 'input')
+        .remove();
+      this.$formContainer = null;
+    },
+
+    /**
+     * @inheritdoc
+     */
+    save: function () {
+      var $formContainer = this.$formContainer;
+      var $submit = $formContainer.find('.quickedit-form-submit');
+      var editorModel = this.model;
+      var fieldModel = this.fieldModel;
+
+      function cleanUpAjax() {
+        Drupal.quickedit.util.form.unajaxifySaving(formSaveAjax);
+        formSaveAjax = null;
+      }
+
+      // Create an AJAX object for the form associated with the field.
+      var formSaveAjax = Drupal.quickedit.util.form.ajaxifySaving({
+        nocssjs: false,
+        other_view_modes: fieldModel.findOtherViewModes()
+      }, $submit);
+
+      // Successfully saved.
+      formSaveAjax.commands.quickeditFieldFormSaved = function (ajax, response, status) {
+        cleanUpAjax();
+        // First, transition the state to 'saved'.
+        fieldModel.set('state', 'saved');
+        // Second, set the 'htmlForOtherViewModes' attribute, so that when this
+        // field is rerendered, the change can be propagated to other instances
+        // of this field, which may be displayed in different view modes.
+        fieldModel.set('htmlForOtherViewModes', response.other_view_modes);
+        // Finally, set the 'html' attribute on the field model. This will cause
+        // the field to be rerendered.
+        _.defer(function () {
+          fieldModel.set('html', response.data);
+        });
+      };
+
+      // Unsuccessfully saved; validation errors.
+      formSaveAjax.commands.quickeditFieldFormValidationErrors = function (ajax, response, status) {
+        editorModel.set('validationErrors', response.data);
+        fieldModel.set('state', 'invalid');
+      };
+
+      // The quickeditFieldForm AJAX command is called upon attempting to save
+      // the form; Form API will mark which form items have errors, if any. This
+      // command is invoked only if validation errors exist and then it runs
+      // before editFieldFormValidationErrors().
+      formSaveAjax.commands.quickeditFieldForm = function (ajax, response, status) {
+        Drupal.AjaxCommands.prototype.insert(ajax, {
+          data: response.data,
+          selector: '#' + $formContainer.attr('id') + ' form'
+        });
+      };
+
+      // Click the form's submit button; the scoped AJAX commands above will
+      // handle the server's response.
+      $submit.trigger('click.quickedit');
+    },
+
+    /**
+     * @inheritdoc
+     */
+    showValidationErrors: function () {
+      this.$formContainer
+        .find('.quickedit-form')
+        .addClass('quickedit-validation-error')
+        .find('form')
+        .prepend(this.model.get('validationErrors'));
+    }
+  });
+
+})(jQuery, Drupal);
diff --git a/core/themes/stable/js/quickedit/editors/plainTextEditor.js b/core/themes/stable/js/quickedit/editors/plainTextEditor.js
new file mode 100644
index 0000000..077a539
--- /dev/null
+++ b/core/themes/stable/js/quickedit/editors/plainTextEditor.js
@@ -0,0 +1,144 @@
+/**
+ * @file
+ * ContentEditable-based in-place editor for plain text content.
+ */
+
+(function ($, _, Drupal) {
+
+  "use strict";
+
+  Drupal.quickedit.editors.plain_text = Drupal.quickedit.EditorView.extend(/** @lends Drupal.quickedit.editors.plain_text# */{
+
+    /**
+     * Stores the textual DOM element that is being in-place edited.
+     */
+    $textElement: null,
+
+    /**
+     * @constructs
+     *
+     * @augments Drupal.quickedit.EditorView
+     *
+     * @param {object} options
+     *   Options for the plain text editor.
+     */
+    initialize: function (options) {
+      Drupal.quickedit.EditorView.prototype.initialize.call(this, options);
+
+      var editorModel = this.model;
+      var fieldModel = this.fieldModel;
+
+      // Store the original value of this field. Necessary for reverting
+      // changes.
+      var $textElement;
+      var $fieldItems = this.$el.find('.field__item');
+      if ($fieldItems.length) {
+        $textElement = this.$textElement = $fieldItems.eq(0);
+      }
+      else {
+        $textElement = this.$textElement = this.$el;
+      }
+      editorModel.set('originalValue', $.trim(this.$textElement.text()));
+
+      // Sets the state to 'changed' whenever the value changes.
+      var previousText = editorModel.get('originalValue');
+      $textElement.on('keyup paste', function (event) {
+        var currentText = $.trim($textElement.text());
+        if (previousText !== currentText) {
+          previousText = currentText;
+          editorModel.set('currentValue', currentText);
+          fieldModel.set('state', 'changed');
+        }
+      });
+    },
+
+    /**
+     * @inheritdoc
+     *
+     * @return {jQuery}
+     *   The text element for the plain text editor.
+     */
+    getEditedElement: function () {
+      return this.$textElement;
+    },
+
+    /**
+     * @inheritdoc
+     *
+     * @param {object} fieldModel
+     *   The field model that holds the state.
+     * @param {string} state
+     *   The state to change to.
+     * @param {object} options
+     *   State options, if needed by the state change.
+     */
+    stateChange: function (fieldModel, state, options) {
+      var from = fieldModel.previous('state');
+      var to = state;
+      switch (to) {
+        case 'inactive':
+          break;
+
+        case 'candidate':
+          if (from !== 'inactive') {
+            this.$textElement.removeAttr('contenteditable');
+          }
+          if (from === 'invalid') {
+            this.removeValidationErrors();
+          }
+          break;
+
+        case 'highlighted':
+          break;
+
+        case 'activating':
+          // Defer updating the field model until the current state change has
+          // propagated, to not trigger a nested state change event.
+          _.defer(function () {
+            fieldModel.set('state', 'active');
+          });
+          break;
+
+        case 'active':
+          this.$textElement.attr('contenteditable', 'true');
+          break;
+
+        case 'changed':
+          break;
+
+        case 'saving':
+          if (from === 'invalid') {
+            this.removeValidationErrors();
+          }
+          this.save(options);
+          break;
+
+        case 'saved':
+          break;
+
+        case 'invalid':
+          this.showValidationErrors();
+          break;
+      }
+    },
+
+    /**
+     * @inheritdoc
+     *
+     * @return {object}
+     *   A settings object for the quick edit UI.
+     */
+    getQuickEditUISettings: function () {
+      return {padding: true, unifiedToolbar: false, fullWidthToolbar: false, popup: false};
+    },
+
+    /**
+     * @inheritdoc
+     */
+    revert: function () {
+      this.$textElement.html(this.model.get('originalValue'));
+    }
+
+  });
+
+})(jQuery, _, Drupal);
diff --git a/core/themes/stable/js/quickedit/models/AppModel.js b/core/themes/stable/js/quickedit/models/AppModel.js
new file mode 100644
index 0000000..bce8aae
--- /dev/null
+++ b/core/themes/stable/js/quickedit/models/AppModel.js
@@ -0,0 +1,57 @@
+/**
+ * @file
+ * A Backbone Model for the state of the in-place editing application.
+ *
+ * @see Drupal.quickedit.AppView
+ */
+
+(function (Backbone, Drupal) {
+
+  "use strict";
+
+  /**
+   * @constructor
+   *
+   * @augments Backbone.Model
+   */
+  Drupal.quickedit.AppModel = Backbone.Model.extend(/** @lends Drupal.quickedit.AppModel# */{
+
+    /**
+     * @type {object}
+     *
+     * @prop {Drupal.quickedit.FieldModel} highlightedField
+     * @prop {Drupal.quickedit.FieldModel} activeField
+     * @prop {Drupal.dialog~dialogDefinition} activeModal
+     */
+    defaults: /** @lends Drupal.quickedit.AppModel# */{
+
+      /**
+       * The currently state='highlighted' Drupal.quickedit.FieldModel, if any.
+       *
+       * @type {Drupal.quickedit.FieldModel}
+       *
+       * @see Drupal.quickedit.FieldModel.states
+       */
+      highlightedField: null,
+
+      /**
+       * The currently state = 'active' Drupal.quickedit.FieldModel, if any.
+       *
+       * @type {Drupal.quickedit.FieldModel}
+       *
+       * @see Drupal.quickedit.FieldModel.states
+       */
+      activeField: null,
+
+      /**
+       * Reference to a {@link Drupal.dialog} instance if a state change
+       * requires confirmation.
+       *
+       * @type {Drupal.dialog~dialogDefinition}
+       */
+      activeModal: null
+    }
+
+  });
+
+}(Backbone, Drupal));
diff --git a/core/themes/stable/js/quickedit/models/BaseModel.js b/core/themes/stable/js/quickedit/models/BaseModel.js
new file mode 100644
index 0000000..718efae
--- /dev/null
+++ b/core/themes/stable/js/quickedit/models/BaseModel.js
@@ -0,0 +1,60 @@
+/**
+ * @file
+ * A Backbone Model subclass that enforces validation when calling set().
+ */
+
+(function (Backbone) {
+
+  "use strict";
+
+  Drupal.quickedit.BaseModel = Backbone.Model.extend(/** @lends Drupal.quickedit.BaseModel# */{
+
+    /**
+     * @constructs
+     *
+     * @augments Backbone.Model
+     *
+     * @param {object} options
+     *   Options for the base model-
+     *
+     * @return {Drupal.quickedit.BaseModel}
+     *   A quickedit base model.
+     */
+    initialize: function (options) {
+      this.__initialized = true;
+      return Backbone.Model.prototype.initialize.call(this, options);
+    },
+
+    /**
+     * Set a value on the model
+     *
+     * @param {object|string} key
+     *   The key to set a value for.
+     * @param {*} val
+     *   The value to set.
+     * @param {object} [options]
+     *   Options for the model.
+     *
+     * @return {*}
+     *   The result of `Backbone.Model.prototype.set` with the specified
+     *   parameters.
+     */
+    set: function (key, val, options) {
+      if (this.__initialized) {
+        // Deal with both the "key", value and {key:value}-style arguments.
+        if (typeof key === 'object') {
+          key.validate = true;
+        }
+        else {
+          if (!options) {
+            options = {};
+          }
+          options.validate = true;
+        }
+      }
+      return Backbone.Model.prototype.set.call(this, key, val, options);
+    }
+
+  });
+
+}(Backbone));
diff --git a/core/themes/stable/js/quickedit/models/EditorModel.js b/core/themes/stable/js/quickedit/models/EditorModel.js
new file mode 100644
index 0000000..142c9cc
--- /dev/null
+++ b/core/themes/stable/js/quickedit/models/EditorModel.js
@@ -0,0 +1,54 @@
+/**
+ * @file
+ * A Backbone Model for the state of an in-place editor.
+ *
+ * @see Drupal.quickedit.EditorView
+ */
+
+(function (Backbone, Drupal) {
+
+  "use strict";
+
+  /**
+   * @constructor
+   *
+   * @augments Backbone.Model
+   */
+  Drupal.quickedit.EditorModel = Backbone.Model.extend(/** @lends Drupal.quickedit.EditorModel# */{
+
+    /**
+     * @type {object}
+     *
+     * @prop {string} originalValue
+     * @prop {string} currentValue
+     * @prop {Array} validationErrors
+     */
+    defaults: /** @lends Drupal.quickedit.EditorModel# */{
+
+      /**
+       * Not the full HTML representation of this field, but the "actual"
+       * original value of the field, stored by the used in-place editor, and
+       * in a representation that can be chosen by the in-place editor.
+       *
+       * @type {string}
+       */
+      originalValue: null,
+
+      /**
+       * Analogous to originalValue, but the current value.
+       *
+       * @type {string}
+       */
+      currentValue: null,
+
+      /**
+       * Stores any validation errors to be rendered.
+       *
+       * @type {Array}
+       */
+      validationErrors: null
+    }
+
+  });
+
+}(Backbone, Drupal));
diff --git a/core/themes/stable/js/quickedit/models/EntityModel.js b/core/themes/stable/js/quickedit/models/EntityModel.js
new file mode 100644
index 0000000..322acb3
--- /dev/null
+++ b/core/themes/stable/js/quickedit/models/EntityModel.js
@@ -0,0 +1,741 @@
+/**
+ * @file
+ * A Backbone Model for the state of an in-place editable entity in the DOM.
+ */
+
+(function (_, $, Backbone, Drupal) {
+
+  "use strict";
+
+  Drupal.quickedit.EntityModel = Drupal.quickedit.BaseModel.extend(/** @lends Drupal.quickedit.EntityModel# */{
+
+    /**
+     * @type {object}
+     */
+    defaults: /** @lends Drupal.quickedit.EntityModel# */{
+
+      /**
+       * The DOM element that represents this entity.
+       *
+       * It may seem bizarre to have a DOM element in a Backbone Model, but we
+       * need to be able to map entities in the DOM to EntityModels in memory.
+       *
+       * @type {HTMLElement}
+       */
+      el: null,
+
+      /**
+       * An entity ID, of the form `<entity type>/<entity ID>`
+       *
+       * @example
+       * "node/1"
+       *
+       * @type {string}
+       */
+      entityID: null,
+
+      /**
+       * An entity instance ID.
+       *
+       * The first instance of a specific entity (i.e. with a given entity ID)
+       * is assigned 0, the second 1, and so on.
+       *
+       * @type {number}
+       */
+      entityInstanceID: null,
+
+      /**
+       * The unique ID of this entity instance on the page, of the form
+       * `<entity type>/<entity ID>[entity instance ID]`
+       *
+       * @example
+       * "node/1[0]"
+       *
+       * @type {string}
+       */
+      id: null,
+
+      /**
+       * The label of the entity.
+       *
+       * @type {string}
+       */
+      label: null,
+
+      /**
+       * A FieldCollection for all fields of the entity.
+       *
+       * @type {Drupal.quickedit.FieldCollection}
+       *
+       * @see Drupal.quickedit.FieldCollection
+       */
+      fields: null,
+
+      // The attributes below are stateful. The ones above will never change
+      // during the life of a EntityModel instance.
+
+      /**
+       * Indicates whether this entity is currently being edited in-place.
+       *
+       * @type {bool}
+       */
+      isActive: false,
+
+      /**
+       * Whether one or more fields are already been stored in PrivateTempStore.
+       *
+       * @type {bool}
+       */
+      inTempStore: false,
+
+      /**
+       * Indicates whether a "Save" button is necessary or not.
+       *
+       * Whether one or more fields have already been stored in PrivateTempStore
+       * *or* the field that's currently being edited is in the 'changed' or a
+       * later state.
+       *
+       * @type {bool}
+       */
+      isDirty: false,
+
+      /**
+       * Whether the request to the server has been made to commit this entity.
+       *
+       * Used to prevent multiple such requests.
+       *
+       * @type {bool}
+       */
+      isCommitting: false,
+
+      /**
+       * The current processing state of an entity.
+       *
+       * @type {string}
+       */
+      state: 'closed',
+
+      /**
+       * IDs of fields whose new values have been stored in PrivateTempStore.
+       *
+       * We must store this on the EntityModel as well (even though it already
+       * is on the FieldModel) because when a field is rerendered, its
+       * FieldModel is destroyed and this allows us to transition it back to
+       * the proper state.
+       *
+       * @type {Array.<string>}
+       */
+      fieldsInTempStore: [],
+
+      /**
+       * A flag the tells the application that this EntityModel must be reloaded
+       * in order to restore the original values to its fields in the client.
+       *
+       * @type {bool}
+       */
+      reload: false
+    },
+
+    /**
+     * @constructs
+     *
+     * @augments Drupal.quickedit.BaseModel
+     */
+    initialize: function () {
+      this.set('fields', new Drupal.quickedit.FieldCollection());
+
+      // Respond to entity state changes.
+      this.listenTo(this, 'change:state', this.stateChange);
+
+      // The state of the entity is largely dependent on the state of its
+      // fields.
+      this.listenTo(this.get('fields'), 'change:state', this.fieldStateChange);
+
+      // Call Drupal.quickedit.BaseModel's initialize() method.
+      Drupal.quickedit.BaseModel.prototype.initialize.call(this);
+    },
+
+    /**
+     * Updates FieldModels' states when an EntityModel change occurs.
+     *
+     * @param {Drupal.quickedit.EntityModel} entityModel
+     *   The entity model
+     * @param {string} state
+     *   The state of the associated entity. One of
+     *   {@link Drupal.quickedit.EntityModel.states}.
+     * @param {object} options
+     *   Options for the entity model.
+     */
+    stateChange: function (entityModel, state, options) {
+      var to = state;
+      switch (to) {
+        case 'closed':
+          this.set({
+            isActive: false,
+            inTempStore: false,
+            isDirty: false
+          });
+          break;
+
+        case 'launching':
+          break;
+
+        case 'opening':
+          // Set the fields to candidate state.
+          entityModel.get('fields').each(function (fieldModel) {
+            fieldModel.set('state', 'candidate', options);
+          });
+          break;
+
+        case 'opened':
+          // The entity is now ready for editing!
+          this.set('isActive', true);
+          break;
+
+        case 'committing':
+          // The user indicated they want to save the entity.
+          var fields = this.get('fields');
+          // For fields that are in an active state, transition them to
+          // candidate.
+          fields.chain()
+            .filter(function (fieldModel) {
+              return _.intersection([fieldModel.get('state')], ['active']).length;
+            })
+            .each(function (fieldModel) {
+              fieldModel.set('state', 'candidate');
+            });
+          // For fields that are in a changed state, field values must first be
+          // stored in PrivateTempStore.
+          fields.chain()
+            .filter(function (fieldModel) {
+              return _.intersection([fieldModel.get('state')], Drupal.quickedit.app.changedFieldStates).length;
+            })
+            .each(function (fieldModel) {
+              fieldModel.set('state', 'saving');
+            });
+          break;
+
+        case 'deactivating':
+          var changedFields = this.get('fields')
+            .filter(function (fieldModel) {
+              return _.intersection([fieldModel.get('state')], ['changed', 'invalid']).length;
+            });
+          // If the entity contains unconfirmed or unsaved changes, return the
+          // entity to an opened state and ask the user if they would like to
+          // save the changes or discard the changes.
+          //   1. One of the fields is in a changed state. The changed field
+          //   might just be a change in the client or it might have been saved
+          //   to tempstore.
+          //   2. The saved flag is empty and the confirmed flag is empty. If
+          //   the entity has been saved to the server, the fields changed in
+          //   the client are irrelevant. If the changes are confirmed, then
+          //   proceed to set the fields to candidate state.
+          if ((changedFields.length || this.get('fieldsInTempStore').length) && (!options.saved && !options.confirmed)) {
+            // Cancel deactivation until the user confirms save or discard.
+            this.set('state', 'opened', {confirming: true});
+            // An action in reaction to state change must be deferred.
+            _.defer(function () {
+              Drupal.quickedit.app.confirmEntityDeactivation(entityModel);
+            });
+          }
+          else {
+            var invalidFields = this.get('fields')
+              .filter(function (fieldModel) {
+                return _.intersection([fieldModel.get('state')], ['invalid']).length;
+              });
+            // Indicate if this EntityModel needs to be reloaded in order to
+            // restore the original values of its fields.
+            entityModel.set('reload', (this.get('fieldsInTempStore').length || invalidFields.length));
+            // Set all fields to the 'candidate' state. A changed field may have
+            // to go through confirmation first.
+            entityModel.get('fields').each(function (fieldModel) {
+              // If the field is already in the candidate state, trigger a
+              // change event so that the entityModel can move to the next state
+              // in deactivation.
+              if (_.intersection([fieldModel.get('state')], ['candidate', 'highlighted']).length) {
+                fieldModel.trigger('change:state', fieldModel, fieldModel.get('state'), options);
+              }
+              else {
+                fieldModel.set('state', 'candidate', options);
+              }
+            });
+          }
+          break;
+
+        case 'closing':
+          // Set all fields to the 'inactive' state.
+          options.reason = 'stop';
+          this.get('fields').each(function (fieldModel) {
+            fieldModel.set({
+              inTempStore: false,
+              state: 'inactive'
+            }, options);
+          });
+          break;
+      }
+    },
+
+    /**
+     * Updates a Field and Entity model's "inTempStore" when appropriate.
+     *
+     * Helper function.
+     *
+     * @param {Drupal.quickedit.EntityModel} entityModel
+     *   The model of the entity for which a field's state attribute has
+     *   changed.
+     * @param {Drupal.quickedit.FieldModel} fieldModel
+     *   The model of the field whose state attribute has changed.
+     *
+     * @see Drupal.quickedit.EntityModel#fieldStateChange
+     */
+    _updateInTempStoreAttributes: function (entityModel, fieldModel) {
+      var current = fieldModel.get('state');
+      var previous = fieldModel.previous('state');
+      var fieldsInTempStore = entityModel.get('fieldsInTempStore');
+      // If the fieldModel changed to the 'saved' state: remember that this
+      // field was saved to PrivateTempStore.
+      if (current === 'saved') {
+        // Mark the entity as saved in PrivateTempStore, so that we can pass the
+        // proper "reset PrivateTempStore" boolean value when communicating with
+        // the server.
+        entityModel.set('inTempStore', true);
+        // Mark the field as saved in PrivateTempStore, so that visual
+        // indicators signifying just that may be rendered.
+        fieldModel.set('inTempStore', true);
+        // Remember that this field is in PrivateTempStore, restore when
+        // rerendered.
+        fieldsInTempStore.push(fieldModel.get('fieldID'));
+        fieldsInTempStore = _.uniq(fieldsInTempStore);
+        entityModel.set('fieldsInTempStore', fieldsInTempStore);
+      }
+      // If the fieldModel changed to the 'candidate' state from the
+      // 'inactive' state, then this is a field for this entity that got
+      // rerendered. Restore its previous 'inTempStore' attribute value.
+      else if (current === 'candidate' && previous === 'inactive') {
+        fieldModel.set('inTempStore', _.intersection([fieldModel.get('fieldID')], fieldsInTempStore).length > 0);
+      }
+    },
+
+    /**
+     * Reacts to state changes in this entity's fields.
+     *
+     * @param {Drupal.quickedit.FieldModel} fieldModel
+     *   The model of the field whose state attribute changed.
+     * @param {string} state
+     *   The state of the associated field. One of
+     *   {@link Drupal.quickedit.FieldModel.states}.
+     */
+    fieldStateChange: function (fieldModel, state) {
+      var entityModel = this;
+      var fieldState = state;
+      // Switch on the entityModel state.
+      // The EntityModel responds to FieldModel state changes as a function of
+      // its state. For example, a field switching back to 'candidate' state
+      // when its entity is in the 'opened' state has no effect on the entity.
+      // But that same switch back to 'candidate' state of a field when the
+      // entity is in the 'committing' state might allow the entity to proceed
+      // with the commit flow.
+      switch (this.get('state')) {
+        case 'closed':
+        case 'launching':
+          // It should be impossible to reach these: fields can't change state
+          // while the entity is closed or still launching.
+          break;
+
+        case 'opening':
+          // We must change the entity to the 'opened' state, but it must first
+          // be confirmed that all of its fieldModels have transitioned to the
+          // 'candidate' state.
+          // We do this here, because this is called every time a fieldModel
+          // changes state, hence each time this is called, we get closer to the
+          // goal of having all fieldModels in the 'candidate' state.
+          // A state change in reaction to another state change must be
+          // deferred.
+          _.defer(function () {
+            entityModel.set('state', 'opened', {
+              'accept-field-states': Drupal.quickedit.app.readyFieldStates
+            });
+          });
+          break;
+
+        case 'opened':
+          // Set the isDirty attribute when appropriate so that it is known when
+          // to display the "Save" button in the entity toolbar.
+          // Note that once a field has been changed, there's no way to discard
+          // that change, hence it will have to be saved into PrivateTempStore,
+          // or the in-place editing of this field will have to be stopped
+          // completely. In other words: once any field enters the 'changed'
+          // field, then for the remainder of the in-place editing session, the
+          // entity is by definition dirty.
+          if (fieldState === 'changed') {
+            entityModel.set('isDirty', true);
+          }
+          else {
+            this._updateInTempStoreAttributes(entityModel, fieldModel);
+          }
+          break;
+
+        case 'committing':
+          // If the field save returned a validation error, set the state of the
+          // entity back to 'opened'.
+          if (fieldState === 'invalid') {
+            // A state change in reaction to another state change must be
+            // deferred.
+            _.defer(function () {
+              entityModel.set('state', 'opened', {reason: 'invalid'});
+            });
+          }
+          else {
+            this._updateInTempStoreAttributes(entityModel, fieldModel);
+          }
+
+          // Attempt to save the entity. If the entity's fields are not yet all
+          // in a ready state, the save will not be processed.
+          var options = {
+            'accept-field-states': Drupal.quickedit.app.readyFieldStates
+          };
+          if (entityModel.set('isCommitting', true, options)) {
+            entityModel.save({
+              success: function () {
+                entityModel.set({
+                  state: 'deactivating',
+                  isCommitting: false
+                }, {saved: true});
+              },
+              error: function () {
+                // Reset the "isCommitting" mutex.
+                entityModel.set('isCommitting', false);
+                // Change the state back to "opened", to allow the user to hit
+                // the "Save" button again.
+                entityModel.set('state', 'opened', {reason: 'networkerror'});
+                // Show a modal to inform the user of the network error.
+                var message = Drupal.t('Your changes to <q>@entity-title</q> could not be saved, either due to a website problem or a network connection problem.<br>Please try again.', {'@entity-title': entityModel.get('label')});
+                Drupal.quickedit.util.networkErrorModal(Drupal.t('Network problem!'), message);
+              }
+            });
+          }
+          break;
+
+        case 'deactivating':
+          // When setting the entity to 'closing', require that all fieldModels
+          // are in either the 'candidate' or 'highlighted' state.
+          // A state change in reaction to another state change must be
+          // deferred.
+          _.defer(function () {
+            entityModel.set('state', 'closing', {
+              'accept-field-states': Drupal.quickedit.app.readyFieldStates
+            });
+          });
+          break;
+
+        case 'closing':
+          // When setting the entity to 'closed', require that all fieldModels
+          // are in the 'inactive' state.
+          // A state change in reaction to another state change must be
+          // deferred.
+          _.defer(function () {
+            entityModel.set('state', 'closed', {
+              'accept-field-states': ['inactive']
+            });
+          });
+          break;
+      }
+    },
+
+    /**
+     * Fires an AJAX request to the REST save URL for an entity.
+     *
+     * @param {object} options
+     *   An object of options that contains:
+     * @param {function} [options.success]
+     *   A function to invoke if the entity is successfully saved.
+     */
+    save: function (options) {
+      var entityModel = this;
+
+      // Create a Drupal.ajax instance to save the entity.
+      var entitySaverAjax = Drupal.ajax({
+        url: Drupal.url('quickedit/entity/' + entityModel.get('entityID')),
+        error: function () {
+          // Let the Drupal.quickedit.EntityModel Backbone model's error()
+          // method handle errors.
+          options.error.call(entityModel);
+        }
+      });
+      // Entity saved successfully.
+      entitySaverAjax.commands.quickeditEntitySaved = function (ajax, response, status) {
+        // All fields have been moved from PrivateTempStore to permanent
+        // storage, update the "inTempStore" attribute on FieldModels, on the
+        // EntityModel and clear EntityModel's "fieldInTempStore" attribute.
+        entityModel.get('fields').each(function (fieldModel) {
+          fieldModel.set('inTempStore', false);
+        });
+        entityModel.set('inTempStore', false);
+        entityModel.set('fieldsInTempStore', []);
+
+        // Invoke the optional success callback.
+        if (options.success) {
+          options.success.call(entityModel);
+        }
+      };
+      // Trigger the AJAX request, which will will return the
+      // quickeditEntitySaved AJAX command to which we then react.
+      entitySaverAjax.execute();
+    },
+
+    /**
+     * Validate the entity model.
+     *
+     * @param {object} attrs
+     *   The attributes changes in the save or set call.
+     * @param {object} options
+     *   An object with the following option:
+     * @param {string} [options.reason]
+     *   A string that conveys a particular reason to allow for an exceptional
+     *   state change.
+     * @param {Array} options.accept-field-states
+     *   An array of strings that represent field states that the entities must
+     *   be in to validate. For example, if `accept-field-states` is
+     *   `['candidate', 'highlighted']`, then all the fields of the entity must
+     *   be in either of these two states for the save or set call to
+     *   validate and proceed.
+     *
+     * @return {string}
+     *   A string to say something about the state of the entity model.
+     */
+    validate: function (attrs, options) {
+      var acceptedFieldStates = options['accept-field-states'] || [];
+
+      // Validate state change.
+      var currentState = this.get('state');
+      var nextState = attrs.state;
+      if (currentState !== nextState) {
+        // Ensure it's a valid state.
+        if (_.indexOf(this.constructor.states, nextState) === -1) {
+          return '"' + nextState + '" is an invalid state';
+        }
+
+        // Ensure it's a state change that is allowed.
+        // Check if the acceptStateChange function accepts it.
+        if (!this._acceptStateChange(currentState, nextState, options)) {
+          return 'state change not accepted';
+        }
+        // If that function accepts it, then ensure all fields are also in an
+        // acceptable state.
+        else if (!this._fieldsHaveAcceptableStates(acceptedFieldStates)) {
+          return 'state change not accepted because fields are not in acceptable state';
+        }
+      }
+
+      // Validate setting isCommitting = true.
+      var currentIsCommitting = this.get('isCommitting');
+      var nextIsCommitting = attrs.isCommitting;
+      if (currentIsCommitting === false && nextIsCommitting === true) {
+        if (!this._fieldsHaveAcceptableStates(acceptedFieldStates)) {
+          return 'isCommitting change not accepted because fields are not in acceptable state';
+        }
+      }
+      else if (currentIsCommitting === true && nextIsCommitting === true) {
+        return "isCommitting is a mutex, hence only changes are allowed";
+      }
+    },
+
+    /**
+     * Checks if a state change can be accepted.
+     *
+     * @param {string} from
+     *   From state.
+     * @param {string} to
+     *   To state.
+     * @param {object} context
+     *   Context for the check.
+     * @param {string} context.reason
+     *   The reason for the state change.
+     * @param {bool} context.confirming
+     *   Whether context is confirming or not.
+     *
+     * @return {bool}
+     *   Whether the state change is accepted or not.
+     *
+     * @see Drupal.quickedit.AppView#acceptEditorStateChange
+     */
+    _acceptStateChange: function (from, to, context) {
+      var accept = true;
+
+      // In general, enforce the states sequence. Disallow going back from a
+      // "later" state to an "earlier" state, except in explicitly allowed
+      // cases.
+      if (!this.constructor.followsStateSequence(from, to)) {
+        accept = false;
+
+        // Allow: closing -> closed.
+        // Necessary to stop editing an entity.
+        if (from === 'closing' && to === 'closed') {
+          accept = true;
+        }
+        // Allow: committing -> opened.
+        // Necessary to be able to correct an invalid field, or to hit the
+        // "Save" button again after a server/network error.
+        else if (from === 'committing' && to === 'opened' && context.reason && (context.reason === 'invalid' || context.reason === 'networkerror')) {
+          accept = true;
+        }
+        // Allow: deactivating -> opened.
+        // Necessary to be able to confirm changes with the user.
+        else if (from === 'deactivating' && to === 'opened' && context.confirming) {
+          accept = true;
+        }
+        // Allow: opened -> deactivating.
+        // Necessary to be able to stop editing.
+        else if (from === 'opened' && to === 'deactivating' && context.confirmed) {
+          accept = true;
+        }
+      }
+
+      return accept;
+    },
+
+    /**
+     * Checks if fields have acceptable states.
+     *
+     * @param {Array} acceptedFieldStates
+     *   An array of acceptable field states to check for.
+     *
+     * @return {bool}
+     *   Whether the fields have an acceptable state.
+     *
+     * @see Drupal.quickedit.EntityModel#validate
+     */
+    _fieldsHaveAcceptableStates: function (acceptedFieldStates) {
+      var accept = true;
+
+      // If no acceptable field states are provided, assume all field states are
+      // acceptable. We want to let validation pass as a default and only
+      // check validity on calls to set that explicitly request it.
+      if (acceptedFieldStates.length > 0) {
+        var fieldStates = this.get('fields').pluck('state') || [];
+        // If not all fields are in one of the accepted field states, then we
+        // still can't allow this state change.
+        if (_.difference(fieldStates, acceptedFieldStates).length) {
+          accept = false;
+        }
+      }
+
+      return accept;
+    },
+
+    /**
+     * Destroys the entity model.
+     *
+     * @param {object} options
+     *   Options for the entity model.
+     */
+    destroy: function (options) {
+      Drupal.quickedit.BaseModel.prototype.destroy.call(this, options);
+
+      this.stopListening();
+
+      // Destroy all fields of this entity.
+      this.get('fields').reset();
+    },
+
+    /**
+     * @inheritdoc
+     */
+    sync: function () {
+      // We don't use REST updates to sync.
+      return;
+    }
+
+  }, /** @lends Drupal.quickedit.EntityModel */{
+
+    /**
+     * Sequence of all possible states an entity can be in during quickediting.
+     *
+     * @type {Array.<string>}
+     */
+    states: [
+      // Initial state, like field's 'inactive' OR the user has just finished
+      // in-place editing this entity.
+      // - Trigger: none (initial) or EntityModel (finished).
+      // - Expected behavior: (when not initial state): tear down
+      //   EntityToolbarView, in-place editors and related views.
+      'closed',
+      // User has activated in-place editing of this entity.
+      // - Trigger: user.
+      // - Expected behavior: the EntityToolbarView is gets set up, in-place
+      //   editors (EditorViews) and related views for this entity's fields are
+      //   set up. Upon completion of those, the state is changed to 'opening'.
+      'launching',
+      // Launching has finished.
+      // - Trigger: application.
+      // - Guarantees: in-place editors ready for use, all entity and field
+      //   views have been set up, all fields are in the 'inactive' state.
+      // - Expected behavior: all fields are changed to the 'candidate' state
+      //   and once this is completed, the entity state will be changed to
+      //   'opened'.
+      'opening',
+      // Opening has finished.
+      // - Trigger: EntityModel.
+      // - Guarantees: see 'opening', all fields are in the 'candidate' state.
+      // - Expected behavior: the user is able to actually use in-place editing.
+      'opened',
+      // User has clicked the 'Save' button (and has thus changed at least one
+      // field).
+      // - Trigger: user.
+      // - Guarantees: see 'opened', plus: either a changed field is in
+      //   PrivateTempStore, or the user has just modified a field without
+      //   activating (switching to) another field.
+      // - Expected behavior: 1) if any of the fields are not yet in
+      //   PrivateTempStore, save them to PrivateTempStore, 2) if then any of
+      //   the fields has the 'invalid' state, then change the entity state back
+      //   to 'opened', otherwise: save the entity by committing it from
+      //   PrivateTempStore into permanent storage.
+      'committing',
+      // User has clicked the 'Close' button, or has clicked the 'Save' button
+      // and that was successfully completed.
+      // - Trigger: user or EntityModel.
+      // - Guarantees: when having clicked 'Close' hardly any: fields may be in
+      //   a variety of states; when having clicked 'Save': all fields are in
+      //   the 'candidate' state.
+      // - Expected behavior: transition all fields to the 'candidate' state,
+      //   possibly requiring confirmation in the case of having clicked
+      //   'Close'.
+      'deactivating',
+      // Deactivation has been completed.
+      // - Trigger: EntityModel.
+      // - Guarantees: all fields are in the 'candidate' state.
+      // - Expected behavior: change all fields to the 'inactive' state.
+      'closing'
+    ],
+
+    /**
+     * Indicates whether the 'from' state comes before the 'to' state.
+     *
+     * @param {string} from
+     *   One of {@link Drupal.quickedit.EntityModel.states}.
+     * @param {string} to
+     *   One of {@link Drupal.quickedit.EntityModel.states}.
+     *
+     * @return {bool}
+     *   Whether the 'from' state comes before the 'to' state.
+     */
+    followsStateSequence: function (from, to) {
+      return _.indexOf(this.states, from) < _.indexOf(this.states, to);
+    }
+
+  });
+
+  /**
+   * @constructor
+   *
+   * @augments Backbone.Collection
+   */
+  Drupal.quickedit.EntityCollection = Backbone.Collection.extend(/** @lends Drupal.quickedit.EntityCollection# */{
+
+    /**
+     * @type {Drupal.quickedit.EntityModel}
+     */
+    model: Drupal.quickedit.EntityModel
+  });
+
+}(_, jQuery, Backbone, Drupal));
diff --git a/core/themes/stable/js/quickedit/models/FieldModel.js b/core/themes/stable/js/quickedit/models/FieldModel.js
new file mode 100644
index 0000000..3925401
--- /dev/null
+++ b/core/themes/stable/js/quickedit/models/FieldModel.js
@@ -0,0 +1,348 @@
+/**
+ * @file
+ * A Backbone Model for the state of an in-place editable field in the DOM.
+ */
+
+(function (_, Backbone, Drupal) {
+
+  "use strict";
+
+  Drupal.quickedit.FieldModel = Drupal.quickedit.BaseModel.extend(/** @lends Drupal.quickedit.FieldModel# */{
+
+    /**
+     * @type {object}
+     */
+    defaults: /** @lends Drupal.quickedit.FieldModel# */{
+
+      /**
+       * The DOM element that represents this field. It may seem bizarre to have
+       * a DOM element in a Backbone Model, but we need to be able to map fields
+       * in the DOM to FieldModels in memory.
+       */
+      el: null,
+
+      /**
+       * A field ID, of the form
+       * `<entity type>/<id>/<field name>/<language>/<view mode>`
+       *
+       * @example
+       * "node/1/field_tags/und/full"
+       */
+      fieldID: null,
+
+      /**
+       * The unique ID of this field within its entity instance on the page, of
+       * the form `<entity type>/<id>/<field name>/<language>/<view
+       * mode>[entity instance ID]`.
+       *
+       * @example
+       * "node/1/field_tags/und/full[0]"
+       */
+      id: null,
+
+      /**
+       * A {@link Drupal.quickedit.EntityModel}. Its "fields" attribute, which
+       * is a FieldCollection, is automatically updated to include this
+       * FieldModel.
+       */
+      entity: null,
+
+      /**
+       * This field's metadata as returned by the
+       * QuickEditController::metadata().
+       */
+      metadata: null,
+
+      /**
+       * Callback function for validating changes between states. Receives the
+       * previous state, new state, context, and a callback.
+       */
+      acceptStateChange: null,
+
+      /**
+       * A logical field ID, of the form
+       * `<entity type>/<id>/<field name>/<language>`, i.e. the fieldID without
+       * the view mode, to be able to identify other instances of the same
+       * field on the page but rendered in a different view mode.
+       *
+       * @example
+       * "node/1/field_tags/und".
+       */
+      logicalFieldID: null,
+
+      // The attributes below are stateful. The ones above will never change
+      // during the life of a FieldModel instance.
+
+      /**
+       * In-place editing state of this field. Defaults to the initial state.
+       * Possible values: {@link Drupal.quickedit.FieldModel.states}.
+       */
+      state: 'inactive',
+
+      /**
+       * The field is currently in the 'changed' state or one of the following
+       * states in which the field is still changed.
+       */
+      isChanged: false,
+
+      /**
+       * Is tracked by the EntityModel, is mirrored here solely for decorative
+       * purposes: so that FieldDecorationView.renderChanged() can react to it.
+       */
+      inTempStore: false,
+
+      /**
+       * The full HTML representation of this field (with the element that has
+       * the data-quickedit-field-id as the outer element). Used to propagate
+       * changes from this field to other instances of the same field storage.
+       */
+      html: null,
+
+      /**
+       * An object containing the full HTML representations (values) of other
+       * view modes (keys) of this field, for other instances of this field
+       * displayed in a different view mode.
+       */
+      htmlForOtherViewModes: null
+    },
+
+    /**
+     * State of an in-place editable field in the DOM.
+     *
+     * @constructs
+     *
+     * @augments Drupal.quickedit.BaseModel
+     *
+     * @param {object} options
+     *   Options for the field model.
+     */
+    initialize: function (options) {
+      // Store the original full HTML representation of this field.
+      this.set('html', options.el.outerHTML);
+
+      // Enlist field automatically in the associated entity's field collection.
+      this.get('entity').get('fields').add(this);
+
+      // Automatically generate the logical field ID.
+      this.set('logicalFieldID', this.get('fieldID').split('/').slice(0, 4).join('/'));
+
+      // Call Drupal.quickedit.BaseModel's initialize() method.
+      Drupal.quickedit.BaseModel.prototype.initialize.call(this, options);
+    },
+
+    /**
+     * Destroys the field model.
+     *
+     * @param {object} options
+     *   Options for the field model.
+     */
+    destroy: function (options) {
+      if (this.get('state') !== 'inactive') {
+        throw new Error("FieldModel cannot be destroyed if it is not inactive state.");
+      }
+      Drupal.quickedit.BaseModel.prototype.destroy.call(this, options);
+    },
+
+    /**
+     * @inheritdoc
+     */
+    sync: function () {
+      // We don't use REST updates to sync.
+      return;
+    },
+
+    /**
+     * Validate function for the field model.
+     *
+     * @param {object} attrs
+     *   The attributes changes in the save or set call.
+     * @param {object} options
+     *   An object with the following option:
+     * @param {string} [options.reason]
+     *   A string that conveys a particular reason to allow for an exceptional
+     *   state change.
+     * @param {Array} options.accept-field-states
+     *   An array of strings that represent field states that the entities must
+     *   be in to validate. For example, if `accept-field-states` is
+     *   `['candidate', 'highlighted']`, then all the fields of the entity must
+     *   be in either of these two states for the save or set call to
+     *   validate and proceed.
+     *
+     * @return {string}
+     *   A string to say something about the state of the field model.
+     */
+    validate: function (attrs, options) {
+      var current = this.get('state');
+      var next = attrs.state;
+      if (current !== next) {
+        // Ensure it's a valid state.
+        if (_.indexOf(this.constructor.states, next) === -1) {
+          return '"' + next + '" is an invalid state';
+        }
+        // Check if the acceptStateChange callback accepts it.
+        if (!this.get('acceptStateChange')(current, next, options, this)) {
+          return 'state change not accepted';
+        }
+      }
+    },
+
+    /**
+     * Extracts the entity ID from this field's ID.
+     *
+     * @return {string}
+     *   An entity ID: a string of the format `<entity type>/<id>`.
+     */
+    getEntityID: function () {
+      return this.get('fieldID').split('/').slice(0, 2).join('/');
+    },
+
+    /**
+     * Extracts the view mode ID from this field's ID.
+     *
+     * @return {string}
+     *   A view mode ID.
+     */
+    getViewMode: function () {
+      return this.get('fieldID').split('/').pop();
+    },
+
+    /**
+     * Find other instances of this field with different view modes.
+     *
+     * @return {Array}
+     *   An array containing view mode IDs.
+     */
+    findOtherViewModes: function () {
+      var currentField = this;
+      var otherViewModes = [];
+      Drupal.quickedit.collections.fields
+        // Find all instances of fields that display the same logical field
+        // (same entity, same field, just a different instance and maybe a
+        // different view mode).
+        .where({logicalFieldID: currentField.get('logicalFieldID')})
+        .forEach(function (field) {
+          // Ignore the current field.
+          if (field === currentField) {
+            return;
+          }
+          // Also ignore other fields with the same view mode.
+          else if (field.get('fieldID') === currentField.get('fieldID')) {
+            return;
+          }
+          else {
+            otherViewModes.push(field.getViewMode());
+          }
+        });
+      return otherViewModes;
+    }
+
+  }, /** @lends Drupal.quickedit.FieldModel */{
+
+    /**
+     * Sequence of all possible states a field can be in during quickediting.
+     *
+     * @type {Array.<string>}
+     */
+    states: [
+      // The field associated with this FieldModel is linked to an EntityModel;
+      // the user can choose to start in-place editing that entity (and
+      // consequently this field). No in-place editor (EditorView) is associated
+      // with this field, because this field is not being in-place edited.
+      // This is both the initial (not yet in-place editing) and the end state
+      // (finished in-place editing).
+      'inactive',
+      // The user is in-place editing this entity, and this field is a
+      // candidate
+      // for in-place editing. In-place editor should not
+      // - Trigger: user.
+      // - Guarantees: entity is ready, in-place editor (EditorView) is
+      //   associated with the field.
+      // - Expected behavior: visual indicators
+      //   around the field indicate it is available for in-place editing, no
+      //   in-place editor presented yet.
+      'candidate',
+      // User is highlighting this field.
+      // - Trigger: user.
+      // - Guarantees: see 'candidate'.
+      // - Expected behavior: visual indicators to convey highlighting, in-place
+      //   editing toolbar shows field's label.
+      'highlighted',
+      // User has activated the in-place editing of this field; in-place editor
+      // is activating.
+      // - Trigger: user.
+      // - Guarantees: see 'candidate'.
+      // - Expected behavior: loading indicator, in-place editor is loading
+      //   remote data (e.g. retrieve form from back-end). Upon retrieval of
+      //   remote data, the in-place editor transitions the field's state to
+      //   'active'.
+      'activating',
+      // In-place editor has finished loading remote data; ready for use.
+      // - Trigger: in-place editor.
+      // - Guarantees: see 'candidate'.
+      // - Expected behavior: in-place editor for the field is ready for use.
+      'active',
+      // User has modified values in the in-place editor.
+      // - Trigger: user.
+      // - Guarantees: see 'candidate', plus in-place editor is ready for use.
+      // - Expected behavior: visual indicator of change.
+      'changed',
+      // User is saving changed field data in in-place editor to
+      // PrivateTempStore. The save mechanism of the in-place editor is called.
+      // - Trigger: user.
+      // - Guarantees: see 'candidate' and 'active'.
+      // - Expected behavior: saving indicator, in-place editor is saving field
+      //   data into PrivateTempStore. Upon successful saving (without
+      //   validation errors), the in-place editor transitions the field's state
+      //   to 'saved', but to 'invalid' upon failed saving (with validation
+      //   errors).
+      'saving',
+      // In-place editor has successfully saved the changed field.
+      // - Trigger: in-place editor.
+      // - Guarantees: see 'candidate' and 'active'.
+      // - Expected behavior: transition back to 'candidate' state because the
+      //   deed is done. Then: 1) transition to 'inactive' to allow the field
+      //   to be rerendered, 2) destroy the FieldModel (which also destroys
+      //   attached views like the EditorView), 3) replace the existing field
+      //   HTML with the existing HTML and 4) attach behaviors again so that the
+      //   field becomes available again for in-place editing.
+      'saved',
+      // In-place editor has failed to saved the changed field: there were
+      // validation errors.
+      // - Trigger: in-place editor.
+      // - Guarantees: see 'candidate' and 'active'.
+      // - Expected behavior: remain in 'invalid' state, let the user make more
+      //   changes so that he can save it again, without validation errors.
+      'invalid'
+    ],
+
+    /**
+     * Indicates whether the 'from' state comes before the 'to' state.
+     *
+     * @param {string} from
+     *   One of {@link Drupal.quickedit.FieldModel.states}.
+     * @param {string} to
+     *   One of {@link Drupal.quickedit.FieldModel.states}.
+     *
+     * @return {bool}
+     *   Whether the 'from' state comes before the 'to' state.
+     */
+    followsStateSequence: function (from, to) {
+      return _.indexOf(this.states, from) < _.indexOf(this.states, to);
+    }
+
+  });
+
+  /**
+   * @constructor
+   *
+   * @augments Backbone.Collection
+   */
+  Drupal.quickedit.FieldCollection = Backbone.Collection.extend(/** @lends Drupal.quickedit.FieldCollection */{
+
+    /**
+     * @type {Drupal.quickedit.FieldModel}
+     */
+    model: Drupal.quickedit.FieldModel
+  });
+
+}(_, Backbone, Drupal));
diff --git a/core/themes/stable/js/quickedit/quickedit.js b/core/themes/stable/js/quickedit/quickedit.js
new file mode 100644
index 0000000..5504e44
--- /dev/null
+++ b/core/themes/stable/js/quickedit/quickedit.js
@@ -0,0 +1,686 @@
+/**
+ * @file
+ * Attaches behavior for the Quick Edit module.
+ *
+ * Everything happens asynchronously, to allow for:
+ *   - dynamically rendered contextual links
+ *   - asynchronously retrieved (and cached) per-field in-place editing metadata
+ *   - asynchronous setup of in-place editable field and "Quick edit" link.
+ *
+ * To achieve this, there are several queues:
+ *   - fieldsMetadataQueue: fields whose metadata still needs to be fetched.
+ *   - fieldsAvailableQueue: queue of fields whose metadata is known, and for
+ *     which it has been confirmed that the user has permission to edit them.
+ *     However, FieldModels will only be created for them once there's a
+ *     contextual link for their entity: when it's possible to initiate editing.
+ *   - contextualLinksQueue: queue of contextual links on entities for which it
+ *     is not yet known whether the user has permission to edit at >=1 of them.
+ */
+
+(function ($, _, Backbone, Drupal, drupalSettings, JSON, storage) {
+
+  "use strict";
+
+  var options = $.extend(drupalSettings.quickedit,
+    // Merge strings on top of drupalSettings so that they are not mutable.
+    {
+      strings: {
+        quickEdit: Drupal.t('Quick edit')
+      }
+    }
+  );
+
+  /**
+   * Tracks fields without metadata. Contains objects with the following keys:
+   *   - DOM el
+   *   - String fieldID
+   *   - String entityID
+   */
+  var fieldsMetadataQueue = [];
+
+  /**
+   * Tracks fields ready for use. Contains objects with the following keys:
+   *   - DOM el
+   *   - String fieldID
+   *   - String entityID
+   */
+  var fieldsAvailableQueue = [];
+
+  /**
+   * Tracks contextual links on entities. Contains objects with the following
+   * keys:
+   *   - String entityID
+   *   - DOM el
+   *   - DOM region
+   */
+  var contextualLinksQueue = [];
+
+  /**
+   * Tracks how many instances exist for each unique entity. Contains key-value
+   * pairs:
+   * - String entityID
+   * - Number count
+   */
+  var entityInstancesTracker = {};
+
+  /**
+   *
+   * @type {Drupal~behavior}
+   */
+  Drupal.behaviors.quickedit = {
+    attach: function (context) {
+      // Initialize the Quick Edit app once per page load.
+      $('body').once('quickedit-init').each(initQuickEdit);
+
+      // Find all in-place editable fields, if any.
+      var $fields = $(context).find('[data-quickedit-field-id]').once('quickedit');
+      if ($fields.length === 0) {
+        return;
+      }
+
+      // Process each entity element: identical entities that appear multiple
+      // times will get a numeric identifier, starting at 0.
+      $(context).find('[data-quickedit-entity-id]').once('quickedit').each(function (index, entityElement) {
+        processEntity(entityElement);
+      });
+
+      // Process each field element: queue to be used or to fetch metadata.
+      // When a field is being rerendered after editing, it will be processed
+      // immediately. New fields will be unable to be processed immediately,
+      // but will instead be queued to have their metadata fetched, which occurs
+      // below in fetchMissingMetaData().
+      $fields.each(function (index, fieldElement) {
+        processField(fieldElement);
+      });
+
+      // Entities and fields on the page have been detected, try to set up the
+      // contextual links for those entities that already have the necessary
+      // meta- data in the client-side cache.
+      contextualLinksQueue = _.filter(contextualLinksQueue, function (contextualLink) {
+        return !initializeEntityContextualLink(contextualLink);
+      });
+
+      // Fetch metadata for any fields that are queued to retrieve it.
+      fetchMissingMetadata(function (fieldElementsWithFreshMetadata) {
+        // Metadata has been fetched, reprocess fields whose metadata was
+        // missing.
+        _.each(fieldElementsWithFreshMetadata, processField);
+
+        // Metadata has been fetched, try to set up more contextual links now.
+        contextualLinksQueue = _.filter(contextualLinksQueue, function (contextualLink) {
+          return !initializeEntityContextualLink(contextualLink);
+        });
+      });
+    },
+    detach: function (context, settings, trigger) {
+      if (trigger === 'unload') {
+        deleteContainedModelsAndQueues($(context));
+      }
+    }
+  };
+
+  /**
+   *
+   * @namespace
+   */
+  Drupal.quickedit = {
+
+    /**
+     * A {@link Drupal.quickedit.AppView} instance.
+     */
+    app: null,
+
+    /**
+     * @type {object}
+     *
+     * @prop {Array.<Drupal.quickedit.EntityModel>} entities
+     * @prop {Array.<Drupal.quickedit.FieldModel>} fields
+     */
+    collections: {
+      // All in-place editable entities (Drupal.quickedit.EntityModel) on the
+      // page.
+      entities: null,
+      // All in-place editable fields (Drupal.quickedit.FieldModel) on the page.
+      fields: null
+    },
+
+    /**
+     * In-place editors will register themselves in this object.
+     *
+     * @namespace
+     */
+    editors: {},
+
+    /**
+     * Per-field metadata that indicates whether in-place editing is allowed,
+     * which in-place editor should be used, etc.
+     *
+     * @namespace
+     */
+    metadata: {
+
+      /**
+       * Check if a field exists in storage.
+       *
+       * @param {string} fieldID
+       *   The field id to check.
+       *
+       * @return {bool}
+       *   Whether it was found or not.
+       */
+      has: function (fieldID) {
+        return storage.getItem(this._prefixFieldID(fieldID)) !== null;
+      },
+
+      /**
+       * Add metadata to a field id.
+       *
+       * @param {string} fieldID
+       *   The field ID to add data to.
+       * @param {object} metadata
+       *   Metadata to add.
+       */
+      add: function (fieldID, metadata) {
+        storage.setItem(this._prefixFieldID(fieldID), JSON.stringify(metadata));
+      },
+
+      /**
+       * Get a key from a field id.
+       *
+       * @param {string} fieldID
+       *   The field ID to check.
+       * @param {string} [key]
+       *   The key to check. If empty, will return all metadata.
+       *
+       * @return {object|*}
+       *   The value for the key, if defined. Otherwise will return all metadata
+       *   for the specified field id.
+       *
+       */
+      get: function (fieldID, key) {
+        var metadata = JSON.parse(storage.getItem(this._prefixFieldID(fieldID)));
+        return (typeof key === 'undefined') ? metadata : metadata[key];
+      },
+
+      /**
+       * Prefix the field id.
+       *
+       * @param {string} fieldID
+       *   The field id to prefix.
+       *
+       * @return {string}
+       *   A prefixed field id.
+       */
+      _prefixFieldID: function (fieldID) {
+        return 'Drupal.quickedit.metadata.' + fieldID;
+      },
+
+      /**
+       * Unprefix the field id.
+       *
+       * @param {string} fieldID
+       *   The field id to unprefix.
+       *
+       * @return {string}
+       *   An unprefixed field id.
+       */
+      _unprefixFieldID: function (fieldID) {
+        // Strip "Drupal.quickedit.metadata.", which is 26 characters long.
+        return fieldID.substring(26);
+      },
+
+      /**
+       * Intersection calculation.
+       *
+       * @param {Array} fieldIDs
+       *   An array of field ids to compare to prefix field id.
+       *
+       * @return {Array}
+       *   The intersection found.
+       */
+      intersection: function (fieldIDs) {
+        var prefixedFieldIDs = _.map(fieldIDs, this._prefixFieldID);
+        var intersection = _.intersection(prefixedFieldIDs, _.keys(sessionStorage));
+        return _.map(intersection, this._unprefixFieldID);
+      }
+    }
+  };
+
+  // Clear the Quick Edit metadata cache whenever the current user's set of
+  // permissions changes.
+  var permissionsHashKey = Drupal.quickedit.metadata._prefixFieldID('permissionsHash');
+  var permissionsHashValue = storage.getItem(permissionsHashKey);
+  var permissionsHash = drupalSettings.user.permissionsHash;
+  if (permissionsHashValue !== permissionsHash) {
+    if (typeof permissionsHash === 'string') {
+      _.chain(storage).keys().each(function (key) {
+        if (key.substring(0, 26) === 'Drupal.quickedit.metadata.') {
+          storage.removeItem(key);
+        }
+      });
+    }
+    storage.setItem(permissionsHashKey, permissionsHash);
+  }
+
+  /**
+   * Detect contextual links on entities annotated by quickedit.
+   *
+   * Queue contextual links to be processed.
+   *
+   * @param {jQuery.Event} event
+   *   The `drupalContextualLinkAdded` event.
+   * @param {object} data
+   *   An object containing the data relevant to the event.
+   *
+   * @listens event:drupalContextualLinkAdded
+   */
+  $(document).on('drupalContextualLinkAdded', function (event, data) {
+    if (data.$region.is('[data-quickedit-entity-id]')) {
+      // If the contextual link is cached on the client side, an entity instance
+      // will not yet have been assigned. So assign one.
+      if (!data.$region.is('[data-quickedit-entity-instance-id]')) {
+        data.$region.once('quickedit');
+        processEntity(data.$region.get(0));
+      }
+      var contextualLink = {
+        entityID: data.$region.attr('data-quickedit-entity-id'),
+        entityInstanceID: data.$region.attr('data-quickedit-entity-instance-id'),
+        el: data.$el[0],
+        region: data.$region[0]
+      };
+      // Set up contextual links for this, otherwise queue it to be set up
+      // later.
+      if (!initializeEntityContextualLink(contextualLink)) {
+        contextualLinksQueue.push(contextualLink);
+      }
+    }
+  });
+
+  /**
+   * Extracts the entity ID from a field ID.
+   *
+   * @param {string} fieldID
+   *   A field ID: a string of the format
+   *   `<entity type>/<id>/<field name>/<language>/<view mode>`.
+   *
+   * @return {string}
+   *   An entity ID: a string of the format `<entity type>/<id>`.
+   */
+  function extractEntityID(fieldID) {
+    return fieldID.split('/').slice(0, 2).join('/');
+  }
+
+  /**
+   * Initialize the Quick Edit app.
+   *
+   * @param {HTMLElement} bodyElement
+   *   This document's body element.
+   */
+  function initQuickEdit(bodyElement) {
+    Drupal.quickedit.collections.entities = new Drupal.quickedit.EntityCollection();
+    Drupal.quickedit.collections.fields = new Drupal.quickedit.FieldCollection();
+
+    // Instantiate AppModel (application state) and AppView, which is the
+    // controller of the whole in-place editing experience.
+    Drupal.quickedit.app = new Drupal.quickedit.AppView({
+      el: bodyElement,
+      model: new Drupal.quickedit.AppModel(),
+      entitiesCollection: Drupal.quickedit.collections.entities,
+      fieldsCollection: Drupal.quickedit.collections.fields
+    });
+  }
+
+  /**
+   * Assigns the entity an instance ID.
+   *
+   * @param {HTMLElement} entityElement
+   *   A Drupal Entity API entity's DOM element with a data-quickedit-entity-id
+   *   attribute.
+   */
+  function processEntity(entityElement) {
+    var entityID = entityElement.getAttribute('data-quickedit-entity-id');
+    if (!entityInstancesTracker.hasOwnProperty(entityID)) {
+      entityInstancesTracker[entityID] = 0;
+    }
+    else {
+      entityInstancesTracker[entityID]++;
+    }
+
+    // Set the calculated entity instance ID for this element.
+    var entityInstanceID = entityInstancesTracker[entityID];
+    entityElement.setAttribute('data-quickedit-entity-instance-id', entityInstanceID);
+  }
+
+  /**
+   * Fetch the field's metadata; queue or initialize it (if EntityModel exists).
+   *
+   * @param {HTMLElement} fieldElement
+   *   A Drupal Field API field's DOM element with a data-quickedit-field-id
+   *   attribute.
+   */
+  function processField(fieldElement) {
+    var metadata = Drupal.quickedit.metadata;
+    var fieldID = fieldElement.getAttribute('data-quickedit-field-id');
+    var entityID = extractEntityID(fieldID);
+    // Figure out the instance ID by looking at the ancestor
+    // [data-quickedit-entity-id] element's data-quickedit-entity-instance-id
+    // attribute.
+    var entityElementSelector = '[data-quickedit-entity-id="' + entityID + '"]';
+    var entityElement = $(fieldElement).closest(entityElementSelector);
+    // In the case of a full entity view page, the entity title is rendered
+    // outside of "the entity DOM node": it's rendered as the page title. So in
+    // this case, we find the lowest common parent element (deepest in the tree)
+    // and consider that the entity element.
+    if (entityElement.length === 0) {
+      var $lowestCommonParent = $(entityElementSelector).parents().has(fieldElement).first();
+      entityElement = $lowestCommonParent.find(entityElementSelector);
+    }
+    var entityInstanceID = entityElement
+      .get(0)
+      .getAttribute('data-quickedit-entity-instance-id');
+
+    // Early-return if metadata for this field is missing.
+    if (!metadata.has(fieldID)) {
+      fieldsMetadataQueue.push({
+        el: fieldElement,
+        fieldID: fieldID,
+        entityID: entityID,
+        entityInstanceID: entityInstanceID
+      });
+      return;
+    }
+    // Early-return if the user is not allowed to in-place edit this field.
+    if (metadata.get(fieldID, 'access') !== true) {
+      return;
+    }
+
+    // If an EntityModel for this field already exists (and hence also a "Quick
+    // edit" contextual link), then initialize it immediately.
+    if (Drupal.quickedit.collections.entities.findWhere({entityID: entityID, entityInstanceID: entityInstanceID})) {
+      initializeField(fieldElement, fieldID, entityID, entityInstanceID);
+    }
+    // Otherwise: queue the field. It is now available to be set up when its
+    // corresponding entity becomes in-place editable.
+    else {
+      fieldsAvailableQueue.push({el: fieldElement, fieldID: fieldID, entityID: entityID, entityInstanceID: entityInstanceID});
+    }
+  }
+
+  /**
+   * Initialize a field; create FieldModel.
+   *
+   * @param {HTMLElement} fieldElement
+   *   The field's DOM element.
+   * @param {string} fieldID
+   *   The field's ID.
+   * @param {string} entityID
+   *   The field's entity's ID.
+   * @param {string} entityInstanceID
+   *   The field's entity's instance ID.
+   */
+  function initializeField(fieldElement, fieldID, entityID, entityInstanceID) {
+    var entity = Drupal.quickedit.collections.entities.findWhere({
+      entityID: entityID,
+      entityInstanceID: entityInstanceID
+    });
+
+    $(fieldElement).addClass('quickedit-field');
+
+    // The FieldModel stores the state of an in-place editable entity field.
+    var field = new Drupal.quickedit.FieldModel({
+      el: fieldElement,
+      fieldID: fieldID,
+      id: fieldID + '[' + entity.get('entityInstanceID') + ']',
+      entity: entity,
+      metadata: Drupal.quickedit.metadata.get(fieldID),
+      acceptStateChange: _.bind(Drupal.quickedit.app.acceptEditorStateChange, Drupal.quickedit.app)
+    });
+
+    // Track all fields on the page.
+    Drupal.quickedit.collections.fields.add(field);
+  }
+
+  /**
+   * Fetches metadata for fields whose metadata is missing.
+   *
+   * Fields whose metadata is missing are tracked at fieldsMetadataQueue.
+   *
+   * @param {function} callback
+   *   A callback function that receives field elements whose metadata will just
+   *   have been fetched.
+   */
+  function fetchMissingMetadata(callback) {
+    if (fieldsMetadataQueue.length) {
+      var fieldIDs = _.pluck(fieldsMetadataQueue, 'fieldID');
+      var fieldElementsWithoutMetadata = _.pluck(fieldsMetadataQueue, 'el');
+      var entityIDs = _.uniq(_.pluck(fieldsMetadataQueue, 'entityID'), true);
+      // Ensure we only request entityIDs for which we don't have metadata yet.
+      entityIDs = _.difference(entityIDs, Drupal.quickedit.metadata.intersection(entityIDs));
+      fieldsMetadataQueue = [];
+
+      $.ajax({
+        url: Drupal.url('quickedit/metadata'),
+        type: 'POST',
+        data: {
+          'fields[]': fieldIDs,
+          'entities[]': entityIDs
+        },
+        dataType: 'json',
+        success: function (results) {
+          // Store the metadata.
+          _.each(results, function (fieldMetadata, fieldID) {
+            Drupal.quickedit.metadata.add(fieldID, fieldMetadata);
+          });
+
+          callback(fieldElementsWithoutMetadata);
+        }
+      });
+    }
+  }
+
+  /**
+   * Loads missing in-place editor's attachments (JavaScript and CSS files).
+   *
+   * Missing in-place editors are those whose fields are actively being used on
+   * the page but don't have.
+   *
+   * @param {function} callback
+   *   Callback function to be called when the missing in-place editors (if any)
+   *   have been inserted into the DOM. i.e. they may still be loading.
+   */
+  function loadMissingEditors(callback) {
+    var loadedEditors = _.keys(Drupal.quickedit.editors);
+    var missingEditors = [];
+    Drupal.quickedit.collections.fields.each(function (fieldModel) {
+      var metadata = Drupal.quickedit.metadata.get(fieldModel.get('fieldID'));
+      if (metadata.access && _.indexOf(loadedEditors, metadata.editor) === -1) {
+        missingEditors.push(metadata.editor);
+        // Set a stub, to prevent subsequent calls to loadMissingEditors() from
+        // loading the same in-place editor again. Loading an in-place editor
+        // requires talking to a server, to download its JavaScript, then
+        // executing its JavaScript, and only then its Drupal.quickedit.editors
+        // entry will be set.
+        Drupal.quickedit.editors[metadata.editor] = false;
+      }
+    });
+    missingEditors = _.uniq(missingEditors);
+    if (missingEditors.length === 0) {
+      callback();
+      return;
+    }
+
+    // @see https://www.drupal.org/node/2029999.
+    // Create a Drupal.Ajax instance to load the form.
+    var loadEditorsAjax = Drupal.ajax({
+      url: Drupal.url('quickedit/attachments'),
+      submit: {'editors[]': missingEditors}
+    });
+    // Implement a scoped insert AJAX command: calls the callback after all AJAX
+    // command functions have been executed (hence the deferred calling).
+    var realInsert = Drupal.AjaxCommands.prototype.insert;
+    loadEditorsAjax.commands.insert = function (ajax, response, status) {
+      _.defer(callback);
+      realInsert(ajax, response, status);
+    };
+    // Trigger the AJAX request, which will should return AJAX commands to
+    // insert any missing attachments.
+    loadEditorsAjax.execute();
+  }
+
+  /**
+   * Attempts to set up a "Quick edit" link and corresponding EntityModel.
+   *
+   * @param {object} contextualLink
+   *   An object with the following properties:
+   *     - String entityID: a Quick Edit entity identifier, e.g. "node/1" or
+   *       "block_content/5".
+   *     - String entityInstanceID: a Quick Edit entity instance identifier,
+   *       e.g. 0, 1 or n (depending on whether it's the first, second, or n+1st
+   *       instance of this entity).
+   *     - DOM el: element pointing to the contextual links placeholder for this
+   *       entity.
+   *     - DOM region: element pointing to the contextual region of this entity.
+   *
+   * @return {bool}
+   *   Returns true when a contextual the given contextual link metadata can be
+   *   removed from the queue (either because the contextual link has been set
+   *   up or because it is certain that in-place editing is not allowed for any
+   *   of its fields). Returns false otherwise.
+   */
+  function initializeEntityContextualLink(contextualLink) {
+    var metadata = Drupal.quickedit.metadata;
+    // Check if the user has permission to edit at least one of them.
+    function hasFieldWithPermission(fieldIDs) {
+      for (var i = 0; i < fieldIDs.length; i++) {
+        var fieldID = fieldIDs[i];
+        if (metadata.get(fieldID, 'access') === true) {
+          return true;
+        }
+      }
+      return false;
+    }
+
+    // Checks if the metadata for all given field IDs exists.
+    function allMetadataExists(fieldIDs) {
+      return fieldIDs.length === metadata.intersection(fieldIDs).length;
+    }
+
+    // Find all fields for this entity instance and collect their field IDs.
+    var fields = _.where(fieldsAvailableQueue, {
+      entityID: contextualLink.entityID,
+      entityInstanceID: contextualLink.entityInstanceID
+    });
+    var fieldIDs = _.pluck(fields, 'fieldID');
+
+    // No fields found yet.
+    if (fieldIDs.length === 0) {
+      return false;
+    }
+    // The entity for the given contextual link contains at least one field that
+    // the current user may edit in-place; instantiate EntityModel,
+    // EntityDecorationView and ContextualLinkView.
+    else if (hasFieldWithPermission(fieldIDs)) {
+      var entityModel = new Drupal.quickedit.EntityModel({
+        el: contextualLink.region,
+        entityID: contextualLink.entityID,
+        entityInstanceID: contextualLink.entityInstanceID,
+        id: contextualLink.entityID + '[' + contextualLink.entityInstanceID + ']',
+        label: Drupal.quickedit.metadata.get(contextualLink.entityID, 'label')
+      });
+      Drupal.quickedit.collections.entities.add(entityModel);
+      // Create an EntityDecorationView associated with the root DOM node of the
+      // entity.
+      var entityDecorationView = new Drupal.quickedit.EntityDecorationView({
+        el: contextualLink.region,
+        model: entityModel
+      });
+      entityModel.set('entityDecorationView', entityDecorationView);
+
+      // Initialize all queued fields within this entity (creates FieldModels).
+      _.each(fields, function (field) {
+        initializeField(field.el, field.fieldID, contextualLink.entityID, contextualLink.entityInstanceID);
+      });
+      fieldsAvailableQueue = _.difference(fieldsAvailableQueue, fields);
+
+      // Initialization should only be called once. Use Underscore's once method
+      // to get a one-time use version of the function.
+      var initContextualLink = _.once(function () {
+        var $links = $(contextualLink.el).find('.contextual-links');
+        var contextualLinkView = new Drupal.quickedit.ContextualLinkView($.extend({
+          el: $('<li class="quickedit"><a href="" role="button" aria-pressed="false"></a></li>').prependTo($links),
+          model: entityModel,
+          appModel: Drupal.quickedit.app.model
+        }, options));
+        entityModel.set('contextualLinkView', contextualLinkView);
+      });
+
+      // Set up ContextualLinkView after loading any missing in-place editors.
+      loadMissingEditors(initContextualLink);
+
+      return true;
+    }
+    // There was not at least one field that the current user may edit in-place,
+    // even though the metadata for all fields within this entity is available.
+    else if (allMetadataExists(fieldIDs)) {
+      return true;
+    }
+
+    return false;
+  }
+
+  /**
+   * Delete models and queue items that are contained within a given context.
+   *
+   * Deletes any contained EntityModels (plus their associated FieldModels and
+   * ContextualLinkView) and FieldModels, as well as the corresponding queues.
+   *
+   * After EntityModels, FieldModels must also be deleted, because it is
+   * possible in Drupal for a field DOM element to exist outside of the entity
+   * DOM element, e.g. when viewing the full node, the title of the node is not
+   * rendered within the node (the entity) but as the page title.
+   *
+   * Note: this will not delete an entity that is actively being in-place
+   * edited.
+   *
+   * @param {jQuery} $context
+   *   The context within which to delete.
+   */
+  function deleteContainedModelsAndQueues($context) {
+    $context.find('[data-quickedit-entity-id]').addBack('[data-quickedit-entity-id]').each(function (index, entityElement) {
+      // Delete entity model.
+      var entityModel = Drupal.quickedit.collections.entities.findWhere({el: entityElement});
+      if (entityModel) {
+        var contextualLinkView = entityModel.get('contextualLinkView');
+        contextualLinkView.undelegateEvents();
+        contextualLinkView.remove();
+        // Remove the EntityDecorationView.
+        entityModel.get('entityDecorationView').remove();
+        // Destroy the EntityModel; this will also destroy its FieldModels.
+        entityModel.destroy();
+      }
+
+      // Filter queue.
+      function hasOtherRegion(contextualLink) {
+        return contextualLink.region !== entityElement;
+      }
+
+      contextualLinksQueue = _.filter(contextualLinksQueue, hasOtherRegion);
+    });
+
+    $context.find('[data-quickedit-field-id]').addBack('[data-quickedit-field-id]').each(function (index, fieldElement) {
+      // Delete field models.
+      Drupal.quickedit.collections.fields.chain()
+        .filter(function (fieldModel) { return fieldModel.get('el') === fieldElement; })
+        .invoke('destroy');
+
+      // Filter queues.
+      function hasOtherFieldElement(field) {
+        return field.el !== fieldElement;
+      }
+
+      fieldsMetadataQueue = _.filter(fieldsMetadataQueue, hasOtherFieldElement);
+      fieldsAvailableQueue = _.filter(fieldsAvailableQueue, hasOtherFieldElement);
+    });
+  }
+
+})(jQuery, _, Backbone, Drupal, drupalSettings, window.JSON, window.sessionStorage);
diff --git a/core/themes/stable/js/quickedit/theme.js b/core/themes/stable/js/quickedit/theme.js
new file mode 100644
index 0000000..8ea7a31
--- /dev/null
+++ b/core/themes/stable/js/quickedit/theme.js
@@ -0,0 +1,187 @@
+/**
+ * @file
+ * Provides theme functions for all of Quick Edit's client-side HTML.
+ */
+
+(function ($, Drupal) {
+
+  "use strict";
+
+  /**
+   * Theme function for a "backstage" for the Quick Edit module.
+   *
+   * @param {object} settings
+   *   Settings object used to construct the markup.
+   * @param {string} settings.id
+   *   The id to apply to the backstage.
+   *
+   * @return {string}
+   *   The corresponding HTML.
+   */
+  Drupal.theme.quickeditBackstage = function (settings) {
+    var html = '';
+    html += '<div id="' + settings.id + '" />';
+    return html;
+  };
+
+  /**
+   * Theme function for a toolbar container of the Quick Edit module.
+   *
+   * @param {object} settings
+   *   Settings object used to construct the markup.
+   * @param {string} settings.id
+   *   the id to apply to the backstage.
+   *
+   * @return {string}
+   *   The corresponding HTML.
+   */
+  Drupal.theme.quickeditEntityToolbar = function (settings) {
+    var html = '';
+    html += '<div id="' + settings.id + '" class="quickedit quickedit-toolbar-container clearfix">';
+    html += '<i class="quickedit-toolbar-pointer"></i>';
+    html += '<div class="quickedit-toolbar-content">';
+    html += '<div class="quickedit-toolbar quickedit-toolbar-entity clearfix icon icon-pencil">';
+    html += '<div class="quickedit-toolbar-label" />';
+    html += '</div>';
+    html += '<div class="quickedit-toolbar quickedit-toolbar-field clearfix" />';
+    html += '</div><div class="quickedit-toolbar-lining"></div></div>';
+    return html;
+  };
+
+  /**
+   * Theme function for a toolbar container of the Quick Edit module.
+   *
+   * @param {object} settings
+   *   Settings object used to construct the markup.
+   * @param {string} settings.entityLabel
+   *   The title of the active entity.
+   * @param {string} settings.fieldLabel
+   *   The label of the highlighted or active field.
+   *
+   * @return {string}
+   *   The corresponding HTML.
+   */
+  Drupal.theme.quickeditEntityToolbarLabel = function (settings) {
+    // @todo Add XSS regression test coverage in https://www.drupal.org/node/2547437
+    return '<span class="field">' + Drupal.checkPlain(settings.fieldLabel) + '</span>' + Drupal.checkPlain(settings.entityLabel);
+  };
+
+  /**
+   * Element defining a containing box for the placement of the entity toolbar.
+   *
+   * @return {string}
+   *   The corresponding HTML.
+   */
+  Drupal.theme.quickeditEntityToolbarFence = function () {
+    return '<div id="quickedit-toolbar-fence" />';
+  };
+
+  /**
+   * Theme function for a toolbar container of the Quick Edit module.
+   *
+   * @param {object} settings
+   *   Settings object used to construct the markup.
+   * @param {string} settings.id
+   *   The id to apply to the toolbar container.
+   *
+   * @return {string}
+   *   The corresponding HTML.
+   */
+  Drupal.theme.quickeditFieldToolbar = function (settings) {
+    return '<div id="' + settings.id + '" />';
+  };
+
+  /**
+   * Theme function for a toolbar toolgroup of the Quick Edit module.
+   *
+   * @param {object} settings
+   *   Settings object used to construct the markup.
+   * @param {string} [settings.id]
+   *   The id of the toolgroup.
+   * @param {string} settings.classes
+   *   The class of the toolgroup.
+   * @param {Array} settings.buttons
+   *   See {@link Drupal.theme.quickeditButtons}.
+   *
+   * @return {string}
+   *   The corresponding HTML.
+   */
+  Drupal.theme.quickeditToolgroup = function (settings) {
+    // Classes.
+    var classes = (settings.classes || []);
+    classes.unshift('quickedit-toolgroup');
+    var html = '';
+    html += '<div class="' + classes.join(' ') + '"';
+    if (settings.id) {
+      html += ' id="' + settings.id + '"';
+    }
+    html += '>';
+    html += Drupal.theme('quickeditButtons', {buttons: settings.buttons});
+    html += '</div>';
+    return html;
+  };
+
+  /**
+   * Theme function for buttons of the Quick Edit module.
+   *
+   * Can be used for the buttons both in the toolbar toolgroups and in the
+   * modal.
+   *
+   * @param {object} settings
+   *   Settings object used to construct the markup.
+   * @param {Array} settings.buttons
+   * - String type: the type of the button (defaults to 'button')
+   * - Array classes: the classes of the button.
+   * - String label: the label of the button.
+   *
+   * @return {string}
+   *   The corresponding HTML.
+   */
+  Drupal.theme.quickeditButtons = function (settings) {
+    var html = '';
+    for (var i = 0; i < settings.buttons.length; i++) {
+      var button = settings.buttons[i];
+      if (!button.hasOwnProperty('type')) {
+        button.type = 'button';
+      }
+      // Attributes.
+      var attributes = [];
+      var attrMap = settings.buttons[i].attributes || {};
+      for (var attr in attrMap) {
+        if (attrMap.hasOwnProperty(attr)) {
+          attributes.push(attr + ((attrMap[attr]) ? '="' + attrMap[attr] + '"' : ''));
+        }
+      }
+      html += '<button type="' + button.type + '" class="' + button.classes + '"' + ' ' + attributes.join(' ') + '>';
+      html += button.label;
+      html += '</button>';
+    }
+    return html;
+  };
+
+  /**
+   * Theme function for a form container of the Quick Edit module.
+   *
+   * @param {object} settings
+   *   Settings object used to construct the markup.
+   * @param {string} settings.id
+   *   The id to apply to the toolbar container.
+   * @param {string} settings.loadingMsg
+   *   The message to show while loading.
+   *
+   * @return {string}
+   *   The corresponding HTML.
+   */
+  Drupal.theme.quickeditFormContainer = function (settings) {
+    var html = '';
+    html += '<div id="' + settings.id + '" class="quickedit-form-container">';
+    html += '  <div class="quickedit-form">';
+    html += '    <div class="placeholder">';
+    html += settings.loadingMsg;
+    html += '    </div>';
+    html += '  </div>';
+    html += '</div>';
+    return html;
+  };
+
+})(jQuery, Drupal);
diff --git a/core/themes/stable/js/quickedit/util.js b/core/themes/stable/js/quickedit/util.js
new file mode 100644
index 0000000..8ef371e
--- /dev/null
+++ b/core/themes/stable/js/quickedit/util.js
@@ -0,0 +1,213 @@
+/**
+ * @file
+ * Provides utility functions for Quick Edit.
+ */
+
+(function ($, Drupal) {
+
+  "use strict";
+
+  /**
+   * @namespace
+   */
+  Drupal.quickedit.util = Drupal.quickedit.util || {};
+
+  /**
+   * @namespace
+   */
+  Drupal.quickedit.util.constants = {};
+
+  /**
+   *
+   * @type {string}
+   */
+  Drupal.quickedit.util.constants.transitionEnd = "transitionEnd.quickedit webkitTransitionEnd.quickedit transitionend.quickedit msTransitionEnd.quickedit oTransitionEnd.quickedit";
+
+  /**
+   * Converts a field id into a formatted url path.
+   *
+   * @example
+   * Drupal.quickedit.util.buildUrl(
+   *   'node/1/body/und/full',
+   *   '/quickedit/form/!entity_type/!id/!field_name/!langcode/!view_mode'
+   * );
+   *
+   * @param {string} id
+   *   The id of an editable field.
+   * @param {string} urlFormat
+   *   The Controller route for field processing.
+   *
+   * @return {string}
+   *   The formatted URL.
+   */
+  Drupal.quickedit.util.buildUrl = function (id, urlFormat) {
+    var parts = id.split('/');
+    return Drupal.formatString(decodeURIComponent(urlFormat), {
+      '!entity_type': parts[0],
+      '!id': parts[1],
+      '!field_name': parts[2],
+      '!langcode': parts[3],
+      '!view_mode': parts[4]
+    });
+  };
+
+  /**
+   * Shows a network error modal dialog.
+   *
+   * @param {string} title
+   *   The title to use in the modal dialog.
+   * @param {string} message
+   *   The message to use in the modal dialog.
+   */
+  Drupal.quickedit.util.networkErrorModal = function (title, message) {
+    var $message = $('<div>' + message + '</div>');
+    var networkErrorModal = Drupal.dialog($message.get(0), {
+      title: title,
+      dialogClass: 'quickedit-network-error',
+      buttons: [
+        {
+          text: Drupal.t('OK'),
+          click: function () {
+            networkErrorModal.close();
+          },
+          primary: true
+        }
+      ],
+      create: function () {
+        $(this).parent().find('.ui-dialog-titlebar-close').remove();
+      },
+      close: function (event) {
+        // Automatically destroy the DOM element that was used for the dialog.
+        $(event.target).remove();
+      }
+    });
+    networkErrorModal.showModal();
+  };
+
+  /**
+   * @namespace
+   */
+  Drupal.quickedit.util.form = {
+
+    /**
+     * Loads a form, calls a callback to insert.
+     *
+     * Leverages {@link Drupal.Ajax}' ability to have scoped (per-instance)
+     * command implementations to be able to call a callback.
+     *
+     * @param {object} options
+     *   An object with the following keys:
+     * @param {string} options.fieldID
+     *   The field ID that uniquely identifies the field for which this form
+     *   will be loaded.
+     * @param {bool} options.nocssjs
+     *   Boolean indicating whether no CSS and JS should be returned (necessary
+     *   when the form is invisible to the user).
+     * @param {bool} options.reset
+     *   Boolean indicating whether the data stored for this field's entity in
+     *   PrivateTempStore should be used or reset.
+     * @param {function} callback
+     *   A callback function that will receive the form to be inserted, as well
+     *   as the ajax object, necessary if the callback wants to perform other
+     *   Ajax commands.
+     */
+    load: function (options, callback) {
+      var fieldID = options.fieldID;
+
+      // Create a Drupal.ajax instance to load the form.
+      var formLoaderAjax = Drupal.ajax({
+        url: Drupal.quickedit.util.buildUrl(fieldID, Drupal.url('quickedit/form/!entity_type/!id/!field_name/!langcode/!view_mode')),
+        submit: {
+          nocssjs: options.nocssjs,
+          reset: options.reset
+        },
+        error: function (xhr, url) {
+          // Show a modal to inform the user of the network error.
+          var fieldLabel = Drupal.quickedit.metadata.get(fieldID, 'label');
+          var message = Drupal.t('Could not load the form for <q>@field-label</q>, either due to a website problem or a network connection problem.<br>Please try again.', {'@field-label': fieldLabel});
+          Drupal.quickedit.util.networkErrorModal(Drupal.t('Network problem!'), message);
+
+          // Change the state back to "candidate", to allow the user to start
+          // in-place editing of the field again.
+          var fieldModel = Drupal.quickedit.app.model.get('activeField');
+          fieldModel.set('state', 'candidate');
+        }
+      });
+      // Implement a scoped quickeditFieldForm AJAX command: calls the callback.
+      formLoaderAjax.commands.quickeditFieldForm = function (ajax, response, status) {
+        callback(response.data, ajax);
+        Drupal.ajax.instances[this.instanceIndex] = null;
+      };
+      // This will ensure our scoped quickeditFieldForm AJAX command gets
+      // called.
+      formLoaderAjax.execute();
+    },
+
+    /**
+     * Creates a {@link Drupal.Ajax} instance that is used to save a form.
+     *
+     * @param {object} options
+     *   Submit options to the form.
+     * @param {bool} options.nocssjs
+     *   Boolean indicating whether no CSS and JS should be returned (necessary
+     *   when the form is invisible to the user).
+     * @param {Array.<string>} options.other_view_modes
+     *   Array containing view mode IDs (of other instances of this field on the
+     *   page).
+     * @param {jQuery} $submit
+     *   The submit element.
+     *
+     * @return {Drupal.Ajax}
+     *   A {@link Drupal.Ajax} instance.
+     */
+    ajaxifySaving: function (options, $submit) {
+      // Re-wire the form to handle submit.
+      var settings = {
+        url: $submit.closest('form').attr('action'),
+        setClick: true,
+        event: 'click.quickedit',
+        progress: false,
+        submit: {
+          nocssjs: options.nocssjs,
+          other_view_modes: options.other_view_modes
+        },
+
+        /**
+         * Reimplement the success handler.
+         *
+         * Ensure {@link Drupal.attachBehaviors} does not get called on the
+         * form.
+         *
+         * @param {Drupal.AjaxCommands~commandDefinition} response
+         *   The Drupal AJAX response.
+         * @param {number} [status]
+         *   The HTTP status code.
+         */
+        success: function (response, status) {
+          for (var i in response) {
+            if (response.hasOwnProperty(i) && response[i].command && this.commands[response[i].command]) {
+              this.commands[response[i].command](this, response[i], status);
+            }
+          }
+        },
+        base: $submit.attr('id'),
+        element: $submit[0]
+      };
+
+      return Drupal.ajax(settings);
+    },
+
+    /**
+     * Cleans up the {@link Drupal.Ajax} instance that is used to save the form.
+     *
+     * @param {Drupal.Ajax} ajax
+     *   A {@link Drupal.Ajax} instance that was returned by
+     *   {@link Drupal.quickedit.form.ajaxifySaving}.
+     */
+    unajaxifySaving: function (ajax) {
+      $(ajax.element).off('click.quickedit');
+    }
+
+  };
+
+})(jQuery, Drupal);
diff --git a/core/themes/stable/js/quickedit/views/AppView.js b/core/themes/stable/js/quickedit/views/AppView.js
new file mode 100644
index 0000000..dbffdd6
--- /dev/null
+++ b/core/themes/stable/js/quickedit/views/AppView.js
@@ -0,0 +1,600 @@
+/**
+ * @file
+ * A Backbone View that controls the overall "in-place editing application".
+ *
+ * @see Drupal.quickedit.AppModel
+ */
+
+(function ($, _, Backbone, Drupal) {
+
+  "use strict";
+
+  // Indicates whether the page should be reloaded after in-place editing has
+  // shut down. A page reload is necessary to re-instate the original HTML of
+  // the edited fields if in-place editing has been canceled and one or more of
+  // the entity's fields were saved to PrivateTempStore: one of them may have
+  // been changed to the empty value and hence may have been rerendered as the
+  // empty string, which makes it impossible for Quick Edit to know where to
+  // restore the original HTML.
+  var reload = false;
+
+  Drupal.quickedit.AppView = Backbone.View.extend(/** @lends Drupal.quickedit.AppView# */{
+
+    /**
+     * @constructs
+     *
+     * @augments Backbone.View
+     *
+     * @param {object} options
+     *   An object with the following keys:
+     * @param {Drupal.quickedit.AppModel} options.model
+     *   The application state model.
+     * @param {Drupal.quickedit.EntityCollection} options.entitiesCollection
+     *   All on-page entities.
+     * @param {Drupal.quickedit.FieldCollection} options.fieldsCollection
+     *   All on-page fields
+     */
+    initialize: function (options) {
+      // AppView's configuration for handling states.
+      // @see Drupal.quickedit.FieldModel.states
+      this.activeFieldStates = ['activating', 'active'];
+      this.singleFieldStates = ['highlighted', 'activating', 'active'];
+      this.changedFieldStates = ['changed', 'saving', 'saved', 'invalid'];
+      this.readyFieldStates = ['candidate', 'highlighted'];
+
+      // Track app state.
+      this.listenTo(options.entitiesCollection, 'change:state', this.appStateChange);
+      this.listenTo(options.entitiesCollection, 'change:isActive', this.enforceSingleActiveEntity);
+
+      // Track app state.
+      this.listenTo(options.fieldsCollection, 'change:state', this.editorStateChange);
+      // Respond to field model HTML representation change events.
+      this.listenTo(options.fieldsCollection, 'change:html', this.renderUpdatedField);
+      this.listenTo(options.fieldsCollection, 'change:html', this.propagateUpdatedField);
+      // Respond to addition.
+      this.listenTo(options.fieldsCollection, 'add', this.rerenderedFieldToCandidate);
+      // Respond to destruction.
+      this.listenTo(options.fieldsCollection, 'destroy', this.teardownEditor);
+    },
+
+    /**
+     * Handles setup/teardown and state changes when the active entity changes.
+     *
+     * @param {Drupal.quickedit.EntityModel} entityModel
+     *   An instance of the EntityModel class.
+     * @param {string} state
+     *   The state of the associated field. One of
+     *   {@link Drupal.quickedit.EntityModel.states}.
+     */
+    appStateChange: function (entityModel, state) {
+      var app = this;
+      var entityToolbarView;
+      switch (state) {
+        case 'launching':
+          reload = false;
+          // First, create an entity toolbar view.
+          entityToolbarView = new Drupal.quickedit.EntityToolbarView({
+            model: entityModel,
+            appModel: this.model
+          });
+          entityModel.toolbarView = entityToolbarView;
+          // Second, set up in-place editors.
+          // They must be notified of state changes, hence this must happen
+          // while the associated fields are still in the 'inactive' state.
+          entityModel.get('fields').each(function (fieldModel) {
+            app.setupEditor(fieldModel);
+          });
+          // Third, transition the entity to the 'opening' state, which will
+          // transition all fields from 'inactive' to 'candidate'.
+          _.defer(function () {
+            entityModel.set('state', 'opening');
+          });
+          break;
+
+        case 'closed':
+          entityToolbarView = entityModel.toolbarView;
+          // First, tear down the in-place editors.
+          entityModel.get('fields').each(function (fieldModel) {
+            app.teardownEditor(fieldModel);
+          });
+          // Second, tear down the entity toolbar view.
+          if (entityToolbarView) {
+            entityToolbarView.remove();
+            delete entityModel.toolbarView;
+          }
+          // A page reload may be necessary to re-instate the original HTML of
+          // the edited fields.
+          if (reload) {
+            reload = false;
+            location.reload();
+          }
+          break;
+      }
+    },
+
+    /**
+     * Accepts or reject editor (Editor) state changes.
+     *
+     * This is what ensures that the app is in control of what happens.
+     *
+     * @param {string} from
+     *   The previous state.
+     * @param {string} to
+     *   The new state.
+     * @param {null|object} context
+     *   The context that is trying to trigger the state change.
+     * @param {Drupal.quickedit.FieldModel} fieldModel
+     *   The fieldModel to which this change applies.
+     *
+     * @return {bool}
+     *   Whether the editor change was accepted or rejected.
+     */
+    acceptEditorStateChange: function (from, to, context, fieldModel) {
+      var accept = true;
+
+      // If the app is in view mode, then reject all state changes except for
+      // those to 'inactive'.
+      if (context && (context.reason === 'stop' || context.reason === 'rerender')) {
+        if (from === 'candidate' && to === 'inactive') {
+          accept = true;
+        }
+      }
+      // Handling of edit mode state changes is more granular.
+      else {
+        // In general, enforce the states sequence. Disallow going back from a
+        // "later" state to an "earlier" state, except in explicitly allowed
+        // cases.
+        if (!Drupal.quickedit.FieldModel.followsStateSequence(from, to)) {
+          accept = false;
+          // Allow: activating/active -> candidate.
+          // Necessary to stop editing a field.
+          if (_.indexOf(this.activeFieldStates, from) !== -1 && to === 'candidate') {
+            accept = true;
+          }
+          // Allow: changed/invalid -> candidate.
+          // Necessary to stop editing a field when it is changed or invalid.
+          else if ((from === 'changed' || from === 'invalid') && to === 'candidate') {
+            accept = true;
+          }
+          // Allow: highlighted -> candidate.
+          // Necessary to stop highlighting a field.
+          else if (from === 'highlighted' && to === 'candidate') {
+            accept = true;
+          }
+          // Allow: saved -> candidate.
+          // Necessary when successfully saved a field.
+          else if (from === 'saved' && to === 'candidate') {
+            accept = true;
+          }
+          // Allow: invalid -> saving.
+          // Necessary to be able to save a corrected, invalid field.
+          else if (from === 'invalid' && to === 'saving') {
+            accept = true;
+          }
+          // Allow: invalid -> activating.
+          // Necessary to be able to correct a field that turned out to be
+          // invalid after the user already had moved on to the next field
+          // (which we explicitly allow to have a fluent UX).
+          else if (from === 'invalid' && to === 'activating') {
+            accept = true;
+          }
+        }
+
+        // If it's not against the general principle, then here are more
+        // disallowed cases to check.
+        if (accept) {
+          var activeField;
+          var activeFieldState;
+          // Ensure only one field (editor) at a time is active … but allow a
+          // user to hop from one field to the next, even if we still have to
+          // start saving the field that is currently active: assume it will be
+          // valid, to allow for a fluent UX. (If it turns out to be invalid,
+          // this block of code also handles that.)
+          if ((this.readyFieldStates.indexOf(from) !== -1 || from === 'invalid') && this.activeFieldStates.indexOf(to) !== -1) {
+            activeField = this.model.get('activeField');
+            if (activeField && activeField !== fieldModel) {
+              activeFieldState = activeField.get('state');
+              // Allow the state change. If the state of the active field is:
+              // - 'activating' or 'active': change it to 'candidate'
+              // - 'changed' or 'invalid': change it to 'saving'
+              // - 'saving' or 'saved': don't do anything.
+              if (this.activeFieldStates.indexOf(activeFieldState) !== -1) {
+                activeField.set('state', 'candidate');
+              }
+              else if (activeFieldState === 'changed' || activeFieldState === 'invalid') {
+                activeField.set('state', 'saving');
+              }
+
+              // If the field that's being activated is in fact already in the
+              // invalid state (which can only happen because above we allowed
+              // the user to move on to another field to allow for a fluent UX;
+              // we assumed it would be saved successfully), then we shouldn't
+              // allow the field to enter the 'activating' state, instead, we
+              // simply change the active editor. All guarantees and
+              // assumptions for this field still hold!
+              if (from === 'invalid') {
+                this.model.set('activeField', fieldModel);
+                accept = false;
+              }
+              // Do not reject: the field is either in the 'candidate' or
+              // 'highlighted' state and we allow it to enter the 'activating'
+              // state!
+            }
+          }
+          // Reject going from activating/active to candidate because of a
+          // mouseleave.
+          else if (_.indexOf(this.activeFieldStates, from) !== -1 && to === 'candidate') {
+            if (context && context.reason === 'mouseleave') {
+              accept = false;
+            }
+          }
+          // When attempting to stop editing a changed/invalid property, ask for
+          // confirmation.
+          else if ((from === 'changed' || from === 'invalid') && to === 'candidate') {
+            if (context && context.reason === 'mouseleave') {
+              accept = false;
+            }
+            else {
+              // Check whether the transition has been confirmed?
+              if (context && context.confirmed) {
+                accept = true;
+              }
+            }
+          }
+        }
+      }
+
+      return accept;
+    },
+
+    /**
+     * Sets up the in-place editor for the given field.
+     *
+     * Must happen before the fieldModel's state is changed to 'candidate'.
+     *
+     * @param {Drupal.quickedit.FieldModel} fieldModel
+     *   The field for which an in-place editor must be set up.
+     */
+    setupEditor: function (fieldModel) {
+      // Get the corresponding entity toolbar.
+      var entityModel = fieldModel.get('entity');
+      var entityToolbarView = entityModel.toolbarView;
+      // Get the field toolbar DOM root from the entity toolbar.
+      var fieldToolbarRoot = entityToolbarView.getToolbarRoot();
+      // Create in-place editor.
+      var editorName = fieldModel.get('metadata').editor;
+      var editorModel = new Drupal.quickedit.EditorModel();
+      var editorView = new Drupal.quickedit.editors[editorName]({
+        el: $(fieldModel.get('el')),
+        model: editorModel,
+        fieldModel: fieldModel
+      });
+
+      // Create in-place editor's toolbar for this field — stored inside the
+      // entity toolbar, the entity toolbar will position itself appropriately
+      // above (or below) the edited element.
+      var toolbarView = new Drupal.quickedit.FieldToolbarView({
+        el: fieldToolbarRoot,
+        model: fieldModel,
+        $editedElement: $(editorView.getEditedElement()),
+        editorView: editorView,
+        entityModel: entityModel
+      });
+
+      // Create decoration for edited element: padding if necessary, sets
+      // classes on the element to style it according to the current state.
+      var decorationView = new Drupal.quickedit.FieldDecorationView({
+        el: $(editorView.getEditedElement()),
+        model: fieldModel,
+        editorView: editorView
+      });
+
+      // Track these three views in FieldModel so that we can tear them down
+      // correctly.
+      fieldModel.editorView = editorView;
+      fieldModel.toolbarView = toolbarView;
+      fieldModel.decorationView = decorationView;
+    },
+
+    /**
+     * Tears down the in-place editor for the given field.
+     *
+     * Must happen after the fieldModel's state is changed to 'inactive'.
+     *
+     * @param {Drupal.quickedit.FieldModel} fieldModel
+     *   The field for which an in-place editor must be torn down.
+     */
+    teardownEditor: function (fieldModel) {
+      // Early-return if this field was not yet decorated.
+      if (typeof fieldModel.editorView === 'undefined') {
+        return;
+      }
+
+      // Unbind event handlers; remove toolbar element; delete toolbar view.
+      fieldModel.toolbarView.remove();
+      delete fieldModel.toolbarView;
+
+      // Unbind event handlers; delete decoration view. Don't remove the element
+      // because that would remove the field itself.
+      fieldModel.decorationView.remove();
+      delete fieldModel.decorationView;
+
+      // Unbind event handlers; delete editor view. Don't remove the element
+      // because that would remove the field itself.
+      fieldModel.editorView.remove();
+      delete fieldModel.editorView;
+    },
+
+    /**
+     * Asks the user to confirm whether he wants to stop editing via a modal.
+     *
+     * @param {Drupal.quickedit.EntityModel} entityModel
+     *   An instance of the EntityModel class.
+     *
+     * @see Drupal.quickedit.AppView#acceptEditorStateChange
+     */
+    confirmEntityDeactivation: function (entityModel) {
+      var that = this;
+      var discardDialog;
+
+      function closeDiscardDialog(action) {
+        discardDialog.close(action);
+        // The active modal has been removed.
+        that.model.set('activeModal', null);
+
+        // If the targetState is saving, the field must be saved, then the
+        // entity must be saved.
+        if (action === 'save') {
+          entityModel.set('state', 'committing', {confirmed: true});
+        }
+        else {
+          entityModel.set('state', 'deactivating', {confirmed: true});
+          // Editing has been canceled and the changes will not be saved. Mark
+          // the page for reload if the entityModel declares that it requires
+          // a reload.
+          if (entityModel.get('reload')) {
+            reload = true;
+            entityModel.set('reload', false);
+          }
+        }
+      }
+
+      // Only instantiate if there isn't a modal instance visible yet.
+      if (!this.model.get('activeModal')) {
+        var $unsavedChanges = $('<div>' + Drupal.t('You have unsaved changes') + '</div>');
+        discardDialog = Drupal.dialog($unsavedChanges.get(0), {
+          title: Drupal.t('Discard changes?'),
+          dialogClass: 'quickedit-discard-modal',
+          resizable: false,
+          buttons: [
+            {
+              text: Drupal.t('Save'),
+              click: function () {
+                closeDiscardDialog('save');
+              },
+              primary: true
+            },
+            {
+              text: Drupal.t('Discard changes'),
+              click: function () {
+                closeDiscardDialog('discard');
+              }
+            }
+          ],
+          // Prevent this modal from being closed without the user making a
+          // choice as per http://stackoverflow.com/a/5438771.
+          closeOnEscape: false,
+          create: function () {
+            $(this).parent().find('.ui-dialog-titlebar-close').remove();
+          },
+          beforeClose: false,
+          close: function (event) {
+            // Automatically destroy the DOM element that was used for the
+            // dialog.
+            $(event.target).remove();
+          }
+        });
+        this.model.set('activeModal', discardDialog);
+
+        discardDialog.showModal();
+      }
+    },
+
+    /**
+     * Reacts to field state changes; tracks global state.
+     *
+     * @param {Drupal.quickedit.FieldModel} fieldModel
+     *   The `fieldModel` holding the state.
+     * @param {string} state
+     *   The state of the associated field. One of
+     *   {@link Drupal.quickedit.FieldModel.states}.
+     */
+    editorStateChange: function (fieldModel, state) {
+      var from = fieldModel.previous('state');
+      var to = state;
+
+      // Keep track of the highlighted field in the global state.
+      if (_.indexOf(this.singleFieldStates, to) !== -1 && this.model.get('highlightedField') !== fieldModel) {
+        this.model.set('highlightedField', fieldModel);
+      }
+      else if (this.model.get('highlightedField') === fieldModel && to === 'candidate') {
+        this.model.set('highlightedField', null);
+      }
+
+      // Keep track of the active field in the global state.
+      if (_.indexOf(this.activeFieldStates, to) !== -1 && this.model.get('activeField') !== fieldModel) {
+        this.model.set('activeField', fieldModel);
+      }
+      else if (this.model.get('activeField') === fieldModel && to === 'candidate') {
+        // Discarded if it transitions from a changed state to 'candidate'.
+        if (from === 'changed' || from === 'invalid') {
+          fieldModel.editorView.revert();
+        }
+        this.model.set('activeField', null);
+      }
+    },
+
+    /**
+     * Render an updated field (a field whose 'html' attribute changed).
+     *
+     * @param {Drupal.quickedit.FieldModel} fieldModel
+     *   The FieldModel whose 'html' attribute changed.
+     * @param {string} html
+     *   The updated 'html' attribute.
+     * @param {object} options
+     *   An object with the following keys:
+     * @param {bool} options.propagation
+     *   Whether this change to the 'html' attribute occurred because of the
+     *   propagation of changes to another instance of this field.
+     */
+    renderUpdatedField: function (fieldModel, html, options) {
+      // Get data necessary to rerender property before it is unavailable.
+      var $fieldWrapper = $(fieldModel.get('el'));
+      var $context = $fieldWrapper.parent();
+
+      var renderField = function () {
+        // Destroy the field model; this will cause all attached views to be
+        // destroyed too, and removal from all collections in which it exists.
+        fieldModel.destroy();
+
+        // Replace the old content with the new content.
+        $fieldWrapper.replaceWith(html);
+
+        // Attach behaviors again to the modified piece of HTML; this will
+        // create a new field model and call rerenderedFieldToCandidate() with
+        // it.
+        Drupal.attachBehaviors($context.get(0));
+      };
+
+      // When propagating the changes of another instance of this field, this
+      // field is not being actively edited and hence no state changes are
+      // necessary. So: only update the state of this field when the rerendering
+      // of this field happens not because of propagation, but because it is
+      // being edited itself.
+      if (!options.propagation) {
+        // Deferred because renderUpdatedField is reacting to a field model
+        // change event, and we want to make sure that event fully propagates
+        // before making another change to the same model.
+        _.defer(function () {
+          // First set the state to 'candidate', to allow all attached views to
+          // clean up all their "active state"-related changes.
+          fieldModel.set('state', 'candidate');
+
+          // Similarly, the above .set() call's change event must fully
+          // propagate before calling it again.
+          _.defer(function () {
+            // Set the field's state to 'inactive', to enable the updating of
+            // its DOM value.
+            fieldModel.set('state', 'inactive', {reason: 'rerender'});
+
+            renderField();
+          });
+        });
+      }
+      else {
+        renderField();
+      }
+    },
+
+    /**
+     * Propagates changes to an updated field to all instances of that field.
+     *
+     * @param {Drupal.quickedit.FieldModel} updatedField
+     *   The FieldModel whose 'html' attribute changed.
+     * @param {string} html
+     *   The updated 'html' attribute.
+     * @param {object} options
+     *   An object with the following keys:
+     * @param {bool} options.propagation
+     *   Whether this change to the 'html' attribute occurred because of the
+     *   propagation of changes to another instance of this field.
+     *
+     * @see Drupal.quickedit.AppView#renderUpdatedField
+     */
+    propagateUpdatedField: function (updatedField, html, options) {
+      // Don't propagate field updates that themselves were caused by
+      // propagation.
+      if (options.propagation) {
+        return;
+      }
+
+      var htmlForOtherViewModes = updatedField.get('htmlForOtherViewModes');
+      Drupal.quickedit.collections.fields
+        // Find all instances of fields that display the same logical field
+        // (same entity, same field, just a different instance and maybe a
+        // different view mode).
+        .where({logicalFieldID: updatedField.get('logicalFieldID')})
+        .forEach(function (field) {
+          // Ignore the field that was already updated.
+          if (field === updatedField) {
+            return;
+          }
+          // If this other instance of the field has the same view mode, we can
+          // update it easily.
+          else if (field.getViewMode() === updatedField.getViewMode()) {
+            field.set('html', updatedField.get('html'));
+          }
+          // If this other instance of the field has a different view mode, and
+          // that is one of the view modes for which a re-rendered version is
+          // available (and that should be the case unless this field was only
+          // added to the page after editing of the updated field began), then
+          // use that view mode's re-rendered version.
+          else {
+            if (field.getViewMode() in htmlForOtherViewModes) {
+              field.set('html', htmlForOtherViewModes[field.getViewMode()], {propagation: true});
+            }
+          }
+        });
+    },
+
+    /**
+     * If the new in-place editable field is for the entity that's currently
+     * being edited, then transition it to the 'candidate' state.
+     *
+     * This happens when a field was modified, saved and hence rerendered.
+     *
+     * @param {Drupal.quickedit.FieldModel} fieldModel
+     *   A field that was just added to the collection of fields.
+     */
+    rerenderedFieldToCandidate: function (fieldModel) {
+      var activeEntity = Drupal.quickedit.collections.entities.findWhere({isActive: true});
+
+      // Early-return if there is no active entity.
+      if (!activeEntity) {
+        return;
+      }
+
+      // If the field's entity is the active entity, make it a candidate.
+      if (fieldModel.get('entity') === activeEntity) {
+        this.setupEditor(fieldModel);
+        fieldModel.set('state', 'candidate');
+      }
+    },
+
+    /**
+     * EntityModel Collection change handler.
+     *
+     * Handler is called `change:isActive` and enforces a single active entity.
+     *
+     * @param {Drupal.quickedit.EntityModel} changedEntityModel
+     *   The entityModel instance whose active state has changed.
+     */
+    enforceSingleActiveEntity: function (changedEntityModel) {
+      // When an entity is deactivated, we don't need to enforce anything.
+      if (changedEntityModel.get('isActive') === false) {
+        return;
+      }
+
+      // This entity was activated; deactivate all other entities.
+      changedEntityModel.collection.chain()
+        .filter(function (entityModel) {
+          return entityModel.get('isActive') === true && entityModel !== changedEntityModel;
+        })
+        .each(function (entityModel) {
+          entityModel.set('state', 'deactivating');
+        });
+    }
+
+  });
+
+}(jQuery, _, Backbone, Drupal));
diff --git a/core/themes/stable/js/quickedit/views/ContextualLinkView.js b/core/themes/stable/js/quickedit/views/ContextualLinkView.js
new file mode 100644
index 0000000..288101a
--- /dev/null
+++ b/core/themes/stable/js/quickedit/views/ContextualLinkView.js
@@ -0,0 +1,81 @@
+/**
+ * @file
+ * A Backbone View that provides a dynamic contextual link.
+ */
+
+(function ($, Backbone, Drupal) {
+
+  "use strict";
+
+  Drupal.quickedit.ContextualLinkView = Backbone.View.extend(/** @lends Drupal.quickedit.ContextualLinkView# */{
+
+    /**
+     * Define all events to listen to.
+     *
+     * @return {object}
+     *   A map of events.
+     */
+    events: function () {
+      // Prevents delay and simulated mouse events.
+      function touchEndToClick(event) {
+        event.preventDefault();
+        event.target.click();
+      }
+
+      return {
+        'click a': function (event) {
+          event.preventDefault();
+          this.model.set('state', 'launching');
+        },
+        'touchEnd a': touchEndToClick
+      };
+    },
+
+    /**
+     * Create a new contextual link view.
+     *
+     * @constructs
+     *
+     * @augments Backbone.View
+     *
+     * @param {object} options
+     *   An object with the following keys:
+     * @param {Drupal.quickedit.EntityModel} options.model
+     *   The associated entity's model.
+     * @param {Drupal.quickedit.AppModel} options.appModel
+     *   The application state model.
+     * @param {object} options.strings
+     *   The strings for the "Quick edit" link.
+     */
+    initialize: function (options) {
+      // Insert the text of the quick edit toggle.
+      this.$el.find('a').text(options.strings.quickEdit);
+      // Initial render.
+      this.render();
+      // Re-render whenever this entity's isActive attribute changes.
+      this.listenTo(this.model, 'change:isActive', this.render);
+    },
+
+    /**
+     * Render function for the contextual link view.
+     *
+     * @param {Drupal.quickedit.EntityModel} entityModel
+     *   The associated `EntityModel`.
+     * @param {bool} isActive
+     *   Whether the in-place editor is active or not.
+     *
+     * @return {Drupal.quickedit.ContextualLinkView}
+     *   The `ContextualLinkView` in question.
+     */
+    render: function (entityModel, isActive) {
+      this.$el.find('a').attr('aria-pressed', isActive);
+
+      // Hides the contextual links if an in-place editor is active.
+      this.$el.closest('.contextual').toggle(!isActive);
+
+      return this;
+    }
+
+  });
+
+})(jQuery, Backbone, Drupal);
diff --git a/core/themes/stable/js/quickedit/views/EditorView.js b/core/themes/stable/js/quickedit/views/EditorView.js
new file mode 100644
index 0000000..31ec5a5
--- /dev/null
+++ b/core/themes/stable/js/quickedit/views/EditorView.js
@@ -0,0 +1,304 @@
+/**
+ * @file
+ * An abstract Backbone View that controls an in-place editor.
+ */
+
+(function ($, Backbone, Drupal) {
+
+  "use strict";
+
+  Drupal.quickedit.EditorView = Backbone.View.extend(/** @lends Drupal.quickedit.EditorView# */{
+
+    /**
+     * A base implementation that outlines the structure for in-place editors.
+     *
+     * Specific in-place editor implementations should subclass (extend) this
+     * View and override whichever method they deem necessary to override.
+     *
+     * Typically you would want to override this method to set the
+     * originalValue attribute in the FieldModel to such a value that your
+     * in-place editor can revert to the original value when necessary.
+     *
+     * @example
+     * <caption>If you override this method, you should call this
+     * method (the parent class' initialize()) first.</caption>
+     * Drupal.quickedit.EditorView.prototype.initialize.call(this, options);
+     *
+     * @constructs
+     *
+     * @augments Backbone.View
+     *
+     * @param {object} options
+     *   An object with the following keys:
+     * @param {Drupal.quickedit.EditorModel} options.model
+     *   The in-place editor state model.
+     * @param {Drupal.quickedit.FieldModel} options.fieldModel
+     *   The field model.
+     *
+     * @see Drupal.quickedit.EditorModel
+     * @see Drupal.quickedit.editors.plain_text
+     */
+    initialize: function (options) {
+      this.fieldModel = options.fieldModel;
+      this.listenTo(this.fieldModel, 'change:state', this.stateChange);
+    },
+
+    /**
+     * @inheritdoc
+     */
+    remove: function () {
+      // The el property is the field, which should not be removed. Remove the
+      // pointer to it, then call Backbone.View.prototype.remove().
+      this.setElement();
+      Backbone.View.prototype.remove.call(this);
+    },
+
+    /**
+     * Returns the edited element.
+     *
+     * For some single cardinality fields, it may be necessary or useful to
+     * not in-place edit (and hence decorate) the DOM element with the
+     * data-quickedit-field-id attribute (which is the field's wrapper), but a
+     * specific element within the field's wrapper.
+     * e.g. using a WYSIWYG editor on a body field should happen on the DOM
+     * element containing the text itself, not on the field wrapper.
+     *
+     * @return {jQuery}
+     *   A jQuery-wrapped DOM element.
+     *
+     * @see Drupal.quickedit.editors.plain_text
+     */
+    getEditedElement: function () {
+      return this.$el;
+    },
+
+    /**
+     *
+     * @return {object}
+     * Returns 3 Quick Edit UI settings that depend on the in-place editor:
+     *  - Boolean padding: indicates whether padding should be applied to the
+     *    edited element, to guarantee legibility of text.
+     *  - Boolean unifiedToolbar: provides the in-place editor with the ability
+     *    to insert its own toolbar UI into Quick Edit's tightly integrated
+     *    toolbar.
+     *  - Boolean fullWidthToolbar: indicates whether Quick Edit's tightly
+     *    integrated toolbar should consume the full width of the element,
+     *    rather than being just long enough to accommodate a label.
+     */
+    getQuickEditUISettings: function () {
+      return {padding: false, unifiedToolbar: false, fullWidthToolbar: false, popup: false};
+    },
+
+    /**
+     * Determines the actions to take given a change of state.
+     *
+     * @param {Drupal.quickedit.FieldModel} fieldModel
+     *   The quickedit `FieldModel` that holds the state.
+     * @param {string} state
+     *   The state of the associated field. One of
+     *   {@link Drupal.quickedit.FieldModel.states}.
+     */
+    stateChange: function (fieldModel, state) {
+      var from = fieldModel.previous('state');
+      var to = state;
+      switch (to) {
+        case 'inactive':
+          // An in-place editor view will not yet exist in this state, hence
+          // this will never be reached. Listed for sake of completeness.
+          break;
+
+        case 'candidate':
+          // Nothing to do for the typical in-place editor: it should not be
+          // visible yet. Except when we come from the 'invalid' state, then we
+          // clean up.
+          if (from === 'invalid') {
+            this.removeValidationErrors();
+          }
+          break;
+
+        case 'highlighted':
+          // Nothing to do for the typical in-place editor: it should not be
+          // visible yet.
+          break;
+
+        case 'activating':
+          // The user has indicated he wants to do in-place editing: if
+          // something needs to be loaded (CSS/JavaScript/server data/…), then
+          // do so at this stage, and once the in-place editor is ready,
+          // set the 'active' state. A "loading" indicator will be shown in the
+          // UI for as long as the field remains in this state.
+          var loadDependencies = function (callback) {
+            // Do the loading here.
+            callback();
+          };
+          loadDependencies(function () {
+            fieldModel.set('state', 'active');
+          });
+          break;
+
+        case 'active':
+          // The user can now actually use the in-place editor.
+          break;
+
+        case 'changed':
+          // Nothing to do for the typical in-place editor. The UI will show an
+          // indicator that the field has changed.
+          break;
+
+        case 'saving':
+          // When the user has indicated he wants to save his changes to this
+          // field, this state will be entered. If the previous saving attempt
+          // resulted in validation errors, the previous state will be
+          // 'invalid'. Clean up those validation errors while the user is
+          // saving.
+          if (from === 'invalid') {
+            this.removeValidationErrors();
+          }
+          this.save();
+          break;
+
+        case 'saved':
+          // Nothing to do for the typical in-place editor. Immediately after
+          // being saved, a field will go to the 'candidate' state, where it
+          // should no longer be visible (after all, the field will then again
+          // just be a *candidate* to be in-place edited).
+          break;
+
+        case 'invalid':
+          // The modified field value was attempted to be saved, but there were
+          // validation errors.
+          this.showValidationErrors();
+          break;
+      }
+    },
+
+    /**
+     * Reverts the modified value to the original, before editing started.
+     */
+    revert: function () {
+      // A no-op by default; each editor should implement reverting itself.
+      // Note that if the in-place editor does not cause the FieldModel's
+      // element to be modified, then nothing needs to happen.
+    },
+
+    /**
+     * Saves the modified value in the in-place editor for this field.
+     */
+    save: function () {
+      var fieldModel = this.fieldModel;
+      var editorModel = this.model;
+      var backstageId = 'quickedit_backstage-' + this.fieldModel.id.replace(/[\/\[\]\_\s]/g, '-');
+
+      function fillAndSubmitForm(value) {
+        var $form = $('#' + backstageId).find('form');
+        // Fill in the value in any <input> that isn't hidden or a submit
+        // button.
+        $form.find(':input[type!="hidden"][type!="submit"]:not(select)')
+          // Don't mess with the node summary.
+          .not('[name$="\\[summary\\]"]').val(value);
+        // Submit the form.
+        $form.find('.quickedit-form-submit').trigger('click.quickedit');
+      }
+
+      var formOptions = {
+        fieldID: this.fieldModel.get('fieldID'),
+        $el: this.$el,
+        nocssjs: true,
+        other_view_modes: fieldModel.findOtherViewModes(),
+        // Reset an existing entry for this entity in the PrivateTempStore (if
+        // any) when saving the field. Logically speaking, this should happen in
+        // a separate request because this is an entity-level operation, not a
+        // field-level operation. But that would require an additional request,
+        // that might not even be necessary: it is only when a user saves a
+        // first changed field for an entity that this needs to happen:
+        // precisely now!
+        reset: !this.fieldModel.get('entity').get('inTempStore')
+      };
+
+      var self = this;
+      Drupal.quickedit.util.form.load(formOptions, function (form, ajax) {
+        // Create a backstage area for storing forms that are hidden from view
+        // (hence "backstage" — since the editing doesn't happen in the form, it
+        // happens "directly" in the content, the form is only used for saving).
+        var $backstage = $(Drupal.theme('quickeditBackstage', {id: backstageId})).appendTo('body');
+        // Hidden forms are stuffed into the backstage container for this field.
+        var $form = $(form).appendTo($backstage);
+        // Disable the browser's HTML5 validation; we only care about server-
+        // side validation. (Not disabling this will actually cause problems
+        // because browsers don't like to set HTML5 validation errors on hidden
+        // forms.)
+        $form.prop('novalidate', true);
+        var $submit = $form.find('.quickedit-form-submit');
+        self.formSaveAjax = Drupal.quickedit.util.form.ajaxifySaving(formOptions, $submit);
+
+        function removeHiddenForm() {
+          Drupal.quickedit.util.form.unajaxifySaving(self.formSaveAjax);
+          delete self.formSaveAjax;
+          $backstage.remove();
+        }
+
+        // Successfully saved.
+        self.formSaveAjax.commands.quickeditFieldFormSaved = function (ajax, response, status) {
+          removeHiddenForm();
+          // First, transition the state to 'saved'.
+          fieldModel.set('state', 'saved');
+          // Second, set the 'htmlForOtherViewModes' attribute, so that when
+          // this field is rerendered, the change can be propagated to other
+          // instances of this field, which may be displayed in different view
+          // modes.
+          fieldModel.set('htmlForOtherViewModes', response.other_view_modes);
+          // Finally, set the 'html' attribute on the field model. This will
+          // cause the field to be rerendered.
+          fieldModel.set('html', response.data);
+        };
+
+        // Unsuccessfully saved; validation errors.
+        self.formSaveAjax.commands.quickeditFieldFormValidationErrors = function (ajax, response, status) {
+          removeHiddenForm();
+          editorModel.set('validationErrors', response.data);
+          fieldModel.set('state', 'invalid');
+        };
+
+        // The quickeditFieldForm AJAX command is only called upon loading the
+        // form for the first time, and when there are validation errors in the
+        // form; Form API then marks which form items have errors. This is
+        // useful for the form-based in-place editor, but pointless for any
+        // other: the form itself won't be visible at all anyway! So, we just
+        // ignore it.
+        self.formSaveAjax.commands.quickeditFieldForm = function () {};
+
+        fillAndSubmitForm(editorModel.get('currentValue'));
+      });
+    },
+
+    /**
+     * Shows validation error messages.
+     *
+     * Should be called when the state is changed to 'invalid'.
+     */
+    showValidationErrors: function () {
+      var $errors = $('<div class="quickedit-validation-errors"></div>')
+        .append(this.model.get('validationErrors'));
+      this.getEditedElement()
+        .addClass('quickedit-validation-error')
+        .after($errors);
+    },
+
+    /**
+     * Cleans up validation error messages.
+     *
+     * Should be called when the state is changed to 'candidate' or 'saving'. In
+     * the case of the latter: the user has modified the value in the in-place
+     * editor again to attempt to save again. In the case of the latter: the
+     * invalid value was discarded.
+     */
+    removeValidationErrors: function () {
+      this.getEditedElement()
+        .removeClass('quickedit-validation-error')
+        .next('.quickedit-validation-errors')
+        .remove();
+    }
+
+  });
+
+}(jQuery, Backbone, Drupal));
diff --git a/core/themes/stable/js/quickedit/views/EntityDecorationView.js b/core/themes/stable/js/quickedit/views/EntityDecorationView.js
new file mode 100644
index 0000000..e4607ef
--- /dev/null
+++ b/core/themes/stable/js/quickedit/views/EntityDecorationView.js
@@ -0,0 +1,40 @@
+/**
+ * @file
+ * A Backbone view that decorates the in-place editable entity.
+ */
+
+(function (Drupal, $, Backbone) {
+
+  "use strict";
+
+  Drupal.quickedit.EntityDecorationView = Backbone.View.extend(/** @lends Drupal.quickedit.EntityDecorationView# */{
+
+    /**
+     * Associated with the DOM root node of an editable entity.
+     *
+     * @constructs
+     *
+     * @augments Backbone.View
+     */
+    initialize: function () {
+      this.listenTo(this.model, 'change', this.render);
+    },
+
+    /**
+     * @inheritdoc
+     */
+    render: function () {
+      this.$el.toggleClass('quickedit-entity-active', this.model.get('isActive'));
+    },
+
+    /**
+     * @inheritdoc
+     */
+    remove: function () {
+      this.setElement(null);
+      Backbone.View.prototype.remove.call(this);
+    }
+
+  });
+
+}(Drupal, jQuery, Backbone));
diff --git a/core/themes/stable/js/quickedit/views/EntityToolbarView.js b/core/themes/stable/js/quickedit/views/EntityToolbarView.js
new file mode 100644
index 0000000..61de7bb
--- /dev/null
+++ b/core/themes/stable/js/quickedit/views/EntityToolbarView.js
@@ -0,0 +1,528 @@
+/**
+ * @file
+ * A Backbone View that provides an entity level toolbar.
+ */
+
+(function ($, _, Backbone, Drupal, debounce) {
+
+  "use strict";
+
+  Drupal.quickedit.EntityToolbarView = Backbone.View.extend(/** @lends Drupal.quickedit.EntityToolbarView# */{
+
+    /**
+     * @type {jQuery}
+     */
+    _fieldToolbarRoot: null,
+
+    /**
+     * @return {object}
+     *   A map of events.
+     */
+    events: function () {
+      var map = {
+        'click button.action-save': 'onClickSave',
+        'click button.action-cancel': 'onClickCancel',
+        'mouseenter': 'onMouseenter'
+      };
+      return map;
+    },
+
+    /**
+     * @constructs
+     *
+     * @augments Backbone.View
+     *
+     * @param {object} options
+     *   Options to construct the view.
+     * @param {Drupal.quickedit.AppModel} options.appModel
+     *   A quickedit `AppModel` to use in the view.
+     */
+    initialize: function (options) {
+      var that = this;
+      this.appModel = options.appModel;
+      this.$entity = $(this.model.get('el'));
+
+      // Rerender whenever the entity state changes.
+      this.listenTo(this.model, 'change:isActive change:isDirty change:state', this.render);
+      // Also rerender whenever a different field is highlighted or activated.
+      this.listenTo(this.appModel, 'change:highlightedField change:activeField', this.render);
+      // Rerender when a field of the entity changes state.
+      this.listenTo(this.model.get('fields'), 'change:state', this.fieldStateChange);
+
+      // Reposition the entity toolbar as the viewport and the position within
+      // the viewport changes.
+      $(window).on('resize.quickedit scroll.quickedit', debounce($.proxy(this.windowChangeHandler, this), 150));
+
+      // Adjust the fence placement within which the entity toolbar may be
+      // positioned.
+      $(document).on('drupalViewportOffsetChange.quickedit', function (event, offsets) {
+        if (that.$fence) {
+          that.$fence.css(offsets);
+        }
+      });
+
+      // Set the entity toolbar DOM element as the el for this view.
+      var $toolbar = this.buildToolbarEl();
+      this.setElement($toolbar);
+      this._fieldToolbarRoot = $toolbar.find('.quickedit-toolbar-field').get(0);
+
+      // Initial render.
+      this.render();
+    },
+
+    /**
+     * @inheritdoc
+     *
+     * @return {Drupal.quickedit.EntityToolbarView}
+     *   The entity toolbar view.
+     */
+    render: function () {
+      if (this.model.get('isActive')) {
+        // If the toolbar container doesn't exist, create it.
+        var $body = $('body');
+        if ($body.children('#quickedit-entity-toolbar').length === 0) {
+          $body.append(this.$el);
+        }
+        // The fence will define a area on the screen that the entity toolbar
+        // will be position within.
+        if ($body.children('#quickedit-toolbar-fence').length === 0) {
+          this.$fence = $(Drupal.theme('quickeditEntityToolbarFence'))
+            .css(Drupal.displace())
+            .appendTo($body);
+        }
+        // Adds the entity title to the toolbar.
+        this.label();
+
+        // Show the save and cancel buttons.
+        this.show('ops');
+        // If render is being called and the toolbar is already visible, just
+        // reposition it.
+        this.position();
+      }
+
+      // The save button text and state varies with the state of the entity
+      // model.
+      var $button = this.$el.find('.quickedit-button.action-save');
+      var isDirty = this.model.get('isDirty');
+      // Adjust the save button according to the state of the model.
+      switch (this.model.get('state')) {
+        // Quick editing is active, but no field is being edited.
+        case 'opened':
+          // The saving throbber is not managed by AJAX system. The
+          // EntityToolbarView manages this visual element.
+          $button
+            .removeClass('action-saving icon-throbber icon-end')
+            .text(Drupal.t('Save'))
+            .removeAttr('disabled')
+            .attr('aria-hidden', !isDirty);
+          break;
+
+        // The changes to the fields of the entity are being committed.
+        case 'committing':
+          $button
+            .addClass('action-saving icon-throbber icon-end')
+            .text(Drupal.t('Saving'))
+            .attr('disabled', 'disabled');
+          break;
+
+        default:
+          $button.attr('aria-hidden', true);
+          break;
+      }
+
+      return this;
+    },
+
+    /**
+     * @inheritdoc
+     */
+    remove: function () {
+      // Remove additional DOM elements controlled by this View.
+      this.$fence.remove();
+
+      // Stop listening to additional events.
+      $(window).off('resize.quickedit scroll.quickedit');
+      $(document).off('drupalViewportOffsetChange.quickedit');
+
+      Backbone.View.prototype.remove.call(this);
+    },
+
+    /**
+     * Repositions the entity toolbar on window scroll and resize.
+     *
+     * @param {jQuery.Event} event
+     *   The scroll or resize event.
+     */
+    windowChangeHandler: function (event) {
+      this.position();
+    },
+
+    /**
+     * Determines the actions to take given a change of state.
+     *
+     * @param {Drupal.quickedit.FieldModel} model
+     *   The `FieldModel` model.
+     * @param {string} state
+     *   The state of the associated field. One of
+     *   {@link Drupal.quickedit.FieldModel.states}.
+     */
+    fieldStateChange: function (model, state) {
+      switch (state) {
+        case 'active':
+          this.render();
+          break;
+
+        case 'invalid':
+          this.render();
+          break;
+      }
+    },
+
+    /**
+     * Uses the jQuery.ui.position() method to position the entity toolbar.
+     *
+     * @param {HTMLElement} [element]
+     *   The element against which the entity toolbar is positioned.
+     */
+    position: function (element) {
+      clearTimeout(this.timer);
+
+      var that = this;
+      // Vary the edge of the positioning according to the direction of language
+      // in the document.
+      var edge = (document.documentElement.dir === 'rtl') ? 'right' : 'left';
+      // A time unit to wait until the entity toolbar is repositioned.
+      var delay = 0;
+      // Determines what check in the series of checks below should be
+      // evaluated.
+      var check = 0;
+      // When positioned against an active field that has padding, we should
+      // ignore that padding when positioning the toolbar, to not unnecessarily
+      // move the toolbar horizontally, which feels annoying.
+      var horizontalPadding = 0;
+      var of;
+      var activeField;
+      var highlightedField;
+      // There are several elements in the page that the entity toolbar might be
+      // positioned against. They are considered below in a priority order.
+      do {
+        switch (check) {
+          case 0:
+            // Position against a specific element.
+            of = element;
+            break;
+
+          case 1:
+            // Position against a form container.
+            activeField = Drupal.quickedit.app.model.get('activeField');
+            of = activeField && activeField.editorView && activeField.editorView.$formContainer && activeField.editorView.$formContainer.find('.quickedit-form');
+            break;
+
+          case 2:
+            // Position against an active field.
+            of = activeField && activeField.editorView && activeField.editorView.getEditedElement();
+            if (activeField && activeField.editorView && activeField.editorView.getQuickEditUISettings().padding) {
+              horizontalPadding = 5;
+            }
+            break;
+
+          case 3:
+            // Position against a highlighted field.
+            highlightedField = Drupal.quickedit.app.model.get('highlightedField');
+            of = highlightedField && highlightedField.editorView && highlightedField.editorView.getEditedElement();
+            delay = 250;
+            break;
+
+          default:
+            var fieldModels = this.model.get('fields').models;
+            var topMostPosition = 1000000;
+            var topMostField = null;
+            // Position against the topmost field.
+            for (var i = 0; i < fieldModels.length; i++) {
+              var pos = fieldModels[i].get('el').getBoundingClientRect().top;
+              if (pos < topMostPosition) {
+                topMostPosition = pos;
+                topMostField = fieldModels[i];
+              }
+            }
+            of = topMostField.get('el');
+            delay = 50;
+            break;
+        }
+        // Prepare to check the next possible element to position against.
+        check++;
+      } while (!of);
+
+      /**
+       * Refines the positioning algorithm of jquery.ui.position().
+       *
+       * Invoked as the 'using' callback of jquery.ui.position() in
+       * positionToolbar().
+       *
+       * @param {*} view
+       *   The view the positions will be calculated from.
+       * @param {object} suggested
+       *   A hash of top and left values for the position that should be set. It
+       *   can be forwarded to .css() or .animate().
+       * @param {object} info
+       *   The position and dimensions of both the 'my' element and the 'of'
+       *   elements, as well as calculations to their relative position. This
+       *   object contains the following properties:
+       * @param {object} info.element
+       *   A hash that contains information about the HTML element that will be
+       *   positioned. Also known as the 'my' element.
+       * @param {object} info.target
+       *   A hash that contains information about the HTML element that the
+       *   'my' element will be positioned against. Also known as the 'of'
+       *   element.
+       */
+      function refinePosition(view, suggested, info) {
+        // Determine if the pointer should be on the top or bottom.
+        var isBelow = suggested.top > info.target.top;
+        info.element.element.toggleClass('quickedit-toolbar-pointer-top', isBelow);
+        // Don't position the toolbar past the first or last editable field if
+        // the entity is the target.
+        if (view.$entity[0] === info.target.element[0]) {
+          // Get the first or last field according to whether the toolbar is
+          // above or below the entity.
+          var $field = view.$entity.find('.quickedit-editable').eq((isBelow) ? -1 : 0);
+          if ($field.length > 0) {
+            suggested.top = (isBelow) ? ($field.offset().top + $field.outerHeight(true)) : $field.offset().top - info.element.element.outerHeight(true);
+          }
+        }
+        // Don't let the toolbar go outside the fence.
+        var fenceTop = view.$fence.offset().top;
+        var fenceHeight = view.$fence.height();
+        var toolbarHeight = info.element.element.outerHeight(true);
+        if (suggested.top < fenceTop) {
+          suggested.top = fenceTop;
+        }
+        else if ((suggested.top + toolbarHeight) > (fenceTop + fenceHeight)) {
+          suggested.top = fenceTop + fenceHeight - toolbarHeight;
+        }
+        // Position the toolbar.
+        info.element.element.css({
+          left: Math.floor(suggested.left),
+          top: Math.floor(suggested.top)
+        });
+      }
+
+      /**
+       * Calls the jquery.ui.position() method on the $el of this view.
+       */
+      function positionToolbar() {
+        that.$el
+          .position({
+            my: edge + ' bottom',
+            // Move the toolbar 1px towards the start edge of the 'of' element,
+            // plus any horizontal padding that may have been added to the
+            // element that is being added, to prevent unwanted horizontal
+            // movement.
+            at: edge + '+' + (1 + horizontalPadding) + ' top',
+            of: of,
+            collision: 'flipfit',
+            using: refinePosition.bind(null, that),
+            within: that.$fence
+          })
+          // Resize the toolbar to match the dimensions of the field, up to a
+          // maximum width that is equal to 90% of the field's width.
+          .css({
+            'max-width': (document.documentElement.clientWidth < 450) ? document.documentElement.clientWidth : 450,
+            // Set a minimum width of 240px for the entity toolbar, or the width
+            // of the client if it is less than 240px, so that the toolbar
+            // never folds up into a squashed and jumbled mess.
+            'min-width': (document.documentElement.clientWidth < 240) ? document.documentElement.clientWidth : 240,
+            'width': '100%'
+          });
+      }
+
+      // Uses the jQuery.ui.position() method. Use a timeout to move the toolbar
+      // only after the user has focused on an editable for 250ms. This prevents
+      // the toolbar from jumping around the screen.
+      this.timer = setTimeout(function () {
+        // Render the position in the next execution cycle, so that animations
+        // on the field have time to process. This is not strictly speaking, a
+        // guarantee that all animations will be finished, but it's a simple
+        // way to get better positioning without too much additional code.
+        _.defer(positionToolbar);
+      }, delay);
+    },
+
+    /**
+     * Set the model state to 'saving' when the save button is clicked.
+     *
+     * @param {jQuery.Event} event
+     *   The click event.
+     */
+    onClickSave: function (event) {
+      event.stopPropagation();
+      event.preventDefault();
+      // Save the model.
+      this.model.set('state', 'committing');
+    },
+
+    /**
+     * Sets the model state to candidate when the cancel button is clicked.
+     *
+     * @param {jQuery.Event} event
+     *   The click event.
+     */
+    onClickCancel: function (event) {
+      event.preventDefault();
+      this.model.set('state', 'deactivating');
+    },
+
+    /**
+     * Clears the timeout that will eventually reposition the entity toolbar.
+     *
+     * Without this, it may reposition itself, away from the user's cursor!
+     *
+     * @param {jQuery.Event} event
+     *   The mouse event.
+     */
+    onMouseenter: function (event) {
+      clearTimeout(this.timer);
+    },
+
+    /**
+     * Builds the entity toolbar HTML; attaches to DOM; sets starting position.
+     *
+     * @return {jQuery}
+     *   The toolbar element.
+     */
+    buildToolbarEl: function () {
+      var $toolbar = $(Drupal.theme('quickeditEntityToolbar', {
+        id: 'quickedit-entity-toolbar'
+      }));
+
+      $toolbar
+        .find('.quickedit-toolbar-entity')
+        // Append the "ops" toolgroup into the toolbar.
+        .prepend(Drupal.theme('quickeditToolgroup', {
+          classes: ['ops'],
+          buttons: [
+            {
+              label: Drupal.t('Save'),
+              type: 'submit',
+              classes: 'action-save quickedit-button icon',
+              attributes: {
+                'aria-hidden': true
+              }
+            },
+            {
+              label: Drupal.t('Close'),
+              classes: 'action-cancel quickedit-button icon icon-close icon-only'
+            }
+          ]
+        }));
+
+      // Give the toolbar a sensible starting position so that it doesn't
+      // animate on to the screen from a far off corner.
+      $toolbar
+        .css({
+          left: this.$entity.offset().left,
+          top: this.$entity.offset().top
+        });
+
+      return $toolbar;
+    },
+
+    /**
+     * Returns the DOM element that fields will attach their toolbars to.
+     *
+     * @return {jQuery}
+     *   The DOM element that fields will attach their toolbars to.
+     */
+    getToolbarRoot: function () {
+      return this._fieldToolbarRoot;
+    },
+
+    /**
+     * Generates a state-dependent label for the entity toolbar.
+     */
+    label: function () {
+      // The entity label.
+      var label = '';
+      var entityLabel = this.model.get('label');
+
+      // Label of an active field, if it exists.
+      var activeField = Drupal.quickedit.app.model.get('activeField');
+      var activeFieldLabel = activeField && activeField.get('metadata').label;
+      // Label of a highlighted field, if it exists.
+      var highlightedField = Drupal.quickedit.app.model.get('highlightedField');
+      var highlightedFieldLabel = highlightedField && highlightedField.get('metadata').label;
+      // The label is constructed in a priority order.
+      if (activeFieldLabel) {
+        label = Drupal.theme('quickeditEntityToolbarLabel', {
+          entityLabel: entityLabel,
+          fieldLabel: activeFieldLabel
+        });
+      }
+      else if (highlightedFieldLabel) {
+        label = Drupal.theme('quickeditEntityToolbarLabel', {
+          entityLabel: entityLabel,
+          fieldLabel: highlightedFieldLabel
+        });
+      }
+      else {
+        // @todo Add XSS regression test coverage in https://www.drupal.org/node/2547437
+        label = Drupal.checkPlain(entityLabel);
+      }
+
+      this.$el
+        .find('.quickedit-toolbar-label')
+        .html(label);
+    },
+
+    /**
+     * Adds classes to a toolgroup.
+     *
+     * @param {string} toolgroup
+     *   A toolgroup name.
+     * @param {string} classes
+     *   A string of space-delimited class names that will be applied to the
+     *   wrapping element of the toolbar group.
+     */
+    addClass: function (toolgroup, classes) {
+      this._find(toolgroup).addClass(classes);
+    },
+
+    /**
+     * Removes classes from a toolgroup.
+     *
+     * @param {string} toolgroup
+     *   A toolgroup name.
+     * @param {string} classes
+     *   A string of space-delimited class names that will be removed from the
+     *   wrapping element of the toolbar group.
+     */
+    removeClass: function (toolgroup, classes) {
+      this._find(toolgroup).removeClass(classes);
+    },
+
+    /**
+     * Finds a toolgroup.
+     *
+     * @param {string} toolgroup
+     *   A toolgroup name.
+     *
+     * @return {jQuery}
+     *   The toolgroup DOM element.
+     */
+    _find: function (toolgroup) {
+      return this.$el.find('.quickedit-toolbar .quickedit-toolgroup.' + toolgroup);
+    },
+
+    /**
+     * Shows a toolgroup.
+     *
+     * @param {string} toolgroup
+     *   A toolgroup name.
+     */
+    show: function (toolgroup) {
+      this.$el.removeClass('quickedit-animate-invisible');
+    }
+
+  });
+
+})(jQuery, _, Backbone, Drupal, Drupal.debounce);
diff --git a/core/themes/stable/js/quickedit/views/FieldDecorationView.js b/core/themes/stable/js/quickedit/views/FieldDecorationView.js
new file mode 100644
index 0000000..b098f7f
--- /dev/null
+++ b/core/themes/stable/js/quickedit/views/FieldDecorationView.js
@@ -0,0 +1,360 @@
+/**
+ * @file
+ * A Backbone View that decorates the in-place edited element.
+ */
+
+(function ($, Backbone, Drupal) {
+
+  "use strict";
+
+  Drupal.quickedit.FieldDecorationView = Backbone.View.extend(/** @lends Drupal.quickedit.FieldDecorationView# */{
+
+    /**
+     * @type {null}
+     */
+    _widthAttributeIsEmpty: null,
+
+    /**
+     * @type {object}
+     */
+    events: {
+      'mouseenter.quickedit': 'onMouseEnter',
+      'mouseleave.quickedit': 'onMouseLeave',
+      'click': 'onClick',
+      'tabIn.quickedit': 'onMouseEnter',
+      'tabOut.quickedit': 'onMouseLeave'
+    },
+
+    /**
+     * @constructs
+     *
+     * @augments Backbone.View
+     *
+     * @param {object} options
+     *   An object with the following keys:
+     * @param {Drupal.quickedit.EditorView} options.editorView
+     *   The editor object view.
+     */
+    initialize: function (options) {
+      this.editorView = options.editorView;
+
+      this.listenTo(this.model, 'change:state', this.stateChange);
+      this.listenTo(this.model, 'change:isChanged change:inTempStore', this.renderChanged);
+    },
+
+    /**
+     * @inheritdoc
+     */
+    remove: function () {
+      // The el property is the field, which should not be removed. Remove the
+      // pointer to it, then call Backbone.View.prototype.remove().
+      this.setElement();
+      Backbone.View.prototype.remove.call(this);
+    },
+
+    /**
+     * Determines the actions to take given a change of state.
+     *
+     * @param {Drupal.quickedit.FieldModel} model
+     *   The `FieldModel` model.
+     * @param {string} state
+     *   The state of the associated field. One of
+     *   {@link Drupal.quickedit.FieldModel.states}.
+     */
+    stateChange: function (model, state) {
+      var from = model.previous('state');
+      var to = state;
+      switch (to) {
+        case 'inactive':
+          this.undecorate();
+          break;
+
+        case 'candidate':
+          this.decorate();
+          if (from !== 'inactive') {
+            this.stopHighlight();
+            if (from !== 'highlighted') {
+              this.model.set('isChanged', false);
+              this.stopEdit();
+            }
+          }
+          this._unpad();
+          break;
+
+        case 'highlighted':
+          this.startHighlight();
+          break;
+
+        case 'activating':
+          // NOTE: this state is not used by every editor! It's only used by
+          // those that need to interact with the server.
+          this.prepareEdit();
+          break;
+
+        case 'active':
+          if (from !== 'activating') {
+            this.prepareEdit();
+          }
+          if (this.editorView.getQuickEditUISettings().padding) {
+            this._pad();
+          }
+          break;
+
+        case 'changed':
+          this.model.set('isChanged', true);
+          break;
+
+        case 'saving':
+          break;
+
+        case 'saved':
+          break;
+
+        case 'invalid':
+          break;
+      }
+    },
+
+    /**
+     * Adds a class to the edited element that indicates whether the field has
+     * been changed by the user (i.e. locally) or the field has already been
+     * changed and stored before by the user (i.e. remotely, stored in
+     * PrivateTempStore).
+     */
+    renderChanged: function () {
+      this.$el.toggleClass('quickedit-changed', this.model.get('isChanged') || this.model.get('inTempStore'));
+    },
+
+    /**
+     * Starts hover; transitions to 'highlight' state.
+     *
+     * @param {jQuery.Event} event
+     *   The mouse event.
+     */
+    onMouseEnter: function (event) {
+      var that = this;
+      that.model.set('state', 'highlighted');
+      event.stopPropagation();
+    },
+
+    /**
+     * Stops hover; transitions to 'candidate' state.
+     *
+     * @param {jQuery.Event} event
+     *   The mouse event.
+     */
+    onMouseLeave: function (event) {
+      var that = this;
+      that.model.set('state', 'candidate', {reason: 'mouseleave'});
+      event.stopPropagation();
+    },
+
+    /**
+     * Transition to 'activating' stage.
+     *
+     * @param {jQuery.Event} event
+     *   The click event.
+     */
+    onClick: function (event) {
+      this.model.set('state', 'activating');
+      event.preventDefault();
+      event.stopPropagation();
+    },
+
+    /**
+     * Adds classes used to indicate an elements editable state.
+     */
+    decorate: function () {
+      this.$el.addClass('quickedit-candidate quickedit-editable');
+    },
+
+    /**
+     * Removes classes used to indicate an elements editable state.
+     */
+    undecorate: function () {
+      this.$el.removeClass('quickedit-candidate quickedit-editable quickedit-highlighted quickedit-editing');
+    },
+
+    /**
+     * Adds that class that indicates that an element is highlighted.
+     */
+    startHighlight: function () {
+      // Animations.
+      var that = this;
+      // Use a timeout to grab the next available animation frame.
+      that.$el.addClass('quickedit-highlighted');
+    },
+
+    /**
+     * Removes the class that indicates that an element is highlighted.
+     */
+    stopHighlight: function () {
+      this.$el.removeClass('quickedit-highlighted');
+    },
+
+    /**
+     * Removes the class that indicates that an element as editable.
+     */
+    prepareEdit: function () {
+      this.$el.addClass('quickedit-editing');
+
+      // Allow the field to be styled differently while editing in a pop-up
+      // in-place editor.
+      if (this.editorView.getQuickEditUISettings().popup) {
+        this.$el.addClass('quickedit-editor-is-popup');
+      }
+    },
+
+    /**
+     * Removes the class that indicates that an element is being edited.
+     *
+     * Reapplies the class that indicates that a candidate editable element is
+     * again available to be edited.
+     */
+    stopEdit: function () {
+      this.$el.removeClass('quickedit-highlighted quickedit-editing');
+
+      // Done editing in a pop-up in-place editor; remove the class.
+      if (this.editorView.getQuickEditUISettings().popup) {
+        this.$el.removeClass('quickedit-editor-is-popup');
+      }
+
+      // Make the other editors show up again.
+      $('.quickedit-candidate').addClass('quickedit-editable');
+    },
+
+    /**
+     * Adds padding around the editable element to make it pop visually.
+     */
+    _pad: function () {
+      // Early return if the element has already been padded.
+      if (this.$el.data('quickedit-padded')) {
+        return;
+      }
+      var self = this;
+
+      // Add 5px padding for readability. This means we'll freeze the current
+      // width and *then* add 5px padding, hence ensuring the padding is added
+      // "on the outside".
+      // 1) Freeze the width (if it's not already set); don't use animations.
+      if (this.$el[0].style.width === "") {
+        this._widthAttributeIsEmpty = true;
+        this.$el
+          .addClass('quickedit-animate-disable-width')
+          .css('width', this.$el.width());
+      }
+
+      // 2) Add padding; use animations.
+      var posProp = this._getPositionProperties(this.$el);
+      setTimeout(function () {
+        // Re-enable width animations (padding changes affect width too!).
+        self.$el.removeClass('quickedit-animate-disable-width');
+
+        // Pad the editable.
+        self.$el
+          .css({
+            'position': 'relative',
+            'top': posProp.top - 5 + 'px',
+            'left': posProp.left - 5 + 'px',
+            'padding-top': posProp['padding-top'] + 5 + 'px',
+            'padding-left': posProp['padding-left'] + 5 + 'px',
+            'padding-right': posProp['padding-right'] + 5 + 'px',
+            'padding-bottom': posProp['padding-bottom'] + 5 + 'px',
+            'margin-bottom': posProp['margin-bottom'] - 10 + 'px'
+          })
+          .data('quickedit-padded', true);
+      }, 0);
+    },
+
+    /**
+     * Removes the padding around the element being edited when editing ceases.
+     */
+    _unpad: function () {
+      // Early return if the element has not been padded.
+      if (!this.$el.data('quickedit-padded')) {
+        return;
+      }
+      var self = this;
+
+      // 1) Set the empty width again.
+      if (this._widthAttributeIsEmpty) {
+        this.$el
+          .addClass('quickedit-animate-disable-width')
+          .css('width', '');
+      }
+
+      // 2) Remove padding; use animations (these will run simultaneously with)
+      // the fading out of the toolbar as its gets removed).
+      var posProp = this._getPositionProperties(this.$el);
+      setTimeout(function () {
+        // Re-enable width animations (padding changes affect width too!).
+        self.$el.removeClass('quickedit-animate-disable-width');
+
+        // Unpad the editable.
+        self.$el
+          .css({
+            'position': 'relative',
+            'top': posProp.top + 5 + 'px',
+            'left': posProp.left + 5 + 'px',
+            'padding-top': posProp['padding-top'] - 5 + 'px',
+            'padding-left': posProp['padding-left'] - 5 + 'px',
+            'padding-right': posProp['padding-right'] - 5 + 'px',
+            'padding-bottom': posProp['padding-bottom'] - 5 + 'px',
+            'margin-bottom': posProp['margin-bottom'] + 10 + 'px'
+          });
+      }, 0);
+      // Remove the marker that indicates that this field has padding. This is
+      // done outside the timed out function above so that we don't get numerous
+      // queued functions that will remove padding before the data marker has
+      // been removed.
+      this.$el.removeData('quickedit-padded');
+    },
+
+    /**
+     * Gets the top and left properties of an element.
+     *
+     * Convert extraneous values and information into numbers ready for
+     * subtraction.
+     *
+     * @param {jQuery} $e
+     *   The element to get position properties from.
+     *
+     * @return {object}
+     *   An object containing css values for the needed properties.
+     */
+    _getPositionProperties: function ($e) {
+      var p;
+      var r = {};
+      var props = [
+        'top', 'left', 'bottom', 'right',
+        'padding-top', 'padding-left', 'padding-right', 'padding-bottom',
+        'margin-bottom'
+      ];
+
+      var propCount = props.length;
+      for (var i = 0; i < propCount; i++) {
+        p = props[i];
+        r[p] = parseInt(this._replaceBlankPosition($e.css(p)), 10);
+      }
+      return r;
+    },
+
+    /**
+     * Replaces blank or 'auto' CSS `position: <value>` values with "0px".
+     *
+     * @param {string} [pos]
+     *   The value for a CSS position declaration.
+     *
+     * @return {string}
+     *   A CSS value that is valid for `position`.
+     */
+    _replaceBlankPosition: function (pos) {
+      if (pos === 'auto' || !pos) {
+        pos = '0px';
+      }
+      return pos;
+    }
+
+  });
+
+})(jQuery, Backbone, Drupal);
diff --git a/core/themes/stable/js/quickedit/views/FieldToolbarView.js b/core/themes/stable/js/quickedit/views/FieldToolbarView.js
new file mode 100644
index 0000000..232f702
--- /dev/null
+++ b/core/themes/stable/js/quickedit/views/FieldToolbarView.js
@@ -0,0 +1,227 @@
+/**
+ * @file
+ * A Backbone View that provides an interactive toolbar (1 per in-place editor).
+ */
+
+(function ($, _, Backbone, Drupal) {
+
+  "use strict";
+
+  Drupal.quickedit.FieldToolbarView = Backbone.View.extend(/** @lends Drupal.quickedit.FieldToolbarView# */{
+
+    /**
+     * The edited element, as indicated by EditorView.getEditedElement.
+     *
+     * @type {jQuery}
+     */
+    $editedElement: null,
+
+    /**
+     * A reference to the in-place editor.
+     *
+     * @type {Drupal.quickedit.EditorView}
+     */
+    editorView: null,
+
+    /**
+     * @type {string}
+     */
+    _id: null,
+
+    /**
+     * @constructs
+     *
+     * @augments Backbone.View
+     *
+     * @param {object} options
+     *   Options object to construct the field toolbar.
+     * @param {jQuery} options.$editedElement
+     *   The element being edited.
+     * @param {Drupal.quickedit.EditorView} options.editorView
+     *   The EditorView the toolbar belongs to.
+     */
+    initialize: function (options) {
+      this.$editedElement = options.$editedElement;
+      this.editorView = options.editorView;
+
+      /**
+       * @type {jQuery}
+       */
+      this.$root = this.$el;
+
+      // Generate a DOM-compatible ID for the form container DOM element.
+      this._id = 'quickedit-toolbar-for-' + this.model.id.replace(/[\/\[\]]/g, '_');
+
+      this.listenTo(this.model, 'change:state', this.stateChange);
+    },
+
+    /**
+     * @inheritdoc
+     *
+     * @return {Drupal.quickedit.FieldToolbarView}
+     *   The current FieldToolbarView.
+     */
+    render: function () {
+      // Render toolbar and set it as the view's element.
+      this.setElement($(Drupal.theme('quickeditFieldToolbar', {
+        id: this._id
+      })));
+
+      // Attach to the field toolbar $root element in the entity toolbar.
+      this.$el.prependTo(this.$root);
+
+      return this;
+    },
+
+    /**
+     * Determines the actions to take given a change of state.
+     *
+     * @param {Drupal.quickedit.FieldModel} model
+     *   The quickedit FieldModel
+     * @param {string} state
+     *   The state of the associated field. One of
+     *   {@link Drupal.quickedit.FieldModel.states}.
+     */
+    stateChange: function (model, state) {
+      var from = model.previous('state');
+      var to = state;
+      switch (to) {
+        case 'inactive':
+          break;
+
+        case 'candidate':
+          // Remove the view's existing element if we went to the 'activating'
+          // state or later, because it will be recreated. Not doing this would
+          // result in memory leaks.
+          if (from !== 'inactive' && from !== 'highlighted') {
+            this.$el.remove();
+            this.setElement();
+          }
+          break;
+
+        case 'highlighted':
+          break;
+
+        case 'activating':
+          this.render();
+
+          if (this.editorView.getQuickEditUISettings().fullWidthToolbar) {
+            this.$el.addClass('quickedit-toolbar-fullwidth');
+          }
+
+          if (this.editorView.getQuickEditUISettings().unifiedToolbar) {
+            this.insertWYSIWYGToolGroups();
+          }
+          break;
+
+        case 'active':
+          break;
+
+        case 'changed':
+          break;
+
+        case 'saving':
+          break;
+
+        case 'saved':
+          break;
+
+        case 'invalid':
+          break;
+      }
+    },
+
+    /**
+     * Insert WYSIWYG markup into the associated toolbar.
+     */
+    insertWYSIWYGToolGroups: function () {
+      this.$el
+        .append(Drupal.theme('quickeditToolgroup', {
+          id: this.getFloatedWysiwygToolgroupId(),
+          classes: ['wysiwyg-floated', 'quickedit-animate-slow', 'quickedit-animate-invisible', 'quickedit-animate-delay-veryfast'],
+          buttons: []
+        }))
+        .append(Drupal.theme('quickeditToolgroup', {
+          id: this.getMainWysiwygToolgroupId(),
+          classes: ['wysiwyg-main', 'quickedit-animate-slow', 'quickedit-animate-invisible', 'quickedit-animate-delay-veryfast'],
+          buttons: []
+        }));
+
+      // Animate the toolgroups into visibility.
+      this.show('wysiwyg-floated');
+      this.show('wysiwyg-main');
+    },
+
+    /**
+     * Retrieves the ID for this toolbar's container.
+     *
+     * Only used to make sane hovering behavior possible.
+     *
+     * @return {string}
+     *   A string that can be used as the ID for this toolbar's container.
+     */
+    getId: function () {
+      return 'quickedit-toolbar-for-' + this._id;
+    },
+
+    /**
+     * Retrieves the ID for this toolbar's floating WYSIWYG toolgroup.
+     *
+     * Used to provide an abstraction for any WYSIWYG editor to plug in.
+     *
+     * @return {string}
+     *   A string that can be used as the ID.
+     */
+    getFloatedWysiwygToolgroupId: function () {
+      return 'quickedit-wysiwyg-floated-toolgroup-for-' + this._id;
+    },
+
+    /**
+     * Retrieves the ID for this toolbar's main WYSIWYG toolgroup.
+     *
+     * Used to provide an abstraction for any WYSIWYG editor to plug in.
+     *
+     * @return {string}
+     *   A string that can be used as the ID.
+     */
+    getMainWysiwygToolgroupId: function () {
+      return 'quickedit-wysiwyg-main-toolgroup-for-' + this._id;
+    },
+
+    /**
+     * Finds a toolgroup.
+     *
+     * @param {string} toolgroup
+     *   A toolgroup name.
+     *
+     * @return {jQuery}
+     *   The toolgroup element.
+     */
+    _find: function (toolgroup) {
+      return this.$el.find('.quickedit-toolgroup.' + toolgroup);
+    },
+
+    /**
+     * Shows a toolgroup.
+     *
+     * @param {string} toolgroup
+     *   A toolgroup name.
+     */
+    show: function (toolgroup) {
+      var $group = this._find(toolgroup);
+      // Attach a transitionEnd event handler to the toolbar group so that
+      // update events can be triggered after the animations have ended.
+      $group.on(Drupal.quickedit.util.constants.transitionEnd, function (event) {
+        $group.off(Drupal.quickedit.util.constants.transitionEnd);
+      });
+      // The call to remove the class and start the animation must be started in
+      // the next animation frame or the event handler attached above won't be
+      // triggered.
+      window.setTimeout(function () {
+        $group.removeClass('quickedit-animate-invisible');
+      }, 0);
+    }
+
+  });
+
+})(jQuery, _, Backbone, Drupal);
diff --git a/core/themes/stable/js/responsive_image/responsive_image.ajax.js b/core/themes/stable/js/responsive_image/responsive_image.ajax.js
new file mode 100644
index 0000000..2c12b71
--- /dev/null
+++ b/core/themes/stable/js/responsive_image/responsive_image.ajax.js
@@ -0,0 +1,16 @@
+(function (Drupal) {
+
+  "use strict";
+
+  /**
+   * Call picturefill so newly added responsive images are processed.
+   */
+  Drupal.behaviors.responsiveImageAJAX = {
+    attach: function () {
+      if (window.picturefill) {
+        window.picturefill();
+      }
+    }
+  };
+
+})(Drupal);
diff --git a/core/themes/stable/js/simpletest/simpletest.js b/core/themes/stable/js/simpletest/simpletest.js
new file mode 100644
index 0000000..ed67ff5
--- /dev/null
+++ b/core/themes/stable/js/simpletest/simpletest.js
@@ -0,0 +1,130 @@
+/**
+ * @file
+ * Simpletest behaviors.
+ */
+
+(function ($) {
+
+  "use strict";
+
+  /**
+   * Collapses table rows followed by group rows on the test listing page.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attach collapse behavior on the test listing page.
+   */
+  Drupal.behaviors.simpleTestGroupCollapse = {
+    attach: function (context) {
+      $(context).find('.simpletest-group').once('simpletest-group-collapse').each(function () {
+        var $group = $(this);
+        var $image = $group.find('.simpletest-image');
+        $image
+          .html(drupalSettings.simpleTest.images[0])
+          .on('click', function () {
+            var $tests = $group.nextUntil('.simpletest-group');
+            var expand = !$group.hasClass('expanded');
+            $group.toggleClass('expanded', expand);
+            $tests.toggleClass('js-hide', !expand);
+            $image.html(drupalSettings.simpleTest.images[+expand]);
+          });
+      });
+    }
+  };
+
+  /**
+   * Toggles test checkboxes to match the group checkbox.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches behavior for selecting all tests in a group.
+   */
+  Drupal.behaviors.simpleTestSelectAll = {
+    attach: function (context) {
+      $(context).find('.simpletest-group').once('simpletest-group-select-all').each(function () {
+        var $group = $(this);
+        var $cell = $group.find('.simpletest-group-select-all');
+        var $groupCheckbox = $('<input type="checkbox" id="' + $cell.attr('id') + '-group-select-all" class="form-checkbox" />');
+        var $testCheckboxes = $group.nextUntil('.simpletest-group').find('input[type=checkbox]');
+        $cell.append($groupCheckbox);
+
+        // Toggle the test checkboxes when the group checkbox is toggled.
+        $groupCheckbox.on('change', function () {
+          var checked = $(this).prop('checked');
+          $testCheckboxes.prop('checked', checked);
+        });
+
+        // Update the group checkbox when a test checkbox is toggled.
+        function updateGroupCheckbox() {
+          var allChecked = true;
+          $testCheckboxes.each(function () {
+            if (!$(this).prop('checked')) {
+              allChecked = false;
+              return false;
+            }
+          });
+          $groupCheckbox.prop('checked', allChecked);
+        }
+
+        $testCheckboxes.on('change', updateGroupCheckbox);
+      });
+    }
+  };
+
+  /**
+   * Filters the test list table by a text input search string.
+   *
+   * Text search input: input.table-filter-text
+   * Target table:      input.table-filter-text[data-table]
+   * Source text:       .table-filter-text-source
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches the filter behavior the the text input element.
+   */
+  Drupal.behaviors.simpletestTableFilterByText = {
+    attach: function (context) {
+      var $input = $('input.table-filter-text').once('table-filter-text');
+      var $table = $($input.attr('data-table'));
+      var $rows;
+      var searched = false;
+
+      function filterTestList(e) {
+        var query = $(e.target).val().toLowerCase();
+
+        function showTestRow(index, row) {
+          var $row = $(row);
+          var $sources = $row.find('.table-filter-text-source');
+          var textMatch = $sources.text().toLowerCase().indexOf(query) !== -1;
+          $row.closest('tr').toggle(textMatch);
+        }
+
+        // Filter if the length of the query is at least 3 characters.
+        if (query.length >= 3) {
+          // Indicate that a search has been performed, and hide the
+          // "select all" checkbox.
+          searched = true;
+          $('#simpletest-form-table thead th.select-all input').hide();
+
+          $rows.each(showTestRow);
+        }
+        // Restore to the original state if any searching has occurred.
+        else if (searched) {
+          searched = false;
+          $('#simpletest-form-table thead th.select-all input').show();
+          // Restore all rows to their original display state.
+          $rows.css('display', '');
+        }
+      }
+
+      if ($table.length) {
+        $rows = $table.find('tbody tr');
+        $input.trigger('focus').on('keyup', Drupal.debounce(filterTestList, 200));
+      }
+    }
+  };
+
+})(jQuery);
diff --git a/core/themes/stable/js/statistics/statistics.js b/core/themes/stable/js/statistics/statistics.js
new file mode 100644
index 0000000..f892dd4
--- /dev/null
+++ b/core/themes/stable/js/statistics/statistics.js
@@ -0,0 +1,18 @@
+/**
+ * @file
+ * Statistics functionality.
+ */
+
+(function ($, Drupal, drupalSettings) {
+
+  "use strict";
+
+  $(document).ready(function () {
+    $.ajax({
+      type: "POST",
+      cache: false,
+      url: drupalSettings.statistics.url,
+      data: drupalSettings.statistics.data
+    });
+  });
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/themes/stable/js/system/system.date.js b/core/themes/stable/js/system/system.date.js
new file mode 100644
index 0000000..75a7869
--- /dev/null
+++ b/core/themes/stable/js/system/system.date.js
@@ -0,0 +1,57 @@
+/**
+ * @file
+ * Provides date format preview feature.
+ */
+
+(function ($, Drupal, drupalSettings) {
+
+  "use strict";
+
+  var dateFormats = drupalSettings.dateFormats;
+
+  /**
+   * Display the preview for date format entered.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attach behavior for previewing date formats on input elements.
+   */
+  Drupal.behaviors.dateFormat = {
+    attach: function (context) {
+      var $context = $(context);
+      var $source = $context.find('[data-drupal-date-formatter="source"]').once('dateFormat');
+      var $target = $context.find('[data-drupal-date-formatter="preview"]').once('dateFormat');
+      var $preview = $target.find('em');
+
+      // All elements have to exist.
+      if (!$source.length || !$target.length) {
+        return;
+      }
+
+      /**
+       * Event handler that replaces date characters with value.
+       *
+       * @param {jQuery.Event} e
+       *   The jQuery event triggered.
+       */
+      function dateFormatHandler(e) {
+        var baseValue = $(e.target).val() || '';
+        var dateString = baseValue.replace(/\\?(.?)/gi, function (key, value) {
+          return dateFormats[key] ? dateFormats[key] : value;
+        });
+
+        $preview.html(dateString);
+        $target.toggleClass('js-hide', !dateString.length);
+      }
+
+      /**
+       * On given event triggers the date character replacement.
+       */
+      $source.on('keyup.dateFormat change.dateFormat input.dateFormat', dateFormatHandler)
+        // Initialize preview.
+        .trigger('keyup');
+    }
+  };
+
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/themes/stable/js/system/system.js b/core/themes/stable/js/system/system.js
new file mode 100644
index 0000000..cbfe4ea
--- /dev/null
+++ b/core/themes/stable/js/system/system.js
@@ -0,0 +1,81 @@
+/**
+ * @file
+ * System behaviors.
+ */
+
+(function ($, Drupal, drupalSettings) {
+
+  "use strict";
+
+  // Cache IDs in an array for ease of use.
+  var ids = [];
+
+  /**
+   * Attaches field copy behavior from input fields to other input fields.
+   *
+   * When a field is filled out, apply its value to other fields that will
+   * likely use the same value. In the installer this is used to populate the
+   * administrator email address with the same value as the site email address.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches the field copy behavior to an input field.
+   */
+  Drupal.behaviors.copyFieldValue = {
+    attach: function (context) {
+      // List of fields IDs on which to bind the event listener.
+      // Create an array of IDs to use with jQuery.
+      for (var sourceId in drupalSettings.copyFieldValue) {
+        if (drupalSettings.copyFieldValue.hasOwnProperty(sourceId)) {
+          ids.push(sourceId);
+        }
+      }
+      if (ids.length) {
+        // Listen to value:copy events on all dependent fields.
+        // We have to use body and not document because of the way jQuery events
+        // bubble up the DOM tree.
+        $('body').once('copy-field-values').on('value:copy', this.valueTargetCopyHandler);
+        // Listen on all source elements.
+        $('#' + ids.join(', #')).once('copy-field-values').on('blur', this.valueSourceBlurHandler);
+      }
+    },
+    detach: function (context, settings, trigger) {
+      if (trigger === 'unload' && ids.length) {
+        $('body').removeOnce('copy-field-values').off('value:copy');
+        $('#' + ids.join(', #')).removeOnce('copy-field-values').off('blur');
+      }
+    },
+
+    /**
+     * Event handler that fill the target element with the specified value.
+     *
+     * @param {jQuery.Event} e
+     *   Event object.
+     * @param {string} value
+     *   Custom value from jQuery trigger.
+     */
+    valueTargetCopyHandler: function (e, value) {
+      var $target = $(e.target);
+      if ($target.val() === '') {
+        $target.val(value);
+      }
+    },
+
+    /**
+     * Handler for a Blur event on a source field.
+     *
+     * This event handler will trigger a 'value:copy' event on all dependent
+     * fields.
+     *
+     * @param {jQuery.Event} e
+     *   The event triggered.
+     */
+    valueSourceBlurHandler: function (e) {
+      var value = $(e.target).val();
+      var targetIds = drupalSettings.copyFieldValue[e.target.id];
+      $('#' + targetIds.join(', #')).trigger('value:copy', value);
+    }
+  };
+
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/themes/stable/js/system/system.modules.js b/core/themes/stable/js/system/system.modules.js
new file mode 100644
index 0000000..f9c08e4
--- /dev/null
+++ b/core/themes/stable/js/system/system.modules.js
@@ -0,0 +1,103 @@
+/**
+ * @file
+ * Module page behaviors.
+ */
+
+(function ($, Drupal, debounce) {
+
+  "use strict";
+
+  /**
+   * Filters the module list table by a text input search string.
+   *
+   * Additionally accounts for multiple tables being wrapped in "package" details
+   * elements.
+   *
+   * Text search input: input.table-filter-text
+   * Target table:      input.table-filter-text[data-table]
+   * Source text:       .table-filter-text-source, .module-name, .module-description
+   *
+   * @type {Drupal~behavior}
+   */
+  Drupal.behaviors.tableFilterByText = {
+    attach: function (context, settings) {
+      var $input = $('input.table-filter-text').once('table-filter-text');
+      var $table = $($input.attr('data-table'));
+      var $rowsAndDetails;
+      var $rows;
+      var $details;
+      var searching = false;
+
+      function hidePackageDetails(index, element) {
+        var $packDetails = $(element);
+        var $visibleRows = $packDetails.find('tbody tr:visible');
+        $packDetails.toggle($visibleRows.length > 0);
+      }
+
+      function filterModuleList(e) {
+        var query = $(e.target).val();
+        // Case insensitive expression to find query at the beginning of a word.
+        var re = new RegExp('\\b' + query, 'i');
+
+        function showModuleRow(index, row) {
+          var $row = $(row);
+          var $sources = $row.find('.table-filter-text-source, .module-name, .module-description');
+          var textMatch = $sources.text().search(re) !== -1;
+          $row.closest('tr').toggle(textMatch);
+        }
+        // Search over all rows and packages.
+        $rowsAndDetails.show();
+
+        // Filter if the length of the query is at least 2 characters.
+        if (query.length >= 2) {
+          searching = true;
+          $rows.each(showModuleRow);
+
+          // Note that we first open all <details> to be able to use ':visible'.
+          // Mark the <details> elements that were closed before filtering, so
+          // they can be reclosed when filtering is removed.
+          $details.not('[open]').attr('data-drupal-system-state', 'forced-open');
+
+          // Hide the package <details> if they don't have any visible rows.
+          // Note that we first show() all <details> to be able to use ':visible'.
+          $details.attr('open', true).each(hidePackageDetails);
+
+          Drupal.announce(
+            Drupal.t(
+              '!modules modules are available in the modified list.',
+              {'!modules': $rowsAndDetails.find('tbody tr:visible').length}
+            )
+          );
+        }
+        else if (searching) {
+          searching = false;
+          $rowsAndDetails.show();
+          // Return <details> elements that had been closed before filtering
+          // to a closed state.
+          $details.filter('[data-drupal-system-state="forced-open"]')
+            .removeAttr('data-drupal-system-state')
+            .attr('open', false);
+        }
+      }
+
+      function preventEnterKey(event) {
+        if (event.which === 13) {
+          event.preventDefault();
+          event.stopPropagation();
+        }
+      }
+
+      if ($table.length) {
+        $rowsAndDetails = $table.find('tr, details');
+        $rows = $table.find('tbody tr');
+        $details = $rowsAndDetails.filter('.package-listing');
+
+        $input.on({
+          keyup: debounce(filterModuleList, 200),
+          keydown: preventEnterKey
+        });
+      }
+    }
+  };
+
+}(jQuery, Drupal, Drupal.debounce));
diff --git a/core/themes/stable/js/taxonomy/taxonomy.js b/core/themes/stable/js/taxonomy/taxonomy.js
new file mode 100644
index 0000000..58c8b43
--- /dev/null
+++ b/core/themes/stable/js/taxonomy/taxonomy.js
@@ -0,0 +1,56 @@
+/**
+ * @file
+ * Taxonomy behaviors.
+ */
+
+(function ($) {
+
+  "use strict";
+
+  /**
+   * Move a block in the blocks table from one region to another.
+   *
+   * This behavior is dependent on the tableDrag behavior, since it uses the
+   * objects initialized in that behavior to update the row.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches the drag behavior to a applicable table element.
+   */
+  Drupal.behaviors.termDrag = {
+    attach: function (context, settings) {
+      var backStep = settings.taxonomy.backStep;
+      var forwardStep = settings.taxonomy.forwardStep;
+      // Get the blocks tableDrag object.
+      var tableDrag = Drupal.tableDrag.taxonomy;
+      var $table = $('#taxonomy');
+      var rows = $table.find('tr').length;
+
+      // When a row is swapped, keep previous and next page classes set.
+      tableDrag.row.prototype.onSwap = function (swappedRow) {
+        $table.find('tr.taxonomy-term-preview').removeClass('taxonomy-term-preview');
+        $table.find('tr.taxonomy-term-divider-top').removeClass('taxonomy-term-divider-top');
+        $table.find('tr.taxonomy-term-divider-bottom').removeClass('taxonomy-term-divider-bottom');
+
+        var tableBody = $table[0].tBodies[0];
+        if (backStep) {
+          for (var n = 0; n < backStep; n++) {
+            $(tableBody.rows[n]).addClass('taxonomy-term-preview');
+          }
+          $(tableBody.rows[backStep - 1]).addClass('taxonomy-term-divider-top');
+          $(tableBody.rows[backStep]).addClass('taxonomy-term-divider-bottom');
+        }
+
+        if (forwardStep) {
+          for (var k = rows - forwardStep - 1; k < rows - 1; k++) {
+            $(tableBody.rows[k]).addClass('taxonomy-term-preview');
+          }
+          $(tableBody.rows[rows - forwardStep - 2]).addClass('taxonomy-term-divider-top');
+          $(tableBody.rows[rows - forwardStep - 1]).addClass('taxonomy-term-divider-bottom');
+        }
+      };
+    }
+  };
+
+})(jQuery);
diff --git a/core/themes/stable/js/text/text.js b/core/themes/stable/js/text/text.js
new file mode 100644
index 0000000..54da56c
--- /dev/null
+++ b/core/themes/stable/js/text/text.js
@@ -0,0 +1,61 @@
+/**
+ * @file
+ * Text behaviors.
+ */
+
+(function ($) {
+
+  "use strict";
+
+  /**
+   * Auto-hide summary textarea if empty and show hide and unhide links.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches auto-hide behavior on `text-summary` events.
+   */
+  Drupal.behaviors.textSummary = {
+    attach: function (context, settings) {
+      $(context).find('.js-text-summary').once('text-summary').each(function () {
+        var $widget = $(this).closest('.js-text-format-wrapper');
+
+        var $summary = $widget.find('.js-text-summary-wrapper');
+        var $summaryLabel = $summary.find('label').eq(0);
+        var $full = $widget.find('.js-text-full').closest('.js-form-item');
+        var $fullLabel = $full.find('label').eq(0);
+
+        // Create a placeholder label when the field cardinality is greater
+        // than 1.
+        if ($fullLabel.length === 0) {
+          $fullLabel = $('<label></label>').prependTo($full);
+        }
+
+        // Set up the edit/hide summary link.
+        var $link = $('<span class="field-edit-link"> (<button type="button" class="link link-edit-summary">' + Drupal.t('Hide summary') + '</button>)</span>');
+        var $button = $link.find('button');
+        var toggleClick = true;
+        $link.on('click', function (e) {
+          if (toggleClick) {
+            $summary.hide();
+            $button.html(Drupal.t('Edit summary'));
+            $link.appendTo($fullLabel);
+          }
+          else {
+            $summary.show();
+            $button.html(Drupal.t('Hide summary'));
+            $link.appendTo($summaryLabel);
+          }
+          e.preventDefault();
+          toggleClick = !toggleClick;
+        }).appendTo($summaryLabel);
+
+        // If no summary is set, hide the summary field.
+        if ($widget.find('.js-text-summary').val() === '') {
+          $link.trigger('click');
+        }
+      });
+    }
+  };
+
+})(jQuery);
diff --git a/core/themes/stable/js/toolbar/escapeAdmin.js b/core/themes/stable/js/toolbar/escapeAdmin.js
new file mode 100644
index 0000000..26b8a26
--- /dev/null
+++ b/core/themes/stable/js/toolbar/escapeAdmin.js
@@ -0,0 +1,48 @@
+/**
+ * @file
+ * Replaces the home link in toolbar with a back to site link.
+ */
+
+(function ($, Drupal, drupalSettings) {
+
+  "use strict";
+
+  var pathInfo = drupalSettings.path;
+  var escapeAdminPath = sessionStorage.getItem('escapeAdminPath');
+  var windowLocation = window.location;
+
+  // Saves the last non-administrative page in the browser to be able to link
+  // back to it when browsing administrative pages. If there is a destination
+  // parameter there is not need to save the current path because the page is
+  // loaded within an existing "workflow".
+  if (!pathInfo.currentPathIsAdmin && !/destination=/.test(windowLocation.search)) {
+    sessionStorage.setItem('escapeAdminPath', windowLocation);
+  }
+
+  /**
+   * Replaces the "Home" link with "Back to site" link.
+   *
+   * Back to site link points to the last non-administrative page the user
+   * visited within the same browser tab.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches the replacement functionality to the toolbar-escape-admin element.
+   */
+  Drupal.behaviors.escapeAdmin = {
+    attach: function () {
+      var $toolbarEscape = $('[data-toolbar-escape-admin]').once('escapeAdmin');
+      if ($toolbarEscape.length && pathInfo.currentPathIsAdmin) {
+        if (escapeAdminPath !== null) {
+          $toolbarEscape.attr('href', escapeAdminPath);
+        }
+        else {
+          $toolbarEscape.text(Drupal.t('Home'));
+        }
+        $toolbarEscape.closest('.toolbar-tab').removeClass('hidden');
+      }
+    }
+  };
+
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/themes/stable/js/toolbar/models/MenuModel.js b/core/themes/stable/js/toolbar/models/MenuModel.js
new file mode 100644
index 0000000..5f302bb
--- /dev/null
+++ b/core/themes/stable/js/toolbar/models/MenuModel.js
@@ -0,0 +1,33 @@
+/**
+ * @file
+ * A Backbone Model for collapsible menus.
+ */
+
+(function (Backbone, Drupal) {
+
+  "use strict";
+
+  /**
+   * Backbone Model for collapsible menus.
+   *
+   * @constructor
+   *
+   * @augments Backbone.Model
+   */
+  Drupal.toolbar.MenuModel = Backbone.Model.extend(/** @lends Drupal.toolbar.MenuModel# */{
+
+    /**
+     * @type {object}
+     *
+     * @prop {object} subtrees
+     */
+    defaults: /** @lends Drupal.toolbar.MenuModel# */{
+
+      /**
+       * @type {object}
+       */
+      subtrees: {}
+    }
+  });
+
+}(Backbone, Drupal));
diff --git a/core/themes/stable/js/toolbar/models/ToolbarModel.js b/core/themes/stable/js/toolbar/models/ToolbarModel.js
new file mode 100644
index 0000000..357692c
--- /dev/null
+++ b/core/themes/stable/js/toolbar/models/ToolbarModel.js
@@ -0,0 +1,157 @@
+/**
+ * @file
+ * A Backbone Model for the toolbar.
+ */
+
+(function (Backbone, Drupal) {
+
+  "use strict";
+
+  /**
+   * Backbone model for the toolbar.
+   *
+   * @constructor
+   *
+   * @augments Backbone.Model
+   */
+  Drupal.toolbar.ToolbarModel = Backbone.Model.extend(/** @lends Drupal.toolbar.ToolbarModel# */{
+
+    /**
+     * @type {object}
+     *
+     * @prop activeTab
+     * @prop activeTray
+     * @prop isOriented
+     * @prop isFixed
+     * @prop areSubtreesLoaded
+     * @prop isViewportOverflowConstrained
+     * @prop orientation
+     * @prop locked
+     * @prop isTrayToggleVisible
+     * @prop height
+     * @prop offsets
+     */
+    defaults: /** @lends Drupal.toolbar.ToolbarModel# */{
+
+      /**
+       * The active toolbar tab. All other tabs should be inactive under
+       * normal circumstances. It will remain active across page loads. The
+       * active item is stored as an ID selector e.g. '#toolbar-item--1'.
+       *
+       * @type {string}
+       */
+      activeTab: null,
+
+      /**
+       * Represents whether a tray is open or not. Stored as an ID selector e.g.
+       * '#toolbar-item--1-tray'.
+       *
+       * @type {string}
+       */
+      activeTray: null,
+
+      /**
+       * Indicates whether the toolbar is displayed in an oriented fashion,
+       * either horizontal or vertical.
+       *
+       * @type {bool}
+       */
+      isOriented: false,
+
+      /**
+       * Indicates whether the toolbar is positioned absolute (false) or fixed
+       * (true).
+       *
+       * @type {bool}
+       */
+      isFixed: false,
+
+      /**
+       * Menu subtrees are loaded through an AJAX request only when the Toolbar
+       * is set to a vertical orientation.
+       *
+       * @type {bool}
+       */
+      areSubtreesLoaded: false,
+
+      /**
+       * If the viewport overflow becomes constrained, isFixed must be true so
+       * that elements in the trays aren't lost off-screen and impossible to
+       * get to.
+       *
+       * @type {bool}
+       */
+      isViewportOverflowConstrained: false,
+
+      /**
+       * The orientation of the active tray.
+       *
+       * @type {string}
+       */
+      orientation: 'vertical',
+
+      /**
+       * A tray is locked if a user toggled it to vertical. Otherwise a tray
+       * will switch between vertical and horizontal orientation based on the
+       * configured breakpoints. The locked state will be maintained across page
+       * loads.
+       *
+       * @type {bool}
+       */
+      locked: false,
+
+      /**
+       * Indicates whether the tray orientation toggle is visible.
+       *
+       * @type {bool}
+       */
+      isTrayToggleVisible: false,
+
+      /**
+       * The height of the toolbar.
+       *
+       * @type {number}
+       */
+      height: null,
+
+      /**
+       * The current viewport offsets determined by {@link Drupal.displace}. The
+       * offsets suggest how a module might position is components relative to
+       * the viewport.
+       *
+       * @type {object}
+       *
+       * @prop {number} top
+       * @prop {number} right
+       * @prop {number} bottom
+       * @prop {number} left
+       */
+      offsets: {
+        top: 0,
+        right: 0,
+        bottom: 0,
+        left: 0
+      }
+    },
+
+    /**
+     * @inheritdoc
+     *
+     * @param {object} attributes
+     *   Attributes for the toolbar.
+     * @param {object} options
+     *   Options for the toolbar.
+     *
+     * @return {string|undefined}
+     *   Returns an error message if validation failed.
+     */
+    validate: function (attributes, options) {
+      // Prevent the orientation being set to horizontal if it is locked, unless
+      // override has not been passed as an option.
+      if (attributes.orientation === 'horizontal' && this.get('locked') && !options.override) {
+        return Drupal.t('The toolbar cannot be set to a horizontal orientation when it is locked.');
+      }
+    }
+  });
+
+}(Backbone, Drupal));
diff --git a/core/themes/stable/js/toolbar/toolbar.js b/core/themes/stable/js/toolbar/toolbar.js
new file mode 100644
index 0000000..9a19205
--- /dev/null
+++ b/core/themes/stable/js/toolbar/toolbar.js
@@ -0,0 +1,257 @@
+/**
+ * @file
+ * Defines the behavior of the Drupal administration toolbar.
+ */
+
+(function ($, Drupal, drupalSettings) {
+
+  "use strict";
+
+  // Merge run-time settings with the defaults.
+  var options = $.extend(
+    {
+      breakpoints: {
+        'toolbar.narrow': '',
+        'toolbar.standard': '',
+        'toolbar.wide': ''
+      }
+    },
+    drupalSettings.toolbar,
+    // Merge strings on top of drupalSettings so that they are not mutable.
+    {
+      strings: {
+        horizontal: Drupal.t('Horizontal orientation'),
+        vertical: Drupal.t('Vertical orientation')
+      }
+    }
+  );
+
+  /**
+   * Registers tabs with the toolbar.
+   *
+   * The Drupal toolbar allows modules to register top-level tabs. These may
+   * point directly to a resource or toggle the visibility of a tray.
+   *
+   * Modules register tabs with hook_toolbar().
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches the toolbar rendering functionality to the toolbar element.
+   */
+  Drupal.behaviors.toolbar = {
+    attach: function (context) {
+      // Verify that the user agent understands media queries. Complex admin
+      // toolbar layouts require media query support.
+      if (!window.matchMedia('only screen').matches) {
+        return;
+      }
+      // Process the administrative toolbar.
+      $(context).find('#toolbar-administration').once('toolbar').each(function () {
+
+        // Establish the toolbar models and views.
+        var model = Drupal.toolbar.models.toolbarModel = new Drupal.toolbar.ToolbarModel({
+          locked: JSON.parse(localStorage.getItem('Drupal.toolbar.trayVerticalLocked')) || false,
+          activeTab: document.getElementById(JSON.parse(localStorage.getItem('Drupal.toolbar.activeTabID')))
+        });
+        Drupal.toolbar.views.toolbarVisualView = new Drupal.toolbar.ToolbarVisualView({
+          el: this,
+          model: model,
+          strings: options.strings
+        });
+        Drupal.toolbar.views.toolbarAuralView = new Drupal.toolbar.ToolbarAuralView({
+          el: this,
+          model: model,
+          strings: options.strings
+        });
+        Drupal.toolbar.views.bodyVisualView = new Drupal.toolbar.BodyVisualView({
+          el: this,
+          model: model
+        });
+
+        // Render collapsible menus.
+        var menuModel = Drupal.toolbar.models.menuModel = new Drupal.toolbar.MenuModel();
+        Drupal.toolbar.views.menuVisualView = new Drupal.toolbar.MenuVisualView({
+          el: $(this).find('.toolbar-menu-administration').get(0),
+          model: menuModel,
+          strings: options.strings
+        });
+
+        // Handle the resolution of Drupal.toolbar.setSubtrees.
+        // This is handled with a deferred so that the function may be invoked
+        // asynchronously.
+        Drupal.toolbar.setSubtrees.done(function (subtrees) {
+          menuModel.set('subtrees', subtrees);
+          var theme = drupalSettings.ajaxPageState.theme;
+          localStorage.setItem('Drupal.toolbar.subtrees.' + theme, JSON.stringify(subtrees));
+          // Indicate on the toolbarModel that subtrees are now loaded.
+          model.set('areSubtreesLoaded', true);
+        });
+
+        // Attach a listener to the configured media query breakpoints.
+        for (var label in options.breakpoints) {
+          if (options.breakpoints.hasOwnProperty(label)) {
+            var mq = options.breakpoints[label];
+            var mql = Drupal.toolbar.mql[label] = window.matchMedia(mq);
+            // Curry the model and the label of the media query breakpoint to
+            // the mediaQueryChangeHandler function.
+            mql.addListener(Drupal.toolbar.mediaQueryChangeHandler.bind(null, model, label));
+            // Fire the mediaQueryChangeHandler for each configured breakpoint
+            // so that they process once.
+            Drupal.toolbar.mediaQueryChangeHandler.call(null, model, label, mql);
+          }
+        }
+
+        // Trigger an initial attempt to load menu subitems. This first attempt
+        // is made after the media query handlers have had an opportunity to
+        // process. The toolbar starts in the vertical orientation by default,
+        // unless the viewport is wide enough to accommodate a horizontal
+        // orientation. Thus we give the Toolbar a chance to determine if it
+        // should be set to horizontal orientation before attempting to load
+        // menu subtrees.
+        Drupal.toolbar.views.toolbarVisualView.loadSubtrees();
+
+        $(document)
+          // Update the model when the viewport offset changes.
+          .on('drupalViewportOffsetChange.toolbar', function (event, offsets) {
+            model.set('offsets', offsets);
+          });
+
+        // Broadcast model changes to other modules.
+        model
+          .on('change:orientation', function (model, orientation) {
+            $(document).trigger('drupalToolbarOrientationChange', orientation);
+          })
+          .on('change:activeTab', function (model, tab) {
+            $(document).trigger('drupalToolbarTabChange', tab);
+          })
+          .on('change:activeTray', function (model, tray) {
+            $(document).trigger('drupalToolbarTrayChange', tray);
+          });
+
+        // If the toolbar's orientation is horizontal and no active tab is
+        // defined then show the tray of the first toolbar tab by default (but
+        // not the first 'Home' toolbar tab).
+        if (Drupal.toolbar.models.toolbarModel.get('orientation') === 'horizontal' && Drupal.toolbar.models.toolbarModel.get('activeTab') === null) {
+          Drupal.toolbar.models.toolbarModel.set({
+            activeTab: $('.toolbar-bar .toolbar-tab:not(.home-toolbar-tab) a').get(0)
+          });
+        }
+      });
+    }
+  };
+
+  /**
+   * Toolbar methods of Backbone objects.
+   *
+   * @namespace
+   */
+  Drupal.toolbar = {
+
+    /**
+     * A hash of View instances.
+     *
+     * @type {object.<string, Backbone.View>}
+     */
+    views: {},
+
+    /**
+     * A hash of Model instances.
+     *
+     * @type {object.<string, Backbone.Model>}
+     */
+    models: {},
+
+    /**
+     * A hash of MediaQueryList objects tracked by the toolbar.
+     *
+     * @type {object.<string, object>}
+     */
+    mql: {},
+
+    /**
+     * Accepts a list of subtree menu elements.
+     *
+     * A deferred object that is resolved by an inlined JavaScript callback.
+     *
+     * @type {jQuery.Deferred}
+     *
+     * @see toolbar_subtrees_jsonp().
+     */
+    setSubtrees: new $.Deferred(),
+
+    /**
+     * Respond to configured narrow media query changes.
+     *
+     * @param {Drupal.toolbar.ToolbarModel} model
+     *   A toolbar model
+     * @param {string} label
+     *   Media query label.
+     * @param {object} mql
+     *   A MediaQueryList object.
+     */
+    mediaQueryChangeHandler: function (model, label, mql) {
+      switch (label) {
+        case 'toolbar.narrow':
+          model.set({
+            isOriented: mql.matches,
+            isTrayToggleVisible: false
+          });
+          // If the toolbar doesn't have an explicit orientation yet, or if the
+          // narrow media query doesn't match then set the orientation to
+          // vertical.
+          if (!mql.matches || !model.get('orientation')) {
+            model.set({orientation: 'vertical'}, {validate: true});
+          }
+          break;
+
+        case 'toolbar.standard':
+          model.set({
+            isFixed: mql.matches
+          });
+          break;
+
+        case 'toolbar.wide':
+          model.set({
+            orientation: ((mql.matches) ? 'horizontal' : 'vertical')
+          }, {validate: true});
+          // The tray orientation toggle visibility does not need to be
+          // validated.
+          model.set({
+            isTrayToggleVisible: mql.matches
+          });
+          break;
+
+        default:
+          break;
+      }
+    }
+  };
+
+  /**
+   * A toggle is an interactive element often bound to a click handler.
+   *
+   * @return {string}
+   *   A string representing a DOM fragment.
+   */
+  Drupal.theme.toolbarOrientationToggle = function () {
+    return '<div class="toolbar-toggle-orientation"><div class="toolbar-lining">' +
+      '<button class="toolbar-icon" type="button"></button>' +
+      '</div></div>';
+  };
+
+  /**
+   * Ajax command to set the toolbar subtrees.
+   *
+   * @param {Drupal.Ajax} ajax
+   *   {@link Drupal.Ajax} object created by {@link Drupal.ajax}.
+   * @param {object} response
+   *   JSON response from the Ajax request.
+   * @param {number} [status]
+   *   XMLHttpRequest status.
+   */
+  Drupal.AjaxCommands.prototype.setToolbarSubtrees = function (ajax, response, status) {
+    Drupal.toolbar.setSubtrees.resolve(response.subtrees);
+  };
+
+}(jQuery, Drupal, drupalSettings));
diff --git a/core/themes/stable/js/toolbar/toolbar.menu.js b/core/themes/stable/js/toolbar/toolbar.menu.js
new file mode 100644
index 0000000..7d300e1
--- /dev/null
+++ b/core/themes/stable/js/toolbar/toolbar.menu.js
@@ -0,0 +1,197 @@
+/**
+ * @file
+ * Builds a nested accordion widget.
+ *
+ * Invoke on an HTML list element with the jQuery plugin pattern.
+ *
+ * @example
+ * $('.toolbar-menu').drupalToolbarMenu();
+ */
+
+(function ($, Drupal, drupalSettings) {
+
+  "use strict";
+
+  /**
+   * Store the open menu tray.
+   */
+  var activeItem = Drupal.url(drupalSettings.path.currentPath);
+
+  $.fn.drupalToolbarMenu = function () {
+
+    var ui = {
+      handleOpen: Drupal.t('Extend'),
+      handleClose: Drupal.t('Collapse')
+    };
+
+    /**
+     * Handle clicks from the disclosure button on an item with sub-items.
+     *
+     * @param {Object} event
+     *   A jQuery Event object.
+     */
+    function toggleClickHandler(event) {
+      var $toggle = $(event.target);
+      var $item = $toggle.closest('li');
+      // Toggle the list item.
+      toggleList($item);
+      // Close open sibling menus.
+      var $openItems = $item.siblings().filter('.open');
+      toggleList($openItems, false);
+    }
+
+    /**
+     * Handle clicks from a menu item link.
+     *
+     * @param {Object} event
+     *   A jQuery Event object.
+     */
+    function linkClickHandler(event) {
+      // If the toolbar is positioned fixed (and therefore hiding content
+      // underneath), then users expect clicks in the administration menu tray
+      // to take them to that destination but for the menu tray to be closed
+      // after clicking: otherwise the toolbar itself is obstructing the view
+      // of the destination they chose.
+      if (!Drupal.toolbar.models.toolbarModel.get('isFixed')) {
+        Drupal.toolbar.models.toolbarModel.set('activeTab', null);
+      }
+      // Stopping propagation to make sure that once a toolbar-box is clicked
+      // (the whitespace part), the page is not redirected anymore.
+      event.stopPropagation();
+    }
+
+    /**
+     * Toggle the open/close state of a list is a menu.
+     *
+     * @param {jQuery} $item
+     *   The li item to be toggled.
+     *
+     * @param {Boolean} switcher
+     *   A flag that forces toggleClass to add or a remove a class, rather than
+     *   simply toggling its presence.
+     */
+    function toggleList($item, switcher) {
+      var $toggle = $item.children('.toolbar-box').children('.toolbar-handle');
+      switcher = (typeof switcher !== 'undefined') ? switcher : !$item.hasClass('open');
+      // Toggle the item open state.
+      $item.toggleClass('open', switcher);
+      // Twist the toggle.
+      $toggle.toggleClass('open', switcher);
+      // Adjust the toggle text.
+      $toggle
+        .find('.action')
+        // Expand Structure, Collapse Structure.
+        .text((switcher) ? ui.handleClose : ui.handleOpen);
+    }
+
+    /**
+     * Add markup to the menu elements.
+     *
+     * Items with sub-elements have a list toggle attached to them. Menu item
+     * links and the corresponding list toggle are wrapped with in a div
+     * classed with .toolbar-box. The .toolbar-box div provides a positioning
+     * context for the item list toggle.
+     *
+     * @param {jQuery} $menu
+     *   The root of the menu to be initialized.
+     */
+    function initItems($menu) {
+      var options = {
+        class: 'toolbar-icon toolbar-handle',
+        action: ui.handleOpen,
+        text: ''
+      };
+      // Initialize items and their links.
+      $menu.find('li > a').wrap('<div class="toolbar-box">');
+      // Add a handle to each list item if it has a menu.
+      $menu.find('li').each(function (index, element) {
+        var $item = $(element);
+        if ($item.children('ul.toolbar-menu').length) {
+          var $box = $item.children('.toolbar-box');
+          options.text = Drupal.t('@label', {'@label': $box.find('a').text()});
+          $item.children('.toolbar-box')
+            .append(Drupal.theme('toolbarMenuItemToggle', options));
+        }
+      });
+    }
+
+    /**
+     * Adds a level class to each list based on its depth in the menu.
+     *
+     * This function is called recursively on each sub level of lists elements
+     * until the depth of the menu is exhausted.
+     *
+     * @param {jQuery} $lists
+     *   A jQuery object of ul elements.
+     *
+     * @param {number} level
+     *   The current level number to be assigned to the list elements.
+     */
+    function markListLevels($lists, level) {
+      level = (!level) ? 1 : level;
+      var $lis = $lists.children('li').addClass('level-' + level);
+      $lists = $lis.children('ul');
+      if ($lists.length) {
+        markListLevels($lists, level + 1);
+      }
+    }
+
+    /**
+     * On page load, open the active menu item.
+     *
+     * Marks the trail of the active link in the menu back to the root of the
+     * menu with .menu-item--active-trail.
+     *
+     * @param {jQuery} $menu
+     *   The root of the menu.
+     */
+    function openActiveItem($menu) {
+      var pathItem = $menu.find('a[href="' + location.pathname + '"]');
+      if (pathItem.length && !activeItem) {
+        activeItem = location.pathname;
+      }
+      if (activeItem) {
+        var $activeItem = $menu.find('a[href="' + activeItem + '"]').addClass('menu-item--active');
+        var $activeTrail = $activeItem.parentsUntil('.root', 'li').addClass('menu-item--active-trail');
+        toggleList($activeTrail, true);
+      }
+    }
+
+    // Return the jQuery object.
+    return this.each(function (selector) {
+      var $menu = $(this).once('toolbar-menu');
+      if ($menu.length) {
+        // Bind event handlers.
+        $menu
+          .on('click.toolbar', '.toolbar-box', toggleClickHandler)
+          .on('click.toolbar', '.toolbar-box a', linkClickHandler);
+
+        $menu.addClass('root');
+        initItems($menu);
+        markListLevels($menu);
+        // Restore previous and active states.
+        openActiveItem($menu);
+      }
+    });
+  };
+
+  /**
+   * A toggle is an interactive element often bound to a click handler.
+   *
+   * @param {object} options
+   *   Options for the button.
+   * @param {string} options.class
+   *   Class to set on the button.
+   * @param {string} options.action
+   *   Action for the button.
+   * @param {string} options.text
+   *   Used as label for the button.
+   *
+   * @return {string}
+   *   A string representing a DOM fragment.
+   */
+  Drupal.theme.toolbarMenuItemToggle = function (options) {
+    return '<button class="' + options['class'] + '"><span class="action">' + options.action + '</span><span class="label">' + options.text + '</span></button>';
+  };
+
+}(jQuery, Drupal, drupalSettings));
diff --git a/core/themes/stable/js/toolbar/views/BodyVisualView.js b/core/themes/stable/js/toolbar/views/BodyVisualView.js
new file mode 100644
index 0000000..7499646
--- /dev/null
+++ b/core/themes/stable/js/toolbar/views/BodyVisualView.js
@@ -0,0 +1,53 @@
+/**
+ * @file
+ * A Backbone view for the body element.
+ */
+
+(function ($, Drupal, Backbone) {
+
+  "use strict";
+
+  Drupal.toolbar.BodyVisualView = Backbone.View.extend(/** @lends Drupal.toolbar.BodyVisualView# */{
+
+    /**
+     * Adjusts the body element with the toolbar position and dimension changes.
+     *
+     * @constructs
+     *
+     * @augments Backbone.View
+     */
+    initialize: function () {
+      this.listenTo(this.model, 'change:orientation change:offsets change:activeTray change:isOriented change:isFixed change:isViewportOverflowConstrained', this.render);
+    },
+
+    /**
+     * @inheritdoc
+     */
+    render: function () {
+      var $body = $('body');
+      var orientation = this.model.get('orientation');
+      var isOriented = this.model.get('isOriented');
+      var isViewportOverflowConstrained = this.model.get('isViewportOverflowConstrained');
+
+      $body
+        // We are using JavaScript to control media-query handling for two
+        // reasons: (1) Using JavaScript let's us leverage the breakpoint
+        // configurations and (2) the CSS is really complex if we try to hide
+        // some styling from browsers that don't understand CSS media queries.
+        // If we drive the CSS from classes added through JavaScript,
+        // then the CSS becomes simpler and more robust.
+        .toggleClass('toolbar-vertical', (orientation === 'vertical'))
+        .toggleClass('toolbar-horizontal', (isOriented && orientation === 'horizontal'))
+        // When the toolbar is fixed, it will not scroll with page scrolling.
+        .toggleClass('toolbar-fixed', (isViewportOverflowConstrained || this.model.get('isFixed')))
+        // Toggle the toolbar-tray-open class on the body element. The class is
+        // applied when a toolbar tray is active. Padding might be applied to
+        // the body element to prevent the tray from overlapping content.
+        .toggleClass('toolbar-tray-open', !!this.model.get('activeTray'))
+        // Apply padding to the top of the body to offset the placement of the
+        // toolbar bar element.
+        .css('padding-top', this.model.get('offsets').top);
+    }
+  });
+
+}(jQuery, Drupal, Backbone));
diff --git a/core/themes/stable/js/toolbar/views/MenuVisualView.js b/core/themes/stable/js/toolbar/views/MenuVisualView.js
new file mode 100644
index 0000000..f75bb71
--- /dev/null
+++ b/core/themes/stable/js/toolbar/views/MenuVisualView.js
@@ -0,0 +1,46 @@
+/**
+ * @file
+ * A Backbone view for the collapsible menus.
+ */
+
+(function ($, Backbone, Drupal) {
+
+  "use strict";
+
+  Drupal.toolbar.MenuVisualView = Backbone.View.extend(/** @lends Drupal.toolbar.MenuVisualView# */{
+
+    /**
+     * Backbone View for collapsible menus.
+     *
+     * @constructs
+     *
+     * @augments Backbone.View
+     */
+    initialize: function () {
+      this.listenTo(this.model, 'change:subtrees', this.render);
+    },
+
+    /**
+     * @inheritdoc
+     */
+    render: function () {
+      var subtrees = this.model.get('subtrees');
+      // Add subtrees.
+      for (var id in subtrees) {
+        if (subtrees.hasOwnProperty(id)) {
+          this.$el
+            .find('#toolbar-link-' + id)
+            .once('toolbar-subtrees')
+            .after(subtrees[id]);
+        }
+      }
+      // Render the main menu as a nested, collapsible accordion.
+      if ('drupalToolbarMenu' in $.fn) {
+        this.$el
+          .children('.toolbar-menu')
+          .drupalToolbarMenu();
+      }
+    }
+  });
+
+}(jQuery, Backbone, Drupal));
diff --git a/core/themes/stable/js/toolbar/views/ToolbarAuralView.js b/core/themes/stable/js/toolbar/views/ToolbarAuralView.js
new file mode 100644
index 0000000..00f5aa5
--- /dev/null
+++ b/core/themes/stable/js/toolbar/views/ToolbarAuralView.js
@@ -0,0 +1,70 @@
+/**
+ * @file
+ * A Backbone view for the aural feedback of the toolbar.
+ */
+
+(function (Backbone, Drupal) {
+
+  "use strict";
+
+  Drupal.toolbar.ToolbarAuralView = Backbone.View.extend(/** @lends Drupal.toolbar.ToolbarAuralView# */{
+
+    /**
+     * Backbone view for the aural feedback of the toolbar.
+     *
+     * @constructs
+     *
+     * @augments Backbone.View
+     *
+     * @param {object} options
+     *   Options for the view.
+     * @param {object} options.strings
+     *   Various strings to use in the view.
+     */
+    initialize: function (options) {
+      this.strings = options.strings;
+
+      this.listenTo(this.model, 'change:orientation', this.onOrientationChange);
+      this.listenTo(this.model, 'change:activeTray', this.onActiveTrayChange);
+    },
+
+    /**
+     * Announces an orientation change.
+     *
+     * @param {Drupal.toolbar.ToolbarModel} model
+     *   The toolbar model in question.
+     * @param {string} orientation
+     *   The new value of the orientation attribute in the model.
+     */
+    onOrientationChange: function (model, orientation) {
+      Drupal.announce(Drupal.t('Tray orientation changed to @orientation.', {
+        '@orientation': orientation
+      }));
+    },
+
+    /**
+     * Announces a changed active tray.
+     *
+     * @param {Drupal.toolbar.ToolbarModel} model
+     *   The toolbar model in question.
+     * @param {HTMLElement} tray
+     *   The new value of the tray attribute in the model.
+     */
+    onActiveTrayChange: function (model, tray) {
+      var relevantTray = (tray === null) ? model.previous('activeTray') : tray;
+      var action = (tray === null) ? Drupal.t('closed') : Drupal.t('opened');
+      var trayNameElement = relevantTray.querySelector('.toolbar-tray-name');
+      var text;
+      if (trayNameElement !== null) {
+        text = Drupal.t('Tray "@tray" @action.', {
+          '@tray': trayNameElement.textContent, '@action': action
+        });
+      }
+      else {
+        text = Drupal.t('Tray @action.', {'@action': action});
+      }
+      Drupal.announce(text);
+    }
+  });
+
+}(Backbone, Drupal));
diff --git a/core/themes/stable/js/toolbar/views/ToolbarVisualView.js b/core/themes/stable/js/toolbar/views/ToolbarVisualView.js
new file mode 100644
index 0000000..be7ab06
--- /dev/null
+++ b/core/themes/stable/js/toolbar/views/ToolbarVisualView.js
@@ -0,0 +1,305 @@
+/**
+ * @file
+ * A Backbone view for the toolbar element. Listens to mouse & touch.
+ */
+
+(function ($, Drupal, drupalSettings, Backbone) {
+
+  "use strict";
+
+  Drupal.toolbar.ToolbarVisualView = Backbone.View.extend(/** @lends Drupal.toolbar.ToolbarVisualView# */{
+
+    /**
+     * Event map for the `ToolbarVisualView`.
+     *
+     * @return {object}
+     *   A map of events.
+     */
+    events: function () {
+      // Prevents delay and simulated mouse events.
+      var touchEndToClick = function (event) {
+        event.preventDefault();
+        event.target.click();
+      };
+
+      return {
+        'click .toolbar-bar .toolbar-tab': 'onTabClick',
+        'click .toolbar-toggle-orientation button': 'onOrientationToggleClick',
+        'touchend .toolbar-bar .toolbar-tab': touchEndToClick,
+        'touchend .toolbar-toggle-orientation button': touchEndToClick
+      };
+    },
+
+    /**
+     * Backbone view for the toolbar element. Listens to mouse & touch.
+     *
+     * @constructs
+     *
+     * @augments Backbone.View
+     *
+     * @param {object} options
+     *   Options for the view object.
+     * @param {object} options.strings
+     *   Various strings to use in the view.
+     */
+    initialize: function (options) {
+      this.strings = options.strings;
+
+      this.listenTo(this.model, 'change:activeTab change:orientation change:isOriented change:isTrayToggleVisible', this.render);
+      this.listenTo(this.model, 'change:mqMatches', this.onMediaQueryChange);
+      this.listenTo(this.model, 'change:offsets', this.adjustPlacement);
+
+      // Add the tray orientation toggles.
+      this.$el
+        .find('.toolbar-tray .toolbar-lining')
+        .append(Drupal.theme('toolbarOrientationToggle'));
+
+      // Trigger an activeTab change so that listening scripts can respond on
+      // page load. This will call render.
+      this.model.trigger('change:activeTab');
+    },
+
+    /**
+     * @inheritdoc
+     *
+     * @return {Drupal.toolbar.ToolbarVisualView}
+     *   The `ToolbarVisualView` instance.
+     */
+    render: function () {
+      this.updateTabs();
+      this.updateTrayOrientation();
+      this.updateBarAttributes();
+      // Load the subtrees if the orientation of the toolbar is changed to
+      // vertical. This condition responds to the case that the toolbar switches
+      // from horizontal to vertical orientation. The toolbar starts in a
+      // vertical orientation by default and then switches to horizontal during
+      // initialization if the media query conditions are met. Simply checking
+      // that the orientation is vertical here would result in the subtrees
+      // always being loaded, even when the toolbar initialization ultimately
+      // results in a horizontal orientation.
+      //
+      // @see Drupal.behaviors.toolbar.attach() where admin menu subtrees
+      // loading is invoked during initialization after media query conditions
+      // have been processed.
+      if (this.model.changed.orientation === 'vertical' || this.model.changed.activeTab) {
+        this.loadSubtrees();
+      }
+      // Trigger a recalculation of viewport displacing elements. Use setTimeout
+      // to ensure this recalculation happens after changes to visual elements
+      // have processed.
+      window.setTimeout(function () {
+        Drupal.displace(true);
+      }, 0);
+      return this;
+    },
+
+    /**
+     * Responds to a toolbar tab click.
+     *
+     * @param {jQuery.Event} event
+     *   The event triggered.
+     */
+    onTabClick: function (event) {
+      // If this tab has a tray associated with it, it is considered an
+      // activatable tab.
+      if (event.target.hasAttribute('data-toolbar-tray')) {
+        var activeTab = this.model.get('activeTab');
+        var clickedTab = event.target;
+
+        // Set the event target as the active item if it is not already.
+        this.model.set('activeTab', (!activeTab || clickedTab !== activeTab) ? clickedTab : null);
+
+        event.preventDefault();
+        event.stopPropagation();
+      }
+    },
+
+    /**
+     * Toggles the orientation of a toolbar tray.
+     *
+     * @param {jQuery.Event} event
+     *   The event triggered.
+     */
+    onOrientationToggleClick: function (event) {
+      var orientation = this.model.get('orientation');
+      // Determine the toggle-to orientation.
+      var antiOrientation = (orientation === 'vertical') ? 'horizontal' : 'vertical';
+      var locked = (antiOrientation === 'vertical') ? true : false;
+      // Remember the locked state.
+      if (locked) {
+        localStorage.setItem('Drupal.toolbar.trayVerticalLocked', 'true');
+      }
+      else {
+        localStorage.removeItem('Drupal.toolbar.trayVerticalLocked');
+      }
+      // Update the model.
+      this.model.set({
+        locked: locked,
+        orientation: antiOrientation
+      }, {
+        validate: true,
+        override: true
+      });
+
+      event.preventDefault();
+      event.stopPropagation();
+    },
+
+    /**
+     * Updates the display of the tabs: toggles a tab and the associated tray.
+     */
+    updateTabs: function () {
+      var $tab = $(this.model.get('activeTab'));
+      // Deactivate the previous tab.
+      $(this.model.previous('activeTab'))
+        .removeClass('is-active')
+        .prop('aria-pressed', false);
+      // Deactivate the previous tray.
+      $(this.model.previous('activeTray'))
+        .removeClass('is-active');
+
+      // Activate the selected tab.
+      if ($tab.length > 0) {
+        $tab
+          .addClass('is-active')
+          // Mark the tab as pressed.
+          .prop('aria-pressed', true);
+        var name = $tab.attr('data-toolbar-tray');
+        // Store the active tab name or remove the setting.
+        var id = $tab.get(0).id;
+        if (id) {
+          localStorage.setItem('Drupal.toolbar.activeTabID', JSON.stringify(id));
+        }
+        // Activate the associated tray.
+        var $tray = this.$el.find('[data-toolbar-tray="' + name + '"].toolbar-tray');
+        if ($tray.length) {
+          $tray.addClass('is-active');
+          this.model.set('activeTray', $tray.get(0));
+        }
+        else {
+          // There is no active tray.
+          this.model.set('activeTray', null);
+        }
+      }
+      else {
+        // There is no active tray.
+        this.model.set('activeTray', null);
+        localStorage.removeItem('Drupal.toolbar.activeTabID');
+      }
+    },
+
+    /**
+     * Update the attributes of the toolbar bar element.
+     */
+    updateBarAttributes: function () {
+      var isOriented = this.model.get('isOriented');
+      if (isOriented) {
+        this.$el.find('.toolbar-bar').attr('data-offset-top', '');
+      }
+      else {
+        this.$el.find('.toolbar-bar').removeAttr('data-offset-top');
+      }
+      // Toggle between a basic vertical view and a more sophisticated
+      // horizontal and vertical display of the toolbar bar and trays.
+      this.$el.toggleClass('toolbar-oriented', isOriented);
+    },
+
+    /**
+     * Updates the orientation of the active tray if necessary.
+     */
+    updateTrayOrientation: function () {
+      var orientation = this.model.get('orientation');
+      // The antiOrientation is used to render the view of action buttons like
+      // the tray orientation toggle.
+      var antiOrientation = (orientation === 'vertical') ? 'horizontal' : 'vertical';
+      // Update the orientation of the trays.
+      var $trays = this.$el.find('.toolbar-tray')
+        .removeClass('toolbar-tray-horizontal toolbar-tray-vertical')
+        .addClass('toolbar-tray-' + orientation);
+
+      // Update the tray orientation toggle button.
+      var iconClass = 'toolbar-icon-toggle-' + orientation;
+      var iconAntiClass = 'toolbar-icon-toggle-' + antiOrientation;
+      var $orientationToggle = this.$el.find('.toolbar-toggle-orientation')
+        .toggle(this.model.get('isTrayToggleVisible'));
+      $orientationToggle.find('button')
+        .val(antiOrientation)
+        .attr('title', this.strings[antiOrientation])
+        .text(this.strings[antiOrientation])
+        .removeClass(iconClass)
+        .addClass(iconAntiClass);
+
+      // Update data offset attributes for the trays.
+      var dir = document.documentElement.dir;
+      var edge = (dir === 'rtl') ? 'right' : 'left';
+      // Remove data-offset attributes from the trays so they can be refreshed.
+      $trays.removeAttr('data-offset-left data-offset-right data-offset-top');
+      // If an active vertical tray exists, mark it as an offset element.
+      $trays.filter('.toolbar-tray-vertical.is-active').attr('data-offset-' + edge, '');
+      // If an active horizontal tray exists, mark it as an offset element.
+      $trays.filter('.toolbar-tray-horizontal.is-active').attr('data-offset-top', '');
+    },
+
+    /**
+     * Sets the tops of the trays so that they align with the bottom of the bar.
+     */
+    adjustPlacement: function () {
+      var $trays = this.$el.find('.toolbar-tray');
+      if (!this.model.get('isOriented')) {
+        $trays.css('margin-top', 0);
+        $trays.removeClass('toolbar-tray-horizontal').addClass('toolbar-tray-vertical');
+      }
+      else {
+        // The toolbar container is invisible. Its placement is used to
+        // determine the container for the trays.
+        $trays.css('margin-top', this.$el.find('.toolbar-bar').outerHeight());
+      }
+    },
+
+    /**
+     * Calls the endpoint URI that builds an AJAX command with the rendered
+     * subtrees.
+     *
+     * The rendered admin menu subtrees HTML is cached on the client in
+     * localStorage until the cache of the admin menu subtrees on the server-
+     * side is invalidated. The subtreesHash is stored in localStorage as well
+     * and compared to the subtreesHash in drupalSettings to determine when the
+     * admin menu subtrees cache has been invalidated.
+     */
+    loadSubtrees: function () {
+      var $activeTab = $(this.model.get('activeTab'));
+      var orientation = this.model.get('orientation');
+      // Only load and render the admin menu subtrees if:
+      //   (1) They have not been loaded yet.
+      //   (2) The active tab is the administration menu tab, indicated by the
+      //       presence of the data-drupal-subtrees attribute.
+      //   (3) The orientation of the tray is vertical.
+      if (!this.model.get('areSubtreesLoaded') && typeof $activeTab.data('drupal-subtrees') !== 'undefined' && orientation === 'vertical') {
+        var subtreesHash = drupalSettings.toolbar.subtreesHash;
+        var theme = drupalSettings.ajaxPageState.theme;
+        var endpoint = Drupal.url('toolbar/subtrees/' + subtreesHash);
+        var cachedSubtreesHash = localStorage.getItem('Drupal.toolbar.subtreesHash.' + theme);
+        var cachedSubtrees = JSON.parse(localStorage.getItem('Drupal.toolbar.subtrees.' + theme));
+        var isVertical = this.model.get('orientation') === 'vertical';
+        // If we have the subtrees in localStorage and the subtree hash has not
+        // changed, then use the cached data.
+        if (isVertical && subtreesHash === cachedSubtreesHash && cachedSubtrees) {
+          Drupal.toolbar.setSubtrees.resolve(cachedSubtrees);
+        }
+        // Only make the call to get the subtrees if the orientation of the
+        // toolbar is vertical.
+        else if (isVertical) {
+          // Remove the cached menu information.
+          localStorage.removeItem('Drupal.toolbar.subtreesHash.' + theme);
+          localStorage.removeItem('Drupal.toolbar.subtrees.' + theme);
+          // The AJAX response's command will trigger the resolve method of the
+          // Drupal.toolbar.setSubtrees Promise.
+          Drupal.ajax({url: endpoint}).execute();
+          // Cache the hash for the subtrees locally.
+          localStorage.setItem('Drupal.toolbar.subtreesHash.' + theme, subtreesHash);
+        }
+      }
+    }
+  });
+
+}(jQuery, Drupal, drupalSettings, Backbone));
diff --git a/core/themes/stable/js/tour/tour.js b/core/themes/stable/js/tour/tour.js
new file mode 100644
index 0000000..c751329
--- /dev/null
+++ b/core/themes/stable/js/tour/tour.js
@@ -0,0 +1,270 @@
+/**
+ * @file
+ * Attaches behaviors for the Tour module's toolbar tab.
+ */
+
+(function ($, Backbone, Drupal, document) {
+
+  "use strict";
+
+  var queryString = decodeURI(window.location.search);
+
+  /**
+   * Attaches the tour's toolbar tab behavior.
+   *
+   * It uses the query string for:
+   * - tour: When ?tour=1 is present, the tour will start automatically after
+   *   the page has loaded.
+   * - tips: Pass ?tips=class in the url to filter the available tips to the
+   *   subset which match the given class.
+   *
+   * @example
+   * http://example.com/foo?tour=1&tips=bar
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attach tour functionality on `tour` events.
+   */
+  Drupal.behaviors.tour = {
+    attach: function (context) {
+      $('body').once('tour').each(function () {
+        var model = new Drupal.tour.models.StateModel();
+        new Drupal.tour.views.ToggleTourView({
+          el: $(context).find('#toolbar-tab-tour'),
+          model: model
+        });
+
+        model
+          // Allow other scripts to respond to tour events.
+          .on('change:isActive', function (model, isActive) {
+            $(document).trigger((isActive) ? 'drupalTourStarted' : 'drupalTourStopped');
+          })
+          // Initialization: check whether a tour is available on the current
+          // page.
+          .set('tour', $(context).find('ol#tour'));
+
+        // Start the tour immediately if toggled via query string.
+        if (/tour=?/i.test(queryString)) {
+          model.set('isActive', true);
+        }
+      });
+    }
+  };
+
+  /**
+   * @namespace
+   */
+  Drupal.tour = Drupal.tour || {
+
+    /**
+     * @namespace Drupal.tour.models
+     */
+    models: {},
+
+    /**
+     * @namespace Drupal.tour.views
+     */
+    views: {}
+  };
+
+  /**
+   * Backbone Model for tours.
+   *
+   * @constructor
+   *
+   * @augments Backbone.Model
+   */
+  Drupal.tour.models.StateModel = Backbone.Model.extend(/** @lends Drupal.tour.models.StateModel# */{
+
+    /**
+     * @type {object}
+     */
+    defaults: /** @lends Drupal.tour.models.StateModel# */{
+
+      /**
+       * Indicates whether the Drupal root window has a tour.
+       *
+       * @type {Array}
+       */
+      tour: [],
+
+      /**
+       * Indicates whether the tour is currently running.
+       *
+       * @type {bool}
+       */
+      isActive: false,
+
+      /**
+       * Indicates which tour is the active one (necessary to cleanly stop).
+       *
+       * @type {Array}
+       */
+      activeTour: []
+    }
+  });
+
+  Drupal.tour.views.ToggleTourView = Backbone.View.extend(/** @lends Drupal.tour.views.ToggleTourView# */{
+
+    /**
+     * @type {object}
+     */
+    events: {click: 'onClick'},
+
+    /**
+     * Handles edit mode toggle interactions.
+     *
+     * @constructs
+     *
+     * @augments Backbone.View
+     */
+    initialize: function () {
+      this.listenTo(this.model, 'change:tour change:isActive', this.render);
+      this.listenTo(this.model, 'change:isActive', this.toggleTour);
+    },
+
+    /**
+     * @inheritdoc
+     *
+     * @return {Drupal.tour.views.ToggleTourView}
+     *   The `ToggleTourView` view.
+     */
+    render: function () {
+      // Render the visibility.
+      this.$el.toggleClass('hidden', this._getTour().length === 0);
+      // Render the state.
+      var isActive = this.model.get('isActive');
+      this.$el.find('button')
+        .toggleClass('is-active', isActive)
+        .prop('aria-pressed', isActive);
+      return this;
+    },
+
+    /**
+     * Model change handler; starts or stops the tour.
+     */
+    toggleTour: function () {
+      if (this.model.get('isActive')) {
+        var $tour = this._getTour();
+        this._removeIrrelevantTourItems($tour, this._getDocument());
+        var that = this;
+        if ($tour.find('li').length) {
+          $tour.joyride({
+            autoStart: true,
+            postRideCallback: function () { that.model.set('isActive', false); },
+            // HTML segments for tip layout.
+            template: {
+              link: '<a href=\"#close\" class=\"joyride-close-tip\">&times;</a>',
+              button: '<a href=\"#\" class=\"button button--primary joyride-next-tip\"></a>'
+            }
+          });
+          this.model.set({isActive: true, activeTour: $tour});
+        }
+      }
+      else {
+        this.model.get('activeTour').joyride('destroy');
+        this.model.set({isActive: false, activeTour: []});
+      }
+    },
+
+    /**
+     * Toolbar tab click event handler; toggles isActive.
+     *
+     * @param {jQuery.Event} event
+     *   The click event.
+     */
+    onClick: function (event) {
+      this.model.set('isActive', !this.model.get('isActive'));
+      event.preventDefault();
+      event.stopPropagation();
+    },
+
+    /**
+     * Gets the tour.
+     *
+     * @return {jQuery}
+     *   A jQuery element pointing to a `<ol>` containing tour items.
+     */
+    _getTour: function () {
+      return this.model.get('tour');
+    },
+
+    /**
+     * Gets the relevant document as a jQuery element.
+     *
+     * @return {jQuery}
+     *   A jQuery element pointing to the document within which a tour would be
+     *   started given the current state.
+     */
+    _getDocument: function () {
+      return $(document);
+    },
+
+    /**
+     * Removes tour items for elements that don't have matching page elements.
+     *
+     * Or that are explicitly filtered out via the 'tips' query string.
+     *
+     * @example
+     * <caption>This will filter out tips that do not have a matching
+     * page element or don't have the "bar" class.</caption>
+     * http://example.com/foo?tips=bar
+     *
+     * @param {jQuery} $tour
+     *   A jQuery element pointing to a `<ol>` containing tour items.
+     * @param {jQuery} $document
+     *   A jQuery element pointing to the document within which the elements
+     *   should be sought.
+     *
+     * @see Drupal.tour.views.ToggleTourView#_getDocument
+     */
+    _removeIrrelevantTourItems: function ($tour, $document) {
+      var removals = false;
+      var tips = /tips=([^&]+)/.exec(queryString);
+      $tour
+        .find('li')
+        .each(function () {
+          var $this = $(this);
+          var itemId = $this.attr('data-id');
+          var itemClass = $this.attr('data-class');
+          // If the query parameter 'tips' is set, remove all tips that don't
+          // have the matching class.
+          if (tips && !$(this).hasClass(tips[1])) {
+            removals = true;
+            $this.remove();
+            return;
+          }
+          // Remove tip from the DOM if there is no corresponding page element.
+          if ((!itemId && !itemClass) ||
+            (itemId && $document.find('#' + itemId).length) ||
+            (itemClass && $document.find('.' + itemClass).length)) {
+            return;
+          }
+          removals = true;
+          $this.remove();
+        });
+
+      // If there were removals, we'll have to do some clean-up.
+      if (removals) {
+        var total = $tour.find('li').length;
+        if (!total) {
+          this.model.set({tour: []});
+        }
+
+        $tour
+          .find('li')
+          // Rebuild the progress data.
+          .each(function (index) {
+            var progress = Drupal.t('!tour_item of !total', {'!tour_item': index + 1, '!total': total});
+            $(this).find('.tour-progress').text(progress);
+          })
+          // Update the last item to have "End tour" as the button.
+          .eq(-1)
+          .attr('data-text', Drupal.t('End tour'));
+      }
+    }
+
+  });
+
+})(jQuery, Backbone, Drupal, document);
diff --git a/core/themes/stable/js/tracker/tracker-history.js b/core/themes/stable/js/tracker/tracker-history.js
new file mode 100644
index 0000000..fa6af55
--- /dev/null
+++ b/core/themes/stable/js/tracker/tracker-history.js
@@ -0,0 +1,122 @@
+/**
+ * Attaches behaviors for the Tracker module's History module integration.
+ *
+ * May only be loaded for authenticated users, with the History module enabled.
+ */
+(function ($, Drupal, window) {
+
+  "use strict";
+
+  /**
+   * Render "new" and "updated" node indicators, as well as "X new" replies links.
+   */
+  Drupal.behaviors.trackerHistory = {
+    attach: function (context) {
+      // Find all "new" comment indicator placeholders newer than 30 days ago that
+      // have not already been read after their last comment timestamp.
+      var nodeIDs = [];
+      var $nodeNewPlaceholders = $(context)
+        .find('[data-history-node-timestamp]')
+        .once('history')
+        .filter(function () {
+          var nodeTimestamp = parseInt(this.getAttribute('data-history-node-timestamp'), 10);
+          var nodeID = this.getAttribute('data-history-node-id');
+          if (Drupal.history.needsServerCheck(nodeID, nodeTimestamp)) {
+            nodeIDs.push(nodeID);
+            return true;
+          }
+          else {
+            return false;
+          }
+        });
+
+      // Find all "new" comment indicator placeholders newer than 30 days ago that
+      // have not already been read after their last comment timestamp.
+      var $newRepliesPlaceholders = $(context)
+        .find('[data-history-node-last-comment-timestamp]')
+        .once('history')
+        .filter(function () {
+          var lastCommentTimestamp = parseInt(this.getAttribute('data-history-node-last-comment-timestamp'), 10);
+          var nodeTimestamp = parseInt(this.previousSibling.previousSibling.getAttribute('data-history-node-timestamp'), 10);
+          // Discard placeholders that have zero comments.
+          if (lastCommentTimestamp === nodeTimestamp) {
+            return false;
+          }
+          var nodeID = this.previousSibling.previousSibling.getAttribute('data-history-node-id');
+          if (Drupal.history.needsServerCheck(nodeID, lastCommentTimestamp)) {
+            if (nodeIDs.indexOf(nodeID) === -1) {
+              nodeIDs.push(nodeID);
+            }
+            return true;
+          }
+          else {
+            return false;
+          }
+        });
+
+      if ($nodeNewPlaceholders.length === 0 && $newRepliesPlaceholders.length === 0) {
+        return;
+      }
+
+      // Fetch the node read timestamps from the server.
+      Drupal.history.fetchTimestamps(nodeIDs, function () {
+        processNodeNewIndicators($nodeNewPlaceholders);
+        processNewRepliesIndicators($newRepliesPlaceholders);
+      });
+    }
+  };
+
+  function processNodeNewIndicators($placeholders) {
+    var newNodeString = Drupal.t('new');
+    var updatedNodeString = Drupal.t('updated');
+
+    $placeholders.each(function (index, placeholder) {
+      var timestamp = parseInt(placeholder.getAttribute('data-history-node-timestamp'), 10);
+      var nodeID = placeholder.getAttribute('data-history-node-id');
+      var lastViewTimestamp = Drupal.history.getLastRead(nodeID);
+
+      if (timestamp > lastViewTimestamp) {
+        var message = (lastViewTimestamp === 0) ? newNodeString : updatedNodeString;
+        $(placeholder).append('<span class="marker">' + message + '</span>');
+      }
+    });
+  }
+
+  function processNewRepliesIndicators($placeholders) {
+    // Figure out which placeholders need the "x new" replies links.
+    var placeholdersToUpdate = {};
+    $placeholders.each(function (index, placeholder) {
+      var timestamp = parseInt(placeholder.getAttribute('data-history-node-last-comment-timestamp'), 10);
+      var nodeID = placeholder.previousSibling.previousSibling.getAttribute('data-history-node-id');
+      var lastViewTimestamp = Drupal.history.getLastRead(nodeID);
+
+      // Queue this placeholder's "X new" replies link to be downloaded from the
+      // server.
+      if (timestamp > lastViewTimestamp) {
+        placeholdersToUpdate[nodeID] = placeholder;
+      }
+    });
+
+    // Perform an AJAX request to retrieve node view timestamps.
+    var nodeIDs = Object.keys(placeholdersToUpdate);
+    if (nodeIDs.length === 0) {
+      return;
+    }
+    $.ajax({
+      url: Drupal.url('comments/render_new_comments_node_links'),
+      type: 'POST',
+      data: {'node_ids[]': nodeIDs},
+      dataType: 'json',
+      success: function (results) {
+        for (var nodeID in results) {
+          if (results.hasOwnProperty(nodeID) && placeholdersToUpdate.hasOwnProperty(nodeID)) {
+            var url = results[nodeID].first_new_comment_link;
+            var text = Drupal.formatPlural(results[nodeID].new_comment_count, '1 new', '@count new');
+            $(placeholdersToUpdate[nodeID]).append('<br /><a href="' + url + '">' + text + '</a>');
+          }
+        }
+      }
+    });
+  }
+
+})(jQuery, Drupal, window);
diff --git a/core/themes/stable/js/user/user.js b/core/themes/stable/js/user/user.js
new file mode 100644
index 0000000..7c829b1
--- /dev/null
+++ b/core/themes/stable/js/user/user.js
@@ -0,0 +1,217 @@
+/**
+ * @file
+ * User behaviors.
+ */
+
+(function ($, Drupal, drupalSettings) {
+
+  "use strict";
+
+  /**
+   * Attach handlers to evaluate the strength of any password fields and to
+   * check that its confirmation is correct.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches password strength indicator and other relevant validation to
+   *   password fields.
+   */
+  Drupal.behaviors.password = {
+    attach: function (context, settings) {
+      var $passwordInput = $(context).find('input.js-password-field').once('password');
+
+      if ($passwordInput.length) {
+        var translate = settings.password;
+
+        var $passwordInputParent = $passwordInput.parent();
+        var $passwordInputParentWrapper = $passwordInputParent.parent();
+        var $passwordSuggestions;
+
+        // Add identifying class to password element parent.
+        $passwordInputParent.addClass('password-parent');
+
+        // Add the password confirmation layer.
+        $passwordInputParentWrapper
+          .find('input.js-password-confirm')
+          .parent()
+          .append('<div aria-live="polite" aria-atomic="true" class="password-confirm js-password-confirm">' + translate.confirmTitle + ' <span></span></div>')
+          .addClass('confirm-parent');
+
+        var $confirmInput = $passwordInputParentWrapper.find('input.js-password-confirm');
+        var $confirmResult = $passwordInputParentWrapper.find('div.js-password-confirm');
+        var $confirmChild = $confirmResult.find('span');
+
+        // If the password strength indicator is enabled, add its markup.
+        if (settings.password.showStrengthIndicator) {
+          var passwordMeter = '<div class="password-strength"><div class="password-strength__meter"><div class="password-strength__indicator js-password-strength__indicator"></div></div><div aria-live="polite" aria-atomic="true" class="password-strength__title">' + translate.strengthTitle + ' <span class="password-strength__text js-password-strength__text"></span></div></div>';
+          $confirmInput.parent().after('<div class="password-suggestions description"></div>');
+          $passwordInputParent.append(passwordMeter);
+          $passwordSuggestions = $passwordInputParentWrapper.find('div.password-suggestions').hide();
+        }
+
+        // Check that password and confirmation inputs match.
+        var passwordCheckMatch = function (confirmInputVal) {
+          var success = $passwordInput.val() === confirmInputVal;
+          var confirmClass = success ? 'ok' : 'error';
+
+          // Fill in the success message and set the class accordingly.
+          $confirmChild.html(translate['confirm' + (success ? 'Success' : 'Failure')])
+            .removeClass('ok error').addClass(confirmClass);
+        };
+
+        // Check the password strength.
+        var passwordCheck = function () {
+          if (settings.password.showStrengthIndicator) {
+            // Evaluate the password strength.
+            var result = Drupal.evaluatePasswordStrength($passwordInput.val(), settings.password);
+
+            // Update the suggestions for how to improve the password.
+            if ($passwordSuggestions.html() !== result.message) {
+              $passwordSuggestions.html(result.message);
+            }
+
+            // Only show the description box if a weakness exists in the
+            // password.
+            $passwordSuggestions.toggle(result.strength !== 100);
+
+            // Adjust the length of the strength indicator.
+            $passwordInputParent.find('.js-password-strength__indicator')
+              .css('width', result.strength + '%')
+              .removeClass('is-weak is-fair is-good is-strong')
+              .addClass(result.indicatorClass);
+
+            // Update the strength indication text.
+            $passwordInputParent.find('.js-password-strength__text').html(result.indicatorText);
+          }
+
+          // Check the value in the confirm input and show results.
+          if ($confirmInput.val()) {
+            passwordCheckMatch($confirmInput.val());
+            $confirmResult.css({visibility: 'visible'});
+          }
+          else {
+            $confirmResult.css({visibility: 'hidden'});
+          }
+        };
+
+        // Monitor input events.
+        $passwordInput.on('input', passwordCheck);
+        $confirmInput.on('input', passwordCheck);
+      }
+    }
+  };
+
+  /**
+   * Evaluate the strength of a user's password.
+   *
+   * Returns the estimated strength and the relevant output message.
+   *
+   * @param {string} password
+   *   The password to evaluate.
+   * @param {object} translate
+   *   An object containing the text to display for each strength level.
+   *
+   * @return {object}
+   *   An object containing strength, message, indicatorText and indicatorClass.
+   */
+  Drupal.evaluatePasswordStrength = function (password, translate) {
+    password = password.trim();
+    var indicatorText;
+    var indicatorClass;
+    var weaknesses = 0;
+    var strength = 100;
+    var msg = [];
+
+    var hasLowercase = /[a-z]/.test(password);
+    var hasUppercase = /[A-Z]/.test(password);
+    var hasNumbers = /[0-9]/.test(password);
+    var hasPunctuation = /[^a-zA-Z0-9]/.test(password);
+
+    // If there is a username edit box on the page, compare password to that,
+    // otherwise use value from the database.
+    var $usernameBox = $('input.username');
+    var username = ($usernameBox.length > 0) ? $usernameBox.val() : translate.username;
+
+    // Lose 5 points for every character less than 12, plus a 30 point penalty.
+    if (password.length < 12) {
+      msg.push(translate.tooShort);
+      strength -= ((12 - password.length) * 5) + 30;
+    }
+
+    // Count weaknesses.
+    if (!hasLowercase) {
+      msg.push(translate.addLowerCase);
+      weaknesses++;
+    }
+    if (!hasUppercase) {
+      msg.push(translate.addUpperCase);
+      weaknesses++;
+    }
+    if (!hasNumbers) {
+      msg.push(translate.addNumbers);
+      weaknesses++;
+    }
+    if (!hasPunctuation) {
+      msg.push(translate.addPunctuation);
+      weaknesses++;
+    }
+
+    // Apply penalty for each weakness (balanced against length penalty).
+    switch (weaknesses) {
+      case 1:
+        strength -= 12.5;
+        break;
+
+      case 2:
+        strength -= 25;
+        break;
+
+      case 3:
+        strength -= 40;
+        break;
+
+      case 4:
+        strength -= 40;
+        break;
+    }
+
+    // Check if password is the same as the username.
+    if (password !== '' && password.toLowerCase() === username.toLowerCase()) {
+      msg.push(translate.sameAsUsername);
+      // Passwords the same as username are always very weak.
+      strength = 5;
+    }
+
+    // Based on the strength, work out what text should be shown by the
+    // password strength meter.
+    if (strength < 60) {
+      indicatorText = translate.weak;
+      indicatorClass = 'is-weak';
+    }
+    else if (strength < 70) {
+      indicatorText = translate.fair;
+      indicatorClass = 'is-fair';
+    }
+    else if (strength < 80) {
+      indicatorText = translate.good;
+      indicatorClass = 'is-good';
+    }
+    else if (strength <= 100) {
+      indicatorText = translate.strong;
+      indicatorClass = 'is-strong';
+    }
+
+    // Assemble the final message.
+    msg = translate.hasWeaknesses + '<ul><li>' + msg.join('</li><li>') + '</li></ul>';
+
+    return {
+      strength: strength,
+      message: msg,
+      indicatorText: indicatorText,
+      indicatorClass: indicatorClass
+    };
+
+  };
+
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/themes/stable/js/user/user.permissions.js b/core/themes/stable/js/user/user.permissions.js
new file mode 100644
index 0000000..abdffa7
--- /dev/null
+++ b/core/themes/stable/js/user/user.permissions.js
@@ -0,0 +1,88 @@
+/**
+ * @file
+ * User permission page behaviors.
+ */
+
+(function ($) {
+
+  "use strict";
+
+  /**
+   * Shows checked and disabled checkboxes for inherited permissions.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches functionality to the permissions table.
+   */
+  Drupal.behaviors.permissions = {
+    attach: function (context) {
+      var self = this;
+      $('table#permissions').once('permissions').each(function () {
+        // On a site with many roles and permissions, this behavior initially
+        // has to perform thousands of DOM manipulations to inject checkboxes
+        // and hide them. By detaching the table from the DOM, all operations
+        // can be performed without triggering internal layout and re-rendering
+        // processes in the browser.
+        var $table = $(this);
+        var $ancestor;
+        var method;
+        if ($table.prev().length) {
+          $ancestor = $table.prev();
+          method = 'after';
+        }
+        else {
+          $ancestor = $table.parent();
+          method = 'append';
+        }
+        $table.detach();
+
+        // Create dummy checkboxes. We use dummy checkboxes instead of reusing
+        // the existing checkboxes here because new checkboxes don't alter the
+        // submitted form. If we'd automatically check existing checkboxes, the
+        // permission table would be polluted with redundant entries. This
+        // is deliberate, but desirable when we automatically check them.
+        var $dummy = $('<input type="checkbox" class="dummy-checkbox js-dummy-checkbox" disabled="disabled" checked="checked" />')
+          .attr('title', Drupal.t("This permission is inherited from the authenticated user role."))
+          .hide();
+
+        $table
+          .find('input[type="checkbox"]')
+          .not('.js-rid-anonymous, .js-rid-authenticated')
+          .addClass('real-checkbox js-real-checkbox')
+          .after($dummy);
+
+        // Initialize the authenticated user checkbox.
+        $table.find('input[type=checkbox].js-rid-authenticated')
+          .on('click.permissions', self.toggle)
+          // .triggerHandler() cannot be used here, as it only affects the first
+          // element.
+          .each(self.toggle);
+
+        // Re-insert the table into the DOM.
+        $ancestor[method]($table);
+      });
+    },
+
+    /**
+     * Toggles all dummy checkboxes based on the checkboxes' state.
+     *
+     * If the "authenticated user" checkbox is checked, the checked and disabled
+     * checkboxes are shown, the real checkboxes otherwise.
+     */
+    toggle: function () {
+      var authCheckbox = this;
+      var $row = $(this).closest('tr');
+      // jQuery performs too many layout calculations for .hide() and .show(),
+      // leading to a major page rendering lag on sites with many roles and
+      // permissions. Therefore, we toggle visibility directly.
+      $row.find('.js-real-checkbox').each(function () {
+        this.style.display = (authCheckbox.checked ? 'none' : '');
+      });
+      $row.find('.js-dummy-checkbox').each(function () {
+        this.style.display = (authCheckbox.checked ? '' : 'none');
+      });
+    }
+  };
+
+})(jQuery);
diff --git a/core/themes/stable/js/views/ajax_view.js b/core/themes/stable/js/views/ajax_view.js
new file mode 100644
index 0000000..9145ea9
--- /dev/null
+++ b/core/themes/stable/js/views/ajax_view.js
@@ -0,0 +1,205 @@
+/**
+ * @file
+ * Handles AJAX fetching of views, including filter submission and response.
+ */
+
+(function ($, Drupal, drupalSettings) {
+
+  "use strict";
+
+  /**
+   * Attaches the AJAX behavior to exposed filters forms and key View links.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches ajaxView functionality to relevant elements.
+   */
+  Drupal.behaviors.ViewsAjaxView = {};
+  Drupal.behaviors.ViewsAjaxView.attach = function () {
+    if (drupalSettings && drupalSettings.views && drupalSettings.views.ajaxViews) {
+      var ajaxViews = drupalSettings.views.ajaxViews;
+      for (var i in ajaxViews) {
+        if (ajaxViews.hasOwnProperty(i)) {
+          Drupal.views.instances[i] = new Drupal.views.ajaxView(ajaxViews[i]);
+        }
+      }
+    }
+  };
+
+  /**
+   * @namespace
+   */
+  Drupal.views = {};
+
+  /**
+   * @type {object.<string, Drupal.views.ajaxView>}
+   */
+  Drupal.views.instances = {};
+
+  /**
+   * Javascript object for a certain view.
+   *
+   * @constructor
+   *
+   * @param {object} settings
+   *   Settings object for the ajax view.
+   * @param {string} settings.view_dom_id
+   *   The DOM id of the view.
+   */
+  Drupal.views.ajaxView = function (settings) {
+    var selector = '.js-view-dom-id-' + settings.view_dom_id;
+    this.$view = $(selector);
+
+    // Retrieve the path to use for views' ajax.
+    var ajax_path = drupalSettings.views.ajax_path;
+
+    // If there are multiple views this might've ended up showing up multiple
+    // times.
+    if (ajax_path.constructor.toString().indexOf("Array") !== -1) {
+      ajax_path = ajax_path[0];
+    }
+
+    // Check if there are any GET parameters to send to views.
+    var queryString = window.location.search || '';
+    if (queryString !== '') {
+      // Remove the question mark and Drupal path component if any.
+      queryString = queryString.slice(1).replace(/q=[^&]+&?|&?render=[^&]+/, '');
+      if (queryString !== '') {
+        // If there is a '?' in ajax_path, clean url are on and & should be
+        // used to add parameters.
+        queryString = ((/\?/.test(ajax_path)) ? '&' : '?') + queryString;
+      }
+    }
+
+    this.element_settings = {
+      url: ajax_path + queryString,
+      submit: settings,
+      setClick: true,
+      event: 'click',
+      selector: selector,
+      progress: {type: 'fullscreen'}
+    };
+
+    this.settings = settings;
+
+    // Add the ajax to exposed forms.
+    this.$exposed_form = $('form#views-exposed-form-' + settings.view_name.replace(/_/g, '-') + '-' + settings.view_display_id.replace(/_/g, '-'));
+    this.$exposed_form.once('exposed-form').each(jQuery.proxy(this.attachExposedFormAjax, this));
+
+    // Add the ajax to pagers.
+    this.$view
+      // Don't attach to nested views. Doing so would attach multiple behaviors
+      // to a given element.
+      .filter(jQuery.proxy(this.filterNestedViews, this))
+      .once('ajax-pager').each(jQuery.proxy(this.attachPagerAjax, this));
+
+    // Add a trigger to update this view specifically. In order to trigger a
+    // refresh use the following code.
+    //
+    // @code
+    // jQuery('.view-name').trigger('RefreshView');
+    // @endcode
+    var self_settings = $.extend({}, this.element_settings, {
+      event: 'RefreshView',
+      base: this.selector,
+      element: this.$view
+    });
+    this.refreshViewAjax = Drupal.ajax(self_settings);
+  };
+
+  /**
+   * @method
+   */
+  Drupal.views.ajaxView.prototype.attachExposedFormAjax = function () {
+    var that = this;
+    this.exposedFormAjax = [];
+    $('input[type=submit], input[type=image]', this.$exposed_form).each(function (index) {
+      var self_settings = $.extend({}, that.element_settings, {
+        base: $(this).attr('id'),
+        element: this
+      });
+      that.exposedFormAjax[index] = Drupal.ajax(self_settings);
+    });
+  };
+
+  /**
+   * @return {bool}
+   *   If there is at least one parent with a view class return false.
+   *
+   * @todo remove .size() replace with .length.
+   */
+  Drupal.views.ajaxView.prototype.filterNestedViews = function () {
+    // If there is at least one parent with a view class, this view
+    // is nested (e.g., an attachment). Bail.
+    return !this.$view.parents('.view').size();
+  };
+
+  /**
+   * Attach the ajax behavior to each link.
+   */
+  Drupal.views.ajaxView.prototype.attachPagerAjax = function () {
+    this.$view.find('ul.js-pager__items > li > a, th.views-field a, .attachment .views-summary a')
+      .each(jQuery.proxy(this.attachPagerLinkAjax, this));
+  };
+
+  /**
+   * Attach the ajax behavior to a singe link.
+   *
+   * @param {string} [id]
+   *   The ID of the link.
+   * @param {HTMLElement} link
+   *   The link element.
+   */
+  Drupal.views.ajaxView.prototype.attachPagerLinkAjax = function (id, link) {
+    var $link = $(link);
+    var viewData = {};
+    var href = $link.attr('href');
+    // Construct an object using the settings defaults and then overriding
+    // with data specific to the link.
+    $.extend(
+      viewData,
+      this.settings,
+      Drupal.Views.parseQueryString(href),
+      // Extract argument data from the URL.
+      Drupal.Views.parseViewArgs(href, this.settings.view_base_path)
+    );
+
+    var self_settings = $.extend({}, this.element_settings, {
+      submit: viewData,
+      base: false,
+      element: $link
+    });
+    this.pagerAjax = Drupal.ajax(self_settings);
+  };
+
+  /**
+   * Views scroll to top ajax command.
+   *
+   * @param {Drupal.Ajax} [ajax]
+   *   A {@link Drupal.ajax} object.
+   * @param {object} response
+   *   Ajax response.
+   * @param {string} response.selector
+   *   Selector to use.
+   */
+  Drupal.AjaxCommands.prototype.viewsScrollTop = function (ajax, response) {
+    // Scroll to the top of the view. This will allow users
+    // to browse newly loaded content after e.g. clicking a pager
+    // link.
+    var offset = $(response.selector).offset();
+    // We can't guarantee that the scrollable object should be
+    // the body, as the view could be embedded in something
+    // more complex such as a modal popup. Recurse up the DOM
+    // and scroll the first element that has a non-zero top.
+    var scrollTarget = response.selector;
+    while ($(scrollTarget).scrollTop() === 0 && $(scrollTarget).parent()) {
+      scrollTarget = $(scrollTarget).parent();
+    }
+    // Only scroll upward.
+    if (offset.top - 10 < $(scrollTarget).scrollTop()) {
+      $(scrollTarget).animate({scrollTop: (offset.top - 10)}, 500);
+    }
+  };
+
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/themes/stable/js/views/base.js b/core/themes/stable/js/views/base.js
new file mode 100644
index 0000000..2fa7f79
--- /dev/null
+++ b/core/themes/stable/js/views/base.js
@@ -0,0 +1,109 @@
+/**
+ * @file
+ * Some basic behaviors and utility functions for Views.
+ */
+
+(function ($, Drupal, drupalSettings) {
+
+  "use strict";
+
+  /**
+   * @namespace
+   */
+  Drupal.Views = {};
+
+  /**
+   * Helper function to parse a querystring.
+   *
+   * @param {string} query
+   *   The querystring to parse.
+   *
+   * @return {object}
+   *   A map of query parameters.
+   */
+  Drupal.Views.parseQueryString = function (query) {
+    var args = {};
+    var pos = query.indexOf('?');
+    if (pos !== -1) {
+      query = query.substring(pos + 1);
+    }
+    var pair;
+    var pairs = query.split('&');
+    for (var i = 0; i < pairs.length; i++) {
+      pair = pairs[i].split('=');
+      // Ignore the 'q' path argument, if present.
+      if (pair[0] !== 'q' && pair[1]) {
+        args[decodeURIComponent(pair[0].replace(/\+/g, ' '))] = decodeURIComponent(pair[1].replace(/\+/g, ' '));
+      }
+    }
+    return args;
+  };
+
+  /**
+   * Helper function to return a view's arguments based on a path.
+   *
+   * @param {string} href
+   *   The href to check.
+   * @param {string} viewPath
+   *   The views path to check.
+   *
+   * @return {object}
+   *   An object containing `view_args` and `view_path`.
+   */
+  Drupal.Views.parseViewArgs = function (href, viewPath) {
+    var returnObj = {};
+    var path = Drupal.Views.getPath(href);
+    // Ensure we have a correct path.
+    if (viewPath && path.substring(0, viewPath.length + 1) === viewPath + '/') {
+      var args = decodeURIComponent(path.substring(viewPath.length + 1, path.length));
+      returnObj.view_args = args;
+      returnObj.view_path = path;
+    }
+    return returnObj;
+  };
+
+  /**
+   * Strip off the protocol plus domain from an href.
+   *
+   * @param {string} href
+   *   The href to strip.
+   *
+   * @return {string}
+   *   The href without the protocol and domain.
+   */
+  Drupal.Views.pathPortion = function (href) {
+    // Remove e.g. http://example.com if present.
+    var protocol = window.location.protocol;
+    if (href.substring(0, protocol.length) === protocol) {
+      // 2 is the length of the '//' that normally follows the protocol.
+      href = href.substring(href.indexOf('/', protocol.length + 2));
+    }
+    return href;
+  };
+
+  /**
+   * Return the Drupal path portion of an href.
+   *
+   * @param {string} href
+   *   The href to check.
+   *
+   * @return {string}
+   *   An internal path.
+   */
+  Drupal.Views.getPath = function (href) {
+    href = Drupal.Views.pathPortion(href);
+    href = href.substring(drupalSettings.path.baseUrl.length, href.length);
+    // 3 is the length of the '?q=' added to the url without clean urls.
+    if (href.substring(0, 3) === '?q=') {
+      href = href.substring(3, href.length);
+    }
+    var chars = ['#', '?', '&'];
+    for (var i = 0; i < chars.length; i++) {
+      if (href.indexOf(chars[i]) > -1) {
+        href = href.substr(0, href.indexOf(chars[i]));
+      }
+    }
+    return href;
+  };
+
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/themes/stable/js/views_ui/ajax.js b/core/themes/stable/js/views_ui/ajax.js
new file mode 100644
index 0000000..1e37607
--- /dev/null
+++ b/core/themes/stable/js/views_ui/ajax.js
@@ -0,0 +1,220 @@
+/**
+ * @file
+ * Handles AJAX submission and response in Views UI.
+ */
+
+(function ($, Drupal, drupalSettings) {
+
+  "use strict";
+
+  /**
+   * Ajax command for highlighting elements.
+   *
+   * @param {Drupal.Ajax} [ajax]
+   *   An Ajax object.
+   * @param {object} response
+   *   The Ajax response.
+   * @param {string} response.selector
+   *   The selector in question.
+   * @param {number} [status]
+   *   The HTTP status code.
+   */
+  Drupal.AjaxCommands.prototype.viewsHighlight = function (ajax, response, status) {
+    $('.hilited').removeClass('hilited');
+    $(response.selector).addClass('hilited');
+  };
+
+  /**
+   * Ajax command to show certain buttons in the views edit form.
+   *
+   * @param {Drupal.Ajax} [ajax]
+   *   An Ajax object.
+   * @param {object} response
+   *   The Ajax response.
+   * @param {bool} response.changed
+   *   Whether the state changed for the buttons or not.
+   * @param {number} [status]
+   *   The HTTP status code.
+   */
+  Drupal.AjaxCommands.prototype.viewsShowButtons = function (ajax, response, status) {
+    $('div.views-edit-view div.form-actions').removeClass('js-hide');
+    if (response.changed) {
+      $('div.views-edit-view div.view-changed.messages').removeClass('js-hide');
+    }
+  };
+
+  /**
+   * Ajax command for triggering preview.
+   *
+   * @param {Drupal.Ajax} [ajax]
+   *   An Ajax object.
+   * @param {object} [response]
+   *   The Ajax response.
+   * @param {number} [status]
+   *   The HTTP status code.
+   */
+  Drupal.AjaxCommands.prototype.viewsTriggerPreview = function (ajax, response, status) {
+    if ($('input#edit-displays-live-preview').is(':checked')) {
+      $('#preview-submit').trigger('click');
+    }
+  };
+
+  /**
+   * Ajax command to replace the title of a page.
+   *
+   * @param {Drupal.Ajax} [ajax]
+   *   An Ajax object.
+   * @param {object} response
+   *   The Ajax response.
+   * @param {string} response.siteName
+   *   The site name.
+   * @param {string} response.title
+   *   The new page title.
+   * @param {number} [status]
+   *   The HTTP status code.
+   */
+  Drupal.AjaxCommands.prototype.viewsReplaceTitle = function (ajax, response, status) {
+    var doc = document;
+    // For the <title> element, make a best-effort attempt to replace the page
+    // title and leave the site name alone. If the theme doesn't use the site
+    // name in the <title> element, this will fail.
+    var oldTitle = doc.title;
+    // Escape the site name, in case it has special characters in it, so we can
+    // use it in our regex.
+    var escapedSiteName = response.siteName.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
+    var re = new RegExp('.+ (.) ' + escapedSiteName);
+    doc.title = oldTitle.replace(re, response.title + ' $1 ' + response.siteName);
+
+    $('h1.page-title').text(response.title);
+  };
+
+  /**
+   * Get rid of irritating tabledrag messages.
+   *
+   * @return {Array}
+   *   An array of messages. Always empty array, to get rid of the messages.
+   */
+  Drupal.theme.tableDragChangedWarning = function () {
+    return [];
+  };
+
+  /**
+   * Trigger preview when the "live preview" checkbox is checked.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches behavior to trigger live preview if the live preview option is
+   *   checked.
+   */
+  Drupal.behaviors.livePreview = {
+    attach: function (context) {
+      $('input#edit-displays-live-preview', context).once('views-ajax').on('click', function () {
+        if ($(this).is(':checked')) {
+          $('#preview-submit').trigger('click');
+        }
+      });
+    }
+  };
+
+  /**
+   * Sync preview display.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches behavior to sync the preview display when needed.
+   */
+  Drupal.behaviors.syncPreviewDisplay = {
+    attach: function (context) {
+      $("#views-tabset a").once('views-ajax').on('click', function () {
+        var href = $(this).attr('href');
+        // Cut of #views-tabset.
+        var display_id = href.substr(11);
+        // Set the form element.
+        $("#views-live-preview #preview-display-id").val(display_id);
+      });
+    }
+  };
+
+  /**
+   * Ajax behaviors for the views_ui module.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches ajax behaviors to the elements with the classes in question.
+   */
+  Drupal.behaviors.viewsAjax = {
+    collapseReplaced: false,
+    attach: function (context, settings) {
+      var base_element_settings = {
+        event: 'click',
+        progress: {type: 'fullscreen'}
+      };
+      // Bind AJAX behaviors to all items showing the class.
+      $('a.views-ajax-link', context).once('views-ajax').each(function () {
+        var element_settings = base_element_settings;
+        element_settings.base = base;
+        element_settings.element = this;
+        // Set the URL to go to the anchor.
+        if ($(this).attr('href')) {
+          element_settings.url = $(this).attr('href');
+        }
+        var base = $(this).attr('id');
+        Drupal.ajax(element_settings);
+      });
+
+      $('div#views-live-preview a')
+        .once('views-ajax').each(function () {
+          // We don't bind to links without a URL.
+          if (!$(this).attr('href')) {
+            return true;
+          }
+
+          var element_settings = base_element_settings;
+          // Set the URL to go to the anchor.
+          element_settings.url = $(this).attr('href');
+          if (Drupal.Views.getPath(element_settings.url).substring(0, 21) !== 'admin/structure/views') {
+            return true;
+          }
+
+          element_settings.wrapper = 'views-preview-wrapper';
+          element_settings.method = 'replaceWith';
+          element_settings.base = base;
+          element_settings.element = this;
+          var base = $(this).attr('id');
+          Drupal.ajax(element_settings);
+        });
+
+      // Within a live preview, make exposed widget form buttons re-trigger the
+      // Preview button.
+      // @todo Revisit this after fixing Views UI to display a Preview outside
+      //   of the main Edit form.
+      $('div#views-live-preview input[type=submit]')
+        .once('views-ajax').each(function (event) {
+          $(this).on('click', function () {
+            this.form.clk = this;
+            return true;
+          });
+          var element_settings = base_element_settings;
+          // Set the URL to go to the anchor.
+          element_settings.url = $(this.form).attr('action');
+          if (Drupal.Views.getPath(element_settings.url).substring(0, 21) !== 'admin/structure/views') {
+            return true;
+          }
+
+          element_settings.wrapper = 'views-preview-wrapper';
+          element_settings.method = 'replaceWith';
+          element_settings.event = 'click';
+          element_settings.base = base;
+          element_settings.element = this;
+
+          var base = $(this).attr('id');
+          Drupal.ajax(element_settings);
+        });
+
+    }
+  };
+
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/themes/stable/js/views_ui/dialog.views.js b/core/themes/stable/js/views_ui/dialog.views.js
new file mode 100644
index 0000000..b68039a
--- /dev/null
+++ b/core/themes/stable/js/views_ui/dialog.views.js
@@ -0,0 +1,58 @@
+/**
+ * @file
+ * Views dialog behaviors.
+ */
+
+(function ($, Drupal, drupalSettings) {
+
+  "use strict";
+
+  function handleDialogResize(e) {
+    var $modal = $(e.currentTarget);
+    var $viewsOverride = $modal.find('[data-drupal-views-offset]');
+    var $scroll = $modal.find('[data-drupal-views-scroll]');
+    var offset = 0;
+    var modalHeight;
+    if ($scroll.length) {
+      // Add a class to do some styles adjustments.
+      $modal.closest('.views-ui-dialog').addClass('views-ui-dialog-scroll');
+      // Let scroll element take all the height available.
+      $scroll.css({overflow: 'visible', height: 'auto'});
+      modalHeight = $modal.height();
+      $viewsOverride.each(function () { offset += $(this).outerHeight(); });
+
+      // Take internal padding into account.
+      var scrollOffset = $scroll.outerHeight() - $scroll.height();
+      $scroll.height(modalHeight - offset - scrollOffset);
+      // Reset scrolling properties.
+      $modal.css('overflow', 'hidden');
+      $scroll.css('overflow', 'auto');
+    }
+  }
+
+  /**
+   * Functionality for views modals.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches modal functionality for views.
+   * @prop {Drupal~behaviorDetach} detach
+   *   Detaches the modal functionality.
+   */
+  Drupal.behaviors.viewsModalContent = {
+    attach: function (context) {
+      $('body').once('viewsDialog').on('dialogContentResize.viewsDialog', '.ui-dialog-content', handleDialogResize);
+      // When expanding details, make sure the modal is resized.
+      $(context).find('.scroll').once('detailsUpdate').on('click', 'summary', function (e) {
+        $(e.currentTarget).trigger('dialogContentResize');
+      });
+    },
+    detach: function (context, settings, trigger) {
+      if (trigger === 'unload') {
+        $('body').removeOnce('viewsDialog').off('.viewsDialog');
+      }
+    }
+  };
+
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/themes/stable/js/views_ui/views-admin.js b/core/themes/stable/js/views_ui/views-admin.js
new file mode 100644
index 0000000..28e2e96
--- /dev/null
+++ b/core/themes/stable/js/views_ui/views-admin.js
@@ -0,0 +1,1202 @@
+/**
+ * @file
+ * Some basic behaviors and utility functions for Views UI.
+ */
+
+(function ($, Drupal, drupalSettings) {
+
+  "use strict";
+
+  /**
+   * @namespace
+   */
+  Drupal.viewsUi = {};
+
+  /**
+   * Improve the user experience of the views edit interface.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches toggling of SQL rewrite warning on the corresponding checkbox.
+   */
+  Drupal.behaviors.viewsUiEditView = {
+    attach: function () {
+      // Only show the SQL rewrite warning when the user has chosen the
+      // corresponding checkbox.
+      $('[data-drupal-selector="edit-query-options-disable-sql-rewrite"]').on('click', function () {
+        $('.sql-rewrite-warning').toggleClass('js-hide');
+      });
+    }
+  };
+
+  /**
+   * In the add view wizard, use the view name to prepopulate form fields such
+   * as page title and menu link.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches behavior for prepopulating page title and menu links, based on
+   *   view name.
+   */
+  Drupal.behaviors.viewsUiAddView = {
+    attach: function (context) {
+      var $context = $(context);
+      // Set up regular expressions to allow only numbers, letters, and dashes.
+      var exclude = new RegExp('[^a-z0-9\\-]+', 'g');
+      var replace = '-';
+      var suffix;
+
+      // The page title, block title, and menu link fields can all be
+      // prepopulated with the view name - no regular expression needed.
+      var $fields = $context.find('[id^="edit-page-title"], [id^="edit-block-title"], [id^="edit-page-link-properties-title"]');
+      if ($fields.length) {
+        if (!this.fieldsFiller) {
+          this.fieldsFiller = new Drupal.viewsUi.FormFieldFiller($fields);
+        }
+        else {
+          // After an AJAX response, this.fieldsFiller will still have event
+          // handlers bound to the old version of the form fields (which don't
+          // exist anymore). The event handlers need to be unbound and then
+          // rebound to the new markup. Note that jQuery.live is difficult to
+          // make work in this case because the IDs of the form fields change
+          // on every AJAX response.
+          this.fieldsFiller.rebind($fields);
+        }
+      }
+
+      // Prepopulate the path field with a URLified version of the view name.
+      var $pathField = $context.find('[id^="edit-page-path"]');
+      if ($pathField.length) {
+        if (!this.pathFiller) {
+          this.pathFiller = new Drupal.viewsUi.FormFieldFiller($pathField, exclude, replace);
+        }
+        else {
+          this.pathFiller.rebind($pathField);
+        }
+      }
+
+      // Populate the RSS feed field with a URLified version of the view name,
+      // and an .xml suffix (to make it unique).
+      var $feedField = $context.find('[id^="edit-page-feed-properties-path"]');
+      if ($feedField.length) {
+        if (!this.feedFiller) {
+          suffix = '.xml';
+          this.feedFiller = new Drupal.viewsUi.FormFieldFiller($feedField, exclude, replace, suffix);
+        }
+        else {
+          this.feedFiller.rebind($feedField);
+        }
+      }
+    }
+  };
+
+  /**
+   * Constructor for the {@link Drupal.viewsUi.FormFieldFiller} object.
+   *
+   * Prepopulates a form field based on the view name.
+   *
+   * @constructor
+   *
+   * @param {jQuery} $target
+   *   A jQuery object representing the form field or fields to prepopulate.
+   * @param {bool} [exclude=false]
+   *   A regular expression representing characters to exclude from
+   *   the target field.
+   * @param {string} [replace='']
+   *   A string to use as the replacement value for disallowed characters.
+   * @param {string} [suffix='']
+   *   A suffix to append at the end of the target field content.
+   */
+  Drupal.viewsUi.FormFieldFiller = function ($target, exclude, replace, suffix) {
+
+    /**
+     *
+     * @type {jQuery}
+     */
+    this.source = $('#edit-label');
+
+    /**
+     *
+     * @type {jQuery}
+     */
+    this.target = $target;
+
+    /**
+     *
+     * @type {bool}
+     */
+    this.exclude = exclude || false;
+
+    /**
+     *
+     * @type {string}
+     */
+    this.replace = replace || '';
+
+    /**
+     *
+     * @type {string}
+     */
+    this.suffix = suffix || '';
+
+    // Create bound versions of this instance's object methods to use as event
+    // handlers. This will let us easily unbind those specific handlers later
+    // on. NOTE: jQuery.proxy will not work for this because it assumes we want
+    // only one bound version of an object method, whereas we need one version
+    // per object instance.
+    var self = this;
+
+    /**
+     * Populate the target form field with the altered source field value.
+     *
+     * @return {*}
+     *   The result of the _populate call, which should be undefined.
+     */
+    this.populate = function () { return self._populate.call(self); };
+
+    /**
+     * Stop prepopulating the form fields.
+     *
+     * @return {*}
+     *   The result of the _unbind call, which should be undefined.
+     */
+    this.unbind = function () { return self._unbind.call(self); };
+
+    this.bind();
+    // Object constructor; no return value.
+  };
+
+  $.extend(Drupal.viewsUi.FormFieldFiller.prototype, /** @lends Drupal.viewsUi.FormFieldFiller# */{
+
+    /**
+     * Bind the form-filling behavior.
+     */
+    bind: function () {
+      this.unbind();
+      // Populate the form field when the source changes.
+      this.source.on('keyup.viewsUi change.viewsUi', this.populate);
+      // Quit populating the field as soon as it gets focus.
+      this.target.on('focus.viewsUi', this.unbind);
+    },
+
+    /**
+     * Get the source form field value as altered by the passed-in parameters.
+     *
+     * @return {string}
+     *   The source form field value.
+     */
+    getTransliterated: function () {
+      var from = this.source.val();
+      if (this.exclude) {
+        from = from.toLowerCase().replace(this.exclude, this.replace);
+      }
+      return from;
+    },
+
+    /**
+     * Populate the target form field with the altered source field value.
+     */
+    _populate: function () {
+      var transliterated = this.getTransliterated();
+      var suffix = this.suffix;
+      this.target.each(function (i) {
+        // Ensure that the maxlength is not exceeded by prepopulating the field.
+        var maxlength = $(this).attr('maxlength') - suffix.length;
+        $(this).val(transliterated.substr(0, maxlength) + suffix);
+      });
+    },
+
+    /**
+     * Stop prepopulating the form fields.
+     */
+    _unbind: function () {
+      this.source.off('keyup.viewsUi change.viewsUi', this.populate);
+      this.target.off('focus.viewsUi', this.unbind);
+    },
+
+    /**
+     * Bind event handlers to new form fields, after they're replaced via Ajax.
+     *
+     * @param {jQuery} $fields
+     *   Fields to rebind functionality to.
+     */
+    rebind: function ($fields) {
+      this.target = $fields;
+      this.bind();
+    }
+  });
+
+  /**
+   * Adds functionality for the add item form.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches the functionality in {@link Drupal.viewsUi.AddItemForm} to the
+   *   forms in question.
+   */
+  Drupal.behaviors.addItemForm = {
+    attach: function (context) {
+      var $context = $(context);
+      var $form = $context;
+      // The add handler form may have an id of views-ui-add-handler-form--n.
+      if (!$context.is('form[id^="views-ui-add-handler-form"]')) {
+        $form = $context.find('form[id^="views-ui-add-handler-form"]');
+      }
+      if ($form.once('views-ui-add-handler-form').length) {
+        // If we we have an unprocessed views-ui-add-handler-form, let's
+        // instantiate.
+        new Drupal.viewsUi.AddItemForm($form);
+      }
+    }
+  };
+
+  /**
+   * Constructs a new AddItemForm.
+   *
+   * @constructor
+   *
+   * @param {jQuery} $form
+   *   The form element used.
+   */
+  Drupal.viewsUi.AddItemForm = function ($form) {
+
+    /**
+     *
+     * @type {jQuery}
+     */
+    this.$form = $form;
+    this.$form.find('.views-filterable-options :checkbox').on('click', $.proxy(this.handleCheck, this));
+
+    /**
+     * Find the wrapper of the displayed text.
+     */
+    this.$selected_div = this.$form.find('.views-selected-options').parent();
+    this.$selected_div.hide();
+
+    /**
+     *
+     * @type {Array}
+     */
+    this.checkedItems = [];
+  };
+
+  /**
+   * Handles a checkbox check.
+   *
+   * @param {jQuery.Event} event
+   *   The event triggered.
+   */
+  Drupal.viewsUi.AddItemForm.prototype.handleCheck = function (event) {
+    var $target = $(event.target);
+    var label = $.trim($target.next().html());
+    // Add/remove the checked item to the list.
+    if ($target.is(':checked')) {
+      this.$selected_div.show().css('display', 'block');
+      this.checkedItems.push(label);
+    }
+    else {
+      var position = $.inArray(label, this.checkedItems);
+      // Delete the item from the list and make sure that the list doesn't have
+      // undefined items left.
+      for (var i = 0; i < this.checkedItems.length; i++) {
+        if (i === position) {
+          this.checkedItems.splice(i, 1);
+          i--;
+          break;
+        }
+      }
+      // Hide it again if none item is selected.
+      if (this.checkedItems.length === 0) {
+        this.$selected_div.hide();
+      }
+    }
+    this.refreshCheckedItems();
+  };
+
+  /**
+   * Refresh the display of the checked items.
+   */
+  Drupal.viewsUi.AddItemForm.prototype.refreshCheckedItems = function () {
+    // Perhaps we should precache the text div, too.
+    this.$selected_div.find('.views-selected-options')
+      .html(this.checkedItems.join(', '))
+      .trigger('dialogContentResize');
+  };
+
+  /**
+   * The input field items that add displays must be rendered as `<input>`
+   * elements. The following behavior detaches the `<input>` elements from the
+   * DOM, wraps them in an unordered list, then appends them to the list of
+   * tabs.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Fixes the input elements needed.
+   */
+  Drupal.behaviors.viewsUiRenderAddViewButton = {
+    attach: function (context) {
+      // Build the add display menu and pull the display input buttons into it.
+      var $menu = $(context).find('#views-display-menu-tabs').once('views-ui-render-add-view-button');
+      if (!$menu.length) {
+        return;
+      }
+
+      var $addDisplayDropdown = $('<li class="add"><a href="#"><span class="icon add"></span>' + Drupal.t('Add') + '</a><ul class="action-list" style="display:none;"></ul></li>');
+      var $displayButtons = $menu.nextAll('input.add-display').detach();
+      $displayButtons.appendTo($addDisplayDropdown.find('.action-list')).wrap('<li>')
+        .parent().eq(0).addClass('first').end().eq(-1).addClass('last');
+      // Remove the 'Add ' prefix from the button labels since they're being
+      // placed in an 'Add' dropdown. @todo This assumes English, but so does
+      // $addDisplayDropdown above. Add support for translation.
+      $displayButtons.each(function () {
+        var label = $(this).val();
+        if (label.substr(0, 4) === 'Add ') {
+          $(this).val(label.substr(4));
+        }
+      });
+      $addDisplayDropdown.appendTo($menu);
+
+      // Add the click handler for the add display button.
+      $menu.find('li.add > a').on('click', function (event) {
+        event.preventDefault();
+        var $trigger = $(this);
+        Drupal.behaviors.viewsUiRenderAddViewButton.toggleMenu($trigger);
+      });
+      // Add a mouseleave handler to close the dropdown when the user mouses
+      // away from the item. We use mouseleave instead of mouseout because
+      // the user is going to trigger mouseout when she moves from the trigger
+      // link to the sub menu items.
+      // We use the live binder because the open class on this item will be
+      // toggled on and off and we want the handler to take effect in the cases
+      // that the class is present, but not when it isn't.
+      $('li.add', $menu).on('mouseleave', function (event) {
+        var $this = $(this);
+        var $trigger = $this.children('a[href="#"]');
+        if ($this.children('.action-list').is(':visible')) {
+          Drupal.behaviors.viewsUiRenderAddViewButton.toggleMenu($trigger);
+        }
+      });
+    }
+  };
+
+  /**
+   * Toggle menu visibility.
+   *
+   * @param {jQuery} $trigger
+   *   The element where the toggle was triggered.
+   *
+   *
+   * @note [@jessebeach] I feel like the following should be a more generic
+   *   function and not written specifically for this UI, but I'm not sure
+   *   where to put it.
+   */
+  Drupal.behaviors.viewsUiRenderAddViewButton.toggleMenu = function ($trigger) {
+    $trigger.parent().toggleClass('open');
+    $trigger.next().slideToggle('fast');
+  };
+
+  /**
+   * Add search options to the views ui.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches {@link Drupal.viewsUi.OptionsSearch} to the views ui filter
+   *   options.
+   */
+  Drupal.behaviors.viewsUiSearchOptions = {
+    attach: function (context) {
+      var $context = $(context);
+      var $form = $context;
+      // The add handler form may have an id of views-ui-add-handler-form--n.
+      if (!$context.is('form[id^="views-ui-add-handler-form"]')) {
+        $form = $context.find('form[id^="views-ui-add-handler-form"]');
+      }
+      // Make sure we don't add more than one event handler to the same form.
+      if ($form.once('views-ui-filter-options').length) {
+        new Drupal.viewsUi.OptionsSearch($form);
+      }
+    }
+  };
+
+  /**
+   * Constructor for the viewsUi.OptionsSearch object.
+   *
+   * The OptionsSearch object filters the available options on a form according
+   * to the user's search term. Typing in "taxonomy" will show only those
+   * options containing "taxonomy" in their label.
+   *
+   * @constructor
+   *
+   * @param {jQuery} $form
+   *   The form element.
+   */
+  Drupal.viewsUi.OptionsSearch = function ($form) {
+
+    /**
+     *
+     * @type {jQuery}
+     */
+    this.$form = $form;
+
+    /**
+     * Add a keyup handler to the search box.
+     */
+    this.$searchBox = this.$form.find('[data-drupal-selector="edit-override-controls-options-search"]');
+    this.$searchBox.on('keyup', $.proxy(this.handleKeyup, this));
+
+    /**
+     * Get a list of option labels and their corresponding divs and maintain it
+     * in memory, so we have as little overhead as possible at keyup time.
+     */
+    this.options = this.getOptions(this.$form.find('.filterable-option'));
+
+    // Restripe on initial loading.
+    this.handleKeyup();
+    // Trap the ENTER key in the search box so that it doesn't submit the form.
+    this.$searchBox.on('keypress', function (event) {
+      if (event.which === 13) {
+        event.preventDefault();
+      }
+    });
+  };
+
+  $.extend(Drupal.viewsUi.OptionsSearch.prototype, /** @lends Drupal.viewsUi.OptionsSearch# */{
+
+    /**
+     * Assemble a list of all the filterable options on the form.
+     *
+     * @param {jQuery} $allOptions
+     *   A jQuery object representing the rows of filterable options to be
+     *   shown and hidden depending on the user's search terms.
+     *
+     * @return {Array}
+     *   An array of all the filterable options.
+     */
+    getOptions: function ($allOptions) {
+      var $label;
+      var $description;
+      var $option;
+      var options = [];
+      var length = $allOptions.length;
+      for (var i = 0; i < length; i++) {
+        $option = $($allOptions[i]);
+        $label = $option.find('label');
+        $description = $option.find('div.description');
+        options[i] = {
+          // Search on the lowercase version of the label text + description.
+          searchText: $label.text().toLowerCase() + " " + $description.text().toLowerCase(),
+          // Maintain a reference to the jQuery object for each row, so we don't
+          // have to create a new object inside the performance-sensitive keyup
+          // handler.
+          $div: $option
+        };
+      }
+      return options;
+    },
+
+    /**
+     * Keyup handler for the search box that hides or shows the relevant
+     * options.
+     *
+     * @param {jQuery.Event} event
+     *   The keyup event.
+     */
+    handleKeyup: function (event) {
+      var found;
+      var option;
+      var zebraClass;
+
+      // Determine the user's search query. The search text has been converted
+      // to lowercase.
+      var search = this.$searchBox.val().toLowerCase();
+      var words = search.split(' ');
+      var wordsLength = words.length;
+
+      // Start the counter for restriping rows.
+      var zebraCounter = 0;
+
+      // Search through the search texts in the form for matching text.
+      var length = this.options.length;
+      for (var i = 0; i < length; i++) {
+        // Use a local variable for the option being searched, for performance.
+        option = this.options[i];
+        found = true;
+        // Each word in the search string has to match the item in order for the
+        // item to be shown.
+        for (var j = 0; j < wordsLength; j++) {
+          if (option.searchText.indexOf(words[j]) === -1) {
+            found = false;
+          }
+        }
+        if (found) {
+          zebraClass = (zebraCounter % 2) ? 'odd' : 'even';
+          // Show the checkbox row, and restripe it.
+          option.$div.removeClass('even odd');
+          option.$div.addClass(zebraClass);
+          option.$div.show();
+          zebraCounter++;
+        }
+        else {
+          // The search string wasn't found; hide this item.
+          option.$div.hide();
+        }
+      }
+    }
+  });
+
+  /**
+   * Preview functionality in the views edit form.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches the preview functionality to the view edit form.
+   */
+  Drupal.behaviors.viewsUiPreview = {
+    attach: function (context) {
+      // Only act on the edit view form.
+      var $contextualFiltersBucket = $(context).find('.views-display-column .views-ui-display-tab-bucket.argument');
+      if ($contextualFiltersBucket.length === 0) {
+        return;
+      }
+
+      // If the display has no contextual filters, hide the form where you
+      // enter the contextual filters for the live preview. If it has contextual
+      // filters, show the form.
+      var $contextualFilters = $contextualFiltersBucket.find('.views-display-setting a');
+      if ($contextualFilters.length) {
+        $('#preview-args').parent().show();
+      }
+      else {
+        $('#preview-args').parent().hide();
+      }
+
+      // Executes an initial preview.
+      if ($('#edit-displays-live-preview').once('edit-displays-live-preview').is(':checked')) {
+        $('#preview-submit').once('edit-displays-live-preview').trigger('click');
+      }
+    }
+  };
+
+  /**
+   * Rearranges the filters.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attach handlers to make it possible to rearange the filters in the form
+   *   in question.
+   *   @see Drupal.viewsUi.RearrangeFilterHandler
+   */
+  Drupal.behaviors.viewsUiRearrangeFilter = {
+    attach: function (context) {
+      // Only act on the rearrange filter form.
+      if (typeof Drupal.tableDrag === 'undefined' || typeof Drupal.tableDrag['views-rearrange-filters'] === 'undefined') {
+        return;
+      }
+      var $context = $(context);
+      var $table = $context.find('#views-rearrange-filters').once('views-rearrange-filters');
+      var $operator = $context.find('.js-form-item-filter-groups-operator').once('views-rearrange-filters');
+      if ($table.length) {
+        new Drupal.viewsUi.RearrangeFilterHandler($table, $operator);
+      }
+    }
+  };
+
+  /**
+   * Improve the UI of the rearrange filters dialog box.
+   *
+   * @constructor
+   *
+   * @param {jQuery} $table
+   *   The table in the filter form.
+   * @param {jQuery} $operator
+   *   The filter groups operator element.
+   */
+  Drupal.viewsUi.RearrangeFilterHandler = function ($table, $operator) {
+
+    /**
+     * Keep a reference to the `<table>` being altered and to the div containing
+     * the filter groups operator dropdown (if it exists).
+     */
+    this.table = $table;
+
+    /**
+     *
+     * @type {jQuery}
+     */
+    this.operator = $operator;
+
+    /**
+     *
+     * @type {bool}
+     */
+    this.hasGroupOperator = this.operator.length > 0;
+
+    /**
+     * Keep a reference to all draggable rows within the table.
+     *
+     * @type {jQuery}
+     */
+    this.draggableRows = $table.find('.draggable');
+
+    /**
+     * Keep a reference to the buttons for adding and removing filter groups.
+     *
+     * @type {jQuery}
+     */
+    this.addGroupButton = $('input#views-add-group');
+
+    /**
+     * @type {jQuery}
+     */
+    this.removeGroupButtons = $table.find('input.views-remove-group');
+
+    // Add links that duplicate the functionality of the (hidden) add and remove
+    // buttons.
+    this.insertAddRemoveFilterGroupLinks();
+
+    // When there is a filter groups operator dropdown on the page, create
+    // duplicates of the dropdown between each pair of filter groups.
+    if (this.hasGroupOperator) {
+
+      /**
+       * @type {jQuery}
+       */
+      this.dropdowns = this.duplicateGroupsOperator();
+      this.syncGroupsOperators();
+    }
+
+    // Add methods to the tableDrag instance to account for operator cells
+    // (which span multiple rows), the operator labels next to each filter
+    // (e.g., "And" or "Or"), the filter groups, and other special aspects of
+    // this tableDrag instance.
+    this.modifyTableDrag();
+
+    // Initialize the operator labels (e.g., "And" or "Or") that are displayed
+    // next to the filters in each group, and bind a handler so that they change
+    // based on the values of the operator dropdown within that group.
+    this.redrawOperatorLabels();
+    $table.find('.views-group-title select')
+      .once('views-rearrange-filter-handler')
+      .on('change.views-rearrange-filter-handler', $.proxy(this, 'redrawOperatorLabels'));
+
+    // Bind handlers so that when a "Remove" link is clicked, we:
+    // - Update the rowspans of cells containing an operator dropdown (since
+    //   they need to change to reflect the number of rows in each group).
+    // - Redraw the operator labels next to the filters in the group (since the
+    //   filter that is currently displayed last in each group is not supposed
+    //   to have a label display next to it).
+    $table.find('a.views-groups-remove-link')
+      .once('views-rearrange-filter-handler')
+      .on('click.views-rearrange-filter-handler', $.proxy(this, 'updateRowspans'))
+      .on('click.views-rearrange-filter-handler', $.proxy(this, 'redrawOperatorLabels'));
+  };
+
+  $.extend(Drupal.viewsUi.RearrangeFilterHandler.prototype, /** @lends Drupal.viewsUi.RearrangeFilterHandler# */{
+
+    /**
+     * Insert links that allow filter groups to be added and removed.
+     */
+    insertAddRemoveFilterGroupLinks: function () {
+
+      // Insert a link for adding a new group at the top of the page, and make
+      // it match the action link styling used in a typical page.html.twig.
+      // Since Drupal does not provide a theme function for this markup this is
+      // the best we can do.
+      $('<ul class="action-links"><li><a id="views-add-group-link" href="#">' + this.addGroupButton.val() + '</a></li></ul>')
+        .prependTo(this.table.parent())
+        // When the link is clicked, dynamically click the hidden form button
+        // for adding a new filter group.
+        .once('views-rearrange-filter-handler')
+        .on('click.views-rearrange-filter-handler', $.proxy(this, 'clickAddGroupButton'));
+
+      // Find each (visually hidden) button for removing a filter group and
+      // insert a link next to it.
+      var length = this.removeGroupButtons.length;
+      var i;
+      for (i = 0; i < length; i++) {
+        var $removeGroupButton = $(this.removeGroupButtons[i]);
+        var buttonId = $removeGroupButton.attr('id');
+        $('<a href="#" class="views-remove-group-link">' + Drupal.t('Remove group') + '</a>')
+          .insertBefore($removeGroupButton)
+          // When the link is clicked, dynamically click the corresponding form
+          // button.
+          .once('views-rearrange-filter-handler')
+          .on('click.views-rearrange-filter-handler', {buttonId: buttonId}, $.proxy(this, 'clickRemoveGroupButton'));
+      }
+    },
+
+    /**
+     * Dynamically click the button that adds a new filter group.
+     *
+     * @param {jQuery.Event} event
+     *   The event triggered.
+     */
+    clickAddGroupButton: function (event) {
+      // Due to conflicts between Drupal core's AJAX system and the Views AJAX
+      // system, the only way to get this to work seems to be to trigger both
+      // the mousedown and submit events.
+      this.addGroupButton
+        .trigger('mousedown')
+        .trigger('submit');
+      event.preventDefault();
+    },
+
+    /**
+     * Dynamically click a button for removing a filter group.
+     *
+     * @param {jQuery.Event} event
+     *   Event being triggered, with event.data.buttonId set to the ID of the
+     *   form button that should be clicked.
+     */
+    clickRemoveGroupButton: function (event) {
+      this.table.find('#' + event.data.buttonId).trigger('mousedown').trigger('submit');
+      event.preventDefault();
+    },
+
+    /**
+     * Move the groups operator so that it's between the first two groups, and
+     * duplicate it between any subsequent groups.
+     *
+     * @return {jQuery}
+     *   An operator element.
+     */
+    duplicateGroupsOperator: function () {
+      var dropdowns;
+      var newRow;
+      var titleRow;
+
+      var titleRows = $('tr.views-group-title').once('duplicateGroupsOperator');
+
+      if (!titleRows.length) {
+        return this.operator;
+      }
+
+      // Get rid of the explanatory text around the operator; its placement is
+      // explanatory enough.
+      this.operator.find('label').add('div.description').addClass('visually-hidden');
+      this.operator.find('select').addClass('form-select');
+
+      // Keep a list of the operator dropdowns, so we can sync their behavior
+      // later.
+      dropdowns = this.operator;
+
+      // Move the operator to a new row just above the second group.
+      titleRow = $('tr#views-group-title-2');
+      newRow = $('<tr class="filter-group-operator-row"><td colspan="5"></td></tr>');
+      newRow.find('td').append(this.operator);
+      newRow.insertBefore(titleRow);
+      var length = titleRows.length;
+      // Starting with the third group, copy the operator to a new row above the
+      // group title.
+      for (var i = 2; i < length; i++) {
+        titleRow = $(titleRows[i]);
+        // Make a copy of the operator dropdown and put it in a new table row.
+        var fakeOperator = this.operator.clone();
+        fakeOperator.attr('id', '');
+        newRow = $('<tr class="filter-group-operator-row"><td colspan="5"></td></tr>');
+        newRow.find('td').append(fakeOperator);
+        newRow.insertBefore(titleRow);
+        dropdowns = dropdowns.add(fakeOperator);
+      }
+
+      return dropdowns;
+    },
+
+    /**
+     * Make the duplicated groups operators change in sync with each other.
+     */
+    syncGroupsOperators: function () {
+      if (this.dropdowns.length < 2) {
+        // We only have one dropdown (or none at all), so there's nothing to
+        // sync.
+        return;
+      }
+
+      this.dropdowns.on('change', $.proxy(this, 'operatorChangeHandler'));
+    },
+
+    /**
+     * Click handler for the operators that appear between filter groups.
+     *
+     * Forces all operator dropdowns to have the same value.
+     *
+     * @param {jQuery.Event} event
+     *   The event triggered.
+     */
+    operatorChangeHandler: function (event) {
+      var $target = $(event.target);
+      var operators = this.dropdowns.find('select').not($target);
+
+      // Change the other operators to match this new value.
+      operators.val($target.val());
+    },
+
+    /**
+     * @method
+     */
+    modifyTableDrag: function () {
+      var tableDrag = Drupal.tableDrag['views-rearrange-filters'];
+      var filterHandler = this;
+
+      /**
+       * Override the row.onSwap method from tabledrag.js.
+       *
+       * When a row is dragged to another place in the table, several things
+       * need to occur.
+       * - The row needs to be moved so that it's within one of the filter
+       * groups.
+       * - The operator cells that span multiple rows need their rowspan
+       * attributes updated to reflect the number of rows in each group.
+       * - The operator labels that are displayed next to each filter need to
+       * be redrawn, to account for the row's new location.
+       */
+      tableDrag.row.prototype.onSwap = function () {
+        if (filterHandler.hasGroupOperator) {
+          // Make sure the row that just got moved (this.group) is inside one
+          // of the filter groups (i.e. below an empty marker row or a
+          // draggable). If it isn't, move it down one.
+          var thisRow = $(this.group);
+          var previousRow = thisRow.prev('tr');
+          if (previousRow.length && !previousRow.hasClass('group-message') && !previousRow.hasClass('draggable')) {
+            // Move the dragged row down one.
+            var next = thisRow.next();
+            if (next.is('tr')) {
+              this.swap('after', next);
+            }
+          }
+          filterHandler.updateRowspans();
+        }
+        // Redraw the operator labels that are displayed next to each filter, to
+        // account for the row's new location.
+        filterHandler.redrawOperatorLabels();
+      };
+
+      /**
+       * Override the onDrop method from tabledrag.js.
+       */
+      tableDrag.onDrop = function () {
+        // If the tabledrag change marker (i.e., the "*") has been inserted
+        // inside a row after the operator label (i.e., "And" or "Or")
+        // rearrange the items so the operator label continues to appear last.
+        var changeMarker = $(this.oldRowElement).find('.tabledrag-changed');
+        if (changeMarker.length) {
+          // Search for occurrences of the operator label before the change
+          // marker, and reverse them.
+          var operatorLabel = changeMarker.prevAll('.views-operator-label');
+          if (operatorLabel.length) {
+            operatorLabel.insertAfter(changeMarker);
+          }
+        }
+
+        // Make sure the "group" dropdown is properly updated when rows are
+        // dragged into an empty filter group. This is borrowed heavily from
+        // the block.js implementation of tableDrag.onDrop().
+        var groupRow = $(this.rowObject.element).prevAll('tr.group-message').get(0);
+        var groupName = groupRow.className.replace(/([^ ]+[ ]+)*group-([^ ]+)-message([ ]+[^ ]+)*/, '$2');
+        var groupField = $('select.views-group-select', this.rowObject.element);
+        if ($(this.rowObject.element).prev('tr').is('.group-message') && !groupField.is('.views-group-select-' + groupName)) {
+          var oldGroupName = groupField.attr('class').replace(/([^ ]+[ ]+)*views-group-select-([^ ]+)([ ]+[^ ]+)*/, '$2');
+          groupField.removeClass('views-group-select-' + oldGroupName).addClass('views-group-select-' + groupName);
+          groupField.val(groupName);
+        }
+      };
+    },
+
+    /**
+     * Redraw the operator labels that are displayed next to each filter.
+     */
+    redrawOperatorLabels: function () {
+      for (var i = 0; i < this.draggableRows.length; i++) {
+        // Within the row, the operator labels are displayed inside the first
+        // table cell (next to the filter name).
+        var $draggableRow = $(this.draggableRows[i]);
+        var $firstCell = $draggableRow.find('td').eq(0);
+        if ($firstCell.length) {
+          // The value of the operator label ("And" or "Or") is taken from the
+          // first operator dropdown we encounter, going backwards from the
+          // current row. This dropdown is the one associated with the current
+          // row's filter group.
+          var operatorValue = $draggableRow.prevAll('.views-group-title').find('option:selected').html();
+          var operatorLabel = '<span class="views-operator-label">' + operatorValue + '</span>';
+          // If the next visible row after this one is a draggable filter row,
+          // display the operator label next to the current row. (Checking for
+          // visibility is necessary here since the "Remove" links hide the
+          // removed row but don't actually remove it from the document).
+          var $nextRow = $draggableRow.nextAll(':visible').eq(0);
+          var $existingOperatorLabel = $firstCell.find('.views-operator-label');
+          if ($nextRow.hasClass('draggable')) {
+            // If an operator label was already there, replace it with the new
+            // one.
+            if ($existingOperatorLabel.length) {
+              $existingOperatorLabel.replaceWith(operatorLabel);
+            }
+            // Otherwise, append the operator label to the end of the table
+            // cell.
+            else {
+              $firstCell.append(operatorLabel);
+            }
+          }
+          // If the next row doesn't contain a filter, then this is the last row
+          // in the group. We don't want to display the operator there (since
+          // operators should only display between two related filters, e.g.
+          // "filter1 AND filter2 AND filter3"). So we remove any existing label
+          // that this row has.
+          else {
+            $existingOperatorLabel.remove();
+          }
+        }
+      }
+    },
+
+    /**
+     * Update the rowspan attribute of each cell containing an operator
+     * dropdown.
+     */
+    updateRowspans: function () {
+      var $row;
+      var $currentEmptyRow;
+      var draggableCount;
+      var $operatorCell;
+      var rows = $(this.table).find('tr');
+      var length = rows.length;
+      for (var i = 0; i < length; i++) {
+        $row = $(rows[i]);
+        if ($row.hasClass('views-group-title')) {
+          // This row is a title row.
+          // Keep a reference to the cell containing the dropdown operator.
+          $operatorCell = $row.find('td.group-operator');
+          // Assume this filter group is empty, until we find otherwise.
+          draggableCount = 0;
+          $currentEmptyRow = $row.next('tr');
+          $currentEmptyRow.removeClass('group-populated').addClass('group-empty');
+          // The cell with the dropdown operator should span the title row and
+          // the "this group is empty" row.
+          $operatorCell.attr('rowspan', 2);
+        }
+        else if ($row.hasClass('draggable') && $row.is(':visible')) {
+          // We've found a visible filter row, so we now know the group isn't
+          // empty.
+          draggableCount++;
+          $currentEmptyRow.removeClass('group-empty').addClass('group-populated');
+          // The operator cell should span all draggable rows, plus the title.
+          $operatorCell.attr('rowspan', draggableCount + 1);
+        }
+      }
+    }
+  });
+
+  /**
+   * Add a select all checkbox, which checks each checkbox at once.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches select all functionality to the views filter form.
+   */
+  Drupal.behaviors.viewsFilterConfigSelectAll = {
+    attach: function (context) {
+      var $context = $(context);
+
+      var $selectAll = $context.find('.js-form-item-options-value-all').once('filterConfigSelectAll');
+      var $selectAllCheckbox = $selectAll.find('input[type=checkbox]');
+      var $checkboxes = $selectAll.closest('.form-checkboxes').find('.js-form-type-checkbox:not(.js-form-item-options-value-all) input[type="checkbox"]');
+
+      if ($selectAll.length) {
+         // Show the select all checkbox.
+        $selectAll.show();
+        $selectAllCheckbox.on('click', function () {
+          // Update all checkbox beside the select all checkbox.
+          $checkboxes.prop('checked', $(this).is(':checked'));
+        });
+
+        // Uncheck the select all checkbox if any of the others are unchecked.
+        $checkboxes.on('click', function () {
+          if ($(this).is('checked') === false) {
+            $selectAllCheckbox.prop('checked', false);
+          }
+        });
+      }
+    }
+  };
+
+  /**
+   * Remove icon class from elements that are themed as buttons or dropbuttons.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Removes the icon class from certain views elements.
+   */
+  Drupal.behaviors.viewsRemoveIconClass = {
+    attach: function (context) {
+      $(context).find('.dropbutton').once('dropbutton-icon').find('.icon').removeClass('icon');
+    }
+  };
+
+  /**
+   * Change "Expose filter" buttons into checkboxes.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Changes buttons into checkboxes via {@link Drupal.viewsUi.Checkboxifier}.
+   */
+  Drupal.behaviors.viewsUiCheckboxify = {
+    attach: function (context, settings) {
+      var $buttons = $('[data-drupal-selector="edit-options-expose-button-button"], [data-drupal-selector="edit-options-group-button-button"]').once('views-ui-checkboxify');
+      var length = $buttons.length;
+      var i;
+      for (i = 0; i < length; i++) {
+        new Drupal.viewsUi.Checkboxifier($buttons[i]);
+      }
+    }
+  };
+
+  /**
+   * Change the default widget to select the default group according to the
+   * selected widget for the exposed group.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Changes the default widget based on user input.
+   */
+  Drupal.behaviors.viewsUiChangeDefaultWidget = {
+    attach: function (context) {
+      var $context = $(context);
+
+      function changeDefaultWidget(event) {
+        if ($(event.target).prop('checked')) {
+          $context.find('input.default-radios').parent().hide();
+          $context.find('td.any-default-radios-row').parent().hide();
+          $context.find('input.default-checkboxes').parent().show();
+        }
+        else {
+          $context.find('input.default-checkboxes').parent().hide();
+          $context.find('td.any-default-radios-row').parent().show();
+          $context.find('input.default-radios').parent().show();
+        }
+      }
+
+      // Update on widget change.
+      $context.find('input[name="options[group_info][multiple]"]')
+        .on('change', changeDefaultWidget)
+        // Update the first time the form is rendered.
+        .trigger('change');
+    }
+  };
+
+  /**
+   * Attaches expose filter button to a checkbox that triggers its click event.
+   *
+   * @constructor
+   *
+   * @param {HTMLElement} button
+   *   The DOM object representing the button to be checkboxified.
+   */
+  Drupal.viewsUi.Checkboxifier = function (button) {
+    this.$button = $(button);
+    this.$parent = this.$button.parent('div.views-expose, div.views-grouped');
+    this.$input = this.$parent.find('input:checkbox, input:radio');
+    // Hide the button and its description.
+    this.$button.hide();
+    this.$parent.find('.exposed-description, .grouped-description').hide();
+
+    this.$input.on('click', $.proxy(this, 'clickHandler'));
+
+  };
+
+  /**
+   * When the checkbox is checked or unchecked, simulate a button press.
+   *
+   * @param {jQuery.Event} e
+   *   The event triggered.
+   */
+  Drupal.viewsUi.Checkboxifier.prototype.clickHandler = function (e) {
+    this.$button
+      .trigger('click')
+      .trigger('submit');
+  };
+
+  /**
+   * Change the Apply button text based upon the override select state.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches behavior to change the Apply button according to the current
+   *   state.
+   */
+  Drupal.behaviors.viewsUiOverrideSelect = {
+    attach: function (context) {
+      $(context).find('[data-drupal-selector="edit-override-dropdown"]').once('views-ui-override-button-text').each(function () {
+        // Closures! :(
+        var $context = $(context);
+        var $submit = $context.find('[id^=edit-submit]');
+        var old_value = $submit.val();
+
+        $submit.once('views-ui-override-button-text')
+          .on('mouseup', function () {
+            $(this).val(old_value);
+            return true;
+          });
+
+        $(this).on('change', function () {
+          var $this = $(this);
+          if ($this.val() === 'default') {
+            $submit.val(Drupal.t('Apply (all displays)'));
+          }
+          else if ($this.val() === 'default_revert') {
+            $submit.val(Drupal.t('Revert to default'));
+          }
+          else {
+            $submit.val(Drupal.t('Apply (this display)'));
+          }
+          var $dialog = $context.closest('.ui-dialog-content');
+          $dialog.trigger('dialogButtonsChange');
+        })
+          .trigger('change');
+      });
+
+    }
+  };
+
+  /**
+   * Functionality for the remove link in the views UI.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches behavior for the remove view and remove display links.
+   */
+  Drupal.behaviors.viewsUiHandlerRemoveLink = {
+    attach: function (context) {
+      var $context = $(context);
+      // Handle handler deletion by looking for the hidden checkbox and hiding
+      // the row.
+      $context.find('a.views-remove-link').once('views').on('click', function (event) {
+        var id = $(this).attr('id').replace('views-remove-link-', '');
+        $context.find('#views-row-' + id).hide();
+        $context.find('#views-removed-' + id).prop('checked', true);
+        event.preventDefault();
+      });
+
+      // Handle display deletion by looking for the hidden checkbox and hiding
+      // the row.
+      $context.find('a.display-remove-link').once('display').on('click', function (event) {
+        var id = $(this).attr('id').replace('display-remove-link-', '');
+        $context.find('#display-row-' + id).hide();
+        $context.find('#display-removed-' + id).prop('checked', true);
+        event.preventDefault();
+      });
+    }
+  };
+
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/themes/stable/js/views_ui/views_ui.listing.js b/core/themes/stable/js/views_ui/views_ui.listing.js
new file mode 100644
index 0000000..ee2199f
--- /dev/null
+++ b/core/themes/stable/js/views_ui/views_ui.listing.js
@@ -0,0 +1,54 @@
+/**
+ * @file
+ * Views listing behaviors.
+ */
+
+(function ($, Drupal) {
+
+  "use strict";
+
+  /**
+   * Filters the view listing tables by a text input search string.
+   *
+   * Text search input: input.views-filter-text
+   * Target table:      input.views-filter-text[data-table]
+   * Source text:       .views-table-filter-text-source
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches the filter functionality to the views admin text search field.
+   */
+  Drupal.behaviors.viewTableFilterByText = {
+    attach: function (context, settings) {
+      var $input = $('input.views-filter-text').once('views-filter-text');
+      var $table = $($input.attr('data-table'));
+      var $rows;
+
+      function filterViewList(e) {
+        var query = $(e.target).val().toLowerCase();
+
+        function showViewRow(index, row) {
+          var $row = $(row);
+          var $sources = $row.find('.views-table-filter-text-source');
+          var textMatch = $sources.text().toLowerCase().indexOf(query) !== -1;
+          $row.closest('tr').toggle(textMatch);
+        }
+
+        // Filter if the length of the query is at least 2 characters.
+        if (query.length >= 2) {
+          $rows.each(showViewRow);
+        }
+        else {
+          $rows.show();
+        }
+      }
+
+      if ($table.length) {
+        $rows = $table.find('tbody tr');
+        $input.on('keyup', filterViewList);
+      }
+    }
+  };
+
+}(jQuery, Drupal));
diff --git a/core/themes/stable/stable.info.yml b/core/themes/stable/stable.info.yml
new file mode 100644
index 0000000..043be1e
--- /dev/null
+++ b/core/themes/stable/stable.info.yml
@@ -0,0 +1,519 @@
+name: Stable
+type: theme
+description: 'Shelter from the Wild Wild West.'
+package: Core
+version: VERSION
+core: 8.x
+base theme: false
+
+libraries-override:
+  block/drupal.block:
+    js:
+      js/block.js: js/block/block.js
+  block/drupal.block.admin:
+    js:
+      js/block.admin.js: js/block/block.admin.js
+    css:
+      theme:
+        css/block.admin.css: css/block/block.admin.css
+
+  block_content/drupal.block_content:
+    js:
+      js/block_content.js: js/block_content/block_content.js
+
+  book/drupal.book:
+    js:
+      book.js: js/book/book.js
+
+  ckeditor/drupal.ckeditor:
+    js:
+      js/ckeditor.js: js/ckeditor/ckeditor.js
+    css:
+      state:
+        css/ckeditor.css: css/ckeditor/ckeditor.css
+  ckeditor/drupal.ckeditor.plugins.drupalimagecaption:
+    css:
+      component:
+        css/plugins/drupalimagecaption/ckeditor.drupalimagecaption.css: css/ckeditor/plugins/drupalimagecaption/ckeditor.drupalimagecaption.css
+  ckeditor/drupal.ckeditor.admin:
+    js:
+      js/ckeditor.admin.js: js/ckeditor/ckeditor.admin.js
+      js/models/Model.js: js/ckeditor/models/Model.js
+      js/views/AuralView.js: js/ckeditor/views/AuralView.js
+      js/views/KeyboardView.js: js/ckeditor/views/KeyboardView.js
+      js/views/ControllerView.js: js/ckeditor/views/ControllerView.js
+      js/views/VisualView.js: js/ckeditor/views/VisualView.js
+    css:
+      theme:
+        css/ckeditor.admin.css: css/ckeditor/ckeditor.admin.css
+  ckeditor/drupal.ckeditor.drupalimage.admin:
+    js:
+      js/ckeditor.drupalimage.admin.js: js/ckeditor/ckeditor.drupalimage.admin.js
+  ckeditor/drupal.ckeditor.stylescombo.admin:
+    js:
+      js/ckeditor.stylescombo.admin.js: js/ckeditor/ckeditor.stylescombo.admin.js
+
+  color/drupal.color:
+    js:
+      color.js: js/color/color.js
+  color/drupal.color.preview:
+    js:
+      preview.js: js/color/preview.js
+  color/admin:
+    css:
+      theme:
+        css/color.admin.css: css/color/color.admin.css
+
+  comment/drupal.comment:
+    js:
+      comment-entity-form.js: js/comment/comment-entity-form.js
+  comment/drupal.comment-by-viewer:
+    js:
+      js/comment-by-viewer.js: js/comment/comment-by-viewer.js
+  comment/drupal.comment-new-indicator:
+    js:
+      js/comment-new-indicator.js: js/comment/comment-new-indicator.js
+  comment/drupal.node-new-comments-link:
+    js:
+      js/node-new-comments-link.js: js/comment/node-new-comments-link.js
+
+  config_translation/drupal.config_translation.admin:
+    css:
+      theme:
+        css/config_translation.admin.css: css/config_translation/config_translation.admin.css
+
+  content_translation/drupal.content_translation.admin:
+    js:
+      content_translation.admin.js: js/content_translation/content_translation.admin.js
+    css:
+      theme:
+        css/content_translation.admin.css: css/content_translation/content_translation.admin.css
+
+  contextual/drupal.contextual-links:
+    js:
+      js/contextual.js: js/contextual/contextual.js
+      js/models/StateModel.js: js/contextual/models/StateModel.js
+      js/views/AuralView.js: js/contextual/views/AuralView.js
+      js/views/KeyboardView.js: js/contextual/views/KeyboardView.js
+      js/views/RegionView.js: js/contextual/views/RegionView.js
+      js/views/VisualView.js: js/contextual/views/VisualView.js
+    css:
+      component:
+        css/contextual.module.css: css/contextual/contextual.module.css
+      theme:
+        css/contextual.theme.css: css/contextual/contextual.theme.css
+        css/contextual.icons.theme.css: css/contextual/contextual.icons.theme.css
+  contextual/drupal.contextual-toolbar:
+    js:
+      js/contextual.toolbar.js: js/contextual/contextual.toolbar.js
+      js/toolbar/models/StateModel.js: js/contextual/toolbar/models/StateModel.js
+      js/toolbar/views/AuralView.js: js/contextual/toolbar/views/AuralView.js
+      js/toolbar/views/VisualView.js: js/contextual/toolbar/views/VisualView.js
+    css:
+      component:
+        css/contextual.toolbar.css: css/contextual/contextual.toolbar.css
+
+  core/drupal:
+    js:
+      misc/drupal.js: js/core/drupal.js
+  core/drupalSettings:
+    js:
+      misc/drupalSettingsLoader.js: js/core/drupalSettingsLoader.js
+  core/drupal.active-link:
+    js:
+      misc/active-link.js: js/core/active-link.js
+  core/drupal.ajax:
+    js:
+      misc/ajax.js: js/core/ajax.js
+  core/drupal.announce:
+    js:
+      misc/announce.js: js/core/announce.js
+  core/drupal.autocomplete:
+    js:
+      misc/autocomplete.js: js/core/autocomplete.js
+  core/drupal.batch:
+    js:
+      misc/batch.js: js/core/batch.js
+  core/drupal.collapse:
+    js:
+      misc/details-aria.js: js/core/details-aria.js
+      misc/collapse.js: js/core/collapse.js
+  core/drupal.date:
+    js:
+      misc/date.js: js/core/date.js
+  core/drupal.debounce:
+    js:
+      misc/debounce.js: js/core/debounce.js
+  core/drupal.dialog:
+    js:
+      misc/dialog/dialog.js: js/core/dialog/dialog.js
+      misc/dialog/dialog.position.js: js/core/dialog/dialog.position.js
+      misc/dialog/dialog.jquery-ui.js: js/core/dialog/dialog.jquery-ui.js
+  core/drupal.dialog.ajax:
+    js:
+      misc/dialog/dialog.ajax.js: js/core/dialog/dialog.ajax.js
+  core/drupal.displace:
+    js:
+      misc/displace.js: js/core/displace.js
+  core/drupal.dropbutton:
+    js:
+      misc/dropbutton/dropbutton.js: js/core/dropbutton/dropbutton.js
+    css:
+      component:
+        core/dropbutton/dropbutton.css: css/core/dropbutton/dropbutton.css
+  core/drupal.form:
+    js:
+      misc/form.js: js/core/form.js
+  core/drupal.machine-name:
+    js:
+      misc/machine-name.js: js/core/machine-name.js
+  core/drupal.progress:
+    js:
+      misc/progress.js: js/core/progress.js
+  core/drupal.states:
+    js:
+      misc/states.js: js/core/states.js
+  core/drupal.tabbingmanager:
+    js:
+      misc/tabbingmanager.js: js/core/tabbingmanager.js
+  core/drupal.tabledrag:
+    js:
+      misc/tabledrag.js: js/core/tabledrag.js
+  core/drupal.tableheader:
+    js:
+      misc/tableheader.js: js/core/tableheader.js
+  core/drupal.tableresponsive:
+    js:
+      misc/tableresponsive.js: js/core/tableresponsive.js
+  core/drupal.tableselect:
+    js:
+      misc/tableselect.js: js/core/tableselect.js
+  core/drupal.timezone:
+    js:
+      misc/timezone.js: js/core/timezone.js
+  core/drupal.vertical-tabs:
+    js:
+      misc/vertical-tabs.js: js/core/vertical-tabs.js
+    css:
+      component:
+        core/vertical-tabs.css: js/core/vertical-tabs.css
+
+  dblog/drupal.dblog:
+    css:
+      component:
+        css/dblog.module.css: css/dblog/dblog.module.css
+
+  editor/drupal.editor.admin:
+    js:
+      js/editor.admin.js: js/editor/editor.admin.js
+  editor/drupal.editor:
+    js:
+      js/editor.js: js/editor/editor.js
+    css:
+      component:
+        css/editor.css: css/editor/editor.css
+  editor/drupal.editor.dialog:
+    js:
+      js/editor.dialog.js: js/editor/editor.dialog.js
+  editor/quickedit.inPlaceEditor.formattedText:
+    js:
+      js/editor.formattedTextEditor.js: js/editor/editor.formattedTextEditor.js
+
+  field_ui/drupal.field_ui:
+    js:
+      field_ui.js: js/field_ui/field_ui.js
+    css:
+      theme:
+        css/field_ui.admin.css: css/field_ui/field_ui.admin.css
+
+  file/drupal.file:
+    js:
+      file.js: js/file/file.js
+    css:
+      theme:
+        css/file.admin.css: css/file/file.admin.css
+      component:
+        # @todo File follow-up to Voltron patch to fix this in file.libraries.yml.
+        css/file.theme.css: css/file/file.theme.css
+
+  filter/caption:
+    css:
+      component:
+        css/filter.caption.css: css/filter/filter.caption.css
+  filter/drupal.filter.admin:
+    js:
+      filter.admin.js: js/filter/filter.admin.js
+    css:
+      theme:
+        css/filter.admin.css: css/filter/filter.admin.css
+  filter/drupal.filter.filter_html.admin:
+    js:
+      filter.filter_html.admin.js: js/filter/filter.filter_html.admin.js
+  filter/drupal.filter:
+    js:
+      filter.js: js/filter/filter.js
+    css:
+      theme:
+        css/filter.admin.css: css/filter/filter.admin.css
+  filter/caption:
+    css:
+      component:
+        css/filter.caption.css: css/filter/filter.caption.css
+
+  history/api:
+    js:
+      js/history.js: js/history/history.js
+  history/mark-as-read:
+    js:
+      js/mark-as-read.js: js/history/mark-as-read.js
+
+  image/admin:
+    css:
+      theme:
+        css/image.admin.css: css/image/image.admin.css
+
+  language/drupal.language.admin:
+    js:
+      language.admin.js: js/language/language.admin.js
+    css:
+      theme:
+        css/language.admin.css: css/language/language.admin.css
+
+  locale/drupal.locale.admin:
+    js:
+      locale.admin.js: js/locale/locale.admin.js
+    css:
+      component:
+        css/locale.admin.css: css/locale/locale.admin.css
+  locale/drupal.locale.datepicker:
+    js:
+      locale.datepicker.js: js/locale/locale.datepicker.js
+
+  menu_ui/drupal.menu_ui:
+    js:
+      menu_ui.js: js/menu_ui/menu_ui.js
+  menu_ui/drupal.menu_ui.admin:
+    js:
+      menu_ui.admin.js: js/menu_ui/menu_ui.admin.js
+  menu_ui/drupal.menu_ui.adminforms:
+    css:
+      theme:
+        css/menu_ui.admin.css: css/menu_ui/menu_ui.admin.css
+
+  node/drupal.node:
+    css:
+      layout:
+        css/node.module.css: css/node/node.module.css
+    js:
+      node.js: js/node/node.js
+  node/drupal.node.preview:
+    css:
+      theme:
+        css/node.preview.css: css/node/node.preview.css
+    js:
+      node.preview.js: js/node/node.preview.js
+  node/drupal.content_types:
+    js:
+      content_types.js: js/node/content_types.js
+  node/form:
+    css:
+      layout:
+        css/node.module.css: css/node/node.module.css
+  node/drupal.node.admin:
+    css:
+      theme:
+        css/node.admin.css: css/node/node.admin.css
+
+  path/drupal.path:
+    js:
+      path.js: js/path/path.js
+
+  quickedit/quickedit:
+    js:
+      js/quickedit.js: js/quickedit/quickedit.js
+      js/util.js: js/quickedit/util.js
+      js/models/BaseModel.js: js/quickedit/models/BaseModel.js
+      js/models/AppModel.js: js/quickedit/models/AppModel.js
+      js/models/EntityModel.js: js/quickedit/models/EntityModel.js
+      js/models/FieldModel.js: js/quickedit/models/FieldModel.js
+      js/models/EditorModel.js: js/quickedit/models/EditorModel.js
+      js/views/AppView.js: js/quickedit/views/AppView.js
+      js/views/FieldDecorationView.js: js/quickedit/views/FieldDecorationView.js
+      js/views/EntityDecorationView.js: js/quickedit/views/EntityDecorationView.js
+      js/views/EntityToolbarView.js: js/quickedit/views/EntityToolbarView.js
+      js/views/ContextualLinkView.js: js/quickedit/views/ContextualLinkView.js
+      js/views/FieldToolbarView.js: js/quickedit/views/FieldToolbarView.js
+      js/views/EditorView.js: js/quickedit/views/EditorView.js
+      js/theme.js: js/quickedit/theme.js
+    css:
+      component:
+        css/quickedit.module.css: css/quickedit/quickedit.module.css
+      theme:
+        css/quickedit.theme.css: css/quickedit/quickedit.theme.css
+        css/quickedit.icons.theme.css: css/quickedit/quickedit.icons.theme.css
+  quickedit/quickedit.inPlaceEditor.form:
+    js:
+      js/editors/formEditor.js: js/quickedit/editors/formEditor.js
+  quickedit/quickedit.inPlaceEditor.plainText:
+    js:
+      js/editors/plainTextEditor.js: js/quickedit/editors/plainTextEditor.js
+
+  responsive_image/ajax:
+    js:
+      js/responsive_image.ajax.js: js/responsive_image/responsive_image.ajax.js
+
+  shortcut/drupal.shortcut:
+    css:
+      theme:
+        css/shortcut.theme.css: css/shortcut/shortcut.theme.css
+        css/shortcut.icons.theme.css: css/shortcut/shortcut.icons.theme.css
+
+  simpletest/drupal.simpletest:
+    js:
+      simpletest.js: js/simpletest/simpletest.js
+    css:
+      component:
+        css/simpletest.module.css: css/simpletest/simpletest.module.css
+
+  statistics/drupal.statistics:
+    js:
+      statistics.js: js/statistics/statistics.js
+
+  system/base:
+    css:
+      component:
+        css/components/ajax-progress.module.css: css/system/components/ajax-progress.module.css
+        css/components/align.module.css: css/system/components/align.module.css
+        css/components/autocomplete-loading.module.css: css/system/components/autocomplete-loading.module.css
+        css/components/fieldgroup.module.css: css/system/components/fieldgroup.module.css
+        css/components/container-inline.module.css: css/system/components/container-inline.module.css
+        css/components/clearfix.module.css: css/system/components/clearfix.module.css
+        css/components/details.module.css: css/system/components/details.module.css
+        css/components/hidden.module.css: css/system/components/hidden.module.css
+        css/components/js.module.css: css/system/components/js.module.css
+        css/components/nowrap.module.css: css/system/components/nowrap.module.css
+        css/components/position-container.module.css: css/system/components/position-container.module.css
+        css/components/progress.module.css: css/system/components/progress.module.css
+        css/components/reset-appearance.module.css: css/system/components/reset-appearance.module.css
+        css/components/resize.module.css: css/system/components/resize.module.css
+        css/components/sticky-header.module.css: css/system/components/sticky-header.module.css
+        css/components/tabledrag.module.css: css/system/components/tabledrag.module.css
+        css/components/tablesort.module.css: css/system/components/tablesort.module.css
+        css/components/tree-child.module.css: css/system/components/tree-child.module.css
+  system/admin:
+    css:
+      theme:
+        css/system.admin.css: css/system/system.admin.css
+  system/maintenance:
+    css:
+      theme:
+        css/system.maintenance.css: css/system/system.maintenance.css
+  system/drupal.system:
+    js:
+      js/system.js: js/system/system.js
+  system/drupal.system.modules:
+    js:
+      js/system.modules.js: js/system/system.modules.js
+  system/diff:
+    css:
+      component:
+        css/system.diff.css: css/system/system.diff.css
+  system/drupal.system.date:
+    js:
+      js/system.date.js: js/system/system.date.js
+
+  taxonomy/drupal.taxonomy:
+    js:
+      taxonomy.js: js/taxonomy/taxonomy.js
+    css:
+      component:
+        css/taxonomy.theme.css: css/taxonomy/taxonomy.theme.css
+
+  text/drupal.text:
+    js:
+      text.js: js/text/text.js
+
+  toolbar/toolbar:
+    js:
+      js/toolbar.js: js/toolbar/toolbar.js
+      js/models/MenuModel.js: js/toolbar/models/MenuModel.js
+      js/models/ToolbarModel.js: js/toolbar/models/ToolbarModel.js
+      js/views/BodyVisualView.js: js/toolbar/views/BodyVisualView.js
+      js/views/MenuVisualView.js: js/toolbar/views/MenuVisualView.js
+      js/views/ToolbarAuralView.js: js/toolbar/views/ToolbarAuralView.js
+      js/views/ToolbarVisualView.js: js/toolbar/views/ToolbarVisualView.js
+    css:
+      component:
+        css/toolbar.module.css: css/toolbar/toolbar.module.css
+      theme:
+        css/toolbar.theme.css: css/toolbar/toolbar.theme.css
+        css/toolbar.icons.theme.css: css/toolbar/toolbar.icons.theme.css
+  toolbar/toolbar.menu:
+    js:
+      js/toolbar.menu.js: js/toolbar/toolbar.menu.js
+    css:
+      state:
+        css/toolbar.menu.css: css/toolbar/toolbar.menu.css
+  toolbar/toolbar.escapeAdmin:
+    js:
+      js/escapeAdmin.js: js/toolbar/escapeAdmin.js
+
+  tour/tour:
+    js:
+      js/tour.js: js/tour/tour.js
+  tour/tour-styling:
+    css:
+      component:
+        css/tour.module.css: css/tour/tour.module.css
+
+  tracker/history:
+    js:
+      js/tracker-history.js: js/tracker/tracker-history.js
+
+  update/drupal.update.admin:
+    css:
+      theme:
+        css/update.admin.theme.css: css/update/update.admin.theme.css
+
+  user/drupal.user:
+    js:
+      user.js: js/user/user.js
+    css:
+      component:
+        css/user.module.css: css/user/user.module.css
+  user/drupal.user.admin:
+    css:
+      theme:
+        css/user.admin.css: css/user/user.admin.css
+  user/drupal.user.permissions:
+    js:
+      user.permissions.js: js/user/user.permissions.js
+  user/drupal.user.icons:
+    css:
+      theme:
+        css/user.icons.admin.css: css/user/user.icons.admin.css
+
+  views/views.module:
+    css:
+      component:
+        css/views.module.css: css/views/views.module.css
+  views/views.ajax:
+    js:
+      js/base.js: js/views/base.js
+      js/ajax_view.js: js/views/ajax_view.js
+
+  views_ui/views_ui.admin:
+    js:
+      js/ajax.js: js/views_ui/ajax.js
+      js/dialog.views.js: js/views_ui/dialog.views.js
+      js/views-admin.js: js/views_ui/views-admin.js
+  views_ui/views_ui.listing:
+    js:
+      js/views_ui.listing.js: js/views_ui/views_ui.listing.js
+  views_ui/admin.styling:
+    css:
+      component:
+        css/views_ui.admin.css: css/views_ui/views_ui.admin.css
+      theme:
+        css/views_ui.admin.theme.css: css/views_ui/views_ui.admin.theme.css
+        css/views_ui.contextual.css: css/views_ui/views_ui.contextual.css
diff --git a/core/themes/stable/stable.libraries.yml b/core/themes/stable/stable.libraries.yml
new file mode 100644
index 0000000..50ed7d6
--- /dev/null
+++ b/core/themes/stable/stable.libraries.yml
@@ -0,0 +1,1138 @@
+admin:
+  version: VERSION
+  css:
+    theme:
+      css/system/system.admin.css: { weight: -10 }
+  dependencies:
+    - stable/base
+
+base:
+  version: VERSION
+  css:
+    # Adjust the weights to load these early.
+    component:
+      css/system/components/ajax-progress.module.css: { weight: -10 }
+      css/system/components/align.module.css: { weight: -10 }
+      css/system/components/autocomplete-loading.module.css: { weight: -10 }
+      css/system/components/fieldgroup.module.css: { weight: -10 }
+      css/system/components/container-inline.module.css: { weight: -10 }
+      css/system/components/clearfix.module.css: { weight: -10 }
+      css/system/components/details.module.css: { weight: -10 }
+      css/system/components/hidden.module.css: { weight: -10 }
+      css/system/components/js.module.css: { weight: -10 }
+      css/system/components/nowrap.module.css: { weight: -10 }
+      css/system/components/position-container.module.css: { weight: -10 }
+      css/system/components/progress.module.css: { weight: -10 }
+      css/system/components/reset-appearance.module.css: { weight: -10 }
+      css/system/components/resize.module.css: { weight: -10 }
+      css/system/components/sticky-header.module.css: { weight: -10 }
+      css/system/components/tabledrag.module.css: { weight: -10 }
+      css/system/components/tablesort.module.css: { weight: -10 }
+      css/system/components/tree-child.module.css: { weight: -10 }
+
+caption:
+  version: VERSION
+  css:
+    component:
+      css/filter/filter.caption.css: {}
+
+color.admin:
+  version: VERSION
+  css:
+    theme:
+      css/color/color.admin.css: {}
+
+diff:
+  version: VERSION
+  css:
+    component:
+      css/system/system.diff.css: {}
+
+drupal:
+  version: VERSION
+  js:
+    js/misc/drupal.js: { weight: -18 }
+  dependencies:
+    - core/domready
+    - core/drupalSettings
+
+drupal.active-link:
+  version: VERSION
+  js:
+    js/misc/active-link.js: {}
+  dependencies:
+    - stable/drupal
+    - core/drupalSettings
+    - core/classList
+
+drupal.ajax:
+  version: VERSION
+  js:
+    js/misc/ajax.js: {}
+  drupalSettings:
+    # These placeholder values will be set by system_js_settings_alter().
+    ajaxPageState:
+      libraries: null
+      theme: null
+      theme_token: null
+    ajaxTrustedUrl: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/drupalSettings
+    - stable/drupal.progress
+    - core/jquery.once
+
+drupal.announce:
+  version: VERSION
+  js:
+    js/misc/announce.js: {}
+  dependencies:
+    - stable/drupal
+    - stable/drupal.debounce
+
+drupal.autocomplete:
+  version: VERSION
+  js:
+    js/misc/autocomplete.js: { weight: -1 }
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/drupalSettings
+    - stable/drupal.ajax
+    - core/jquery.ui.autocomplete
+
+drupal.batch:
+  version: VERSION
+  js:
+    js/misc/batch.js: { cache: false }
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/drupalSettings
+    - stable/drupal.ajax
+    - stable/drupal.progress
+    - core/jquery.once
+
+drupal.block:
+  version: VERSION
+  js:
+    js/block/block.js: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+
+drupal.block.admin:
+  version: VERSION
+  js:
+    js/block/block.admin.js: {}
+  css:
+    theme:
+      css/block/block.admin.css: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - stable/drupal.ajax
+
+drupal.block_content:
+  version: VERSION
+  js:
+    js/block_content/block_content.js: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - stable/drupal.form
+
+drupal.book:
+  version: VERSION
+  js:
+    js/book/book.js: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - stable/drupal.form
+
+drupal.ckeditor:
+  version: VERSION
+  js:
+    js/ckeditor/ckeditor.js: {}
+  css:
+    state:
+      css/ckeditor/ckeditor.css: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - stable/drupal.debounce
+    - core/ckeditor
+    - stable/drupal.editor
+
+drupal.ckeditor.plugins.drupalimagecaption:
+  version: VERSION
+  css:
+    component:
+      css/ckeditor/plugins/drupalimagecaption/ckeditor.drupalimagecaption.css: {}
+  dependencies:
+    - stable/caption
+
+drupal.ckeditor.admin:
+  version: VERSION
+  js:
+    # Core.
+    js/ckeditor/ckeditor.admin.js: {}
+    # Models.
+    js/ckeditor/models/Model.js: {}
+    # Views.
+    js/ckeditor/views/AuralView.js: {}
+    js/ckeditor/views/KeyboardView.js: {}
+    js/ckeditor/views/ControllerView.js: {}
+    js/ckeditor/views/VisualView.js: {}
+  css:
+    theme:
+      css/ckeditor/ckeditor.admin.css: {}
+      /core/assets/vendor/ckeditor/skins/moono/editor.css: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/drupalSettings
+    - core/jquery.once
+    - core/jquery.ui.sortable
+    - core/jquery.ui.draggable
+    - core/jquery.ui.touch-punch
+    - core/backbone
+    - stable/drupal.dialog
+    - stable/drupal.announce
+    - core/ckeditor
+    - stable/drupal.editor.admin
+    # Ensure to run after core/drupal.vertical-tabs.
+    - stable/drupal.vertical-tabs
+
+drupal.ckeditor.drupalimage.admin:
+  version: VERSION
+  js:
+    js/ckeditor/ckeditor.drupalimage.admin.js: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/jquery.once
+    - stable/drupal.vertical-tabs
+    - core/drupalSettings
+
+drupal.ckeditor.stylescombo.admin:
+  version: VERSION
+  js:
+    js/ckeditor/ckeditor.stylescombo.admin.js: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/jquery.once
+    - stable/drupal.vertical-tabs
+    - core/drupalSettings
+    # Ensure to run after ckeditor/drupal.ckeditor.admin.
+    - stable/drupal.ckeditor.admin
+
+drupal.collapse:
+  version: VERSION
+  js:
+    js/misc/details-aria.js: {}
+    js/misc/collapse.js: {}
+  dependencies:
+    - core/jquery
+    - core/modernizr
+    - stable/drupal
+    - stable/drupal.form
+    - core/jquery.once
+
+drupal.color:
+  version: VERSION
+  js:
+    js/color/color.js: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/jquery.once
+    - core/jquery.farbtastic
+    - stable/drupal.color.preview
+
+drupal.color.preview:
+  version: VERSION
+  js:
+    js/color/preview.js: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/drupalSettings
+    - core/jquery.once
+
+drupal.comment:
+  version: VERSION
+  js:
+    js/comment/comment-entity-form.js: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - stable/drupal.form
+
+drupal.comment-by-viewer:
+  version: VERSION
+  js:
+    js/comment/comment-by-viewer.js: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/drupalSettings
+
+drupal.comment-new-indicator:
+  version: VERSION
+  js:
+    js/comment/comment-new-indicator.js: {}
+  dependencies:
+    - core/jquery
+    - core/jquery.once
+    - stable/drupal
+    - stable/history.api
+    - stable/drupal.displace
+
+drupal.config_translation.admin:
+  version: VERSION
+  css:
+    theme:
+      css/config_translation/config_translation.admin.css: {}
+
+drupal.content_translation.admin:
+  version: VERSION
+  js:
+    js/content_translation/content_translation.admin.js: {}
+  css:
+    theme:
+      css/content_translation/content_translation.admin.css: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/jquery.once
+
+drupal.content_types:
+  version: VERSION
+  js:
+    js/node/content_types.js: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - stable/drupal.form
+
+drupal.contextual-links:
+  version: VERSION
+  js:
+    # Ensure to run before contextual/drupal.context-toolbar.
+    # Core.
+    js/contextual/contextual.js: { weight: -2 }
+    # Models.
+    js/contextual/models/StateModel.js: { weight: -2 }
+    # Views.
+    js/contextual/views/AuralView.js: { weight: -2 }
+    js/contextual/views/KeyboardView.js: { weight: -2 }
+    js/contextual/views/RegionView.js: { weight: -2 }
+    js/contextual/views/VisualView.js: { weight: -2 }
+  css:
+    component:
+      css/contextual/contextual.module.css: {}
+    theme:
+      css/contextual/contextual.theme.css: {}
+      css/contextual/contextual.icons.theme.css: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/drupalSettings
+    - core/backbone
+    - core/modernizr
+    - core/jquery.once
+
+drupal.contextual-toolbar:
+  version: VERSION
+  js:
+    js/contextual/contextual.toolbar.js: {}
+    # Models.
+    js/contextual/toolbar/models/StateModel.js: {}
+    # Views.
+    js/contextual/toolbar/views/AuralView.js: {}
+    js/contextual/toolbar/views/VisualView.js: {}
+  css:
+    component:
+      css/contextual/contextual.toolbar.css: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/backbone
+    - core/jquery.once
+    - stable/drupal.tabbingmanager
+    - stable/drupal.announce
+
+drupal.date:
+  version: VERSION
+  js:
+    js/misc/date.js: {}
+  dependencies:
+    - stable/drupal
+    - core/modernizr
+    - core/jquery.ui.datepicker
+
+drupal.dblog:
+  version: VERSION
+  css:
+    component:
+      css/dblog/dblog.module.css: {}
+
+drupal.debounce:
+  version: VERSION
+  js:
+    js/misc/debounce.js: {}
+  dependencies:
+    # @todo Remove Drupal dependency.
+    - stable/drupal
+
+drupal.dialog:
+  version: VERSION
+  js:
+    js/dialog/dialog.js: {}
+    js/dialog/dialog.position.js: {}
+    js/dialog/dialog.jquery-ui.js: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/drupalSettings
+    - stable/drupal.debounce
+    - stable/drupal.displace
+    - core/jquery.ui.dialog
+
+drupal.dialog.ajax:
+  version: VERSION
+  js:
+    js/dialog/dialog.ajax.js: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/drupalSettings
+    - stable/drupal.ajax
+    - stable/drupal.dialog
+
+drupal.displace:
+  version: VERSION
+  js:
+    js/misc/displace.js: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - stable/drupal.debounce
+
+drupal.dropbutton:
+  version: VERSION
+  js:
+    js/dropbutton/dropbutton.js: {}
+  css:
+    component:
+      css/dropbutton/dropbutton.css: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/drupalSettings
+    - core/jquery.once
+
+drupal.editor.admin:
+  version: VERSION
+  js:
+    js/editor/editor.admin.js: {}
+  dependencies:
+    - core/jquery
+    - core/jquery.once
+    - stable/drupal
+
+drupal.editor:
+  version: VERSION
+  js:
+    js/editor/editor.js: {}
+  css:
+    component:
+      css/editor/editor.css: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/drupalSettings
+    - core/jquery.once
+    - stable/drupal.dialog
+
+drupal.editor.dialog:
+  version: VERSION
+  js:
+    js/editor/editor.dialog.js: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal.dialog
+    - stable/drupal.ajax
+    - core/drupalSettings
+
+drupal.field_ui:
+  version: VERSION
+  js:
+    js/field_ui/field_ui.js: {}
+  css:
+    theme:
+      css/field_ui/field_ui.admin.css: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/drupalSettings
+    - core/jquery.once
+
+drupal.file:
+  version: VERSION
+  js:
+    js/file/file.js: {}
+  css:
+    theme:
+      css/file/file.admin.css: {}
+    component:
+      css/file/file.theme.css: {}
+  dependencies:
+    - core/jquery
+    - core/jquery.once
+    - stable/drupal
+    - core/drupalSettings
+
+drupal.filter.admin:
+  version: VERSION
+  js:
+    js/filter/filter.admin.js: {}
+  css:
+    theme:
+      css/filter/filter.admin.css: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/jquery.once
+    - stable/drupal.form
+
+drupal.filter.filter_html.admin:
+  version: VERSION
+  js:
+    js/filter/filter.filter_html.admin.js: {}
+  dependencies:
+    - core/jquery
+    - core/jquery.once
+    - core/underscore
+
+drupal.filter:
+  version: VERSION
+  js:
+    js/filter/filter.js: {}
+  css:
+    theme:
+      # @todo Misnomer: Does not contain administrative styles.
+      css/filter/filter.admin.css: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/jquery.once
+
+drupal.form:
+  version: VERSION
+  js:
+    js/misc/form.js: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - stable/drupal.debounce
+    - core/jquery.cookie
+    - core/jquery.once
+
+drupal.language.admin:
+  version: VERSION
+  js:
+    js/language/language.admin.js: {}
+  css:
+    theme:
+      css/language/language.admin.css: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/jquery.once
+
+drupal.locale.admin:
+  version: VERSION
+  js:
+    js/locale/locale.admin.js: {}
+  css:
+    component:
+      css/locale/locale.admin.css: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - stable/drupal.form
+    - core/jquery.once
+
+drupal.locale.datepicker:
+  version: VERSION
+  js:
+    js/locale/locale.datepicker.js: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/drupalSettings
+
+drupal.machine-name:
+  version: VERSION
+  js:
+    js/misc/machine-name.js: {}
+  dependencies:
+    - core/jquery
+    - core/jquery.once
+    - stable/drupal
+    - core/drupalSettings
+    - stable/drupal.form
+
+drupal.menu_ui:
+  version: VERSION
+  js:
+    js/menu_ui/menu_ui.js: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - stable/drupal.form
+
+drupal.menu_ui.admin:
+  version: VERSION
+  js:
+    js/menu_ui/menu_ui.admin.js: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+
+drupal.menu_ui.adminforms:
+  version: VERSION
+  css:
+    theme:
+      css/menu_ui/menu_ui.admin.css: {}
+
+drupal.node:
+  version: VERSION
+  css:
+    layout:
+      css/node/node.module.css: {}
+  js:
+    js/node/node.js: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/drupalSettings
+    - stable/drupal.form
+
+drupal.node.admin:
+  version: VERSION
+  css:
+    theme:
+      css/node/node.admin.css: {}
+
+drupal.node.preview:
+  version: VERSION
+  css:
+    theme:
+      css/node/node.preview.css: {}
+  js:
+    js/node/node.preview.js: {}
+  dependencies:
+    - core/jquery
+    - core/jquery.once
+    - stable/drupal
+    - stable/drupal.form
+
+drupal.node-new-comments-link:
+  version: VERSION
+  js:
+    js/comment/node-new-comments-link.js: {}
+  dependencies:
+    - core/jquery
+    - core/jquery.once
+    - stable/drupal
+    - stable/history.api
+
+drupal.path:
+  version: VERSION
+  js:
+    js/path/path.js: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - stable/drupal.form
+
+drupal.progress:
+  version: VERSION
+  js:
+    js/misc/progress.js: {}
+  dependencies:
+    - stable/drupal
+    - core/jquery
+    - core/drupalSettings
+
+drupal.shortcut:
+  version: VERSION
+  css:
+    theme:
+      css/shortcut/shortcut.theme.css: {}
+      css/shortcut/shortcut.icons.theme.css: {}
+
+drupal.simpletest:
+  version: VERSION
+  js:
+    js/simpletest/simpletest.js: {}
+  css:
+    component:
+      css/simpletest/simpletest.module.css: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/drupalSettings
+    - core/jquery.once
+    - stable/drupal.tableselect
+    - stable/drupal.debounce
+
+drupal.statistics:
+  version: VERSION
+  js:
+    js/statistics/statistics.js: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/drupalSettings
+
+drupal.states:
+  version: VERSION
+  js:
+    js/misc/states.js: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/drupalSettings
+    - core/jquery.once
+
+drupal.system:
+  version: VERSION
+  js:
+    js/system/system.js: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/drupalSettings
+    - core/jquery.once
+
+drupal.system.modules:
+  version: VERSION
+  js:
+    js/system/system.modules.js: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - stable/drupal.debounce
+    - core/jquery.once
+
+drupal.system.date:
+  version: VERSION
+  js:
+    js/system/system.date.js: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/drupalSettings
+    - core/jquery.once
+    - stable/drupal.form
+
+drupal.tabbingmanager:
+  version: VERSION
+  js:
+    js/misc/tabbingmanager.js: {}
+  dependencies:
+    - core/jquery
+    # Supplies the ':tabbable' pseudo selector.
+    - core/jquery.ui
+    - stable/drupal
+
+drupal.tabledrag:
+  version: VERSION
+  js:
+    js/misc/tabledrag.js: { weight: -1 }
+  dependencies:
+    - core/jquery
+    - core/modernizr
+    - stable/drupal
+    - core/drupalSettings
+    - core/jquery.once
+    - core/jquery.cookie
+
+drupal.tableheader:
+  version: VERSION
+  js:
+    js/misc/tableheader.js: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/drupalSettings
+    - core/jquery.once
+    - stable/drupal.displace
+
+drupal.tableresponsive:
+  version: VERSION
+  js:
+    js/misc/tableresponsive.js: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/jquery.once
+
+drupal.tableselect:
+  version: VERSION
+  js:
+    js/misc/tableselect.js: {}
+  dependencies:
+    - stable/drupal
+    - core/jquery
+    - core/jquery.once
+
+drupal.taxonomy:
+  version: VERSION
+  js:
+    js/taxonomy/taxonomy.js: {}
+  css:
+    component:
+      css/taxonomy/taxonomy.theme.css: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/drupalSettings
+    - stable/drupal.tabledrag
+
+drupal.text:
+  version: VERSION
+  js:
+    js/text/text.js: {}
+  dependencies:
+    - core/jquery
+    - core/jquery.once
+    - stable/drupal
+
+drupal.timezone:
+  version: VERSION
+  js:
+    js/misc/timezone.js: {}
+  dependencies:
+    - core/jquery
+    - core/jquery.once
+    - stable/drupal
+
+drupal.update.admin:
+  version: VERSION
+  css:
+    theme:
+      css/update/update.admin.theme.css: {}
+
+drupal.user:
+  version: VERSION
+  js:
+    js/user/user.js: {}
+  css:
+    component:
+      css/user/user.module.css: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/jquery.once
+
+drupal.user.admin:
+  version: VERSION
+  css:
+    theme:
+      css/user/user.admin.css: {}
+
+drupal.user.permissions:
+  version: VERSION
+  js:
+    js/useruser.permissions.js: {}
+  dependencies:
+    - core/jquery
+    - core/jquery.once
+    - stable/drupal
+    - core/drupalSettings
+    - stable/drupal.user.admin
+
+drupal.user.icons:
+  version: VERSION
+  css:
+    theme:
+      css/user/user.icons.admin.css: {}
+
+drupal.vertical-tabs:
+  version: VERSION
+  js:
+    # Load before core/drupal.collapse.
+    js/misc/vertical-tabs.js: { weight: -1 }
+  css:
+    component:
+      css/misc/vertical-tabs.css: {}
+  dependencies:
+    - core/jquery
+    - core/jquery.once
+    - stable/drupal
+    - core/drupalSettings
+    - stable/drupal.form
+
+form:
+  version: VERSION
+  css:
+    layout:
+      css/node/node.module.css: {}
+
+history:
+  version: VERSION
+  js:
+    js/tracker/tracker-history.js: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - stable/history.api
+
+history.api:
+  version: VERSION
+  js:
+    js/history/history.js: {}
+  dependencies:
+    - core/jquery
+    - core/drupalSettings
+    - stable/drupal
+    - stable/drupal.ajax
+
+image.admin:
+  version: VERSION
+  css:
+    theme:
+      css/image/image.admin.css: {}
+
+maintenance:
+  version: VERSION
+  css:
+    theme:
+      css/system/system.maintenance.css: { weight: -10 }
+  dependencies:
+    - stable/base
+    - stable/admin
+
+mark-as-read:
+  version: VERSION
+  js:
+    js/history/mark-as-read.js: {}
+  dependencies:
+    - stable/history.api
+
+quickedit:
+  version: VERSION
+  js:
+    # Core.
+    js/quickedit/quickedit.js: {}
+    js/quickedit/util.js: {}
+    # Models.
+    js/quickedit/models/BaseModel.js: {}
+    js/quickedit/models/AppModel.js: {}
+    js/quickedit/models/EntityModel.js: {}
+    js/quickedit/models/FieldModel.js: {}
+    js/quickedit/models/EditorModel.js: {}
+    # Views.
+    js/quickedit/views/AppView.js: {}
+    js/quickedit/views/FieldDecorationView.js: {}
+    js/quickedit/views/EntityDecorationView.js: {}
+    js/quickedit/views/EntityToolbarView.js: {}
+    js/quickedit/views/ContextualLinkView.js: {}
+    js/quickedit/views/FieldToolbarView.js: {}
+    js/quickedit/views/EditorView.js: {}
+    # Other.
+    js/quickedit/theme.js: {}
+  css:
+    component:
+      css/quickedit/quickedit.module.css: {}
+    theme:
+      css/quickedit/quickedit.theme.css: {}
+      css/quickedit/quickedit.icons.theme.css: {}
+  dependencies:
+    - core/jquery
+    - core/jquery.once
+    - core/underscore
+    - core/backbone
+    - core/jquery.form
+    - core/jquery.ui.position
+    - stable/drupal
+    - stable/drupal.displace
+    - stable/drupal.form
+    - stable/drupal.ajax
+    - stable/drupal.debounce
+    - core/drupalSettings
+    - stable/drupal.dialog
+
+quickedit.inPlaceEditor.form:
+  version: VERSION
+  js:
+    js/quickedit/editors/formEditor.js: {}
+  dependencies:
+    - stable/quickedit
+
+quickedit.inPlaceEditor.plainText:
+  version: VERSION
+  js:
+    js/quickedit/editors/plainTextEditor.js: {}
+  dependencies:
+    - stable/quickedit
+
+quickedit.inPlaceEditor.formattedText:
+  version: VERSION
+  js:
+    js/editor/editor.formattedTextEditor.js: { attributes: { defer: true } }
+  dependencies:
+    - stable/quickedit
+    - stable/drupal.editor
+    - stable/drupal.ajax
+    - core/drupalSettings
+
+responsive_image.ajax:
+  version: VERSION
+  js:
+    js/responsive_image/responsive_image.ajax.js: {}
+
+toolbar:
+  version: VERSION
+  js:
+    # Core.
+    js/toolbar/toolbar.js: {}
+    # Models.
+    js/toolbar/models/MenuModel.js: {}
+    js/toolbar/models/ToolbarModel.js: {}
+    # Views.
+    js/toolbar/views/BodyVisualView.js: {}
+    js/toolbar/views/MenuVisualView.js: {}
+    js/toolbar/views/ToolbarAuralView.js: {}
+    js/toolbar/views/ToolbarVisualView.js: {}
+  css:
+    component:
+      css/toolbar/toolbar.module.css: {}
+    theme:
+      css/toolbar/toolbar.theme.css: {}
+      css/toolbar/toolbar.icons.theme.css: {}
+  dependencies:
+    - core/modernizr
+    - core/jquery
+    - stable/drupal
+    - core/drupalSettings
+    - stable/drupal.ajax
+    - stable/drupal.announce
+    - core/backbone
+    - core/matchmedia
+    - core/matchmedia.addListener
+    - core/jquery.once
+    - stable/drupal.displace
+    - stable/toolbar.menu
+
+toolbar.menu:
+  version: VERSION
+  js:
+    js/toolbar/toolbar.menu.js: {}
+  css:
+    state:
+      css/toolbar/toolbar.menu.css: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/jquery.once
+
+toolbar.escapeAdmin:
+  version: VERSION
+  js:
+    js/toolbar/escapeAdmin.js: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/drupalSettings
+    - core/jquery.once
+
+tour:
+  version: VERSION
+  js:
+    js/tour/tour.js: {}
+  dependencies:
+    - core/jquery
+    - core/jquery.once
+    - stable/drupal
+    - core/backbone
+    - core/jquery.joyride
+    - stable/tour-styling
+
+tour-styling:
+  version: VERSION
+  css:
+    component:
+      css/tour/tour.module.css: { media: screen }
+
+translations:
+  # No sensible version can be specified, since the translations may change at
+  # any time.
+  js:
+    # This file does not actually exist; it's a placeholder file that will be
+    # overriden by locale_js_alter(), to use the file that contains the actual
+    # translations, for the language used in the current request.
+    js/locale/locale.translation.js: {}
+
+views.module:
+  version: VERSION
+  css:
+    component:
+      css/views/views.module.css: {}
+
+views.ajax:
+  version: VERSION
+  js:
+    js/views/base.js: {}
+    js/views/ajax_view.js: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/drupalSettings
+    - core/jquery.once
+    - core/jquery.form
+    - stable/drupal.ajax
+
+views_ui.admin:
+  version: VERSION
+  js:
+    js/views_ui/ajax.js: {}
+    js/views_ui/dialog.views.js: {}
+    js/views_ui/views-admin.js: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/drupalSettings
+    - core/jquery.once
+    - core/jquery.form
+    - stable/drupal.ajax
+    - stable/drupal.dropbutton
+    - stable/views.ajax
+    - stable/views_ui.admin.styling
+
+views_ui.admin.styling:
+  version: VERSION
+  css:
+    component:
+      css/views_ui/views_ui.admin.css: {}
+    theme:
+      css/views_ui/views_ui.admin.theme.css: {}
+      css/views_ui/views_ui.contextual.css: {}
+
+views_ui.listing:
+  version: VERSION
+  js:
+    js/views_ui/views_ui.listing.js: {}
+  dependencies:
+    - core/jquery
+    - stable/drupal
+    - core/jquery.once
+    - stable/views_ui.admin.styling
diff --git a/core/themes/stable/templates/admin/admin-block-content.html.twig b/core/themes/stable/templates/admin/admin-block-content.html.twig
new file mode 100644
index 0000000..f1c5f27
--- /dev/null
+++ b/core/themes/stable/templates/admin/admin-block-content.html.twig
@@ -0,0 +1,34 @@
+{#
+/**
+ * @file
+ * Default theme implementation for the content of an administrative block.
+ *
+ * Available variables:
+ * - content: A list containing information about the block. Each element
+ *   of the array represents an administrative menu item, and must at least
+ *   contain the keys 'title', 'link_path', and 'localized_options', which are
+ *   passed to l(). A 'description' key may also be provided.
+ * - attributes: HTML attributes to be added to the element.
+ * - compact: Boolean indicating whether compact mode is turned on or not.
+ *
+ * @see template_preprocess_admin_block_content()
+ *
+ * @ingroup themeable
+ */
+#}
+{%
+  set classes = [
+    'list-group',
+    compact ? 'compact',
+  ]
+%}
+{% if content %}
+  <dl{{ attributes.addClass(classes) }}>
+    {% for item in content %}
+      <dt class="list-group__link">{{ item.link }}</dt>
+      {% if item.description %}
+        <dd class="list-group__description">{{ item.description }}</dd>
+      {% endif %}
+    {% endfor %}
+  </dl>
+{% endif %}
diff --git a/core/themes/stable/templates/admin/admin-block.html.twig b/core/themes/stable/templates/admin/admin-block.html.twig
new file mode 100644
index 0000000..c3dab0e
--- /dev/null
+++ b/core/themes/stable/templates/admin/admin-block.html.twig
@@ -0,0 +1,26 @@
+{#
+/**
+ * @file
+ * Default theme implementation for an administrative block.
+ *
+ * Available variables:
+ * - block: An array of information about the block, including:
+ *   - show: A flag indicating if the block should be displayed.
+ *   - title: The block title.
+ *   - content: (optional) The content of the block.
+ *   - description: (optional) A description of the block.
+ *     (Description should only be output if content is not available).
+ *
+ * @ingroup themeable
+ */
+#}
+<div class="panel">
+  {% if block.title %}
+    <h3 class="panel__title">{{ block.title }}</h3>
+  {% endif %}
+  {% if block.content %}
+    <div class="panel__content">{{ block.content }}</div>
+  {% elseif block.description %}
+    <div class="panel__description">{{ block.description }}</div>
+  {% endif %}
+</div>
diff --git a/core/themes/stable/templates/admin/admin-page.html.twig b/core/themes/stable/templates/admin/admin-page.html.twig
new file mode 100644
index 0000000..a2483f1
--- /dev/null
+++ b/core/themes/stable/templates/admin/admin-page.html.twig
@@ -0,0 +1,27 @@
+{#
+/**
+ * @file
+ * Default theme implementation for an administrative page.
+ *
+ * Available variables:
+ * - system_compact_link: Themed link to toggle compact view.
+ * - containers: An list of administrative blocks keyed by position: left or
+ *   right. Contains:
+ *   - blocks: A list of blocks within a container.
+ *
+ * @see template_preprocess_admin_page()
+ *
+ * @ingroup themeable
+ */
+#}
+
+<div class="clearfix">
+  {{ system_compact_link }}
+  {% for container in containers %}
+    <div class="layout-column layout-column--half">
+      {% for block in container.blocks %}
+        {{ block }}
+      {% endfor %}
+    </div>
+  {% endfor %}
+</div>
diff --git a/core/themes/stable/templates/admin/authorize-report.html.twig b/core/themes/stable/templates/admin/authorize-report.html.twig
new file mode 100644
index 0000000..9144586
--- /dev/null
+++ b/core/themes/stable/templates/admin/authorize-report.html.twig
@@ -0,0 +1,23 @@
+{#
+/**
+ * @file
+ * Default theme implementation for authorize.php operation report templates.
+ *
+ * This report displays the results of an operation run via authorize.php.
+ *
+ * Available variables:
+ * - messages: A list of result messages.
+ * - attributes: HTML attributes for the element.
+ *
+ * @see template_preprocess_authorize_report()
+ *
+ * @ingroup themeable
+ */
+#}
+{% if messages %}
+  <div{{ attributes.addClass('authorize-results') }}>
+    {% for message_group in messages %}
+      {{ message_group }}
+    {% endfor %}
+  </div>
+{% endif %}
diff --git a/core/themes/stable/templates/admin/block-content-add-list.html.twig b/core/themes/stable/templates/admin/block-content-add-list.html.twig
new file mode 100644
index 0000000..0810461
--- /dev/null
+++ b/core/themes/stable/templates/admin/block-content-add-list.html.twig
@@ -0,0 +1,24 @@
+{#
+/**
+ * @file
+ * Default theme implementation to present a list of custom block types.
+ *
+ * Available variables:
+ * - types: A collection of all the available custom block types.
+ *   Each block type contains the following:
+ *   - link: A link to add a block of this type.
+ *   - description: A description of this custom block type.
+ *
+ * @see template_preprocess_block_content_add_list()
+ *
+ * @ingroup themeable
+ */
+#}
+{% spaceless %}
+  <dl>
+    {% for type in types %}
+      <dt>{{ type.link }}</dt>
+      <dd>{{ type.description }}</dd>
+    {% endfor %}
+  </dl>
+{% endspaceless %}
diff --git a/core/themes/stable/templates/admin/block-list.html.twig b/core/themes/stable/templates/admin/block-list.html.twig
new file mode 100644
index 0000000..854d3ce
--- /dev/null
+++ b/core/themes/stable/templates/admin/block-list.html.twig
@@ -0,0 +1,23 @@
+{#
+/**
+ * @file
+ * Two column template for the block add/edit form.
+ *
+ * This template will be used when a block edit form specifies 'block_edit_form'
+ * as its #theme callback.  Otherwise, by default, block add/edit forms will be
+ * themed by form.html.twig.
+ *
+ * Available variables:
+ * - form: The block add/edit form.
+ *
+ * @ingroup themeable
+ */
+#}
+<div class="layout-block-list clearfix">
+  <div class="layout-region block-list-primary">
+    {{ form|without('place_blocks') }}
+  </div>
+  <div class="layout-region block-list-secondary">
+    {{ form.place_blocks }}
+  </div>
+</div>
diff --git a/core/themes/stable/templates/admin/ckeditor-settings-toolbar.html.twig b/core/themes/stable/templates/admin/ckeditor-settings-toolbar.html.twig
new file mode 100644
index 0000000..bc8e836
--- /dev/null
+++ b/core/themes/stable/templates/admin/ckeditor-settings-toolbar.html.twig
@@ -0,0 +1,75 @@
+{#
+/**
+ * @file
+ * Default theme implementation for the CKEditor settings toolbar.
+ *
+ * Available variables:
+ * - multiple_buttons: A list of buttons that may be added multiple times.
+ * - disabled_buttons: A list of disabled buttons.
+ * - active_buttons: A list of active button rows.
+ *
+ * @see template_preprocess_ckeditor_settings_toolbar()
+ *
+ * @ingroup themeable
+ */
+#}
+{% spaceless %}
+  <fieldset role="form" aria-labelledby="ckeditor-button-configuration ckeditor-button-description">
+    <legend id="ckeditor-button-configuration">{{ 'Toolbar configuration'|t }}</legend>
+    <div class="fieldset-wrapper">
+      <div id="ckeditor-button-description" class="fieldset-description">
+      {%- trans -%}
+        Move a button into the <em>Active toolbar</em> to enable it, or into the list of <em>Available buttons</em> to disable it. Buttons may be moved with the mouse or keyboard arrow keys. Toolbar group names are provided to support screen reader users. Empty toolbar groups will be removed upon save.
+      {%- endtrans -%}
+      </div>
+      <div class="ckeditor-toolbar-disabled clearfix">
+        {# Available buttons. #}
+        <div class="ckeditor-toolbar-available">
+          <label for="ckeditor-available-buttons">{{ 'Available buttons'|t }}</label>
+          <ul id="ckeditor-available-buttons" class="ckeditor-buttons clearfix" role="form" data-drupal-ckeditor-button-sorting="source">
+          {% for disabled_button in disabled_buttons %}
+             <li{{ disabled_button.attributes.addClass('ckeditor-button') }}>{{ disabled_button.value }}</li>
+          {% endfor %}
+          </ul>
+        </div>
+        {# Dividers. #}
+        <div class="ckeditor-toolbar-dividers">
+          <label for="ckeditor-multiple-buttons">{{ 'Button divider'|t }}</label>
+          <ul id="ckeditor-multiple-buttons" class="ckeditor-multiple-buttons" role="form" data-drupal-ckeditor-button-sorting="dividers">
+          {% for multiple_button in multiple_buttons %}
+            <li{{ multiple_button.attributes.addClass('ckeditor-multiple-button') }}>{{ multiple_button.value }}</li>
+          {% endfor %}
+          </ul>
+        </div>
+      </div>
+      {# Active toolbar. #}
+      <div class="clearfix">
+        <label id="ckeditor-active-toolbar">{{ 'Active toolbar'|t }}</label>
+      </div>
+      <div data-toolbar="active" role="form" class="ckeditor-toolbar ckeditor-toolbar-active clearfix">
+        <ul class="ckeditor-active-toolbar-configuration" role="presentation" aria-label="{{ 'CKEditor toolbar and button configuration.'|t }}">
+        {% for button_row in active_buttons %}
+          <li class="ckeditor-row" role="group" aria-labelledby="ckeditor-active-toolbar">
+            <ul class="ckeditor-toolbar-groups clearfix">
+            {% for group_name, button_group in button_row %}
+              <li class="ckeditor-toolbar-group" role="presentation" data-drupal-ckeditor-type="group" data-drupal-ckeditor-toolbar-group-name="{{ group_name }}" tabindex="0">
+                <h3 class="ckeditor-toolbar-group-name" id="ckeditor-toolbar-group-aria-label-for-{{ button_group.group_name_class }}">{{ group_name }}</h3>
+                <ul class="ckeditor-buttons ckeditor-toolbar-group-buttons" role="toolbar" data-drupal-ckeditor-button-sorting="target" aria-labelledby="ckeditor-toolbar-group-aria-label-for-{{ button_group.group_name_class }}">
+                {% for active_button in button_group.buttons %}
+                  <li{{ active_button.attributes.addClass(active_button.multiple ? 'ckeditor-multiple-button' : 'ckeditor-button') }}>{{ active_button.value }}</li>
+                {% endfor %}
+                </ul>
+              </li>
+            {% endfor %}
+            </ul>
+          </li>
+        {% else %}
+          <li>
+            <ul class="ckeditor-buttons"></ul>
+          </li>
+        {% endfor %}
+        </ul>
+      </div>
+    </div>
+  </fieldset>
+{% endspaceless %}
diff --git a/core/themes/stable/templates/admin/color-scheme-form.html.twig b/core/themes/stable/templates/admin/color-scheme-form.html.twig
new file mode 100644
index 0000000..3c6fdd0
--- /dev/null
+++ b/core/themes/stable/templates/admin/color-scheme-form.html.twig
@@ -0,0 +1,26 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a theme's color form.
+ *
+ * Available variables:
+ * - form: Form elements for the color scheme form, including:
+ *   - scheme: A color scheme form element. For example: a select element with
+ *     color theme presets, or a color picker widget.
+ *   - palette: Color fields that can be changed by entering in a new hex value.
+ * - html_preview: A HTML preview of the theme's current color scheme.
+ *
+ * @see template_preprocess_color_scheme_form()
+ *
+ * @ingroup themeable
+ */
+#}
+<div class="color-form clearfix">
+  {{ form.scheme }}
+  <div class="clearfix color-palette js-color-palette">
+    {{ form.palette }}
+  </div>
+  {{ form|without('scheme', 'palette') }}
+  <h2>{{ 'Preview'|t }}</h2>
+  {{ html_preview }}
+</div>
diff --git a/core/themes/stable/templates/admin/config_translation_manage_form_element.html.twig b/core/themes/stable/templates/admin/config_translation_manage_form_element.html.twig
new file mode 100644
index 0000000..777514f
--- /dev/null
+++ b/core/themes/stable/templates/admin/config_translation_manage_form_element.html.twig
@@ -0,0 +1,24 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a form element in config_translation.
+ *
+ * Available variables:
+ * - element: Array that represents the element shown in the form.
+ *   - source: The source of the translation.
+ *   - translation: The translation for the target language.
+ *
+ * @see template_preprocess()
+ * @see template_preprocess_config_translation_manage_form_element()
+ *
+ * @ingroup themeable
+ */
+#}
+<div class="translation-set clearfix">
+  <div class="layout-column layout-column--half translation-set__source">
+    {{ element.source }}
+  </div>
+  <div class="layout-column layout-column--half translation-set__translated">
+    {{ element.translation }}
+  </div>
+</div>
diff --git a/core/themes/stable/templates/admin/image-anchor.html.twig b/core/themes/stable/templates/admin/image-anchor.html.twig
new file mode 100644
index 0000000..eb670a4
--- /dev/null
+++ b/core/themes/stable/templates/admin/image-anchor.html.twig
@@ -0,0 +1,14 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a 3x3 grid of checkboxes for image anchors.
+ *
+ * Available variables:
+ * - table: HTML for the table of image anchors.
+ *
+ * @see template_preprocess_image_anchor()
+ *
+ * @ingroup themeable
+ */
+#}
+{{ table }}
diff --git a/core/themes/stable/templates/admin/image-crop-summary.html.twig b/core/themes/stable/templates/admin/image-crop-summary.html.twig
new file mode 100644
index 0000000..fc991b6
--- /dev/null
+++ b/core/themes/stable/templates/admin/image-crop-summary.html.twig
@@ -0,0 +1,32 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a summary of an image crop effect.
+ *
+ * Available variables:
+ * - data: The current configuration for this resize effect, including:
+ *   - width: The width of the resized image.
+ *   - height: The height of the resized image.
+ *   - anchor: The part of the image that will be retained after cropping.
+ *   - anchor_label: The translated label of the crop anchor.
+ * - effect: The effect information, including:
+ *   - id: The effect identifier.
+ *   - label: The effect name.
+ *   - description: The effect description.
+ *
+ * @ingroup themeable
+ */
+#}
+{% if data.width and data.height -%}
+  {{ data.width|e }}×{{ data.height|e }}
+{%- else -%}
+  {% if data.width %}
+    {% trans %}
+      width {{ data.width|e }}
+    {% endtrans %}
+  {% elseif data.height %}
+    {% trans %}
+      height {{ data.height|e }}
+    {% endtrans %}
+  {% endif %}
+{%- endif %}
diff --git a/core/themes/stable/templates/admin/image-resize-summary.html.twig b/core/themes/stable/templates/admin/image-resize-summary.html.twig
new file mode 100644
index 0000000..f4084ef
--- /dev/null
+++ b/core/themes/stable/templates/admin/image-resize-summary.html.twig
@@ -0,0 +1,30 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a summary of an image resize effect.
+ *
+ * Available variables:
+ * - data: The current configuration for this resize effect, including:
+ *   - width: The width of the resized image.
+ *   - height: The height of the resized image.
+ * - effect: The effect information, including:
+ *   - id: The effect identifier.
+ *   - label: The effect name.
+ *   - description: The effect description.
+ *
+ * @ingroup themeable
+ */
+#}
+{% if data.width and data.height -%}
+  {{ data.width|e }}×{{ data.height|e }}
+{%- else -%}
+  {% if data.width %}
+    {% trans %}
+      width {{ data.width|e }}
+    {% endtrans %}
+  {% elseif data.height %}
+    {% trans %}
+      height {{ data.height|e }}
+    {% endtrans %}
+  {% endif %}
+{%- endif %}
diff --git a/core/themes/stable/templates/admin/image-rotate-summary.html.twig b/core/themes/stable/templates/admin/image-rotate-summary.html.twig
new file mode 100644
index 0000000..705a0eb
--- /dev/null
+++ b/core/themes/stable/templates/admin/image-rotate-summary.html.twig
@@ -0,0 +1,27 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a summary of an image rotate effect.
+ *
+ * Available variables:
+ * - data: The current configuration for this resize effect, including:
+ *   - degrees: Degrees to rotate the image, positive values will rotate the
+ *     image clockwise, negative values counter-clockwise.
+ *   - bgcolor: The hex background color of the new areas created as consequence
+ *     of rotation.
+ *   - random: If the rotation angle is randomized.
+ * - effect: The effect information, including:
+ *   - id: The effect identifier.
+ *   - label: The effect name.
+ *   - description: The effect description.
+ *
+ * @ingroup themeable
+ */
+#}
+{% if data.random %}
+  {% trans %}
+    random between -{{ data.degrees|abs }}° and {{ data.degrees|abs }}°
+  {% endtrans %}
+{% else %}
+  {{ data.degrees }}°
+{% endif %}
diff --git a/core/themes/stable/templates/admin/image-scale-summary.html.twig b/core/themes/stable/templates/admin/image-scale-summary.html.twig
new file mode 100644
index 0000000..32d75cc
--- /dev/null
+++ b/core/themes/stable/templates/admin/image-scale-summary.html.twig
@@ -0,0 +1,37 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a summary of an image scale effect.
+ *
+ * Available variables:
+ * - data: The current configuration for this resize effect, including:
+ *   - width: The width of the resized image.
+ *   - height: The height of the resized image.
+ *   - upscale: If images larger than their original size can scale.
+ * - effect: The effect information, including:
+ *   - id: The effect identifier.
+ *   - label: The effect name.
+ *   - description: The effect description.
+ *
+ * @ingroup themeable
+ */
+#}
+{% if data.width and data.height -%}
+  {{ data.width|e }}×{{ data.height|e }}
+{%- else -%}
+  {% if data.width %}
+    {% trans %}
+      width {{ data.width|e }}
+    {% endtrans %}
+  {% elseif data.height %}
+    {% trans %}
+      height {{ data.height|e }}
+    {% endtrans %}
+  {% endif %}
+{%- endif %}
+
+{% if data.upscale %}
+  {% trans %}
+    (upscaling allowed)
+  {% endtrans %}
+{% endif %}
diff --git a/core/themes/stable/templates/admin/image-style-preview.html.twig b/core/themes/stable/templates/admin/image-style-preview.html.twig
new file mode 100644
index 0000000..d6e715c
--- /dev/null
+++ b/core/themes/stable/templates/admin/image-style-preview.html.twig
@@ -0,0 +1,57 @@
+{#
+/**
+ * @file
+ * Default theme implementation to display a preview of an image style.
+ *
+ * Available variables:
+ * - style_id: The ID of the image style.
+ * - style_name: The name of the image style.
+ * - cache_bypass: A timestamp token used to avoid browser caching of images.
+ * - original: An associative array containing:
+ *   - url: The URL of the original image.
+ *   - width: The width in pixels of the original image.
+ *   - height: The height in pixels of the original image.
+ *   - rendered: The render array for the original image.
+ * - derivative: An associative array containing:
+ *   - url: The URL of the derivative image.
+ *   - width: The width in pixels of the derivative image.
+ *   - height: The height in pixels of the derivative image.
+ *   - rendered:  The rendered derivative image.
+ * - preview: An associative array containing:
+ *   - original: An associative array containing:
+ *     - width: The width in pixels of the original image in the preview.
+ *     - height: The height in pixels of the original image in the preview.
+ *   - derivative: An associative array containing:
+ *     - width: The width in pixels of the derivative image in the preview.
+ *     - height: The height in pixels of the derivative image in the preview.
+ *
+ * @see template_preprocess_image_style_preview()
+ *
+ * @ingroup themeable
+ */
+#}
+<div class="image-style-preview preview clearfix">
+  {# Preview of the original image. #}
+  <div class="preview-image-wrapper">
+      {{ 'original'|t }} (<a href="{{ original.url }}">{{ 'view actual size'|t }}</a>)
+      <div class="preview-image original-image" style="width: {{ preview.original.width }}px; height: {{ preview.original.height }}px;">
+        <a href="{{ original.url }}">
+          {{ original.rendered }}
+        </a>
+      <div class="height" style="height: {{ preview.original.height }}px"><span>{{ original.height }}px</span></div>
+      <div class="width" style="width: {{ preview.original.width }}px"><span>{{ original.width }}px</span></div>
+    </div>
+  </div>
+
+  {# Derivative of the image style. #}
+  <div class="preview-image-wrapper">
+    {{ style_name }} (<a href="{{ derivative.url }}?{{ cache_bypass }}">{{ 'view actual size'|t }}</a>)
+    <div class="preview-image modified-image" style="width: {{ preview.derivative.width }}px; height: {{ preview.derivative.height }}px;">
+      <a href="{{ derivative.url }}?{{ cache_bypass }}">
+        {{ derivative.rendered }}
+      </a>
+      <div class="height" style="height: {{ preview.derivative.height }}px"><span>{{ derivative.height }}px</span></div>
+      <div class="width" style="width: {{ preview.derivative.width }}px"><span>{{ derivative.width }}px</span></div>
+    </div>
+  </div>
+</div>
diff --git a/core/themes/stable/templates/admin/indentation.html.twig b/core/themes/stable/templates/admin/indentation.html.twig
new file mode 100644
index 0000000..c7067fb
--- /dev/null
+++ b/core/themes/stable/templates/admin/indentation.html.twig
@@ -0,0 +1,14 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a set of indentation divs.
+ *
+ * These <div> tags are used for drag and drop tables.
+ *
+ * Available variables:
+ * - size: Optional. The number of indentations to create.
+ *
+ * @ingroup themeable
+ */
+#}
+{% for i in 1..size if size > 0 %}<div class="js-indentation indentation">&nbsp;</div>{% endfor %}
diff --git a/core/themes/stable/templates/admin/language-negotiation-configure-form.html.twig b/core/themes/stable/templates/admin/language-negotiation-configure-form.html.twig
new file mode 100644
index 0000000..12528ac
--- /dev/null
+++ b/core/themes/stable/templates/admin/language-negotiation-configure-form.html.twig
@@ -0,0 +1,40 @@
+{#
+/**
+* @file
+* Default theme implementation for a language negotiation configuration form.
+*
+* Available variables:
+* - language_types: A list of language negotiation types. Each language type
+*   contains the following:
+*   - type: The machine name for the negotiation type.
+*   - title: The language negotiation type name.
+*   - description: A description for how the language negotiation type operates.
+*   - configurable: A radio element to toggle the table.
+*   - table: A draggable table for the language detection methods of this type.
+*   - children: Remaining form items for the group.
+*   - attributes: A list of HTML attributes for the wrapper element.
+* - children: Remaining form items for all groups.
+*
+* @see template_preprocess_language_negotiation_configure_form()
+*
+* @ingroup themeable
+*/
+#}
+{% for language_type in language_types %}
+  {%
+    set language_classes = [
+      'js-form-item',
+      'form-item',
+      'table-language-group',
+      'table-' ~ language_type.type ~ '-wrapper',
+    ]
+  %}
+  <div{{ language_type.attributes.addClass(language_classes) }}>
+    <h2>{{ language_type.title }}</h2>
+    <div class="description">{{ language_type.description }}</div>
+    {{ language_type.configurable }}
+    {{ language_type.table }}
+    {{ language_type.children }}
+  </div>
+{% endfor %}
+{{ children }}
diff --git a/core/themes/stable/templates/admin/locale-translation-last-check.html.twig b/core/themes/stable/templates/admin/locale-translation-last-check.html.twig
new file mode 100644
index 0000000..a54574b
--- /dev/null
+++ b/core/themes/stable/templates/admin/locale-translation-last-check.html.twig
@@ -0,0 +1,25 @@
+{#
+/**
+ * @file
+ * Default theme implementation for the last time we checked for update data.
+ *
+ * Available variables:
+ * - last_checked: Whether or not locale updates have been checked before.
+ * - time: The formatted time ago when the site last checked for available
+ *   updates.
+ * - link: A link to manually check available updates.
+ *
+ * @see template_preprocess_locale_translation_last_check()
+ *
+ * @ingroup themeable
+ */
+#}
+<div class="locale checked">
+  <p>
+  {% if last_checked %}
+    {% trans %} Last checked: {{ time }} ago {% endtrans %}
+  {% else %}
+    {{ 'Last checked: never'|t }}
+  {% endif %}
+  <span class="check-manually">({{ link }})</span></p>
+</div>
diff --git a/core/themes/stable/templates/admin/locale-translation-update-info.html.twig b/core/themes/stable/templates/admin/locale-translation-update-info.html.twig
new file mode 100644
index 0000000..74cbffc
--- /dev/null
+++ b/core/themes/stable/templates/admin/locale-translation-update-info.html.twig
@@ -0,0 +1,59 @@
+{#
+/**
+ * @file
+ * Default theme implementation for displaying translation status information.
+ *
+ * Displays translation status information per language.
+ *
+ * Available variables:
+ * - modules: A list of modules names that have available translation updates.
+ * - updates: A list of available translation updates.
+ * - not_found: A list of modules missing translation updates.
+ *
+ * @see template_preprocess_locale_translation_update_info()
+ *
+ * @ingroup themeable
+ */
+#}
+<div class="locale-translation-update__wrapper" tabindex="0" role="button">
+  <span class="locale-translation-update__prefix visually-hidden">Show description</span>
+  {% if modules %}
+    {% set module_list = modules|safe_join(', ') %}
+    <span class="locale-translation-update__message">{% trans %}Updates for: {{ module_list }}{% endtrans %}</span>
+  {% elseif not_found %}
+    <span class="locale-translation-update__message">
+      {%- trans -%}
+        Missing translations for one project
+      {%- plural not_found|length -%}
+        Missing translations for @count projects
+      {%- endtrans -%}
+    </span>
+  {% endif %}
+  {% if updates or not_found %}
+    <div class="locale-translation-update__details">
+      {% if updates %}
+        <ul>
+          {% for update in updates %}
+            <li>{{ update.name }} ({{ update.timestamp|format_date('html_date') }})</li>
+          {% endfor %}
+        </ul>
+      {% endif %}
+      {% if not_found %}
+        {#
+          Prefix the missing updates list if there is an available updates lists
+          before it.
+        #}
+        {% if updates %}
+          {{ 'Missing translations for:'|t }}
+        {% endif %}
+        {% if not_found %}
+          <ul>
+            {% for update in not_found %}
+              <li>{{ update.name }} ({{ update.version|default('no version'|t) }}). {{ update.info }}</li>
+            {% endfor %}
+          </ul>
+        {% endif %}
+      {% endif %}
+    </div>
+  {% endif %}
+</div>
diff --git a/core/themes/stable/templates/admin/maintenance-task-list.html.twig b/core/themes/stable/templates/admin/maintenance-task-list.html.twig
new file mode 100644
index 0000000..8b120a4
--- /dev/null
+++ b/core/themes/stable/templates/admin/maintenance-task-list.html.twig
@@ -0,0 +1,25 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a list of maintenance tasks to perform.
+ *
+ * Available variables:
+ * - tasks: A list of maintenance tasks to perform. Each item in the list has
+ *   the following variables:
+ *   - item: The maintenance task.
+ *   - attributes: HTML attributes for the maintenance task.
+ *   - status: (optional) Text describing the status of the maintenance task,
+ *     'active' or 'done'.
+ *
+ * @ingroup themeable
+ */
+#}
+<h2 class="visually-hidden">{{ 'Installation tasks'|t }}</h2>
+<ol class="task-list">
+{% for task in tasks %}
+  <li{{ task.attributes }}>
+    {{ task.item }}
+    {% if task.status %}<span class="visually-hidden"> ({{ task.status }})</span>{% endif %}
+  </li>
+{% endfor %}
+</ol>
diff --git a/core/themes/stable/templates/admin/simpletest-result-summary.html.twig b/core/themes/stable/templates/admin/simpletest-result-summary.html.twig
new file mode 100644
index 0000000..2f87ed0
--- /dev/null
+++ b/core/themes/stable/templates/admin/simpletest-result-summary.html.twig
@@ -0,0 +1,22 @@
+{#
+/**
+ * @file
+ * Default theme implementation for simpletest result summaries.
+ *
+ * Available variables:
+ * - label: An optional label to be rendered before the results.
+ * - items: Pluralized summaries for each result type (number of passes, fails,
+ *   exceptions, and debug messages).
+ * - pass: The number of passes.
+ * - fail: The number of fails.
+ * - exception: The number of exceptions.
+ * - debug: The number of debug messages.
+ *
+ * @see template_preprocess_simpletest_result_summary()
+ *
+ * @ingroup themeable
+ */
+#}
+<div class="simpletest-{{ fail + exception == 0 ? 'pass' : 'fail' }}">
+  {{ label }} {{ items|join(', ') }}
+</div>
diff --git a/core/themes/stable/templates/admin/system-admin-index.html.twig b/core/themes/stable/templates/admin/system-admin-index.html.twig
new file mode 100644
index 0000000..17f8150
--- /dev/null
+++ b/core/themes/stable/templates/admin/system-admin-index.html.twig
@@ -0,0 +1,27 @@
+{#
+/**
+ * @file
+ * Default theme implementation for the admin index page.
+ *
+ * Available variables:
+ * - system_compact_link: Themed link to toggle compact view.
+ * - containers: A list of administrative containers keyed by position: left or
+ *   right. Each container in the list contains:
+ *   - blocks: A list of administrative blocks, rendered
+ *     through admin-block.html.twig.
+ *
+ * @see template_preprocess_system_admin_index()
+ *
+ * @ingroup themeable
+ */
+#}
+<div class="admin clearfix">
+  {{ system_compact_link }}
+  {% for position, blocks in containers %}
+    <div class="{{ position }} clearfix">
+      {% for block in blocks %}
+        {{ block }}
+      {% endfor %}
+    </div>
+  {% endfor %}
+</div>
diff --git a/core/themes/stable/templates/admin/system-config-form.html.twig b/core/themes/stable/templates/admin/system-config-form.html.twig
new file mode 100644
index 0000000..51e6fc7
--- /dev/null
+++ b/core/themes/stable/templates/admin/system-config-form.html.twig
@@ -0,0 +1,17 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a system settings form.
+ *
+ * This template will be used when a system config form specifies 'config_form'
+ * as its #theme callback.  Otherwise, by default, system config forms will be
+ * themed by theme_form(). This does not alter the appearance of a form at all,
+ * but is provided as a convenience for themers.
+ *
+ * Available variables:
+ * - form: The confirm form.
+ *
+ * @ingroup themeable
+ */
+#}
+{{ form }}
diff --git a/core/themes/stable/templates/admin/system-themes-page.html.twig b/core/themes/stable/templates/admin/system-themes-page.html.twig
new file mode 100644
index 0000000..6e65d76
--- /dev/null
+++ b/core/themes/stable/templates/admin/system-themes-page.html.twig
@@ -0,0 +1,76 @@
+{#
+/**
+ * @file
+ * Default theme implementation for the Appearance page.
+ *
+ * Available variables:
+ * - attributes: HTML attributes for the main container.
+ * - theme_groups: A list of theme groups. Each theme group contains:
+ *   - attributes: HTML attributes specific to this theme group.
+ *   - title: Title for the theme group.
+ *   - state: State of the theme group, e.g. installed or uninstalled.
+ *   - themes: A list of themes within the theme group. Each theme contains:
+ *     - attributes: HTML attributes specific to this theme.
+ *     - screenshot: A screenshot representing the theme.
+ *     - description: Description of the theme.
+ *     - name: Theme name.
+ *     - version: The theme's version number.
+ *     - is_default: Boolean indicating whether the theme is the default theme
+ *       or not.
+ *     - is_admin: Boolean indicating whether the theme is the admin theme or
+ *       not.
+ *     - notes: Identifies what context this theme is being used in, e.g.,
+ *       default theme, admin theme.
+ *     - incompatible: Text describing any compatibility issues.
+ *     - operations: A list of operation links, e.g., Settings, Enable, Disable,
+ *       etc. these links should only be displayed if the theme is compatible.
+ *
+ * @see template_preprocess_system_themes_page()
+ *
+ * @ingroup themeable
+ */
+#}
+<div{{ attributes }}>
+  {% for theme_group in theme_groups %}
+    {%
+      set theme_group_classes = [
+        'system-themes-list',
+        'system-themes-list-' ~ theme_group.state,
+        'clearfix',
+      ]
+    %}
+    <div{{ theme_group.attributes.addClass(theme_group_classes) }}>
+      <h2 class="system-themes-list__header">{{ theme_group.title }}</h2>
+      {% for theme in theme_group.themes %}
+        {%
+          set theme_classes = [
+            theme.is_default ? 'theme-default',
+            theme.is_admin ? 'theme-admin',
+            'theme-selector',
+            'clearfix',
+          ]
+        %}
+        <div{{ theme.attributes.addClass(theme_classes) }}>
+          {% if theme.screenshot %}
+            {{ theme.screenshot }}
+          {% endif %}
+          <div class="theme-info">
+            <h3 class="theme-info__header">
+              {{- theme.name }} {{ theme.version -}}
+              {% if theme.notes %}
+                ({{ theme.notes|safe_join(', ') }})
+              {%- endif -%}
+            </h3>
+            <div class="theme-info__description">{{ theme.description }}</div>
+            {# Display operation links if the theme is compatible. #}
+            {% if theme.incompatible %}
+              <div class="incompatible">{{ theme.incompatible }}</div>
+            {% else %}
+              {{ theme.operations }}
+            {% endif %}
+          </div>
+        </div>
+      {% endfor %}
+    </div>
+  {% endfor %}
+</div>
diff --git a/core/themes/stable/templates/admin/tablesort-indicator.html.twig b/core/themes/stable/templates/admin/tablesort-indicator.html.twig
new file mode 100644
index 0000000..3601c6d
--- /dev/null
+++ b/core/themes/stable/templates/admin/tablesort-indicator.html.twig
@@ -0,0 +1,28 @@
+{#
+/**
+ * @file
+ * Default theme implementation for displaying a tablesort indicator.
+ *
+ * Available variables:
+ * - style: Either 'asc' or 'desc', indicating the sorting direction.
+ *
+ * @see template_preprocess_tablesort_indicator()
+ *
+ * @ingroup themeable
+ */
+#}
+{%
+  set classes = [
+    'tablesort',
+    'tablesort--' ~ style,
+  ]
+%}
+<span{{ attributes.addClass(classes) }}>
+  <span class="visually-hidden">
+    {% if style == 'asc' -%}
+      {{ 'Sort ascending'|t }}
+    {% else -%}
+      {{ 'Sort descending'|t }}
+    {% endif %}
+  </span>
+</span>
diff --git a/core/themes/stable/templates/admin/update-last-check.html.twig b/core/themes/stable/templates/admin/update-last-check.html.twig
new file mode 100644
index 0000000..213f724
--- /dev/null
+++ b/core/themes/stable/templates/admin/update-last-check.html.twig
@@ -0,0 +1,23 @@
+{#
+/**
+ * @file
+ * Default theme implementation for the last time update data was checked.
+ *
+ * Available variables:
+ * - last: The timestamp that the site was last checked for updates.
+ * - time: The formatted time since the site last checked for updates.
+ * - link: A link to check for updates manually.
+ *
+ * @see template_preprocess_update_last_check()
+ *
+ * @ingroup themeable
+ */
+#}
+<p>
+  {% if last %}
+    {{ 'Last checked: @time ago'|t({'@time': time}) }}
+  {% else %}
+    {{ 'Last checked: never'|t }}
+  {% endif %}
+  ({{ link }})
+</p>
diff --git a/core/themes/stable/templates/admin/update-project-status.html.twig b/core/themes/stable/templates/admin/update-project-status.html.twig
new file mode 100644
index 0000000..4cc9a19
--- /dev/null
+++ b/core/themes/stable/templates/admin/update-project-status.html.twig
@@ -0,0 +1,106 @@
+{#
+/**
+ * @file
+ * Default theme implementation for the project status report.
+ *
+ * Available variables:
+ * - title: The project title.
+ * - url: The project url.
+ * - status: The project status.
+ *   - label: The project status label.
+ *   - attributes: HTML attributes for the project status.
+ *   - reason: The reason you should update the project.
+ *   - icon: The project status version indicator icon.
+ * - existing_version: The version of the installed project.
+ * - versions: The available versions of the project.
+ * - install_type: The type of project (e.g., dev).
+ * - datestamp: The date/time of a project version's release.
+ * - extras: HTML attributes and additional information about the project.
+ *   - attributes: HTML attributes for the extra item.
+ *   - label: The label for an extra item.
+ *   - data: The data about an extra item.
+ * - includes: The projects within the project.
+ * - disabled: The currently disabled projects in the project.
+ *
+ * @see template_preprocess_update_project_status()
+ *
+ * @ingroup themeable
+ */
+#}
+{%
+  set status_classes = [
+    project.status == constant('UPDATE_NOT_SECURE') ? 'project-update__status--security-error',
+    project.status == constant('UPDATE_REVOKED') ? 'project-update__status--revoked',
+    project.status == constant('UPDATE_NOT_SUPPORTED') ? 'project-update__status--not-supported',
+    project.status == constant('UPDATE_NOT_CURRENT') ? 'project-update__status--not-current',
+    project.status == constant('UPDATE_CURRENT') ? 'project-update__status--current',
+  ]
+%}
+<div{{ status.attributes.addClass('project-update__status', status_classes) }}>
+  {%- if status.label -%}
+    <span>{{ status.label }}</span>
+  {%- else -%}
+    {{ status.reason }}
+  {%- endif %}
+  <span class="project-update__status-icon">
+    {{ status.icon }}
+  </span>
+</div>
+
+<div class="project-update__title">
+  {%- if url -%}
+    <a href="{{ url }}">{{ title }}</a>
+  {%- else -%}
+    {{ title }}
+  {%- endif %}
+  {{ existing_version }}
+  {% if install_type == 'dev' and datestamp %}
+    <span class="project-update__version-date">({{ datestamp }})</span>
+  {% endif %}
+</div>
+
+{% if versions %}
+  {% for version in versions %}
+    {{ version }}
+  {% endfor %}
+{% endif %}
+
+{%
+  set extra_classes = [
+    project.status == constant('UPDATE_NOT_SECURE') ? 'project-not-secure',
+    project.status == constant('UPDATE_REVOKED') ? 'project-revoked',
+    project.status == constant('UPDATE_NOT_SUPPORTED') ? 'project-not-supported',
+  ]
+%}
+<div class="project-updates__details">
+  {% if extras %}
+    <div class="extra">
+      {% for extra in extras %}
+        <div{{ extra.attributes.addClass(extra_classes) }}>
+          {{ extra.label }}: {{ extra.data }}
+        </div>
+      {% endfor %}
+    </div>
+  {% endif %}
+  {% set includes = includes|join(', ') %}
+  {% if disabled %}
+    {{ 'Includes:'|t }}
+    <ul>
+      <li>
+        {% trans %}
+          Enabled: {{ includes|placeholder }}
+        {% endtrans %}
+      </li>
+      <li>
+        {% set disabled = disabled|join(', ') %}
+        {% trans %}
+          Disabled: {{ disabled|placeholder }}
+        {% endtrans %}
+      </li>
+    </ul>
+  {% else %}
+    {% trans %}
+      Includes: {{ includes|placeholder }}
+    {% endtrans %}
+  {% endif %}
+</div>
diff --git a/core/themes/stable/templates/admin/update-report.html.twig b/core/themes/stable/templates/admin/update-report.html.twig
new file mode 100644
index 0000000..ae121cc
--- /dev/null
+++ b/core/themes/stable/templates/admin/update-report.html.twig
@@ -0,0 +1,25 @@
+{#
+/**
+ * @file
+ * Default theme implementation for the project status report.
+ *
+ * Available variables:
+ * - last_checked: Themed last time update data was checked.
+ * - no_updates_message: Message when there are no project updates.
+ * - project_types: A list of project types.
+ *   - label: The project type label.
+ *   - table: The project status table.
+ *
+ * @see template_preprocess_update_report()
+ *
+ * @ingroup themeable
+ */
+#}
+{{ last_checked }}
+
+{% for project_type in project_types %}
+  <h3>{{ project_type.label }}</h3>
+  {{ project_type.table }}
+{% else %}
+  <p>{{ no_updates_message }}</p>
+{% endfor %}
diff --git a/core/themes/stable/templates/admin/update-version.html.twig b/core/themes/stable/templates/admin/update-version.html.twig
new file mode 100644
index 0000000..2d5520f
--- /dev/null
+++ b/core/themes/stable/templates/admin/update-version.html.twig
@@ -0,0 +1,38 @@
+{#
+/**
+ * @file
+ * Default theme implementation for the version display of a project.
+ *
+ * Available variables:
+ * - attributes: HTML attributes suitable for a container element.
+ * - title: The title of the project.
+ * - version:  A list of data about the latest released version, containing:
+ *   - version: The version number.
+ *   - date: The date of the release.
+ *   - download_link: The URL for the downloadable file.
+ *   - release_link: The URL for the release notes.
+ *
+ * @see template_preprocess_update_version()
+ *
+ * @ingroup themeable
+ */
+#}
+<div class="{{ attributes.class }} project-update__version"{{ attributes|without('class') }}>
+  <div class="clearfix">
+    <div class="project-update__version-title layout-column layout-column--quarter">{{ title }}</div>
+    <div class="project-update__version-details layout-column layout-column--quarter">
+      <a href="{{ version.release_link }}">{{ version.version }}</a>
+      <span class="project-update__version-date">({{ version.date|date('Y-M-d') }})</span>
+    </div>
+    <div class="layout-column layout-column--half">
+      <ul class="project-update__version-links">
+        <li class="project-update__download-link">
+          <a href="{{ version.download_link }}">{{ 'Download'|t }}</a>
+        </li>
+        <li class="project-update__release-notes-link">
+          <a href="{{ version.release_link }}">{{ 'Release notes'|t }}</a>
+        </li>
+      </ul>
+    </div>
+  </div>
+</div>
diff --git a/core/themes/stable/templates/admin/views-ui-build-group-filter-form.html.twig b/core/themes/stable/templates/admin/views-ui-build-group-filter-form.html.twig
new file mode 100644
index 0000000..0e05c27
--- /dev/null
+++ b/core/themes/stable/templates/admin/views-ui-build-group-filter-form.html.twig
@@ -0,0 +1,57 @@
+{#
+/**
+ * @file
+ * Default theme implementation for Views UI build group filter form.
+ *
+ * Available variables:
+ * - form: A render element representing the form. Contains the following:
+ *   - form_description: The exposed filter's description.
+ *   - expose_button: The button to toggle the expose filter form.
+ *   - group_button: Toggle options between single and grouped filters.
+ *   - label: A filter label input field.
+ *   - description: A filter description field.
+ *   - value: The filters available values.
+ *   - optional: A checkbox to require this filter or not.
+ *   - remember: A checkbox to remember selected filter value(s) (per user).
+ *   - widget: Radio Buttons to select the filter widget.
+ *   - add_group: A button to add another row to the table.
+ *   - more: A details element for additional field exposed filter fields.
+ * - table: A rendered table element of the group filter form.
+ *
+ * @see template_preprocess_views_ui_build_group_filter_form()
+ *
+ * @ingroup themeable
+ */
+#}
+{{ form.form_description }}
+{{ form.expose_button }}
+{{ form.group_button }}
+<div class="views-left-40">
+  {{ form.optional }}
+  {{ form.remember }}
+</div>
+<div class="views-right-60">
+  {{ form.widget }}
+  {{ form.label }}
+  {{ form.description }}
+</div>
+{#
+  Render the rest of the form elements excluding elements that are rendered
+  elsewhere.
+#}
+{{ form|without(
+    'form_description',
+    'expose_button',
+    'group_button',
+    'optional',
+    'remember',
+    'widget',
+    'label',
+    'description',
+    'add_group',
+    'more'
+  )
+}}
+{{ table }}
+{{ form.add_group }}
+{{ form.more }}
diff --git a/core/themes/stable/templates/admin/views-ui-container.html.twig b/core/themes/stable/templates/admin/views-ui-container.html.twig
new file mode 100644
index 0000000..d45b158
--- /dev/null
+++ b/core/themes/stable/templates/admin/views-ui-container.html.twig
@@ -0,0 +1,15 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a generic views UI container/wrapper.
+ *
+ * Available variables:
+ * - attributes: HTML attributes to apply to the container element.
+ * - children: The remaining elements such as dropbuttons and tabs.
+ *
+ * @see template_preprocess_views_ui_container()
+ *
+ * @ingroup themeable
+ */
+#}
+<div{{ attributes }}>{{ children }}</div>
diff --git a/core/themes/stable/templates/admin/views-ui-display-tab-bucket.html.twig b/core/themes/stable/templates/admin/views-ui-display-tab-bucket.html.twig
new file mode 100644
index 0000000..88ab1bd
--- /dev/null
+++ b/core/themes/stable/templates/admin/views-ui-display-tab-bucket.html.twig
@@ -0,0 +1,35 @@
+{#
+/**
+ * @file
+ * Default theme implementation for each "box" on the display query edit screen.
+ *
+ * Available variables:
+ * - attributes: HTML attributes to apply to the container element.
+ * - actions: Action links such as "Add", "And/Or, Rearrange" for the content.
+ * - title: The title of the bucket, e.g. "Fields", "Filter Criteria", etc.
+ * - content: Content items such as fields or settings in this container.
+ * - name: The name of the bucket, e.g. "Fields", "Filter Criteria", etc.
+ * - overridden: A boolean indicating the setting has been overridden from the
+ *   default.
+ *
+ * @see template_preprocess_views_ui_display_tab_bucket()
+ *
+ * @ingroup themeable
+ */
+#}
+{%
+  set classes = [
+    'views-ui-display-tab-bucket',
+    name ? name|clean_class,
+    overridden ? 'overridden',
+  ]
+%}
+<div{{ attributes.addClass(classes) }}>
+  {% if title -%}
+    <h3 class="views-ui-display-tab-bucket__title">{{ title }}</h3>
+  {%- endif %}
+  {% if actions -%}
+    {{ actions }}
+  {%- endif %}
+  {{ content }}
+</div>
diff --git a/core/themes/stable/templates/admin/views-ui-display-tab-setting.html.twig b/core/themes/stable/templates/admin/views-ui-display-tab-setting.html.twig
new file mode 100644
index 0000000..c90e5c9
--- /dev/null
+++ b/core/themes/stable/templates/admin/views-ui-display-tab-setting.html.twig
@@ -0,0 +1,37 @@
+{#
+/**
+ * @file
+ * Default theme implementation for Views UI display tab settings.
+ *
+ * Template for each row inside the "boxes" on the display query edit screen.
+ *
+ * Available variables:
+ * - attributes: HTML attributes such as class for the container.
+ * - description: The description or label for this setting.
+ * - settings_links: A list of links for this setting.
+ * - defaulted: A boolean indicating the setting is in its default state.
+ * - overridden: A boolean indicating the setting has been overridden from the
+ *   default.
+ *
+ * @see template_preprocess_views_ui_display_tab_setting()
+ *
+ * @ingroup themeable
+ */
+#}
+{%
+  set classes = [
+    'views-display-setting',
+    'clearfix',
+    'views-ui-display-tab-setting',
+    defaulted ? 'defaulted',
+    overridden ? 'overridden',
+]
+%}
+<div{{ attributes.addClass(classes) }}>
+  {% if description -%}
+    <span class="label">{{ description }}</span>
+  {%- endif %}
+  {% if settings_links %}
+    {{ settings_links|safe_join('<span class="label">&nbsp;|&nbsp;</span>') }}
+  {% endif %}
+</div>
diff --git a/core/themes/stable/templates/admin/views-ui-expose-filter-form.html.twig b/core/themes/stable/templates/admin/views-ui-expose-filter-form.html.twig
new file mode 100644
index 0000000..dfc4775
--- /dev/null
+++ b/core/themes/stable/templates/admin/views-ui-expose-filter-form.html.twig
@@ -0,0 +1,67 @@
+{#
+/**
+ * @file
+ * Default theme implementation for exposed filter form.
+ *
+ * Available variables:
+ * - form_description: The exposed filter's description.
+ * - expose_button: The button to toggle the expose filter form.
+ * - group_button: Toggle options between single and grouped filters.
+ * - required: A checkbox to require this filter or not.
+ * - label: A filter label input field.
+ * - description: A filter description field.
+ * - operator: The operators for how the filters value should be treated.
+ *   - #type: The operator type.
+ * - value: The filters available values.
+ * - use_operator: Checkbox to allow the user to expose the operator.
+ * - more: A details element for additional field exposed filter fields.
+ *
+ * @ingroup themeable
+ */
+#}
+{{ form.form_description }}
+{{ form.expose_button }}
+{{ form.group_button }}
+{{ form.required }}
+{{ form.label }}
+{{ form.description }}
+
+{{ form.operator }}
+{{ form.value }}
+
+{% if form.use_operator %}
+  <div class="views-left-40">
+  {{ form.use_operator }}
+  </div>
+{% endif %}
+
+{#
+  Collect a list of elements printed to exclude when printing the
+  remaining elements.
+#}
+{% set remaining_form = form|without(
+  'form_description',
+  'expose_button',
+  'group_button',
+  'required',
+  'label',
+  'description',
+  'operator',
+  'value',
+  'use_operator',
+  'more'
+  )
+%}
+
+{#
+  Only output the right column markup if there's a left column to begin with.
+#}
+{% if form.operator['#type'] %}
+  <div class="views-right-60">
+  {{ remaining_form }}
+  </div>
+{% else %}
+  {{ remaining_form }}
+{% endif %}
+
+{{ form.more }}
diff --git a/core/themes/stable/templates/admin/views-ui-rearrange-filter-form.html.twig b/core/themes/stable/templates/admin/views-ui-rearrange-filter-form.html.twig
new file mode 100644
index 0000000..b003a89
--- /dev/null
+++ b/core/themes/stable/templates/admin/views-ui-rearrange-filter-form.html.twig
@@ -0,0 +1,27 @@
+{#
+/**
+ * @file
+ * Default theme implementation for Views UI rearrange filter form.
+ *
+ * Available variables:
+ * - form: A render element representing the form.
+ * - grouping: A flag whether or not there is more than one group.
+ * - ungroupable_table: The ungroupable filter table.
+ * - table: The groupable filter table.
+ *
+ * @see template_preprocess_views_ui_rearrange_filter_form()
+ *
+ * @ingroup themeable
+ */
+#}
+{{ form.override }}
+<div class="scroll" data-drupal-views-scroll>
+  {% if grouping %}
+    {{ form.filter_groups.operator }}
+  {% else %}
+    {{ form.filter_groups.groups.0 }}
+  {% endif %}
+  {{ ungroupable_table }}
+  {{ table }}
+</div>
+{{ form|without('override', 'filter_groups', 'remove_groups', 'filters') }}
diff --git a/core/themes/stable/templates/admin/views-ui-style-plugin-table.html.twig b/core/themes/stable/templates/admin/views-ui-style-plugin-table.html.twig
new file mode 100644
index 0000000..27463f5
--- /dev/null
+++ b/core/themes/stable/templates/admin/views-ui-style-plugin-table.html.twig
@@ -0,0 +1,18 @@
+{#
+/**
+ * @file
+ * Default template for the settings of a table style views display.
+ *
+ * Available variables:
+ * - table: A table of options for each field in this display.
+ * - form: Any remaining form fields not included in the table.
+ *   - description_markup: An overview for the settings of this display.
+ *
+ * @see template_preprocess_views_ui_style_plugin_table()
+ *
+ * @ingroup themeable
+ */
+#}
+{{ form.description_markup }}
+{{ table }}
+{{ form }}
diff --git a/core/themes/stable/templates/admin/views-ui-view-info.html.twig b/core/themes/stable/templates/admin/views-ui-view-info.html.twig
new file mode 100644
index 0000000..fa32a0c
--- /dev/null
+++ b/core/themes/stable/templates/admin/views-ui-view-info.html.twig
@@ -0,0 +1,28 @@
+{#
+/**
+ * @file
+ * Default theme implementation for basic administrative info about a View.
+ *
+ * Available variables:
+ * - displays: List of displays.
+ *
+ * @ingroup themeable
+ */
+#}
+<h3 class="views-ui-view-title views-table-filter-text-source">{{ view.label }}</h3>
+<div class="views-ui-view-displays">
+  {% if displays %}
+    {% trans %}
+      Display
+    {% plural displays %}
+      Displays
+    {% endtrans %}:
+    <em>{{ displays|safe_join(', ') }}</em>
+  {% else %}
+    {{ 'None'|t }}
+  {% endif %}
+</div>
+<div class="views-ui-view-machine-name">
+  {{ 'Machine name:'|t }}
+  <span class="views-table-filter-text-source">{{ view.id }}</span>
+</div>
diff --git a/core/themes/stable/templates/admin/views-ui-view-preview-section.html.twig b/core/themes/stable/templates/admin/views-ui-view-preview-section.html.twig
new file mode 100644
index 0000000..cfc840f
--- /dev/null
+++ b/core/themes/stable/templates/admin/views-ui-view-preview-section.html.twig
@@ -0,0 +1,20 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a views UI preview section.
+ *
+ * Available variables:
+ * - title: The human readable section title.
+ * - links: A list of contextual links.
+ * - content: The content for this section preview.
+ *
+ * @see template_preprocess_views_ui_view_preview_section()
+ *
+ * @ingroup themeable
+ */
+#}
+<h1 class="section-title">{{ title }}</h1>
+{% if links %}
+  <div class="contextual">{{ links }}</div>
+{% endif %}
+<div class="preview-section">{{ content }}</div>
diff --git a/core/themes/classy/templates/block/block--local-actions-block.html.twig b/core/themes/stable/templates/block/block--local-actions-block.html.twig
similarity index 63%
copy from core/themes/classy/templates/block/block--local-actions-block.html.twig
copy to core/themes/stable/templates/block/block--local-actions-block.html.twig
index 2a0f5c4..f272329 100644
--- a/core/themes/classy/templates/block/block--local-actions-block.html.twig
+++ b/core/themes/stable/templates/block/block--local-actions-block.html.twig
@@ -1,4 +1,4 @@
-{% extends "@block/block.html.twig" %}
+{% extends "@stable/block/block.html.twig" %}
 {#
 /**
  * @file
@@ -7,6 +7,6 @@
 #}
 {% block content %}
   {% if content %}
-    <nav class="action-links">{{ content }}</nav>
+    <nav>{{ content }}</nav>
   {% endif %}
 {% endblock %}
diff --git a/core/themes/stable/templates/block/block--system-branding-block.html.twig b/core/themes/stable/templates/block/block--system-branding-block.html.twig
new file mode 100644
index 0000000..2a98687
--- /dev/null
+++ b/core/themes/stable/templates/block/block--system-branding-block.html.twig
@@ -0,0 +1,28 @@
+{% extends "block.html.twig" %}
+{#
+/**
+ * @file
+ * Default theme implementation for a branding block.
+ *
+ * Each branding element variable (logo, name, slogan) is only available if
+ * enabled in the block configuration.
+ *
+ * Available variables:
+ * - site_logo: Logo for site as defined in Appearance or theme settings.
+ * - site_name: Name for site as defined in Site information settings.
+ * - site_slogan: Slogan for site as defined in Site information settings.
+ *
+ * @ingroup themeable
+ */
+#}
+{% block content %}
+  {% if site_logo %}
+    <a href="{{ path('<front>') }}" title="{{ 'Home'|t }}" rel="home">
+      <img src="{{ site_logo }}" alt="{{ 'Home'|t }}" />
+    </a>
+  {% endif %}
+  {% if site_name %}
+    <a href="{{ path('<front>') }}" title="{{ 'Home'|t }}" rel="home">{{ site_name }}</a>
+  {% endif %}
+  {{ site_slogan }}
+{% endblock %}
diff --git a/core/themes/stable/templates/block/block--system-menu-block.html.twig b/core/themes/stable/templates/block/block--system-menu-block.html.twig
new file mode 100644
index 0000000..617bb52
--- /dev/null
+++ b/core/themes/stable/templates/block/block--system-menu-block.html.twig
@@ -0,0 +1,51 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a menu block.
+ *
+ * Available variables:
+ * - plugin_id: The ID of the block implementation.
+ * - label: The configured label of the block if visible.
+ * - configuration: A list of the block's configuration values.
+ *   - label: The configured label for the block.
+ *   - label_display: The display settings for the label.
+ *   - module: The module that provided this block plugin.
+ *   - cache: The cache settings.
+ *   - Block plugin specific settings will also be stored here.
+ * - content: The content of this block.
+ * - attributes: HTML attributes for the containing element.
+ *   - id: A valid HTML ID and guaranteed unique.
+ * - title_attributes: HTML attributes for the title element.
+ * - content_attributes: HTML attributes for the content element.
+ * - title_prefix: Additional output populated by modules, intended to be
+ *   displayed in front of the main title tag that appears in the template.
+ * - title_suffix: Additional output populated by modules, intended to be
+ *   displayed after the main title tag that appears in the template.
+ *
+ * Headings should be used on navigation menus that consistently appear on
+ * multiple pages. When this menu block's label is configured to not be
+ * displayed, it is automatically made invisible using the 'visually-hidden' CSS
+ * class, which still keeps it visible for screen-readers and assistive
+ * technology. Headings allow screen-reader and keyboard only users to navigate
+ * to or skip the links.
+ * See http://juicystudio.com/article/screen-readers-display-none.php and
+ * http://www.w3.org/TR/WCAG-TECHS/H42.html for more information.
+ *
+ * @ingroup themeable
+ */
+#}
+{% set heading_id = attributes.id ~ '-menu'|clean_id %}
+<nav role="navigation" aria-labelledby="{{ heading_id }}"{{ attributes|without('role', 'aria-labelledby') }}>
+  {# Label. If not displayed, we still provide it for screen readers. #}
+  {% if not configuration.label_display %}
+    {% set title_attributes = title_attributes.addClass('visually-hidden') %}
+  {% endif %}
+  {{ title_prefix }}
+  <h2{{ title_attributes }}>{{ configuration.label }}</h2>
+  {{ title_suffix }}
+
+  {# Menu. #}
+  {% block content %}
+    {{ content }}
+  {% endblock %}
+</nav>
diff --git a/core/themes/stable/templates/block/block--system-messages-block.html.twig b/core/themes/stable/templates/block/block--system-messages-block.html.twig
new file mode 100644
index 0000000..f35af14
--- /dev/null
+++ b/core/themes/stable/templates/block/block--system-messages-block.html.twig
@@ -0,0 +1,15 @@
+{#
+/**
+ * @file
+ * Default theme implementation for the messages block.
+ *
+ * Removes wrapper elements from block so that empty block does not appear when
+ * there are no messages.
+ *
+ * Available variables:
+ * - content: The content of this block.
+ *
+ * @ingroup themeable
+ */
+#}
+{{ content }}
diff --git a/core/themes/stable/templates/block/block.html.twig b/core/themes/stable/templates/block/block.html.twig
new file mode 100644
index 0000000..11cc17e
--- /dev/null
+++ b/core/themes/stable/templates/block/block.html.twig
@@ -0,0 +1,40 @@
+{#
+/**
+ * @file
+ * Default theme implementation to display a block.
+ *
+ * Available variables:
+ * - plugin_id: The ID of the block implementation.
+ * - label: The configured label of the block if visible.
+ * - configuration: A list of the block's configuration values.
+ *   - label: The configured label for the block.
+ *   - label_display: The display settings for the label.
+ *   - module: The module that provided this block plugin.
+ *   - cache: The cache settings.
+ *   - Block plugin specific settings will also be stored here.
+ * - content: The content of this block.
+ * - attributes: array of HTML attributes populated by modules, intended to
+ *   be added to the main container tag of this template.
+ *   - id: A valid HTML ID and guaranteed unique.
+ * - title_attributes: Same as attributes, except applied to the main title
+ *   tag that appears in the template.
+ * - title_prefix: Additional output populated by modules, intended to be
+ *   displayed in front of the main title tag that appears in the template.
+ * - title_suffix: Additional output populated by modules, intended to be
+ *   displayed after the main title tag that appears in the template.
+ *
+ * @see template_preprocess_block()
+ *
+ * @ingroup themeable
+ */
+#}
+<div{{ attributes }}>
+  {{ title_prefix }}
+  {% if label %}
+    <h2{{ title_attributes }}>{{ label }}</h2>
+  {% endif %}
+  {{ title_suffix }}
+  {% block content %}
+    {{ content }}
+  {% endblock %}
+</div>
diff --git a/core/themes/stable/templates/content-edit/file-managed-file.html.twig b/core/themes/stable/templates/content-edit/file-managed-file.html.twig
new file mode 100644
index 0000000..39de9b4
--- /dev/null
+++ b/core/themes/stable/templates/content-edit/file-managed-file.html.twig
@@ -0,0 +1,23 @@
+{#
+/**
+ * @file
+ * Default theme implementation to display a file form widget.
+ *
+ * Available variables:
+ * - element: Form element for the file upload.
+ * - attributes: HTML attributes for the containing element.
+ *
+ * @see template_preprocess_file_managed_file()
+ *
+ * @ingroup themeable
+ */
+#}
+{%
+  set classes = [
+    'js-form-managed-file',
+    'form-managed-file',
+  ]
+%}
+<div{{ attributes.addClass(classes) }}>
+  {{ element }}
+</div>
diff --git a/core/themes/stable/templates/content-edit/file-upload-help.html.twig b/core/themes/stable/templates/content-edit/file-upload-help.html.twig
new file mode 100644
index 0000000..8fa6b3e
--- /dev/null
+++ b/core/themes/stable/templates/content-edit/file-upload-help.html.twig
@@ -0,0 +1,14 @@
+{#
+/**
+ * @file
+ * Default theme implementation to display help text for file fields.
+ *
+ * Available variables:
+ * - descriptions: Lines of help text for uploading a file.
+ *
+ * @see template_preprocess_file_upload_help()
+ *
+ * @ingroup themeable
+ */
+#}
+{{ descriptions|safe_join('<br />') }}
diff --git a/core/themes/stable/templates/content-edit/file-widget-multiple.html.twig b/core/themes/stable/templates/content-edit/file-widget-multiple.html.twig
new file mode 100644
index 0000000..25534a5
--- /dev/null
+++ b/core/themes/stable/templates/content-edit/file-widget-multiple.html.twig
@@ -0,0 +1,16 @@
+{#
+/**
+ * @file
+ * Default theme implementation to display a multi file form widget.
+ *
+ * Available variables:
+ * - table: Table of previously uploaded files.
+ * - element: The form element for uploading another file.
+ *
+ * @see template_preprocess_file_widget_multiple()
+ *
+ * @ingroup themeable
+ */
+#}
+{{ table }}
+{{ element }}
diff --git a/core/themes/stable/templates/content-edit/file-widget.html.twig b/core/themes/stable/templates/content-edit/file-widget.html.twig
new file mode 100644
index 0000000..892ed3d
--- /dev/null
+++ b/core/themes/stable/templates/content-edit/file-widget.html.twig
@@ -0,0 +1,17 @@
+{#
+/**
+ * @file
+ * Default theme implementation to display a file widget.
+ *
+ * Available variables:
+ * - element: Form element for the managed file.
+ * - attributes: Remaining HTML attributes for the containing element.
+ *
+ * @see template_preprocess_file_widget()
+ *
+ * @ingroup themeable
+ */
+#}
+<div{{ attributes }}>
+  {{ element }}
+</div>
diff --git a/core/themes/stable/templates/content-edit/filter-caption.html.twig b/core/themes/stable/templates/content-edit/filter-caption.html.twig
new file mode 100644
index 0000000..c90e35b
--- /dev/null
+++ b/core/themes/stable/templates/content-edit/filter-caption.html.twig
@@ -0,0 +1,18 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a filter caption.
+ *
+ * Returns HTML for a captioned image, audio, video or other tag.
+ *
+ * Available variables
+ * - string node: The complete HTML tag whose contents are being captioned.
+ * - string tag: The name of the HTML tag whose contents are being captioned.
+ * - string caption: The caption text.
+ * - string classes: The classes of the captioned HTML tag.
+ */
+#}
+<figure role="group"{%- if classes %} class="{{ classes }}"{%- endif %}>
+{{ node }}
+<figcaption>{{ caption }}</figcaption>
+</figure>
diff --git a/core/themes/stable/templates/content-edit/filter-guidelines.html.twig b/core/themes/stable/templates/content-edit/filter-guidelines.html.twig
new file mode 100644
index 0000000..317cfa6
--- /dev/null
+++ b/core/themes/stable/templates/content-edit/filter-guidelines.html.twig
@@ -0,0 +1,25 @@
+{#
+/**
+ * @file
+ * Default theme implementation for guidelines for a text format.
+ *
+ * Available variables:
+ * - format: Contains information about the current text format, including the
+ *   following:
+ *   - name: The name of the text format, potentially unsafe and needs to be
+ *     escaped.
+ *   - format: The machine name of the text format, e.g. 'basic_html'.
+ * - attributes: HTML attributes for the containing element.
+ * - tips: Descriptions and a CSS ID in the form of 'module-name/filter-id'
+ *   (only used when 'long' is TRUE) for each filter in one or more text
+ *   formats.
+ *
+ * @see template_preprocess_filter_tips()
+ *
+ * @ingroup themeable
+ */
+#}
+<div{{ attributes }}>
+  <h4>{{ format.label }}</h4>
+  {{ tips }}
+</div>
diff --git a/core/themes/stable/templates/content-edit/filter-tips.html.twig b/core/themes/stable/templates/content-edit/filter-tips.html.twig
new file mode 100644
index 0000000..b9ed2d6
--- /dev/null
+++ b/core/themes/stable/templates/content-edit/filter-tips.html.twig
@@ -0,0 +1,46 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a set of filter tips.
+ *
+ * Available variables:
+ * - tips: Descriptions and a CSS ID in the form of 'module-name/filter-id'
+ *   (only used when 'long' is TRUE) for each filter in one or more text
+ *   formats.
+ * - long: A flag indicating whether the passed-in filter tips contain extended
+ *   explanations, i.e. intended to be output on the path 'filter/tips'
+ *   (TRUE), or are in a short format, i.e. suitable to be displayed below a
+ *   form element. Defaults to FALSE.
+ * - multiple: A flag indicating there is more than one filter tip.
+ *
+ * @see template_preprocess_filter_tips()
+ *
+ * @ingroup themeable
+ */
+#}
+{% if multiple %}
+  <h2>{{ 'Text Formats'|t }}</h2>
+{% endif %}
+
+{% if tips|length %}
+  {% for name, tip in tips %}
+
+    {% if multiple %}
+      <div{{ attributes }}>
+      <h3>{{ tip.name }}</h3>
+    {% endif %}
+
+    {% if tip.list|length %}
+      <ul>
+      {% for item in tip.list %}
+        <li{{ tip.attributes }}>{{ item.tip }}</li>
+      {% endfor %}
+      </ul>
+    {% endif %}
+
+    {% if multiple %}
+      </div>
+    {% endif %}
+
+  {% endfor %}
+{% endif %}
diff --git a/core/themes/stable/templates/content-edit/image-widget.html.twig b/core/themes/stable/templates/content-edit/image-widget.html.twig
new file mode 100644
index 0000000..3a3d3da
--- /dev/null
+++ b/core/themes/stable/templates/content-edit/image-widget.html.twig
@@ -0,0 +1,19 @@
+{#
+/**
+ * @file
+ * Default theme implementation for an image field widget.
+ *
+ * Available variables:
+ * - attributes: HTML attributes for the containing element.
+ * - data: Render elements of the image widget.
+ *
+ * @see template_preprocess_image_widget()
+ *
+ * @ingroup themeable
+ */
+#}
+<div{{ attributes }}>
+  {{ data.preview }}
+  {# Render widget data without the image preview that was output already. #}
+  {{ data|without('preview') }}
+</div>
diff --git a/core/themes/stable/templates/content-edit/node-add-list.html.twig b/core/themes/stable/templates/content-edit/node-add-list.html.twig
new file mode 100644
index 0000000..c64751b
--- /dev/null
+++ b/core/themes/stable/templates/content-edit/node-add-list.html.twig
@@ -0,0 +1,32 @@
+{#
+/**
+ * @file
+ * Default theme implementation to list node types available for adding content.
+ *
+ * This list is displayed on the Add content admin page.
+ *
+ * Available variables:
+ * - types: A list of content types, each with the following properties:
+ *   - add_link: Link to create a piece of content of this type.
+ *   - description: Description of this type of content.
+ *
+ * @see template_preprocess_node_add_list()
+ *
+ * @ingroup themeable
+ */
+#}
+{% if types is not empty %}
+  <dl>
+    {% for type in types %}
+      <dt>{{ type.add_link }}</dt>
+      <dd>{{ type.description }}</dd>
+    {% endfor %}
+  </dl>
+{% else %}
+  <p>
+    {% set create_content = path('node.type_add') %}
+    {% trans %}
+      You have not created any content types yet. Go to the <a href="{{ create_content }}">content type creation page</a> to add a new content type.
+    {% endtrans %}
+  </p>
+{% endif %}
diff --git a/core/themes/stable/templates/content-edit/node-edit-form.html.twig b/core/themes/stable/templates/content-edit/node-edit-form.html.twig
new file mode 100644
index 0000000..fa455e6
--- /dev/null
+++ b/core/themes/stable/templates/content-edit/node-edit-form.html.twig
@@ -0,0 +1,20 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a node edit form.
+ *
+ * Two column template for the node add/edit form.
+ *
+ * This template will be used when a node edit form specifies 'node_edit_form'
+ * as its #theme callback.  Otherwise, by default, node add/edit forms will be
+ * themed by form.html.twig.
+ *
+ * Available variables:
+ * - form: The node add/edit form.
+ *
+ * @see seven_form_node_form_alter()
+ *
+ * @ingroup themeable
+ */
+#}
+{{ form }}
diff --git a/core/themes/stable/templates/content-edit/text-format-wrapper.html.twig b/core/themes/stable/templates/content-edit/text-format-wrapper.html.twig
new file mode 100644
index 0000000..de61d92
--- /dev/null
+++ b/core/themes/stable/templates/content-edit/text-format-wrapper.html.twig
@@ -0,0 +1,23 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a text format-enabled form element.
+ *
+ * Available variables:
+ * - children: Text format element children.
+ * - description: Text format element description.
+ * - attributes: HTML attributes for the containing element.
+ * - aria_description: Flag for whether or not an ARIA description has been
+ *   added to the description container.
+ *
+ * @see template_preprocess_text_format_wrapper()
+ *
+ * @ingroup themeable
+ */
+#}
+<div class="js-text-format-wrapper js-form-item form-item">
+  {{ children }}
+  {% if description %}
+    <div{{ attributes }}>{{ description }}</div>
+  {% endif %}
+</div>
diff --git a/core/themes/stable/templates/content/aggregator-item.html.twig b/core/themes/stable/templates/content/aggregator-item.html.twig
new file mode 100644
index 0000000..2391ad8
--- /dev/null
+++ b/core/themes/stable/templates/content/aggregator-item.html.twig
@@ -0,0 +1,26 @@
+{#
+/**
+ * @file
+ * Default theme implementation to present a feed item in an aggregator page.
+ *
+ * Available variables:
+ * - url: URL to the originating feed item.
+ * - title: Title of the feed item.
+ * - content: All field items. Use {{ content }} to print them all,
+ *   or print a subset such as {{ content.field_example }}. Use
+ *   {{ content|without('field_example') }} to temporarily suppress the printing
+ *   of a given element.
+ *
+ * @see template_preprocess_aggregator_item()
+ *
+ * @ingroup themeable
+ */
+#}
+<article{{ attributes }}>
+  {{ title_prefix }}
+  <h3>
+    <a href="{{ url }}">{{ title }}</a>
+  </h3>
+  {{ title_suffix }}
+  {{ content }}
+</article>
diff --git a/core/themes/stable/templates/content/book-node-export-html.html.twig b/core/themes/stable/templates/content/book-node-export-html.html.twig
new file mode 100644
index 0000000..39a41fc
--- /dev/null
+++ b/core/themes/stable/templates/content/book-node-export-html.html.twig
@@ -0,0 +1,22 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a single node in a printer-friendly outline.
+ *
+ * Available variables:
+ * - node: Fully loaded node.
+ * - depth: Depth of the current node inside the outline.
+ * - title: Node title.
+ * - content: Node content.
+ * - children: All the child nodes recursively rendered through this file.
+ *
+ * @see template_preprocess_book_node_export_html()
+ *
+ * @ingroup themeable
+ */
+#}
+<article>
+  <h1>{{ title }}</h1>
+  {{ content }}
+  {{ children }}
+</article>
diff --git a/core/themes/stable/templates/content/comment.html.twig b/core/themes/stable/templates/content/comment.html.twig
new file mode 100644
index 0000000..14c52eb
--- /dev/null
+++ b/core/themes/stable/templates/content/comment.html.twig
@@ -0,0 +1,102 @@
+{#
+/**
+ * @file
+ * Default theme implementation for comments.
+ *
+ * Available variables:
+ * - author: Comment author. Can be a link or plain text.
+ * - content: The content-related items for the comment display. Use
+ *   {{ content }} to print them all, or print a subset such as
+ *   {{ content.field_example }}. Use the following code to temporarily suppress
+ *   the printing of a given child element:
+ *   @code
+ *   {{ content|without('field_example') }}
+ *   @endcode
+ * - created: Formatted date and time for when the comment was created.
+ *   Preprocess functions can reformat it by calling format_date() with the
+ *   desired parameters on the 'comment.created' variable.
+ * - changed: Formatted date and time for when the comment was last changed.
+ *   Preprocess functions can reformat it by calling format_date() with the
+ *   desired parameters on the 'comment.changed' variable.
+ * - permalink: Comment permalink.
+ * - submitted: Submission information created from author and created
+ *   during template_preprocess_comment().
+ * - user_picture: The comment author's profile picture.
+ * - status: Comment status. Possible values are:
+ *   unpublished, published, or preview.
+ * - title: Comment title, linked to the comment.
+ * - attributes: HTML attributes for the containing element.
+ *   The attributes.class may contain one or more of the following classes:
+ *   - comment: The current template type; e.g., 'theming hook'.
+ *   - by-anonymous: Comment by an unregistered user.
+ *   - by-{entity-type}-author: Comment by the author of the parent entity,
+ *     eg. by-node-author.
+ *   - preview: When previewing a new or edited comment.
+ *   The following applies only to viewers who are registered users:
+ *   - unpublished: An unpublished comment visible only to administrators.
+ * - title_prefix: Additional output populated by modules, intended to be
+ *   displayed in front of the main title tag that appears in the template.
+ * - title_suffix: Additional output populated by modules, intended to be
+ *   displayed after the main title tag that appears in the template.
+ * - content_attributes: List of classes for the styling of the comment content.
+ * - title_attributes: Same as attributes, except applied to the main title
+ *   tag that appears in the template.
+ * - threaded: A flag indicating whether the comments are threaded or not.
+ *
+ * These variables are provided to give context about the parent comment (if
+ * any):
+ * - comment_parent: Full parent comment entity (if any).
+ * - parent_author: Equivalent to author for the parent comment.
+ * - parent_created: Equivalent to created for the parent comment.
+ * - parent_changed: Equivalent to changed for the parent comment.
+ * - parent_title: Equivalent to title for the parent comment.
+ * - parent_permalink: Equivalent to permalink for the parent comment.
+ * - parent: A text string of parent comment submission information created from
+ *   'parent_author' and 'parent_created' during template_preprocess_comment().
+ *   This information is presented to help screen readers follow lengthy
+ *   discussion threads. You can hide this from sighted users using the class
+ *   visually-hidden.
+ *
+ * These two variables are provided for context:
+ * - comment: Full comment object.
+ * - entity: Entity the comments are attached to.
+ *
+ * @see template_preprocess_comment()
+ *
+ * @ingroup themeable
+ */
+#}
+
+<article{{ attributes.addClass('js-comment') }}>
+  {#
+    Hide the "new" indicator by default, let a piece of JavaScript ask the
+    server which comments are new for the user. Rendering the final "new"
+    indicator here would break the render cache.
+  #}
+  <mark class="hidden" data-comment-timestamp="{{ new_indicator_timestamp }}"></mark>
+
+  <footer>
+    {{ user_picture }}
+    <p>{{ submitted }}</p>
+
+    {#
+      Indicate the semantic relationship between parent and child comments for
+      accessibility. The list is difficult to navigate in a screen reader
+      without this information.
+    #}
+    {% if parent %}
+      <p class="visually-hidden">{{ parent }}</p>
+    {% endif %}
+
+    {{ permalink }}
+  </footer>
+
+  <div{{ content_attributes }}>
+    {% if title %}
+      {{ title_prefix }}
+      <h3{{ title_attributes }}>{{ title }}</h3>
+      {{ title_suffix }}
+    {% endif %}
+    {{ content }}
+  </div>
+</article>
diff --git a/core/themes/stable/templates/content/mark.html.twig b/core/themes/stable/templates/content/mark.html.twig
new file mode 100644
index 0000000..6da921a
--- /dev/null
+++ b/core/themes/stable/templates/content/mark.html.twig
@@ -0,0 +1,22 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a marker for new or updated content.
+ *
+ * Available variables:
+ * - status: Number representing the marker status to display. Use the constants
+ *   below for comparison:
+ *   - MARK_NEW
+ *   - MARK_UPDATED
+ *   - MARK_READ
+ *
+ * @ingroup themeable
+ */
+#}
+{% if logged_in %}
+  {% if status is constant('MARK_NEW') %}
+    {{ 'new'|t }}
+  {% elseif status is constant('MARK_UPDATED') %}
+    {{ 'updated'|t }}
+  {% endif %}
+{% endif %}
diff --git a/core/themes/stable/templates/content/node.html.twig b/core/themes/stable/templates/content/node.html.twig
new file mode 100644
index 0000000..5ed0775
--- /dev/null
+++ b/core/themes/stable/templates/content/node.html.twig
@@ -0,0 +1,94 @@
+{#
+/**
+ * @file
+ * Default theme implementation to display a node.
+ *
+ * Available variables:
+ * - node: Full node entity.
+ *   - id: The node ID.
+ *   - bundle: The type of the node, for example, "page" or "article".
+ *   - authorid: The user ID of the node author.
+ *   - createdtime: Time the node was published formatted in Unix timestamp.
+ *   - changedtime: Time the node was changed formatted in Unix timestamp.
+ * - label: The title of the node.
+ * - content: All node items. Use {{ content }} to print them all,
+ *   or print a subset such as {{ content.field_example }}. Use
+ *   {{ content|without('field_example') }} to temporarily suppress the printing
+ *   of a given child element.
+ * - author_picture: The node author user entity, rendered using the "compact"
+ *   view mode.
+ * - metadata: Metadata for this node.
+ * - date: Themed creation date field.
+ * - author_name: Themed author name field.
+ * - url: Direct URL of the current node.
+ * - display_submitted: Whether submission information should be displayed.
+ * - attributes: HTML attributes for the containing element.
+ *   The attributes.class element may contain one or more of the following
+ *   classes:
+ *   - node: The current template type (also known as a "theming hook").
+ *   - node--type-[type]: The current node type. For example, if the node is an
+ *     "Article" it would result in "node--type-article". Note that the machine
+ *     name will often be in a short form of the human readable label.
+ *   - node--view-mode-[view_mode]: The View Mode of the node; for example, a
+ *     teaser would result in: "node--view-mode-teaser", and
+ *     full: "node--view-mode-full".
+ *   The following are controlled through the node publishing options.
+ *   - node--promoted: Appears on nodes promoted to the front page.
+ *   - node--sticky: Appears on nodes ordered above other non-sticky nodes in
+ *     teaser listings.
+ *   - node--unpublished: Appears on unpublished nodes visible only to site
+ *     admins.
+ * - title_attributes: Same as attributes, except applied to the main title
+ *   tag that appears in the template.
+ * - content_attributes: Same as attributes, except applied to the main
+ *   content tag that appears in the template.
+ * - author_attributes: Same as attributes, except applied to the author of
+ *   the node tag that appears in the template.
+ * - title_prefix: Additional output populated by modules, intended to be
+ *   displayed in front of the main title tag that appears in the template.
+ * - title_suffix: Additional output populated by modules, intended to be
+ *   displayed after the main title tag that appears in the template.
+ * - view_mode: View mode; for example, "teaser" or "full".
+ * - teaser: Flag for the teaser state. Will be true if view_mode is 'teaser'.
+ * - page: Flag for the full page state. Will be true if view_mode is 'full'.
+ * - readmore: Flag for more state. Will be true if the teaser content of the
+ *   node cannot hold the main body content.
+ * - logged_in: Flag for authenticated user status. Will be true when the
+ *   current user is a logged-in member.
+ * - is_admin: Flag for admin user status. Will be true when the current user
+ *   is an administrator.
+ *
+ * @see template_preprocess_node()
+ *
+ * @todo Remove the id attribute (or make it a class), because if that gets
+ *   rendered twice on a page this is invalid CSS for example: two lists
+ *   in different view modes.
+ *
+ * @ingroup themeable
+ */
+#}
+<article{{ attributes }}>
+
+  {{ title_prefix }}
+  {% if not page %}
+    <h2{{ title_attributes }}>
+      <a href="{{ url }}" rel="bookmark">{{ label }}</a>
+    </h2>
+  {% endif %}
+  {{ title_suffix }}
+
+  {% if display_submitted %}
+    <footer>
+      {{ author_picture }}
+      <div{{ author_attributes }}>
+        {% trans %}Submitted by {{ author_name }} on {{ date }}{% endtrans %}
+        {{ metadata }}
+      </div>
+    </footer>
+  {% endif %}
+
+  <div{{ content_attributes }}>
+    {{ content }}
+  </div>
+
+</article>
diff --git a/core/themes/stable/templates/content/page-title.html.twig b/core/themes/stable/templates/content/page-title.html.twig
new file mode 100644
index 0000000..2b994bc
--- /dev/null
+++ b/core/themes/stable/templates/content/page-title.html.twig
@@ -0,0 +1,23 @@
+{#
+/**
+ * @file
+ * Default theme implementation for page titles.
+ *
+ * Available variables:
+ * - title_attributes: HTML attributes for the page title element.
+ * - title_prefix: Additional output populated by modules, intended to be
+ *   displayed in front of the main title tag that appears in the template.
+ * - title: The page title, for use in the actual content.
+ * - title_suffix: Additional output populated by modules, intended to be
+ *   displayed after the main title tag that appears in the template.
+ *
+ * @see template_preprocess_page_title()
+ *
+ * @ingroup themeable
+ */
+#}
+{{ title_prefix }}
+{% if title %}
+  <h1{{ title_attributes }}>{{ title }}</h1>
+{% endif %}
+{{ title_suffix }}
diff --git a/core/themes/stable/templates/content/search-result.html.twig b/core/themes/stable/templates/content/search-result.html.twig
new file mode 100644
index 0000000..8982e90
--- /dev/null
+++ b/core/themes/stable/templates/content/search-result.html.twig
@@ -0,0 +1,71 @@
+{#
+/**
+ * @file
+ * Default theme implementation for displaying a single search result.
+ *
+ * This template renders a single search result. The list of results is
+ * rendered using '#theme' => 'item_list', with suggestions of:
+ * - item_list__search_results__(plugin_id)
+ * - item_list__search_results
+ *
+ * Available variables:
+ * - url: URL of the result.
+ * - title: Title of the result.
+ * - snippet: A small preview of the result. Does not apply to user searches.
+ * - info: String of all the meta information ready for print. Does not apply
+ *   to user searches.
+ * - plugin_id: The machine-readable name of the plugin being executed,such
+ *   as "node_search" or "user_search".
+ * - title_prefix: Additional output populated by modules, intended to be
+ *   displayed in front of the main title tag that appears in the template.
+ * - title_suffix: Additional output populated by modules, intended to be
+ *   displayed after the main title tag that appears in the template.
+ * - info_split: Contains same data as info, but split into separate parts.
+ *   - info_split.type: Node type (or item type string supplied by module).
+ *   - info_split.user: Author of the node linked to users profile. Depends
+ *     on permission.
+ *   - info_split.date: Last update of the node. Short formatted.
+ *   - info_split.comment: Number of comments output as "% comments", %
+ *     being the count. (Depends on comment.module).
+ * @todo The info variable needs to be made drillable and each of these sub
+ *   items should instead be within info and renamed info.foo, info.bar, etc.
+ *
+ * Other variables:
+ * - title_attributes: HTML attributes for the title.
+ * - content_attributes: HTML attributes for the content.
+ *
+ * Since info_split is keyed, a direct print of the item is possible.
+ * This array does not apply to user searches so it is recommended to check
+ * for its existence before printing. The default keys of 'type', 'user' and
+ * 'date' always exist for node searches. Modules may provide other data.
+ * @code
+ *   {% if (info_split.comment) %}
+ *     <span class="info-comment">
+ *       {{ info_split.comment }}
+ *     </span>
+ *   {% endif %}
+ * @endcode
+ *
+ * To check for all available data within info_split, use the code below.
+ * @code
+ *   <pre>
+ *     {{ dump(info_split) }}
+ *   </pre>
+ * @endcode
+ *
+ * @see template_preprocess_search_result()
+ *
+ * @ingroup themeable
+ */
+#}
+{{ title_prefix }}
+<h3{{ title_attributes }}>
+  <a href="{{ url }}">{{ title }}</a>
+</h3>
+{{ title_suffix }}
+{% if snippet %}
+  <p{{ content_attributes }}>{{ snippet }}</p>
+{% endif %}
+{% if info %}
+  <p>{{ info }}</p>
+{% endif %}
diff --git a/core/themes/stable/templates/content/taxonomy-term.html.twig b/core/themes/stable/templates/content/taxonomy-term.html.twig
new file mode 100644
index 0000000..d6fb9bd
--- /dev/null
+++ b/core/themes/stable/templates/content/taxonomy-term.html.twig
@@ -0,0 +1,35 @@
+{#
+/**
+ * @file
+ * Default theme implementation to display a taxonomy term.
+ *
+ * Available variables:
+ * - url: URL of the current term.
+ * - name: Name of the current term.
+ * - content: Items for the content of the term (fields and description).
+ *   Use 'content' to print them all, or print a subset such as
+ *   'content.description'. Use the following code to exclude the
+ *   printing of a given child element:
+ *   @code
+ *   {{ content|without('description') }}
+ *   @endcode
+ * - attributes: HTML attributes for the wrapper.
+ * - page: Flag for the full page state.
+ * - term: The taxonomy term entity, including:
+ *   - id: The ID of the taxonomy term.
+ *   - bundle: Machine name of the current vocabulary.
+ * - view_mode: View mode, e.g. 'full', 'teaser', etc.
+ *
+ * @see template_preprocess_taxonomy_term()
+ *
+ * @ingroup themeable
+ */
+#}
+<div{{ attributes}}>
+  {{ title_prefix }}
+  {% if not page %}
+    <h2><a href="{{ url }}">{{ name }}</a></h2>
+  {% endif %}
+  {{ title_suffix }}
+  {{ content }}
+</div>
diff --git a/core/themes/stable/templates/dataset/aggregator-feed.html.twig b/core/themes/stable/templates/dataset/aggregator-feed.html.twig
new file mode 100644
index 0000000..ee44c35
--- /dev/null
+++ b/core/themes/stable/templates/dataset/aggregator-feed.html.twig
@@ -0,0 +1,27 @@
+{#
+/**
+ * @file
+ * Default theme implementation to present an aggregator feed.
+ *
+ * The contents are rendered above feed listings when browsing source feeds.
+ * For example, "example.com/aggregator/sources/1".
+ *
+ * Available variables:
+ * - title: Title of the feed item.
+ * - content: All field items. Use {{ content }} to print them all,
+ *   or print a subset such as {{ content.field_example }}. Use
+ *   {{ content|without('field_example') }} to temporarily suppress the printing
+ *   of a given element.
+ *
+ * @see template_preprocess_aggregator_feed()
+ *
+ * @ingroup themeable
+ */
+#}
+{{ title_prefix }}
+{% if not full %}
+  <h2{{ title_attributes }}>{{ title }}</h2>
+{% endif %}
+{{ title_suffix }}
+
+{{ content }}
diff --git a/core/themes/stable/templates/dataset/forum-icon.html.twig b/core/themes/stable/templates/dataset/forum-icon.html.twig
new file mode 100644
index 0000000..8a5e325
--- /dev/null
+++ b/core/themes/stable/templates/dataset/forum-icon.html.twig
@@ -0,0 +1,26 @@
+{#
+/**
+ * @file
+ * Default theme implementation to display a status icon for a forum post.
+ *
+ * Available variables:
+ * - attributes: HTML attributes to be applied to the wrapper element.
+ *   - class: HTML classes that determine which icon to display. May be one of
+ *     'hot', 'hot-new', 'new', 'default', 'closed', or 'sticky'.
+ *   - title: Text alternative for the forum icon.
+ * - icon_title: Text alternative for the forum icon, same as above.
+ * - new_posts: '1' when this topic contains new posts, otherwise '0'.
+ * - first_new: '1' when this is the first topic with new posts, otherwise '0'.
+ * - icon_status: Indicates which status icon should be used.
+ *
+ * @see template_preprocess_forum_icon()
+ *
+ * @ingroup themeable
+ */
+#}
+<div{{ attributes }}>
+  {% if first_new -%}
+    <a id="new"></a>
+  {%- endif %}
+  <span class="visually-hidden">{{ icon_title }}</span>
+</div>
diff --git a/core/themes/stable/templates/dataset/forum-list.html.twig b/core/themes/stable/templates/dataset/forum-list.html.twig
new file mode 100644
index 0000000..c2e5567
--- /dev/null
+++ b/core/themes/stable/templates/dataset/forum-list.html.twig
@@ -0,0 +1,77 @@
+{#
+/**
+ * @file
+ * Default theme implementation to display a list of forums and containers.
+ *
+ * Available variables:
+ * - forums: A collection of forums and containers to display. It is keyed to
+ *   the numeric IDs of all child forums and containers. Each forum in forums
+ *   contains:
+ *   - is_container: A flag indicating if the forum can contain other
+ *     forums. Otherwise, the forum can only contain topics.
+ *   - depth: How deep the forum is in the current hierarchy.
+ *   - zebra: 'even' or 'odd', used for row class.
+ *   - icon_class: 'default' or 'new', used for forum icon class.
+ *   - icon_title: Text alternative for the forum icon.
+ *   - name: The name of the forum.
+ *   - link: The URL to link to this forum.
+ *   - description: The description field for the forum, containing:
+ *     - value: The descriptive text for the forum.
+ *   - new_topics: A flag indicating if the forum contains unread posts.
+ *   - new_url: A URL to the forum's unread posts.
+ *   - new_text: Text for the above URL, which tells how many new posts.
+ *   - old_topics: A count of posts that have already been read.
+ *   - num_posts: The total number of posts in the forum.
+ *   - last_reply: Text representing the last time a forum was posted or
+ *     commented in.
+ * - forum_id: Forum ID for the current forum. Parent to all items within the
+ *   forums array.
+ *
+ * @see template_preprocess_forum_list()
+ *
+ * @ingroup themeable
+ */
+#}
+<table>
+  <thead>
+    <tr>
+      <th>{{ 'Forum'|t }}</th>
+      <th>{{ 'Topics'|t }}</th>
+      <th>{{ 'Posts'|t }}</th>
+      <th>{{ 'Last post'|t }}</th>
+    </tr>
+  </thead>
+  <tbody>
+  {% for child_id, forum in forums %}
+    <tr>
+      <td{% if forum.is_container == true %} colspan="4"{% endif %}>
+        {#
+          Enclose the contents of this cell with X divs, where X is the
+          depth this forum resides at. This will allow us to use CSS
+          left-margin for indenting.
+        #}
+        {% for i in 1..forum.depth if forum.depth > 0 %}<div class="indent">{% endfor %}
+          <div title="{{ forum.icon_title }}">
+            <span class="visually-hidden">{{ forum.icon_title }}</span>
+          </div>
+          <div><a href="{{ forum.link }}">{{ forum.label }}</a></div>
+          {% if forum.description.value %}
+            <div>{{ forum.description.value }}</div>
+          {% endif %}
+        {% for i in 1..forum.depth if forum.depth > 0 %}</div>{% endfor %}
+      </td>
+      {% if forum.is_container == false %}
+        <td>
+          {{ forum.num_topics }}
+          {% if forum.new_topics == true %}
+            <br />
+            <a href="{{ forum.new_url }}">{{ forum.new_text }}</a>
+          {% endif %}
+        </td>
+        <td>{{ forum.num_posts }}</td>
+        <td>{{ forum.last_reply }}</td>
+      {% endif %}
+    </tr>
+  {% endfor %}
+  </tbody>
+</table>
diff --git a/core/themes/stable/templates/dataset/forums.html.twig b/core/themes/stable/templates/dataset/forums.html.twig
new file mode 100644
index 0000000..5116c76
--- /dev/null
+++ b/core/themes/stable/templates/dataset/forums.html.twig
@@ -0,0 +1,23 @@
+{#
+/**
+ * @file
+ * Default theme implementation to display a forum.
+ *
+ * May contain forum containers as well as forum topics.
+ *
+ * Available variables:
+ * - forums: The forums to display (as processed by forum-list.html.twig).
+ * - topics: The topics to display.
+ * - topics_pager: The topics pager.
+ * - forums_defined: A flag to indicate that the forums are configured.
+ *
+ * @see template_preprocess_forums()
+ *
+ * @ingroup themeable
+ */
+#}
+{% if forums_defined %}
+  {{ forums }}
+  {{ topics }}
+  {{ topics_pager }}
+{% endif %}
diff --git a/core/themes/stable/templates/dataset/item-list.html.twig b/core/themes/stable/templates/dataset/item-list.html.twig
new file mode 100644
index 0000000..1462cf4
--- /dev/null
+++ b/core/themes/stable/templates/dataset/item-list.html.twig
@@ -0,0 +1,41 @@
+{#
+/**
+ * @file
+ * Default theme implementation for an item list.
+ *
+ * Available variables:
+ * - items: A list of items. Each item contains:
+ *   - attributes: HTML attributes to be applied to each list item.
+ *   - value: The content of the list element.
+ * - title: The title of the list.
+ * - list_type: The tag for list element ("ul" or "ol").
+ * - wrapper_attributes: HTML attributes to be applied to the list wrapper.
+ * - attributes: HTML attributes to be applied to the list.
+ * - empty: A message to display when there are no items. Allowed value is a
+ *   string or render array.
+ * - context: A list of contextual data associated with the list. May contain:
+ *   - list_style: The custom list style.
+ *
+ * @see template_preprocess_item_list()
+ *
+ * @ingroup themeable
+ */
+#}
+{% if context.list_style %}
+  {%- set attributes = attributes.addClass('item-list__' ~ context.list_style) %}
+{% endif %}
+{% if items or empty %}
+  {%- if title is not empty -%}
+    <h3>{{ title }}</h3>
+  {%- endif -%}
+
+  {%- if items -%}
+    <{{ list_type }}{{ attributes }}>
+      {%- for item in items -%}
+        <li{{ item.attributes }}>{{ item.value }}</li>
+      {%- endfor -%}
+    </{{ list_type }}>
+  {%- else -%}
+    {{- empty -}}
+  {%- endif -%}
+{%- endif %}
diff --git a/core/themes/stable/templates/dataset/table.html.twig b/core/themes/stable/templates/dataset/table.html.twig
new file mode 100644
index 0000000..f6b32e4
--- /dev/null
+++ b/core/themes/stable/templates/dataset/table.html.twig
@@ -0,0 +1,105 @@
+{#
+/**
+ * @file
+ * Default theme implementation to display a table.
+ *
+ * Available variables:
+ * - attributes: HTML attributes to apply to the <table> tag.
+ * - caption: A localized string for the <caption> tag.
+ * - colgroups: Column groups. Each group contains the following properties:
+ *   - attributes: HTML attributes to apply to the <col> tag.
+ *     Note: Drupal currently supports only one table header row, see
+ *     https://www.drupal.org/node/893530 and
+ *     http://api.drupal.org/api/drupal/includes!theme.inc/function/theme_table/7#comment-5109.
+ * - header: Table header cells. Each cell contains the following properties:
+ *   - tag: The HTML tag name to use; either TH or TD.
+ *   - attributes: HTML attributes to apply to the tag.
+ *   - content: A localized string for the title of the column.
+ *   - field: Field name (required for column sorting).
+ *   - sort: Default sort order for this column ("asc" or "desc").
+ * - sticky: A flag indicating whether to use a "sticky" table header.
+ * - rows: Table rows. Each row contains the following properties:
+ *   - attributes: HTML attributes to apply to the <tr> tag.
+ *   - data: Table cells.
+ *   - no_striping: A flag indicating that the row should receive no
+ *     'even / odd' styling. Defaults to FALSE.
+ *   - cells: Table cells of the row. Each cell contains the following keys:
+ *     - tag: The HTML tag name to use; either TH or TD.
+ *     - attributes: Any HTML attributes, such as "colspan", to apply to the
+ *       table cell.
+ *     - content: The string to display in the table cell.
+ *     - active_table_sort: A boolean indicating whether the cell is the active
+         table sort.
+ * - footer: Table footer rows, in the same format as the rows variable.
+ * - empty: The message to display in an extra row if table does not have
+ *   any rows.
+ * - no_striping: A boolean indicating that the row should receive no striping.
+ * - header_columns: The number of columns in the header.
+ *
+ * @see template_preprocess_table()
+ *
+ * @ingroup themeable
+ */
+#}
+<table{{ attributes }}>
+  {% if caption %}
+    <caption>{{ caption }}</caption>
+  {% endif %}
+
+  {% for colgroup in colgroups %}
+    {% if colgroup.cols %}
+      <colgroup{{ colgroup.attributes }}>
+        {% for col in colgroup.cols %}
+          <col{{ col.attributes }} />
+        {% endfor %}
+      </colgroup>
+    {% else %}
+      <colgroup{{ colgroup.attributes }} />
+    {% endif %}
+  {% endfor %}
+
+  {% if header %}
+    <thead>
+      <tr>
+        {% for cell in header %}
+          <{{ cell.tag }}{{ cell.attributes }}>
+            {{- cell.content -}}
+          </{{ cell.tag }}>
+        {% endfor %}
+      </tr>
+    </thead>
+  {% endif %}
+
+  {% if rows %}
+    <tbody>
+      {% for row in rows %}
+        <tr{{ row.attributes }}>
+          {% for cell in row.cells %}
+            <{{ cell.tag }}{{ cell.attributes }}>
+              {{- cell.content -}}
+            </{{ cell.tag }}>
+          {% endfor %}
+        </tr>
+      {% endfor %}
+    </tbody>
+  {% elseif empty %}
+    <tbody>
+      <tr>
+        <td colspan="{{ header_columns }}">{{ empty }}</td>
+      </tr>
+    </tbody>
+  {% endif %}
+  {% if footer %}
+    <tfoot>
+      {% for row in footer %}
+        <tr{{ row.attributes }}>
+          {% for cell in row.cells %}
+            <{{ cell.tag }}{{ cell.attributes }}>
+              {{- cell.content -}}
+            </{{ cell.tag }}>
+          {% endfor %}
+        </tr>
+      {% endfor %}
+    </tfoot>
+  {% endif %}
+</table>
diff --git a/core/themes/stable/templates/field/field--comment.html.twig b/core/themes/stable/templates/field/field--comment.html.twig
new file mode 100644
index 0000000..879f4d5
--- /dev/null
+++ b/core/themes/stable/templates/field/field--comment.html.twig
@@ -0,0 +1,43 @@
+{#
+/**
+ * @file
+ * Default theme override for comment fields.
+ *
+ * Available variables:
+ * - attributes: HTML attributes for the containing element.
+ * - label_hidden: Whether to show the field label or not.
+ * - title_attributes: HTML attributes for the title.
+ * - label: The label for the field.
+ * - title_prefix: Additional output populated by modules, intended to be
+ *   displayed in front of the main title tag that appears in the template.
+ * - title_suffix: Additional title output populated by modules, intended to
+ *   be displayed after the main title tag that appears in the template.
+ * - comments: List of comments rendered through comment.html.twig.
+ * - content_attributes: HTML attributes for the form title.
+ * - comment_form: The 'Add new comment' form.
+ * - comment_display_mode: Is the comments are threaded.
+ * - comment_type: The comment type bundle ID for the comment field.
+ * - entity_type: The entity type to which the field belongs.
+ * - field_name: The name of the field.
+ * - field_type: The type of the field.
+ * - label_display: The display settings for the label.
+ *
+ * @see template_preprocess_field()
+ * @see comment_preprocess_field()
+ */
+#}
+<section{{ attributes }}>
+  {% if comments and not label_hidden %}
+    {{ title_prefix }}
+    <h2{{ title_attributes }}>{{ label }}</h2>
+    {{ title_suffix }}
+  {% endif %}
+
+  {{ comments }}
+
+  {% if comment_form %}
+    <h2{{ content_attributes }}>{{ 'Add new comment'|t }}</h2>
+    {{ comment_form }}
+  {% endif %}
+
+</section>
diff --git a/core/themes/stable/templates/field/field--node--created.html.twig b/core/themes/stable/templates/field/field--node--created.html.twig
new file mode 100644
index 0000000..049144f
--- /dev/null
+++ b/core/themes/stable/templates/field/field--node--created.html.twig
@@ -0,0 +1,28 @@
+{#
+/**
+ * @file
+ * Default theme implementation for the node created field.
+ *
+ * This is an override of field.html.twig for the node created field. See that
+ * template for documentation about its details and overrides.
+ *
+ * Available variables:
+ * - attributes: HTML attributes for the containing span element.
+ * - items: List of all the field items. Each item contains:
+ *   - attributes: List of HTML attributes for each item.
+ *   - content: The field item content.
+ * - entity_type: The entity type to which the field belongs.
+ * - field_name: The name of the field.
+ * - field_type: The type of the field.
+ * - label_display: The display settings for the label.
+ *
+ * @see field.html.twig
+ *
+ * @ingroup themeable
+ */
+#}
+<span{{ attributes }}>
+  {%- for item in items -%}
+    {{ item.content }}
+  {%- endfor -%}
+</span>
diff --git a/core/themes/stable/templates/field/field--node--title.html.twig b/core/themes/stable/templates/field/field--node--title.html.twig
new file mode 100644
index 0000000..68142ca
--- /dev/null
+++ b/core/themes/stable/templates/field/field--node--title.html.twig
@@ -0,0 +1,28 @@
+{#
+/**
+ * @file
+ * Default theme implementation for the node title field.
+ *
+ * This is an override of field.html.twig for the node title field. See that
+ * template for documentation about its details and overrides.
+ *
+ * Available variables:
+ * - attributes: HTML attributes for the containing span element.
+ * - items: List of all the field items. Each item contains:
+ *   - attributes: List of HTML attributes for each item.
+ *   - content: The field item content.
+ * - entity_type: The entity type to which the field belongs.
+ * - field_name: The name of the field.
+ * - field_type: The type of the field.
+ * - label_display: The display settings for the label.
+ *
+ * @see field.html.twig
+ *
+ * @ingroup themeable
+ */
+#}
+<span{{ attributes }}>
+  {%- for item in items -%}
+    {{ item.content }}
+  {%- endfor -%}
+</span>
diff --git a/core/themes/stable/templates/field/field--node--uid.html.twig b/core/themes/stable/templates/field/field--node--uid.html.twig
new file mode 100644
index 0000000..91a97b3
--- /dev/null
+++ b/core/themes/stable/templates/field/field--node--uid.html.twig
@@ -0,0 +1,28 @@
+{#
+/**
+ * @file
+ * Default theme implementation for the node user field.
+ *
+ * This is an override of field.html.twig for the node user field. See that
+ * template for documentation about its details and overrides.
+ *
+ * Available variables:
+ * - attributes: HTML attributes for the containing span element.
+ * - items: List of all the field items. Each item contains:
+ *   - attributes: List of HTML attributes for each item.
+ *   - content: The field item content.
+ * - entity_type: The entity type to which the field belongs.
+ * - field_name: The name of the field.
+ * - field_type: The type of the field.
+ * - label_display: The display settings for the label.
+ *
+ * @see field.html.twig
+ *
+ * @ingroup themeable
+ */
+#}
+<span{{ attributes }}>
+  {%- for item in items -%}
+    {{ item.content }}
+  {%- endfor -%}
+</span>
diff --git a/core/themes/stable/templates/field/field.html.twig b/core/themes/stable/templates/field/field.html.twig
new file mode 100644
index 0000000..babc512
--- /dev/null
+++ b/core/themes/stable/templates/field/field.html.twig
@@ -0,0 +1,67 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a field.
+ *
+ * To override output, copy the "field.html.twig" from the templates directory
+ * to your theme's directory and customize it, just like customizing other
+ * Drupal templates such as page.html.twig or node.html.twig.
+ *
+ * Instead of overriding the theming for all fields, you can also just override
+ * theming for a subset of fields using
+ * @link themeable Theme hook suggestions. @endlink For example,
+ * here are some theme hook suggestions that can be used for a field_foo field
+ * on an article node type:
+ * - field--node--field-foo--article.html.twig
+ * - field--node--field-foo.html.twig
+ * - field--node--article.html.twig
+ * - field--field-foo.html.twig
+ * - field--text-with-summary.html.twig
+ * - field.html.twig
+ *
+ * Available variables:
+ * - attributes: HTML attributes for the containing element.
+ * - label_hidden: Whether to show the field label or not.
+ * - title_attributes: HTML attributes for the title.
+ * - label: The label for the field.
+ * - multiple: TRUE if a field can contain multiple items.
+ * - items: List of all the field items. Each item contains:
+ *   - attributes: List of HTML attributes for each item.
+ *   - content: The field item's content.
+ * - entity_type: The entity type to which the field belongs.
+ * - field_name: The name of the field.
+ * - field_type: The type of the field.
+ * - label_display: The display settings for the label.
+ *
+ * @see template_preprocess_field()
+ *
+ * @ingroup themeable
+ */
+#}
+
+{% if label_hidden %}
+  {% if multiple %}
+    <div{{ attributes }}>
+      {% for item in items %}
+        <div{{ item.attributes }}>{{ item.content }}</div>
+      {% endfor %}
+    </div>
+  {% else %}
+    {% for item in items %}
+      <div{{ attributes }}>{{ item.content }}</div>
+    {% endfor %}
+  {% endif %}
+{% else %}
+  <div{{ attributes }}>
+    <div{{ title_attributes }}>{{ label }}</div>
+    {% if multiple %}
+      <div>
+    {% endif %}
+    {% for item in items %}
+      <div{{ item.attributes }}>{{ item.content }}</div>
+    {% endfor %}
+    {% if multiple %}
+      </div>
+    {% endif %}
+  </div>
+{% endif %}
diff --git a/core/themes/stable/templates/field/file-link.html.twig b/core/themes/stable/templates/field/file-link.html.twig
new file mode 100644
index 0000000..44cbd7f
--- /dev/null
+++ b/core/themes/stable/templates/field/file-link.html.twig
@@ -0,0 +1,15 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a link to a file.
+ *
+ * Available variables:
+ * - attributes: The HTML attributes for the containing element.
+ * - link: A link to the file.
+ *
+ * @see template_preprocess_file_link()
+ *
+ * @ingroup themeable
+ */
+#}
+<span{{ attributes }}>{{ link }}</span>
diff --git a/core/themes/stable/templates/field/image-formatter.html.twig b/core/themes/stable/templates/field/image-formatter.html.twig
new file mode 100644
index 0000000..63ca3c6
--- /dev/null
+++ b/core/themes/stable/templates/field/image-formatter.html.twig
@@ -0,0 +1,20 @@
+{#
+/**
+ * @file
+ * Default theme implementation to display a formatted image field.
+ *
+ * Available variables:
+ * - image: A collection of image data.
+ * - image_style: An optional image style.
+ * - url: An optional URL the image can be linked to.
+ *
+ * @see template_preprocess_image_formatter()
+ *
+ * @ingroup themeable
+ */
+#}
+{% if url %}
+  <a href="{{ url }}">{{ image }}</a>
+{% else %}
+  {{ image }}
+{% endif %}
diff --git a/core/themes/stable/templates/field/image-style.html.twig b/core/themes/stable/templates/field/image-style.html.twig
new file mode 100644
index 0000000..6d70ade
--- /dev/null
+++ b/core/themes/stable/templates/field/image-style.html.twig
@@ -0,0 +1,20 @@
+{#
+/**
+ * @file
+ * Default theme implementation for an image using a specific image style.
+ *
+ * Available variables:
+ * - attributes: HTML attributes for the image, including the following:
+ *   - src: Full URL or relative path to the image file.
+ *   - class: One or more classes to be applied to the image.
+ *   - width: The width of the image (if known).
+ *   - height: The height of the image (if known).
+ *   - title: The title of the image.
+ *   - alt: The alternative text for the image.
+ *
+ * @see template_preprocess_image_style()
+ *
+ * @ingroup themeable
+ */
+#}
+{{ image }}
diff --git a/core/themes/stable/templates/field/image.html.twig b/core/themes/stable/templates/field/image.html.twig
new file mode 100644
index 0000000..6411eaa
--- /dev/null
+++ b/core/themes/stable/templates/field/image.html.twig
@@ -0,0 +1,15 @@
+{#
+/**
+ * @file
+ * Default theme implementation of an image.
+ *
+ * Available variables:
+ * - attributes: HTML attributes for the img tag.
+ * - style_name: (optional) The name of the image style applied.
+ *
+ * @see template_preprocess_image()
+ *
+ * @ingroup themeable
+ */
+#}
+<img{{ attributes }} />
diff --git a/core/themes/stable/templates/field/link-formatter-link-separate.html.twig b/core/themes/stable/templates/field/link-formatter-link-separate.html.twig
new file mode 100644
index 0000000..469cd9a
--- /dev/null
+++ b/core/themes/stable/templates/field/link-formatter-link-separate.html.twig
@@ -0,0 +1,20 @@
+{#
+/**
+ * @file
+ * Default theme implementation of a link with separate title and URL elements.
+ *
+ * Available variables:
+ * - link: The link that has already been formatted by l().
+ * - title: (optional) A descriptive or alternate title for the link, which may
+ *   be different than the actual link text.
+ *
+ * @see template_preprocess()
+ * @see template_preprocess_link_formatter_link_separate()
+ *
+ * @ingroup themeable
+ */
+#}
+{% spaceless %}
+  {{ title }}
+  {{ link }}
+{% endspaceless %}
diff --git a/core/themes/stable/templates/field/responsive-image-formatter.html.twig b/core/themes/stable/templates/field/responsive-image-formatter.html.twig
new file mode 100644
index 0000000..93caba8
--- /dev/null
+++ b/core/themes/stable/templates/field/responsive-image-formatter.html.twig
@@ -0,0 +1,19 @@
+{#
+/**
+ * @file
+ * Default theme implementation to display a formatted responsive image field.
+ *
+ * Available variables:
+ * - responsive_image: A collection of responsive image data.
+ * - url: An optional URL the image can be linked to.
+ *
+ * @see template_preprocess_responsive_image_formatter()
+ *
+ * @ingroup themeable
+ */
+#}
+{% if url %}
+  <a href="{{ url }}">{{ responsive_image }}</a>
+{% else %}
+  {{ responsive_image }}
+{% endif %}
diff --git a/core/themes/stable/templates/field/responsive-image.html.twig b/core/themes/stable/templates/field/responsive-image.html.twig
new file mode 100644
index 0000000..99fca6b
--- /dev/null
+++ b/core/themes/stable/templates/field/responsive-image.html.twig
@@ -0,0 +1,36 @@
+{#
+/**
+ * @file
+ * Default theme implementation of a responsive image.
+ *
+ * Available variables:
+ * - sources: The attributes of the <source> tags for this <picture> tag.
+ * - img_element: The controlling image, with the fallback image in srcset.
+ * - output_image_tag: Whether or not to output an <img> tag instead of a
+ *   <picture> tag.
+ *
+ * @see template_preprocess()
+ * @see template_preprocess_responsive_image()
+ *
+ * @ingroup themeable
+ */
+#}
+{% if output_image_tag %}
+  {{ img_element }}
+{% else %}
+  <picture>
+    {% if sources %}
+      {#
+      Internet Explorer 9 doesn't recognise source elements that are wrapped in
+      picture tags. See http://scottjehl.github.io/picturefill/#ie9
+      #}
+      <!--[if IE 9]><video style="display: none;"><![endif]-->
+      {% for source_attributes in sources %}
+        <source{{ source_attributes }}/>
+      {% endfor %}
+      <!--[if IE 9]></video><![endif]-->
+    {% endif %}
+    {# The controlling image, with the fallback image in srcset. #}
+    {{ img_element }}
+  </picture>
+{% endif %}
diff --git a/core/themes/stable/templates/field/time.html.twig b/core/themes/stable/templates/field/time.html.twig
new file mode 100644
index 0000000..9fc2dfa
--- /dev/null
+++ b/core/themes/stable/templates/field/time.html.twig
@@ -0,0 +1,22 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a date / time element.
+ *
+ * Available variables
+ * - timestamp: (optional) A UNIX timestamp for the datetime attribute. If the
+ *   datetime cannot be represented as a UNIX timestamp, use a valid datetime
+ *   attribute value in attributes.datetime.
+ * - text: (optional) The content to display within the <time> element.
+ *   Defaults to a human-readable representation of the timestamp value or the
+ *   datetime attribute value using format_date().
+ * - attributes: (optional) HTML attributes to apply to the <time> element.
+ *   A datetime attribute in 'attributes' overrides the 'timestamp'. To
+ *   create a valid datetime attribute value from a UNIX timestamp, use
+ *   format_date() with one of the predefined 'html_*' formats.
+ *
+ * @see template_preprocess_time()
+ * @see http://www.w3.org/TR/html5-author/the-time-element.html#attr-time-datetime
+ */
+#}
+<time{{ attributes }}>{{ text }}</time>
diff --git a/core/themes/stable/templates/form/checkboxes.html.twig b/core/themes/stable/templates/form/checkboxes.html.twig
new file mode 100644
index 0000000..19209d9
--- /dev/null
+++ b/core/themes/stable/templates/form/checkboxes.html.twig
@@ -0,0 +1,17 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a 'checkboxes' #type form element.
+ *
+ * Available variables
+ * - attributes: A list of HTML attributes for the wrapper element.
+ * - children: The rendered checkboxes.
+ *
+ * @see template_preprocess_checkboxes()
+ *
+ * @ingroup themeable
+ */
+ @todo: remove this file once https://www.drupal.org/node/1819284 is resolved.
+ This is identical to core/modules/system/templates/container.html.twig
+#}
+<div{{ attributes.addClass('form-checkboxes') }}>{{ children }}</div>
diff --git a/core/themes/stable/templates/form/confirm-form.html.twig b/core/themes/stable/templates/form/confirm-form.html.twig
new file mode 100644
index 0000000..d714917
--- /dev/null
+++ b/core/themes/stable/templates/form/confirm-form.html.twig
@@ -0,0 +1,15 @@
+{#
+/**
+ * @file
+ * Default theme implementation for confirm form.
+ *
+ * By default this does not alter the appearance of a form at all,
+ * but is provided as a convenience for themers.
+ *
+ * Available variables:
+ * - form: The confirm form.
+ *
+ * @ingroup themeable
+ */
+#}
+{{ form }}
diff --git a/core/themes/stable/templates/form/container.html.twig b/core/themes/stable/templates/form/container.html.twig
new file mode 100644
index 0000000..167965c
--- /dev/null
+++ b/core/themes/stable/templates/form/container.html.twig
@@ -0,0 +1,26 @@
+{#
+/**
+ * @file
+ * Default theme implementation of a container used to wrap child elements.
+ *
+ * Used for grouped form items. Can also be used as a #theme_wrapper for any
+ * renderable element, to surround it with a <div> and HTML attributes.
+ *
+ * Available variables:
+ * - attributes: HTML attributes for the containing element.
+ * - children: The rendered child elements of the container.
+ * - has_parent: A flag to indicate that the container has one or more parent
+     containers.
+ *
+ * @see template_preprocess_container()
+ *
+ * @ingroup themeable
+ */
+#}
+{%
+  set classes = [
+    has_parent ? 'js-form-wrapper',
+    has_parent ? 'form-wrapper',
+  ]
+%}
+<div{{ attributes.addClass(classes) }}>{{ children }}</div>
diff --git a/core/themes/stable/templates/form/datetime-form.html.twig b/core/themes/stable/templates/form/datetime-form.html.twig
new file mode 100644
index 0000000..fef4b5e
--- /dev/null
+++ b/core/themes/stable/templates/form/datetime-form.html.twig
@@ -0,0 +1,17 @@
+{#
+/**
+ * @file
+ * Default theme implementation of a datetime form element.
+ *
+ * Available variables:
+ * - attributes: HTML attributes for the datetime form element.
+ * - content: The datelist form element to be output.
+ *
+ * @see template_preprocess_datetime_form()
+ *
+ * @ingroup themeable
+ */
+#}
+<div{{ attributes }}>
+  {{ content }}
+</div>
diff --git a/core/themes/stable/templates/form/datetime-wrapper.html.twig b/core/themes/stable/templates/form/datetime-wrapper.html.twig
new file mode 100644
index 0000000..8430baa
--- /dev/null
+++ b/core/themes/stable/templates/form/datetime-wrapper.html.twig
@@ -0,0 +1,33 @@
+{#
+/**
+ * @file
+ * Default theme implementation of a datetime form wrapper.
+ *
+ * Available variables:
+ * - content: The form element to be output, usually a datelist, or datetime.
+ * - title: The title of the form element.
+ * - title_attributes: HTML attributes for the title wrapper.
+ * - description: Description text for the form element.
+ * - required: An indicator for whether the associated form element is required.
+ *
+ * @see template_preprocess_datetime_wrapper()
+ *
+ * @ingroup themeable
+ */
+#}
+{%
+  set title_classes = [
+    required ? 'js-form-required',
+    required ? 'form-required',
+  ]
+%}
+{% if title %}
+  <h4{{ title_attributes.addClass(title_classes) }}>{{ title }}</h4>
+{% endif %}
+{{ content }}
+{% if errors %}
+  <div>
+    {{ errors }}
+  </div>
+{% endif %}
+{{ description }}
diff --git a/core/themes/stable/templates/form/details.html.twig b/core/themes/stable/templates/form/details.html.twig
new file mode 100644
index 0000000..cf50eb0
--- /dev/null
+++ b/core/themes/stable/templates/form/details.html.twig
@@ -0,0 +1,33 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a details element.
+ *
+ * Available variables
+ * - attributes: A list of HTML attributes for the details element.
+ * - errors: (optional) Any errors for this details element, may not be set.
+ * - title: (optional) The title of the element, may not be set.
+ * - description: (optional) The description of the element, may not be set.
+ * - children: (optional) The children of the element, may not be set.
+ * - value: (optional) The value of the element, may not be set.
+ *
+ * @see template_preprocess_details()
+ *
+ * @ingroup themeable
+ */
+#}
+<details{{ attributes }}>
+  {%- if title -%}
+    <summary{{ summary_attributes }}>{{ title }}</summary>
+  {%- endif -%}
+
+  {% if errors %}
+    <div>
+      {{ errors }}
+    </div>
+  {% endif %}
+
+  {{ description }}
+  {{ children }}
+  {{ value }}
+</details>
diff --git a/core/themes/stable/templates/form/dropbutton-wrapper.html.twig b/core/themes/stable/templates/form/dropbutton-wrapper.html.twig
new file mode 100644
index 0000000..ca0ff7e
--- /dev/null
+++ b/core/themes/stable/templates/form/dropbutton-wrapper.html.twig
@@ -0,0 +1,23 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a dropbutton wrapper.
+ *
+ * Available variables:
+ * - children: Contains the child elements of the dropbutton menu.
+ *
+ * @see template_preprocess()
+ * @see template_preprocess_dropbutton_wrapper()
+ *
+ * @ingroup themeable
+ */
+#}
+{% if children %}
+  {% spaceless %}
+    <div class="dropbutton-wrapper">
+      <div class="dropbutton-widget">
+        {{ children }}
+      </div>
+    </div>
+  {% endspaceless %}
+{% endif %}
diff --git a/core/themes/stable/templates/form/field-multiple-value-form.html.twig b/core/themes/stable/templates/form/field-multiple-value-form.html.twig
new file mode 100644
index 0000000..1108d6e
--- /dev/null
+++ b/core/themes/stable/templates/form/field-multiple-value-form.html.twig
@@ -0,0 +1,38 @@
+{#
+/**
+ * @file
+ * Default theme implementation for an individual form element.
+ *
+ * Available variables for all fields:
+ * - multiple: Whether there are multiple instances of the field.
+ *
+ * Available variables for single cardinality fields:
+ * - elements: Form elements to be rendered.
+ *
+ * Available variables when there are multiple fields.
+ * - table: Table of field items.
+ * - description: The description element containing the following properties:
+ *   - content: The description content of the form element.
+ *   - attributes: HTML attributes to apply to the description container.
+ * - button: "Add another item" button.
+ *
+ * @see template_preprocess_field_multiple_value_form()
+ *
+ * @ingroup themeable
+ */
+#}
+{% if multiple %}
+  <div class="js-form-item form-item">
+    {{ table }}
+    {% if description.content %}
+      <div{{ description.attributes.addClass('description') }} >{{ description.content }}</div>
+    {% endif %}
+    {% if button %}
+      <div class="clearfix">{{ button }}</div>
+    {% endif %}
+  </div>
+{% else %}
+  {% for element in elements %}
+    {{ element }}
+  {% endfor %}
+{% endif %}
diff --git a/core/themes/stable/templates/form/fieldset.html.twig b/core/themes/stable/templates/form/fieldset.html.twig
new file mode 100644
index 0000000..b67ec85
--- /dev/null
+++ b/core/themes/stable/templates/form/fieldset.html.twig
@@ -0,0 +1,62 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a fieldset element and its children.
+ *
+ * Available variables:
+ * - attributes: HTML attributes for the fieldset element.
+ * - errors: (optional) Any errors for this fieldset element, may not be set.
+ * - required: Boolean indicating whether the fieldeset element is required.
+ * - legend: The legend element containing the following properties:
+ *   - title: Title of the fieldset, intended for use as the text of the legend.
+ *   - attributes: HTML attributes to apply to the legend.
+ * - description: The description element containing the following properties:
+ *   - content: The description content of the fieldset.
+ *   - attributes: HTML attributes to apply to the description container.
+ * - children: The rendered child elements of the fieldset.
+ * - prefix: The content to add before the fieldset children.
+ * - suffix: The content to add after the fieldset children.
+ *
+ * @see template_preprocess_fieldset()
+ *
+ * @ingroup themeable
+ */
+#}
+{%
+  set classes = [
+    'js-form-item',
+    'form-item',
+    'js-form-wrapper',
+    'form-wrapper',
+  ]
+%}
+<fieldset{{ attributes.addClass(classes) }}>
+  {%
+    set legend_span_classes = [
+      'fieldset-legend',
+      required ? 'js-form-required',
+      required ? 'form-required',
+    ]
+  %}
+  {#  Always wrap fieldset legends in a SPAN for CSS positioning. #}
+  <legend{{ legend.attributes }}>
+    <span{{ legend_span.attributes.addClass(legend_span_classes) }}>{{ legend.title }}</span>
+  </legend>
+  <div class="fieldset-wrapper">
+    {% if errors %}
+      <div>
+        {{ errors }}
+      </div>
+    {% endif %}
+    {% if prefix %}
+      <span class="field-prefix">{{ prefix }}</span>
+    {% endif %}
+    {{ children }}
+    {% if suffix %}
+      <span class="field-suffix">{{ suffix }}</span>
+    {% endif %}
+    {% if description.content %}
+      <div{{ description.attributes.addClass('description') }}>{{ description.content }}</div>
+    {% endif %}
+  </div>
+</fieldset>
diff --git a/core/themes/stable/templates/form/form-element-label.html.twig b/core/themes/stable/templates/form/form-element-label.html.twig
new file mode 100644
index 0000000..7696609
--- /dev/null
+++ b/core/themes/stable/templates/form/form-element-label.html.twig
@@ -0,0 +1,27 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a form element label.
+ *
+ * Available variables:
+ * - title: The label's text.
+ * - title_display: Elements title_display setting.
+ * - required: An indicator for whether the associated form element is required.
+ * - attributes: A list of HTML attributes for the label.
+ *
+ * @see template_preprocess_form_element_label()
+ *
+ * @ingroup themeable
+ */
+#}
+{%
+  set classes = [
+    title_display == 'after' ? 'option',
+    title_display == 'invisible' ? 'visually-hidden',
+    required ? 'js-form-required',
+    required ? 'form-required',
+  ]
+%}
+{% if title is not empty or required -%}
+  <label{{ attributes.addClass(classes) }}>{{ title }}</label>
+{%- endif %}
diff --git a/core/themes/stable/templates/form/form-element.html.twig b/core/themes/stable/templates/form/form-element.html.twig
new file mode 100644
index 0000000..3311ebc
--- /dev/null
+++ b/core/themes/stable/templates/form/form-element.html.twig
@@ -0,0 +1,96 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a form element.
+ *
+ * Available variables:
+ * - attributes: HTML attributes for the containing element.
+ * - errors: (optional) Any errors for this form element, may not be set.
+ * - prefix: (optional) The form element prefix, may not be set.
+ * - suffix: (optional) The form element suffix, may not be set.
+ * - required: The required marker, or empty if the associated form element is
+ *   not required.
+ * - type: The type of the element.
+ * - name: The name of the element.
+ * - label: A rendered label element.
+ * - label_display: Label display setting. It can have these values:
+ *   - before: The label is output before the element. This is the default.
+ *     The label includes the #title and the required marker, if #required.
+ *   - after: The label is output after the element. For example, this is used
+ *     for radio and checkbox #type elements. If the #title is empty but the
+ *     field is #required, the label will contain only the required marker.
+ *   - invisible: Labels are critical for screen readers to enable them to
+ *     properly navigate through forms but can be visually distracting. This
+ *     property hides the label for everyone except screen readers.
+ *   - attribute: Set the title attribute on the element to create a tooltip but
+ *     output no label element. This is supported only for checkboxes and radios
+ *     in \Drupal\Core\Render\Element\CompositeFormElementTrait::preRenderCompositeFormElement().
+ *     It is used where a visual label is not needed, such as a table of
+ *     checkboxes where the row and column provide the context. The tooltip will
+ *     include the title and required marker.
+ * - description: (optional) A list of description properties containing:
+ *    - content: A description of the form element, may not be set.
+ *    - attributes: (optional) A list of HTML attributes to apply to the
+ *      description content wrapper. Will only be set when description is set.
+ * - description_display: Description display setting. It can have these values:
+ *   - before: The description is output before the element.
+ *   - after: The description is output after the element. This is the default
+ *     value.
+ *   - invisible: The description is output after the element, hidden visually
+ *     but available to screen readers.
+ * - disabled: True if the element is disabled.
+ * - title_display: Title display setting.
+ *
+ * @see template_preprocess_form_element()
+ *
+ * @ingroup themeable
+ */
+#}
+{%
+  set classes = [
+    'js-form-item',
+    'form-item',
+    'js-form-type-' ~ type|clean_class,
+    'form-item-' ~ name|clean_class,
+    'js-form-item-' ~ name|clean_class,
+    title_display not in ['after', 'before'] ? 'form-no-label',
+    disabled == 'disabled' ? 'form-disabled',
+    errors ? 'form-item--error',
+  ]
+%}
+{%
+  set description_classes = [
+    'description',
+    description_display == 'invisible' ? 'visually-hidden',
+  ]
+%}
+<div{{ attributes.addClass(classes) }}>
+  {% if label_display in ['before', 'invisible'] %}
+    {{ label }}
+  {% endif %}
+  {% if prefix is not empty %}
+    <span class="field-prefix">{{ prefix }}</span>
+  {% endif %}
+  {% if description_display == 'before' and description.content %}
+    <div{{ description.attributes }}>
+      {{ description.content }}
+    </div>
+  {% endif %}
+  {{ children }}
+  {% if suffix is not empty %}
+    <span class="field-suffix">{{ suffix }}</span>
+  {% endif %}
+  {% if label_display == 'after' %}
+    {{ label }}
+  {% endif %}
+  {% if errors %}
+    <div class="form-item--error-message">
+      {{ errors }}
+    </div>
+  {% endif %}
+  {% if description_display in ['after', 'invisible'] and description.content %}
+    <div{{ description.attributes.addClass(description_classes) }}>
+      {{ description.content }}
+    </div>
+  {% endif %}
+</div>
diff --git a/core/themes/stable/templates/form/form.html.twig b/core/themes/stable/templates/form/form.html.twig
new file mode 100644
index 0000000..2cd1e95
--- /dev/null
+++ b/core/themes/stable/templates/form/form.html.twig
@@ -0,0 +1,17 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a 'form' element.
+ *
+ * Available variables
+ * - attributes: A list of HTML attributes for the wrapper element.
+ * - children: The child elements of the form.
+ *
+ * @see template_preprocess_form()
+ *
+ * @ingroup themeable
+ */
+#}
+<form{{ attributes }}>
+  {{ children }}
+</form>
diff --git a/core/themes/stable/templates/form/input.html.twig b/core/themes/stable/templates/form/input.html.twig
new file mode 100644
index 0000000..1409c25
--- /dev/null
+++ b/core/themes/stable/templates/form/input.html.twig
@@ -0,0 +1,15 @@
+{#
+/**
+ * @file
+ * Default theme implementation for an 'input' #type form element.
+ *
+ * Available variables:
+ * - attributes: A list of HTML attributes for the input element.
+ * - children: Optional additional rendered elements.
+ *
+ * @see template_preprocess_input()
+ *
+ * @ingroup themeable
+ */
+#}
+<input{{ attributes }} />{{ children }}
diff --git a/core/themes/stable/templates/form/radios.html.twig b/core/themes/stable/templates/form/radios.html.twig
new file mode 100644
index 0000000..e397644
--- /dev/null
+++ b/core/themes/stable/templates/form/radios.html.twig
@@ -0,0 +1,15 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a 'radios' #type form element.
+ *
+ * Available variables
+ * - attributes: A list of HTML attributes for the wrapper element.
+ * - children: The rendered radios.
+ *
+ * @see template_preprocess_radios()
+ *
+ * @ingroup themeable
+ */
+#}
+<div{{ attributes }}>{{ children }}</div>
diff --git a/core/themes/stable/templates/form/select.html.twig b/core/themes/stable/templates/form/select.html.twig
new file mode 100644
index 0000000..fb7226e
--- /dev/null
+++ b/core/themes/stable/templates/form/select.html.twig
@@ -0,0 +1,29 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a select element.
+ *
+ * Available variables:
+ * - attributes: HTML attributes for the select tag.
+ * - options: The option element children.
+ *
+ * @see template_preprocess_select()
+ *
+ * @ingroup themeable
+ */
+#}
+{% spaceless %}
+  <select{{ attributes }}>
+    {% for option in options %}
+      {% if option.type == 'optgroup' %}
+        <optgroup label="{{ option.label }}">
+          {% for sub_option in option.options %}
+            <option value="{{ sub_option.value }}"{{ sub_option.selected ? ' selected="selected"' }}>{{ sub_option.label }}</option>
+          {% endfor %}
+        </optgroup>
+      {% elseif option.type == 'option' %}
+        <option value="{{ option.value }}"{{ option.selected ? ' selected="selected"' }}>{{ option.label }}</option>
+      {% endif %}
+    {% endfor %}
+  </select>
+{% endspaceless %}
diff --git a/core/themes/stable/templates/form/textarea.html.twig b/core/themes/stable/templates/form/textarea.html.twig
new file mode 100644
index 0000000..6a1a46e
--- /dev/null
+++ b/core/themes/stable/templates/form/textarea.html.twig
@@ -0,0 +1,20 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a 'textarea' #type form element.
+ *
+ * Available variables
+ * - wrapper_attributes: A list of HTML attributes for the wrapper element.
+ * - attributes: A list of HTML attributes for the textarea element.
+ * - resizable: An indicator for whether the textarea is resizable.
+ * - required: An indicator for whether the textarea is required.
+ * - value: The textarea content.
+ *
+ * @see template_preprocess_textarea()
+ *
+ * @ingroup themeable
+ */
+#}
+<div{{ wrapper_attributes }}>
+  <textarea{{ attributes }}>{{ value }}</textarea>
+</div>
diff --git a/core/themes/stable/templates/layout/book-export-html.html.twig b/core/themes/stable/templates/layout/book-export-html.html.twig
new file mode 100644
index 0000000..5fb2e97
--- /dev/null
+++ b/core/themes/stable/templates/layout/book-export-html.html.twig
@@ -0,0 +1,47 @@
+{#
+/**
+ * @file
+ * Default theme implementation for printed version of book outline.
+ *
+ * Available variables:
+ * - title: Top level node title.
+ * - head: Header tags.
+ * - language: Language object.
+ * - language_rtl: A flag indicating whether the current display language is a
+ *   right to left language.
+ * - base_url: URL to the home page.
+ * - contents: Nodes within the current outline rendered through
+ *   book-node-export-html.html.twig.
+ *
+ * @see template_preprocess_book_export_html()
+ *
+ * @ingroup themeable
+ */
+#}
+<!DOCTYPE html>
+<html{{ html_attributes }}>
+  <head>
+    <title>{{ title }}</title>
+    {{ page.head }}
+    <base href="{{ base_url }}" />
+    <link type="text/css" rel="stylesheet" href="misc/print.css" />
+  </head>
+  <body>
+    {#
+      The given node is embedded to its absolute depth in a top level section.
+      For example, a child node with depth 2 in the hierarchy is contained in
+      (otherwise empty) div elements corresponding to depth 0 and depth 1. This
+      is intended to support WYSIWYG output - e.g., level 3 sections always look
+      like level 3 sections, no matter their depth relative to the node selected
+      to be exported as printer-friendly HTML.
+    #}
+
+  {% for i in 1..depth-1 if depth > 1 %}
+    <div>
+  {% endfor %}
+  {{ contents }}
+  {% for i in 1..depth-1 if depth > 1 %}
+    </div>
+  {% endfor %}
+  </body>
+</html>
diff --git a/core/themes/stable/templates/layout/html.html.twig b/core/themes/stable/templates/layout/html.html.twig
new file mode 100644
index 0000000..39702c2
--- /dev/null
+++ b/core/themes/stable/templates/layout/html.html.twig
@@ -0,0 +1,45 @@
+{#
+/**
+ * @file
+ * Default theme implementation for the basic structure of a single Drupal page.
+ *
+ * Variables:
+ * - logged_in: A flag indicating if user is logged in.
+ * - root_path: The root path of the current page (e.g., node, admin, user).
+ * - node_type: The content type for the current node, if the page is a node.
+ * - head_title: List of text elements that make up the head_title variable.
+ *   May contain or more of the following:
+ *   - title: The title of the page.
+ *   - name: The name of the site.
+ *   - slogan: The slogan of the site.
+ * - page_top: Initial rendered markup. This should be printed before 'page'.
+ * - page: The rendered page markup.
+ * - page_bottom: Closing rendered markup. This variable should be printed after
+ *   'page'.
+ * - db_offline: A flag indicating if the database is offline.
+ * - placeholder_token: The token for generating head, css, js and js-bottom
+ *   placeholders.
+ *
+ * @see template_preprocess_html()
+ *
+ * @ingroup themeable
+ */
+#}
+<!DOCTYPE html>
+<html{{ html_attributes }}>
+  <head>
+    <head-placeholder token="{{ placeholder_token|raw }}">
+    <title>{{ head_title|safe_join(' | ') }}</title>
+    <css-placeholder token="{{ placeholder_token|raw }}">
+    <js-placeholder token="{{ placeholder_token|raw }}">
+  </head>
+  <body{{ attributes }}>
+    <a href="#main-content" class="visually-hidden focusable">
+      {{ 'Skip to main content'|t }}
+    </a>
+    {{ page_top }}
+    {{ page }}
+    {{ page_bottom }}
+    <js-bottom-placeholder token="{{ placeholder_token|raw }}">
+  </body>
+</html>
diff --git a/core/themes/stable/templates/layout/install-page.html.twig b/core/themes/stable/templates/layout/install-page.html.twig
new file mode 100644
index 0000000..1d0e479
--- /dev/null
+++ b/core/themes/stable/templates/layout/install-page.html.twig
@@ -0,0 +1,55 @@
+{#
+/**
+ * @file
+ * Default theme implementation to display a Drupal installation page.
+ *
+ * All available variables are mirrored in page.html.twig.
+ * Some may be blank but they are provided for consistency.
+ *
+ * @see template_preprocess_install_page()
+ *
+ * @ingroup themeable
+ */
+#}
+  <div class="layout-container">
+
+    <header role="banner">
+      {% if site_name or site_slogan %}
+        <div class="name-and-slogan">
+          {% if site_name %}
+            <h1>{{ site_name }}</h1>
+          {% endif %}
+          {% if site_slogan %}
+            <div class="site-slogan">{{ site_slogan }}</div>
+          {% endif %}
+        </div>{# /.name-and-slogan #}
+      {% endif %}
+    </header>
+
+    <main role="main">
+      {% if title %}
+        <h1>{{ title }}</h1>
+      {% endif %}
+      {{ page.highlighted }}
+      {{ page.content }}
+    </main>
+
+    {% if page.sidebar_first %}
+      <aside class="layout-sidebar-first" role="complementary">
+        {{ page.sidebar_first }}
+      </aside>{# /.layout-sidebar-first #}
+    {% endif %}
+
+    {% if page.sidebar_second %}
+      <aside class="layout-sidebar-second" role="complementary">
+        {{ page.sidebar_second }}
+      </aside>{# /.layout-sidebar-second #}
+    {% endif %}
+
+    {% if page.footer %}
+      <footer role="contentinfo">
+        {{ page.footer }}
+      </footer>
+    {% endif %}
+
+  </div>{# /.layout-container #}
diff --git a/core/themes/stable/templates/layout/maintenance-page.html.twig b/core/themes/stable/templates/layout/maintenance-page.html.twig
new file mode 100644
index 0000000..748ed5a
--- /dev/null
+++ b/core/themes/stable/templates/layout/maintenance-page.html.twig
@@ -0,0 +1,60 @@
+{#
+/**
+ * @file
+ * Default theme implementation to display a single Drupal page while offline.
+ *
+ * All available variables are mirrored in page.html.twig.
+ * Some may be blank but they are provided for consistency.
+ *
+ * @see template_preprocess_maintenance_page()
+ *
+ * @ingroup themeable
+ */
+#}
+<header role="banner">
+  {% if logo %}
+    <a href="{{ front_page }}" title="{{ 'Home'|t }}" rel="home">
+      <img src="{{ logo }}" alt="{{ 'Home'|t }}"/>
+    </a>
+  {% endif %}
+
+  {% if site_name or site_slogan %}
+    {% if site_name %}
+      <h1>
+        <a href="{{ front_page }}" title="{{ 'Home'|t }}" rel="home">{{ site_name }}</a>
+      </h1>
+    {% endif %}
+
+    {% if site_slogan %}
+      <div>{{ site_slogan }}</div>
+    {% endif %}
+  {% endif %}
+</header>
+
+<main role="main">
+  {% if title %}
+    <h1>{{ title }}</h1>
+  {% endif %}
+
+  {{ page.highlighted }}
+
+  {{ page.content }}
+</main>
+
+{% if page.sidebar_first %}
+  <aside role="complementary">
+    {{ page.sidebar_first }}
+  </aside>
+{% endif %}
+
+{% if page.sidebar_second %}
+  <aside role="complementary">
+    {{ page.sidebar_second }}
+  </aside>
+{% endif %}
+
+{% if page.footer %}
+  <footer role="contentinfo">
+    {{ page.footer }}
+  </footer>
+{% endif %}
diff --git a/core/themes/stable/templates/layout/page.html.twig b/core/themes/stable/templates/layout/page.html.twig
new file mode 100644
index 0000000..2f58210
--- /dev/null
+++ b/core/themes/stable/templates/layout/page.html.twig
@@ -0,0 +1,95 @@
+{#
+/**
+ * @file
+ * Default theme implementation to display a single page.
+ *
+ * The doctype, html, head and body tags are not in this template. Instead they
+ * can be found in the html.html.twig template in this directory.
+ *
+ * Available variables:
+ *
+ * General utility variables:
+ * - base_path: The base URL path of the Drupal installation. Will usually be
+ *   "/" unless you have installed Drupal in a sub-directory.
+ * - is_front: A flag indicating if the current page is the front page.
+ * - logged_in: A flag indicating if the user is registered and signed in.
+ * - is_admin: A flag indicating if the user has permission to access
+ *   administration pages.
+ *
+ * Site identity:
+ * - front_page: The URL of the front page. Use this instead of base_path when
+ *   linking to the front page. This includes the language domain or prefix.
+ * - logo: The url of the logo image, as defined in theme settings.
+ * - site_name: The name of the site. This is empty when displaying the site
+ *   name has been disabled in the theme settings.
+ * - site_slogan: The slogan of the site. This is empty when displaying the site
+ *   slogan has been disabled in theme settings.
+ *
+ * Page content (in order of occurrence in the default page.html.twig):
+ * - messages: Status and error messages. Should be displayed prominently.
+ * - node: Fully loaded node, if there is an automatically-loaded node
+ *   associated with the page and the node ID is the second argument in the
+ *   page's path (e.g. node/12345 and node/12345/revisions, but not
+ *   comment/reply/12345).
+ *
+ * Regions:
+ * - page.header: Items for the header region.
+ * - page.primary_menu: Items for the primary menu region.
+ * - page.secondary_menu: Items for the secondary menu region.
+ * - page.highlighted: Items for the highlighted content region.
+ * - page.help: Dynamic help text, mostly for admin pages.
+ * - page.content: The main content of the current page.
+ * - page.sidebar_first: Items for the first sidebar.
+ * - page.sidebar_second: Items for the second sidebar.
+ * - page.footer: Items for the footer region.
+ * - page.breadcrumb: Items for the breadcrumb region.
+ *
+ * @see template_preprocess_page()
+ * @see html.html.twig
+ *
+ * @ingroup themeable
+ */
+#}
+<div class="layout-container">
+
+  <header role="banner">
+    {{ page.header }}
+  </header>
+
+  {{ page.primary_menu }}
+  {{ page.secondary_menu }}
+
+  {{ page.breadcrumb }}
+
+  {{ page.highlighted }}
+
+  {{ page.help }}
+
+  <main role="main">
+    <a id="main-content" tabindex="-1"></a>{# link is in html.html.twig #}
+
+    <div class="layout-content">
+      {{ page.content }}
+    </div>{# /.layout-content #}
+
+    {% if page.sidebar_first %}
+      <aside class="layout-sidebar-first" role="complementary">
+        {{ page.sidebar_first }}
+      </aside>
+    {% endif %}
+
+    {% if page.sidebar_second %}
+      <aside class="layout-sidebar-second" role="complementary">
+        {{ page.sidebar_second }}
+      </aside>
+    {% endif %}
+
+  </main>
+
+  {% if page.footer %}
+    <footer role="contentinfo">
+      {{ page.footer }}
+    </footer>
+  {% endif %}
+
+</div>{# /.layout-container #}
diff --git a/core/themes/stable/templates/layout/region.html.twig b/core/themes/stable/templates/layout/region.html.twig
new file mode 100644
index 0000000..e009455
--- /dev/null
+++ b/core/themes/stable/templates/layout/region.html.twig
@@ -0,0 +1,21 @@
+{#
+/**
+ * @file
+ * Default theme implementation to display a region.
+ *
+ * Available variables:
+ * - content: The content for this region, typically blocks.
+ * - attributes: HTML attributes for the region div.
+ * - region: The name of the region variable as defined in the theme's
+ *   .info.yml file.
+ *
+ * @see template_preprocess_region()
+ *
+ * @ingroup themeable
+ */
+#}
+{% if content %}
+  <div{{ attributes }}>
+    {{ content }}
+  </div>
+{% endif %}
diff --git a/core/themes/stable/templates/misc/feed-icon.html.twig b/core/themes/stable/templates/misc/feed-icon.html.twig
new file mode 100644
index 0000000..9b321e0
--- /dev/null
+++ b/core/themes/stable/templates/misc/feed-icon.html.twig
@@ -0,0 +1,17 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a feed icon.
+ *
+ * Available variables:
+ * - url: An internal system path or a fully qualified external URL of the feed.
+ * - attributes: Remaining HTML attributes for the feed link.
+ *   - title: A descriptive title of the feed link.
+ *   - class: HTML classes to be applied to the feed link.
+ *
+ * @ingroup themeable
+ */
+#}
+<a href="{{ url }}"{{ attributes.addClass('feed-icon') }}>
+  {{ 'Subscribe to @title'|t({'@title': title}) }}
+</a>
diff --git a/core/themes/stable/templates/misc/progress-bar.html.twig b/core/themes/stable/templates/misc/progress-bar.html.twig
new file mode 100644
index 0000000..885a80f
--- /dev/null
+++ b/core/themes/stable/templates/misc/progress-bar.html.twig
@@ -0,0 +1,23 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a progress bar.
+ *
+ * Note that the core Batch API uses this only for non-JavaScript batch jobs.
+ *
+ * Available variables:
+ * - label: The label of the working task.
+ * - percent: The percentage of the progress.
+ * - message: A string containing information to be displayed.
+ *
+ * @ingroup themeable
+ */
+#}
+<div class="progress" data-drupal-progress>
+  {% if label %}
+    <div class="progress__label">{{ label }}</div>
+  {% endif %}
+  <div class="progress__track"><div class="progress__bar" style="width: {{ percent }}%"></div></div>
+  <div class="progress__percentage">{{ percent }}%</div>
+  <div class="progress__description">{{ message }}</div>
+</div>
diff --git a/core/themes/stable/templates/misc/rdf-metadata.html.twig b/core/themes/stable/templates/misc/rdf-metadata.html.twig
new file mode 100644
index 0000000..4d96136
--- /dev/null
+++ b/core/themes/stable/templates/misc/rdf-metadata.html.twig
@@ -0,0 +1,22 @@
+{#
+/**
+ * @file
+ * Default theme implementation for empty spans with RDF attributes.
+ *
+ * The XHTML+RDFa doctype allows either <span></span> or <span /> syntax to
+ * be used, but for maximum browser compatibility, W3C recommends the
+ * former when serving pages using the text/html media type, see
+ * http://www.w3.org/TR/xhtml1/#C_3.
+ *
+ * Available variables:
+ * - metadata: Each item within corresponds to its own set of attributes,
+ *   and therefore, needs its own 'attributes' element.
+ *
+ * @see template_preprocess_rdf_metadata()
+ *
+ * @ingroup themeable
+ */
+#}
+{% for attributes in metadata %}
+  <span{{ attributes.addClass('hidden') }}></span>
+{% endfor %}
diff --git a/core/themes/stable/templates/misc/rdf-wrapper.html.twig b/core/themes/stable/templates/misc/rdf-wrapper.html.twig
new file mode 100644
index 0000000..cfdb31e
--- /dev/null
+++ b/core/themes/stable/templates/misc/rdf-wrapper.html.twig
@@ -0,0 +1,13 @@
+{#
+/**
+ * @file
+ * Default theme implementation for wrapping content with RDF attributes.
+ *
+ * Available variables:
+ * - content: The content being wrapped with RDF attributes.
+ * - attributes: HTML attributes, including RDF attributes for wrapper element.
+ *
+ * @ingroup themeable
+ */
+#}
+<span{{ attributes }}>{{ content }}</span>
diff --git a/core/themes/stable/templates/misc/status-messages.html.twig b/core/themes/stable/templates/misc/status-messages.html.twig
new file mode 100644
index 0000000..e479b87
--- /dev/null
+++ b/core/themes/stable/templates/misc/status-messages.html.twig
@@ -0,0 +1,49 @@
+{#
+/**
+ * @file
+ * Default theme implementation for status messages.
+ *
+ * Displays status, error, and warning messages, grouped by type.
+ *
+ * An invisible heading identifies the messages for assistive technology.
+ * Sighted users see a colored box. See http://www.w3.org/TR/WCAG-TECHS/H69.html
+ * for info.
+ *
+ * Add an ARIA label to the contentinfo area so that assistive technology
+ * user agents will better describe this landmark.
+ *
+ * Available variables:
+ * - message_list: List of messages to be displayed, grouped by type.
+ * - status_headings: List of all status types.
+ * - display: (optional) May have a value of 'status' or 'error' when only
+ *   displaying messages of that specific type.
+ * - attributes: HTML attributes for the element, including:
+ *   - class: HTML classes.
+ *
+ * @see template_preprocess_status_messages()
+ *
+ * @ingroup themeable
+ */
+#}
+{% for type, messages in message_list %}
+  <div role="contentinfo" aria-label="{{ status_headings[type] }}"{{ attributes|without('role', 'aria-label') }}>
+    {% if type == 'error' %}
+      <div role="alert">
+    {% endif %}
+      {% if status_headings[type] %}
+        <h2 class="visually-hidden">{{ status_headings[type] }}</h2>
+      {% endif %}
+      {% if messages|length > 1 %}
+        <ul>
+          {% for message in messages %}
+            <li>{{ message }}</li>
+          {% endfor %}
+        </ul>
+      {% else %}
+        {{ messages|first }}
+      {% endif %}
+    {% if type == 'error' %}
+      </div>
+    {% endif %}
+  </div>
+{% endfor %}
diff --git a/core/themes/stable/templates/navigation/book-all-books-block.html.twig b/core/themes/stable/templates/navigation/book-all-books-block.html.twig
new file mode 100644
index 0000000..a4d0c9a
--- /dev/null
+++ b/core/themes/stable/templates/navigation/book-all-books-block.html.twig
@@ -0,0 +1,24 @@
+{#
+/**
+ * @file
+ * Default theme implementation for rendering book outlines within a block.
+ *
+ * This template is used only when the block is configured to "show block on all
+ * pages", which presents multiple independent books on all pages.
+ *
+ * Available variables:
+ * - book_menus: Book outlines.
+ *   - id: The parent book ID.
+ *   - title: The parent book title.
+ *   - menu: The top-level book links.
+ *
+ * @see template_preprocess_book_all_books_block()
+ *
+ * @ingroup themeable
+ */
+#}
+{% for book in book_menus %}
+  <nav role="navigation" aria-label="{% trans %}Book outline for {{ book.title }}{% endtrans %}">
+    {{ book.menu }}
+  </nav>
+{% endfor %}
diff --git a/core/themes/stable/templates/navigation/book-navigation.html.twig b/core/themes/stable/templates/navigation/book-navigation.html.twig
new file mode 100644
index 0000000..0f12980
--- /dev/null
+++ b/core/themes/stable/templates/navigation/book-navigation.html.twig
@@ -0,0 +1,57 @@
+{#
+/**
+ * @file
+ * Default theme implementation to navigate books.
+ *
+ * Presented under nodes that are a part of book outlines.
+ *
+ * Available variables:
+ * - tree: The immediate children of the current node rendered as an unordered
+ *   list.
+ * - current_depth: Depth of the current node within the book outline. Provided
+ *   for context.
+ * - prev_url: URL to the previous node.
+ * - prev_title: Title of the previous node.
+ * - parent_url: URL to the parent node.
+ * - parent_title: Title of the parent node. Not printed by default. Provided
+ *   as an option.
+ * - next_url: URL to the next node.
+ * - next_title: Title of the next node.
+ * - has_links: Flags TRUE whenever the previous, parent or next data has a
+ *   value.
+ * - book_id: The book ID of the current outline being viewed. Same as the node
+ *   ID containing the entire outline. Provided for context.
+ * - book_url: The book/node URL of the current outline being viewed. Provided
+ *   as an option. Not used by default.
+ * - book_title: The book/node title of the current outline being viewed.
+ *
+ * @see template_preprocess_book_navigation()
+ *
+ * @ingroup themeable
+ */
+#}
+{% if tree or has_links %}
+  <nav role="navigation" aria-labelledby="book-label-{{ book_id }}">
+    {{ tree }}
+    {% if has_links %}
+      <h2>{{ 'Book traversal links for'|t }} {{ book_title }}</h2>
+      <ul>
+      {% if prev_url %}
+        <li>
+          <a href="{{ prev_url }}" rel="prev" title="{{ 'Go to previous page'|t }}"><b>{{ '‹'|t }}</b> {{ prev_title }}</a>
+        </li>
+      {% endif %}
+      {% if parent_url %}
+        <li>
+          <a href="{{ parent_url }}" title="{{ 'Go to parent page'|t }}">{{ 'Up'|t }}</a>
+        </li>
+      {% endif %}
+      {% if next_url %}
+        <li>
+          <a href="{{ next_url }}" rel="next" title="{{ 'Go to next page'|t }}">{{ next_title }} <b>{{ '›'|t }}</b></a>
+        </li>
+      {% endif %}
+    </ul>
+    {% endif %}
+  </nav>
+{% endif %}
diff --git a/core/themes/stable/templates/navigation/book-tree.html.twig b/core/themes/stable/templates/navigation/book-tree.html.twig
new file mode 100644
index 0000000..bf7424f
--- /dev/null
+++ b/core/themes/stable/templates/navigation/book-tree.html.twig
@@ -0,0 +1,49 @@
+{#
+/**
+ * @file
+ * Default theme implementation to display a book tree.
+ *
+ * Returns HTML for a wrapper for a book sub-tree.
+ *
+ * Available variables:
+ * - items: A nested list of book items. Each book item contains:
+ *   - attributes: HTML attributes for the book item.
+ *   - below: The book item child items.
+ *   - title: The book link title.
+ *   - url: The book link URL, instance of \Drupal\Core\Url.
+ *   - is_expanded: TRUE if the link has visible children within the current
+ *     book tree.
+ *   - is_collapsed: TRUE if the link has children within the current book tree
+ *     that are not currently visible.
+ *   - in_active_trail: TRUE if the link is in the active trail.
+ *
+ * @ingroup themeable
+ */
+#}
+{% import _self as book_tree %}
+
+{#
+  We call a macro which calls itself to render the full tree.
+  @see http://twig.sensiolabs.org/doc/tags/macro.html
+#}
+{{ book_tree.book_links(items, attributes, 0) }}
+
+{% macro book_links(items, attributes, menu_level) %}
+  {% import _self as book_tree %}
+  {% if items %}
+    {% if menu_level == 0 %}
+      <ul{{ attributes }}>
+    {% else %}
+      <ul>
+    {% endif %}
+    {% for item in items %}
+      <li{{ item.attributes }}>
+        {{ link(item.title, item.url) }}
+        {% if item.below %}
+          {{ book_tree.book_links(item.below, attributes, menu_level + 1) }}
+        {% endif %}
+      </li>
+    {% endfor %}
+    </ul>
+  {% endif %}
+{% endmacro %}
diff --git a/core/themes/stable/templates/navigation/breadcrumb.html.twig b/core/themes/stable/templates/navigation/breadcrumb.html.twig
new file mode 100644
index 0000000..91d8376
--- /dev/null
+++ b/core/themes/stable/templates/navigation/breadcrumb.html.twig
@@ -0,0 +1,27 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a breadcrumb trail.
+ *
+ * Available variables:
+ * - breadcrumb: Breadcrumb trail items.
+ *
+ * @ingroup themeable
+ */
+#}
+{% if breadcrumb %}
+  <nav role="navigation" aria-labelledby="system-breadcrumb">
+    <h2 class="visually-hidden">{{ 'Breadcrumb'|t }}</h2>
+    <ol>
+    {% for item in breadcrumb %}
+      <li>
+        {% if item.url %}
+          <a href="{{ item.url }}">{{ item.text }}</a>
+        {% else %}
+          {{ item.text }}
+        {% endif %}
+      </li>
+    {% endfor %}
+    </ol>
+  </nav>
+{% endif %}
diff --git a/core/themes/stable/templates/navigation/links.html.twig b/core/themes/stable/templates/navigation/links.html.twig
new file mode 100644
index 0000000..86713b5
--- /dev/null
+++ b/core/themes/stable/templates/navigation/links.html.twig
@@ -0,0 +1,58 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a set of links.
+ *
+ * Available variables:
+ * - attributes: Attributes for the UL containing the list of links.
+ * - links: Links to be output.
+ *   Each link will have the following elements:
+ *   - title: The link text.
+ *   - href: The link URL. If omitted, the 'title' is shown as a plain text
+ *     item in the links list. If 'href' is supplied, the entire link is passed
+ *     to l() as its $options parameter.
+ *   - attributes: (optional) HTML attributes for the anchor, or for the <span>
+ *     tag if no 'href' is supplied.
+ *   - link_key: The link CSS class.
+ * - heading: (optional) A heading to precede the links.
+ *   - text: The heading text.
+ *   - level: The heading level (e.g. 'h2', 'h3').
+ *   - attributes: (optional) A keyed list of attributes for the heading.
+ *   If the heading is a string, it will be used as the text of the heading and
+ *   the level will default to 'h2'.
+ *
+ *   Headings should be used on navigation menus and any list of links that
+ *   consistently appears on multiple pages. To make the heading invisible use
+ *   the 'visually-hidden' CSS class. Do not use 'display:none', which
+ *   removes it from screen readers and assistive technology. Headings allow
+ *   screen reader and keyboard only users to navigate to or skip the links.
+ *   See http://juicystudio.com/article/screen-readers-display-none.php and
+ *   http://www.w3.org/TR/WCAG-TECHS/H42.html for more information.
+ *
+ * @see template_preprocess_links()
+ *
+ * @ingroup themeable
+ */
+#}
+{% if links -%}
+  {%- if heading -%}
+    {%- if heading.level -%}
+      <{{ heading.level }}{{ heading.attributes }}>{{ heading.text }}</{{ heading.level }}>
+    {%- else -%}
+      <h2{{ heading.attributes }}>{{ heading.text }}</h2>
+    {%- endif -%}
+  {%- endif -%}
+  <ul{{ attributes }}>
+    {%- for key, item in links -%}
+      <li{{ item.attributes.addClass(key|clean_class) }}>
+        {%- if item.link -%}
+          {{ item.link }}
+        {%- elseif item.text_attributes -%}
+          <span{{ item.text_attributes }}>{{ item.text }}</span>
+        {%- else -%}
+          {{ item.text }}
+        {%- endif -%}
+      </li>
+    {%- endfor -%}
+  </ul>
+{%- endif %}
diff --git a/core/themes/stable/templates/navigation/menu--toolbar.html.twig b/core/themes/stable/templates/navigation/menu--toolbar.html.twig
new file mode 100644
index 0000000..659e8f5
--- /dev/null
+++ b/core/themes/stable/templates/navigation/menu--toolbar.html.twig
@@ -0,0 +1,57 @@
+{#
+/**
+ * @file
+ * Default theme implementation to display a toolbar menu.
+ *
+ * Available variables:
+ * - menu_name: The machine name of the menu.
+ * - items: A nested list of menu items. Each menu item contains:
+ *   - attributes: HTML attributes for the menu item.
+ *   - below: The menu item child items.
+ *   - title: The menu link title.
+ *   - url: The menu link url, instance of \Drupal\Core\Url
+ *   - localized_options: Menu link localized options.
+ *   - is_expanded: TRUE if the link has visible children within the current
+ *     menu tree.
+ *   - is_collapsed: TRUE if the link has children within the current menu tree
+ *     that are not currently visible.
+ *   - in_active_trail: TRUE if the link is in the active trail.
+ *
+ * @ingroup themeable
+ */
+#}
+{% import _self as menus %}
+
+{#
+  We call a macro which calls itself to render the full tree.
+  @see http://twig.sensiolabs.org/doc/tags/macro.html
+#}
+{{ menus.menu_links(items, attributes, 0) }}
+
+{% macro menu_links(items, attributes, menu_level) %}
+  {% import _self as menus %}
+  {% if items %}
+    {% if menu_level == 0 %}
+      <ul{{ attributes.addClass('toolbar-menu') }}>
+    {% else %}
+      <ul class="toolbar-menu">
+    {% endif %}
+    {% for item in items %}
+      {%
+        set classes = [
+          'menu-item',
+          item.is_expanded ? 'menu-item--expanded',
+          item.is_collapsed ? 'menu-item--collapsed',
+          item.in_active_trail ? 'menu-item--active-trail',
+        ]
+      %}
+      <li{{ item.attributes.addClass(classes) }}>
+        {{ link(item.title, item.url) }}
+        {% if item.below %}
+          {{ menus.menu_links(item.below, attributes, menu_level + 1) }}
+        {% endif %}
+      </li>
+    {% endfor %}
+    </ul>
+  {% endif %}
+{% endmacro %}
diff --git a/core/themes/stable/templates/navigation/menu-local-action.html.twig b/core/themes/stable/templates/navigation/menu-local-action.html.twig
new file mode 100644
index 0000000..0eb03a9
--- /dev/null
+++ b/core/themes/stable/templates/navigation/menu-local-action.html.twig
@@ -0,0 +1,15 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a single local action link.
+ *
+ * Available variables:
+ * - attributes: HTML attributes for the wrapper element.
+ * - link: A rendered link element.
+ *
+ * @see template_preprocess_menu_local_action()
+ *
+ * @ingroup themeable
+ */
+#}
+<li{{ attributes }}>{{ link }}</li>
diff --git a/core/themes/stable/templates/navigation/menu-local-task.html.twig b/core/themes/stable/templates/navigation/menu-local-task.html.twig
new file mode 100644
index 0000000..ec02a8d
--- /dev/null
+++ b/core/themes/stable/templates/navigation/menu-local-task.html.twig
@@ -0,0 +1,19 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a local task link.
+ *
+ * Available variables:
+ * - attributes: HTML attributes for the wrapper element.
+ * - is_active: Whether the task item is an active tab.
+ * - link: A rendered link element.
+ *
+ * Note: This template renders the content for each task item in
+ * menu-local-tasks.html.twig.
+ *
+ * @see template_preprocess_menu_local_task()
+ *
+ * @ingroup themeable
+ */
+#}
+<li{{ attributes }}>{{ link }}</li>
diff --git a/core/themes/stable/templates/navigation/menu-local-tasks.html.twig b/core/themes/stable/templates/navigation/menu-local-tasks.html.twig
new file mode 100644
index 0000000..3f3a24b
--- /dev/null
+++ b/core/themes/stable/templates/navigation/menu-local-tasks.html.twig
@@ -0,0 +1,25 @@
+{#
+/**
+ * @file
+ * Default theme implementation to display primary and secondary local tasks.
+ *
+ * Available variables:
+ * - primary: HTML list items representing primary tasks.
+ * - secondary: HTML list items representing primary tasks.
+ *
+ * Each item in these variables (primary and secondary) can be individually
+ * themed in menu-local-task.html.twig.
+ *
+ * @see template_preprocess_menu_local_tasks()
+ *
+ * @ingroup themeable
+ */
+#}
+{% if primary %}
+  <h2 class="visually-hidden">{{ 'Primary tabs'|t }}</h2>
+  <ul>{{ primary }}</ul>
+{% endif %}
+{% if secondary %}
+  <h2 class="visually-hidden">{{ 'Secondary tabs'|t }}</h2>
+  <ul>{{ secondary }}</ul>
+{% endif %}
diff --git a/core/themes/stable/templates/navigation/menu.html.twig b/core/themes/stable/templates/navigation/menu.html.twig
new file mode 100644
index 0000000..03704f2
--- /dev/null
+++ b/core/themes/stable/templates/navigation/menu.html.twig
@@ -0,0 +1,49 @@
+{#
+/**
+ * @file
+ * Default theme implementation to display a menu.
+ *
+ * Available variables:
+ * - menu_name: The machine name of the menu.
+ * - items: A nested list of menu items. Each menu item contains:
+ *   - attributes: HTML attributes for the menu item.
+ *   - below: The menu item child items.
+ *   - title: The menu link title.
+ *   - url: The menu link url, instance of \Drupal\Core\Url
+ *   - localized_options: Menu link localized options.
+ *   - is_expanded: TRUE if the link has visible children within the current
+ *     menu tree.
+ *   - is_collapsed: TRUE if the link has children within the current menu tree
+ *     that are not currently visible.
+ *   - in_active_trail: TRUE if the link is in the active trail.
+ *
+ * @ingroup themeable
+ */
+#}
+{% import _self as menus %}
+
+{#
+  We call a macro which calls itself to render the full tree.
+  @see http://twig.sensiolabs.org/doc/tags/macro.html
+#}
+{{ menus.menu_links(items, attributes, 0) }}
+
+{% macro menu_links(items, attributes, menu_level) %}
+  {% import _self as menus %}
+  {% if items %}
+    {% if menu_level == 0 %}
+      <ul{{ attributes }}>
+    {% else %}
+      <ul>
+    {% endif %}
+    {% for item in items %}
+      <li{{ item.attributes }}>
+        {{ link(item.title, item.url) }}
+        {% if item.below %}
+          {{ menus.menu_links(item.below, attributes, menu_level + 1) }}
+        {% endif %}
+      </li>
+    {% endfor %}
+    </ul>
+  {% endif %}
+{% endmacro %}
diff --git a/core/themes/stable/templates/navigation/pager.html.twig b/core/themes/stable/templates/navigation/pager.html.twig
new file mode 100644
index 0000000..741180b
--- /dev/null
+++ b/core/themes/stable/templates/navigation/pager.html.twig
@@ -0,0 +1,100 @@
+{#
+/**
+ * @file
+ * Default theme implementation to display a pager.
+ *
+ * Available variables:
+ * - items: List of pager items.
+ *   The list is keyed by the following elements:
+ *   - first: Item for the first page; not present on the first page of results.
+ *   - previous: Item for the previous page; not present on the first page
+ *     of results.
+ *   - next: Item for the next page; not present on the last page of results.
+ *   - last: Item for the last page; not present on the last page of results.
+ *   - pages: List of pages, keyed by page number.
+ *   Sub-sub elements:
+ *   items.first, items.previous, items.next, items.last, and each item inside
+ *   items.pages contain the following elements:
+ *   - href: URL with appropriate query parameters for the item.
+ *   - attributes: A keyed list of HTML attributes for the item.
+ *   - text: The visible text used for the item link, such as "‹ previous"
+ *     or "next ›".
+ * - current: The page number of the current page.
+ * - ellipses: If there are more pages than the quantity allows, then an
+ *   ellipsis before or after the listed pages may be present.
+ *   - previous: Present if the currently visible list of pages does not start
+ *     at the first page.
+ *   - next: Present if the visible list of pages ends before the last page.
+ *
+ * @see template_preprocess_pager()
+ *
+ * @ingroup themeable
+ */
+#}
+{% if items %}
+  <nav class="pager" role="navigation" aria-labelledby="pagination-heading">
+    <h4 id="pagination-heading" class="visually-hidden">{{ 'Pagination'|t }}</h4>
+    <ul class="pager__items js-pager__items">
+      {# Print first item if we are not on the first page. #}
+      {% if items.first %}
+        <li class="pager__item pager__item--first">
+          <a href="{{ items.first.href }}" title="{{ 'Go to first page'|t }}"{{ items.first.attributes|without('href', 'title') }}>
+            <span class="visually-hidden">{{ 'First page'|t }}</span>
+            <span aria-hidden="true">{{ items.first.text|default('« first'|t) }}</span>
+          </a>
+        </li>
+      {% endif %}
+      {# Print previous item if we are not on the first page. #}
+      {% if items.previous %}
+        <li class="pager__item pager__item--previous">
+          <a href="{{ items.previous.href }}" title="{{ 'Go to previous page'|t }}" rel="prev"{{ items.previous.attributes|without('href', 'title', 'rel') }}>
+            <span class="visually-hidden">{{ 'Previous page'|t }}</span>
+            <span aria-hidden="true">{{ items.previous.text|default('‹ previous'|t) }}</span>
+          </a>
+        </li>
+      {% endif %}
+      {# Add an ellipsis if there are further previous pages. #}
+      {% if ellipses.previous %}
+        <li class="pager__item pager__item--ellipsis" role="presentation">&hellip;</li>
+      {% endif %}
+      {# Now generate the actual pager piece. #}
+      {% for key, item in items.pages %}
+        <li class="pager__item{{ current == key ? ' is-active' : '' }}">
+          {% if current == key %}
+            {% set title = 'Current page'|t %}
+          {% else %}
+            {% set title = 'Go to page @key'|t({'@key': key}) %}
+          {% endif %}
+          <a href="{{ item.href }}" title="{{ title }}"{{ item.attributes|without('href', 'title') }}>
+            <span class="visually-hidden">
+              {{ current == key ? 'Current page'|t : 'Page'|t }}
+            </span>
+            {{- key -}}
+          </a>
+        </li>
+      {% endfor %}
+      {# Add an ellipsis if there are further next pages. #}
+      {% if ellipses.next %}
+        <li class="pager__item pager__item--ellipsis" role="presentation">&hellip;</li>
+      {% endif %}
+      {# Print next item if we are not on the last page. #}
+      {% if items.next %}
+        <li class="pager__item pager__item--next">
+          <a href="{{ items.next.href }}" title="{{ 'Go to next page'|t }}" rel="next"{{ items.next.attributes|without('href', 'title', 'rel') }}>
+            <span class="visually-hidden">{{ 'Next page'|t }}</span>
+            <span aria-hidden="true">{{ items.next.text|default('next ›'|t) }}</span>
+          </a>
+        </li>
+      {% endif %}
+      {# Print last item if we are not on the last page. #}
+      {% if items.last %}
+        <li class="pager__item pager__item--last">
+          <a href="{{ items.last.href }}" title="{{ 'Go to last page'|t }}"{{ items.last.attributes|without('href', 'title') }}>
+            <span class="visually-hidden">{{ 'Last page'|t }}</span>
+            <span aria-hidden="true">{{ items.last.text|default('last »'|t) }}</span>
+          </a>
+        </li>
+      {% endif %}
+    </ul>
+  </nav>
+{% endif %}
diff --git a/core/themes/stable/templates/navigation/toolbar.html.twig b/core/themes/stable/templates/navigation/toolbar.html.twig
new file mode 100644
index 0000000..cb82774
--- /dev/null
+++ b/core/themes/stable/templates/navigation/toolbar.html.twig
@@ -0,0 +1,48 @@
+{#
+/**
+ * @file
+ * Default theme implementation for the administrative toolbar.
+ *
+ * Available variables:
+ * - attributes: HTML attributes for the wrapper.
+ * - toolbar_attributes: HTML attributes to apply to the toolbar.
+ * - toolbar_heading: The heading or label for the toolbar.
+ * - tabs: List of tabs for the toolbar.
+ *   - attributes: HTML attributes for the tab container.
+ *   - link: Link or button for the menu tab.
+ * - trays: Toolbar tray list, each associated with a tab. Each tray in trays
+ *   contains:
+ *   - attributes: HTML attributes to apply to the tray.
+ *   - label: The tray's label.
+ *   - links: The tray menu links.
+ * - remainder: Any non-tray, non-tab elements left to be rendered.
+ *
+ * @see template_preprocess_toolbar()
+ *
+ * @ingroup themeable
+ */
+#}
+<div{{ attributes.addClass('toolbar') }}>
+  <nav{{ toolbar_attributes.addClass('toolbar-bar') }}>
+    <h2 class="visually-hidden">{{ toolbar_heading }}</h2>
+    {% for key, tab in tabs %}
+      {% set tray = trays[key] %}
+      <div{{ tab.attributes.addClass('toolbar-tab') }}>
+        {{ tab.link }}
+        {% spaceless %}
+          <div{{ tray.attributes }}>
+            {% if tray.label %}
+              <nav class="toolbar-lining clearfix" role="navigation" aria-label="{{ tray.label }}">
+                <h3 class="toolbar-tray-name visually-hidden">{{ tray.label }}</h3>
+            {% else %}
+              <nav class="toolbar-lining clearfix" role="navigation">
+            {% endif %}
+            {{ tray.links }}
+            </nav>
+          </div>
+        {% endspaceless %}
+      </div>
+    {% endfor %}
+  </nav>
+  {{ remainder }}
+</div>
diff --git a/core/themes/stable/templates/navigation/vertical-tabs.html.twig b/core/themes/stable/templates/navigation/vertical-tabs.html.twig
new file mode 100644
index 0000000..7a20efa
--- /dev/null
+++ b/core/themes/stable/templates/navigation/vertical-tabs.html.twig
@@ -0,0 +1,15 @@
+{#
+/**
+ * @file
+ * Default theme implementation for vertical tabs.
+ *
+ * Available variables
+ * - attributes: A list of HTML attributes for the wrapper element.
+ * - children: The rendered checkboxes.
+ *
+ * @see template_preprocess_vertical_tabs()
+ *
+ * @ingroup themeable
+ */
+#}
+<div{{ attributes.setAttribute('data-vertical-tabs-panes', TRUE) }}>{{ children }}</div>
diff --git a/core/themes/stable/templates/user/forum-submitted.html.twig b/core/themes/stable/templates/user/forum-submitted.html.twig
new file mode 100644
index 0000000..2cc992a
--- /dev/null
+++ b/core/themes/stable/templates/user/forum-submitted.html.twig
@@ -0,0 +1,23 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a forum post submission string.
+ *
+ * The submission string indicates when and by whom a topic was submitted.
+ *
+ * Available variables:
+ * - author: The author of the post.
+ * - time: How long ago the post was created.
+ * - topic: An object with the raw data of the post. Potentially unsafe. Be
+ *   sure to clean this data before printing.
+ *
+ * @see template_preprocess_forum_submitted()
+ *
+ * @ingroup themeable
+ */
+#}
+{% if time %}
+  <span>{% trans %}By {{ author }} {{ time }} ago{% endtrans %}</span>
+{% else %}
+  {{ 'n/a'|t }}
+{% endif %}
diff --git a/core/themes/stable/templates/user/user.html.twig b/core/themes/stable/templates/user/user.html.twig
new file mode 100644
index 0000000..9075ab1
--- /dev/null
+++ b/core/themes/stable/templates/user/user.html.twig
@@ -0,0 +1,25 @@
+{#
+/**
+ * @file
+ * Default theme implementation to present all user data.
+ *
+ * This template is used when viewing a registered user's page,
+ * e.g., example.com/user/123. 123 being the user's ID.
+ *
+ * Available variables:
+ * - content: A list of content items. Use 'content' to print all content, or
+ *   print a subset such as 'content.field_example'. Fields attached to a user
+ *   such as 'user_picture' are available as 'content.user_picture'.
+ * - attributes: HTML attributes for the container element.
+ * - user: A Drupal User entity.
+ *
+ * @see template_preprocess_user()
+ *
+ * @ingroup themeable
+ */
+#}
+<article{{ attributes }}>
+  {% if content %}
+    {{- content -}}
+  {% endif %}
+</article>
diff --git a/core/themes/stable/templates/user/username.html.twig b/core/themes/stable/templates/user/username.html.twig
new file mode 100644
index 0000000..5d4f2f7
--- /dev/null
+++ b/core/themes/stable/templates/user/username.html.twig
@@ -0,0 +1,25 @@
+{#
+/**
+ * @file
+ * Default theme implementation for displaying a username.
+ *
+ * Available variables:
+ * - account: The full account information for the user.
+ * - name: The user's name, sanitized.
+ * - extra: Additional text to append to the user's name, sanitized.
+ * - link_path: The path or URL of the user's profile page, home page,
+ *   or other desired page to link to for more information about the user.
+ * - link_options: Options to pass to the url() function's $options parameter if
+ *   linking the user's name to the user's page.
+ * - attributes: HTML attributes for the containing element.
+ *
+ * @see template_preprocess_username()
+ *
+ * @ingroup themeable
+ */
+#}
+{% if link_path -%}
+  <a{{ attributes }}>{{ name }}{{ extra }}</a>
+{%- else -%}
+  <span{{ attributes }}>{{ name }}{{ extra }}</span>
+{%- endif -%}
diff --git a/core/themes/stable/templates/views/views-exposed-form.html.twig b/core/themes/stable/templates/views/views-exposed-form.html.twig
new file mode 100644
index 0000000..f8a180e
--- /dev/null
+++ b/core/themes/stable/templates/views/views-exposed-form.html.twig
@@ -0,0 +1,21 @@
+{#
+/**
+ * @file
+ * Default theme implementation of a views exposed form.
+ *
+ * Available variables:
+ * - form: A render element representing the form.
+ *
+ * @see template_preprocess_views_exposed_form()
+ *
+ * @ingroup themeable
+ */
+#}
+{% if q is not empty %}
+  {#
+    This ensures that, if clean URLs are off, the 'q' is added first,
+    as a hidden form element, so that it shows up first in the POST URL.
+  #}
+{{ q }}
+{% endif %}
+{{ form }}
diff --git a/core/themes/stable/templates/views/views-mini-pager.html.twig b/core/themes/stable/templates/views/views-mini-pager.html.twig
new file mode 100644
index 0000000..2f36b16
--- /dev/null
+++ b/core/themes/stable/templates/views/views-mini-pager.html.twig
@@ -0,0 +1,43 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a views mini-pager.
+ *
+ * Available variables:
+ * - items: List of pager items.
+ *
+ * @see template_preprocess_views_mini_pager()
+ *
+ * @ingroup themeable
+ */
+#}
+{% if items.previous or items.next %}
+  <nav role="navigation" aria-labelledby="pagination-heading">
+    <h4 class="visually-hidden">{{ 'Pagination'|t }}</h4>
+    <ul class="js-pager__items">
+      {% if items.previous %}
+        <li>
+          <a href="{{ items.previous.href }}" title="{{ 'Go to previous page'|t }}" rel="prev"{{ items.previous.attributes|without('href', 'title', 'rel') }}>
+            <span class="visually-hidden">{{ 'Previous page'|t }}</span>
+            <span aria-hidden="true">{{ items.previous.text|default('‹‹'|t) }}</span>
+          </a>
+        </li>
+      {% endif %}
+      {% if items.current %}
+        <li>
+          {% trans %}
+            Page {{ items.current }}
+          {% endtrans %}
+        </li>
+      {% endif %}
+      {% if items.next %}
+        <li>
+          <a href="{{ items.next.href }}" title="{{ 'Go to next page'|t }}" rel="next"{{ items.next.attributes|without('href', 'title', 'rel') }}>
+            <span class="visually-hidden">{{ 'Next page'|t }}</span>
+            <span aria-hidden="true">{{ items.next.text|default('››'|t) }}</span>
+          </a>
+        </li>
+      {% endif %}
+    </ul>
+  </nav>
+{% endif %}
diff --git a/core/themes/stable/templates/views/views-view-field.html.twig b/core/themes/stable/templates/views/views-view-field.html.twig
new file mode 100644
index 0000000..f2027a9
--- /dev/null
+++ b/core/themes/stable/templates/views/views-view-field.html.twig
@@ -0,0 +1,27 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a single field in a view.
+ *
+ * It is not actually used in default views, as this is registered as a theme
+ * function which has better performance. For single overrides, the template is
+ * perfectly okay.
+ *
+ * Available variables:
+ * - view: The view that the field belongs to.
+ * - field: The field handler that can process the input.
+ * - row: The raw result of the database query that generated this field.
+ * - output: The processed output that will normally be used.
+ *
+ * When fetching output from the row this construct should be used:
+ * data = row[field.field_alias]
+ *
+ * The above will guarantee that you'll always get the correct data, regardless
+ * of any changes in the aliasing that might happen if the view is modified.
+ *
+ * @see template_preprocess_views_view_field()
+ *
+ * @ingroup themeable
+ */
+#}
+{{ output -}}
diff --git a/core/themes/stable/templates/views/views-view-fields.html.twig b/core/themes/stable/templates/views/views-view-fields.html.twig
new file mode 100644
index 0000000..64bd791
--- /dev/null
+++ b/core/themes/stable/templates/views/views-view-fields.html.twig
@@ -0,0 +1,54 @@
+{#
+/**
+ * @file
+ * Default view template to display all the fields in a row.
+ *
+ * Available variables:
+ * - view: The view in use.
+ * - fields: A list of fields, each one contains:
+ *   - content: The output of the field.
+ *   - raw: The raw data for the field, if it exists. This is NOT output safe.
+ *   - class: The safe class ID to use.
+ *   - handler: The Views field handler controlling this field.
+ *   - inline: Whether or not the field should be inline.
+ *   - wrapper_element: An HTML element for a wrapper.
+ *   - wrapper_attributes: List of attributes for wrapper element.
+ *   - separator: An optional separator that may appear before a field.
+ *   - label: The field's label text.
+ *   - label_element: An HTML element for a label wrapper.
+ *   - label_attributes: List of attributes for label wrapper.
+ *   - label_suffix: Colon after the label.
+ *   - element_type: An HTML element for the field content.
+ *   - element_attributes: List of attributes for HTML element for field content.
+ *   - has_label_colon: A boolean indicating whether to display a colon after
+ *     the label.
+ *   - element_type: An HTML element for the field content.
+ *   - element_attributes: List of attributes for HTML element for field content.
+ * - row: The raw result from the query, with all data it fetched.
+ *
+ * @see template_preprocess_views_view_fields()
+ *
+ * @ingroup themeable
+ */
+#}
+{% for field in fields -%}
+  {{ field.separator }}
+  {%- if field.wrapper_element -%}
+    <{{ field.wrapper_element }}{{ field.wrapper_attributes }}>
+  {%- endif %}
+  {%- if field.label -%}
+    {%- if field.label_element -%}
+      <{{ field.label_element }}{{ field.label_attributes }}>{{ field.label }}{{ field.label_suffix }}</{{ field.label_element }}>
+    {%- else -%}
+      {{ field.label }}{{ field.label_suffix }}
+    {%- endif %}
+  {%- endif %}
+  {%- if field.element_type -%}
+    <{{ field.element_type }}{{ field.element_attributes }}>{{ field.content }}</{{ field.element_type }}>
+  {%- else -%}
+    {{ field.content }}
+  {%- endif %}
+  {%- if field.wrapper_element -%}
+    </{{ field.wrapper_element }}>
+  {%- endif %}
+{%- endfor %}
diff --git a/core/themes/stable/templates/views/views-view-grid.html.twig b/core/themes/stable/templates/views/views-view-grid.html.twig
new file mode 100644
index 0000000..f2b1552
--- /dev/null
+++ b/core/themes/stable/templates/views/views-view-grid.html.twig
@@ -0,0 +1,78 @@
+{#
+/**
+ * @file
+ * Default theme implementation for views to display rows in a grid.
+ *
+ * Available variables:
+ * - attributes: HTML attributes for the wrapping element.
+ * - title: The title of this group of rows.
+ * - view: The view object.
+ * - rows: The rendered view results.
+ * - options: The view plugin style options.
+ *   - row_class_default: A flag indicating whether default classes should be
+ *     used on rows.
+ *   - col_class_default: A flag indicating whether default classes should be
+ *     used on columns.
+ * - items: A list of grid items. Each item contains a list of rows or columns.
+ *   The order in what comes first (row or column) depends on which alignment
+ *   type is chosen (horizontal or vertical).
+ *   - attributes: HTML attributes for each row or column.
+ *   - content: A list of columns or rows. Each row or column contains:
+ *     - attributes: HTML attributes for each row or column.
+ *     - content: The row or column contents.
+ *
+ * @see template_preprocess_views_view_grid()
+ *
+ * @ingroup themeable
+ */
+#}
+{%
+  set classes = [
+    'views-view-grid',
+    options.alignment,
+    'cols-' ~ options.columns,
+    'clearfix',
+  ]
+%}
+{% if options.row_class_default %}
+  {%
+    set row_classes = [
+      'views-row',
+      options.alignment == 'horizontal' ? 'clearfix',
+    ]
+  %}
+{% endif %}
+{% if options.col_class_default %}
+  {%
+    set col_classes = [
+      'views-col',
+      options.alignment == 'vertical' ? 'clearfix',
+    ]
+  %}
+{% endif %}
+{% if title %}
+  <h3>{{ title }}</h3>
+{% endif %}
+<div{{ attributes.addClass(classes) }}>
+  {% if options.alignment == 'horizontal' %}
+    {% for row in items %}
+      <div{{ row.attributes.addClass(row_classes, options.row_class_default ? 'row-' ~ loop.index) }}>
+        {% for column in row.content %}
+          <div{{ column.attributes.addClass(col_classes, options.col_class_default ? 'col-' ~ loop.index) }}>
+            {{ column.content }}
+          </div>
+        {% endfor %}
+      </div>
+    {% endfor %}
+  {% else %}
+    {% for column in items %}
+      <div{{ column.attributes.addClass(col_classes, options.col_class_default ? 'col-' ~ loop.index) }}>
+        {% for row in column.content %}
+          <div{{ row.attributes.addClass(row_classes, options.row_class_default ? 'row-' ~ loop.index) }}>
+            {{ row.content }}
+          </div>
+        {% endfor %}
+      </div>
+    {% endfor %}
+  {% endif %}
+</div>
diff --git a/core/themes/stable/templates/views/views-view-grouping.html.twig b/core/themes/stable/templates/views/views-view-grouping.html.twig
new file mode 100644
index 0000000..d786749
--- /dev/null
+++ b/core/themes/stable/templates/views/views-view-grouping.html.twig
@@ -0,0 +1,20 @@
+{#
+/**
+ * @file
+ * Default theme implementation to display a single views grouping.
+ *
+ * Available variables:
+ * - view: The view object.
+ * - grouping: The grouping instruction.
+ * - grouping_level: A number indicating the hierarchical level of the grouping.
+ * - title: The group heading.
+ * - content: The content to be grouped.
+ * - rows: The rows returned from the view.
+ *
+ * @see template_preprocess_views_view_grouping()
+ *
+ * @ingroup themeable
+ */
+#}
+{{ title }}
+{{ content }}
diff --git a/core/themes/stable/templates/views/views-view-list.html.twig b/core/themes/stable/templates/views/views-view-list.html.twig
new file mode 100644
index 0000000..8de787c
--- /dev/null
+++ b/core/themes/stable/templates/views/views-view-list.html.twig
@@ -0,0 +1,38 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a view template to display a list of rows.
+ *
+ * Available variables:
+ * - attributes: HTML attributes for the container.
+ * - rows: A list of rows for this list.
+ *   - attributes: The row's HTML attributes.
+ *   - content: The row's contents.
+ * - title: The title of this group of rows. May be empty.
+ * - list: @todo.
+ *   - type: Starting tag will be either a ul or ol.
+ *   - attributes: HTML attributes for the list element.
+ *
+ * @see template_preprocess_views_view_list()
+ *
+ * @ingroup themeable
+ */
+#}
+{% if attributes -%}
+  <div{{ attributes }}>
+{% endif %}
+  {% if title %}
+    <h3>{{ title }}</h3>
+  {% endif %}
+
+  <{{ list.type }}{{ list.attributes }}>
+
+    {% for row in rows %}
+      <li{{ row.attributes }}>{{ row.content }}</li>
+    {% endfor %}
+
+  </{{ list.type }}>
+
+{% if attributes -%}
+  </div>
+{% endif %}
diff --git a/core/themes/stable/templates/views/views-view-mapping-test.html.twig b/core/themes/stable/templates/views/views-view-mapping-test.html.twig
new file mode 100644
index 0000000..eacaa84
--- /dev/null
+++ b/core/themes/stable/templates/views/views-view-mapping-test.html.twig
@@ -0,0 +1,14 @@
+{#
+/**
+ * @file
+ * Default theme implementation for testing the mapping row style.
+ *
+ * Available variables:
+ * - element: The view content.
+ *
+ * @see template_preprocess_views_view_mapping_test()
+ *
+ * @ingroup themeable
+ */
+#}
+{{ element }}
diff --git a/core/themes/stable/templates/views/views-view-opml.html.twig b/core/themes/stable/templates/views/views-view-opml.html.twig
new file mode 100644
index 0000000..0564bf6
--- /dev/null
+++ b/core/themes/stable/templates/views/views-view-opml.html.twig
@@ -0,0 +1,25 @@
+{#
+/**
+ * @file
+ * Default template for feed displays that use the OPML style.
+ *
+ * Available variables:
+ * - title: The title of the feed (as set in the view).
+ * - updated: The modified date of the feed.
+ * - items: The feed items themselves.
+ *
+ * @see template_preprocess_views_view_opml()
+ *
+ * @ingroup themeable
+ */
+#}
+<?xml version="1.0" encoding="utf-8" ?>
+<opml version="2.0">
+  <head>
+    <title>{{ title }}</title>
+    <dateModified>{{ updated }}</dateModified>
+  </head>
+  <body>
+    {{ items }}
+  </body>
+</opml>
diff --git a/core/themes/stable/templates/views/views-view-row-opml.html.twig b/core/themes/stable/templates/views/views-view-row-opml.html.twig
new file mode 100644
index 0000000..431dc5c
--- /dev/null
+++ b/core/themes/stable/templates/views/views-view-row-opml.html.twig
@@ -0,0 +1,14 @@
+{#
+/**
+ * @file
+ * Default theme implementation to display an item in a views OPML feed.
+ *
+ * Available variables:
+ * - attributes: Attributes for outline element.
+ *
+ * @see template_preprocess_views_view_row_opml()
+ *
+ * @ingroup themeable
+ */
+#}
+    <outline{{ attributes }}/>
diff --git a/core/themes/stable/templates/views/views-view-row-rss.html.twig b/core/themes/stable/templates/views/views-view-row-rss.html.twig
new file mode 100644
index 0000000..caffb33
--- /dev/null
+++ b/core/themes/stable/templates/views/views-view-row-rss.html.twig
@@ -0,0 +1,30 @@
+{#
+/**
+ * @file
+ * Default theme implementation to display an item in a views RSS feed.
+ *
+ * Available variables:
+ * - title: RSS item title.
+ * - link: RSS item link.
+ * - description: RSS body text.
+ * - item_elements: RSS item elements to be rendered as XML (pubDate, creator,
+ *   guid).
+ *
+ * @see template_preprocess_views_view_row_rss()
+ *
+ * @ingroup themeable
+ */
+#}
+<item>
+  <title>{{ title }}</title>
+  <link>{{ link }}</link>
+  <description>{{ description }}</description>
+  {% for item in item_elements -%}
+    <{{ item.key }}{{ item.attributes -}}
+    {% if item.value -%}
+      >{{ item.value }}</{{ item.key }}>
+    {% else -%}
+      {{ ' />' }}
+    {% endif %}
+  {%- endfor %}
+</item>
diff --git a/core/themes/stable/templates/views/views-view-rss.html.twig b/core/themes/stable/templates/views/views-view-rss.html.twig
new file mode 100644
index 0000000..19b5097
--- /dev/null
+++ b/core/themes/stable/templates/views/views-view-rss.html.twig
@@ -0,0 +1,30 @@
+{#
+/**
+ * @file
+ * Default template for feed displays that use the RSS style.
+ *
+ * Available variables:
+ * - link: The link to the feed (the view path).
+ * - namespaces: The XML namespaces (added automatically).
+ * - title: The title of the feed (as set in the view).
+ * - description: The feed description (from feed settings).
+ * - langcode: The language encoding.
+ * - channel_elements: The formatted channel elements.
+ * - items: The feed items themselves.
+ *
+ * @see template_preprocess_views_view_rss()
+ *
+ * @ingroup themeable
+ */
+#}
+<?xml version="1.0" encoding="utf-8" ?>
+<rss version="2.0" xml:base="{{ link }}"{{ namespaces }}>
+  <channel>
+    <title>{{ title }}</title>
+    <link>{{ link }}</link>
+    <description>{{ description }}</description>
+    <language>{{ langcode }}</language>
+    {{ channel_elements }}
+    {{ items }}
+  </channel>
+</rss>
diff --git a/core/themes/stable/templates/views/views-view-summary-unformatted.html.twig b/core/themes/stable/templates/views/views-view-summary-unformatted.html.twig
new file mode 100644
index 0000000..15b955b
--- /dev/null
+++ b/core/themes/stable/templates/views/views-view-summary-unformatted.html.twig
@@ -0,0 +1,33 @@
+{#
+/**
+ * @file
+ * Default theme implementation for unformatted summary links.
+ *
+ * Available variables:
+ * - rows: The rows contained in this view.
+ *   - url: The URL to this row's content.
+ *   - count: The number of items this summary item represents.
+ *   - separator: A separator between each row.
+ *   - attributes: HTML attributes for a row.
+ *   - active: A flag indicating whether the row is active.
+ * - options: Flags indicating how each row should be displayed. This contains:
+ *   - count: A flag indicating whether the row's 'count' should be displayed.
+ *   - inline: A flag indicating whether the item should be wrapped in an inline
+ *     or block level HTML element.
+ *
+ * @see template_preprocess_views_view_summary_unformatted()
+ *
+ * @ingroup themeable
+ */
+#}
+{% for row in rows  %}
+  {{ options.inline ? '<span' : '<div' }} >
+  {% if row.separator -%}
+    {{ row.separator }}
+  {%- endif %}
+  <a href="{{ row.url }}"{{ row.attributes.addClass(row.active ? 'is-active')|without('href') }}>{{ row.link }}</a>
+  {% if options.count %}
+    ({{ row.count }})
+  {% endif %}
+  {{ options.inline ? '</span>' : '</div>' }}
+{% endfor %}
diff --git a/core/themes/stable/templates/views/views-view-summary.html.twig b/core/themes/stable/templates/views/views-view-summary.html.twig
new file mode 100644
index 0000000..3dde427
--- /dev/null
+++ b/core/themes/stable/templates/views/views-view-summary.html.twig
@@ -0,0 +1,31 @@
+{#
+/**
+ * @file
+ * Default theme implementation to display a list of summary lines.
+ *
+ * Available variables:
+ * - rows: The rows contained in this view.
+ *   Each row contains:
+ *   - url: The summary link URL.
+ *   - link: The summary link text.
+ *   - count: The number of items under this grouping.
+ *   - attributes: HTML attributes to apply to each row.
+ *   - active: A flag indicating whtether the row is active.
+ * - options: Flags indicating how the summary should be displayed.
+ *   This contains:
+ *   - count: A flag indicating whether the count should be displayed.
+ *
+ * @see template_preprocess_views_view_summary()
+ *
+ * @ingroup themeable
+ */
+#}
+<ul>
+  {% for row in rows %}
+    <li><a href="{{ row.url }}"{{ row.attributes.addClass(row.active ? 'is-active')|without('href') }}>{{ row.link }}</a>
+      {% if options.count %}
+        ({{ row.count }})
+      {% endif %}
+    </li>
+  {% endfor %}
+</ul>
diff --git a/core/themes/stable/templates/views/views-view-table.html.twig b/core/themes/stable/templates/views/views-view-table.html.twig
new file mode 100644
index 0000000..0c0b445
--- /dev/null
+++ b/core/themes/stable/templates/views/views-view-table.html.twig
@@ -0,0 +1,126 @@
+{#
+/**
+ * @file
+ * Default theme implementation for displaying a view as a table.
+ *
+ * Available variables:
+ * - attributes: Remaining HTML attributes for the element.
+ *   - class: HTML classes that can be used to style contextually through CSS.
+ * - title : The title of this group of rows.
+ * - header: The table header columns.
+ *   - attributes: Remaining HTML attributes for the element.
+ *   - content: HTML classes to apply to each header cell, indexed by
+ *   the header's key.
+ *   - default_classes: A flag indicating whether default classes should be
+ *     used.
+ * - caption_needed: Is the caption tag needed.
+ * - caption: The caption for this table.
+ * - accessibility_description: Extended description for the table details.
+ * - accessibility_summary: Summary for the table details.
+ * - rows: Table row items. Rows are keyed by row number.
+ *   - attributes: HTML classes to apply to each row.
+ *   - columns: Row column items. Columns are keyed by column number.
+ *     - attributes: HTML classes to apply to each column.
+ *     - content: The column content.
+ *   - default_classes: A flag indicating whether default classes should be
+ *     used.
+ * - responsive: A flag indicating whether table is responsive.
+ * - sticky: A flag indicating whether table header is sticky.
+ *
+ * @see template_preprocess_views_view_table()
+ *
+ * @ingroup themeable
+ */
+#}
+{%
+  set classes = [
+    'cols-' ~ header|length,
+    responsive ? 'responsive-enabled',
+    sticky ? 'sticky-enabled',
+  ]
+%}
+<table{{ attributes.addClass(classes) }}>
+  {% if caption_needed %}
+    <caption>
+    {% if caption %}
+      {{ caption }}
+    {% else %}
+      {{ title }}
+    {% endif %}
+    {% if (summary is not empty) or (description is not empty) %}
+      <details>
+        {% if summary is not empty %}
+          <summary>{{ summary }}</summary>
+        {% endif %}
+        {% if description is not empty %}
+          {{ description }}
+        {% endif %}
+      </details>
+    {% endif %}
+    </caption>
+  {% endif %}
+  {% if header %}
+    <thead>
+      <tr>
+        {% for key, column in header %}
+          {% if column.default_classes %}
+            {%
+              set column_classes = [
+                'views-field',
+                'views-field-' ~ fields[key],
+              ]
+            %}
+          {% endif %}
+          <th{{ column.attributes.addClass(column_classes).setAttribute('scope', 'col') }}>
+            {%- if column.wrapper_element -%}
+              <{{ column.wrapper_element }}>
+                {%- if column.url -%}
+                  <a href="{{ column.url }}" title="{{ column.title }}">{{ column.content }}{{ column.sort_indicator }}</a>
+                {%- else -%}
+                  {{ column.content }}{{ column.sort_indicator }}
+                {%- endif -%}
+              </{{ column.wrapper_element }}>
+            {%- else -%}
+              {%- if column.url -%}
+                <a href="{{ column.url }}" title="{{ column.title }}">{{ column.content }}{{ column.sort_indicator }}</a>
+              {%- else -%}
+                {{- column.content }}{{ column.sort_indicator }}
+              {%- endif -%}
+            {%- endif -%}
+          </th>
+        {% endfor %}
+      </tr>
+    </thead>
+  {% endif %}
+  <tbody>
+    {% for row in rows %}
+      <tr{{ row.attributes }}>
+        {% for key, column in row.columns %}
+          {% if column.default_classes %}
+            {%
+              set column_classes = [
+                'views-field'
+              ]
+            %}
+            {% for field in column.fields %}
+              {% set column_classes = column_classes|merge(['views-field-' ~ field]) %}
+            {% endfor %}
+          {% endif %}
+          <td{{ column.attributes.addClass(column_classes) }}>
+            {%- if column.wrapper_element -%}
+              <{{ column.wrapper_element }}>
+              {% for content in column.content %}
+                {{ content.separator }}{{ content.field_output }}
+              {% endfor %}
+              </{{ column.wrapper_element }}>
+            {%- else -%}
+              {% for content in column.content %}
+                {{- content.separator }}{{ content.field_output -}}
+              {% endfor %}
+            {%- endif %}
+          </td>
+        {% endfor %}
+      </tr>
+    {% endfor %}
+  </tbody>
+</table>
diff --git a/core/themes/stable/templates/views/views-view-unformatted.html.twig b/core/themes/stable/templates/views/views-view-unformatted.html.twig
new file mode 100644
index 0000000..7ee1f81
--- /dev/null
+++ b/core/themes/stable/templates/views/views-view-unformatted.html.twig
@@ -0,0 +1,32 @@
+{#
+/**
+ * @file
+ * Default theme implementation to display a view of unformatted rows.
+ *
+ * Available variables:
+ * - title: The title of this group of rows. May be empty.
+ * - rows: A list of the view's row items.
+ *   - attributes: The row's HTML attributes.
+ *   - content: The row's content.
+ * - view: The view object.
+ * - default_row_class: A flag indicating whether default classes should be
+ *   used on rows.
+ *
+ * @see template_preprocess_views_view_unformatted()
+ *
+ * @ingroup themeable
+ */
+#}
+{% if title %}
+  <h3>{{ title }}</h3>
+{% endif %}
+{% for row in rows %}
+  {%
+    set row_classes = [
+      default_row_class ? 'views-row',
+    ]
+  %}
+  <div{{ row.attributes.addClass(row_classes) }}>
+    {{ row.content }}
+  </div>
+{% endfor %}
diff --git a/core/themes/stable/templates/views/views-view.html.twig b/core/themes/stable/templates/views/views-view.html.twig
new file mode 100644
index 0000000..e81b1ab
--- /dev/null
+++ b/core/themes/stable/templates/views/views-view.html.twig
@@ -0,0 +1,68 @@
+{#
+/**
+ * @file
+ * Default theme implementation for main view template.
+ *
+ * Available variables:
+ * - attributes: Remaining HTML attributes for the element.
+ * - css_name: A css-safe version of the view name.
+ * - css_class: The user-specified classes names, if any.
+ * - header: The optional header.
+ * - footer: The optional footer.
+ * - rows: The results of the view query, if any.
+ * - empty: The content to display if there are no rows.
+ * - pager: The optional pager next/prev links to display.
+ * - exposed: Exposed widget form/info to display.
+ * - feed_icons: Optional feed icons to display.
+ * - more: An optional link to the next page of results.
+ * - title: Title of the view, only used when displaying in the admin preview.
+ * - title_prefix: Additional output populated by modules, intended to be
+ *   displayed in front of the view title.
+ * - title_suffix: Additional output populated by modules, intended to be
+ *   displayed after the view title.
+ * - attachment_before: An optional attachment view to be displayed before the
+ *   view content.
+ * - attachment_after: An optional attachment view to be displayed after the
+ *   view content.
+ * - dom_id: Unique id for every view being printed to give unique class for
+ *   Javascript.
+ *
+ * @see template_preprocess_views_view()
+ *
+ * @ingroup themeable
+ */
+#}
+{%
+  set classes = [
+    dom_id ? 'js-view-dom-id-' ~ dom_id,
+  ]
+%}
+<div{{ attributes.addClass(classes) }}>
+  {{ title_prefix }}
+  {{ title }}
+  {{ title_suffix }}
+
+  {% if header %}
+    <header>
+      {{ header }}
+    </header>
+  {% endif %}
+
+  {{ exposed }}
+  {{ attachment_before }}
+
+  {{ rows }}
+  {{ empty }}
+  {{ pager }}
+
+  {{ attachment_after }}
+  {{ more }}
+
+  {% if footer %}
+    <footer>
+      {{ footer }}
+    </footer>
+  {% endif %}
+
+  {{ feed_icons }}
+</div>
diff --git a/core/themes/stark/stark.info.yml b/core/themes/stark/stark.info.yml
index 70609c1..f6c9ba5 100644
--- a/core/themes/stark/stark.info.yml
+++ b/core/themes/stark/stark.info.yml
@@ -4,3 +4,4 @@ description: 'An intentionally plain theme with no styling to demonstrate defaul
 package: Core
 version: VERSION
 core: 8.x
+base theme: false
