diff --git a/composer.json b/composer.json
index 9ac297d29f5386d2bf1a219f5e46cedfb8f39702..6cf4d6d98e031f1f7935c89c6e96c74e53c95277 100644
--- a/composer.json
+++ b/composer.json
@@ -111,6 +111,7 @@
         "drupal/honeypot": "^1.28",
         "drupal/image_popup": "1.1",
         "drupal/inline_entity_form": "1.0-rc1",
+        "drupal/libraries": "^3.0@alpha",
         "drupal/link_attributes": "1.0",
         "drupal/linkit": "5.0-beta6",
         "drupal/magnific_popup": "1.3",
diff --git a/composer.lock b/composer.lock
index 3c0b7861833e5fa83708872fc5ef6659d7086c0e..0dd0fe6f7a8e09a265d13f728a39af2889e2ac61 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
         "This file is @generated automatically"
     ],
-    "content-hash": "d82d22e73cabb9ed6e70cdb5eff577c4",
+    "content-hash": "412cdee216390390463e9758c73ddff2",
     "packages": [
         {
             "name": "alchemy/zippy",
@@ -4335,6 +4335,67 @@
                 "source": "http://cgit.drupalcode.org/inline_entity_form"
             }
         },
+        {
+            "name": "drupal/libraries",
+            "version": "3.0.0-alpha1",
+            "source": {
+                "type": "git",
+                "url": "https://git.drupal.org/project/libraries",
+                "reference": "8.x-3.0-alpha1"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://ftp.drupal.org/files/projects/libraries-8.x-3.0-alpha1.zip",
+                "reference": "8.x-3.0-alpha1",
+                "shasum": "bb07036b1eaeea7d736fc7e72416238830cd8d67"
+            },
+            "require": {
+                "drupal/core": "~8.0"
+            },
+            "type": "drupal-module",
+            "extra": {
+                "branch-alias": {
+                    "dev-3.x": "3.x-dev"
+                },
+                "drupal": {
+                    "version": "8.x-3.0-alpha1",
+                    "datestamp": "1517046484",
+                    "security-coverage": {
+                        "status": "not-covered",
+                        "message": "Alpha releases are not covered by Drupal security advisories."
+                    }
+                }
+            },
+            "notification-url": "https://packages.drupal.org/8/downloads",
+            "license": [
+                "GPL-2.0+"
+            ],
+            "authors": [
+                {
+                    "name": "Pol",
+                    "homepage": "https://www.drupal.org/user/47194"
+                },
+                {
+                    "name": "rjacobs",
+                    "homepage": "https://www.drupal.org/user/422459"
+                },
+                {
+                    "name": "sun",
+                    "homepage": "https://www.drupal.org/user/54136"
+                },
+                {
+                    "name": "tstoeckler",
+                    "homepage": "https://www.drupal.org/user/107158"
+                }
+            ],
+            "description": "Allows version-dependent and shared usage of external libraries in Drupal.",
+            "homepage": "http://drupal.org/project/libraries",
+            "support": {
+                "source": "http://cgit.drupalcode.org/libraries",
+                "issues": "http://drupal.org/project/issues/libraries",
+                "irc": "irc://irc.freenode.org/drupal-contribute"
+            }
+        },
         {
             "name": "drupal/link_attributes",
             "version": "1.0.0",
@@ -10898,6 +10959,7 @@
         "drupal/bigmenu": 15,
         "drupal/block_field": 15,
         "drupal/entity_clone": 10,
+        "drupal/libraries": 15,
         "drupal/migrate_devel": 20,
         "drupal/realname": 5,
         "drupal/roleassign": 15,
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
index fcaa71f8ec64089bcefde301c15a875e2509d821..c7006146980475231b4380de8a2a088e1f11e1bb 100644
--- a/vendor/composer/installed.json
+++ b/vendor/composer/installed.json
@@ -4468,6 +4468,69 @@
             "source": "http://cgit.drupalcode.org/inline_entity_form"
         }
     },
+    {
+        "name": "drupal/libraries",
+        "version": "3.0.0-alpha1",
+        "version_normalized": "3.0.0.0-alpha1",
+        "source": {
+            "type": "git",
+            "url": "https://git.drupal.org/project/libraries",
+            "reference": "8.x-3.0-alpha1"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://ftp.drupal.org/files/projects/libraries-8.x-3.0-alpha1.zip",
+            "reference": "8.x-3.0-alpha1",
+            "shasum": "bb07036b1eaeea7d736fc7e72416238830cd8d67"
+        },
+        "require": {
+            "drupal/core": "~8.0"
+        },
+        "type": "drupal-module",
+        "extra": {
+            "branch-alias": {
+                "dev-3.x": "3.x-dev"
+            },
+            "drupal": {
+                "version": "8.x-3.0-alpha1",
+                "datestamp": "1517046484",
+                "security-coverage": {
+                    "status": "not-covered",
+                    "message": "Alpha releases are not covered by Drupal security advisories."
+                }
+            }
+        },
+        "installation-source": "dist",
+        "notification-url": "https://packages.drupal.org/8/downloads",
+        "license": [
+            "GPL-2.0+"
+        ],
+        "authors": [
+            {
+                "name": "Pol",
+                "homepage": "https://www.drupal.org/user/47194"
+            },
+            {
+                "name": "rjacobs",
+                "homepage": "https://www.drupal.org/user/422459"
+            },
+            {
+                "name": "sun",
+                "homepage": "https://www.drupal.org/user/54136"
+            },
+            {
+                "name": "tstoeckler",
+                "homepage": "https://www.drupal.org/user/107158"
+            }
+        ],
+        "description": "Allows version-dependent and shared usage of external libraries in Drupal.",
+        "homepage": "http://drupal.org/project/libraries",
+        "support": {
+            "source": "http://cgit.drupalcode.org/libraries",
+            "issues": "http://drupal.org/project/issues/libraries",
+            "irc": "irc://irc.freenode.org/drupal-contribute"
+        }
+    },
     {
         "name": "drupal/link_attributes",
         "version": "1.0.0",
diff --git a/web/modules/libraries/CHANGELOG.txt b/web/modules/libraries/CHANGELOG.txt
new file mode 100644
index 0000000000000000000000000000000000000000..6d1daa8351781ca92e24b008ece84bc2a2e27539
--- /dev/null
+++ b/web/modules/libraries/CHANGELOG.txt
@@ -0,0 +1,163 @@
+
+Libraries 8.x-3.x, xxxx-xx-xx
+-----------------------------
+#2833756 by 20th: Check that public://library-definitions directory does not exist
+#2825940 by tstoeckler, rjacobs: Remove LibrariesServiceProvider
+#2816115 by rjacobs: Remove ExtensionHandler service
+#2770983 by rjacobs: Update services.yml to reference new registry URL of http://cgit.drupalcode.org/libraries_registry/plain/registry/8
+#2770983 by rjacobs: Update shipped config to reference new registry URL of http://cgit.drupalcode.org/libraries_registry/plain/registry/8
+#2815189 by rjacobs, tstoeckler: Library dependency checking can fail for install profile
+#2770983 by tstoeckler, rjacobs: Fetch definitions remotely and store them locally.
+#2770983 by tstoeckler: Make Libraries API work on Windows.
+#2770983 by tstoeckler: Allow libraries to be symlinked to their right place.
+#2756265 by rajeshwari10: Replace deprecated usage of SafeMarkup::checkPlain().
+#2742333 by jrockowitz: Fix missing @group in LinePatternDetectorTest
+by tstoeckler: Document plugin alter hooks
+by tstoeckler: Fix coding standards
+by tstoeckler: Split out library dependencies to a separate interface
+by tstoeckler: Implement version detection for libraries
+by tstoeckler: Add deprecation notices to all legacy functions and hooks
+#2090623 by tstoeckler: Introduce the notion of library types
+#2090623 by tstoeckler: Provide support for remote asset libraries
+#2090623 by tstoeckler: Introduce a stream wrapper for asset libraries
+#2090623 by tstoeckler: Introduce the concept of locators
+#2606420 by googletorp, tstoeckler: Fix profile library detection
+#2090623 by tstoeckler: Add an external library registry
+#2572401 by rjacobs, yas: Fix missing @group annotation in PhpFileLibraryTest
+#2090623 by tstoeckler: Add a test for PHP file loading
+#2090623 by tstoeckler: Provide a modern, flexible library API
+#2525898 by rjacobs, jonhattan: Fix obsoleted cache bin declaration.
+#2471501 by LKS90: Replace all occurrences of String with the SafeMarkup equivalent.
+by tstoeckler: Fix drush libraries-list and drush cache-clear libraries.
+#2427801 by Anushka-mp, tstoeckler: Replace module_invoke() call.
+by tstoeckler: Fix tests.
+#2390301 by rjacobs: Fix DrupalUnitTestBase no longer exists so tests can't load.
+#2332157 by tstoeckler: Add a composer.json.
+#2287529 by drupalshrek, tstoeckler: Update installation link in README.txt.
+by tstoeckler: Fix tests.
+#2309203 by JayeshSolanki: Replace removed functions with module handler service.
+#2290767 by yukare: Replace removed cache() function.
+#2183087 by tstoeckler, rjacobs: Update for removed core functions.
+by tstoeckler: Fix tests.
+by tstoeckler: Provide required 'type' key in test library info file.
+#2090351 by tstoeckler: Remove obsolete hook_flush_caches().
+#2090425 by tstoeckler: Adapt for renamed ControllerInterface.
+#2090323 by tstoeckler: Remove obsolete libraries_parse_dependency().
+#2090379 by tstoeckler: Change 'pattern' to 'path' in routing YAML file.
+#2058371 by gordon: Re-port to Drupal 8 (.info.yml, controllers, cache service, ...).
+#1779714 by tstoeckler, klonos: Wrong filepath in README.txt and fix JS testing.
+#1167496 by tstoeckler: Remove leftover libraries.test file.
+#1167496 by tstoeckler, benshell: Port to Drupal 8.
+
+
+Libraries 7.x-3.x, xxxx-xx-xx
+-----------------------------
+#1938638 by tstoeckler: Remove unneeded check.
+
+
+Libraries 7.x-2.x, xxxx-xx-xx
+-----------------------------
+#2352251 by netw3rker: Fix incorrect hook name in libraries.api.php.
+#2352237 by netw3rker, tstoeckler: Allow clearing the libraries cache from Drush.
+#2193969 by tstoeckler: Avoid warnings for stale library caches.
+#2287529 by drupalshrek, tstoeckler: Update installation link in README.txt.
+
+Libraries 7.x-2.2, 2014-02-09
+-----------------------------
+#2046919 by tstoeckler: Clarify 'version' docs.
+#1946110 by munroe_richard: Allow uppercase letters as library machine names.
+#1953260 by tstoeckler: Improve documentation of libraries_get_version().
+#1855918 by tstoeckler: Make integration file loading backwards-compatible.
+#1876124 by tstoeckler: Fix integration files for themes.
+#1876124 by tstoeckler: Add tests for theme-provided library information.
+#1876124 by tstoeckler: Prepare for adding a test theme.
+#1876124 by tstoeckler | whastings, fubhy: Fix hook_libraries_info() for themes.
+#2015721 by tstoeckler, CaptainHook: Protect against files overriding local variables.
+#2046919 by tstoeckler: Improve documentation around 'version callback'.
+#1844272 by tstoeckler, jweowu: Fix typos in libraries.api.php.
+#1938638 by tstoeckler: Prevent weird PHP notice on update.
+#1329388 by RobLoach, tstoeckler: Clear static caches in libraries_flush_caches().
+#1855918 by rbayliss: Load integration files after library files.
+#1938638 by Pol: Fix typo in libraries.api.php.
+
+Libraries 7.x-2.1, 2013-03-09
+-----------------------------
+#1937446 by Pol, tstoeckler: Add a 'pre-dependencies-load' callback group.
+#1775668 by tstoeckler: Fix bogus assertion message in assertLibraryFiles().
+#1773640 by tstoeckler: Use drupal_get_path() to find the profile directory.
+#1565426 by tstoeckler: Invoke hook_libraries_info() in enabled themes.
+
+Libraries 7.x-2.0, 2012-07-29
+-----------------------------
+#1606018 by chemical: Tests fail if the module is downloaded from Drupal.org.
+#1386250 by tstoeckler: Clarify module and library installation in README.txt.
+#1578618 by iamEAP: Fixed Fatal cache flush failure on major version upgrades.
+#1449346 by tstoeckler, sun: Clean-up libraries.test
+
+Libraries 7.x-2.0-alpha2, 2011-12-15
+------------------------------------
+#1299076 by tstoeckler: Improve testing of JS, CSS, and PHP files.
+#1347214 by rfay: Improve update function 7200.
+#1323530 by tstoeckler: Document libraries_get_version() pattern matching.
+#1325524 by sun, Rob Loach, tstoeckler: Statically cache libraries_detect().
+#1321372 by Rob Loach: Provide a 'post-load' callback group.
+#1205854 by tstoeckler, sun: Test library caching.
+
+Libraries 7.x-2.0-alpha1, 2011-10-01
+------------------------------------
+#1268342 by tstoeckler: Clean up drush libraries-list command.
+#962214 by tstoeckler, sun: Add support for library dependencies.
+#1224838 by sun, mjpa: Fix library path not being prepended to JS/CSS files.
+#1023258 by tstoeckler: Make 'files' consistently keyed by filename.
+#958162 by sun, tstoeckler: Add pre-detect callback group.
+#958162 by sun, tstoeckler: Make tests debuggable and provide libraries_info_defaults().
+#961476 by tstoeckler: Changed libraries_get_path() to return FALSE by default.
+#958162 by tstoeckler, sun, good_man: Allow to apply callbacks to libraries.
+#1125904 by tstoeckler, boombatower: Fix drush libraries-list.
+#1050076 by tstoeckler: Re-utilize libraries_detect() and remove libraries_detect_library().
+#466090 by tstoeckler: Add update function.
+#466090 by tstoeckler: Allow cache to be flushed.
+#466090 by tstoeckler, sun: Cache library information.
+#1064008 by tstoeckler, bfroehle: Fix outdated API examples in libraries.api.php.
+#1028744 by tstoeckler: Code clean-up.
+#1023322 by tstoeckler, sun: Fixed libraries shouldn't be loaded multiple times.
+#1024080 by hswong3i, tstoeckler: Fixed installation profile retrieval.
+#995988 by good_man: Wrong default install profile.
+#975498 by Gábor Hojtsy: Update JS/CSS-loading to new drupal_add_js/css() API.
+#958162 by tsteoeckler, sun: Consistent variable naming.
+#924130 by aaronbauman: Fixed libraries_get_path() should use drupal_static().
+#958162 by tstoeckler, sun: Code clean-up, tests revamp, more robust loading.
+#919632 by tstoeckler, sun: Allow library information to be stored in info files.
+by sun: Fixed testbot breaks upon directory name/info file name mismatch.
+#864376 by tstoeckler, sun: Code-cleanup, allow hard-coded 'version'.
+#939174 by sun, tstoeckler: Rename example.info to libraries_example.info.
+by sun: Fixed testbot breaks upon .info file without .module file.
+#542940 by tstoeckler, sun: Add libraries-list command.
+#919632 by tstoeckler: Add example library info file for testing purposes.
+#719896 by tstoeckler, sun: Documentation clean-up and tests improvement.
+#542940 by sun: Added initial Drush integration file.
+#719896 by tstoeckler, sun: Improved library detection and library loading.
+#855050 by Gábor Hojtsy: Avoid call-time pass by reference in libraries_detect().
+#719896 by tstoeckler, sun: Added starting point for hook_libraries_info().
+
+
+Libraries 7.x-1.x, xxxx-xx-xx
+-----------------------------
+
+Libraries 7.x-1.0, 2010-01-27
+-----------------------------
+#743522 by sun: Ported to D7.
+
+
+Libraries 6.x-1.x, xxxx-xx-xx
+-----------------------------
+
+Libraries 6.x-1.0, 2010-01-27
+-----------------------------
+#1028744 by tstoeckler: Code clean-up.
+#496732 by tstoeckler, robphillips: Allow placing libraries in root directory.
+
+Libraries 6.x-1.0-alpha1, 2009-12-30
+------------------------------------
+#480440 by markus_petrux: Fixed base_path() not applied to default library path.
+#320562 by sun: Added basic functions.
diff --git a/web/modules/libraries/LICENSE.txt b/web/modules/libraries/LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d159169d1050894d3ea3b98e1c965c4058208fe1
--- /dev/null
+++ b/web/modules/libraries/LICENSE.txt
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/web/modules/libraries/README.txt b/web/modules/libraries/README.txt
new file mode 100644
index 0000000000000000000000000000000000000000..9ff0ece53b15239fbe50aac61ebf9db03f9819d0
--- /dev/null
+++ b/web/modules/libraries/README.txt
@@ -0,0 +1,40 @@
+
+# Libraries API
+
+## General information
+
+Libraries API provides external library handling for Drupal modules.
+
+Relevant links:
+- [Project page](https://www.drupal.org/project/libraries)
+- [Issue tracker](https://www.drupal.org/project/issues/libraries)
+- [Repository viewer](http://cgit.drupalcode.org/libraries)
+
+### Installation
+
+Install like any module, see the
+[Drupal.org handbook](https://www.drupal.org/documentation/install/modules-themes/modules-8)
+for further information. Note that installing external libraries is separate from
+installing this module and should happen in the top-level `libraries` directory.
+See the online [module documentation](https://www.drupal.org/node/1440066) for more
+information.
+
+
+### Maintainers
+
+Current maintainers:
+- Daniel F. Kudwien ([sun](https://www.drupal.org/u/sun))
+- Tobias Stöckler ([tstoeckler](http://www.drupal.org/u/tstoeckler))
+- Ryan Jacobs ([rjacobs](http://www.drupal.org/u/rjacobs))
+- Pol Dellaiera ([pol](http://www.drupal.org/u/pol))
+
+### Sponsorship
+
+This project has been sponsored by:
+- **UNLEASHED MIND**
+  Specialized in consulting and planning of Drupal powered sites, UNLEASHED
+  MIND offers installation, development, theming, customization, and hosting
+  to get you started. Visit
+  [http://www.unleashedmind.com](http://www.unleashedmind.com) for more
+  information.
+
diff --git a/web/modules/libraries/composer.json b/web/modules/libraries/composer.json
new file mode 100644
index 0000000000000000000000000000000000000000..4af11e12e838280670e4095241afeef46d2d91ca
--- /dev/null
+++ b/web/modules/libraries/composer.json
@@ -0,0 +1,14 @@
+{
+  "name": "drupal/libraries",
+  "description": "Allows version-dependent and shared usage of external libraries in Drupal.",
+  "type": "drupal-module",
+  "homepage": "http://drupal.org/project/libraries",
+  "authors": [
+  ],
+  "support": {
+    "issues": "http://drupal.org/project/issues/libraries",
+    "irc": "irc://irc.freenode.org/drupal-contribute",
+    "source": "http://cgit.drupalcode.org/libraries"
+  },
+  "license": "GPL-2.0+"
+}
diff --git a/web/modules/libraries/config/install/libraries.settings.yml b/web/modules/libraries/config/install/libraries.settings.yml
new file mode 100644
index 0000000000000000000000000000000000000000..0981884b1ad3ee32726149b1d6b4f4e950a16354
--- /dev/null
+++ b/web/modules/libraries/config/install/libraries.settings.yml
@@ -0,0 +1,10 @@
+definition:
+  local:
+    # @todo Implement a stream wrapper that finds library definitions in e.g.
+    # sites/all/libraries.
+    path: 'public://library-definitions'
+  remote:
+    enable: TRUE
+    urls:
+      - 'http://cgit.drupalcode.org/libraries_registry/plain/registry/8'
+global_locators: []
\ No newline at end of file
diff --git a/web/modules/libraries/config/schema/libraries.schema.yml b/web/modules/libraries/config/schema/libraries.schema.yml
new file mode 100644
index 0000000000000000000000000000000000000000..72c4c52f2e80873654a90422578c90d5ae68948f
--- /dev/null
+++ b/web/modules/libraries/config/schema/libraries.schema.yml
@@ -0,0 +1,53 @@
+# Configuration schema for the Libraries API module.
+
+# Base configuration schema
+libraries.settings:
+  type: config_object
+  label: 'Libraries API settings'
+  mapping:
+    definition:
+      type: mapping
+      label: 'Library definition settings'
+      mapping:
+        local:
+          type: mapping
+          label: 'Local'
+          mapping:
+            path:
+              type: path
+              label: 'Local path'
+        remote:
+          type: mapping
+          title: 'Remote'
+          mapping:
+            enable:
+              type: boolean
+              label: 'Enable remote fetching of library definitions'
+            urls:
+              type: sequence
+              label: 'A list of remote library registry URLs'
+              sequence:
+                type: uri
+                label: 'The URL of a remote library registry'
+    global_locators:
+      type: sequence
+      title: 'Global library locators'
+      sequence:
+        type: mapping
+        title: 'Global locator plugins'
+        mapping:
+          id:
+            type: string
+            title: 'The locator plugin id'
+          configuration:
+            type: libraries.locator.[%parent.id]
+            title: 'The plugin configuration'
+
+# Dynamic locator plugin schema
+libraries.locator.uri:
+  type: mapping
+  label: 'URI locator configuration'
+  mapping:
+    uri:
+      type: uri
+      label: 'The locator URI'
\ No newline at end of file
diff --git a/web/modules/libraries/libraries.api.php b/web/modules/libraries/libraries.api.php
new file mode 100644
index 0000000000000000000000000000000000000000..859e6023eb4529bcdb3731fefc7a44971744aabe
--- /dev/null
+++ b/web/modules/libraries/libraries.api.php
@@ -0,0 +1,609 @@
+<?php
+
+/**
+ * @file
+ * Documents API functions for Libraries module.
+ */
+
+/**
+ * @defgroup libraries External libraries
+ * @{
+ * External libraries are not shipped as part of contributed modules for
+ * licensing and maintenance reasons. The Libraries API module aims to solve the
+ * problem of integrating with and loading external libraries as part of the
+ * Drupal request-response process in a generic way.
+ *
+ * @section sec_definitions Library definitions
+ * In order to be useful to other modules Libraries API needs a list of known
+ * libraries and metadata about each of the libraries. Because multiple modules
+ * themes may integrate with the same external library a key objective of
+ * Libraries API is to keep this information separate from any one module or
+ * theme.
+ *
+ * Definitions are accessed via a discovery that is responsible for checking
+ * whether a given definition exists and fetching it, if it is. See
+ * LibraryRegistryInterface and StreamDefinitionDiscovery for more information.
+ *
+ * @subsection sub_definitions_machine_name
+ * A central part of a library's metadata is the library's machine name or ID.
+ * For maximum interoperability it must consist of only lowercase ASCII letters,
+ * numbers, and underscores. As the machine name is the single identifier of a
+ * library and is independent of any given module or theme name it must be
+ * unique among all libraries known to Libraries API.
+ *
+ * @subsection sub_definitions_history Historical background
+ * In Drupal 7 library information could already be provided by
+ * module-independent info files, but this was not widely used, because there
+ * was no way to distribute these info files properly. The information was
+ * predominantly provided by a hook that modules could implement, which caused
+ * race conditions between modules providing information for the same library.
+ * Thus, in Drupal 8 there is no longer a hook making it necessary to properly
+ * solve the problem of centrally maintaining and distributing library info
+ * files. This has yet to be done. See https://www.drupal.org/node/773508 for
+ * more information.
+ *
+ * @section sec_types Library types
+ * Libraries are classed objects that implement LibraryInterface. This generic
+ * interface only dictates that a library is aware of its ID. Any further
+ * functionality depends on the type of library, each type of library comes with
+ * a dedicated interface. See LibraryInterface for more information.
+ *
+ * @subsection sub_types_version Version detection
+ * A central aspect of Libraries API is version detection. Modules or themes may
+ * only work with a specific version of an external library, so Libraries API
+ * needs a way to detect the version of a library by inspecting the library
+ * files.
+ *
+ * As the mechanism for doing this is generally not specific to any one
+ * library, it is handled by version detector plugins. A 'line_pattern' plugin
+ * that scans a file line by line whether for whether a pattern containing the
+ * version is matched. It can be used if the version is always specified in a
+ * particular place in a particular file, for example a changelog. See
+ * VersionDetectorInterface and LinePatternDetector for more information.
+ *
+ * @subsection sub_types_dependency Dependency handling
+ * Many libraries depend on other libraries to function. Thus, most library
+ * classes should implement DependentLibraryInterface to allow libraries to
+ * declare their dependencies as part of their metadata. In case of API changes
+ * in the dependencies libraries need to be able to declare dependencies on
+ * specific versions or version ranges of other libraries. This has yet to be
+ * implemented.
+ *
+ * Furthermore, Libraries API must also maintain a list of libraries that are
+ * required by the installed installation profile, modules, and themes
+ * (extensions). With this information installation of extensions with library
+ * dependencies can be prevented until the libraries are properly installed.
+ * This is currently not implemented. In the future this will be used to
+ * automatically retrieve library definitions of required libraries, and
+ * possibly to automatically download the libraries themselves.
+ *
+ * To declare library dependencies extensions can place a 'library_dependencies'
+ * key in their info file with a list of library machine names as the value.
+ * For example:
+ * @code
+ *   name: My module
+ *   type: module
+ *   core: 8.x
+ *   library_dependencies:
+ *     - flexslider
+ *     - jquery_mobile
+ * @endcode
+ *
+ * @subsection sub_types_asset Asset libraries
+ * With Drupal 8 relying on Composer for autoloading and dependency resolution
+ * of PHP libraries, asset libraries are the primary use-case for Libraries API.
+ * Because asset libraries cannot be loaded ad-hoc, but must be attached to a
+ * renderable element, Libraries API registers external asset libraries that are
+ * required by the installed extensions with the core asset library system. See
+ * AssetLibraryInterface for more information.
+ *
+ * @subsection sub_types_php_file
+ * For feature parity with the Drupal 7 version of this module, a PHP file
+ * library type is provided, that can load a list of PHP files on demand.
+ * Generally, it is encouraged to use Composer instead of this library type and
+ * avoid Libraries API altogether for PHP libraries. See PhpFileLibraryInterface
+ * for more information.
+ *
+ * This library type might be removed in a future version of Libraries API.
+ *
+ * @see \Drupal\libraries\ExternalLibrary\Definition\DefinitionDiscoveryInterface
+ * @see \Drupal\libraries\ExternalLibrary\Definition\StreamDefinitionDiscovery
+ * @see \Drupal\libraries\ExternalLibrary\LibraryInterface
+ * @see \Drupal\libraries\ExternalLibrary\Version\VersionDetectorInterface
+ * @see \Drupal\libraries\Plugin\libraries\VersionDetector\LinePatternDetector
+ * @see \Drupal\libraries\ExternalLibrary\Version\VersionedLibraryInterface
+ * @see \Drupal\libraries\ExternalLibrary\Dependency\DependentLibraryInterface
+ * @see \Drupal\libraries\ExternalLibrary\Asset\AssetLibraryInterface
+ * @see \Drupal\libraries\ExternalLibrary\PhpFile\PhpFileLibraryInterface
+ *
+ * @}
+ */
+
+/**
+ * Alter library type information.
+ *
+ * @param array $library_types
+ *   An array of library types keyed by ID. Each library type is an array with
+ *   the following keys:
+ *   - id: The ID of the library type.
+ *   - class: The class to use for this library type.
+ *   - provider: The provider of this library type.
+ */
+function hook_libraries_library_type_info_alter(array &$library_types) {
+  // Use a different class for the asset library type. Note that this class is
+  // distinct from the class actually for asset libraries themselves.
+  $library_types['asset']['class'] = 'Drupal\mymodule\ExternalLibrary\BetterAssetLibraryType';
+}
+
+/**
+ * Alter library locator information.
+ *
+ * @param array $locators
+ *   An array of library locators keyed by ID. Each locator is an array with the
+ *   following keys:
+ *   - id: The ID of the library locator.
+ *   - class: The class to use for this library locator.
+ *   - provider: The provider of this library locator.
+ */
+function hook_libraries_locator_info_alter(array &$locators) {
+  // Use a different class for the stream locator.
+  $locators['stream']['class'] = 'Drupal\mymodule\ExternalLibrary\BetterStreamLocator';
+}
+
+/**
+ * Alter library version detector information.
+ *
+ * @param array $version_detectors
+ *   An array of library version detectors keyed by ID. Each detector is an
+ *   array with the following keys:
+ *   - id: The ID of the library version detector.
+ *   - class: The class to use for this library version detector.
+ *   - provider: The provider of this library version detector.
+ */
+function hook_libraries_version_detector_info_alter(array &$version_detectors) {
+  // Use a different class for the line pattern locator.
+  $version_detectors['line_pattern']['class'] = 'Drupal\mymodule\ExternalLibrary\BetterLinePatternDetector';
+}
+
+/**
+ * Return information about external libraries.
+ *
+ * @return
+ *   An associative array whose keys are internal names of libraries and whose
+ *   values are describing each library. Each key is the directory name below
+ *   the 'libraries' directory, in which the library may be found. Each value is
+ *   an associative array containing:
+ *   - name: The official, human-readable name of the library.
+ *   - vendor url: The URL of the homepage of the library.
+ *   - download url: The URL of a web page on which the library can be obtained.
+ *   - path: (optional) A relative path from the directory of the library to the
+ *     actual library. Only required if the extracted download package contains
+ *     the actual library files in a sub-directory.
+ *   - library path: (optional) The absolute path to the library directory. This
+ *     should not be declared normally, as it is automatically detected, to
+ *     allow for multiple possible library locations. A valid use-case is an
+ *     external library, in which case the full URL to the library should be
+ *     specified here.
+ *   - version: (optional) The version of the library. This should not be
+ *     declared normally, as it is automatically detected (see 'version
+ *     callback' below) to allow for version changes of libraries without code
+ *     changes of implementing modules and to support different versions of a
+ *     library simultaneously (though only one version can be installed per
+ *     site). A valid use-case is an external library whose version cannot be
+ *     determined programmatically.
+ *   - version callback: (optional) The name of a function that detects and
+ *     returns the full version string of the library. The first argument is
+ *     always $library, an array containing all library information as described
+ *     here. There are two ways to declare the version callback's additional
+ *     arguments, either as a single $options parameter or as multiple
+ *     parameters, which correspond to the two ways to specify the argument
+ *     values (see 'version arguments'). Defaults to libraries_get_version().
+ *   - version arguments: A list of arguments to pass to the version callback.
+ *     Version arguments can be declared either as an associative array whose
+ *     keys are the argument names or as an indexed array without specifying
+ *     keys. If declared as an associative array, the arguments get passed to
+ *     the version callback as a single $options parameter whose keys are the
+ *     argument names (i.e. $options is identical to the specified array). If
+ *     declared as an indexed array, the array values get passed to the version
+ *     callback as separate arguments in the order they were declared. The
+ *     default version callback libraries_get_version() expects a single,
+ *     associative array with named keys:
+ *     - file: The filename to parse for the version, relative to the library
+ *       path. For example: 'docs/changelog.txt'.
+ *     - pattern: A string containing a regular expression (PCRE) to match the
+ *       library version. For example: '@version\s+([0-9a-zA-Z\.-]+)@'. Note
+ *       that the returned version is not the match of the entire pattern (i.e.
+ *       '@version 1.2.3' in the above example) but the match of the first
+ *       sub-pattern (i.e. '1.2.3' in the above example).
+ *     - lines: (optional) The maximum number of lines to search the pattern in.
+ *       Defaults to 20.
+ *     - cols: (optional) The maximum number of characters per line to take into
+ *       account. Defaults to 200. In case of minified or compressed files, this
+ *       prevents reading the entire file into memory.
+ *   - files: An associative array of library files to load. Supported keys are:
+ *     - js: A list of JavaScript files to load, using the same syntax as Drupal
+ *       core's hook_library().
+ *     - css: A list of CSS files to load, using the same syntax as Drupal
+ *       core's hook_library().
+ *     - php: A list of PHP files to load.
+ *   - dependencies: An array of libraries this library depends on. Similar to
+ *     declaring module dependencies, the dependency declaration may contain
+ *     information on the supported version. Examples of supported declarations:
+ *     @code
+ *     $libraries['dependencies'] = array(
+ *       // Load the 'example' library, regardless of the version available:
+ *       'example',
+ *       // Only load the 'example' library, if version 1.2 is available:
+ *       'example (1.2)',
+ *       // Only load a version later than 1.3-beta2 of the 'example' library:
+ *       'example (>1.3-beta2)'
+ *       // Only load a version equal to or later than 1.3-beta3:
+ *       'example (>=1.3-beta3)',
+ *       // Only load a version earlier than 1.5:
+ *       'example (<1.5)',
+ *       // Only load a version equal to or earlier than 1.4:
+ *       'example (<=1.4)',
+ *       // Combinations of the above are allowed as well:
+ *       'example (>=1.3-beta2, <1.5)',
+ *     );
+ *     @endcode
+ *   - variants: (optional) An associative array of available library variants.
+ *     For example, the top-level 'files' property may refer to a default
+ *     variant that is compressed. If the library also ships with a minified and
+ *     uncompressed/source variant, those can be defined here. Each key should
+ *     describe the variant type, e.g. 'minified' or 'source'. Each value is an
+ *     associative array of top-level properties that are entirely overridden by
+ *     the variant, most often just 'files'. Additionally, each variant can
+ *     contain following properties:
+ *     - variant callback: (optional) The name of a function that detects the
+ *       variant and returns TRUE or FALSE, depending on whether the variant is
+ *       available or not. The first argument is always $library, an array
+ *       containing all library information as described here. The second
+ *       argument is always a string containing the variant name. There are two
+ *       ways to declare the variant callback's additional arguments, either as a
+ *       single $options parameter or as multiple parameters, which correspond
+ *       to the two ways to specify the argument values (see 'variant
+ *       arguments'). If omitted, the variant is expected to always be
+ *       available.
+ *     - variant arguments: A list of arguments to pass to the variant callback.
+ *       Variant arguments can be declared either as an associative array whose
+ *       keys are the argument names or as an indexed array without specifying
+ *       keys. If declared as an associative array, the arguments get passed to
+ *       the variant callback as a single $options parameter whose keys are the
+ *       argument names (i.e. $options is identical to the specified array). If
+ *       declared as an indexed array, the array values get passed to the
+ *       variant callback as separate arguments in the order they were declared.
+ *     Variants can be version-specific (see 'versions').
+ *   - versions: (optional) An associative array of supported library versions.
+ *     Naturally, libraries evolve over time and so do their APIs. In case a
+ *     library changes between versions, different 'files' may need to be
+ *     loaded, different 'variants' may become available, or Drupal modules need
+ *     to load different integration files adapted to the new version. Each key
+ *     is a version *string* (PHP does not support floats as keys). Each value
+ *     is an associative array of top-level properties that are entirely
+ *     overridden by the version.
+ *   - integration files: (optional) An associative array whose keys are module
+ *     names and whose values are sets of files to load for the module, using
+ *     the same notion as the top-level 'files' property. Each specified file
+ *     should contain the path to the file relative to the module it belongs to.
+ *   - callbacks: An associative array whose keys are callback groups and whose
+ *     values are arrays of callbacks to apply to the library in that group.
+ *     Each callback receives the following arguments:
+ *     - $library: An array of library information belonging to the top-level
+ *       library, a specific version, a specific variant or a specific variant
+ *       of a specific version. Because library information such as the 'files'
+ *       property (see above) can be declared in all these different locations
+ *       of the library array, but a callback may have to act on all these
+ *       different parts of the library, it is called recursively for each
+ *       library with a certain part of the libraries array passed as $library
+ *       each time.
+ *     - $version: If the $library array belongs to a certain version (see
+ *       above), a string containing the version. This argument may be empty, so
+ *       NULL should be specified as the default value.
+ *     - $variant: If the $library array belongs to a certain variant (see
+ *       above), a string containing the variant name. This argument may be
+ *       empty, so NULL should be specified as the default value.
+ *     Valid callback groups are:
+ *     - info: Callbacks registered in this group are applied after the library
+ *       information has been retrieved via hook_libraries_info() or info files.
+ *     - pre-detect: Callbacks registered in this group are applied after the
+ *       library path has been determined and before the version callback is
+ *       invoked. At this point the following additional information is available:
+ *       - $library['library path']: The path on the file system to the library.
+ *     - post-detect: Callbacks registered in this group are applied after the
+ *       library has been successfully detected. At this point the library
+ *       contains the version-specific information, if specified, and following
+ *       additional information is available:
+ *       - $library['installed']: A boolean indicating whether the library is
+ *         installed or not.
+ *       - $library['version']: If it could be detected, a string containing the
+ *         version of the library.
+ *       - $library['variants'][$variant]['installed']: For each specified
+ *         variant, a boolean indicating whether the variant is installed or
+ *         not.
+ *       Note that in this group the 'versions' property is no longer available.
+ *     - pre-load: Callbacks registered in this group are applied directly
+ *       before this library is loaded. At this point the library contains
+ *       variant-specific information, if specified. Note that in this group the
+ *       'variants' property is no longer available.
+ *     - post-load: Callbacks registered in this group are applied directly
+ *       after this library is loaded. At this point, the library contains a
+ *       'loaded' key, which contains the number of files that were loaded.
+ *   Additional top-level properties can be registered as needed.
+ *
+ * @see hook_library()
+ *
+ * @deprecated Will be removed before a stable Drupal 8 release.
+ */
+function hook_libraries_info() {
+  // The following is a full explanation of all properties. See below for more
+  // concrete example implementations.
+
+  // This array key lets Libraries API search for 'sites/all/libraries/example'
+  // directory, which should contain the entire, original extracted library.
+  $libraries['example'] = array(
+    // Only used in administrative UI of Libraries API.
+    'name' => 'Example library',
+    'vendor url' => 'http://example.com',
+    'download url' => 'http://example.com/download',
+    // Optional: If, after extraction, the actual library files are contained in
+    // 'sites/all/libraries/example/lib', specify the relative path here.
+    'path' => 'lib',
+    // Optional: Define a custom version detection callback, if required.
+    'version callback' => 'mymodule_get_version',
+    // Specify arguments for the version callback. By default,
+    // libraries_get_version() takes a named argument array:
+    'version arguments' => array(
+      'file' => 'docs/CHANGELOG.txt',
+      'pattern' => '@version\s+([0-9a-zA-Z\.-]+)@',
+      'lines' => 5,
+      'cols' => 20,
+    ),
+    // Default list of files of the library to load. Important: Only specify
+    // third-party files belonging to the library here, not integration files of
+    // your module.
+    'files' => array(
+      // 'js' and 'css' follow the syntax of hook_library(), but file paths are
+      // relative to the library path.
+      'js' => array(
+        'exlib.js',
+        'gadgets/foo.js',
+      ),
+      'css' => array(
+        'lib_style.css',
+        'skin/example.css',
+      ),
+      // For PHP libraries, specify include files here, still relative to the
+      // library path.
+      'php' => array(
+        'exlib.php',
+        'exlib.inc',
+      ),
+    ),
+    // Optional: Specify alternative variants of the library, if available.
+    'variants' => array(
+      // All properties defined for 'minified' override top-level properties.
+      'minified' => array(
+        'files' => array(
+          'js' => array(
+            'exlib.min.js',
+            'gadgets/foo.min.js',
+          ),
+          'css' => array(
+            'lib_style.css',
+            'skin/example.css',
+          ),
+        ),
+        'variant callback' => 'mymodule_check_variant',
+        'variant arguments' => array(
+          'variant' => 'minified',
+        ),
+      ),
+    ),
+    // Optional, but usually required: Override top-level properties for later
+    // versions of the library. The properties of the minimum version that is
+    // matched override the top-level properties. Note:
+    // - When registering 'versions', it usually does not make sense to register
+    //   'files', 'variants', and 'integration files' on the top-level, as most
+    //   of those likely need to be different per version and there are no
+    //   defaults.
+    // - The array keys have to be strings, as PHP does not support floats for
+    //   array keys.
+    'versions' => array(
+      '2' => array(
+        'files' => array(
+          'js' => array('exlib.js'),
+          'css' => array('exlib_style.css'),
+        ),
+      ),
+      '3.0' => array(
+        'files' => array(
+          'js' => array('exlib.js'),
+          'css' => array('lib_style.css'),
+        ),
+      ),
+      '3.2' => array(
+        'files' => array(
+          'js' => array(
+            'exlib.js',
+            'gadgets/foo.js',
+          ),
+          'css' => array(
+            'lib_style.css',
+            'skin/example.css',
+          ),
+        ),
+      ),
+    ),
+    // Optional: Register files to auto-load for your module. All files must be
+    // keyed by module, and follow the syntax of the 'files' property.
+    'integration files' => array(
+      'mymodule' => array(
+        'js' => array('ex_lib.inc'),
+      ),
+    ),
+    // Optionally register callbacks to apply to the library during different
+    // stages of its lifetime ('callback groups').
+    'callbacks' => array(
+      // Used to alter the info associated with the library.
+      'info' => array(
+        'mymodule_example_libraries_info_callback',
+      ),
+      // Called before detecting the given library.
+      'pre-detect' => array(
+        'mymodule_example_libraries_predetect_callback',
+      ),
+      // Called after detecting the library.
+      'post-detect' => array(
+        'mymodule_example_libraries_postdetect_callback',
+      ),
+      // Called before the library is loaded.
+      'pre-load' => array(
+        'mymodule_example_libraries_preload_callback',
+      ),
+      // Called after the library is loaded.
+      'post-load' => array(
+        'mymodule_example_libraries_postload_callback',
+      ),
+    ),
+  );
+
+  // A very simple library. No changing APIs (hence, no versions), no variants.
+  // Expected to be extracted into 'sites/all/libraries/simple'.
+  $libraries['simple'] = array(
+    'name' => 'Simple library',
+    'vendor url' => 'http://example.com/simple',
+    'download url' => 'http://example.com/simple',
+    'version arguments' => array(
+      'file' => 'readme.txt',
+      // Best practice: Document the actual version strings for later reference.
+      // 1.x: Version 1.0
+      'pattern' => '/Version (\d+)/',
+      'lines' => 5,
+    ),
+    'files' => array(
+      'js' => array('simple.js'),
+    ),
+  );
+
+  // A library that (naturally) evolves over time with API changes.
+  $libraries['tinymce'] = array(
+    'name' => 'TinyMCE',
+    'vendor url' => 'http://tinymce.moxiecode.com',
+    'download url' => 'http://tinymce.moxiecode.com/download.php',
+    'path' => 'jscripts/tiny_mce',
+    // The regular expression catches two parts (the major and the minor
+    // version), which libraries_get_version() doesn't allow.
+    'version callback' => 'tinymce_get_version',
+    'version arguments' => array(
+      // It can be easier to parse the first characters of a minified file
+      // instead of doing a multi-line pattern matching in a source file. See
+      // 'lines' and 'cols' below.
+      'file' => 'jscripts/tiny_mce/tiny_mce.js',
+      // Best practice: Document the actual version strings for later reference.
+      // 2.x: this.majorVersion="2";this.minorVersion="1.3"
+      // 3.x: majorVersion:'3',minorVersion:'2.0.1'
+      'pattern' => '@majorVersion[=:]["\'](\d).+?minorVersion[=:]["\']([\d\.]+)@',
+      'lines' => 1,
+      'cols' => 100,
+    ),
+    'versions' => array(
+      '2.1' => array(
+        'files' => array(
+          'js' => array('tiny_mce.js'),
+        ),
+        'variants' => array(
+          'source' => array(
+            'files' => array(
+              'js' => array('tiny_mce_src.js'),
+            ),
+          ),
+        ),
+        'integration files' => array(
+          'wysiwyg' => array(
+            'js' => array('editors/js/tinymce-2.js'),
+            'css' => array('editors/js/tinymce-2.css'),
+          ),
+        ),
+      ),
+      // Definition used if 3.1 or above is detected.
+      '3.1' => array(
+        // Does not support JS aggregation.
+        'files' => array(
+          'js' => array(
+            'tiny_mce.js' => array('preprocess' => FALSE),
+          ),
+        ),
+        'variants' => array(
+          // New variant leveraging jQuery. Not stable yet; therefore not the
+          // default variant.
+          'jquery' => array(
+            'files' => array(
+              'js' => array(
+                'tiny_mce_jquery.js' => array('preprocess' => FALSE),
+              ),
+            ),
+          ),
+          'source' => array(
+            'files' => array(
+              'js' => array(
+                'tiny_mce_src.js' => array('preprocess' => FALSE),
+              ),
+            ),
+          ),
+        ),
+        'integration files' => array(
+          'wysiwyg' => array(
+            'js' => array('editors/js/tinymce-3.js'),
+            'css' => array('editors/js/tinymce-3.css'),
+          ),
+        ),
+      ),
+    ),
+  );
+  return $libraries;
+}
+
+/**
+ * Alter the library information before detection and caching takes place.
+ *
+ * The library definitions are passed by reference. A common use-case is adding
+ * a module's integration files to the library array, so that the files are
+ * loaded whenever the library is. As noted above, it is important to declare
+ * integration files inside of an array, whose key is the module name.
+ *
+ * @see hook_libraries_info()
+ *
+ * @deprecated Will be removed before a stable Drupal 8 release.
+ */
+function hook_libraries_info_alter(&$libraries) {
+  $files = array(
+    'php' => array('example_module.php_spellchecker.inc'),
+  );
+  $libraries['php_spellchecker']['integration files']['example_module'] = $files;
+}
+
+/**
+ * Specify paths to look for library info files.
+ *
+ * Libraries API looks in the following directories for library info files by
+ * default:
+ * - libraries
+ * - profiles/$profile/libraries
+ * - sites/all/libraries
+ * - sites/$site/libraries
+ * This hook allows you to specify additional locations to look for library info
+ * files. This should only be used for modules that declare many libraries.
+ * Modules that only implement a few libraries should implement
+ * hook_libraries_info().
+ *
+ * @return
+ *   An array of paths.
+ *
+ * @deprecated Will be removed before a stable Drupal 8 release.
+ */
+function hook_libraries_info_file_paths() {
+  // Taken from the Libraries test module, which needs to specify the path to
+  // the test library.
+  return array(drupal_get_path('module', 'libraries_test') . '/example');
+}
diff --git a/web/modules/libraries/libraries.drush.inc b/web/modules/libraries/libraries.drush.inc
new file mode 100644
index 0000000000000000000000000000000000000000..22b7d62da06a06d4e827d689126d42c61a5e25f1
--- /dev/null
+++ b/web/modules/libraries/libraries.drush.inc
@@ -0,0 +1,169 @@
+<?php
+
+/**
+ * @file
+ * Drush integration for Libraries API.
+ */
+
+use Drupal\Component\Utility\Unicode;
+
+/**
+ * Implements hook_drush_command().
+ */
+function libraries_drush_command() {
+  $items['libraries-list'] = array(
+    'callback' => 'libraries_drush_list',
+    'description' => dt('Lists registered library information.'),
+    'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_FULL,
+  );
+  /**$items['libraries-download'] = array(
+    'callback' => 'libraries_drush_download',
+    'description' => dt('Downloads a registered library into the libraries directory for the active site.'),
+    'arguments' => array(
+      'name' => dt('The internal name of the registered library.'),
+    ),
+  );*/
+  return $items;
+}
+
+/**
+ * Implements hook_drush_help().
+ */
+function libraries_drush_help($section) {
+  switch ($section) {
+    case 'drush:libraries-list':
+      return dt('Lists registered library information.');
+
+    case 'drush:libraries-download':
+      return dt('Downloads a registered library into the libraries directory for the active site.
+
+See libraries-list for a list of registered libraries.');
+  }
+}
+
+/**
+ * Implements hook_drush_cache_clear().
+ *
+ * @see drush_cache_clear_types()
+ */
+function libraries_drush_cache_clear(&$types) {
+  $types['libraries'] = 'libraries_drush_invalidate_cache';
+}
+
+/**
+ * Clears the library cache.
+ */
+function libraries_drush_invalidate_cache() {
+  \Drupal::cache('libraries')->deleteAll();
+}
+
+/**
+ * Lists registered library information.
+ */
+function libraries_drush_list() {
+  $libraries = array();
+  foreach (libraries_info() as $name => $info) {
+    $libraries[$name] = libraries_detect($name);
+  }
+  ksort($libraries);
+
+  if (empty($libraries)) {
+    drush_print('There are no registered libraries.');
+  }
+
+  else {
+    $rows = array();
+    // drush_print_table() automatically treats the first row as the header, if
+    // $header is TRUE.
+    $rows[] = array(dt('Name'), dt('Status'), dt('Version'), dt('Variants'), dt('Dependencies'));
+    foreach ($libraries as $name => $library) {
+      $status = ($library['installed'] ? dt('OK') : Unicode::ucfirst($library['error']));
+      $version = (($library['installed'] && !empty($library['version'])) ? $library['version'] : '-');
+
+      // Only list installed variants.
+      $variants = array();
+      foreach ($library['variants'] as $variant_name => $variant) {
+        if ($variant['installed']) {
+          $variants[] = $variant_name;
+        }
+      }
+      $variants = (empty($variants) ? '-' : implode(', ', $variants));
+
+      $dependencies = (!empty($library['dependencies']) ? implode(', ', $library['dependencies']) : '-');
+
+      $rows[] = array($name, $status, $version, $variants, $dependencies);
+    }
+    // Make the possible values for the 'Status' column and the 'Version' header
+    // wrap nicely.
+    $widths = array(0, 12, 7, 0, 0);
+    drush_print_table($rows, TRUE, $widths);
+  }
+}
+
+/**
+ * Downloads a library.
+ *
+ * @param $name
+ *   The internal name of the library to download.
+ */
+function libraries_drush_download($name) {
+  return;
+
+  // @todo Looks wonky?
+  if (!drush_shell_exec('type unzip')) {
+    return drush_set_error(dt('Missing dependency: unzip. Install it before using this command.'));
+  }
+
+  // @todo Simply use current drush site.
+  $args = func_get_args();
+  if ($args[0]) {
+    $path = $args[0];
+  }
+  else {
+    $path = 'sites/all/libraries';
+  }
+
+  // Create the path if it does not exist.
+  if (!is_dir($path)) {
+    drush_op('mkdir', $path);
+    drush_log(dt('Directory @path was created', array('@path' => $path)), 'notice');
+  }
+
+  // Set the directory to the download location.
+  $olddir = getcwd();
+  chdir($path);
+
+  $filename = basename(COLORBOX_DOWNLOAD_URI);
+  $dirname = basename(COLORBOX_DOWNLOAD_URI, '.zip');
+
+  // Remove any existing Colorbox plugin directory
+  if (is_dir($dirname)) {
+    drush_log(dt('A existing Colorbox plugin was overwritten at @path', array('@path' => $path)), 'notice');
+  }
+  // Remove any existing Colorbox plugin zip archive
+  if (is_file($filename)) {
+    drush_op('unlink', $filename);
+  }
+
+  // Download the zip archive
+  if (!drush_shell_exec('wget '. COLORBOX_DOWNLOAD_URI)) {
+    drush_shell_exec('curl -O '. COLORBOX_DOWNLOAD_URI);
+  }
+
+  if (is_file($filename)) {
+    // Decompress the zip archive
+    drush_shell_exec('unzip -qq -o '. $filename);
+    // Remove the zip archive
+    drush_op('unlink', $filename);
+  }
+
+  // Set working directory back to the previous working directory.
+  chdir($olddir);
+
+  if (is_dir($path .'/'. $dirname)) {
+    drush_log(dt('Colorbox plugin has been downloaded to @path', array('@path' => $path)), 'success');
+  }
+  else {
+    drush_log(dt('Drush was unable to download the Colorbox plugin to @path', array('@path' => $path)), 'error');
+  }
+}
diff --git a/web/modules/libraries/libraries.info.yml b/web/modules/libraries/libraries.info.yml
new file mode 100644
index 0000000000000000000000000000000000000000..02136222c77638e32a78040d0087cc6853c16d30
--- /dev/null
+++ b/web/modules/libraries/libraries.info.yml
@@ -0,0 +1,10 @@
+name: Libraries
+type: module
+description: Allows version-dependent and shared usage of external libraries.
+# core: 8.x
+
+# Information added by Drupal.org packaging script on 2018-01-27
+version: '8.x-3.0-alpha1'
+core: '8.x'
+project: 'libraries'
+datestamp: 1517046488
diff --git a/web/modules/libraries/libraries.install b/web/modules/libraries/libraries.install
new file mode 100644
index 0000000000000000000000000000000000000000..a2edea4928282ff1d92f9450917fe12d6f584911
--- /dev/null
+++ b/web/modules/libraries/libraries.install
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file
+ * Containsinstall, uninstall and update functions for Libraries API.
+ */
+
+use Drupal\libraries\ExternalLibrary\Definition\FileDefinitionDiscovery;
+
+/**
+ * Implements hook_install().
+ */
+function libraries_install() {
+  if (!is_dir('public://library-definitions')) {
+    /** @var \Drupal\Core\File\FileSystemInterface $file_system */
+    $file_system = \Drupal::service('file_system');
+    $file_system->mkdir('public://library-definitions');
+  }
+}
+
+/**
+ * Implements hook_uninstall().
+ */
+function libraries_uninstall() {
+  if (is_dir('public://library-definitions')) {
+    file_unmanaged_delete_recursive('public://library-definitions');
+  }
+}
diff --git a/web/modules/libraries/libraries.module b/web/modules/libraries/libraries.module
new file mode 100644
index 0000000000000000000000000000000000000000..3006c345ceb1716bce03c98045f108430fb6a39f
--- /dev/null
+++ b/web/modules/libraries/libraries.module
@@ -0,0 +1,868 @@
+<?php
+
+/**
+ * @file
+ * External library handling for Drupal modules.
+ */
+
+use Drupal\Core\DrupalKernel;
+use Drupal\Core\Extension\ModuleHandler;
+use Drupal\libraries\ExternalLibrary\Asset\AttachableAssetLibraryRegistrationInterface;
+use Drupal\libraries\ExternalLibrary\Utility\LibraryAccessorInterface;
+use Drupal\libraries\ExternalLibrary\Utility\LibraryIdAccessorInterface;
+use Symfony\Component\Yaml\Parser;
+
+/**
+ * Implements hook_library_info_build().
+ *
+ * Register external asset libraries with Drupal core's library APIs.
+ */
+function libraries_library_info_build() {
+  /** @var \Drupal\libraries\ExternalLibrary\LibraryManagerInterface $library_manager */
+  $library_manager = \Drupal::service('libraries.manager');
+  $attachable_libraries = [];
+  $libraries_with_errors = [];
+  foreach ($library_manager->getRequiredLibraryIds() as $external_library_id) {
+    try {
+      $external_library = $library_manager->getLibrary($external_library_id);
+      $library_type = $external_library->getType();
+      if ($library_type instanceof AttachableAssetLibraryRegistrationInterface) {
+        $attachable_libraries += $library_type->getAttachableAssetLibraries($external_library, $library_manager);
+      }
+    }
+    catch (\Exception $e) {
+      // Library-specific exceptions should not be allowed to kill the rest of
+      // the build process, but should be logged.
+      if ($e instanceof LibraryIdAccessorInterface || $e instanceof LibraryAccessorInterface) {
+        $libraries_with_errors[] = $external_library_id;
+        watchdog_exception('libraries', $e);
+      }
+      else {
+        // Re-throw exceptions that are not library-specific.
+        throw $e;
+      }
+    }
+  }
+  // If we had library specific errors also log an informative message to
+  // tell admins that detection will not be run again without a cache clear.
+  if ($libraries_with_errors) {
+    \Drupal::logger('libraries')->error('The following external libraries could not successfully be registered with Drupal core: @libs. See earlier log entries for more details. Once these issues are addressed please be sure to clear your Drupal library cache to ensure external library detection is run again.', ['@libs' => implode(',', $libraries_with_errors)]);
+  }
+  return $attachable_libraries;
+}
+
+/**
+ * Gets the path of a library.
+ *
+ * @param $name
+ *   The machine name of a library to return the path for.
+ * @param $base_path
+ *   Whether to prefix the resulting path with base_path().
+ *
+ * @return
+ *   The path to the specified library or FALSE if the library wasn't found.
+ *
+ * @ingroup libraries
+ *
+ * @deprecated Will be removed before a stable Drupal 8 release. Please use the
+ * new library load and managment concepts described at:
+ * https://www.drupal.org/node/2170763
+ */
+function libraries_get_path($name, $base_path = FALSE) {
+  $libraries = &drupal_static(__FUNCTION__);
+
+  if (!isset($libraries)) {
+    $libraries = libraries_get_libraries();
+  }
+
+  $path = ($base_path ? base_path() : '');
+  if (!isset($libraries[$name])) {
+    return FALSE;
+  }
+  else {
+    $path .= $libraries[$name];
+  }
+
+  return $path;
+}
+
+/**
+ * Returns an array of library directories.
+ *
+ * Returns an array of library directories from the all-sites directory
+ * (i.e. sites/all/libraries/), the profiles directory, and site-specific
+ * directory (i.e. sites/somesite/libraries/). The returned array will be keyed
+ * by the library name. Site-specific libraries are prioritized over libraries
+ * in the default directories. That is, if a library with the same name appears
+ * in both the site-wide directory and site-specific directory, only the
+ * site-specific version will be listed.
+ *
+ * @return
+ *   A list of library directories.
+ *
+ * @ingroup libraries
+ *
+ * @deprecated Will be removed before a stable Drupal 8 release. Please use the
+ * new library load and managment concepts described at:
+ * https://www.drupal.org/node/2170763
+ */
+function libraries_get_libraries() {
+  $searchdir = array();
+  $config = DrupalKernel::findSitePath(\Drupal::request());
+
+  // @todo core/libraries
+
+  // Similar to 'modules' and 'themes' directories inside an installation
+  // profile, installation profiles may want to place libraries into a
+  // 'libraries' directory.
+  if ($profile = drupal_get_profile()) {
+    $profile_path = drupal_get_path('profile', $profile);
+    $searchdir[] = "$profile_path/libraries";
+  };
+
+  // Search sites/all/libraries for backwards-compatibility.
+  $searchdir[] = 'sites/all/libraries';
+
+  // Always search the root 'libraries' directory.
+  $searchdir[] = 'libraries';
+
+  // Also search sites/<domain>/*.
+  $searchdir[] = "$config/libraries";
+
+  // Retrieve list of directories.
+  $directories = array();
+  $nomask = array('CVS');
+  foreach ($searchdir as $dir) {
+    if (is_dir($dir) && $handle = opendir($dir)) {
+      while (FALSE !== ($file = readdir($handle))) {
+        if (!in_array($file, $nomask) && $file[0] != '.') {
+          if (is_dir("$dir/$file")) {
+            $directories[$file] = "$dir/$file";
+          }
+        }
+      }
+      closedir($handle);
+    }
+  }
+
+  return $directories;
+}
+
+/**
+ * Looks for library info files.
+ *
+ * This function scans the following directories for info files:
+ * - libraries
+ * - profiles/$profilename/libraries
+ * - sites/all/libraries
+ * - sites/$sitename/libraries
+ * - any directories specified via hook_libraries_info_file_paths()
+ *
+ * @return
+ *   An array of info files, keyed by library name. The values are the paths of
+ *   the files.
+ *
+ * @deprecated Will be removed before a stable Drupal 8 release. Please use the
+ * new library load and managment concepts described at:
+ * https://www.drupal.org/node/2170763
+ */
+function libraries_scan_info_files() {
+  $profile = drupal_get_path('profile', drupal_get_profile());
+  $config = DrupalKernel::findSitePath(\Drupal::request());
+
+  // Build a list of directories.
+  $directories = \Drupal::moduleHandler()->invokeAll('libraries_info_file_paths', $args = array());
+  $directories[] = "$profile/libraries";
+  $directories[] = 'sites/all/libraries';
+  $directories[] = 'libraries';
+  $directories[] = "$config/libraries";
+
+  // Scan for info files.
+  $files = array();
+  foreach ($directories as $dir) {
+    if (file_exists($dir)) {
+      $files = array_merge($files, file_scan_directory($dir, '@^[a-z0-9._-]+\.libraries\.info\.yml$@', array(
+        'key' => 'name',
+        'recurse' => FALSE,
+      )));
+    }
+  }
+
+  foreach ($files as $filename => $file) {
+    $files[basename($filename, '.libraries.info')] = $file;
+    unset($files[$filename]);
+  }
+
+  return $files;
+}
+
+/**
+ * Invokes library callbacks.
+ *
+ * @param $group
+ *   A string containing the group of callbacks that is to be applied. Should be
+ *   either 'info', 'pre-detect', 'post-detect', or 'load'.
+ * @param $library
+ *   An array of library information, passed by reference.
+ *
+ * @deprecated Will be removed before a stable Drupal 8 release. Please use the
+ * new library load and managment concepts described at:
+ * https://www.drupal.org/node/2170763
+ */
+function libraries_invoke($group, &$library) {
+  foreach ($library['callbacks'][$group] as $callback) {
+    libraries_traverse_library($library, $callback);
+  }
+}
+
+/**
+ * Helper function to apply a callback to all parts of a library.
+ *
+ * Because library declarations can include variants and versions, and those
+ * version declarations can in turn include variants, modifying e.g. the 'files'
+ * property everywhere it is declared can be quite cumbersome, in which case
+ * this helper function is useful.
+ *
+ * @param $library
+ *   An array of library information, passed by reference.
+ * @param $callback
+ *   A string containing the callback to apply to all parts of a library.
+ *
+ * @deprecated Will be removed before a stable Drupal 8 release. Please use the
+ * new library load and managment concepts described at:
+ * https://www.drupal.org/node/2170763
+ */
+function libraries_traverse_library(&$library, $callback) {
+  // Always apply the callback to the top-level library.
+  $callback($library, NULL, NULL);
+
+  // Apply the callback to versions.
+  if (isset($library['versions'])) {
+    foreach ($library['versions'] as $version_string => &$version) {
+      $callback($version, $version_string, NULL);
+      // Versions can include variants as well.
+      if (isset($version['variants'])) {
+        foreach ($version['variants'] as $version_variant_name => &$version_variant) {
+          $callback($version_variant, $version_string, $version_variant_name);
+        }
+      }
+    }
+  }
+
+  // Apply the callback to variants.
+  if (isset($library['variants'])) {
+    foreach ($library['variants'] as $variant_name => &$variant) {
+      $callback($variant, NULL, $variant_name);
+    }
+  }
+}
+
+/**
+ * Library info callback to make all 'files' properties consistent.
+ *
+ * This turns libraries' file information declared as e.g.
+ * @code
+ * $library['files']['js'] = array('example_1.js', 'example_2.js');
+ * @endcode
+ * into
+ * @code
+ * $library['files']['js'] = array(
+ *   'example_1.js' => array(),
+ *   'example_2.js' => array(),
+ * );
+ * @endcode
+ * It does the same for the 'integration files' property.
+ *
+ * @param $library
+ *   An associative array of library information or a part of it, passed by
+ *   reference.
+ * @param $version
+ *   If the library information belongs to a specific version, the version
+ *   string. NULL otherwise.
+ * @param $variant
+ *   If the library information belongs to a specific variant, the variant name.
+ *   NULL otherwise.
+ *
+ * @see libraries_info()
+ * @see libraries_invoke()
+ *
+ * @deprecated Will be removed before a stable Drupal 8 release. Please use the
+ * new library load and managment concepts described at:
+ * https://www.drupal.org/node/2170763
+ */
+function libraries_prepare_files(&$library, $version = NULL, $variant = NULL) {
+  // Both the 'files' property and the 'integration files' property contain file
+  // declarations, and we want to make both consistent.
+  $file_types = array();
+  if (isset($library['files'])) {
+    $file_types[] = &$library['files'];
+  }
+  if (isset($library['integration files'])) {
+    // Integration files are additionally keyed by module.
+    foreach ($library['integration files'] as &$integration_files) {
+      $file_types[] = &$integration_files;
+    }
+  }
+  foreach ($file_types as &$files) {
+    // Go through all supported types of files.
+    foreach (array('js', 'css', 'php') as $type) {
+      if (isset($files[$type])) {
+        foreach ($files[$type] as $key => $value) {
+          // Unset numeric keys and turn the respective values into keys.
+          if (is_numeric($key)) {
+            $files[$type][$value] = array();
+            unset($files[$type][$key]);
+          }
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Library post-detect callback to process and detect dependencies.
+ *
+ * It checks whether each of the dependencies of a library are installed and
+ * available in a compatible version.
+ *
+ * @param $library
+ *   An associative array of library information or a part of it, passed by
+ *   reference.
+ * @param $version
+ *   If the library information belongs to a specific version, the version
+ *   string. NULL otherwise.
+ * @param $variant
+ *   If the library information belongs to a specific variant, the variant name.
+ *   NULL otherwise.
+ *
+ * @see libraries_info()
+ * @see libraries_invoke()
+ *
+ * @deprecated Will be removed before a stable Drupal 8 release. Please use the
+ * new library load and managment concepts described at:
+ * https://www.drupal.org/node/2170763
+ */
+function libraries_detect_dependencies(&$library, $version = NULL, $variant = NULL) {
+  if (isset($library['dependencies'])) {
+    foreach ($library['dependencies'] as &$dependency_string) {
+      $dependency_info = ModuleHandler::parseDependency($dependency_string);
+      $dependency = libraries_detect($dependency_info['name']);
+      if (!$dependency['installed']) {
+        $library['installed'] = FALSE;
+        $library['error'] = 'missing dependency';
+        $library['error message'] = t('The %dependency library, which the %library library depends on, is not installed.', array(
+          '%dependency' => $dependency['name'],
+          '%library' => $library['name'],
+        ));
+      }
+      elseif (drupal_check_incompatibility($dependency_info, $dependency['version'])) {
+        $library['installed'] = FALSE;
+        $library['error'] = 'incompatible dependency';
+        $library['error message'] = t('The version %dependency_version of the %dependency library is not compatible with the %library library.', array(
+          '%dependency_version' => $dependency['version'],
+          '%dependency' => $dependency['name'],
+          '%library' => $library['name'],
+        ));
+      }
+
+      // Remove the version string from the dependency, so libraries_load() can
+      // load the libraries directly.
+      $dependency_string = $dependency_info['name'];
+    }
+  }
+}
+
+/**
+ * Returns information about registered libraries.
+ *
+ * The returned information is unprocessed; i.e., as registered by modules.
+ *
+ * @param $name
+ *   (optional) The machine name of a library to return registered information
+ *   for. If omitted, information about all registered libraries is returned.
+ *
+ * @return array|false
+ *   An associative array containing registered information for all libraries,
+ *   the registered information for the library specified by $name, or FALSE if
+ *   the library $name is not registered.
+ *
+ * @see hook_libraries_info()
+ *
+ * @todo Re-introduce support for include file plugin system - either by copying
+ *   Wysiwyg's code, or directly switching to CTools.
+ *
+ * @deprecated Will be removed before a stable Drupal 8 release. Please use the
+ * new library load and managment concepts described at:
+ * https://www.drupal.org/node/2170763
+ */
+function &libraries_info($name = NULL) {
+  // This static cache is re-used by libraries_detect() to save memory.
+  $libraries = &drupal_static(__FUNCTION__);
+
+  if (!isset($libraries)) {
+    $libraries = array();
+    // Gather information from hook_libraries_info().
+    $module_handler = \Drupal::moduleHandler();
+    foreach ($module_handler->getImplementations('libraries_info') as $module) {
+      foreach ($module_handler->invoke($module, 'libraries_info') as $machine_name => $properties) {
+        $properties['module'] = $module;
+        $libraries[$machine_name] = $properties;
+      }
+    }
+    // Gather information from hook_libraries_info() in enabled themes.
+    // @see drupal_alter()
+    global $theme, $base_theme_info;
+    if (isset($theme)) {
+      $theme_keys = array();
+      foreach ($base_theme_info as $base) {
+        $theme_keys[] = $base->name;
+      }
+      $theme_keys[] = $theme;
+      foreach ($theme_keys as $theme_key) {
+        $function = $theme_key . '_' . 'libraries_info';
+        if (function_exists($function)) {
+          foreach ($function() as $machine_name => $properties) {
+            $properties['theme'] = $theme_key;
+            $libraries[$machine_name] = $properties;
+          }
+        }
+      }
+    }
+
+    // Gather information from .info files.
+    // .info files override module definitions.
+    // In order to stop Drupal's extension and the Drupal.org packaging
+    // system from finding library info files we use the 'libraries.info.yml'
+    // file extension. Therefore, having a 'type' key, like info files of
+    // modules, themes, and profiles have, is superfluous.
+    // \Drupal\Core\Extension\InfoParser, however, enforces the existence of a
+    // 'type' key in info files. We therefore use Symfony's YAML parser
+    // directly.
+    // @todo Consider creating a dedicating InfoParser for library info files
+    //   similar to \Drupal\Core\Extension\InfoParser
+    $parser = new Parser();
+    foreach (libraries_scan_info_files() as $machine_name => $file) {
+      $properties = $parser->parse(file_get_contents($file->uri));
+      $properties['info file'] = $file->uri;
+      $libraries[$machine_name] = $properties;
+    }
+
+    // Provide defaults.
+    foreach ($libraries as $machine_name => &$properties) {
+      libraries_info_defaults($properties, $machine_name);
+    }
+
+    // Allow modules to alter the registered libraries.
+    $module_handler->alter('libraries_info', $libraries);
+
+    // Invoke callbacks in the 'info' group.
+    foreach ($libraries as &$properties) {
+      libraries_invoke('info', $properties);
+    }
+  }
+
+  if (isset($name)) {
+    if (!empty($libraries[$name])) {
+      return $libraries[$name];
+    }
+    else {
+      $false = FALSE;
+      return $false;
+    }
+  }
+  return $libraries;
+}
+
+/**
+ * Applies default properties to a library definition.
+ *
+ * @library
+ *   An array of library information, passed by reference.
+ * @name
+ *   The machine name of the passed-in library.
+ *
+ * @deprecated Will be removed before a stable Drupal 8 release. Please use the
+ * new library load and managment concepts described at:
+ * https://www.drupal.org/node/2170763
+ */
+function libraries_info_defaults(&$library, $name) {
+  $library += array(
+    'machine name' => $name,
+    'name' => $name,
+    'vendor url' => '',
+    'download url' => '',
+    'path' => '',
+    'library path' => NULL,
+    'version callback' => 'libraries_get_version',
+    'version arguments' => array(),
+    'files' => array(),
+    'dependencies' => array(),
+    'variants' => array(),
+    'versions' => array(),
+    'integration files' => array(),
+    'callbacks' => array(),
+  );
+  $library['callbacks'] += array(
+    'info' => array(),
+    'pre-detect' => array(),
+    'post-detect' => array(),
+    'pre-load' => array(),
+    'post-load' => array(),
+  );
+
+  // Add our own callbacks before any others.
+  array_unshift($library['callbacks']['info'], 'libraries_prepare_files');
+  array_unshift($library['callbacks']['post-detect'], 'libraries_detect_dependencies');
+
+  return $library;
+}
+
+/**
+ * Tries to detect a library and its installed version.
+ *
+ * @param $name
+ *   The machine name of a library to return registered information for.
+ *
+ * @return array|false
+ *   An associative array containing registered information for the library
+ *   specified by $name, or FALSE if the library $name is not registered.
+ *   In addition to the keys returned by libraries_info(), the following keys
+ *   are contained:
+ *   - installed: A boolean indicating whether the library is installed. Note
+ *     that not only the top-level library, but also each variant contains this
+ *     key.
+ *   - version: If the version could be detected, the full version string.
+ *   - error: If an error occurred during library detection, one of the
+ *     following error statuses: "not found", "not detected", "not supported".
+ *   - error message: If an error occurred during library detection, a detailed
+ *     error message.
+ *
+ * @see libraries_info()
+ *
+ * @deprecated Will be removed before a stable Drupal 8 release. Please use the
+ * new library load and managment concepts described at:
+ * https://www.drupal.org/node/2170763
+ */
+function libraries_detect($name) {
+  // Re-use the statically cached value of libraries_info() to save memory.
+  $library = &libraries_info($name);
+
+  if ($library === FALSE) {
+    return $library;
+  }
+  // If 'installed' is set, library detection ran already.
+  if (isset($library['installed'])) {
+    return $library;
+  }
+
+  $library['installed'] = FALSE;
+
+  // Check whether the library exists.
+  if (!isset($library['library path'])) {
+    $library['library path'] = libraries_get_path($library['machine name']);
+  }
+  if ($library['library path'] === FALSE || !file_exists($library['library path'])) {
+    $library['error'] = 'not found';
+    $library['error message'] = t('The %library library could not be found.', array(
+      '%library' => $library['name'],
+    ));
+    return $library;
+  }
+
+  // Invoke callbacks in the 'pre-detect' group.
+  libraries_invoke('pre-detect', $library);
+
+  // Detect library version, if not hardcoded.
+  if (!isset($library['version'])) {
+    // We support both a single parameter, which is an associative array, and an
+    // indexed array of multiple parameters.
+    if (isset($library['version arguments'][0])) {
+      // Add the library as the first argument.
+      $library['version'] = call_user_func_array($library['version callback'], array_merge(array($library), $library['version arguments']));
+    }
+    else {
+      $library['version'] = $library['version callback']($library, $library['version arguments']);
+    }
+    if (empty($library['version'])) {
+      $library['error'] = 'not detected';
+      $library['error message'] = t('The version of the %library library could not be detected.', array(
+        '%library' => $library['name'],
+      ));
+      return $library;
+    }
+  }
+
+  // Determine to which supported version the installed version maps.
+  if (!empty($library['versions'])) {
+    ksort($library['versions']);
+    $version = 0;
+    foreach ($library['versions'] as $supported_version => $version_properties) {
+      if (version_compare($library['version'], $supported_version, '>=')) {
+        $version = $supported_version;
+      }
+    }
+    if (!$version) {
+      $library['error'] = 'not supported';
+      $library['error message'] = t('The installed version %version of the %library library is not supported.', array(
+        '%version' => $library['version'],
+        '%library' => $library['name'],
+      ));
+      return $library;
+    }
+
+    // Apply version specific definitions and overrides.
+    $library = array_merge($library, $library['versions'][$version]);
+    unset($library['versions']);
+  }
+
+  // Check each variant if it is installed.
+  if (!empty($library['variants'])) {
+    foreach ($library['variants'] as $variant_name => &$variant) {
+      // If no variant callback has been set, assume the variant to be
+      // installed.
+      if (!isset($variant['variant callback'])) {
+        $variant['installed'] = TRUE;
+      }
+      else {
+        // We support both a single parameter, which is an associative array,
+        // and an indexed array of multiple parameters.
+        if (isset($variant['variant arguments'][0])) {
+          // Add the library as the first argument, and the variant name as the second.
+          $variant['installed'] = call_user_func_array($variant['variant callback'], array_merge(array($library, $variant_name), $variant['variant arguments']));
+        }
+        else {
+          $variant['installed'] = $variant['variant callback']($library, $variant_name, $variant['variant arguments']);
+        }
+        if (!$variant['installed']) {
+          $variant['error'] = 'not found';
+          $variant['error message'] = t('The %variant variant of the %library library could not be found.', array(
+            '%variant' => $variant_name,
+            '%library' => $library['name'],
+          ));
+        }
+      }
+    }
+  }
+
+  // If we end up here, the library should be usable.
+  $library['installed'] = TRUE;
+
+  // Invoke callbacks in the 'post-detect' group.
+  libraries_invoke('post-detect', $library);
+
+  return $library;
+}
+
+/**
+ * Loads a library.
+ *
+ * @param $name
+ *   The name of the library to load.
+ * @param $variant
+ *   The name of the variant to load. Note that only one variant of a library
+ *   can be loaded within a single request. The variant that has been passed
+ *   first is used; different variant names in subsequent calls are ignored.
+ *
+ * @return
+ *   An associative array of the library information as returned from
+ *   libraries_info(). The top-level properties contain the effective definition
+ *   of the library (variant) that has been loaded. Additionally:
+ *   - installed: Whether the library is installed, as determined by
+ *     libraries_detect_library().
+ *   - loaded: Either the amount of library files that have been loaded, or
+ *     FALSE if the library could not be loaded.
+ *   See hook_libraries_info() for more information.
+ *
+ * @deprecated Will be removed before a stable Drupal 8 release. Please use the
+ * new library load and managment concepts described at:
+ * https://www.drupal.org/node/2170763
+ */
+function libraries_load($name, $variant = NULL) {
+  $loaded = &drupal_static(__FUNCTION__, array());
+
+  if (!isset($loaded[$name])) {
+    $library = \Drupal::cache('libraries')->get($name);
+    if ($library) {
+      $library = $library->data;
+    }
+    else {
+      $library = libraries_detect($name);
+      \Drupal::cache('libraries')->set($name, $library);
+    }
+    // If a variant was specified, override the top-level properties with the
+    // variant properties.
+    if (isset($variant)) {
+      // Ensure that the $variant key exists, and if it does not, set its
+      // 'installed' property to FALSE by default. This will prevent the loading
+      // of the library files below.
+      $library['variants'] += array($variant => array('installed' => FALSE));
+      $library = array_merge($library, $library['variants'][$variant]);
+    }
+    // Regardless of whether a specific variant was requested or not, there can
+    // only be one variant of a library within a single request.
+    unset($library['variants']);
+
+    // If the library (variant) is installed, load it.
+    $library['loaded'] = FALSE;
+    if ($library['installed']) {
+      // Load library dependencies.
+      if (isset($library['dependencies'])) {
+        foreach ($library['dependencies'] as $dependency) {
+          libraries_load($dependency);
+        }
+      }
+
+      // Invoke callbacks in the 'pre-load' group.
+      libraries_invoke('pre-load', $library);
+
+      // Load all the files associated with the library.
+      $library['loaded'] = libraries_load_files($library);
+
+      // Invoke callbacks in the 'post-load' group.
+      libraries_invoke('post-load', $library);
+    }
+    $loaded[$name] = $library;
+  }
+
+  return $loaded[$name];
+}
+
+/**
+ * Loads a library's files.
+ *
+ * @param $library
+ *   An array of library information as returned by libraries_info().
+ *
+ * @return
+ *   The number of loaded files.
+ *
+ * @deprecated Will be removed before a stable Drupal 8 release. Please use the
+ * new library load and managment concepts described at:
+ * https://www.drupal.org/node/2170763
+ */
+function libraries_load_files($library) {
+
+  // Construct the full path to the library for later use.
+  $path = $library['library path'];
+  $path = ($library['path'] !== '' ? $path . '/' . $library['path'] : $path);
+
+  // Count the number of loaded files for the return value.
+  $count = 0;
+
+  // Load both the JavaScript and the CSS files.
+  // The parameters for drupal_add_js() and drupal_add_css() require special
+  // handling.
+  // @see drupal_process_attached()
+  foreach (array('js', 'css') as $type) {
+    // Given the removal of core functions like _drupal_add_js and
+    // _drupal_add_css the logic below cannot safely be run anymore.
+    // @see https://www.drupal.org/node/2702563
+    break;
+    if (!empty($library['files'][$type])) {
+      foreach ($library['files'][$type] as $data => $options) {
+        // If the value is not an array, it's a filename and passed as first
+        // (and only) argument.
+        if (!is_array($options)) {
+          $data = $options;
+          $options = array();
+        }
+        // In some cases, the first parameter ($data) is an array. Arrays can't
+        // be passed as keys in PHP, so we have to get $data from the value
+        // array.
+        if (is_numeric($data)) {
+          $data = $options['data'];
+          unset($options['data']);
+        }
+        // Prepend the library path to the file name.
+        $data = "$path/$data";
+        // Apply the default group if the group isn't explicitly given.
+        if (!isset($options['group'])) {
+          $options['group'] = ($type == 'js') ? JS_DEFAULT : CSS_AGGREGATE_DEFAULT;
+        }
+        if ($type === 'js') {
+          $options['version'] = -1;
+        }
+        // @todo Avoid the usage of _drupal_add_js() and _drupal_add_css()
+        call_user_func('_drupal_add_' . $type, $data, $options);
+        $count++;
+      }
+    }
+  }
+
+  // Load PHP files.
+  if (!empty($library['files']['php'])) {
+    foreach ($library['files']['php'] as $file => $array) {
+      $file_path = DRUPAL_ROOT . '/' . $path . '/' . $file;
+      if (file_exists($file_path)) {
+        require_once $file_path;
+        $count++;
+      }
+    }
+  }
+
+  // Load integration files.
+  if (!empty($library['integration files'])) {
+    foreach ($library['integration files'] as $module => $files) {
+      libraries_load_files(array(
+        'files' => $files,
+        'path' => '',
+        'library path' => drupal_get_path('module', $module),
+      ));
+    }
+  }
+
+  return $count;
+}
+
+/**
+ * Gets the version information from an arbitrary library.
+ *
+ * @param $library
+ *   An associative array containing all information about the library.
+ * @param $options
+ *   An associative array containing with the following keys:
+ *   - file: The filename to parse for the version, relative to the library
+ *     path. For example: 'docs/changelog.txt'.
+ *   - pattern: A string containing a regular expression (PCRE) to match the
+ *     library version. For example: '@version\s+([0-9a-zA-Z\.-]+)@'. Note that
+ *     the returned version is not the match of the entire pattern (i.e.
+ *     '@version 1.2.3' in the above example) but the match of the first
+ *     sub-pattern (i.e. '1.2.3' in the above example).
+ *   - lines: (optional) The maximum number of lines to search the pattern in.
+ *     Defaults to 20.
+ *   - cols: (optional) The maximum number of characters per line to take into
+ *     account. Defaults to 200. In case of minified or compressed files, this
+ *     prevents reading the entire file into memory.
+ *
+ * @return
+ *   A string containing the version of the library.
+ *
+ * @see libraries_get_path()
+ *
+ * @deprecated Will be removed before a stable Drupal 8 release. Please use the
+ * new library load and managment concepts described at:
+ * https://www.drupal.org/node/2170763
+ */
+function libraries_get_version($library, $options) {
+  // Provide defaults.
+  $options += array(
+    'file' => '',
+    'pattern' => '',
+    'lines' => 20,
+    'cols' => 200,
+  );
+
+  $file = DRUPAL_ROOT . '/' . $library['library path'] . '/' . $options['file'];
+  if (empty($options['file']) || !file_exists($file)) {
+    return;
+  }
+  $file = fopen($file, 'r');
+  while ($options['lines'] && $line = fgets($file, $options['cols'])) {
+    if (preg_match($options['pattern'], $line, $version)) {
+      fclose($file);
+      return $version[1];
+    }
+    $options['lines']--;
+  }
+  fclose($file);
+}
diff --git a/web/modules/libraries/libraries.services.yml b/web/modules/libraries/libraries.services.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b2880ad5724639f929802a12a69d90ca7818599f
--- /dev/null
+++ b/web/modules/libraries/libraries.services.yml
@@ -0,0 +1,73 @@
+services:
+  libraries.manager:
+    class: Drupal\libraries\ExternalLibrary\LibraryManager
+    arguments:
+      - '@libraries.definition.discovery'
+      - '@plugin.manager.libraries.library_type'
+
+  # By default Libraries API downloads library definitions from a number of
+  # remote library registries, the canonical one being
+  # https://www.drupal.org/project/libraries_registry, and stores them locally
+  # in the public://library-definitions directory. The URLs of the remote
+  # library registries and the local base path can be configured. The remote
+  # fetching can also be disabled altogether.
+  libraries.definition.discovery:
+    class: Drupal\libraries\ExternalLibrary\Definition\DefinitionDiscoveryInterface
+    factory: 'libraries.definition.discovery.factory:get'
+  libraries.definition.discovery.factory:
+    class: Drupal\libraries\ExternalLibrary\Definition\DefinitionDiscoveryFactory
+    arguments:
+      - '@config.factory'
+      - '@serialization.json'
+      - '@http_client'
+      - '@serialization.json'
+  # If you instead want to check your library definitions into version control
+  # and use YAML for them instead of JSON, you can place the following service
+  # definition in your site's services.yml file:
+  # libraries.definition.discovery:
+  #   class: Drupal\libraries\ExternalLibrary\Definition\FileDefinitionDiscovery
+  #   arguments:
+  #     - '@serialization.yaml'
+  #     # Replace this with the location of the library definitions in your setup.
+  #     - '../library-definitions'
+
+  plugin.manager.libraries.library_type:
+    class: Drupal\libraries\ExternalLibrary\Type\LibraryTypeFactory
+    parent: default_plugin_manager
+  plugin.manager.libraries.locator:
+    class: Drupal\libraries\ExternalLibrary\Local\LocatorManager
+    parent: default_plugin_manager
+  plugin.manager.libraries.version_detector:
+    class: Drupal\libraries\ExternalLibrary\Version\VersionDetectorManager
+    parent: default_plugin_manager
+
+  libraries.config_subscriber:
+    class: Drupal\libraries\Config\LibrariesConfigSubscriber
+    arguments: ['@service_container']
+    tags:
+      - { name: event_subscriber }
+
+  libraries.php_file_loader:
+    class: Drupal\libraries\ExternalLibrary\PhpFile\PhpRequireLoader
+
+  stream_wrapper.library_definitions:
+    class: Drupal\libraries\StreamWrapper\LibraryDefinitionsStream
+    arguments: ['@config.factory']
+    tags:
+      - { name: stream_wrapper, scheme: 'library-definitions' }
+  stream_wrapper.asset_libraries:
+    class: Drupal\libraries\StreamWrapper\AssetLibrariesStream
+    tags:
+      - { name: stream_wrapper, scheme: 'asset' }
+  stream_wrapper.php_file_libraries:
+    class: Drupal\libraries\StreamWrapper\PhpFileLibrariesStream
+    tags:
+      - { name: stream_wrapper, scheme: 'php-file' }
+
+
+  cache.libraries:
+    class: Drupal\Core\Cache\CacheBackendInterface
+    tags:
+      - { name: cache.bin }
+    factory: cache_factory:get
+    arguments: [library]
diff --git a/web/modules/libraries/src/Annotation/LibraryType.php b/web/modules/libraries/src/Annotation/LibraryType.php
new file mode 100644
index 0000000000000000000000000000000000000000..3cf5491165e6eddf763632ea2522e458f962196f
--- /dev/null
+++ b/web/modules/libraries/src/Annotation/LibraryType.php
@@ -0,0 +1,14 @@
+<?php
+
+namespace Drupal\libraries\Annotation;
+
+use Drupal\Component\Annotation\PluginID;
+
+/**
+ * Provides an annotation class for locator plugins.
+ *
+ * @Annotation
+ */
+class LibraryType extends PluginID {
+
+}
diff --git a/web/modules/libraries/src/Annotation/Locator.php b/web/modules/libraries/src/Annotation/Locator.php
new file mode 100644
index 0000000000000000000000000000000000000000..ab4311d156da8712ab7a73aa44500633356218ac
--- /dev/null
+++ b/web/modules/libraries/src/Annotation/Locator.php
@@ -0,0 +1,14 @@
+<?php
+
+namespace Drupal\libraries\Annotation;
+
+use Drupal\Component\Annotation\PluginID;
+
+/**
+ * Provides an annotation class for locator plugins.
+ *
+ * @Annotation
+ */
+class Locator extends PluginID {
+
+}
diff --git a/web/modules/libraries/src/Annotation/VersionDetector.php b/web/modules/libraries/src/Annotation/VersionDetector.php
new file mode 100644
index 0000000000000000000000000000000000000000..a664f994f2b9a46b1a20d44026a7ed92f8e94160
--- /dev/null
+++ b/web/modules/libraries/src/Annotation/VersionDetector.php
@@ -0,0 +1,14 @@
+<?php
+
+namespace Drupal\libraries\Annotation;
+
+use Drupal\Component\Annotation\PluginID;
+
+/**
+ * Provides an annotation class for version detector plugins.
+ *
+ * @Annotation
+ */
+class VersionDetector extends PluginID {
+
+}
diff --git a/web/modules/libraries/src/Config/LibrariesConfigSubscriber.php b/web/modules/libraries/src/Config/LibrariesConfigSubscriber.php
new file mode 100644
index 0000000000000000000000000000000000000000..6578f42c6d6eaae190575fb48d42832ad92f28bf
--- /dev/null
+++ b/web/modules/libraries/src/Config/LibrariesConfigSubscriber.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace Drupal\libraries\Config;
+
+use Drupal\Core\Config\ConfigCrudEvent;
+use Drupal\Core\Config\ConfigEvents;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Reacts to configuration changes of the 'libraries.settings' configuration.
+ */
+class LibrariesConfigSubscriber implements EventSubscriberInterface {
+
+  /**
+   * The service container.
+   *
+   * @var \Symfony\Component\DependencyInjection\ContainerInterface
+   */
+  protected $container;
+
+  /**
+   * Constructs a Libraries API configuration subscriber.
+   *
+   * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
+   *   The service container.
+   */
+  public function __construct(ContainerInterface $container) {
+    $this->container = $container;
+  }
+
+  /**
+   * Unsets the definition discovery service when its configuration changes.
+   *
+   * @param \Drupal\Core\Config\ConfigCrudEvent $event
+   *   The configuration event.
+   */
+  public function onConfigSave(ConfigCrudEvent $event) {
+    if (($event->getConfig()->getName() === 'libraries.settings') && $event->isChanged('definition')) {
+      $this->container->set('libraries.definition.discovery', NULL);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents() {
+    return [ConfigEvents::SAVE => 'onConfigSave'];
+  }
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Asset/AssetLibrary.php b/web/modules/libraries/src/ExternalLibrary/Asset/AssetLibrary.php
new file mode 100644
index 0000000000000000000000000000000000000000..50a31868682d824ee0f3cdcc7fa35d0b3f963a8d
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Asset/AssetLibrary.php
@@ -0,0 +1,128 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Asset;
+
+use Drupal\Component\Plugin\Factory\FactoryInterface;
+use Drupal\libraries\ExternalLibrary\Exception\LibraryNotInstalledException;
+use Drupal\libraries\ExternalLibrary\LibraryBase;
+use Drupal\libraries\ExternalLibrary\LibraryManagerInterface;
+use Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface;
+use Drupal\libraries\ExternalLibrary\Local\LocalLibraryTrait;
+use Drupal\libraries\ExternalLibrary\Remote\RemoteLibraryInterface;
+use Drupal\libraries\ExternalLibrary\Remote\RemoteLibraryTrait;
+use Drupal\libraries\ExternalLibrary\Type\LibraryTypeInterface;
+
+/**
+ * Provides a class for a single attachable asset library.
+ */
+class AssetLibrary extends LibraryBase implements
+  AssetLibraryInterface,
+  LocalLibraryInterface,
+  RemoteLibraryInterface
+{
+
+  use
+    LocalLibraryTrait,
+    RemoteLibraryTrait,
+    LocalRemoteAssetTrait
+  ;
+
+  /**
+   * An array containing the CSS assets of the library.
+   *
+   * @var array
+   */
+  protected $cssAssets = [];
+
+  /**
+   * An array containing the JavaScript assets of the library.
+   *
+   * @var array
+   */
+  protected $jsAssets = [];
+
+  /**
+   * An array of attachable asset library IDs that this library depends on.
+   *
+   * @todo Explain the difference to regular dependencies.
+   */
+  protected $attachableDependencies = [];
+
+  /**
+   * Construct an external library.
+   *
+   * @param string $id
+   *   The library ID.
+   * @param array $definition
+   *   The library definition array.
+   * @param \Drupal\libraries\ExternalLibrary\Type\LibraryTypeInterface $library_type
+   *   The library type of the library.
+   */
+  public function __construct($id, array $definition, LibraryTypeInterface $library_type) {
+    parent::__construct($id, $definition, $library_type);
+    $this->remoteUrl = $definition['remote_url'];
+    $this->cssAssets = $definition['css'];
+    $this->jsAssets = $definition['js'];
+    $this->attachableDependencies = $definition['attachable_dependencies'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static function processDefinition(array &$definition) {
+    parent::processDefinition($definition);
+    $definition += [
+      'remote_url' => '',
+      'css' => [],
+      'js' => [],
+      'attachable_dependencies' => [],
+    ];
+  }
+
+  /**
+   * Returns a core library array structure for this library.
+   *
+   * @param \Drupal\libraries\ExternalLibrary\LibraryManagerInterface $library_manager
+   *   The library manager that can be used to fetch dependencies.
+   *
+   * @return array
+   *
+   * @see \Drupal\libraries\ExternalLibrary\Asset\getAttachableAssetLibraries::getAttachableAssetLibraries()
+   *
+   * @throws \Drupal\libraries\ExternalLibrary\Exception\InvalidLibraryDependencyException
+   * @throws \Drupal\libraries\ExternalLibrary\Exception\LibraryDefinitionNotFoundException
+   * @throws \Drupal\libraries\ExternalLibrary\Exception\LibraryTypeNotFoundException
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   *
+   * @todo Document the return value.
+   */
+  public function getAttachableAssetLibrary(LibraryManagerInterface $library_manager) {
+    if (!$this->canBeAttached()) {
+      throw new LibraryNotInstalledException($this);
+    }
+    return [
+      'version' => $this->getVersion(),
+      'css' => $this->processCssAssets($this->cssAssets),
+      'js' => $this->processJsAssets($this->jsAssets),
+      'dependencies' => $this->attachableDependencies,
+    ];
+  }
+
+  /**
+   * Gets the locator of this library using the locator factory.
+   *
+   * @param \Drupal\Component\Plugin\Factory\FactoryInterface $locator_factory
+   *
+   * @return \Drupal\libraries\ExternalLibrary\Local\LocatorInterface
+   *
+   * @see \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface::getLocator()
+   */
+  public function getLocator(FactoryInterface $locator_factory) {
+    // @todo Consider consolidating the stream wrappers used here. For now we
+    // allow asset libs to live almost anywhere.
+    return $locator_factory->createInstance('chain')
+      ->addLocator($locator_factory->createInstance('uri', ['uri' => 'asset://']))
+      ->addLocator($locator_factory->createInstance('uri', ['uri' => 'php-file://']));
+  }
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Asset/AssetLibraryInterface.php b/web/modules/libraries/src/ExternalLibrary/Asset/AssetLibraryInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..eac629024d5ce2e3928d1af45531e2263a7d1827
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Asset/AssetLibraryInterface.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Asset;
+
+use Drupal\libraries\ExternalLibrary\Dependency\DependentLibraryInterface;
+use Drupal\libraries\ExternalLibrary\LibraryInterface;
+use Drupal\libraries\ExternalLibrary\LibraryManagerInterface;
+use Drupal\libraries\ExternalLibrary\Version\VersionedLibraryInterface;
+
+/**
+ * Provides an interface for external asset libraries with a single library.
+ *
+ * Asset is the generic term for CSS and JavaScript files.
+ *
+ * In order to load assets of external libraries as part of a page request the
+ * assets must be registered with Drupal core's library system. Therefore,
+ * Libraries API makes all libraries that are required by the installed
+ * installation profile, modules, and themes available as core asset libraries
+ * with the identifier 'libraries/[machine_name]' where '[machine_name]' is
+ * the Libraries API machine name of the external library.
+ *
+ * Thus, assuming that the external library 'flexslider' has been declared as a
+ * dependency, for example, it can be attached to a render array in the $build
+ * variable with the following code:
+ * @code
+ *   $build['#attached']['library'] = ['libraries/flexslider'];
+ * @endcode
+ *
+ * In some cases an external library may contain multiple components, that
+ * should be loadable independently from each other. In this case, implement
+ * MultipleAssetLibraryInterface instead.
+ *
+ * @see libraries_library_info_build()
+ * @see \Drupal\libraries\ExternalLibrary\Asset\AssetLibraryTrait
+ * @see \Drupal\libraries\ExternalLibrary\Asset\MultipleAssetLibraryInterface
+ *
+ * @todo Support loading of source or minified assets.
+ * @todo Document how library dependencies work.
+ *
+ * @ingroup libraries
+ */
+interface AssetLibraryInterface extends
+  LibraryInterface,
+  VersionedLibraryInterface,
+  DependentLibraryInterface
+{
+
+  /**
+   * Returns a core asset library array structure for this library.
+   *
+   * @param \Drupal\libraries\ExternalLibrary\LibraryManagerInterface $library_manager
+   *   The library manager that can be used to fetch dependencies.
+   *
+   * @return array
+   *
+   * @see \Drupal\libraries\ExternalLibrary\Asset\SingleAssetLibraryTrait
+   *
+   * @throws \Drupal\libraries\ExternalLibrary\Exception\InvalidLibraryDependencyException
+   *
+   * @todo Document the return value.
+   * @todo Reconsider passing the library manager.
+   */
+  public function getAttachableAssetLibrary(LibraryManagerInterface $library_manager);
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Asset/AttachableAssetLibraryRegistrationInterface.php b/web/modules/libraries/src/ExternalLibrary/Asset/AttachableAssetLibraryRegistrationInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..8a8a6e5cfb1496dce64fa7d1f6c43069cad13e41
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Asset/AttachableAssetLibraryRegistrationInterface.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Asset;
+
+use Drupal\libraries\ExternalLibrary\LibraryInterface;
+use Drupal\libraries\ExternalLibrary\LibraryManagerInterface;
+
+/**
+ * An interface for library types that want to react to library instantiation.
+ */
+interface AttachableAssetLibraryRegistrationInterface {
+
+  /**
+   * Reacts to the instantiation of a library.
+   *
+   * @param \Drupal\libraries\ExternalLibrary\LibraryInterface $external_library
+   *   The library that is being instantiated.
+   * @param \Drupal\libraries\ExternalLibrary\LibraryManagerInterface $library_manager
+   */
+  public function getAttachableAssetLibraries(LibraryInterface $external_library, LibraryManagerInterface $library_manager);
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Asset/LocalRemoteAssetTrait.php b/web/modules/libraries/src/ExternalLibrary/Asset/LocalRemoteAssetTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..e673caba975e86faf32d2d7249d77d3dfb5d1cc7
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Asset/LocalRemoteAssetTrait.php
@@ -0,0 +1,108 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Asset;
+
+/**
+ * A trait for asset libraries that serve local and remote files.
+ *
+ * If the library files are available locally, they are served locally.
+ * Otherwise, the remote files are served, assuming a remote URL is specified.
+ *
+ * This trait should only be used in classes implementing LocalLibraryInterface
+ * and RemoteLibraryInterface.
+ *
+ * @see \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface
+ * @see \Drupal\libraries\ExternalLibrary\Remote\RemoteLibraryInterface
+ */
+trait LocalRemoteAssetTrait {
+
+  /**
+   * Checks whether this library can be attached.
+   *
+   * @return bool
+   *   TRUE if the library can be attached; FALSE otherwise.
+   *
+   * @see \Drupal\libraries\ExternalLibrary\Asset\SingleAssetLibraryTrait::canBeAttached()
+   */
+  protected function canBeAttached() {
+    /** @var \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface|\Drupal\libraries\ExternalLibrary\Remote\RemoteLibraryInterface $this */
+    return ($this->isInstalled() || $this->hasRemoteUrl());
+  }
+
+  /**
+   * Gets the prefix to prepend to file paths.
+   *
+   * For local libraries this is the library path, for remote libraries this is
+   * the remote URL.
+   *
+   * @return string
+   *   The path prefix.
+   */
+  protected function getPathPrefix() {
+    /** @var \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface|\Drupal\libraries\ExternalLibrary\Remote\RemoteLibraryInterface $this */
+    if ($this->isInstalled()) {
+      // LocalLibraryInterface::getLocalPath() returns the path relative to the
+      // app root. In order for the core core asset system to register the path
+      // as relative to the app root, a leading slash is required.
+      /** @see \Drupal\Core\Asset\LibraryDiscoveryParser::buildByExtension() */
+      return '/' . $this->getLocalPath();
+    }
+    elseif ($this->hasRemoteUrl()) {
+      return $this->getRemoteUrl();
+    }
+    else {
+      // @todo Throw an exception.
+    }
+  }
+
+  /**
+   * Gets the CSS assets attached to this library.
+   *
+   * @param array $assets
+   *
+   * @return array
+   *   An array of CSS assets of the library following the core library CSS
+   *   structure. The keys of the array must be among the SMACSS categories
+   *   'base', 'layout, 'component', 'state', and 'theme'. The value of each
+   *   category is in turn an array where the keys are the file paths of the CSS
+   *   files and values are CSS options.
+   *
+   * @see https://smacss.com/
+   *
+   * @see \Drupal\libraries\ExternalLibrary\Asset\SingleAssetLibraryTrait::getCssAssets()
+   */
+  protected function processCssAssets(array $assets) {
+    // @todo Consider somehow caching the processed information.
+    $processed_assets = [];
+    foreach ($assets as $category => $category_assets) {
+      // @todo Somehow consolidate this with getJsAssets().
+      foreach ($category_assets as $filename => $options) {
+        $processed_assets[$category][$this->getPathPrefix() . '/' . $filename] = $options;
+      }
+    }
+    return $processed_assets;
+  }
+
+  /**
+   * Gets the JavaScript assets attached to this library.
+   *
+   * @param array $assets
+   *
+   * @return array
+   *   An array of JavaScript assets of the library. The keys of the array are
+   *   the file paths of the JavaScript files and the values are JavaScript
+   *   options.
+   *
+   * @see \Drupal\libraries\ExternalLibrary\Asset\SingleAssetLibraryTrait::getJsAssets()
+   */
+  protected function processJsAssets(array $assets) {
+    // @todo Consider somehow caching the processed information.
+    $processed_assets = [];
+    // @todo Somehow consolidate this with getCssAssets().
+    foreach ($assets as $filename => $options) {
+      $processed_assets[$this->getPathPrefix() . '/' . $filename] = $options;
+    }
+    return $processed_assets;
+  }
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Asset/MultipleAssetLibrary.php b/web/modules/libraries/src/ExternalLibrary/Asset/MultipleAssetLibrary.php
new file mode 100644
index 0000000000000000000000000000000000000000..f2c3d3c46c23d1b5d25acb405d6d1096ea2392f0
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Asset/MultipleAssetLibrary.php
@@ -0,0 +1,123 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Asset;
+
+use Drupal\Component\Plugin\Factory\FactoryInterface;
+use Drupal\libraries\ExternalLibrary\Dependency\DependentLibraryInterface;
+use Drupal\libraries\ExternalLibrary\Exception\LibraryNotInstalledException;
+use Drupal\libraries\ExternalLibrary\LibraryBase;
+use Drupal\libraries\ExternalLibrary\LibraryManagerInterface;
+use Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface;
+use Drupal\libraries\ExternalLibrary\Local\LocalLibraryTrait;
+use Drupal\libraries\ExternalLibrary\Remote\RemoteLibraryInterface;
+use Drupal\libraries\ExternalLibrary\Remote\RemoteLibraryTrait;
+use Drupal\libraries\ExternalLibrary\Type\LibraryTypeInterface;
+use Drupal\libraries\ExternalLibrary\Version\VersionedLibraryInterface;
+
+/**
+ * Provides a class for a library with multiple attachable asset libraries.
+ */
+class MultipleAssetLibrary extends LibraryBase implements
+  MultipleAssetLibraryInterface,
+  VersionedLibraryInterface,
+  DependentLibraryInterface,
+  LocalLibraryInterface,
+  RemoteLibraryInterface
+{
+
+  use
+    LocalLibraryTrait,
+    RemoteLibraryTrait,
+    LocalRemoteAssetTrait
+  ;
+
+  /**
+   * An array of attachable asset libraries.
+   */
+  protected $libraries = [];
+
+  /**
+   * Construct an external library.
+   *
+   * @param string $id
+   *   The library ID.
+   * @param array $definition
+   *   The library definition array.
+   * @param \Drupal\libraries\ExternalLibrary\Type\LibraryTypeInterface $library_type
+   *   The library type of the library.
+   */
+  public function __construct($id, array $definition, LibraryTypeInterface $library_type) {
+    parent::__construct($id, $definition, $library_type);
+    $this->remoteUrl = $definition['remote_url'];
+    $this->libraries = $definition['libraries'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static function processDefinition(array &$definition) {
+    parent::processDefinition($definition);
+    $definition += [
+      'remote_url' => '',
+      'libraries' => [],
+    ];
+    foreach ($definition['libraries'] as &$library) {
+      $library += [
+        'css' => [],
+        'js' => [],
+        'dependencies' => [],
+      ];
+    }
+  }
+
+  /**
+   * Returns a core library array structure for this library.
+   *
+   * @param \Drupal\libraries\ExternalLibrary\LibraryManagerInterface $library_manager
+   *   The library manager that can be used to fetch dependencies.
+   *
+   * @return array
+   *
+   * @see \Drupal\libraries\ExternalLibrary\Asset\getAttachableAssetLibraries::getAttachableAssetLibraries()
+   *
+   * @throws \Drupal\libraries\ExternalLibrary\Exception\InvalidLibraryDependencyException
+   * @throws \Drupal\libraries\ExternalLibrary\Exception\LibraryDefinitionNotFoundException
+   * @throws \Drupal\libraries\ExternalLibrary\Exception\LibraryTypeNotFoundException
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   *
+   * @todo Document the return value.
+   */
+  public function getAttachableAssetLibraries(LibraryManagerInterface $library_manager) {
+    if (!$this->canBeAttached()) {
+      throw new LibraryNotInstalledException($this);
+    }
+    $attachable_libraries = [];
+    foreach ($this->libraries as $attachable_library_id => $attachable_library) {
+      $attachable_libraries[$attachable_library_id] = [
+        'version' => $this->getVersion(),
+        'css' => $this->processCssAssets($attachable_library['css']),
+        'js' => $this->processJsAssets($attachable_library['js']),
+        'dependencies' => $attachable_library['dependencies'],
+      ];
+    }
+    return $attachable_libraries;
+  }
+
+  /**
+   * Gets the locator of this library using the locator factory.
+   *
+   * @param \Drupal\Component\Plugin\Factory\FactoryInterface $locator_factory
+   *
+   * @return \Drupal\libraries\ExternalLibrary\Local\LocatorInterface
+   *
+   * @see \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface::getLocator()
+   */
+  public function getLocator(FactoryInterface $locator_factory) {
+    // @todo Consider consolidating the stream wrappers used here. For now we
+    // allow asset libs to live almost anywhere.
+    return $locator_factory->createInstance('chain')
+      ->addLocator($locator_factory->createInstance('uri', ['uri' => 'asset://']))
+      ->addLocator($locator_factory->createInstance('uri', ['uri' => 'php-file://']));
+  }
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Asset/MultipleAssetLibraryInterface.php b/web/modules/libraries/src/ExternalLibrary/Asset/MultipleAssetLibraryInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..5dc43ffdc700714fad11a149a0e69aac727411a3
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Asset/MultipleAssetLibraryInterface.php
@@ -0,0 +1,64 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Asset;
+
+use Drupal\libraries\ExternalLibrary\LibraryInterface;
+use Drupal\libraries\ExternalLibrary\LibraryManagerInterface;
+
+/**
+ * Provides an interface for external asset libraries with multiple libraries.
+ *
+ * See SingleAssetLibraryInterface for more information on external asset
+ * libraries in general.
+ *
+ * In case an external asset library contains multiple components that should
+ * be loadable independently from each other, Libraries API registers each
+ * library component as a separate library in the core asset library system. The
+ * resulting core library identifier is
+ * 'libraries/[machine_name].[component_name]' where '[machine_name]' is the
+ * Libraries API machine name of the external library and '[component_name]' is
+ * the component name specified by the library definition.
+ *
+ * Thus, assuming that the external library 'bootstrap' has been declared as a
+ * dependency, for example, and it has 'button' and 'form' components, they can
+ * be attached to a render array in the $build variable with the following code:
+ * @code
+ *   $build['#attached']['library'] = [
+ *     'libraries/bootstrap.button',
+ *     'libraries/bootstrap.form',
+ *   ];
+ * @endcode
+ *
+ * @see \Drupal\libraries\ExternalLibrary\Asset\AssetLibraryInterface
+ *
+ * @todo Support loading of source or minified assets.
+ * @todo Document how library dependencies work.
+ */
+interface MultipleAssetLibraryInterface extends LibraryInterface {
+
+  /**
+   * Separates the library machine name from its component name.
+   *
+   * The period is chosen in alignment with core asset libraries, which are
+   * named, for example, 'core/jquery.once'.
+   */
+  const SEPARATOR = '.';
+
+  /**
+   * Returns a core asset library array structure for this library.
+   *
+   * @param \Drupal\libraries\ExternalLibrary\LibraryManagerInterface $library_manager
+   *   The library manager that can be used to fetch dependencies.
+   *
+   * @return array
+   *
+   * @see \Drupal\libraries\ExternalLibrary\Asset\SingleAssetLibraryTrait
+   *
+   * @throws \Drupal\libraries\ExternalLibrary\Exception\InvalidLibraryDependencyException
+   *
+   * @todo Document the return value.
+   * @todo Reconsider passing the library manager.
+   */
+  public function getAttachableAssetLibraries(LibraryManagerInterface $library_manager);
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Definition/ChainDefinitionDiscovery.php b/web/modules/libraries/src/ExternalLibrary/Definition/ChainDefinitionDiscovery.php
new file mode 100644
index 0000000000000000000000000000000000000000..f58801816d39fafd7e172cfa368b8ec1f576eab7
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Definition/ChainDefinitionDiscovery.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Definition;
+
+use Drupal\libraries\ExternalLibrary\Exception\LibraryDefinitionNotFoundException;
+
+/**
+ * Provides a definition discovery that checks a list of other discoveries.
+ *
+ * The discoveries are checked sequentially. If the definition was not present
+ * in some discoveries but is found in a later discovery the definition will be
+ * written to the earlier discoveries if they implement
+ * WritableDefinitionDiscoveryInterface.
+ *
+ * @see \Drupal\libraries\ExternalLibrary\Definition\WritableDefinitionDiscoveryInterface
+ */
+class ChainDefinitionDiscovery implements DefinitionDiscoveryInterface {
+
+  /**
+   * The list of definition discoveries that will be checked.
+   *
+   * @var \Drupal\libraries\ExternalLibrary\Definition\DefinitionDiscoveryInterface[]
+   */
+  protected $discoveries = [];
+
+  /**
+   * {@inheritdoc}
+   */
+  public function hasDefinition($id) {
+    foreach ($this->discoveries as $discovery) {
+      if ($discovery->hasDefinition($id)) {
+        return TRUE;
+      }
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDefinition($id) {
+    /** @var \Drupal\libraries\ExternalLibrary\Definition\WritableDefinitionDiscoveryInterface[] $discoveries_to_write */
+    $discoveries_to_write = [];
+    foreach ($this->discoveries as $discovery) {
+      if ($discovery->hasDefinition($id)) {
+        $definition = $discovery->getDefinition($id);
+        break;
+      }
+      elseif ($discovery instanceof WritableDefinitionDiscoveryInterface) {
+        $discoveries_to_write[] = $discovery;
+      }
+    }
+
+    if (!isset($definition)) {
+      throw new LibraryDefinitionNotFoundException($id);
+    }
+
+    foreach ($discoveries_to_write as $discovery_to_write) {
+      $discovery_to_write->writeDefinition($id, $definition);
+    }
+
+    return $definition;
+  }
+
+  /**
+   * Adds a definition discovery to the list to check.
+   *
+   * @param \Drupal\libraries\ExternalLibrary\Definition\DefinitionDiscoveryInterface $discovery
+   *   The definition discovery to add.
+   *
+   * @return $this
+   */
+  public function addDiscovery(DefinitionDiscoveryInterface $discovery) {
+    $this->discoveries[] = $discovery;
+    return $this;
+  }
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Definition/DefinitionDiscoveryFactory.php b/web/modules/libraries/src/ExternalLibrary/Definition/DefinitionDiscoveryFactory.php
new file mode 100644
index 0000000000000000000000000000000000000000..970385e5f0ebacd40ab50952b84a55377207bd70
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Definition/DefinitionDiscoveryFactory.php
@@ -0,0 +1,104 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Definition;
+
+use Drupal\Component\Serialization\SerializationInterface;
+use Drupal\Core\Config\ConfigFactoryInterface;
+use GuzzleHttp\ClientInterface;
+
+/**
+ * Instantiates a library definition discovery based on configuration.
+ */
+class DefinitionDiscoveryFactory {
+
+  /**
+   * The configuration factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
+  /**
+   * The serializer for local definition files.
+   *
+   * @var \Drupal\Component\Serialization\SerializationInterface
+   */
+  protected $localSerializer;
+
+  /**
+   * The HTTP client used to fetch remote definitions.
+   *
+   * @var \GuzzleHttp\ClientInterface
+   */
+  protected $httpClient;
+
+  /**
+   * The serializer for remote definitions.
+   *
+   * @var \Drupal\Component\Serialization\SerializationInterface
+   */
+  protected $remoteSerializer;
+
+  /**
+   * Constructs a definition discovery factory.
+   *
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   The configuration factory.
+   * @param \Drupal\Component\Serialization\SerializationInterface $local_serializer
+   *   The serializer for local definition files.
+   * @param \GuzzleHttp\ClientInterface $http_client
+   *   The HTTP client used to fetch remote definitions.
+   * @param \Drupal\Component\Serialization\SerializationInterface $remote_serializer
+   *   The serializer for remote definitions.
+   */
+  public function __construct(
+    ConfigFactoryInterface $config_factory,
+    SerializationInterface $local_serializer,
+    ClientInterface $http_client,
+    SerializationInterface $remote_serializer
+  ) {
+    $this->configFactory = $config_factory;
+    $this->localSerializer = $local_serializer;
+    $this->httpClient = $http_client;
+    $this->remoteSerializer = $remote_serializer;
+  }
+
+  /**
+   * Gets a library definition discovery.
+   *
+   * @return \Drupal\libraries\ExternalLibrary\Definition\DefinitionDiscoveryInterface
+   *   The library definition discovery.
+   */
+  public function get() {
+    $config = $this->configFactory->get('libraries.settings');
+
+    if ($config->get('definition.remote.enable')) {
+      $discovery = new ChainDefinitionDiscovery();
+
+      $local_discovery = new WritableFileDefinitionDiscovery(
+        $this->localSerializer,
+        $config->get('definition.local.path')
+      );
+      $discovery->addDiscovery($local_discovery);
+
+      foreach ($config->get('definition.remote.urls') as $remote_url) {
+        $remote_discovery = new GuzzleDefinitionDiscovery(
+          $this->httpClient,
+          $this->remoteSerializer,
+          $remote_url
+        );
+
+        $discovery->addDiscovery($remote_discovery);
+      }
+    }
+    else {
+      $discovery = new FileDefinitionDiscovery(
+        $this->localSerializer,
+        $config->get('definition.local.path')
+      );
+    }
+
+    return $discovery;
+  }
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Definition/DefinitionDiscoveryInterface.php b/web/modules/libraries/src/ExternalLibrary/Definition/DefinitionDiscoveryInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..61fc589ccab3fabec86faf97a7b3db0caab25bdc
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Definition/DefinitionDiscoveryInterface.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Definition;
+
+/**
+ * Provides an interface for library definition discoveries.
+ *
+ * This is similar to the plugin system's DiscoveryInterface, except that this
+ * does not require knowing all definitions upfront, so there is no
+ * getDefinitions() method.
+ *
+ * @see \Drupal\Component\Plugin\Discovery\DiscoveryInterface
+ *
+ * @ingroup libraries
+ */
+interface DefinitionDiscoveryInterface {
+
+  /**
+   * Checks whether a library definition exists.
+   *
+   * @param string $id
+   *   The library ID.
+   *
+   * @return bool
+   *   TRUE if a library definition with the given ID exists; FALSE otherwise.
+   */
+  public function hasDefinition($id);
+
+  /**
+   * Gets a library definition by its ID.
+   *
+   * @param string $id
+   *   The library ID.
+   *
+   * @return array
+   *   The library definition.
+   *
+   * @throws \Drupal\libraries\ExternalLibrary\Exception\LibraryDefinitionNotFoundException
+   *
+   * @todo Consider returning a classed object instead of an array or at least
+   *   document and validate the array structure.
+   */
+  public function getDefinition($id);
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Definition/FileDefinitionDiscovery.php b/web/modules/libraries/src/ExternalLibrary/Definition/FileDefinitionDiscovery.php
new file mode 100644
index 0000000000000000000000000000000000000000..6ad62c491516d1174a49b1db2274c782627985dc
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Definition/FileDefinitionDiscovery.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Definition;
+
+use Drupal\Component\Serialization\SerializationInterface;
+use Drupal\libraries\ExternalLibrary\Exception\LibraryDefinitionNotFoundException;
+
+/**
+ * Provides a libraries definition discovery using PHP's native file functions.
+ *
+ * It supports either a URI with a stream wrapper, an absolute file path or a
+ * file path relative to the Drupal root as a base URI.
+ *
+ * By default YAML files are used.
+ *
+ * @see \Drupal\libraries\StreamWrapper\LibraryDefinitionsStream
+ *
+ * @ingroup libraries
+ */
+class FileDefinitionDiscovery extends FileDefinitionDiscoveryBase implements DefinitionDiscoveryInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function hasDefinition($id) {
+    return file_exists($this->getFileUri($id));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getSerializedDefinition($id) {
+    return file_get_contents($this->getFileUri($id));
+  }
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Definition/FileDefinitionDiscoveryBase.php b/web/modules/libraries/src/ExternalLibrary/Definition/FileDefinitionDiscoveryBase.php
new file mode 100644
index 0000000000000000000000000000000000000000..0151f2794980042ce22a912823c93f5792d4373a
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Definition/FileDefinitionDiscoveryBase.php
@@ -0,0 +1,82 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Definition;
+
+use Drupal\Component\Serialization\SerializationInterface;
+use Drupal\libraries\ExternalLibrary\Exception\LibraryDefinitionNotFoundException;
+
+/**
+ * Provides a base implementation for file-based definition discoveries.
+ *
+ * This discovery assumes that library files contain the serialized library
+ * definition and are accessible under a common base URI. The expected library
+ * file URI will be constructed from this by appending '/$id.$extension' to
+ * this, where $id is the library ID and $extension is the serializer extension.
+ */
+abstract class FileDefinitionDiscoveryBase implements DefinitionDiscoveryInterface {
+
+  /**
+   * The serializer for the library definition files.
+   *
+   * @var \Drupal\Component\Serialization\SerializationInterface
+   */
+  protected $serializer;
+
+  /**
+   * The base URI for the library files.
+   *
+   * @var string
+   */
+  protected $baseUri;
+
+  /**
+   * Constructs a stream-based library definition discovery.
+   *
+   * @param \Drupal\Component\Serialization\SerializationInterface $serializer
+   *   The serializer for the library definition files.
+   * @param string $base_uri
+   *   The base URI for the library files.
+   */
+  public function __construct(SerializationInterface $serializer, $base_uri) {
+    $this->serializer = $serializer;
+    $this->baseUri = $base_uri;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDefinition($id) {
+    if (!$this->hasDefinition($id)) {
+      throw new LibraryDefinitionNotFoundException($id);
+    }
+    return $this->serializer->decode($this->getSerializedDefinition($id));
+  }
+
+  /**
+   * Gets the contents of the library file.
+   *
+   * @param $id
+   *   The library ID to retrieve the serialized definition for.
+   *
+   * @return string
+   *   The serialized library definition.
+   *
+   * @throws \Drupal\libraries\ExternalLibrary\Exception\LibraryDefinitionNotFoundException
+   */
+  abstract protected function getSerializedDefinition($id);
+
+  /**
+   * Returns the file URI of the library definition file for a given library ID.
+   *
+   * @param $id
+   *   The ID of the external library.
+   *
+   * @return string
+   *   The file URI of the file the library definition resides in.
+   */
+  protected function getFileUri($id) {
+    $filename = $id . '.' . $this->serializer->getFileExtension();
+    return "$this->baseUri/$filename";
+  }
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Definition/GuzzleDefinitionDiscovery.php b/web/modules/libraries/src/ExternalLibrary/Definition/GuzzleDefinitionDiscovery.php
new file mode 100644
index 0000000000000000000000000000000000000000..59745b78c7fbd6523287da0bce6f14ce601d7f22
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Definition/GuzzleDefinitionDiscovery.php
@@ -0,0 +1,71 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Definition;
+
+use Drupal\Component\Serialization\SerializationInterface;
+use Drupal\libraries\ExternalLibrary\Exception\LibraryDefinitionNotFoundException;
+use GuzzleHttp\ClientInterface;
+use GuzzleHttp\Exception\GuzzleException;
+
+/**
+ * Provides a definition discovery that fetches remote definitions using Guzzle.
+ *
+ * By default JSON files are assumed to be in JSON format.
+ *
+ * @todo Cache responses statically by ID to avoid multiple HTTP requests when
+ *   calling hasDefinition() and getDefinition() sequentially.
+ */
+class GuzzleDefinitionDiscovery extends FileDefinitionDiscoveryBase implements DefinitionDiscoveryInterface {
+
+  /**
+   * The HTTP client.
+   *
+   * @var \GuzzleHttp\ClientInterface
+   */
+  protected $httpClient;
+
+  /**
+   * Constructs a Guzzle-based definition discvoery.
+   *
+   * @param \GuzzleHttp\ClientInterface $http_client
+   *   The HTTP client.
+   * @param \Drupal\Component\Serialization\SerializationInterface $serializer
+   *   The serializer for the library definition files.
+   * @param string $base_url
+   *   The base URL for the library files.
+   */
+  public function __construct(ClientInterface $http_client, SerializationInterface $serializer, $base_url) {
+    parent::__construct($serializer, $base_url);
+    $this->httpClient = $http_client;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function hasDefinition($id) {
+    try {
+      $response = $this->httpClient->request('GET', $this->getFileUri($id));
+      return $response->getStatusCode() === 200;
+    }
+    catch (GuzzleException $exception) {
+      return FALSE;
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getSerializedDefinition($id) {
+    try {
+      $response = $this->httpClient->request('GET', $this->getFileUri($id));
+      return (string) $response->getBody();
+    }
+    catch (GuzzleException $exception) {
+      throw new LibraryDefinitionNotFoundException($id, '', 0, $exception);
+    }
+    catch (\RuntimeException $exception) {
+      throw new LibraryDefinitionNotFoundException($id, '', 0, $exception);
+    }
+  }
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Definition/WritableDefinitionDiscoveryInterface.php b/web/modules/libraries/src/ExternalLibrary/Definition/WritableDefinitionDiscoveryInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..f644a0d36ee9a49385105b195c4621960f210810
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Definition/WritableDefinitionDiscoveryInterface.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Definition;
+
+/**
+ * Provides an interface for library definition discoveries that are writable.
+ *
+ * @see \Drupal\libraries\ExternalLibrary\Definition\DefinitionDiscoveryInterface
+ * @see \Drupal\libraries\ExternalLibrary\Definition\ChainDefinitionDiscovery
+ */
+interface WritableDefinitionDiscoveryInterface extends DefinitionDiscoveryInterface {
+
+  /**
+   * Writes a library definition persistently.
+   *
+   * @param string $id
+   *   The library ID.
+   * @param array $definition
+   *   The library definition to write.
+   *
+   * @return $this
+   */
+  public function writeDefinition($id, $definition);
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Definition/WritableFileDefinitionDiscovery.php b/web/modules/libraries/src/ExternalLibrary/Definition/WritableFileDefinitionDiscovery.php
new file mode 100644
index 0000000000000000000000000000000000000000..b8204fb9e508abdcbd0c5758b90ddc7329455361
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Definition/WritableFileDefinitionDiscovery.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Definition;
+
+/**
+ * Provides a definition discovery based on a writable directory or stream.
+ *
+ * @see \Drupal\libraries\ExternalLibrary\Definition\FileDefinitionDiscovery
+ */
+class WritableFileDefinitionDiscovery extends FileDefinitionDiscovery implements WritableDefinitionDiscoveryInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function writeDefinition($id, $definition) {
+    file_put_contents($this->getFileUri($id), $this->serializer->encode($definition));
+    return $this;
+  }
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Dependency/DependentLibraryInterface.php b/web/modules/libraries/src/ExternalLibrary/Dependency/DependentLibraryInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..fd838101f325f81b6dc8c8a8c5e4d5d3ebab7827
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Dependency/DependentLibraryInterface.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Dependency;
+
+use Drupal\libraries\ExternalLibrary\LibraryInterface;
+
+/**
+ * Provides an interface for libraries that depend on other libraries.
+ *
+ * @todo Implement versioned dependencies.
+ */
+interface DependentLibraryInterface extends LibraryInterface {
+
+  /**
+   * Returns the libraries dependencies, if any.
+   *
+   * @return string[]
+   *   An array of library IDs of libraries that the library depends on.
+   */
+  public function getDependencies();
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Dependency/DependentLibraryTrait.php b/web/modules/libraries/src/ExternalLibrary/Dependency/DependentLibraryTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..e7623ae7a2ec36c8a622e818c10ed87881459e16
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Dependency/DependentLibraryTrait.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Dependency;
+
+/**
+ * Provides a trait for libraries that depend on other libraries.
+ */
+trait DependentLibraryTrait {
+
+  /**
+   * An array of library IDs of libraries that the library depends on.
+   *
+   * @return string[]
+   */
+  protected $dependencies;
+
+  /**
+   * Returns the libraries dependencies, if any.
+   *
+   * @return string[]
+   *   An array of library IDs of libraries that the library depends on.
+   *
+   * @see \Drupal\libraries\ExternalLibrary\Dependency\DependentLibraryInterface::getDependencies()
+   */
+  public function getDependencies() {
+    return $this->dependencies;
+  }
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Exception/InvalidLibraryDependencyException.php b/web/modules/libraries/src/ExternalLibrary/Exception/InvalidLibraryDependencyException.php
new file mode 100644
index 0000000000000000000000000000000000000000..7ab3eeb7e4ce73dd3b3bbef97f2571ab13b214e0
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Exception/InvalidLibraryDependencyException.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Exception;
+
+use Drupal\libraries\ExternalLibrary\Utility\DependencyAccessorTrait;
+use Drupal\libraries\ExternalLibrary\LibraryInterface;
+use Drupal\libraries\ExternalLibrary\Utility\LibraryAccessorTrait;
+use Drupal\libraries\ExternalLibrary\Utility\LibraryAccessorInterface;
+
+/**
+ * Provides an exception for an invalid library exception.
+ */
+class InvalidLibraryDependencyException extends \UnexpectedValueException implements LibraryAccessorInterface {
+
+  use LibraryAccessorTrait;
+  use DependencyAccessorTrait;
+
+  /**
+   * Constructs a library exception.
+   *
+   * @param \Drupal\libraries\ExternalLibrary\LibraryInterface $library
+   *   The library with the invalid dependency.
+   * @param \Drupal\libraries\ExternalLibrary\LibraryInterface $dependency
+   *   The dependency.
+   * @param string $message
+   *   (optional) The exception message.
+   * @param int $code
+   *   (optional) The error code.
+   * @param \Exception $previous
+   *   (optional) The previous exception.
+   */
+  public function __construct(
+    LibraryInterface $library,
+    LibraryInterface $dependency,
+    $message = '',
+    $code = 0,
+    \Exception $previous = NULL
+  ) {
+    $this->library = $library;
+    $this->dependency = $dependency;
+    $message = $message ?: "The library '{$this->library->getId()}' cannot depend on the library '{$this->dependency->getId()}'.";
+    parent::__construct($message, $code, $previous);
+  }
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Exception/LibraryDefinitionNotFoundException.php b/web/modules/libraries/src/ExternalLibrary/Exception/LibraryDefinitionNotFoundException.php
new file mode 100644
index 0000000000000000000000000000000000000000..c274302d699892a8b2950f7964b3f90af253f571
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Exception/LibraryDefinitionNotFoundException.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Exception;
+
+use Drupal\libraries\ExternalLibrary\Utility\LibraryIdAccessorTrait;
+use Drupal\libraries\ExternalLibrary\Utility\LibraryIdAccessorInterface;
+
+/**
+ * Provides an exception for a library definition that cannot be found.
+ */
+class LibraryDefinitionNotFoundException extends \RuntimeException implements LibraryIdAccessorInterface {
+
+  use LibraryIdAccessorTrait;
+
+  /**
+   * Constructs a library exception.
+   *
+   * @param string $library_id
+   *   The library ID.
+   * @param string $message
+   *   (optional) The exception message.
+   * @param int $code
+   *   (optional) The error code.
+   * @param \Exception $previous
+   *   (optional) The previous exception.
+   */
+  public function __construct(
+    $library_id,
+    $message = '',
+    $code = 0,
+    \Exception $previous = NULL
+  ) {
+    $this->libraryId = (string) $library_id;
+    $message = $message ?: "The library definition for the library '{$this->libraryId}' could not be found.";
+    parent::__construct($message, $code, $previous);
+  }
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Exception/LibraryNotInstalledException.php b/web/modules/libraries/src/ExternalLibrary/Exception/LibraryNotInstalledException.php
new file mode 100644
index 0000000000000000000000000000000000000000..ed48a629c458165cd5a30e0584e6cc70e92a9a2d
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Exception/LibraryNotInstalledException.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Exception;
+
+use Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface;
+use Drupal\libraries\ExternalLibrary\Utility\LibraryAccessorTrait;
+use Drupal\libraries\ExternalLibrary\Utility\LibraryAccessorInterface;
+
+/**
+ * Provides an exception for a library that is not installed.
+ */
+class LibraryNotInstalledException extends \RuntimeException implements LibraryAccessorInterface {
+
+  use LibraryAccessorTrait;
+
+  /**
+   * Constructs a library exception.
+   *
+   * @param \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface $library
+   *   The library that is not installed.
+   * @param string $message
+   *   (optional) The exception message.
+   * @param int $code
+   *   (optional) The error code.
+   * @param \Exception $previous
+   *   (optional) The previous exception.
+   */
+  public function __construct(
+    LocalLibraryInterface $library,
+    $message = '',
+    $code = 0,
+    \Exception $previous = NULL
+  ) {
+    $this->library = $library;
+    $message = $message ?: "The library '{$this->library->getId()}' is not installed.";
+    parent::__construct($message, $code, $previous);
+  }
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Exception/LibraryTypeNotFoundException.php b/web/modules/libraries/src/ExternalLibrary/Exception/LibraryTypeNotFoundException.php
new file mode 100644
index 0000000000000000000000000000000000000000..b5655bba09a626a1d7b73a60b2971a1fc19c5a83
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Exception/LibraryTypeNotFoundException.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Exception;
+
+use Drupal\libraries\ExternalLibrary\Utility\LibraryIdAccessorTrait;
+use Drupal\libraries\ExternalLibrary\Utility\LibraryIdAccessorInterface;
+
+/**
+ * Provides an exception for a library definition without a type declaration.
+ */
+class LibraryTypeNotFoundException extends \RuntimeException implements LibraryAccessorInterface {
+
+  use LibraryIdAccessorTrait;
+
+  /**
+   * Constructs a library exception.
+   *
+   * @param string $library_id
+   *   The library ID.
+   * @param string $message
+   *   (optional) The exception message.
+   * @param int $code
+   *   (optional) The error code.
+   * @param \Exception $previous
+   *   (optional) The previous exception.
+   */
+  public function __construct(
+    $library_id,
+    $message = '',
+    $code = 0,
+    \Exception $previous = NULL
+  ) {
+    $this->libraryId = (string) $library_id;
+    $message = $message ?: "The library type for the library '{$this->libraryId}' could not be found.";
+    parent::__construct($message, $code, $previous);
+  }
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Exception/UnknownLibraryVersionException.php b/web/modules/libraries/src/ExternalLibrary/Exception/UnknownLibraryVersionException.php
new file mode 100644
index 0000000000000000000000000000000000000000..4afe0593feb4f48770fdbd1651164bf337f5af8b
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Exception/UnknownLibraryVersionException.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Exception;
+
+use Drupal\libraries\ExternalLibrary\Utility\LibraryAccessorTrait;
+use Drupal\libraries\ExternalLibrary\Utility\LibraryAccessorInterface;
+use Drupal\libraries\ExternalLibrary\Version\VersionedLibraryInterface;
+
+/**
+ * Provides an exception for libraries whose version has not been detected.
+ */
+class UnknownLibraryVersionException extends \RuntimeException implements LibraryAccessorInterface {
+
+  use LibraryAccessorTrait;
+
+  /**
+   * Constructs a library exception.
+   *
+   * @param \Drupal\libraries\ExternalLibrary\Version\VersionedLibraryInterface $library
+   *   The library.
+   * @param string $message
+   *   (optional) The exception message.
+   * @param int $code
+   *   (optional) The error code.
+   * @param \Exception $previous
+   *   (optional) The previous exception.
+   */
+  public function __construct(
+    VersionedLibraryInterface $library,
+    $message = '',
+    $code = 0,
+    \Exception $previous = NULL
+  ) {
+    $this->library = $library;
+    $message = $message ?: "The version of library '{$this->library->getId()}' could not be detected.";
+    parent::__construct($message, $code, $previous);
+  }
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/LibraryBase.php b/web/modules/libraries/src/ExternalLibrary/LibraryBase.php
new file mode 100644
index 0000000000000000000000000000000000000000..8571552c7dda49504545397e96472a2c14b360d2
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/LibraryBase.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary;
+
+use Drupal\libraries\ExternalLibrary\Dependency\DependentLibraryInterface;
+use Drupal\libraries\ExternalLibrary\Dependency\DependentLibraryTrait;
+use Drupal\libraries\ExternalLibrary\Type\LibraryTypeInterface;
+use Drupal\libraries\ExternalLibrary\Utility\IdAccessorTrait;
+use Drupal\libraries\ExternalLibrary\Version\VersionedLibraryInterface;
+use Drupal\libraries\ExternalLibrary\Version\VersionedLibraryTrait;
+
+/**
+ * Provides a base external library implementation.
+ */
+abstract class LibraryBase implements
+  LibraryInterface,
+  DependentLibraryInterface,
+  VersionedLibraryInterface
+{
+
+  use
+    IdAccessorTrait,
+    DependentLibraryTrait,
+    VersionedLibraryTrait
+  ;
+
+  /**
+   * The library type of this library.
+   *
+   * @var \Drupal\libraries\ExternalLibrary\Type\LibraryTypeInterface
+   */
+  protected $type;
+
+  /**
+   * Constructs a library.
+   *
+   * @param string $id
+   *   The library ID.
+   * @param array $definition
+   *   The library definition array.
+   * @param \Drupal\libraries\ExternalLibrary\Type\LibraryTypeInterface $type
+   *   The library type of this library.
+   */
+  public function __construct($id, array $definition, LibraryTypeInterface $type) {
+    $this->id = (string) $id;
+    $this->type = $type;
+    $this->dependencies = $definition['dependencies'];
+    $this->versionDetector = $definition['version_detector'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create($id, array $definition, LibraryTypeInterface $type) {
+    static::processDefinition($definition);
+    return new static($id, $definition, $type);
+  }
+
+  /**
+   * Gets library definition defaults.
+   *
+   * @param array $definition
+   *   A library definition array.
+   */
+  protected static function processDefinition(array &$definition) {
+    $definition += [
+      'dependencies' => [],
+      // @todo This fallback is not very elegant.
+      'version_detector' => [
+        'id' => 'static',
+        'configuration' => ['version' => ''],
+      ],
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getType() {
+    return $this->type;
+  }
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/LibraryInterface.php b/web/modules/libraries/src/ExternalLibrary/LibraryInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..ebf43a9b9fdccffc2fd80145265b77c4446c6dc6
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/LibraryInterface.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary;
+
+use Drupal\libraries\ExternalLibrary\Type\LibraryTypeInterface;
+
+/**
+ * Provides an interface for different types of external libraries.
+ *
+ * @ingroup libraries
+ */
+interface LibraryInterface {
+
+  /**
+   * Creates an instance of the library from its definition.
+   *
+   * @param string $id
+   *   The library ID.
+   * @param array $definition
+   *   The library definition array.
+   * @param \Drupal\libraries\ExternalLibrary\Type\LibraryTypeInterface $type
+   *   The library type of this library.
+   *
+   * @return static
+   */
+  public static function create($id, array $definition, LibraryTypeInterface $type);
+
+  /**
+   * Returns the ID of the library.
+   *
+   * @return string
+   *   The library ID. This must be unique among all known libraries.
+   */
+  public function getId();
+
+  /**
+   * Returns the library type of the library.
+   *
+   * @return \Drupal\libraries\ExternalLibrary\Type\LibraryTypeInterface
+   *   The library of the library.
+   */
+  public function getType();
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/LibraryManager.php b/web/modules/libraries/src/ExternalLibrary/LibraryManager.php
new file mode 100644
index 0000000000000000000000000000000000000000..17a62236e292b2ddf0b2a71e3914bf97c080bb9e
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/LibraryManager.php
@@ -0,0 +1,120 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary;
+
+use Drupal\Component\Plugin\Factory\FactoryInterface;
+use Drupal\libraries\ExternalLibrary\Exception\LibraryTypeNotFoundException;
+use Drupal\libraries\ExternalLibrary\Type\LibraryCreationListenerInterface;
+use Drupal\libraries\ExternalLibrary\Type\LibraryLoadingListenerInterface;
+use Drupal\libraries\ExternalLibrary\Definition\DefinitionDiscoveryInterface;
+
+/**
+ * Provides a manager for external libraries.
+ *
+ * @todo Dispatch events at various points in the library lifecycle.
+ * @todo Automatically load PHP file libraries that are required by modules or
+ *   themes.
+ */
+class LibraryManager implements LibraryManagerInterface {
+
+  /**
+   * The library definition discovery.
+   *
+   * @var \Drupal\libraries\ExternalLibrary\Definition\DefinitionDiscoveryInterface
+   */
+  protected $definitionDiscovery;
+
+  /**
+   * The library type factory.
+   *
+   * @var \Drupal\Component\Plugin\Factory\FactoryInterface
+   */
+  protected $libraryTypeFactory;
+
+  /**
+   * Constructs an external library manager.
+   *
+   * @param \Drupal\libraries\ExternalLibrary\Definition\DefinitionDiscoveryInterface $definition_disovery
+   *   The library registry.
+   * @param \Drupal\Component\Plugin\Factory\FactoryInterface $library_type_factory
+   *   The library type factory.
+   */
+  public function __construct(
+    DefinitionDiscoveryInterface $definition_disovery,
+    FactoryInterface $library_type_factory
+  ) {
+    $this->definitionDiscovery = $definition_disovery;
+    $this->libraryTypeFactory = $library_type_factory;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLibrary($id) {
+    $definition = $this->definitionDiscovery->getDefinition($id);
+    return $this->getLibraryFromDefinition($id, $definition);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRequiredLibraryIds() {
+    $library_ids = [];
+    foreach (['module', 'theme'] as $type) {
+      foreach (system_get_info($type) as $info) {
+        if (isset($info['library_dependencies'])) {
+          $library_ids = array_merge($library_ids, $info['library_dependencies']);
+        }
+      }
+    }
+    return array_unique($library_ids);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function load($id) {
+    $definition = $this->definitionDiscovery->getDefinition($id);
+    $library_type = $this->getLibraryType($id, $definition);
+    // @todo Throw an exception instead of silently failing.
+    if ($library_type instanceof LibraryLoadingListenerInterface) {
+      $library_type->onLibraryLoad($this->getLibraryFromDefinition($id, $definition));
+    }
+  }
+
+  /**
+   * @param $id
+   * @param $definition
+   * @return mixed
+   */
+  protected function getLibraryFromDefinition($id, $definition) {
+    $library_type = $this->getLibraryType($id, $definition);
+
+    // @todo Make this alter-able.
+    $class = $library_type->getLibraryClass();
+
+    // @todo Make sure that the library class implements the correct interface.
+    $library = $class::create($id, $definition, $library_type);
+
+    if ($library_type instanceof LibraryCreationListenerInterface) {
+      $library_type->onLibraryCreate($library);
+      return $library;
+    }
+    return $library;
+  }
+
+  /**
+   * @param string $id
+   * @param array $definition
+   *
+   * @return \Drupal\libraries\ExternalLibrary\Type\LibraryTypeInterface
+   */
+  protected function getLibraryType($id, $definition) {
+    // @todo Validate that the type is a string.
+    if (!isset($definition['type'])) {
+      throw new LibraryTypeNotFoundException($id);
+    }
+    return $this->libraryTypeFactory->createInstance($definition['type']);
+  }
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/LibraryManagerInterface.php b/web/modules/libraries/src/ExternalLibrary/LibraryManagerInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..0c4383f37ae3efc8625089098f1d0c806183e13d
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/LibraryManagerInterface.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary;
+
+/**
+ * Provides an interface for external library managers.
+ */
+interface LibraryManagerInterface {
+
+  /**
+   * Gets a library by its ID.
+   *
+   * @param string $id
+   *   The library ID.
+   *
+   * @return \Drupal\libraries\ExternalLibrary\LibraryInterface
+   *   The library object.
+   *
+   * @throws \Drupal\libraries\ExternalLibrary\Exception\LibraryDefinitionNotFoundException
+   * @throws \Drupal\libraries\ExternalLibrary\Exception\LibraryTypeNotFoundException
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   */
+  public function getLibrary($id);
+
+  /**
+   * Gets the list of libraries that are required by enabled extensions.
+   *
+   * Modules, themes, and installation profiles can declare library dependencies
+   * by specifying a 'library_dependencies' key in their info files.
+   *
+   * @return string[]
+   *   An array of library IDs.
+   */
+  public function getRequiredLibraryIds();
+
+  /**
+   * Loads library files for a library.
+   *
+   * Note that not all library types support explicit loading. Asset libraries,
+   * in particular, are declared to Drupal core's library system and are then
+   * loaded using that.
+   *
+   * @param string $id
+   *   The ID of the library.
+   *
+   * @throws \Drupal\libraries\ExternalLibrary\Exception\LibraryDefinitionNotFoundException
+   * @throws \Drupal\libraries\ExternalLibrary\Exception\LibraryNotInstalledException
+   */
+  public function load($id);
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Local/LocalLibraryInterface.php b/web/modules/libraries/src/ExternalLibrary/Local/LocalLibraryInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..f8c1fbe5730f04b6f6e1f1183d98179ab741d049
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Local/LocalLibraryInterface.php
@@ -0,0 +1,82 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Local;
+
+use Drupal\Component\Plugin\Factory\FactoryInterface;
+use Drupal\libraries\ExternalLibrary\LibraryInterface;
+
+/**
+ * Provides an interface for local libraries.
+ *
+ * Local libraries are libraries that can be found on the filesystem. If the
+ * library files can be found in the filesystem a library is considered
+ * installed and its library path can be retrieved.
+ *
+ * Because determining whether or not the library is available locally is not
+ * the responsibility of the library itself, but of a designated locator, this
+ * interface declares setter methods, as well.
+ *
+ * @see \Drupal\libraries\ExternalLibrary\Local\LocatorInterface
+ */
+interface LocalLibraryInterface extends LibraryInterface {
+
+  /**
+   * Checks whether the library is installed.
+   *
+   * @return bool
+   *   TRUE if the library is installed; FALSE otherwise;
+   */
+  public function isInstalled();
+
+  /**
+   * Marks the library as uninstalled.
+   *
+   * A corresponding method to mark the library as installed is not provided as
+   * an installed library should have a library path, so that
+   * LocalLibraryInterface::setLibraryPath() can be used instead.
+   *
+   * @return $this
+   *
+   * @see \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface::isInstalled()
+   * @see \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface::setLocalPath()
+   */
+  public function setUninstalled();
+
+  /**
+   * Gets the local path to the library.
+   *
+   * @return string
+   *   The absolute path to the library on the filesystem.
+   *
+   * @throws \Drupal\libraries\ExternalLibrary\Exception\LibraryNotInstalledException
+   *
+   * @see \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface::setLocalPath()
+   */
+  public function getLocalPath();
+
+  /**
+   * Sets the local path of the library.
+   *
+   * @param string $path
+   *   The path to the library.
+   *
+   * @see \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface::getLocalPath()
+   */
+  public function setLocalPath($path);
+
+  /**
+   * Gets the locator of this library using the locator factory.
+   *
+   * Because determining the installation status and library path of a library
+   * is not specific to any library or even any library type, this logic is
+   * offloaded to separate locator objects.
+   *
+   * @param \Drupal\Component\Plugin\Factory\FactoryInterface $locator_factory
+   *
+   * @return \Drupal\libraries\ExternalLibrary\Local\LocatorInterface
+   *
+   * @see \Drupal\libraries\ExternalLibrary\Local\LocatorInterface
+   */
+  public function getLocator(FactoryInterface $locator_factory);
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Local/LocalLibraryTrait.php b/web/modules/libraries/src/ExternalLibrary/Local/LocalLibraryTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..3f08e4ba4c1f2d346aa6158f6594e56ea3034cba
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Local/LocalLibraryTrait.php
@@ -0,0 +1,99 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Local;
+use Drupal\libraries\ExternalLibrary\Exception\LibraryNotInstalledException;
+
+/**
+ * Provides a trait for local libraries utilizing a stream wrapper.
+ *
+ * It assumes that the library files can be accessed using a specified stream
+ * wrapper and that the first component of the file URIs are the library IDs.
+ * Thus, file URIs are of the form:
+ * stream-wrapper-scheme://library-id/path/to/file/within/the/library/filename
+ *
+ * This trait should only be used by classes implementing LocalLibraryInterface.
+ *
+ * @see \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface
+ */
+trait LocalLibraryTrait {
+
+  /**
+   * Whether or not the library is installed.
+   *
+   * A library being installed means its files can be found on the filesystem.
+   *
+   * @var bool
+   */
+  protected $installed = FALSE;
+
+  /**
+   * The local path to the library relative to the app root.
+   *
+   * @var string
+   */
+  protected $localPath;
+
+  /**
+   * Checks whether the library is installed.
+   *
+   * @return bool
+   *   TRUE if the library is installed; FALSE otherwise;
+   *
+   * @see \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface::isInstalled()
+   */
+  public function isInstalled() {
+    return $this->installed;
+  }
+
+  /**
+   * Marks the library as uninstalled.
+   *
+   * A corresponding method to mark the library as installed is not provided as
+   * an installed library should have a library path, so that
+   * LocalLibraryInterface::setLibraryPath() can be used instead.
+   *
+   * @return $this
+   *
+   * @see \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface::setUninstalled()
+   */
+  public function setUninstalled() {
+    $this->installed = FALSE;
+    return $this;
+  }
+
+  /**
+   * Gets the path to the library.
+   *
+   * @return string
+   *   The path to the library relative to the app root.
+   *
+   * @throws \Drupal\libraries\ExternalLibrary\Exception\LibraryNotInstalledException
+   *
+   * @see \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface::getLocalPath()
+   */
+  public function getLocalPath() {
+    if (!$this->isInstalled()) {
+      /** @var \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface $this */
+      throw new LibraryNotInstalledException($this);
+    }
+
+    return $this->localPath;
+  }
+
+  /**
+   * Sets the library path of the library.
+   *
+   * @param string $path
+   *   The path to the library.
+   *
+   * @see \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface::getLocalPath()
+   */
+  public function setLocalPath($path) {
+    $this->installed = TRUE;
+    $this->localPath = (string) $path;
+
+    assert('$this->localPath !== ""');
+    assert('$this->localPath[0] !== "/"');
+  }
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Local/LocatorInterface.php b/web/modules/libraries/src/ExternalLibrary/Local/LocatorInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..a1be89440c07d4f540de456b67f0cede16a4ff5d
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Local/LocatorInterface.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Local;
+
+/**
+ * Provides an interface for library locators.
+ *
+ * Because determining the installation status and library path of a library
+ * is not specific to any library or even any library type, this logic can be
+ * implemented generically in form of a locator.
+ */
+interface LocatorInterface {
+
+  /**
+   * Locates a library.
+   *
+   * @param \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface $library
+   *   The library to locate.
+   */
+  public function locate(LocalLibraryInterface $library);
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Local/LocatorManager.php b/web/modules/libraries/src/ExternalLibrary/Local/LocatorManager.php
new file mode 100644
index 0000000000000000000000000000000000000000..2f377ff06542a1e2b90e06a4a9d0946beb804fd9
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Local/LocatorManager.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Local;
+
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Plugin\DefaultPluginManager;
+use Drupal\libraries\Annotation\Locator;
+
+/**
+ * Provides a plugin manager for library locator plugins.
+ *
+ * @see \Drupal\libraries\ExternalLibrary\Local\LocatorInterface
+ */
+class LocatorManager extends DefaultPluginManager {
+
+  /**
+   * Constructs a locator manager.
+   *
+   * @param \Traversable $namespaces
+   *   An object that implements \Traversable which contains the root paths
+   *   keyed by the corresponding namespace to look for plugin implementations.
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
+   *   Cache backend instance to use.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler to invoke the alter hook with.
+   */
+  public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
+    parent::__construct('Plugin/libraries/Locator', $namespaces, $module_handler, LocatorInterface::class, Locator::class);
+    $this->alterInfo('libraries_locator_info');
+    $this->setCacheBackend($cache_backend, 'libraries_locator_info');
+  }
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/PhpFile/PhpFileLibrary.php b/web/modules/libraries/src/ExternalLibrary/PhpFile/PhpFileLibrary.php
new file mode 100644
index 0000000000000000000000000000000000000000..bf3c68328c205225920ebb79e6770d0f6e7b88fd
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/PhpFile/PhpFileLibrary.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\PhpFile;
+
+use Drupal\Component\Plugin\Factory\FactoryInterface;
+use Drupal\libraries\ExternalLibrary\Exception\LibraryNotInstalledException;
+use Drupal\libraries\ExternalLibrary\LibraryBase;
+use Drupal\libraries\ExternalLibrary\Local\LocalLibraryTrait;
+use Drupal\libraries\ExternalLibrary\Type\LibraryTypeInterface;
+
+/**
+ * Provides a base PHP file library implementation.
+ */
+class PhpFileLibrary extends LibraryBase implements PhpFileLibraryInterface {
+
+  use LocalLibraryTrait;
+
+  /**
+   * An array of PHP files for this library.
+   *
+   * @var array
+   */
+  protected $files = [];
+
+  /**
+   * Constructs a PHP file library.
+   *
+   * @param string $id
+   *   The library ID.
+   * @param array $definition
+   *   The library definition array.
+   * @param \Drupal\libraries\ExternalLibrary\Type\LibraryTypeInterface $type
+   *   The library type of this library.
+   */
+  public function __construct($id, array $definition, LibraryTypeInterface $type) {
+    parent::__construct($id, $definition, $type);
+    $this->files = $definition['files'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static function processDefinition(array &$definition) {
+    parent::processDefinition($definition);
+    $definition += [
+      'files' => [],
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPhpFiles() {
+    if (!$this->isInstalled()) {
+      throw new LibraryNotInstalledException($this);
+    }
+
+    $processed_files = [];
+    foreach ($this->files as $file) {
+      $processed_files[] = $this->getLocalPath() . '/' . $file;
+    }
+    return $processed_files;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLocator(FactoryInterface $locator_factory) {
+    // @todo Consider refining the stream wrapper used here.
+    return $locator_factory->createInstance('uri', ['uri' => 'php-file://']);
+  }
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/PhpFile/PhpFileLibraryInterface.php b/web/modules/libraries/src/ExternalLibrary/PhpFile/PhpFileLibraryInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..01ca453713e55045af6947fcb5f39c86c15540fe
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/PhpFile/PhpFileLibraryInterface.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\PhpFile;
+
+use Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface;
+
+/**
+ * Provides an interface for libraries with PHP files.
+ *
+ * @see \Drupal\libraries\ExternalLibrary\PhpFile\PhpFileLoaderInterface
+ */
+interface PhpFileLibraryInterface extends LocalLibraryInterface {
+
+  /**
+   * Returns the PHP files of this library.
+   *
+   * @return string[]
+   *   An array of absolute file paths of PHP files.
+   */
+  public function getPhpFiles();
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/PhpFile/PhpFileLoaderInterface.php b/web/modules/libraries/src/ExternalLibrary/PhpFile/PhpFileLoaderInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..52c1c620f5182ab6baa8d754bb756f1c2a087312
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/PhpFile/PhpFileLoaderInterface.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\PhpFile;
+
+/**
+ * Provides an interface for PHP file loaders.
+ *
+ * @see \Drupal\libraries\ExternalLibrary\PhpFile\PhpFileLibraryInterface
+ */
+interface PhpFileLoaderInterface {
+
+  /**
+   * Loads a PHP file.
+   *
+   * @param string $file
+   *   The absolute file path to the PHP file.
+   */
+  public function load($file);
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/PhpFile/PhpRequireLoader.php b/web/modules/libraries/src/ExternalLibrary/PhpFile/PhpRequireLoader.php
new file mode 100644
index 0000000000000000000000000000000000000000..ef6b695f64ae3aa1a6adc04e3104e0938b59e571
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/PhpFile/PhpRequireLoader.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\PhpFile;
+
+/**
+ * Provides a PHP file loader using PHP's require_once.
+ *
+ * @todo Provide a separate PhpIncludeOnceLoader.
+ */
+class PhpRequireLoader implements PhpFileLoaderInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function load($file) {
+    // @todo Because libraries cannot be loaded twice it should be possible to
+    //   use 'require' instead of 'require_once'.
+    /** @noinspection PhpIncludeInspection */
+    require_once $file;
+  }
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Remote/RemoteLibraryInterface.php b/web/modules/libraries/src/ExternalLibrary/Remote/RemoteLibraryInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..1fe7016ad020c17ec0081bd1eef48747aa389218
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Remote/RemoteLibraryInterface.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Remote;
+
+use Drupal\libraries\ExternalLibrary\LibraryInterface;
+
+/**
+ * Provides an interface for remote libraries.
+ *
+ * Assuming they declare a remote URL, remote libraries are always loaded. It is
+ * not checked whether or not the Drupal site has network access or the remote
+ * resource is available.
+ */
+interface RemoteLibraryInterface extends LibraryInterface {
+
+  /**
+   * Checks whether the library has a remote URL.
+   *
+   * This check allows using the same library class for multiple libraries only
+   * some of which are available remotely.
+   *
+   * @return bool
+   *   TRUE if the library has a remote URL; FALSE otherwise.
+   *
+   * @see \Drupal\libraries\ExternalLibrary\Asset\AssetLibraryInterface
+   */
+  public function hasRemoteUrl();
+
+  /**
+   * Returns the remote URL of the library.
+   *
+   * @return string
+   *   The remote URL of the library.
+   *
+   * @todo Consider throwing an exception if hasRemoteUrl() return FALSE.
+   */
+  public function getRemoteUrl();
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Remote/RemoteLibraryTrait.php b/web/modules/libraries/src/ExternalLibrary/Remote/RemoteLibraryTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..d55f3c23891f4f5cb6922889bb5511ef21d4abba
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Remote/RemoteLibraryTrait.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Remote;
+
+/**
+ * Provides a trait for remote libraries.
+ */
+trait RemoteLibraryTrait {
+
+  /**
+   * The remote library URL.
+   *
+   * @var string
+   */
+  protected $remoteUrl;
+
+  /**
+   * Checks whether the library has a remote URL.
+   *
+   * This check allows using the same library class for multiple libraries only
+   * some of which are available remotely.
+   *
+   * @return bool
+   *   TRUE if the library has a remote URL; FALSE otherwise.
+   *
+   * @see \Drupal\libraries\ExternalLibrary\Remote\RemoteLibraryInterface::hasRemoteUrl()
+   */
+  public function hasRemoteUrl() {
+    return !empty($this->remoteUrl);
+  }
+
+  /**
+   * Returns the remote URL of the library.
+   *
+   * @return string
+   *   The remote URL of the library.
+   *
+   * \Drupal\libraries\ExternalLibrary\Remote\RemoteLibraryInterface::getRemoteUrl()
+   */
+  public function getRemoteUrl() {
+    return $this->remoteUrl;
+  }
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Type/LibraryCreationListenerInterface.php b/web/modules/libraries/src/ExternalLibrary/Type/LibraryCreationListenerInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..e86115f26e8ccbad23091abb5f6299ccb0de3bec
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Type/LibraryCreationListenerInterface.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Type;
+
+use Drupal\libraries\ExternalLibrary\LibraryInterface;
+
+/**
+ * An interface for library types that want to react to library instantiation.
+ */
+interface LibraryCreationListenerInterface {
+
+  /**
+   * Reacts to the instantiation of a library.
+   *
+   * @param \Drupal\libraries\ExternalLibrary\LibraryInterface $library
+   *   The library that is being instantiated.
+   */
+  public function onLibraryCreate(LibraryInterface $library);
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Type/LibraryLoadingListenerInterface.php b/web/modules/libraries/src/ExternalLibrary/Type/LibraryLoadingListenerInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..962e9dbb1e151c7478d9ebebb814f85224d5d795
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Type/LibraryLoadingListenerInterface.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Type;
+
+use Drupal\libraries\ExternalLibrary\LibraryInterface;
+
+/**
+ * An interface for library types that want to react to library instantiation.
+ */
+interface LibraryLoadingListenerInterface {
+
+  /**
+   * Reacts to the instantiation of a library.
+   *
+   * @param \Drupal\libraries\ExternalLibrary\LibraryInterface $library
+   *   The library that is being instantiated.
+   */
+  public function onLibraryLoad(LibraryInterface $library);
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Type/LibraryTypeBase.php b/web/modules/libraries/src/ExternalLibrary/Type/LibraryTypeBase.php
new file mode 100644
index 0000000000000000000000000000000000000000..6cd9f59d099c8b3d9f88dca4b27297088b595efe
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Type/LibraryTypeBase.php
@@ -0,0 +1,87 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Type;
+
+use Drupal\Component\Plugin\Factory\FactoryInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\libraries\ExternalLibrary\LibraryInterface;
+use Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface;
+use Drupal\libraries\ExternalLibrary\Utility\IdAccessorTrait;
+use Drupal\libraries\ExternalLibrary\Version\VersionedLibraryInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides a base class for library types.
+ */
+abstract class LibraryTypeBase implements
+  LibraryTypeInterface,
+  LibraryCreationListenerInterface,
+  ContainerFactoryPluginInterface
+{
+
+  use IdAccessorTrait;
+
+  /**
+   * The locator factory.
+   *
+   * @var \Drupal\Component\Plugin\Factory\FactoryInterface
+   */
+  protected $locatorFactory;
+
+  /**
+   * The version detector factory.
+   *
+   * @var \Drupal\Component\Plugin\Factory\FactoryInterface
+   */
+  protected $detectorFactory;
+
+  /**
+   * Constructs the asset library type.
+   *
+   * @param string $plugin_id
+   *   The plugin ID taken from the class annotation.
+   * @param \Drupal\Component\Plugin\Factory\FactoryInterface $locator_factory
+   *   The locator factory.
+   * @param \Drupal\Component\Plugin\Factory\FactoryInterface $detector_factory
+   *   The version detector factory.
+   */
+  public function __construct($plugin_id, FactoryInterface $locator_factory, FactoryInterface $detector_factory) {
+    $this->id = $plugin_id;
+    $this->locatorFactory = $locator_factory;
+    $this->detectorFactory = $detector_factory;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $plugin_id,
+      $container->get('plugin.manager.libraries.locator'),
+      $container->get('plugin.manager.libraries.version_detector')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function onLibraryCreate(LibraryInterface $library) {
+    if ($library instanceof LocalLibraryInterface) {
+      $library->getLocator($this->locatorFactory)->locate($library);
+      // Fallback on global locators.
+      // @todo Consider if global locators should be checked as a fallback or as
+      // the primary locator source.
+      if (!$library->isInstalled()) {
+        $this->locatorFactory->createInstance('global')->locate($library);
+      }
+      // Also fetch version information.
+      if ($library instanceof VersionedLibraryInterface) {
+        // @todo Consider if this should be wrapped in some conditional logic
+        // or exception handling so that version detection errors do not
+        // prevent a library from being loaded.
+        $library->getVersionDetector($this->detectorFactory)->detectVersion($library);
+      }
+    }
+  }
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Type/LibraryTypeFactory.php b/web/modules/libraries/src/ExternalLibrary/Type/LibraryTypeFactory.php
new file mode 100644
index 0000000000000000000000000000000000000000..6689f51b8734b56538bd2e12edf6fa68cc6159dd
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Type/LibraryTypeFactory.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Type;
+
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Plugin\DefaultPluginManager;
+use Drupal\libraries\Annotation\LibraryType;
+
+/**
+ * Provides a plugin manager for library type plugins.
+ */
+class LibraryTypeFactory extends DefaultPluginManager {
+
+  /**
+   * Constructs a locator manager.
+   *
+   * @param \Traversable $namespaces
+   *   An object that implements \Traversable which contains the root paths
+   *   keyed by the corresponding namespace to look for plugin implementations.
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
+   *   Cache backend instance to use.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler to invoke the alter hook with.
+   */
+  public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
+    parent::__construct('Plugin/libraries/Type', $namespaces, $module_handler, LibraryTypeInterface::class, LibraryType::class);
+    $this->alterInfo('libraries_library_type_info');
+    $this->setCacheBackend($cache_backend, 'libraries_library_type_info');
+  }
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Type/LibraryTypeInterface.php b/web/modules/libraries/src/ExternalLibrary/Type/LibraryTypeInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..d6537fb370f7b4b58afdba199e9a031e30aee8cd
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Type/LibraryTypeInterface.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Type;
+
+/**
+ * Provides an interface for library types.
+ */
+interface LibraryTypeInterface {
+
+  /**
+   * Returns the ID of the library type.
+   *
+   * @return string
+   *   The library type ID.
+   */
+  public function getId();
+
+  /**
+   * Returns the class used for libraries of this type.
+   *
+   * @return string|\Drupal\libraries\ExternalLibrary\LibraryInterface
+   *   The library class for this library type.
+   *
+   * @todo Consider adding a getLibraryInterface() method, as well.
+   */
+  public function getLibraryClass();
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Utility/DependencyAccessorTrait.php b/web/modules/libraries/src/ExternalLibrary/Utility/DependencyAccessorTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..3ec56fee7306660996ee4bf29894ade02ab78397
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Utility/DependencyAccessorTrait.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Utility;
+
+/**
+ * Provides a trait for classes giving access to a library dependency.
+ */
+trait DependencyAccessorTrait {
+
+  /**
+   * The dependency.
+   *
+   * @var \Drupal\libraries\ExternalLibrary\LibraryInterface
+   */
+  protected $dependency;
+
+  /**
+   * Returns the dependency.
+   *
+   * @return \Drupal\libraries\ExternalLibrary\LibraryInterface
+   *   The library.
+   */
+  public function getLibrary() {
+    return $this->dependency;
+  }
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Utility/IdAccessorTrait.php b/web/modules/libraries/src/ExternalLibrary/Utility/IdAccessorTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..34476f2cc4cdae7429eade5694b6214a214b3fe5
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Utility/IdAccessorTrait.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Utility;
+
+/**
+ * Provides a trait for classes that have a string identifier.
+ */
+trait IdAccessorTrait {
+
+  /**
+   * The ID.
+   *
+   * @var string
+   */
+  protected $id;
+
+  /**
+   * Returns the ID.
+   *
+   * @return string
+   *   The ID.
+   *
+   * @see \Drupal\libraries\ExternalLibrary\LibraryInterface::getId()
+   * @see \Drupal\libraries\ExternalLibrary\LibraryType\LibraryTypeInterface::getId()
+   */
+  public function getId() {
+    return $this->id;
+  }
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Utility/LibraryAccessorInterface.php b/web/modules/libraries/src/ExternalLibrary/Utility/LibraryAccessorInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..72b0093c4dcb732ae8f4d21aa03a320408cd9e22
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Utility/LibraryAccessorInterface.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Utility;
+
+/**
+ * Provides an interface for classes giving access to a library.
+ */
+interface LibraryAccessorInterface {
+
+  /**
+   * Returns the library.
+   *
+   * @return \Drupal\libraries\ExternalLibrary\LibraryInterface
+   *   The library.
+   */
+  public function getLibrary();
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Utility/LibraryAccessorTrait.php b/web/modules/libraries/src/ExternalLibrary/Utility/LibraryAccessorTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..f8ad92037d479a4167414fb601581151a155fad3
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Utility/LibraryAccessorTrait.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Utility;
+
+/**
+ * Provides a trait for classes giving access to a library.
+ */
+trait LibraryAccessorTrait {
+
+  /**
+   * The library.
+   *
+   * @var \Drupal\libraries\ExternalLibrary\LibraryInterface
+   */
+  protected $library;
+
+  /**
+   * Returns the library.
+   *
+   * @return \Drupal\libraries\ExternalLibrary\LibraryInterface
+   *   The library.
+   */
+  public function getLibrary() {
+    return $this->library;
+  }
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Utility/LibraryIdAccessorInterface.php b/web/modules/libraries/src/ExternalLibrary/Utility/LibraryIdAccessorInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..98928e9deccd67a28b7734199456885bf43087c0
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Utility/LibraryIdAccessorInterface.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Utility;
+
+/**
+ * Provides an interface for classes giving access to a library ID.
+ */
+interface LibraryAccessorIdInterface {
+
+  /**
+   * Returns the ID of the library.
+   *
+   * @return string
+   *   The library ID.
+   */
+  public function getLibraryId();
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Utility/LibraryIdAccessorTrait.php b/web/modules/libraries/src/ExternalLibrary/Utility/LibraryIdAccessorTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..8ec901f534432cc6f2f2e7ed42f5a653ddb4b243
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Utility/LibraryIdAccessorTrait.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Utility;
+
+/**
+ * Provides a trait for classes giving access to a library ID.
+ */
+trait LibraryIdAccessorTrait {
+
+  /**
+   * The ID of the library.
+   *
+   * @var string
+   */
+  protected $libraryId;
+
+  /**
+   * Returns the ID of the library.
+   *
+   * @return string
+   *   The library ID.
+   */
+  public function getLibraryId() {
+    return $this->libraryId;
+  }
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Version/VersionDetectorInterface.php b/web/modules/libraries/src/ExternalLibrary/Version/VersionDetectorInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..8b6d2a5bd04a2661ce569aeca00e78ae3d635295
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Version/VersionDetectorInterface.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Version;
+
+/**
+ * Provides an interface for version detectors.
+ *
+ * @ingroup libraries
+ */
+interface VersionDetectorInterface {
+
+  /**
+   * Detects the version of a library.
+   *
+   * @param \Drupal\libraries\ExternalLibrary\Version\VersionedLibraryInterface $library
+   *   The library whose version to detect.
+   *
+   * @throws \Drupal\libraries\ExternalLibrary\Exception\UnknownLibraryVersionException
+   *
+   * @todo Provide a mechanism for version detectors to provide a reason for
+   *   failing.
+   */
+  public function detectVersion(VersionedLibraryInterface $library);
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Version/VersionDetectorManager.php b/web/modules/libraries/src/ExternalLibrary/Version/VersionDetectorManager.php
new file mode 100644
index 0000000000000000000000000000000000000000..6272c5a263c8b5fb7a5ec6e32a7e052d873187d9
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Version/VersionDetectorManager.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Version;
+
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Plugin\DefaultPluginManager;
+use Drupal\libraries\Annotation\VersionDetector;
+
+/**
+ * Provides a plugin manager for library version detector plugins.
+ *
+ * @see \Drupal\libraries\ExternalLibrary\Version\VersionDetectorInterface
+ */
+class VersionDetectorManager extends DefaultPluginManager {
+
+  /**
+   * Constructs a version detector manager.
+   *
+   * @param \Traversable $namespaces
+   *   An object that implements \Traversable which contains the root paths
+   *   keyed by the corresponding namespace to look for plugin implementations.
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
+   *   Cache backend instance to use.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler to invoke the alter hook with.
+   */
+  public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
+    parent::__construct('Plugin/libraries/VersionDetector', $namespaces, $module_handler, VersionDetectorInterface::class, VersionDetector::class);
+    $this->alterInfo('libraries_version_detector_info');
+    $this->setCacheBackend($cache_backend, 'libraries_version_detector_info');
+  }
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Version/VersionedLibraryInterface.php b/web/modules/libraries/src/ExternalLibrary/Version/VersionedLibraryInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..abeb758cd2974a3a2a50b94d1eef673c54a86d95
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Version/VersionedLibraryInterface.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Version;
+
+use Drupal\Component\Plugin\Factory\FactoryInterface;
+use Drupal\libraries\ExternalLibrary\LibraryInterface;
+
+/**
+ * Provides an interface for versioned libraries.
+ *
+ * Version detection and negotiation is a key aspect of Libraries API's
+ * functionality so most libraries should implement this interface. In theory,
+ * however, it might be possible for the same library to be available in
+ * multiple versions and, for example, different versions being loaded on
+ * different pages. In this case, a simple getVersion() method, does not make
+ * sense. To support such advanced version detection behavior in the future or
+ * in a separate module, version detection is split into a separate interface.
+ *
+ * @ingroup libraries
+ *
+ * @todo Support versioned metadata, i.e. different library file names or
+ *   locations for different library versions.
+ */
+interface VersionedLibraryInterface extends LibraryInterface {
+
+  /**
+   * Gets the version of the library.
+   *
+   * @return string
+   *   The version string, for example 1.0, 2.1.4, or 3.0.0-alpha5.
+   *
+   * @throws \Drupal\libraries\ExternalLibrary\Exception\UnknownLibraryVersionException
+   *
+   * @see \Drupal\libraries\ExternalLibrary\Version\VersionedLibraryInterface::setVersion()
+   */
+  public function getVersion();
+
+  /**
+   * Sets the version of the library.
+   *
+   * @param string $version
+   *   The version of the library.
+   *
+   * @reutrn $this
+   *
+   * @see \Drupal\libraries\ExternalLibrary\Version\VersionedLibraryInterface::getVersion()
+   */
+  public function setVersion($version);
+
+  /**
+   * Gets the version detector of this library using the detector factory.
+   *
+   * Because determining the installation version of a library is not specific
+   * to any library or even any library type, this logic is offloaded to
+   * separate detector objects.
+   *
+   * @param \Drupal\Component\Plugin\Factory\FactoryInterface $detector_factory
+   *
+   * @return \Drupal\libraries\ExternalLibrary\Version\VersionDetectorInterface
+   *
+   * @see \Drupal\libraries\ExternalLibrary\Version\VersionDetectorInterface
+   */
+  public function getVersionDetector(FactoryInterface $detector_factory);
+
+}
diff --git a/web/modules/libraries/src/ExternalLibrary/Version/VersionedLibraryTrait.php b/web/modules/libraries/src/ExternalLibrary/Version/VersionedLibraryTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..2404a109ca29f6506672b3c9da9d8a1fd86bcb4e
--- /dev/null
+++ b/web/modules/libraries/src/ExternalLibrary/Version/VersionedLibraryTrait.php
@@ -0,0 +1,85 @@
+<?php
+
+namespace Drupal\libraries\ExternalLibrary\Version;
+
+use Drupal\Component\Plugin\Factory\FactoryInterface;
+use Drupal\libraries\ExternalLibrary\Exception\UnknownLibraryVersionException;
+
+/**
+ * Provides a trait for versioned libraries.
+ *
+ * @see \Drupal\libraries\ExternalLibrary\Version\VersionedLibraryInterface
+ */
+trait VersionedLibraryTrait {
+
+  /**
+   * The library version.
+   *
+   * @var string
+   */
+  protected $version;
+
+  /**
+   * Information about the version detector to use fo rthis library.
+   *
+   * Contains the following keys:
+   * id: The plugin ID of the version detector.
+   * configuration: The plugin configuration of the version detector.
+   *
+   * @var array
+   */
+  protected $versionDetector = [
+    'id' => NULL,
+    'configuration' => [],
+  ];
+
+  /**
+   * Gets the version of the library.
+   *
+   * @return string
+   *   The version string, for example 1.0, 2.1.4, or 3.0.0-alpha5.
+   *
+   * @throws \Drupal\libraries\ExternalLibrary\Exception\UnknownLibraryVersionException
+   *
+   * @see \Drupal\libraries\ExternalLibrary\Version\VersionedLibraryInterface::getVersion()
+   */
+  public function getVersion() {
+    if (!isset($this->version)) {
+      throw new UnknownLibraryVersionException($this);
+    }
+    return $this->version;
+  }
+
+  /**
+   * Sets the version of the library.
+   *
+   * @param string $version
+   *   The version of the library.
+   *
+   * @return $this
+   *
+   * @see \Drupal\libraries\ExternalLibrary\Version\VersionedLibraryInterface::setVersion()
+   */
+  public function setVersion($version) {
+    $this->version = (string) $version;
+    return $this;
+  }
+
+  /**
+   * Gets the version detector of this library using the detector factory.
+   *
+   * Because determining the installation version of a library is not specific
+   * to any library or even any library type, this logic is offloaded to
+   * separate detector objects.
+   *
+   * @param \Drupal\Component\Plugin\Factory\FactoryInterface $detector_factory
+   *
+   * @return \Drupal\libraries\ExternalLibrary\Version\VersionDetectorInterface
+   *
+   * @see \Drupal\libraries\ExternalLibrary\Version\VersionDetectorInterface
+   */
+  public function getVersionDetector(FactoryInterface $detector_factory) {
+    return $detector_factory->createInstance($this->versionDetector['id'], $this->versionDetector['configuration']);
+  }
+
+}
diff --git a/web/modules/libraries/src/Plugin/MissingPluginConfigurationException.php b/web/modules/libraries/src/Plugin/MissingPluginConfigurationException.php
new file mode 100644
index 0000000000000000000000000000000000000000..515f133405ffb4e29ab8607c74e0eb288bc43af4
--- /dev/null
+++ b/web/modules/libraries/src/Plugin/MissingPluginConfigurationException.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace Drupal\libraries\Plugin;
+
+use Drupal\Component\Plugin\Exception\PluginException;
+
+/**
+ * Provides an exception class for missing plugin configuration.
+ *
+ * The plugin system allows passing arbitrary data to plugins in form of the
+ * $configuration array. Some plugins, however, may depend on certain keys to
+ * be present in $configuration. This exception class can be used if such keys
+ * are missing.
+ *
+ * @todo Provide accessors for the passed-in information.
+ */
+class MissingPluginConfigurationException extends PluginException {
+
+  /**
+   * Constructs an exception for a missing plugin configuration value.
+   *
+   * @param string $plugin_id
+   *   The plugin ID.
+   * @param $plugin_definition
+   *   The plugin definition
+   * @param array $configuration
+   *   The plugin configuration.
+   * @param $missing_key
+   *   The missing key in the configuration.
+   * @param string $message
+   *   (optional) The exception message.
+   * @param int $code
+   *   (optional) The error code.
+   * @param \Exception $previous
+   *   (optional) The previous exception.
+   */
+  public function __construct(
+    $plugin_id,
+    $plugin_definition,
+    array $configuration,
+    $missing_key,
+    $message = '',
+    $code = 0,
+    \Exception $previous = NULL
+  ) {
+    $message = $message ?: "The '{$missing_key}' key is missing in the configuration of the '{$plugin_id}' plugin.";
+    parent::__construct($message, $code, $previous);
+  }
+
+}
diff --git a/web/modules/libraries/src/Plugin/libraries/Locator/ChainLocator.php b/web/modules/libraries/src/Plugin/libraries/Locator/ChainLocator.php
new file mode 100644
index 0000000000000000000000000000000000000000..4e0acff84e1630c44e5552e2114e32b7eb9538de
--- /dev/null
+++ b/web/modules/libraries/src/Plugin/libraries/Locator/ChainLocator.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace Drupal\libraries\Plugin\libraries\Locator;
+
+use Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface;
+use Drupal\libraries\ExternalLibrary\Local\LocatorInterface;
+
+/**
+ * Provides a locator utilizing a chain of other individual locators.
+ *
+ * @Locator("chain")
+ *
+ * @see \Drupal\libraries\ExternalLibrary\Local\LocatorInterface
+ */
+class ChainLocator implements LocatorInterface {
+
+  /**
+   * The locators to check.
+   *
+   * @var \Drupal\libraries\ExternalLibrary\Local\LocatorInterface[]
+   */
+  protected $locators = [];
+
+  /**
+   * Add a locator to the chain.
+   *
+   * @param \Drupal\libraries\ExternalLibrary\Local\LocatorInterface $locator
+   *   A locator to add to the chain.
+   */
+  public function addLocator(LocatorInterface $locator) {
+    $this->locators[] = $locator;
+    return $this;
+  }
+
+  /**
+   * Locates a library.
+   *
+   * @param \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface $library
+   *   The library to locate.
+   *
+   * @see \Drupal\libraries\ExternalLibrary\Local\LocatorInterface::locate()
+   */
+  public function locate(LocalLibraryInterface $library) {
+    foreach ($this->locators as $locator) {
+      $locator->locate($library);
+      if ($library->isInstalled()) {
+        return;
+      }
+    }
+    $library->setUninstalled();
+  }
+
+}
diff --git a/web/modules/libraries/src/Plugin/libraries/Locator/GlobalLocator.php b/web/modules/libraries/src/Plugin/libraries/Locator/GlobalLocator.php
new file mode 100644
index 0000000000000000000000000000000000000000..3501338d4f66133e9c2d49ecb32ec9b70ef45650
--- /dev/null
+++ b/web/modules/libraries/src/Plugin/libraries/Locator/GlobalLocator.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace Drupal\libraries\Plugin\libraries\Locator;
+
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Component\Plugin\Factory\FactoryInterface;
+use Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface;
+use Drupal\libraries\ExternalLibrary\Local\LocatorInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides a locator based on global configuration.
+ *
+ * @Locator("global")
+ *
+ * @see \Drupal\libraries\ExternalLibrary\Local\LocatorInterface
+ */
+class GlobalLocator implements LocatorInterface, ContainerFactoryPluginInterface {
+
+  /**
+   * The Drupal config factory service.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
+  /**
+   * The locator factory.
+   *
+   * @var \Drupal\Component\Plugin\Factory\FactoryInterface
+   */
+  protected $locatorFactory;
+
+  /**
+   * Constructs a global locator.
+   *
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   *   The Drupal config factory service.
+   * @param \Drupal\Component\Plugin\Factory\FactoryInterface $locator_factory
+   *   The locator factory.
+   */
+  public function __construct(ConfigFactoryInterface $config_factory, FactoryInterface $locator_factory) {
+    $this->configFactory = $config_factory;
+    $this->locatorFactory = $locator_factory;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $container->get('config.factory'),
+      $container->get('plugin.manager.libraries.locator')
+    );
+  }
+
+  /**
+   * Locates a library.
+   *
+   * @param \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface $library
+   *   The library to locate.
+   *
+   * @see \Drupal\libraries\ExternalLibrary\Local\LocatorInterface::locate()
+   */
+  public function locate(LocalLibraryInterface $library) {
+    foreach ($this->configFactory->get('libraries.settings')->get('global_locators') as $locator) {
+      $this->locatorFactory->createInstance($locator['id'], $locator['configuration'])->locate($library);
+      if ($library->isInstalled()) {
+        return;
+      }
+    }
+    $library->setUninstalled();
+  }
+
+}
diff --git a/web/modules/libraries/src/Plugin/libraries/Locator/UriLocator.php b/web/modules/libraries/src/Plugin/libraries/Locator/UriLocator.php
new file mode 100644
index 0000000000000000000000000000000000000000..348be0c247c8aec40cbba68e28c132437b9c0938
--- /dev/null
+++ b/web/modules/libraries/src/Plugin/libraries/Locator/UriLocator.php
@@ -0,0 +1,89 @@
+<?php
+
+namespace Drupal\libraries\Plugin\libraries\Locator;
+
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\StreamWrapper\StreamWrapperManagerInterface;
+use Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface;
+use Drupal\libraries\ExternalLibrary\Local\LocatorInterface;
+use Drupal\libraries\Plugin\MissingPluginConfigurationException;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides a locator utilizing a URI.
+ *
+ * It makes the following assumptions:
+ * - The library files can be accessed using a specified stream.
+ * - The stream wrapper is local (i.e. it is a subclass of
+ *   \Drupal\Core\StreamWrapper\LocalStream).
+ * - The first component of the file URIs are the library IDs (i.e. file URIs
+ *   are of the form: scheme://library-id/path/to/file/filename).
+ *
+ * @Locator("uri")
+ *
+ * @see \Drupal\libraries\ExternalLibrary\Local\LocatorInterface
+ */
+class UriLocator implements LocatorInterface, ContainerFactoryPluginInterface {
+
+  /**
+   * The stream wrapper manager.
+   *
+   * @var \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface
+   */
+  protected $streamWrapperManager;
+
+  /**
+   * The URI to check.
+   *
+   * @var string
+   */
+  protected $uri;
+
+  /**
+   * Constructs a URI locator.
+   *
+   * @param \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface $stream_wrapper_manager
+   *   The stream wrapper manager.
+   * @param string $uri
+   *   The URI to check.
+   */
+  public function __construct(StreamWrapperManagerInterface $stream_wrapper_manager, $uri) {
+    $this->streamWrapperManager = $stream_wrapper_manager;
+    $this->uri = (string) $uri;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    if (!isset($configuration['uri'])) {
+      throw new MissingPluginConfigurationException($plugin_id, $plugin_definition, $configuration, 'uri');
+    }
+    return new static($container->get('stream_wrapper_manager'), $configuration['uri']);
+  }
+
+  /**
+   * Locates a library.
+   *
+   * @param \Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface $library
+   *   The library to locate.
+   *
+   * @see \Drupal\libraries\ExternalLibrary\Local\LocatorInterface::locate()
+   */
+  public function locate(LocalLibraryInterface $library) {
+    /** @var \Drupal\Core\StreamWrapper\LocalStream $stream_wrapper */
+    $stream_wrapper = $this->streamWrapperManager->getViaUri($this->uri);
+    assert('$stream_wrapper instanceof \Drupal\Core\StreamWrapper\LocalStream');
+    // Calling LocalStream::getDirectoryPath() explicitly avoids the realpath()
+    // usage in LocalStream::getLocalPath(), which breaks if Libraries API is
+    // symbolically linked into the Drupal installation.
+    list($scheme, $target) = explode('://', $this->uri, 2);
+    $base_path = str_replace('//', '/', $stream_wrapper->getDirectoryPath() . '/' . $target . '/' . $library->getId());
+    if (is_dir($base_path) && is_readable($base_path)) {
+      $library->setLocalPath($base_path);
+      return;
+    }
+    $library->setUninstalled();
+  }
+
+}
diff --git a/web/modules/libraries/src/Plugin/libraries/Type/AssetLibraryType.php b/web/modules/libraries/src/Plugin/libraries/Type/AssetLibraryType.php
new file mode 100644
index 0000000000000000000000000000000000000000..ce0b276b17eb58aa46789392ae6eb6fb92e1c866
--- /dev/null
+++ b/web/modules/libraries/src/Plugin/libraries/Type/AssetLibraryType.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace Drupal\libraries\Plugin\libraries\Type;
+
+use Drupal\libraries\ExternalLibrary\Asset\AssetLibrary;
+use Drupal\libraries\ExternalLibrary\Asset\AttachableAssetLibraryRegistrationInterface;
+use Drupal\libraries\ExternalLibrary\LibraryInterface;
+use Drupal\libraries\ExternalLibrary\LibraryManagerInterface;
+use Drupal\libraries\ExternalLibrary\Type\LibraryTypeBase;
+
+/**
+ * @LibraryType("asset")
+ */
+class AssetLibraryType extends LibraryTypeBase implements AttachableAssetLibraryRegistrationInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLibraryClass() {
+    return AssetLibrary::class;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAttachableAssetLibraries(LibraryInterface $library, LibraryManagerInterface $library_manager) {
+    assert('$library instanceof \Drupal\libraries\ExternalLibrary\Asset\AssetLibraryInterface');
+    /** @var \Drupal\libraries\ExternalLibrary\Asset\AssetLibraryInterface $library */
+    return [$library->getId() => $library->getAttachableAssetLibrary($library_manager)];
+  }
+
+}
diff --git a/web/modules/libraries/src/Plugin/libraries/Type/MultipleAssetLibraryType.php b/web/modules/libraries/src/Plugin/libraries/Type/MultipleAssetLibraryType.php
new file mode 100644
index 0000000000000000000000000000000000000000..31ec6a62c6c41aace30735496788f4f85118900d
--- /dev/null
+++ b/web/modules/libraries/src/Plugin/libraries/Type/MultipleAssetLibraryType.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace Drupal\libraries\Plugin\libraries\Type;
+
+use Drupal\libraries\ExternalLibrary\Asset\AttachableAssetLibraryRegistrationInterface;
+use Drupal\libraries\ExternalLibrary\Asset\MultipleAssetLibrary;
+use Drupal\libraries\ExternalLibrary\Asset\MultipleAssetLibraryInterface;
+use Drupal\libraries\ExternalLibrary\LibraryInterface;
+use Drupal\libraries\ExternalLibrary\LibraryManagerInterface;
+use Drupal\libraries\ExternalLibrary\Type\LibraryTypeBase;
+
+/**
+ * @LibraryType("asset_multiple")
+ */
+class MultipleAssetLibraryType extends LibraryTypeBase implements AttachableAssetLibraryRegistrationInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLibraryClass() {
+    return MultipleAssetLibrary::class;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAttachableAssetLibraries(LibraryInterface $external_library, LibraryManagerInterface $library_manager) {
+    assert('$external_library instanceof \Drupal\libraries\ExternalLibrary\Asset\MultipleAssetLibraryInterface');
+    /** @var \Drupal\libraries\ExternalLibrary\Asset\MultipleAssetLibraryInterface $external_library */
+    $attachable_libraries = [];
+    foreach ($external_library->getAttachableAssetLibraries($library_manager) as $component_name => $attachable_library) {
+      $attachable_library_id = $this->getAttachableLibraryId($external_library, $component_name);
+      $attachable_libraries[$attachable_library_id] = $attachable_library;
+    }
+    return $attachable_libraries;
+  }
+
+  /**
+   * @param \Drupal\libraries\ExternalLibrary\LibraryInterface $external_library
+   * @param string $component_name
+   *
+   * @return string
+   */
+  protected function getAttachableLibraryId(LibraryInterface $external_library, $component_name) {
+    return $external_library->getId() . MultipleAssetLibraryInterface::SEPARATOR . $component_name;
+  }
+
+}
diff --git a/web/modules/libraries/src/Plugin/libraries/Type/PhpFileLibraryType.php b/web/modules/libraries/src/Plugin/libraries/Type/PhpFileLibraryType.php
new file mode 100644
index 0000000000000000000000000000000000000000..aa93b003c2976c84935cb04afb770a8b88c22d33
--- /dev/null
+++ b/web/modules/libraries/src/Plugin/libraries/Type/PhpFileLibraryType.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace Drupal\libraries\Plugin\libraries\Type;
+
+use Drupal\Component\Plugin\Factory\FactoryInterface;
+use Drupal\libraries\ExternalLibrary\LibraryInterface;
+use Drupal\libraries\ExternalLibrary\Type\LibraryLoadingListenerInterface;
+use Drupal\libraries\ExternalLibrary\PhpFile\PhpFileLibrary;
+use Drupal\libraries\ExternalLibrary\PhpFile\PhpFileLoaderInterface;
+use Drupal\libraries\ExternalLibrary\Type\LibraryTypeBase;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * @LibraryType("php_file")
+ */
+class PhpFileLibraryType extends LibraryTypeBase implements LibraryLoadingListenerInterface {
+
+  /**
+   * The PHP file loader.
+   *
+   * @var \Drupal\libraries\ExternalLibrary\PhpFile\PhpFileLoaderInterface
+   */
+  protected $phpFileLoader;
+
+  /**
+   * Constructs the PHP file library type.
+   *
+   * @param string $plugin_id
+   *   The plugin ID taken from the class annotation.
+   * @param \Drupal\Component\Plugin\Factory\FactoryInterface $locator_factory
+   *   The locator factory.
+   * @param \Drupal\Component\Plugin\Factory\FactoryInterface $detector_factory
+   *   The version detector factory.
+   * @param \Drupal\libraries\ExternalLibrary\PhpFile\PhpFileLoaderInterface $php_file_loader
+   *   The PHP file loader.
+   */
+  public function __construct($plugin_id, FactoryInterface $locator_factory, FactoryInterface $detector_factory, PhpFileLoaderInterface $php_file_loader) {
+    parent::__construct($plugin_id, $locator_factory, $detector_factory);
+    $this->phpFileLoader = $php_file_loader;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $plugin_id,
+      $container->get('plugin.manager.libraries.locator'),
+      $container->get('plugin.manager.libraries.version_detector'),
+      $container->get('libraries.php_file_loader')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLibraryClass() {
+    return PhpFileLibrary::class;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function onLibraryLoad(LibraryInterface $library) {
+    /** @var \Drupal\libraries\ExternalLibrary\PhpFile\PhpFileLibraryInterface $library */
+    // @todo Prevent loading a library multiple times.
+    foreach ($library->getPhpFiles() as $file) {
+      $this->phpFileLoader->load($file);
+    }
+  }
+
+}
diff --git a/web/modules/libraries/src/Plugin/libraries/VersionDetector/LinePatternDetector.php b/web/modules/libraries/src/Plugin/libraries/VersionDetector/LinePatternDetector.php
new file mode 100644
index 0000000000000000000000000000000000000000..25a799c536d344e105400274e6ce2de5924a60d5
--- /dev/null
+++ b/web/modules/libraries/src/Plugin/libraries/VersionDetector/LinePatternDetector.php
@@ -0,0 +1,89 @@
+<?php
+
+namespace Drupal\libraries\Plugin\libraries\VersionDetector;
+
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\Plugin\PluginBase;
+use Drupal\libraries\ExternalLibrary\Exception\UnknownLibraryVersionException;
+use Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface;
+use Drupal\libraries\ExternalLibrary\Version\VersionDetectorInterface;
+use Drupal\libraries\ExternalLibrary\Version\VersionedLibraryInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Detects the version by matching lines in a file against a specified pattern.
+ *
+ * This version detector can be used if the library version is denoted in a
+ * particular format in a changelog or readme file, for example.
+ *
+ * @VersionDetector("line_pattern")
+ *
+ * @ingroup libraries
+ */
+class LinePatternDetector extends PluginBase implements VersionDetectorInterface, ContainerFactoryPluginInterface {
+
+  /**
+   * The app root.
+   *
+   * @var string
+   */
+  protected $appRoot;
+
+  /**
+   * Constructs a line pattern version detector.
+   *
+   * @param array $configuration
+   * @param string $plugin_id
+   * @param array $plugin_definition
+   * @param string $app_root
+   */
+  public function __construct(array $configuration, $plugin_id, array $plugin_definition, $app_root) {
+    $configuration += [
+      'file' => '',
+      'pattern' => '',
+      'lines' => 20,
+      'columns' => 200,
+    ];
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->appRoot = $app_root;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('app.root')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function detectVersion(VersionedLibraryInterface $library) {
+    if (!($library instanceof LocalLibraryInterface)) {
+      throw new UnknownLibraryVersionException($library);
+    }
+
+    $filepath = $this->appRoot . '/' . $library->getLocalPath() . '/' . $this->configuration['file'];
+    if (!file_exists($filepath)) {
+      throw new UnknownLibraryVersionException($library);
+    }
+
+    $file = fopen($filepath, 'r');
+    $lines = $this->configuration['lines'];
+    while ($lines && $line = fgets($file, $this->configuration['columns'])) {
+      if (preg_match($this->configuration['pattern'], $line, $version)) {
+        fclose($file);
+        $library->setVersion($version[1]);
+        return;
+      }
+      $lines--;
+    }
+    fclose($file);
+  }
+
+}
diff --git a/web/modules/libraries/src/Plugin/libraries/VersionDetector/StaticDetector.php b/web/modules/libraries/src/Plugin/libraries/VersionDetector/StaticDetector.php
new file mode 100644
index 0000000000000000000000000000000000000000..ed697de50b07452f1178adbdd12631657130b5c7
--- /dev/null
+++ b/web/modules/libraries/src/Plugin/libraries/VersionDetector/StaticDetector.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace Drupal\libraries\Plugin\libraries\VersionDetector;
+
+use Drupal\Core\Plugin\PluginBase;
+use Drupal\libraries\ExternalLibrary\Exception\UnknownLibraryVersionException;
+use Drupal\libraries\ExternalLibrary\Version\VersionDetectorInterface;
+use Drupal\libraries\ExternalLibrary\Version\VersionedLibraryInterface;
+
+/**
+ * Detects the version by returning a static string.
+ *
+ * As this does not perform any actual detection and, thus, circumvents any
+ * negotiation of versions by Libraries API it should only be used for testing
+ * or when the version of a library cannot be determined from the source code
+ * itself.
+ *
+ * @VersionDetector("static")
+ */
+class StaticDetector extends PluginBase implements VersionDetectorInterface {
+
+  /**
+   * Constructs a static version detector.
+   *
+   * @param array $configuration
+   * @param string $plugin_id
+   * @param array $plugin_definition
+   */
+  public function __construct(array $configuration, $plugin_id, array $plugin_definition) {
+    $configuration += [
+      'version' => NULL,
+    ];
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function detectVersion(VersionedLibraryInterface $library) {
+    if (!isset($this->configuration['version'])) {
+      throw new UnknownLibraryVersionException($library);
+    }
+    $library->setVersion($this->configuration['version']);
+  }
+
+}
diff --git a/web/modules/libraries/src/StreamWrapper/AssetLibrariesStream.php b/web/modules/libraries/src/StreamWrapper/AssetLibrariesStream.php
new file mode 100644
index 0000000000000000000000000000000000000000..54893a7d04a82d53365f0afb6a0348c852e0903b
--- /dev/null
+++ b/web/modules/libraries/src/StreamWrapper/AssetLibrariesStream.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace Drupal\libraries\StreamWrapper;
+
+use Drupal\Core\StreamWrapper\LocalStream;
+
+/**
+ * Provides a stream wrapper for asset libraries.
+ *
+ * Can be used with the 'asset://' scheme, for example
+ * 'asset://jquery/jquery.js'.
+ */
+class AssetLibrariesStream extends LocalStream {
+
+  use LocalHiddenStreamTrait;
+  use PrivateStreamTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getName() {
+    return t('Assets');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDescription() {
+    return t('Provides access to asset library files.');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDirectoryPath() {
+    // @todo Provide support for site-specific directories, etc.
+    return 'sites/all/assets/vendor';
+  }
+
+}
diff --git a/web/modules/libraries/src/StreamWrapper/LibraryDefinitionsStream.php b/web/modules/libraries/src/StreamWrapper/LibraryDefinitionsStream.php
new file mode 100644
index 0000000000000000000000000000000000000000..7cf0764ec0c75d3cd9cc2d0384c15f395c2811cd
--- /dev/null
+++ b/web/modules/libraries/src/StreamWrapper/LibraryDefinitionsStream.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace Drupal\libraries\StreamWrapper;
+
+use Drupal\Core\StreamWrapper\LocalStream;
+
+/**
+ * Provides a stream wrapper for library definitions.
+ *
+ * Can be used with the 'library-definitions' scheme, for example
+ * 'library-definitions://example.json' for a library ID of 'example'.
+ *
+ * By default this stream wrapper reads from a single directory that is
+ * configurable and points to the 'library-definitions' directory within the
+ * public files directory by default. This makes library definitions writable
+ * by the webserver by default, which is in anticipation of a user interface
+ * that fetches definitions from a remote repository and stores them locally.
+ * For improved security the library definitions can be managed manually (or put
+ * under version control) and placed in a directory that is not writable by the
+ * webserver.
+ *
+ * The idea of using a stream wrapper for this as well as the default location
+ * is taken from the 'translations' stream wrapper provided by the Interface
+ * Translation module.
+ *
+ * @see \Drupal\locale\StreamWrapper\TranslationsStream
+ *
+ * @todo Use a setting instead of configuration for the directory.
+ */
+class LibraryDefinitionsStream extends LocalStream {
+
+  use LocalHiddenStreamTrait;
+  use PrivateStreamTrait;
+
+  /**
+   * The config factory
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
+  /**
+   * Constructs an external library registry.
+   *
+   * @todo Dependency injection.
+   */
+  public function __construct() {
+    $this->configFactory = \Drupal::configFactory();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getName() {
+    return t('Library definitions');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDescription() {
+    return t('Provides access to library definition files.');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDirectoryPath() {
+    return $this->getConfig('local.path');
+  }
+
+  /**
+   * Fetches a configuration value from the library definitions configuration.
+   * @param $key
+   *   The configuration key to fetch.
+   *
+   * @return array|mixed|null
+   *   The configuration value.
+   */
+  protected function getConfig($key) {
+    return $this->configFactory
+      ->get('libraries.settings')
+      ->get("definitions.$key");
+  }
+
+}
diff --git a/web/modules/libraries/src/StreamWrapper/LocalHiddenStreamTrait.php b/web/modules/libraries/src/StreamWrapper/LocalHiddenStreamTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..f71c0de99c735dc3cae2c0062c8f78e5267602c1
--- /dev/null
+++ b/web/modules/libraries/src/StreamWrapper/LocalHiddenStreamTrait.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Drupal\libraries\StreamWrapper;
+
+use Drupal\Core\StreamWrapper\StreamWrapperInterface;
+
+/**
+ * Provides a trait for local hidden streams.
+ */
+trait LocalHiddenStreamTrait {
+
+  /**
+   * Returns the type of stream wrapper.
+   *
+   * @return int
+   *
+   * @see \Drupal\Core\StreamWrapper\StreamWrapperInterface::getType()
+   */
+  public static function getType() {
+    return StreamWrapperInterface::LOCAL_HIDDEN;
+  }
+
+}
diff --git a/web/modules/libraries/src/StreamWrapper/PhpFileLibrariesStream.php b/web/modules/libraries/src/StreamWrapper/PhpFileLibrariesStream.php
new file mode 100644
index 0000000000000000000000000000000000000000..a4933858e106d5f63129f2c3956420abe82ff2e1
--- /dev/null
+++ b/web/modules/libraries/src/StreamWrapper/PhpFileLibrariesStream.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace Drupal\libraries\StreamWrapper;
+
+use Drupal\Core\StreamWrapper\LocalStream;
+
+/**
+ * Provides a stream wrapper for PHP file libraries.
+ *
+ * Can be used with the 'php-file://' scheme, for example
+ * 'php-file-library://guzzle/src/functions_include.php'.
+ */
+class PhpFileLibrariesStream extends LocalStream {
+
+  use LocalHiddenStreamTrait;
+  use PrivateStreamTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getName() {
+    return t('PHP library files');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDescription() {
+    return t('Provides access to PHP library files.');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDirectoryPath() {
+    // @todo Provide support for site-specific directories, etc.
+    return 'sites/all/libraries';
+  }
+
+}
diff --git a/web/modules/libraries/src/StreamWrapper/PrivateStreamTrait.php b/web/modules/libraries/src/StreamWrapper/PrivateStreamTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..4690551b9bcb26de73b42c9bf9ffb5a59bde3d9b
--- /dev/null
+++ b/web/modules/libraries/src/StreamWrapper/PrivateStreamTrait.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace Drupal\libraries\StreamWrapper;
+
+/**
+ * Provides a trait for local streams that are not publicly accessible.
+ *
+ * @see \Drupal\locale\StreamWrapper\TranslationsStream
+ */
+trait PrivateStreamTrait {
+
+  /**
+   * Returns a web accessible URL for the resource.
+   *
+   * This function should return a URL that can be embedded in a web page
+   * and accessed from a browser. For example, the external URL of
+   * "youtube://xIpLd0WQKCY" might be
+   * "http://www.youtube.com/watch?v=xIpLd0WQKCY".
+   *
+   * @return string
+   *   Returns a string containing a web accessible URL for the resource.
+   *
+   * @see \Drupal\Core\StreamWrapper\StreamWrapperInterface::getExternalUrl()
+   */
+  function getExternalUrl() {
+    throw new \LogicException("{$this->getName()} should not be public.");
+  }
+
+  /**
+   * Returns the name of the stream wrapper for use in the UI.
+   *
+   * @return string
+   *   The stream wrapper name.
+   *
+   * @see \Drupal\Core\StreamWrapper\StreamWrapperInterface::getName()
+   */
+  abstract public function getName();
+
+}
diff --git a/web/modules/libraries/src/Tests/LibrariesUnitTest.php b/web/modules/libraries/src/Tests/LibrariesUnitTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..f616a92b8ea4caa3855b53244a81fa65f2b7ed0a
--- /dev/null
+++ b/web/modules/libraries/src/Tests/LibrariesUnitTest.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace Drupal\libraries\Tests;
+
+use Drupal\simpletest\KernelTestBase;
+
+/**
+ * Tests basic Libraries API functions.
+ *
+ * @group libraries
+ */
+class LibrariesUnitTest extends KernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = array('libraries');
+
+  /**
+   * Tests libraries_get_path().
+   */
+  function testLibrariesGetPath() {
+    // Note that, even though libraries_get_path() doesn't find the 'example'
+    // library, we are able to make it 'installed' by specifying the 'library
+    // path' up-front. This is only used for testing purposed and is strongly
+    // discouraged as it defeats the purpose of Libraries API in the first
+    // place.
+    $this->assertEqual(libraries_get_path('example'), FALSE, 'libraries_get_path() returns FALSE for a missing library.');
+  }
+
+  /**
+   * Tests libraries_prepare_files().
+   */
+  function testLibrariesPrepareFiles() {
+    $expected = array(
+      'files' => array(
+        'js' => array('example.js' => array()),
+        'css' => array('example.css' => array()),
+        'php' => array('example.php' => array()),
+      ),
+    );
+    $library = array(
+      'files' => array(
+        'js' => array('example.js'),
+        'css' => array('example.css'),
+        'php' => array('example.php'),
+      ),
+    );
+    libraries_prepare_files($library, NULL, NULL);
+    $this->assertEqual($expected, $library, 'libraries_prepare_files() works correctly.');
+  }
+
+}
diff --git a/web/modules/libraries/src/Tests/LibrariesWebTest.php b/web/modules/libraries/src/Tests/LibrariesWebTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..2c9254c45323010c0145c28f58785d39e4e46f81
--- /dev/null
+++ b/web/modules/libraries/src/Tests/LibrariesWebTest.php
@@ -0,0 +1,525 @@
+<?php
+
+namespace Drupal\libraries\Tests;
+
+use Drupal\Component\Utility\Html;
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Tests basic detection and loading of libraries.
+ *
+ * @group libraries
+ */
+class LibrariesWebTest extends WebTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $profile = 'testing';
+
+  /**
+   * Modules to install.
+   *
+   * @var array
+   */
+  public static $modules = array('libraries', 'libraries_test');
+
+  /**
+   * The URL generator used in this test.
+   *
+   * @var \Drupal\Core\Utility\UnroutedUrlAssemblerInterface
+   */
+  protected $urlAssembler;
+
+  /**
+   * The state service used in this test.
+   *
+   * @var \Drupal\Core\State\StateInterface
+   */
+  protected $state;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->urlAssembler = $this->container->get('unrouted_url_assembler');
+    $this->state = $this->container->get('state');
+  }
+
+  /**
+   * Tests libraries_detect_dependencies().
+   */
+  function testLibrariesDetectDependencies() {
+    $library = array(
+      'name' => 'Example',
+      'dependencies' => array('example_missing'),
+    );
+    libraries_detect_dependencies($library);
+    $this->assertEqual($library['error'], 'missing dependency', 'libraries_detect_dependencies() detects missing dependency');
+    $error_message = t('The %dependency library, which the %library library depends on, is not installed.', array(
+      '%dependency' => 'Example missing',
+      '%library' => $library['name'],
+    ));
+    $this->verbose("Expected:<br>$error_message");
+    $this->verbose('Actual:<br>' . $library['error message']);
+    $this->assertEqual($library['error message'], $error_message, 'Correct error message for a missing dependency');
+    // Test versioned dependencies.
+    $version = '1.1';
+    $compatible = array(
+      '1.1',
+      '<=1.1',
+      '>=1.1',
+      '<1.2',
+      '<2.0',
+      '>1.0',
+      '>1.0-rc1',
+      '>1.0-beta2',
+      '>1.0-alpha3',
+      '>0.1',
+      '<1.2, >1.0',
+      '>0.1, <=1.1',
+    );
+    $incompatible = array(
+      '1.2',
+      '2.0',
+      '<1.1',
+      '>1.1',
+      '<=1.0',
+      '<=1.0-rc1',
+      '<=1.0-beta2',
+      '<=1.0-alpha3',
+      '>=1.2',
+      '<1.1, >0.9',
+      '>=0.1, <1.1',
+    );
+    $library = array(
+      'name' => 'Example',
+    );
+    foreach ($compatible as $version_string) {
+      $library['dependencies'][0] = "example_dependency ($version_string)";
+      // libraries_detect_dependencies() is a post-detect callback, so
+      // 'installed' is already set, when it is called. It sets the value to
+      // FALSE for missing or incompatible dependencies.
+      $library['installed'] = TRUE;
+      libraries_detect_dependencies($library);
+      $this->assertTrue($library['installed'], "libraries_detect_dependencies() detects compatible version string: '$version_string' is compatible with '$version'");
+    }
+    foreach ($incompatible as $version_string) {
+      $library['dependencies'][0] = "example_dependency ($version_string)";
+      $library['installed'] = TRUE;
+      unset($library['error'], $library['error message']);
+      libraries_detect_dependencies($library);
+      $this->assertEqual($library['error'], 'incompatible dependency', "libraries_detect_dependencies() detects incompatible version strings: '$version_string' is incompatible with '$version'");
+    }
+    // Instead of repeating this assertion for each version string, we just
+    // re-use the $library variable from the foreach loop.
+    $error_message = t('The version %dependency_version of the %dependency library is not compatible with the %library library.', array(
+      '%dependency_version' => $version,
+      '%dependency' => 'Example dependency',
+      '%library' => $library['name'],
+    ));
+    $this->verbose("Expected:<br>$error_message");
+    $this->verbose('Actual:<br>' . $library['error message']);
+    $this->assertEqual($library['error message'], $error_message, 'Correct error message for an incompatible dependency');
+  }
+
+  /**
+   * Tests libraries_scan_info_files().
+   */
+  function testLibrariesScanInfoFiles() {
+    $expected = array('example_info_file' => (object) array(
+      'uri' => drupal_get_path('module', 'libraries') . '/tests/example/example_info_file.libraries.info.yml',
+      'filename' => 'example_info_file.libraries.info.yml',
+      'name' => 'example_info_file.libraries.info',
+    ));
+    $actual = libraries_scan_info_files();
+    $this->verbose('Expected:<pre>' . var_export($expected, TRUE) . '</pre>');
+    $this->verbose('Actual:<pre>' . var_export($actual, TRUE) . '</pre>');
+    $this->assertEqual($actual, $expected, 'libraries_scan_info_files() correctly finds the example info file.');
+    $this->verbose('<pre>' . var_export(libraries_scan_info_files(), TRUE) . '</pre>');
+  }
+
+  /**
+   * Tests libraries_info().
+   */
+  function testLibrariesInfo() {
+    // Test that library information is found correctly.
+    $expected = array(
+      'name' => 'Example files',
+      'library path' => drupal_get_path('module', 'libraries') . '/tests/example',
+      'version' => '1',
+      'files' => array(
+        'js' => array('example_1.js' => array()),
+        'css' => array('example_1.css' => array()),
+        'php' => array('example_1.php' => array()),
+      ),
+      'module' => 'libraries_test',
+    );
+    libraries_info_defaults($expected, 'example_files');
+    $library = libraries_info('example_files');
+    $this->verbose('Expected:<pre>' . var_export($expected, TRUE) . '</pre>');
+    $this->verbose('Actual:<pre>' . var_export($library, TRUE) . '</pre>');
+    $this->assertEqual($library, $expected, 'Library information is correctly gathered.');
+
+    // Test a library specified with an .info file gets detected.
+    $expected = array(
+      'name' => 'Example info file',
+      'info file' => drupal_get_path('module', 'libraries') . '/tests/example/example_info_file.libraries.info.yml',
+    );
+    libraries_info_defaults($expected, 'example_info_file');
+    $library = libraries_info('example_info_file');
+    // If this module was downloaded from Drupal.org, the Drupal.org packaging
+    // system has corrupted the test info file.
+    // @see http://drupal.org/node/1606606
+    unset($library['core'], $library['datestamp'], $library['project'], $library['version']);
+    $this->verbose('Expected:<pre>' . var_export($expected, TRUE) . '</pre>');
+    $this->verbose('Actual:<pre>' . var_export($library, TRUE) . '</pre>');
+    $this->assertEqual($library, $expected, 'Library specified with an .info file found');
+  }
+
+  /**
+   * Tests libraries_detect().
+   */
+  function testLibrariesDetect() {
+    // Test missing library.
+    $library = libraries_detect('example_missing');
+    $this->verbose('<pre>' . var_export($library, TRUE) . '</pre>');
+    $this->assertEqual($library['error'], 'not found', 'Missing library not found.');
+    $error_message = t('The %library library could not be found.', array(
+      '%library' => $library['name'],
+    ));
+    $this->assertEqual($library['error message'], $error_message, 'Correct error message for a missing library.');
+
+    // Test unknown library version.
+    $library = libraries_detect('example_undetected_version');
+    $this->verbose('<pre>' . var_export($library, TRUE) . '</pre>');
+    $this->assertEqual($library['error'], 'not detected', 'Undetected version detected as such.');
+    $error_message = t('The version of the %library library could not be detected.', array(
+      '%library' => $library['name'],
+    ));
+    $this->assertEqual($library['error message'], $error_message, 'Correct error message for a library with an undetected version.');
+
+    // Test unsupported library version.
+    $library = libraries_detect('example_unsupported_version');
+    $this->verbose('<pre>' . var_export($library, TRUE) . '</pre>');
+    $this->assertEqual($library['error'], 'not supported', 'Unsupported version detected as such.');
+    $error_message = t('The installed version %version of the %library library is not supported.', array(
+      '%version' => $library['version'],
+      '%library' => $library['name'],
+    ));
+    $this->assertEqual($library['error message'], $error_message, 'Correct error message for a library with an unsupported version.');
+
+    // Test supported library version.
+    $library = libraries_detect('example_supported_version');
+    $this->verbose('<pre>' . var_export($library, TRUE) . '</pre>');
+    $this->assertEqual($library['installed'], TRUE, 'Supported library version found.');
+
+    // Test libraries_get_version().
+    $library = libraries_detect('example_default_version_callback');
+    $this->verbose('<pre>' . var_export($library, TRUE) . '</pre>');
+    $this->assertEqual($library['version'], '1', 'Expected version returned by default version callback.');
+
+    // Test a multiple-parameter version callback.
+    $library = libraries_detect('example_multiple_parameter_version_callback');
+    $this->verbose('<pre>' . var_export($library, TRUE) . '</pre>');
+    $this->assertEqual($library['version'], '1', 'Expected version returned by multiple parameter version callback.');
+
+    // Test a top-level files property.
+    $library = libraries_detect('example_files');
+    $files = array(
+      'js' => array('example_1.js' => array()),
+      'css' => array('example_1.css' => array()),
+      'php' => array('example_1.php' => array()),
+    );
+    $this->verbose('<pre>' . var_export($library, TRUE) . '</pre>');
+    $this->assertEqual($library['files'], $files, 'Top-level files property works.');
+
+    // Test version-specific library files.
+    $library = libraries_detect('example_versions');
+    $files = array(
+      'js' => array('example_2.js' => array()),
+      'css' => array('example_2.css' => array()),
+      'php' => array('example_2.php' => array()),
+    );
+    $this->verbose('<pre>' . var_export($library, TRUE) . '</pre>');
+    $this->assertEqual($library['files'], $files, 'Version-specific library files found.');
+
+    // Test missing variant.
+    $library = libraries_detect('example_variant_missing');
+    $this->verbose('<pre>' . var_export($library, TRUE) . '</pre>');
+    $this->assertEqual($library['variants']['example_variant']['error'], 'not found', 'Missing variant not found');
+    $error_message = t('The %variant variant of the %library library could not be found.', array(
+      '%variant' => 'example_variant',
+      '%library' => 'Example variant missing',
+    ));
+    $this->assertEqual($library['variants']['example_variant']['error message'], $error_message, 'Correct error message for a missing variant.');
+
+    // Test existing variant.
+    $library = libraries_detect('example_variant');
+    $this->verbose('<pre>' . var_export($library, TRUE) . '</pre>');
+    $this->assertEqual($library['variants']['example_variant']['installed'], TRUE, 'Existing variant found.');
+  }
+
+  /**
+   * Tests libraries_load().
+   *
+   * @todo Remove or rewrite to accomodate integration with core Libraries.
+   */
+  function _testLibrariesLoad() {
+    // Test dependencies.
+    $library = libraries_load('example_dependency_missing');
+    $this->verbose('<pre>' . var_export($library, TRUE) . '</pre>');
+    $this->assertFalse($library['loaded'], 'Library with missing dependency cannot be loaded');
+    $library = libraries_load('example_dependency_incompatible');
+    $this->verbose('<pre>' . var_export($library, TRUE) . '</pre>');
+    $this->assertFalse($library['loaded'], 'Library with incompatible dependency cannot be loaded');
+    $library = libraries_load('example_dependency_compatible');
+    $this->verbose('<pre>' . var_export($library, TRUE) . '</pre>');
+    $this->assertEqual($library['loaded'], 1, 'Library with compatible dependency is loaded');
+    $loaded = &drupal_static('libraries_load');
+    $this->verbose('<pre>' . var_export($loaded, TRUE) . '</pre>');
+    $this->assertEqual($loaded['example_dependency']['loaded'], 1, 'Dependency library is also loaded');
+  }
+
+  /**
+   * Tests the applying of callbacks.
+   */
+  function testCallbacks() {
+    $expected = array(
+      'name' => 'Example callback',
+      'library path' => drupal_get_path('module', 'libraries') . '/tests/example',
+      'version' => '1',
+      'versions' => array(
+        '1' => array(
+          'variants' => array(
+            'example_variant' => array(
+              'info callback' => 'not applied',
+              'pre-detect callback' => 'not applied',
+              'post-detect callback' => 'not applied',
+              'pre-load callback' => 'not applied',
+              'post-load callback' => 'not applied',
+            ),
+          ),
+          'info callback' => 'not applied',
+          'pre-detect callback' => 'not applied',
+          'post-detect callback' => 'not applied',
+          'pre-load callback' => 'not applied',
+          'post-load callback' => 'not applied',
+        ),
+      ),
+      'variants' => array(
+        'example_variant' => array(
+          'info callback' => 'not applied',
+          'pre-detect callback' => 'not applied',
+          'post-detect callback' => 'not applied',
+          'pre-load callback' => 'not applied',
+          'post-load callback' => 'not applied',
+        ),
+      ),
+      'callbacks' => array(
+        'info' => array('_libraries_test_info_callback'),
+        'pre-detect' => array('_libraries_test_pre_detect_callback'),
+        'post-detect' => array('_libraries_test_post_detect_callback'),
+        'pre-load' => array('_libraries_test_pre_load_callback'),
+        'post-load' => array('_libraries_test_post_load_callback'),
+      ),
+      'info callback' => 'not applied',
+      'pre-detect callback' => 'not applied',
+      'post-detect callback' => 'not applied',
+      'pre-load callback' => 'not applied',
+      'post-load callback' => 'not applied',
+      'module' => 'libraries_test',
+    );
+    libraries_info_defaults($expected, 'example_callback');
+
+    // Test a callback in the 'info' group.
+    $expected['info callback'] = 'applied (top-level)';
+    $expected['versions']['1']['info callback'] = 'applied (version 1)';
+    $expected['versions']['1']['variants']['example_variant']['info callback'] = 'applied (version 1, variant example_variant)';
+    $expected['variants']['example_variant']['info callback'] = 'applied (variant example_variant)';
+    $library = libraries_info('example_callback');
+    $this->verbose('Expected:<pre>' . var_export($expected, TRUE) . '</pre>');
+    $this->verbose('Actual:<pre>' . var_export($library, TRUE) . '</pre>');
+    $this->assertEqual($library, $expected, 'Prepare callback was applied correctly.');
+
+    // Test a callback in the 'pre-detect' and 'post-detect' phases.
+    // Successfully detected libraries should only contain version information
+    // for the detected version and thus, be marked as installed.
+    unset($expected['versions']);
+    $expected['installed'] = TRUE;
+    // Additionally, version-specific properties of the detected version are
+    // supposed to override the corresponding top-level properties.
+    $expected['info callback'] = 'applied (version 1)';
+    $expected['variants']['example_variant']['installed'] = TRUE;
+    $expected['variants']['example_variant']['info callback'] = 'applied (version 1, variant example_variant)';
+    // Version-overloading takes place after the 'pre-detect' callbacks have
+    // been applied.
+    $expected['pre-detect callback'] = 'applied (version 1)';
+    $expected['post-detect callback'] = 'applied (top-level)';
+    $expected['variants']['example_variant']['pre-detect callback'] = 'applied (version 1, variant example_variant)';
+    $expected['variants']['example_variant']['post-detect callback'] = 'applied (variant example_variant)';
+    $library = libraries_detect('example_callback');
+    $this->verbose('Expected:<pre>' . var_export($expected, TRUE) . '</pre>');
+    $this->verbose('Actual:<pre>' . var_export($library, TRUE) . '</pre>');
+    $this->assertEqual($library, $expected, 'Detect callback was applied correctly.');
+
+    // Test a callback in the 'pre-load' and 'post-load' phases.
+    // Successfully loaded libraries should only contain information about the
+    // already loaded variant.
+    unset($expected['variants']);
+    $expected['loaded'] = 0;
+    $expected['pre-load callback'] = 'applied (top-level)';
+    $expected['post-load callback'] = 'applied (top-level)';
+    $library = libraries_load('example_callback');
+    $this->verbose('Expected:<pre>' . var_export($expected, TRUE) . '</pre>');
+    $this->verbose('Actual:<pre>' . var_export($library, TRUE) . '</pre>');
+    $this->assertEqual($library, $expected, 'Pre-load and post-load callbacks were applied correctly.');
+    // This is not recommended usually and is only used for testing purposes.
+    drupal_static_reset('libraries_load');
+    // Successfully loaded library variants are supposed to contain the specific
+    // variant information only.
+    $expected['info callback'] = 'applied (version 1, variant example_variant)';
+    $expected['pre-detect callback'] = 'applied (version 1, variant example_variant)';
+    $expected['post-detect callback'] = 'applied (variant example_variant)';
+    $library = libraries_load('example_callback', 'example_variant');
+    $this->verbose('Expected:<pre>' . var_export($expected, TRUE) . '</pre>');
+    $this->verbose('Actual:<pre>' . var_export($library, TRUE) . '</pre>');
+    $this->assertEqual($library, $expected, 'Pre-detect and post-detect callbacks were applied correctly to a variant.');
+  }
+
+  /**
+   * Tests that library files are properly added to the page output.
+   *
+   * We check for JavaScript and CSS files directly in the DOM and add a list of
+   * included PHP files manually to the page output.
+   *
+   * @todo Remove or rewrite to accomodate integration with core Libraries.
+   * @see _libraries_test_load()
+   */
+  function _testLibrariesOutput() {
+    // Test loading of a simple library with a top-level files property.
+    $this->drupalGet('libraries_test/files');
+    $this->assertLibraryFiles('example_1', 'File loading');
+
+    // Test loading of integration files.
+    $this->drupalGet('libraries_test/integration_files');
+    $this->assertRaw('libraries_test.js', 'Integration file loading: libraries_test.js found');
+    $this->assertRaw('libraries_test.css', 'Integration file loading: libraries_test.css found');
+    $this->assertRaw('libraries_test.inc', 'Integration file loading: libraries_test.inc found');
+
+    // Test version overloading.
+    $this->drupalGet('libraries_test/versions');
+    $this->assertLibraryFiles('example_2', 'Version overloading');
+
+    // Test variant loading.
+    $this->drupalGet('libraries_test/variant');
+    $this->assertLibraryFiles('example_3', 'Variant loading');
+
+    // Test version overloading and variant loading.
+    $this->drupalGet('libraries_test/versions_and_variants');
+    $this->assertLibraryFiles('example_4', 'Concurrent version and variant overloading');
+
+    // Test caching.
+    \Drupal::state()->set('libraries_test.cache', TRUE);
+    \Drupal::cache('libraries')->delete('example_callback');
+    // When the library information is not cached, all callback groups should be
+    // invoked.
+    $this->drupalGet('libraries_test/cache');
+    $this->assertRaw('The <em>info</em> callback group was invoked.', 'Info callback invoked for uncached libraries.');
+    $this->assertRaw('The <em>pre-detect</em> callback group was invoked.', 'Pre-detect callback invoked for uncached libraries.');
+    $this->assertRaw('The <em>post-detect</em> callback group was invoked.', 'Post-detect callback invoked for uncached libraries.');
+    $this->assertRaw('The <em>pre-load</em> callback group was invoked.', 'Pre-load callback invoked for uncached libraries.');
+    $this->assertRaw('The <em>post-load</em> callback group was invoked.', 'Post-load callback invoked for uncached libraries.');
+    // When the library information is cached only the 'pre-load' and
+    // 'post-load' callback groups should be invoked.
+    $this->drupalGet('libraries_test/cache');
+    $this->assertNoRaw('The <em>info</em> callback group was not invoked.', 'Info callback not invoked for cached libraries.');
+    $this->assertNoRaw('The <em>pre-detect</em> callback group was not invoked.', 'Pre-detect callback not invoked for cached libraries.');
+    $this->assertNoRaw('The <em>post-detect</em> callback group was not invoked.', 'Post-detect callback not invoked for cached libraries.');
+    $this->assertRaw('The <em>pre-load</em> callback group was invoked.', 'Pre-load callback invoked for cached libraries.');
+    $this->assertRaw('The <em>post-load</em> callback group was invoked.', 'Post-load callback invoked for cached libraries.');
+    \Drupal::state()->set('libraries_test.cache', FALSE);
+  }
+
+  /**
+   * Helper function to assert that a library was correctly loaded.
+   *
+   * Asserts that all the correct files were loaded and all the incorrect ones
+   * were not.
+   *
+   * @param $name
+   *   The name of the files that should be loaded. The current testing system
+   *   knows of 'example_1', 'example_2', 'example_3' and 'example_4'. Each name
+   *   has an associated JavaScript, CSS and PHP file that will be asserted. All
+   *   other files will be asserted to not be loaded. See
+   *   tests/example/README.txt for more information on how the loading of the
+   *   files is tested.
+   * @param $label
+   *   (optional) A label to prepend to the assertion messages, to make them
+   *   less ambiguous.
+   * @param $extensions
+   *   (optional) The expected file extensions of $name. Defaults to
+   *   array('js', 'css', 'php').
+   */
+  function assertLibraryFiles($name, $label = '', $extensions = array('js', 'css', 'php')) {
+    $label = ($label !== '' ? "$label: " : '');
+
+    // Test that the wrong files are not loaded...
+    $names = array(
+      'example_1' => FALSE,
+      'example_2' => FALSE,
+      'example_3' => FALSE,
+      'example_4' => FALSE,
+    );
+    // ...and the correct ones are.
+    $names[$name] = TRUE;
+
+    // Test for the specific HTML that the different file types appear as in the
+    // DOM.
+    $html = array(
+      'js' => array('<script src="', '"></script>'),
+      'css' => array('<link rel="stylesheet" href="', '" media="all" />'),
+      // PHP files do not get added to the DOM directly.
+      // @see _libraries_test_load()
+      'php' => array('<li>', '</li>'),
+    );
+
+    $html_expected = array();
+    $html_not_expected = array();
+
+    foreach ($names as $name => $expected) {
+      foreach ($extensions as $extension) {
+        $filepath = drupal_get_path('module', 'libraries') . "/tests/example/$name.$extension";
+        // JavaScript and CSS files appear as full URLs and with an appended
+        // query string.
+        if (in_array($extension, array('js', 'css'))) {
+          $filepath = $this->urlAssembler->assemble("base://$filepath", [
+            'query' => [
+              $this->state->get('system.css_js_query_string') ?: '0' => NULL,
+            ],
+            'absolute' => TRUE,
+          ]);
+          // If index.php is part of the generated URLs, we need to strip it.
+          //$filepath = str_replace('index.php/', '', $filepath);
+        }
+        list($prefix, $suffix) = $html[$extension];
+        $raw = $prefix . $filepath . $suffix;
+        if ($expected) {
+          $html_expected[] = Html::escape($raw);
+          $this->assertRaw($raw, "$label$name.$extension found.");
+        }
+        else {
+          $html_not_expected[] = Html::escape($raw);
+          $this->assertNoRaw($raw, "$label$name.$extension not found.");
+        }
+      }
+    }
+
+    $html_expected = '<ul><li><pre>' . implode('</pre></li><li><pre>', $html_expected) . '</pre></li></ul>';
+    $html_not_expected = '<ul><li><pre>' . implode('</pre></li><li><pre>', $html_not_expected) . '</pre></li></ul>';
+    $this->verbose("Strings of HTML that are expected to be present:{$html_expected}Strings of HTML that are expected to not be present:{$html_not_expected}");
+  }
+
+}
diff --git a/web/modules/libraries/tests/assets/vendor/test_asset_library/example.css b/web/modules/libraries/tests/assets/vendor/test_asset_library/example.css
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/web/modules/libraries/tests/assets/vendor/test_asset_library/example.js b/web/modules/libraries/tests/assets/vendor/test_asset_library/example.js
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/web/modules/libraries/tests/assets/vendor/test_asset_multiple_library/example.first.css b/web/modules/libraries/tests/assets/vendor/test_asset_multiple_library/example.first.css
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/web/modules/libraries/tests/assets/vendor/test_asset_multiple_library/example.first.js b/web/modules/libraries/tests/assets/vendor/test_asset_multiple_library/example.first.js
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/web/modules/libraries/tests/assets/vendor/test_asset_multiple_library/example.second.css b/web/modules/libraries/tests/assets/vendor/test_asset_multiple_library/example.second.css
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/web/modules/libraries/tests/assets/vendor/test_asset_multiple_library/example.second.js b/web/modules/libraries/tests/assets/vendor/test_asset_multiple_library/example.second.js
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/web/modules/libraries/tests/example/README.txt b/web/modules/libraries/tests/example/README.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e3582c2abbbb822f47fc834c058ad87d88c50265
--- /dev/null
+++ b/web/modules/libraries/tests/example/README.txt
@@ -0,0 +1,42 @@
+
+Example library
+
+Version 1
+
+This file is an example file to test version detection.
+
+The various other files in this directory are to test the loading of JavaScript,
+CSS and PHP files.
+- JavaScript: The filenames of the JavaScript files are asserted to be in the
+  raw HTML via SimpleTest. Since the filename could appear, for instance, in an
+  error message, this is not very robust. Explicit testing of JavaScript,
+  though, is not yet possible with SimpleTest. To allow for easier debugging, we
+  place the following text on the page:
+  "If this text shows up, no JavaScript test file was loaded."
+  This text is replaced via JavaScript by a text of the form:
+  "If this text shows up, [[file] was loaded successfully."
+  [file] is either 'example_1.js', 'example_2.js', 'example_3.js',
+  'example_4.js' or 'libraries_test.js'. If you have SimpleTest's verbose mode
+  enabled and see the above text in one of the debug pages, the noted JavaScript
+  file was loaded successfully.
+- CSS: The filenames of the CSS files are asserted to be in the raw HTML via
+  SimpleTest. Since the filename could appear, for instance, in an error
+  message, this is not very robust. Explicit testing of CSS, though, is not yet
+  possible with SimpleTest. Hence, the CSS files, if loaded, make the following
+  text a certain color:
+  "If one of the CSS test files has been loaded, this text will be colored:
+  - example_1: red
+  - example_2: green
+  - example_3: orange
+  - example_4: blue
+  - libraries_test: purple"
+  If you have SimpleTest's verbose mode enabled, and see the above text in a
+  certain color (i.e. not in black), a CSS file was loaded successfully. Which
+  file depends on the color as referenced in the text above.
+- PHP: The loading of PHP files is tested by defining a dummy function in the
+  PHP files and then checking whether this function was defined using
+  function_exists(). This can be checked programatically with SimpleTest.
+The loading of integration files is tested with the same method. The integration
+files are libraries_test.js, libraries_test.css, libraries_test.inc and are
+located in the tests directory alongside libraries_test.module (i.e. they are
+not in the same directory as this file).
diff --git a/web/modules/libraries/tests/example/example_1.css b/web/modules/libraries/tests/example/example_1.css
new file mode 100644
index 0000000000000000000000000000000000000000..a732bda5fca4cfcc1734b46a16cb8ac84e6856a2
--- /dev/null
+++ b/web/modules/libraries/tests/example/example_1.css
@@ -0,0 +1,11 @@
+
+/**
+ * @file
+ * Test CSS file for Libraries loading.
+ *
+ * Color the 'libraries-test-css' div red. See README.txt for more information.
+ */
+
+.libraries-test-css {
+  color: red;
+}
diff --git a/web/modules/libraries/tests/example/example_1.js b/web/modules/libraries/tests/example/example_1.js
new file mode 100644
index 0000000000000000000000000000000000000000..8a1b9a27f45fc82535cddf79569848b8edcfd6c5
--- /dev/null
+++ b/web/modules/libraries/tests/example/example_1.js
@@ -0,0 +1,18 @@
+
+/**
+ * @file
+ * Test JavaScript file for Libraries loading.
+ *
+ * Replace the text in the 'libraries-test-javascript' div. See README.txt for
+ * more information.
+ */
+
+(function ($) {
+
+Drupal.behaviors.librariesTest = {
+  attach: function(context, settings) {
+    $('.libraries-test-javascript').text('If this text shows up, example_1.js was loaded successfully.')
+  }
+};
+
+})(jQuery);
diff --git a/web/modules/libraries/tests/example/example_1.php b/web/modules/libraries/tests/example/example_1.php
new file mode 100644
index 0000000000000000000000000000000000000000..92e6711a792fee3acac339a8c79c8227ac6ba393
--- /dev/null
+++ b/web/modules/libraries/tests/example/example_1.php
@@ -0,0 +1,12 @@
+<?php
+
+/**
+ * @file
+ * Test PHP file for Libraries loading.
+ */
+
+/**
+ * Dummy function to see if this file was loaded.
+ */
+function _libraries_test_example_1() {
+}
diff --git a/web/modules/libraries/tests/example/example_2.css b/web/modules/libraries/tests/example/example_2.css
new file mode 100644
index 0000000000000000000000000000000000000000..c8f92899de569ec1caf4ee44e720f0e604d24c59
--- /dev/null
+++ b/web/modules/libraries/tests/example/example_2.css
@@ -0,0 +1,11 @@
+
+/**
+ * @file
+ * Test CSS file for Libraries loading.
+ *
+ * Color the 'libraries-test-css' div green. See README.txt for more information.
+ */
+
+.libraries-test-css {
+  color: green;
+}
diff --git a/web/modules/libraries/tests/example/example_2.js b/web/modules/libraries/tests/example/example_2.js
new file mode 100644
index 0000000000000000000000000000000000000000..1b744bb166754c8320a19822c5b05c8ed3a9c7ca
--- /dev/null
+++ b/web/modules/libraries/tests/example/example_2.js
@@ -0,0 +1,18 @@
+
+/**
+ * @file
+ * Test JavaScript file for Libraries loading.
+ *
+ * Replace the text in the 'libraries-test-javascript' div. See README.txt for
+ * more information.
+ */
+
+(function ($) {
+
+Drupal.behaviors.librariesTest = {
+  attach: function(context, settings) {
+    $('.libraries-test-javascript').text('If this text shows up, example_2.js was loaded successfully.')
+  }
+};
+
+})(jQuery);
diff --git a/web/modules/libraries/tests/example/example_2.php b/web/modules/libraries/tests/example/example_2.php
new file mode 100644
index 0000000000000000000000000000000000000000..ddded40c3c3c65fff4bc488b5f69b0fc54e879cc
--- /dev/null
+++ b/web/modules/libraries/tests/example/example_2.php
@@ -0,0 +1,12 @@
+<?php
+
+/**
+ * @file
+ * Test PHP file for Libraries loading.
+ */
+
+/**
+ * Dummy function to see if this file was loaded.
+ */
+function _libraries_test_example_2() {
+}
diff --git a/web/modules/libraries/tests/example/example_3.css b/web/modules/libraries/tests/example/example_3.css
new file mode 100644
index 0000000000000000000000000000000000000000..ffef054a43d8aa575bdbed0b722c0ee7064780b4
--- /dev/null
+++ b/web/modules/libraries/tests/example/example_3.css
@@ -0,0 +1,11 @@
+
+/**
+ * @file
+ * Test CSS file for Libraries loading.
+ *
+ * Color the 'libraries-test-css' div orange. See README.txt for more information.
+ */
+
+.libraries-test-css {
+  color: orange;
+}
diff --git a/web/modules/libraries/tests/example/example_3.js b/web/modules/libraries/tests/example/example_3.js
new file mode 100644
index 0000000000000000000000000000000000000000..d6a3fa4e5b0f1623fc318f360324f1302c382ada
--- /dev/null
+++ b/web/modules/libraries/tests/example/example_3.js
@@ -0,0 +1,18 @@
+
+/**
+ * @file
+ * Test JavaScript file for Libraries loading.
+ *
+ * Replace the text in the 'libraries-test-javascript' div. See README.txt for
+ * more information.
+ */
+
+(function ($) {
+
+Drupal.behaviors.librariesTest = {
+  attach: function(context, settings) {
+    $('.libraries-test-javascript').text('If this text shows up, example_3.js was loaded successfully.')
+  }
+};
+
+})(jQuery);
diff --git a/web/modules/libraries/tests/example/example_3.php b/web/modules/libraries/tests/example/example_3.php
new file mode 100644
index 0000000000000000000000000000000000000000..cf74cf0811bda306b39a22dda9d13fa11cfbf580
--- /dev/null
+++ b/web/modules/libraries/tests/example/example_3.php
@@ -0,0 +1,12 @@
+<?php
+
+/**
+ * @file
+ * Test PHP file for Libraries loading.
+ */
+
+/**
+ * Dummy function to see if this file was loaded.
+ */
+function _libraries_test_example_3() {
+}
diff --git a/web/modules/libraries/tests/example/example_4.css b/web/modules/libraries/tests/example/example_4.css
new file mode 100644
index 0000000000000000000000000000000000000000..5030a614808ba266727b8f7a2ee55b935752aa8b
--- /dev/null
+++ b/web/modules/libraries/tests/example/example_4.css
@@ -0,0 +1,11 @@
+
+/**
+ * @file
+ * Test CSS file for Libraries loading.
+ *
+ * Color the 'libraries-test-css' div blue. See README.txt for more information.
+ */
+
+.libraries-test-css {
+  color: blue;
+}
diff --git a/web/modules/libraries/tests/example/example_4.js b/web/modules/libraries/tests/example/example_4.js
new file mode 100644
index 0000000000000000000000000000000000000000..ce5dc26e31fd6e22c721cb5cb378153553306c50
--- /dev/null
+++ b/web/modules/libraries/tests/example/example_4.js
@@ -0,0 +1,18 @@
+
+/**
+ * @file
+ * Test JavaScript file for Libraries loading.
+ *
+ * Replace the text in the 'libraries-test-javascript' div. See README.txt for
+ * more information.
+ */
+
+(function ($) {
+
+Drupal.behaviors.librariesTest = {
+  attach: function(context, settings) {
+    $('.libraries-test-javascript').text('If this text shows up, example_4.js was loaded successfully.')
+  }
+};
+
+})(jQuery);
diff --git a/web/modules/libraries/tests/example/example_4.php b/web/modules/libraries/tests/example/example_4.php
new file mode 100644
index 0000000000000000000000000000000000000000..b46507f540551ef2c07ac17eb7c871fcabf57e8d
--- /dev/null
+++ b/web/modules/libraries/tests/example/example_4.php
@@ -0,0 +1,12 @@
+<?php
+
+/**
+ * @file
+ * Test PHP file for Libraries loading.
+ */
+
+/**
+ * Dummy function to see if this file was loaded.
+ */
+function _libraries_test_example_4() {
+}
diff --git a/web/modules/libraries/tests/example/example_info_file.libraries.info.yml b/web/modules/libraries/tests/example/example_info_file.libraries.info.yml
new file mode 100644
index 0000000000000000000000000000000000000000..529af045cf3d5cfce7fb74a9ce74dc98aaec9c88
--- /dev/null
+++ b/web/modules/libraries/tests/example/example_info_file.libraries.info.yml
@@ -0,0 +1,8 @@
+# This is an example info file of a library used for testing purposes.
+name: Example info file
+
+# Information added by Drupal.org packaging script on 2018-01-27
+version: '8.x-3.0-alpha1'
+core: '8.x'
+project: 'libraries'
+datestamp: 1517046488
diff --git a/web/modules/libraries/tests/libraries/test_php_file_library/test_php_file_library.php b/web/modules/libraries/tests/libraries/test_php_file_library/test_php_file_library.php
new file mode 100644
index 0000000000000000000000000000000000000000..8ef69fb553bde2f4d725e3205feb9c62a012a810
--- /dev/null
+++ b/web/modules/libraries/tests/libraries/test_php_file_library/test_php_file_library.php
@@ -0,0 +1,16 @@
+<?php
+
+/**
+ * @file
+ * Test PHP file.
+ *
+ * This file is part of the 'test_php_file_library' test library.
+ *
+ * @see \Drupal\Tests\libraries\Kernel\ExternalLibrary\PhpFile\PhpFileLibraryTest
+ */
+
+/**
+ * A test function to be able to test whether this file was loaded or not.
+ */
+function _libraries_test_php_function() {
+}
diff --git a/web/modules/libraries/tests/library_definitions/test_asset_library.json b/web/modules/libraries/tests/library_definitions/test_asset_library.json
new file mode 100644
index 0000000000000000000000000000000000000000..aca82a39a796efb25bf2908d4ccfdaf67b029615
--- /dev/null
+++ b/web/modules/libraries/tests/library_definitions/test_asset_library.json
@@ -0,0 +1,18 @@
+{
+  "type": "asset",
+  "version_detector": {
+    "id": "static",
+    "configuration": {
+      "version": "1.0.0"
+    }
+  },
+  "remote_url": "http://example.com",
+  "css": {
+    "base": {
+      "example.css": {}
+    }
+  },
+  "js": {
+    "example.js": {}
+  }
+}
diff --git a/web/modules/libraries/tests/library_definitions/test_asset_multiple_library.json b/web/modules/libraries/tests/library_definitions/test_asset_multiple_library.json
new file mode 100644
index 0000000000000000000000000000000000000000..8bcff3c1c538aa964358593e41b5bdcdb5971ae8
--- /dev/null
+++ b/web/modules/libraries/tests/library_definitions/test_asset_multiple_library.json
@@ -0,0 +1,32 @@
+{
+  "type": "asset_multiple",
+  "version_detector": {
+    "id": "static",
+    "configuration": {
+      "version": "1.0.0"
+    }
+  },
+  "remote_url": "http://example.com",
+  "libraries": {
+    "first": {
+      "css": {
+        "base": {
+          "example.first.css": {}
+        }
+      },
+      "js": {
+        "example.first.js": {}
+      }
+    },
+    "second": {
+      "css": {
+        "base": {
+          "example.second.css": {}
+        }
+      },
+      "js": {
+        "example.second.js": {}
+      }
+    }
+  }
+}
diff --git a/web/modules/libraries/tests/library_definitions/test_php_file_library.json b/web/modules/libraries/tests/library_definitions/test_php_file_library.json
new file mode 100644
index 0000000000000000000000000000000000000000..61cb28c9c5f3242fae474328e94fdcb37cce7cb2
--- /dev/null
+++ b/web/modules/libraries/tests/library_definitions/test_php_file_library.json
@@ -0,0 +1,6 @@
+{
+  "type": "php_file",
+  "files": [
+    "test_php_file_library.php"
+  ]
+}
diff --git a/web/modules/libraries/tests/modules/libraries_test/libraries_test.css b/web/modules/libraries/tests/modules/libraries_test/libraries_test.css
new file mode 100644
index 0000000000000000000000000000000000000000..350528129c69498a52f749ffb07f9c323fd3b850
--- /dev/null
+++ b/web/modules/libraries/tests/modules/libraries_test/libraries_test.css
@@ -0,0 +1,12 @@
+
+/**
+ * @file
+ * Test CSS file for Libraries loading.
+ *
+ * Color the 'libraries-test-css' div purple. See README.txt for more
+ * information.
+ */
+
+.libraries-test-css {
+  color: purple;
+}
diff --git a/web/modules/libraries/tests/modules/libraries_test/libraries_test.inc b/web/modules/libraries/tests/modules/libraries_test/libraries_test.inc
new file mode 100644
index 0000000000000000000000000000000000000000..68e2d2eab37071cca089b18010f21b68e8495ac6
--- /dev/null
+++ b/web/modules/libraries/tests/modules/libraries_test/libraries_test.inc
@@ -0,0 +1,12 @@
+<?php
+
+/**
+ * @file
+ * Test PHP file for Libraries loading.
+ */
+
+/**
+ * Dummy function to see if this file was loaded.
+ */
+function _libraries_test_integration_file() {
+}
diff --git a/web/modules/libraries/tests/modules/libraries_test/libraries_test.info.yml b/web/modules/libraries/tests/modules/libraries_test/libraries_test.info.yml
new file mode 100644
index 0000000000000000000000000000000000000000..be54ba548ffeeb66d574293940fbcbdb06524972
--- /dev/null
+++ b/web/modules/libraries/tests/modules/libraries_test/libraries_test.info.yml
@@ -0,0 +1,16 @@
+name: Libraries test module
+type: module
+description: Tests library detection and loading.
+# core: 8.x
+dependencies:
+  - libraries
+hidden: TRUE
+library_dependencies:
+  - test_asset_library
+  - test_asset_multiple_library
+
+# Information added by Drupal.org packaging script on 2018-01-27
+version: '8.x-3.0-alpha1'
+core: '8.x'
+project: 'libraries'
+datestamp: 1517046488
diff --git a/web/modules/libraries/tests/modules/libraries_test/libraries_test.js b/web/modules/libraries/tests/modules/libraries_test/libraries_test.js
new file mode 100644
index 0000000000000000000000000000000000000000..25ac3ec7de2cdcb4ca0545ac7cdb88bb0bccaf20
--- /dev/null
+++ b/web/modules/libraries/tests/modules/libraries_test/libraries_test.js
@@ -0,0 +1,18 @@
+
+/**
+ * @file
+ * Test JavaScript file for Libraries loading.
+ *
+ * Replace the text in the 'libraries-test-javascript' div. See README.txt for
+ * more information.
+ */
+
+(function ($) {
+
+Drupal.behaviors.librariesTest = {
+  attach: function(context, settings) {
+    $('.libraries-test-javascript').text('If this text shows up, libraries_test.js was loaded successfully.')
+  }
+};
+
+})(jQuery);
diff --git a/web/modules/libraries/tests/modules/libraries_test/libraries_test.module b/web/modules/libraries/tests/modules/libraries_test/libraries_test.module
new file mode 100644
index 0000000000000000000000000000000000000000..1a30ebc1c3c78ffab668b0b0a848bb370a2307cd
--- /dev/null
+++ b/web/modules/libraries/tests/modules/libraries_test/libraries_test.module
@@ -0,0 +1,497 @@
+<?php
+
+/**
+ * @file
+ * Tests the library detection and loading.
+ */
+
+use Drupal\Component\Utility\SafeMarkup;
+
+/**
+ * Implements hook_libraries_info().
+ */
+function libraries_test_libraries_info() {
+  // Test library detection.
+  $libraries['example_missing'] = array(
+    'name' => 'Example missing',
+    'library path' => drupal_get_path('module', 'libraries') . '/tests/missing',
+  );
+  $libraries['example_undetected_version'] = array(
+    'name' => 'Example undetected version',
+    'library path' => drupal_get_path('module', 'libraries') . '/tests',
+    'version callback' => '_libraries_test_return_version',
+    'version arguments' => array(FALSE),
+  );
+  $libraries['example_unsupported_version'] = array(
+    'name' => 'Example unsupported version',
+    'library path' => drupal_get_path('module', 'libraries') . '/tests',
+    'version callback' => '_libraries_test_return_version',
+    'version arguments' => array('1'),
+    'versions' => array(
+      '2' => array(),
+    ),
+  );
+
+  $libraries['example_supported_version'] = array(
+    'name' => 'Example supported version',
+    'library path' => drupal_get_path('module', 'libraries') . '/tests',
+    'version callback' => '_libraries_test_return_version',
+    'version arguments' => array('1'),
+    'versions' => array(
+      '1' => array(),
+    ),
+  );
+
+  // Test the default version callback.
+  $libraries['example_default_version_callback'] = array(
+    'name' => 'Example default version callback',
+    'library path' => drupal_get_path('module', 'libraries') . '/tests/example',
+    'version arguments' => array(
+      'file' => 'README.txt',
+      // Version 1
+      'pattern' => '/Version (\d+)/',
+      'lines' => 5,
+    ),
+  );
+
+  // Test a multiple-parameter version callback.
+  $libraries['example_multiple_parameter_version_callback'] = array(
+    'name' => 'Example multiple parameter version callback',
+    'library path' => drupal_get_path('module', 'libraries') . '/tests/example',
+    // Version 1
+    'version callback' => '_libraries_test_get_version',
+    'version arguments' => array('README.txt', '/Version (\d+)/', 5),
+  );
+
+  // Test a top-level files property.
+  $libraries['example_files'] = array(
+    'name' => 'Example files',
+    'library path' => drupal_get_path('module', 'libraries') . '/tests/example',
+    'version' => '1',
+    'files' => array(
+      'js' => array('example_1.js'),
+      'css' => array('example_1.css'),
+      'php' => array('example_1.php'),
+    ),
+  );
+
+  // Test loading of integration files.
+  // Normally added by the corresponding module via hook_libraries_info_alter(),
+  // these files should be automatically loaded when the library is loaded.
+  $libraries['example_integration_files'] = array(
+    'name' => 'Example integration files',
+    'library path' => drupal_get_path('module', 'libraries') . '/tests/example',
+    'version' => '1',
+    'integration files' => array(
+      'libraries_test' => array(
+        'js' => array('libraries_test.js'),
+        'css' => array('libraries_test.css'),
+        'php' => array('libraries_test.inc'),
+      ),
+    ),
+  );
+
+  // Test version overloading.
+  $libraries['example_versions'] = array(
+    'name' => 'Example versions',
+    'library path' => drupal_get_path('module', 'libraries') . '/tests/example',
+    'version' => '2',
+    'versions' => array(
+      '1' => array(
+        'files' => array(
+          'js' => array('example_1.js'),
+          'css' => array('example_1.css'),
+          'php' => array('example_1.php'),
+        ),
+      ),
+      '2' => array(
+        'files' => array(
+          'js' => array('example_2.js'),
+          'css' => array('example_2.css'),
+          'php' => array('example_2.php'),
+        ),
+      ),
+    ),
+  );
+
+  // Test variant detection.
+  $libraries['example_variant_missing'] = array(
+    'name' => 'Example variant missing',
+    'library path' => drupal_get_path('module', 'libraries') . '/tests/example',
+    'version' => '1',
+    'variants' => array(
+      'example_variant' => array(
+        'files' => array(
+          'js' => array('example_3.js'),
+          'css' => array('example_3.css'),
+          'php' => array('example_3.php'),
+        ),
+        'variant callback' => '_libraries_test_return_installed',
+        'variant arguments' => array(FALSE),
+      ),
+    ),
+  );
+
+  $libraries['example_variant'] = array(
+    'name' => 'Example variant',
+    'library path' => drupal_get_path('module', 'libraries') . '/tests/example',
+    'version' => '1',
+    'variants' => array(
+      'example_variant' => array(
+        'files' => array(
+          'js' => array('example_3.js'),
+          'css' => array('example_3.css'),
+          'php' => array('example_3.php'),
+        ),
+        'variant callback' => '_libraries_test_return_installed',
+        'variant arguments' => array(TRUE),
+      ),
+    ),
+  );
+
+  // Test correct behaviour with multiple versions and multiple variants.
+  $libraries['example_versions_and_variants'] = array(
+    'name' => 'Example versions and variants',
+    'library path' => drupal_get_path('module', 'libraries') . '/tests/example',
+    'version' => '2',
+    'versions' => array(
+      '1' => array(
+        'variants' => array(
+          'example_variant_1' => array(
+            'files' => array(
+              'js' => array('example_1.js'),
+              'css' => array('example_1.css'),
+              'php' => array('example_1.php'),
+            ),
+            'variant callback' => '_libraries_test_return_installed',
+            'variant arguments' => array(TRUE),
+          ),
+          'example_variant_2' => array(
+            'files' => array(
+              'js' => array('example_2.js'),
+              'css' => array('example_2.css'),
+              'php' => array('example_2.php'),
+            ),
+            'variant callback' => '_libraries_test_return_installed',
+            'variant arguments' => array(TRUE),
+          ),
+        ),
+      ),
+      '2' => array(
+        'variants' => array(
+          'example_variant_1' => array(
+            'files' => array(
+              'js' => array('example_3.js'),
+              'css' => array('example_3.css'),
+              'php' => array('example_3.php'),
+            ),
+            'variant callback' => '_libraries_test_return_installed',
+            'variant arguments' => array(TRUE),
+          ),
+          'example_variant_2' => array(
+            'files' => array(
+              'js' => array('example_4.js'),
+              'css' => array('example_4.css'),
+              'php' => array('example_4.php'),
+            ),
+            'variant callback' => '_libraries_test_return_installed',
+            'variant arguments' => array(TRUE),
+          ),
+        ),
+      ),
+    ),
+  );
+
+  // Test dependency loading.
+  // We add one file to each library to be able to verify if it was loaded with
+  // libraries_load().
+  // This library acts as a dependency for the libraries below.
+  $libraries['example_dependency'] = array(
+    'name' => 'Example dependency',
+    'library path' => drupal_get_path('module', 'libraries') . '/tests/example',
+    'version' => '1.1',
+    'files' => array('js' => array('example_1.js')),
+  );
+  $libraries['example_dependency_missing'] = array(
+    'name' => 'Example dependency missing',
+    'library path' => drupal_get_path('module', 'libraries') . '/tests/example',
+    'version' => '1',
+    'dependencies' => array('example_missing'),
+    'files' => array('js' => array('example_1.js')),
+  );
+  $libraries['example_dependency_incompatible'] = array(
+    'name' => 'Example dependency incompatible',
+    'library path' => drupal_get_path('module', 'libraries') . '/tests/example',
+    'version' => '1',
+    'dependencies' => array('example_dependency (>1.1)'),
+    'files' => array('js' => array('example_1.js')),
+  );
+  $libraries['example_dependency_compatible'] = array(
+    'name' => 'Example dependency compatible',
+    'library path' => drupal_get_path('module', 'libraries') . '/tests/example',
+    'version' => '1',
+    'dependencies' => array('example_dependency (>=1.1)'),
+    'files' => array('js' => array('example_1.js')),
+  );
+
+  // Test the applying of callbacks.
+  $libraries['example_callback'] = array(
+    'name' => 'Example callback',
+    'library path' => drupal_get_path('module', 'libraries') . '/tests/example',
+    'version' => '1',
+    'versions' => array(
+      '1' => array(
+        'variants' => array(
+          'example_variant' => array(
+            // These keys are for testing purposes only.
+            'info callback' => 'not applied',
+            'pre-detect callback' => 'not applied',
+            'post-detect callback' => 'not applied',
+            'pre-load callback' => 'not applied',
+            'post-load callback' => 'not applied',
+          ),
+        ),
+        // These keys are for testing purposes only.
+        'info callback' => 'not applied',
+        'pre-detect callback' => 'not applied',
+        'post-detect callback' => 'not applied',
+        'pre-load callback' => 'not applied',
+        'post-load callback' => 'not applied',
+      ),
+    ),
+    'variants' => array(
+      'example_variant' => array(
+        // These keys are for testing purposes only.
+        'info callback' => 'not applied',
+        'pre-detect callback' => 'not applied',
+        'post-detect callback' => 'not applied',
+        'pre-load callback' => 'not applied',
+        'post-load callback' => 'not applied',
+      ),
+    ),
+    'callbacks' => array(
+      'info' => array('_libraries_test_info_callback'),
+      'pre-detect' => array('_libraries_test_pre_detect_callback'),
+      'post-detect' => array('_libraries_test_post_detect_callback'),
+      'pre-load' => array('_libraries_test_pre_load_callback'),
+      'post-load' => array('_libraries_test_post_load_callback'),
+    ),
+    // These keys are for testing purposes only.
+    'info callback' => 'not applied',
+    'pre-detect callback' => 'not applied',
+    'post-detect callback' => 'not applied',
+    'pre-load callback' => 'not applied',
+    'post-load callback' => 'not applied',
+  );
+
+  return $libraries;
+}
+
+/**
+ * Implements hook_libraries_info_file_paths()
+ */
+function libraries_test_libraries_info_file_paths() {
+  return array(drupal_get_path('module', 'libraries') . '/tests/example');
+}
+
+/**
+ * Gets the version of an example library.
+ *
+ * Returns exactly the version string entered as the $version parameter. This
+ * function cannot be collapsed with _libraries_test_return_installed(), because
+ * of the different arguments that are passed automatically.
+ */
+function _libraries_test_return_version($library, $version) {
+  return $version;
+}
+
+/**
+ * Gets the version information from an arbitrary library.
+ *
+ * Test function for a version callback with multiple arguments. This is an
+ * exact copy of libraries_get_version(), which uses a single $option argument,
+ * except for the fact that it uses multiple arguments. Since we support both
+ * type of version callbacks, detecting the version of a test library with this
+ * function ensures that the arguments are passed correctly. This function might
+ * be a useful reference for a custom version callback that uses multiple
+ * parameters.
+ *
+ * @param $library
+ *   An associative array containing all information about the library.
+ * @param $file
+ *   The filename to parse for the version, relative to the library path. For
+ *   example: 'docs/changelog.txt'.
+ * @param pattern
+ *   A string containing a regular expression (PCRE) to match the library
+ *   version. For example: '/@version (\d+)\.(\d+)/'.
+ * @param lines
+ *   (optional) The maximum number of lines to search the pattern in. Defaults
+ *   to 20.
+ * @param cols
+ *   (optional) The maximum number of characters per line to take into account.
+ *   Defaults to 200. In case of minified or compressed files, this prevents
+ *   reading the entire file into memory.
+ *
+ * @return
+ *   A string containing the version of the library.
+ *
+ * @see libraries_get_version()
+ */
+function _libraries_test_get_version($library, $file, $pattern, $lines = 20, $cols = 200) {
+
+  $file = DRUPAL_ROOT . '/' . $library['library path'] . '/' . $file;
+  if (!file_exists($file)) {
+    return;
+  }
+  $file = fopen($file, 'r');
+  while ($lines && $line = fgets($file, $cols)) {
+    if (preg_match($pattern, $line, $version)) {
+      fclose($file);
+      return $version[1];
+    }
+    $lines--;
+  }
+  fclose($file);
+}
+
+/**
+ * Detects the variant of an example library.
+ *
+ * Returns exactly the value of $installed, either TRUE or FALSE. This function
+ * cannot be collapsed with _libraries_test_return_version(), because of the
+ * different arguments that are passed automatically.
+ */
+function _libraries_test_return_installed($library, $name, $installed) {
+  return $installed;
+}
+
+/**
+ * Sets the 'info callback' key.
+ *
+ * This function is used as a test callback for the 'info' callback group.
+ *
+ * @see _libraries_test_callback()
+ */
+function _libraries_test_info_callback(&$library, $version, $variant) {
+  _libraries_test_callback($library, $version, $variant, 'info');
+}
+
+/**
+ * Sets the 'pre-detect callback' key.
+ *
+ * This function is used as a test callback for the 'pre-detect' callback group.
+ *
+ * @see _libraries_test_callback()
+ */
+function _libraries_test_pre_detect_callback(&$library, $version, $variant) {
+  _libraries_test_callback($library, $version, $variant, 'pre-detect');
+}
+
+/**
+ * Sets the 'post-detect callback' key.
+ *
+ * This function is used as a test callback for the 'post-detect callback group.
+ *
+ * @see _libraries_test_callback()
+ */
+function _libraries_test_post_detect_callback(&$library, $version, $variant) {
+  _libraries_test_callback($library, $version, $variant, 'post-detect');
+}
+
+/**
+ * Sets the 'pre-load callback' key.
+ *
+ * This function is used as a test callback for the 'pre-load' callback group.
+ *
+ * @see _libraries_test_callback()
+ */
+function _libraries_test_pre_load_callback(&$library, $version, $variant) {
+  _libraries_test_callback($library, $version, $variant, 'pre-load');
+}
+
+/**
+ * Sets the 'post-load callback' key.
+ *
+ * This function is used as a test callback for the 'post-load' callback group.
+ *
+ * @see _libraries_test_callback()
+ */
+function _libraries_test_post_load_callback(&$library, $version, $variant) {
+  _libraries_test_callback($library, $version, $variant, 'post-load');
+}
+
+/**
+ * Sets the '[group] callback' key, where [group] is prepare, detect, or load.
+ *
+ * This function is used as a test callback for the all callback groups.
+ *
+ * It sets the '[group] callback' (see above) key to 'applied ([part])' where
+ * [part] is either 'top-level', 'version x.y' (where x.y is the passed-in
+ * version string), 'variant example' (where example is the passed-in variant
+ * name), or 'version x.y, variant example' (see above), depending on the part
+ * of the library the passed-in library information belongs to.
+ *
+ * @param $library
+ *   An array of library information, which may be version- or variant-specific.
+ *   Passed by reference.
+ * @param $version
+ *   The version the library information passed in $library belongs to, or NULL
+ *   if the passed library information is not version-specific.
+ * @param $variant
+ *   The variant the library information passed in $library belongs to, or NULL
+ *   if the passed library information is not variant-specific.
+ */
+function _libraries_test_callback(&$library, $version, $variant, $group) {
+  $string = 'applied';
+  if (isset($version) && isset($variant)) {
+    $string .= " (version $version, variant $variant)";
+  }
+  elseif (isset($version)) {
+    $string .= " (version $version)";
+  }
+  elseif (isset($variant)) {
+    $string .= " (variant $variant)";
+  }
+  else {
+    $string .= ' (top-level)';
+  }
+  $library["$group callback"] = $string;
+
+  // The following is used to test caching of library information.
+  // Only set the message for the top-level library to prevent confusing,
+  // duplicate messages.
+  if (!isset($version) && !isset($variant) && \Drupal::state()->get('libraries_test.cache', FALSE)) {
+    drupal_set_message(SafeMarkup::set("The <em>$group</em> callback group was invoked."));
+  }
+}
+
+/**
+ * Implements hook_menu().
+ */
+function libraries_test_menu() {
+  $items['libraries_test/files'] = array(
+    'title' => 'Test files',
+    'route_name' => 'libraries_test_files',
+  );
+  $items['libraries_test/integration_files'] = array(
+    'title' => 'Test integration files',
+    'route_name' => 'libraries_test_integration_files',
+  );
+  $items['libraries_test/versions'] = array(
+    'title' => 'Test version loading',
+    'route_name' => 'libraries_test_versions',
+  );
+  $items['libraries_test/variant'] = array(
+    'title' => 'Test variant loading',
+    'route_name' => 'libraries_test_variant',
+  );
+  $items['libraries_test/versions_and_variants'] = array(
+    'title' => 'Test concurrent version and variant loading',
+    'route_name' => 'libraries_test_versions_and_variants',
+  );
+  $items['libraries_test/cache'] = array(
+    'title' => 'Test caching of library information',
+    'route_name' => 'libraries_test_cache',
+  );
+  return $items;
+}
diff --git a/web/modules/libraries/tests/modules/libraries_test/libraries_test.routing.yml b/web/modules/libraries/tests/modules/libraries_test/libraries_test.routing.yml
new file mode 100644
index 0000000000000000000000000000000000000000..a11f0bececf331cf93796d8a6031eaa6aff2c4e5
--- /dev/null
+++ b/web/modules/libraries/tests/modules/libraries_test/libraries_test.routing.yml
@@ -0,0 +1,36 @@
+libraries_test_files:
+  path: '/libraries_test/files'
+  defaults:
+    _controller: Drupal\libraries_test\Controller\ExampleController::files
+  requirements:
+    _access: 'TRUE'
+libraries_test_integration_files:
+  path: '/libraries_test/integration_files'
+  defaults:
+    _controller: Drupal\libraries_test\Controller\ExampleController::integration
+  requirements:
+    _access: 'TRUE'
+libraries_test_versions:
+  path: '/libraries_test/versions'
+  defaults:
+    _controller: Drupal\libraries_test\Controller\ExampleController::versions
+  requirements:
+    _access: 'TRUE'
+libraries_test_variant:
+  path: '/libraries_test/variant'
+  defaults:
+    _controller: Drupal\libraries_test\Controller\ExampleController::variant
+  requirements:
+    _access: 'TRUE'
+libraries_test_versions_and_variants:
+  path: '/libraries_test/versions_and_variants'
+  defaults:
+    _controller: Drupal\libraries_test\Controller\ExampleController::versionsAndVariants
+  requirements:
+    _access: 'TRUE'
+libraries_test_cache:
+  path: '/libraries_test/cache'
+  defaults:
+    _controller: Drupal\libraries_test\Controller\ExampleController::cache
+  requirements:
+    _access: 'TRUE'
diff --git a/web/modules/libraries/tests/modules/libraries_test/src/Controller/ExampleController.php b/web/modules/libraries/tests/modules/libraries_test/src/Controller/ExampleController.php
new file mode 100644
index 0000000000000000000000000000000000000000..217ebf2f8ab4028aff545bd7560818355f602957
--- /dev/null
+++ b/web/modules/libraries/tests/modules/libraries_test/src/Controller/ExampleController.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace Drupal\libraries_test\Controller;
+
+use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+class ExampleController implements ContainerInjectionInterface {
+
+  /**
+   * Injects BookManager Service.
+   */
+  public static function create(ContainerInterface $container) {
+    return new static();
+  }
+
+  /**
+   * Loads a specified library (variant) for testing.
+   *
+   * JavaScript and CSS files can be checked directly by SimpleTest, so we only
+   * need to manually check for PHP files. We provide information about the loaded
+   * JavaScript and CSS files for easier debugging. See example/README.txt for
+   * more information.
+   */
+  private function buildPage($library, $variant = NULL) {
+    libraries_load($library, $variant);
+    // JavaScript and CSS files can be checked directly by SimpleTest, so we only
+    // need to manually check for PHP files.
+    $output = '';
+
+    // For easer debugging of JS loading, a text is shown that the JavaScript will
+    // replace.
+    $output .= '<h2>JavaScript</h2>';
+    $output .= '<div class="libraries-test-javascript">';
+    $output .= 'If this text shows up, no JavaScript test file was loaded.';
+    $output .= '</div>';
+
+    // For easier debugging of CSS loading, the loaded CSS files will color the
+    // following text.
+    $output .= '<h2>CSS</h2>';
+    $output .= '<div class="libraries-test-css">';
+    $output .= 'If one of the CSS test files has been loaded, this text will be colored:';
+    $output .= '<ul>';
+    // Do not reference the actual CSS files (i.e. including '.css'), because that
+    // breaks testing.
+    $output .= '<li>example_1: red</li>';
+    $output .= '<li>example_2: green</li>';
+    $output .= '<li>example_3: orange</li>';
+    $output .= '<li>example_4: blue</li>';
+    $output .= '<li>libraries_test: purple</li>';
+    $output .= '</ul>';
+    $output .= '</div>';
+
+    $output .= '<h2>PHP</h2>';
+    $output .= '<div class="libraries-test-php">';
+    $output .= 'The following is a list of all loaded test PHP files:';
+    $output .= '<ul>';
+    $files = get_included_files();
+    foreach ($files as $file) {
+      if ((strpos($file, 'libraries/test') || strpos($file, 'libraries_test')) && !strpos($file, 'libraries_test.module') && !strpos($file, 'lib/Drupal/libraries_test')) {
+        $output .= '<li>' . str_replace(DRUPAL_ROOT . '/', '', $file) . '</li>';
+      }
+    }
+    $output .= '</ul>';
+    $output .= '</div>';
+
+    return ['#markup' => $output];
+  }
+
+  public function files() {
+    return $this->buildPage('example_files');
+  }
+
+  public function integration() {
+    return $this->buildPage('example_integration_files');
+  }
+
+  public function versions() {
+    return $this->buildPage('example_versions');
+  }
+
+  public function variant() {
+    return $this->buildPage('example_variant', 'example_variant');
+  }
+
+  public function versionsAndVariants() {
+    return $this->buildPage('example_versions_and_variants', 'example_variant_2');
+  }
+
+  public function cache() {
+    return $this->buildPage('example_callback');
+  }
+
+}
diff --git a/web/modules/libraries/tests/src/Functional/ExternalLibrary/Definition/DefinitionDiscoveryFactoryTest.php b/web/modules/libraries/tests/src/Functional/ExternalLibrary/Definition/DefinitionDiscoveryFactoryTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..14bf74e4f6ecfbbd5e1a7a693c4a88349af084be
--- /dev/null
+++ b/web/modules/libraries/tests/src/Functional/ExternalLibrary/Definition/DefinitionDiscoveryFactoryTest.php
@@ -0,0 +1,118 @@
+<?php
+
+namespace Drupal\Tests\libraries\Functional\ExternalLibrary\Definition;
+
+use Drupal\Tests\BrowserTestBase;
+
+/**
+ * Tests that remote library definitions are found and downloaded.
+ *
+ * This is a browser test because Guzzle is not usable from a kernel test.
+ *
+ * @group libraries
+ *
+ * @todo Make this a kernel test when https://www.drupal.org/node/2571475 is in.
+ */
+class DefinitionDiscoveryFactoryTest extends BrowserTestBase {
+
+  /**
+   * The 'libraries.settings' configuration object.
+   *
+   * @var \Drupal\Core\Config\Config
+   */
+  protected $config;
+
+  /**
+   * The path to the test library definitions.
+   *
+   * @var string
+   */
+  protected $definitionPath;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['libraries'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    /** @var \Drupal\Core\Config\ConfigFactoryInterface $config_factory */
+    $config_factory = $this->container->get('config.factory');
+    $this->config = $config_factory->getEditable('libraries.settings');
+
+    // Set up the remote library definition URL to point to the local website.
+    /** @var \Drupal\Core\Extension\ModuleHandlerInterface $module_handler */
+    $module_handler = $this->container->get('module_handler');
+    $module_path = $module_handler->getModule('libraries')->getPath();
+    $this->definitionPath = "$module_path/tests/library_definitions";
+  }
+
+  /**
+   * Tests that the discovery works according to the configuration.
+   */
+  public function testDiscovery() {
+    $library_id = 'test_asset_library';
+    $expected_definition = [
+      'type' => 'asset',
+      'version_detector' => [
+        'id' => 'static',
+        'configuration' => [
+          'version' => '1.0.0'
+        ],
+      ],
+      'remote_url' => 'http://example.com',
+      'css' => [
+        'base' => [
+          'example.css' => [],
+        ],
+      ],
+      'js' => [
+        'example.js' => [],
+      ],
+    ];
+    $discovery_service_id = 'libraries.definition.discovery';
+
+    // Test the local discovery with an incorrect path.
+    $this->config
+      ->set('definition.local.path', 'path/that/does/not/exist')
+      ->set('definition.remote.enable', FALSE)
+      ->save();
+    $discovery = $this->container->get($discovery_service_id);
+    $this->assertFalse($discovery->hasDefinition($library_id));
+
+    // Test the local discovery with a proper path.
+    $this->config
+      ->set('definition.local.path', $this->definitionPath)
+      ->save();
+    $discovery = $this->container->get($discovery_service_id);
+    $this->assertTrue($discovery->hasDefinition($library_id));
+
+    // Test a remote discovery with an incorrect path.
+    $definitions_directory = 'public://library-definitions';
+    $this->config
+      ->set('definition.local.path', $definitions_directory)
+      ->set('definition.remote.enable', TRUE)
+      ->set('definition.remote.urls', ["$this->baseUrl/path/that/does/not/exist"])
+      ->save();
+    $discovery = $this->container->get($discovery_service_id);
+    $this->assertFalse($discovery->hasDefinition($library_id));
+
+    // Test a remote discovery with a proper path.
+    $this->config
+      ->set('definition.remote.urls', ["$this->baseUrl/$this->definitionPath"])
+      ->save();
+    /** @var \Drupal\libraries\ExternalLibrary\Definition\DefinitionDiscoveryInterface $discovery */
+    $discovery = $this->container->get($discovery_service_id);
+    $definition_file = "$definitions_directory/$library_id.json";
+    $this->assertFalse(file_exists($definition_file));
+    $this->assertTrue($discovery->hasDefinition($library_id));
+    $this->assertFalse(file_exists($definition_file));
+    $this->assertEquals($discovery->getDefinition($library_id), $expected_definition);
+    $this->assertTrue(file_exists($definition_file));
+  }
+
+}
diff --git a/web/modules/libraries/tests/src/Kernel/ExternalLibrary/Asset/AssetLibraryTest.php b/web/modules/libraries/tests/src/Kernel/ExternalLibrary/Asset/AssetLibraryTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..cb2551e22fcd2132a1b8b1b8f267c246be1d67b8
--- /dev/null
+++ b/web/modules/libraries/tests/src/Kernel/ExternalLibrary/Asset/AssetLibraryTest.php
@@ -0,0 +1,114 @@
+<?php
+
+namespace Drupal\Tests\libraries\Kernel\ExternalLibrary\Asset;
+
+use Drupal\Tests\libraries\Kernel\ExternalLibrary\TestLibraryFilesStream;
+
+/**
+ * Tests that external asset libraries are registered as core asset libraries.
+ *
+ * @group libraries
+ */
+class AssetLibraryTest extends AssetLibraryTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getLibraryTypeId() {
+    return 'asset';
+  }
+
+  /**
+   * Tests that attachable asset library info is correctly gathered.
+   */
+  public function testAttachableAssetInfo() {
+    /** @var \Drupal\libraries\ExternalLibrary\Asset\AttachableAssetLibraryRegistrationInterface $library_type */
+    $library_type = $this->getLibraryType();
+    $library = $this->getLibrary();
+    $expected = [
+      'test_asset_library' => [
+        'version' => '1.0.0',
+        'css' => ['base' => ['http://example.com/example.css' => []]],
+        'js' => ['http://example.com/example.js' => []],
+        'dependencies' => [],
+      ],
+    ];
+    $this->assertEquals($expected, $library_type->getAttachableAssetLibraries($library, $this->libraryManager));
+  }
+
+  /**
+   * Tests that a remote asset library is registered as a core asset library.
+   *
+   * @see \Drupal\libraries\Extension\Extension
+   * @see \Drupal\libraries\Extension\ExtensionHandler
+   * @see \Drupal\libraries\ExternalLibrary\Asset\AssetLibrary
+   * @see \Drupal\libraries\ExternalLibrary\Asset\AssetLibraryTrait
+   * @see \Drupal\libraries\ExternalLibrary\ExternalLibraryManager
+   * @see \Drupal\libraries\ExternalLibrary\ExternalLibraryTrait
+   * @see \Drupal\libraries\ExternalLibrary\Registry\ExternalLibraryRegistry
+   */
+  public function testAssetLibraryRemote() {
+    $library = $this->coreLibraryDiscovery->getLibraryByName('libraries', 'test_asset_library');
+    $expected = [
+      'version' => '1.0.0',
+      'css' => [[
+        'weight' => -200,
+        'group' => 0,
+        'type' => 'external',
+        'data' => 'http://example.com/example.css',
+        'version' => '1.0.0',
+      ]],
+      'js' => [[
+        'group' => -100,
+        'type' => 'external',
+        'data' => 'http://example.com/example.js',
+        'version' => '1.0.0',
+      ]],
+      'dependencies' => [],
+      'license' => [
+        'name' => 'GNU-GPL-2.0-or-later',
+        'url' => 'https://www.drupal.org/licensing/faq',
+        'gpl-compatible' => TRUE,
+      ]
+    ];
+    $this->assertEquals($expected, $library);
+  }
+
+  /**
+   * Tests that a local asset library is registered as a core asset library.
+   */
+  public function testAssetLibraryLocal() {
+    $this->container->set('stream_wrapper.asset_libraries', new TestLibraryFilesStream(
+      $this->container->get('module_handler'),
+      $this->container->get('string_translation'),
+      'assets/vendor'
+    ));
+    $this->coreLibraryDiscovery->clearCachedDefinitions();
+    $library = $this->coreLibraryDiscovery->getLibraryByName('libraries', 'test_asset_library');
+    $expected = [
+      'version' => '1.0.0',
+      'css' => [[
+        'weight' => -200,
+        'group' => 0,
+        'type' => 'file',
+        'data' => $this->modulePath . '/tests/assets/vendor/test_asset_library/example.css',
+        'version' => '1.0.0',
+      ]],
+      'js' => [[
+        'group' => -100,
+        'type' => 'file',
+        'data' => $this->modulePath . '/tests/assets/vendor/test_asset_library/example.js',
+        'version' => '1.0.0',
+        'minified' => FALSE,
+      ]],
+      'dependencies' => [],
+      'license' => [
+        'name' => 'GNU-GPL-2.0-or-later',
+        'url' => 'https://www.drupal.org/licensing/faq',
+        'gpl-compatible' => TRUE,
+      ]
+    ];
+    $this->assertEquals($expected, $library);
+  }
+
+}
diff --git a/web/modules/libraries/tests/src/Kernel/ExternalLibrary/Asset/AssetLibraryTestBase.php b/web/modules/libraries/tests/src/Kernel/ExternalLibrary/Asset/AssetLibraryTestBase.php
new file mode 100644
index 0000000000000000000000000000000000000000..4f835b72550fd0bf5909aeb5d0d6667b13af9978
--- /dev/null
+++ b/web/modules/libraries/tests/src/Kernel/ExternalLibrary/Asset/AssetLibraryTestBase.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Drupal\Tests\libraries\Kernel\ExternalLibrary\Asset;
+
+use Drupal\Tests\libraries\Kernel\LibraryTypeKernelTestBase;
+
+/**
+ * Provides a base test class for asset library type tests.
+ */
+abstract class AssetLibraryTestBase extends LibraryTypeKernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   *
+   * LibraryManager requires system_get_info() which is in system.module.
+   *
+   * @see \Drupal\libraries\ExternalLibrary\LibraryManager::getRequiredLibraryIds()
+   */
+  public static $modules = ['system'];
+
+  /**
+   * The Drupal core library discovery.
+   *
+   * @var \Drupal\Core\Asset\LibraryDiscoveryInterface
+   */
+  protected $coreLibraryDiscovery;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->coreLibraryDiscovery = $this->container->get('library.discovery');
+  }
+
+}
diff --git a/web/modules/libraries/tests/src/Kernel/ExternalLibrary/Asset/MultipleAssetLibraryTest.php b/web/modules/libraries/tests/src/Kernel/ExternalLibrary/Asset/MultipleAssetLibraryTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..4a2af5c3d7a7424a13dd1d30faa93c0d040be8fe
--- /dev/null
+++ b/web/modules/libraries/tests/src/Kernel/ExternalLibrary/Asset/MultipleAssetLibraryTest.php
@@ -0,0 +1,172 @@
+<?php
+
+namespace Drupal\Tests\libraries\Kernel\ExternalLibrary\Asset;
+
+use Drupal\Tests\libraries\Kernel\ExternalLibrary\TestLibraryFilesStream;
+
+/**
+ * Tests that external asset libraries can register multiple core libraries.
+ *
+ * @group libraries
+ */
+class MultipleAssetLibraryTest extends AssetLibraryTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getLibraryTypeId() {
+    return 'asset_multiple';
+  }
+
+  /**
+   * Tests that attachable asset library info is correctly gathered.
+   */
+  public function testAttachableAssetInfo() {
+    /** @var \Drupal\libraries\ExternalLibrary\Asset\AttachableAssetLibraryRegistrationInterface $library_type */
+    $library_type = $this->getLibraryType();
+    $library = $this->getLibrary();
+    $expected = [
+      'test_asset_multiple_library.first' => [
+        'version' => '1.0.0',
+        'css' => ['base' => ['http://example.com/example.first.css' => []]],
+        'js' => ['http://example.com/example.first.js' => []],
+        'dependencies' => [],
+      ],
+      'test_asset_multiple_library.second' => [
+        'version' => '1.0.0',
+        'css' => ['base' => ['http://example.com/example.second.css' => []]],
+        'js' => ['http://example.com/example.second.js' => []],
+        'dependencies' => [],
+      ],
+    ];
+    $this->assertEquals($expected, $library_type->getAttachableAssetLibraries($library, $this->libraryManager));
+  }
+
+  /**
+   * Tests that a remote asset library is registered as a core asset library.
+   *
+   * @see \Drupal\libraries\Extension\Extension
+   * @see \Drupal\libraries\Extension\ExtensionHandler
+   * @see \Drupal\libraries\ExternalLibrary\Asset\AssetLibrary
+   * @see \Drupal\libraries\ExternalLibrary\Asset\AssetLibraryTrait
+   * @see \Drupal\libraries\ExternalLibrary\ExternalLibraryManager
+   * @see \Drupal\libraries\ExternalLibrary\ExternalLibraryTrait
+   * @see \Drupal\libraries\ExternalLibrary\Registry\ExternalLibraryRegistry
+   */
+  public function testAssetLibraryRemote() {
+    $library = $this->coreLibraryDiscovery->getLibraryByName('libraries', 'test_asset_multiple_library.first');
+    $expected = [
+      'version' => '1.0.0',
+      'css' => [[
+        'weight' => -200,
+        'group' => 0,
+        'type' => 'external',
+        'data' => 'http://example.com/example.first.css',
+        'version' => '1.0.0',
+      ]],
+      'js' => [[
+        'group' => -100,
+        'type' => 'external',
+        'data' => 'http://example.com/example.first.js',
+        'version' => '1.0.0',
+      ]],
+      'dependencies' => [],
+      'license' => [
+        'name' => 'GNU-GPL-2.0-or-later',
+        'url' => 'https://www.drupal.org/licensing/faq',
+        'gpl-compatible' => TRUE,
+      ]
+    ];
+    $this->assertEquals($expected, $library);
+
+    $library = $this->coreLibraryDiscovery->getLibraryByName('libraries', 'test_asset_multiple_library.second');
+    $expected = [
+      'version' => '1.0.0',
+      'css' => [[
+        'weight' => -200,
+        'group' => 0,
+        'type' => 'external',
+        'data' => 'http://example.com/example.second.css',
+        'version' => '1.0.0',
+      ]],
+      'js' => [[
+        'group' => -100,
+        'type' => 'external',
+        'data' => 'http://example.com/example.second.js',
+        'version' => '1.0.0',
+      ]],
+      'dependencies' => [],
+      'license' => [
+        'name' => 'GNU-GPL-2.0-or-later',
+        'url' => 'https://www.drupal.org/licensing/faq',
+        'gpl-compatible' => TRUE,
+      ]
+    ];
+    $this->assertEquals($expected, $library);
+  }
+
+  /**
+   * Tests that a local asset library is registered as a core asset library.
+   */
+  public function testAssetLibraryLocal() {
+    $this->container->set('stream_wrapper.asset_libraries', new TestLibraryFilesStream(
+      $this->container->get('module_handler'),
+      $this->container->get('string_translation'),
+      'assets/vendor'
+    ));
+    $this->coreLibraryDiscovery->clearCachedDefinitions();
+
+    $library = $this->coreLibraryDiscovery->getLibraryByName('libraries', 'test_asset_multiple_library.first');
+    $expected = [
+      'version' => '1.0.0',
+      'css' => [[
+        'weight' => -200,
+        'group' => 0,
+        'type' => 'file',
+        'data' => $this->modulePath . '/tests/assets/vendor/test_asset_multiple_library/example.first.css',
+        'version' => '1.0.0',
+      ]],
+      'js' => [[
+        'group' => -100,
+        'type' => 'file',
+        'data' => $this->modulePath . '/tests/assets/vendor/test_asset_multiple_library/example.first.js',
+        'version' => '1.0.0',
+        'minified' => FALSE,
+      ]],
+      'dependencies' => [],
+      'license' => [
+        'name' => 'GNU-GPL-2.0-or-later',
+        'url' => 'https://www.drupal.org/licensing/faq',
+        'gpl-compatible' => TRUE,
+      ]
+    ];
+    $this->assertEquals($expected, $library);
+
+    $library = $this->coreLibraryDiscovery->getLibraryByName('libraries', 'test_asset_multiple_library.second');
+    $expected = [
+      'version' => '1.0.0',
+      'css' => [[
+        'weight' => -200,
+        'group' => 0,
+        'type' => 'file',
+        'data' => $this->modulePath . '/tests/assets/vendor/test_asset_multiple_library/example.second.css',
+        'version' => '1.0.0',
+      ]],
+      'js' => [[
+        'group' => -100,
+        'type' => 'file',
+        'data' => $this->modulePath . '/tests/assets/vendor/test_asset_multiple_library/example.second.js',
+        'version' => '1.0.0',
+        'minified' => FALSE,
+      ]],
+      'dependencies' => [],
+      'license' => [
+        'name' => 'GNU-GPL-2.0-or-later',
+        'url' => 'https://www.drupal.org/licensing/faq',
+        'gpl-compatible' => TRUE,
+      ]
+    ];
+    $this->assertEquals($expected, $library);
+  }
+
+}
diff --git a/web/modules/libraries/tests/src/Kernel/ExternalLibrary/GlobalLocatorTest.php b/web/modules/libraries/tests/src/Kernel/ExternalLibrary/GlobalLocatorTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..84eb86eb1e532f06e8ded013c6f9b6da93587d34
--- /dev/null
+++ b/web/modules/libraries/tests/src/Kernel/ExternalLibrary/GlobalLocatorTest.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace Drupal\Tests\libraries\Kernel\ExternalLibrary;
+
+use Drupal\Tests\libraries\Kernel\ExternalLibrary\TestLibraryFilesStream;
+use Drupal\Tests\libraries\Kernel\LibraryTypeKernelTestBase;
+
+/**
+ * Tests that a global locator can be properly used to load a libraries.
+ *
+ * @group libraries
+ */
+class GlobalLocatorTest extends LibraryTypeKernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    // Assign our test stream (which points to the test php lib) to the asset
+    // scheme. This gives us a scheme to work with in the test that is not
+    // used to locate a php lib by default.
+    $this->container->set('stream_wrapper.asset_libraries', new TestLibraryFilesStream(
+      $this->container->get('module_handler'),
+      $this->container->get('string_translation'),
+      'libraries'
+    ));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getLibraryTypeId() {
+    return 'php_file';
+  }
+
+  /**
+   * Tests that the library is located via the global loactor.
+   */
+  public function testGlobalLocator() {
+    // By default the library will not be locatable (control assertion) until we
+    // add the asset stream to the global loctors conf list.
+    $library = $this->getLibrary();
+    $this->assertFalse($library->isInstalled());
+    $config_factory = $this->container->get('config.factory');
+    $config_factory->getEditable('libraries.settings')
+      ->set('global_locators', [['id' => 'uri', 'configuration' => ['uri' => 'asset://']]])
+      ->save();
+    $library = $this->getLibrary();
+    $this->assertTrue($library->isInstalled());
+  }
+
+}
diff --git a/web/modules/libraries/tests/src/Kernel/ExternalLibrary/PhpFile/PhpFileLibraryTest.php b/web/modules/libraries/tests/src/Kernel/ExternalLibrary/PhpFile/PhpFileLibraryTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..10c1d2cfee45cc789d1632925badba861e15391a
--- /dev/null
+++ b/web/modules/libraries/tests/src/Kernel/ExternalLibrary/PhpFile/PhpFileLibraryTest.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace Drupal\Tests\libraries\Kernel\ExternalLibrary\PhpFile;
+
+use Drupal\Tests\libraries\Kernel\ExternalLibrary\TestLibraryFilesStream;
+use Drupal\Tests\libraries\Kernel\LibraryTypeKernelTestBase;
+
+/**
+ * Tests that the external library manager properly loads PHP file libraries.
+ *
+ * @group libraries
+ */
+class PhpFileLibraryTest extends LibraryTypeKernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->container->set('stream_wrapper.php_file_libraries', new TestLibraryFilesStream(
+      $this->container->get('module_handler'),
+      $this->container->get('string_translation'),
+      'libraries'
+    ));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getLibraryTypeId() {
+    return 'php_file';
+  }
+
+  /**
+   * Tests that the list of PHP files is correctly gathered.
+   */
+  public function testPhpFileInfo() {
+    /** @var \Drupal\libraries\ExternalLibrary\PhpFile\PhpFileLibrary $library */
+    $library = $this->getLibrary();
+    $this->assertTrue($library->isInstalled());
+    $library_path = $this->modulePath . '/tests/libraries/test_php_file_library';
+    $this->assertEquals($library_path, $library->getLocalPath());
+    $this->assertEquals(["$library_path/test_php_file_library.php"], $library->getPhpFiles());
+  }
+
+  /**
+   * Tests that the external library manager properly loads PHP files.
+   *
+   * @see \Drupal\libraries\ExternalLibrary\ExternalLibraryManager
+   * @see \Drupal\libraries\ExternalLibrary\ExternalLibraryTrait
+   * @see \Drupal\libraries\ExternalLibrary\PhpFile\PhpRequireLoader
+   */
+  public function testFileLoading() {
+    $function_name = '_libraries_test_php_function';
+    if (function_exists($function_name)) {
+      $this->markTestSkipped('Cannot test file inclusion if the file to be included has already been included prior.');
+      return;
+    }
+
+    $this->assertFalse(function_exists($function_name));
+    $this->libraryManager->load('test_php_file_library');
+    $this->assertTrue(function_exists($function_name));
+  }
+
+}
diff --git a/web/modules/libraries/tests/src/Kernel/ExternalLibrary/TestLibraryFilesStream.php b/web/modules/libraries/tests/src/Kernel/ExternalLibrary/TestLibraryFilesStream.php
new file mode 100644
index 0000000000000000000000000000000000000000..ec20647ead3187b8a3cee8a35cd5dfc9d101adb4
--- /dev/null
+++ b/web/modules/libraries/tests/src/Kernel/ExternalLibrary/TestLibraryFilesStream.php
@@ -0,0 +1,80 @@
+<?php
+
+namespace Drupal\Tests\libraries\Kernel\ExternalLibrary;
+
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\StreamWrapper\LocalStream;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\StringTranslation\TranslationInterface;
+use Drupal\libraries\StreamWrapper\LocalHiddenStreamTrait;
+use Drupal\libraries\StreamWrapper\PrivateStreamTrait;
+
+/**
+ * Provides a stream wrapper for accessing test library files.
+ */
+class TestLibraryFilesStream extends LocalStream {
+
+  use LocalHiddenStreamTrait;
+  use PrivateStreamTrait;
+  use StringTranslationTrait;
+
+  /**
+   * The module handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected $moduleHandler;
+
+  /**
+   * The test directory.
+   *
+   * @var string
+   */
+  protected $directory;
+
+  /**
+   * Constructs a stream wrapper for test library files.
+   *
+   * Dependency injection is generally not possible to implement for stream
+   * wrappers, because stream wrappers are initialized before the container is
+   * booted, but this stream wrapper is only registered explicitly from tests
+   * so it is possible here.
+   *
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler.
+   * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
+   *   The string translation handler.
+   * @param string $directory
+   *   The directory within the Libraries API's tests directory that is to be
+   *   searched for test library files.
+   */
+  public function __construct(ModuleHandlerInterface $module_handler, TranslationInterface $string_translation, $directory) {
+    $this->moduleHandler = $module_handler;
+    $this->directory = (string) $directory;
+
+    $this->setStringTranslation($string_translation);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getName() {
+    $this->t('Test library files');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDescription() {
+    $this->t('Provides access to test library files.');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDirectoryPath() {
+    $module_path = $this->moduleHandler->getModule('libraries')->getPath();
+    return $module_path . '/tests/' . $this->directory;
+  }
+
+}
diff --git a/web/modules/libraries/tests/src/Kernel/LibraryTypeKernelTestBase.php b/web/modules/libraries/tests/src/Kernel/LibraryTypeKernelTestBase.php
new file mode 100644
index 0000000000000000000000000000000000000000..8185794763c2cb88df2c3479805987cf2a02e68e
--- /dev/null
+++ b/web/modules/libraries/tests/src/Kernel/LibraryTypeKernelTestBase.php
@@ -0,0 +1,171 @@
+<?php
+
+namespace Drupal\Tests\libraries\Kernel;
+
+use Drupal\Component\Plugin\Exception\PluginException;
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\libraries\ExternalLibrary\Exception\LibraryDefinitionNotFoundException;
+use Drupal\libraries\ExternalLibrary\Exception\LibraryTypeNotFoundException;
+use Drupal\libraries\ExternalLibrary\LibraryInterface;
+use Drupal\libraries\ExternalLibrary\Type\LibraryTypeInterface;
+
+/**
+ * Provides an improved version of the core kernel test base class.
+ */
+abstract class LibraryTypeKernelTestBase extends KernelTestBase {
+
+  /**
+   * The external library manager.
+   *
+   * @var \Drupal\libraries\ExternalLibrary\LibraryManagerInterface
+   */
+  protected $libraryManager;
+
+  /**
+   * The library type factory.
+   *
+   * @var \Drupal\Component\Plugin\Factory\FactoryInterface
+   */
+  protected $libraryTypeFactory;
+
+  /**
+   * The absolute path to the Libraries API module.
+   *
+   * @var string
+   */
+  protected $modulePath;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['libraries', 'libraries_test'];
+
+  /**
+   * Gets the ID of the library type that is being tested.
+   *
+   * @return string
+   */
+  abstract protected function getLibraryTypeId();
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    /** @var \Drupal\Core\Extension\ModuleHandlerInterface $module_handler */
+    $module_handler = $this->container->get('module_handler');
+    $this->modulePath = $module_handler->getModule('libraries')->getPath();
+
+    $this->installConfig('libraries');
+    // Disable remote definition fetching and set the local definitions path to
+    // the module directory.
+    /** @var \Drupal\Core\Config\ConfigFactoryInterface $config_factory */
+    $config_factory = $this->container->get('config.factory');
+    $config_factory->getEditable('libraries.settings')
+      ->set('definition.local.path', "{$this->modulePath}/tests/library_definitions")
+      ->set('definition.remote.enable', FALSE)
+      ->save();
+
+    // LibrariesConfigSubscriber::onConfigSave() invalidates the container so
+    // that it is rebuilt on the next request. We need the container rebuilt
+    // immediately, however.
+    /** @var \Drupal\Core\DrupalKernelInterface $kernel */
+    $kernel = $this->container->get('kernel');
+    $this->container = $kernel->rebuildContainer();
+
+    $this->libraryManager = $this->container->get('libraries.manager');
+    $this->libraryTypeFactory = $this->container->get('plugin.manager.libraries.library_type');
+  }
+
+  /**
+   * Tests that the library type can be instantiated.
+   */
+  public function testLibraryType() {
+    $type_id = $this->getLibraryTypeId();
+    try {
+      $this->libraryTypeFactory->createInstance($type_id);
+      $this->assertTrue(TRUE, "Library type '$type_id' can be instantiated.");
+    }
+    catch (PluginException $exception) {
+      $this->fail("Library type '$type_id' cannot be instantiated.");
+    }
+  }
+
+  /**
+   * Tests that the test library can be instantiated.
+   */
+  public function testLibrary() {
+    $type_id = $this->getLibraryTypeId();
+    $id = $this->getLibraryId();
+    try {
+      $library = $this->libraryManager->getLibrary($id);
+      $this->assertTrue(TRUE, "Test $type_id library can be instantiated.");
+      $this->assertInstanceOf($this->getLibraryType()->getLibraryClass(), $library);
+      $this->assertEquals($this->getLibraryId(), $library->getId());
+
+    }
+    catch (LibraryDefinitionNotFoundException $exception) {
+      $this->fail("Missing library definition for test $type_id library.");
+    }
+    catch (LibraryTypeNotFoundException $exception) {
+      $this->fail("Missing library type declaration for test $type_id library.");
+    }
+  }
+
+  /**
+   * Returns the library type that is being tested.
+   *
+   * @return \Drupal\libraries\ExternalLibrary\Type\LibraryTypeInterface
+   *   The test library type.
+   */
+  protected function getLibraryType() {
+    try {
+      $library_type = $this->libraryTypeFactory->createInstance($this->getLibraryTypeId());
+    }
+    catch (PluginException $exception) {
+      $library_type = $this->prophesize(LibraryTypeInterface::class)->reveal();
+    }
+    finally {
+      return $library_type;
+    }
+  }
+
+  /**
+   * Retuns the library ID of the library used in the test.
+   *
+   * Defaults to 'test_[library_type]_library', where [library_type] is the
+   * ID of the library type being tested.
+   *
+   * @return string
+   */
+  protected function getLibraryId() {
+    $type_id = $this->getLibraryTypeId();
+    return "test_{$type_id}_library";
+  }
+
+  /**
+   * Returns the test library for this library type.
+   *
+   * @return \Drupal\libraries\ExternalLibrary\LibraryInterface
+   *   The test library.
+   */
+  protected function getLibrary() {
+    try {
+      $library = $this->libraryManager->getLibrary($this->getLibraryId());
+    }
+    catch (LibraryDefinitionNotFoundException $exception) {
+      $library = $this->prophesize(LibraryInterface::class)->reveal();
+    }
+    catch (LibraryTypeNotFoundException $exception) {
+      $library = $this->prophesize(LibraryInterface::class)->reveal();
+    }
+    catch (PluginException $exception) {
+      $library = $this->prophesize(LibraryInterface::class)->reveal();
+    }
+    finally {
+      return $library;
+    }
+  }
+
+}
diff --git a/web/modules/libraries/tests/src/Unit/Plugin/libraries/VersionDetector/LinePatternDetectorTest.php b/web/modules/libraries/tests/src/Unit/Plugin/libraries/VersionDetector/LinePatternDetectorTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..5e4e34b93148f7b195e3bae014ac18db940870c5
--- /dev/null
+++ b/web/modules/libraries/tests/src/Unit/Plugin/libraries/VersionDetector/LinePatternDetectorTest.php
@@ -0,0 +1,197 @@
+<?php
+
+namespace Drupal\Tests\libraries\Unit\Plugin\libraries\VersionDetector;
+
+use Drupal\libraries\ExternalLibrary\Local\LocalLibraryInterface;
+use Drupal\libraries\ExternalLibrary\Version\VersionedLibraryInterface;
+use Drupal\libraries\Plugin\libraries\VersionDetector\LinePatternDetector;
+use Drupal\Tests\UnitTestCase;
+use org\bovigo\vfs\vfsStream;
+
+/**
+ * Tests the line pattern version detector.
+ *
+ * @group libraries
+ *
+ * @coversDefaultClass \Drupal\libraries\Plugin\libraries\VersionDetector\LinePatternDetector
+ */
+class LinePatternDetectorTest extends UnitTestCase {
+
+  protected $libraryId = 'test_library';
+
+  /**
+   * Tests that version detection fails for a non-local library.
+   *
+   * @expectedException \Drupal\libraries\ExternalLibrary\Exception\UnknownLibraryVersionException
+   *
+   * @covers ::detectVersion
+   */
+  public function testDetectVersionNonLocal() {
+    $library = $this->prophesize(VersionedLibraryInterface::class);
+    $detector = $this->setupDetector();
+    $detector->detectVersion($library->reveal());
+  }
+
+  /**
+   * Tests that version detection fails for a missing file.
+   *
+   * @expectedException \Drupal\libraries\ExternalLibrary\Exception\UnknownLibraryVersionException
+   *
+   * @covers ::detectVersion
+   */
+  public function testDetectVersionMissingFile() {
+    $library = $this->setupLibrary();
+
+    $detector = $this->setupDetector(['file' => 'CHANGELOG.txt']);
+    $detector->detectVersion($library->reveal());
+  }
+
+  /**
+   * Tests that version detection fails without a version in the file.
+   *
+   * @dataProvider providerTestDetectVersionNoVersion
+   *
+   * @covers ::detectVersion
+   */
+  public function testDetectVersionNoVersion($configuration, $file_contents) {
+    $library = $this->setupLibrary();
+
+    $detector = $this->setupDetector($configuration);
+    $this->setupFile($configuration['file'], $file_contents);
+
+    $library->setVersion()->shouldNotBeCalled();
+    $detector->detectVersion($library->reveal());
+  }
+
+  /**
+   * @return array
+   */
+  public function providerTestDetectVersionNoVersion() {
+    $test_cases = [];
+
+    $configuration = [
+      'file' => 'CHANGELOG.txt',
+      'pattern' => '/@version (\d+\.\d+\.\d+)/'
+    ];
+
+    $test_cases['empty_file'] = [$configuration, ''];
+
+    $test_cases['no_version'] = [$configuration, <<<EOF
+This is a file with
+multiple lines that does
+not contain a version.
+EOF
+    ];
+
+    $configuration['lines'] = 3;
+    $test_cases['long_file'] = [$configuration, <<<EOF
+This is a file that
+contains the version after
+the maximum number of lines
+to test has been surpassed.
+
+@version 1.2.3
+EOF
+    ];
+
+    $configuration['columns'] = 10;
+    // @todo Document why this is necessary.
+    $configuration['lines'] = 2;
+    $test_cases['long_column'] = [$configuration, <<<EOF
+This is a file that contains the version after
+the maximum number of columns to test has been surpassed. @version 1.2.3
+EOF
+    ];
+
+    return $test_cases;
+  }
+
+  /**
+   * Tests that version detection succeeds with a version in the file.
+   *
+   * @dataProvider providerTestDetectVersion
+   *
+   * @covers ::detectVersion
+   */
+  public function testDetectVersion($configuration, $file_contents, $version) {
+    $library = $this->setupLibrary();
+
+    $detector = $this->setupDetector($configuration);
+    $this->setupFile($configuration['file'], $file_contents);
+
+    $library->setVersion($version)->shouldBeCalled();
+    $detector->detectVersion($library->reveal());
+  }
+
+  /**
+   * @return array
+   */
+  public function providerTestDetectVersion() {
+    $test_cases = [];
+
+    $configuration = [
+      'file' => 'CHANGELOG.txt',
+      'pattern' => '/@version (\d+\.\d+\.\d+)/'
+    ];
+    $version = '1.2.3';
+
+    $test_cases['version'] = [$configuration, <<<EOF
+This a file with a version
+
+@version $version
+EOF
+    , $version];
+
+    return $test_cases;
+  }
+
+  /**
+   * Sets up the library prophecy and returns it.
+   *
+   * @return \Prophecy\Prophecy\ObjectProphecy
+   */
+  protected function setupLibrary() {
+    $library = $this->prophesize(VersionedLibraryInterface::class);
+    $library->willImplement(LocalLibraryInterface::class);
+    $library->getId()->willReturn($this->libraryId);
+    $library->getLocalPath()->willReturn('libraries/' . $this->libraryId);
+    return $library;
+  }
+
+  /**
+   * Sets up the version detector for testing and returns it.
+   *
+   * @param array $configuration
+   *   The plugin configuration to set the version detector up with.
+   *
+   * @return \Drupal\libraries\Plugin\libraries\VersionDetector\LinePatternDetector
+   *   The line pattern version detector to test.
+   */
+  protected function setupDetector(array $configuration = []) {
+    $app_root = 'root';
+    vfsStream::setup($app_root);
+
+    $plugin_id = 'line_pattern';
+    $plugin_definition = [
+      'id' => $plugin_id,
+      'class' => LinePatternDetector::class,
+      'provider' => 'libraries',
+    ];
+    return new LinePatternDetector($configuration, $plugin_id, $plugin_definition, 'vfs://' . $app_root);
+  }
+
+  /**
+   * @param $file
+   * @param $file_contents
+   */
+  protected function setupFile($file, $file_contents) {
+    vfsStream::create([
+      'libraries' => [
+        $this->libraryId => [
+          $file => $file_contents,
+        ],
+      ],
+    ]);
+  }
+
+}