diff --git a/SOURCES/pki-core-Change-var-TPS-to-tps.patch b/SOURCES/pki-core-Change-var-TPS-to-tps.patch
new file mode 100644
index 0000000..25705df
--- /dev/null
+++ b/SOURCES/pki-core-Change-var-TPS-to-tps.patch
@@ -0,0 +1,55 @@
+From 6d535b39fca585b9db2e314f75dc1bcb41ba2480 Mon Sep 17 00:00:00 2001
+From: Alexander Scheel <ascheel@redhat.com>
+Date: Mon, 22 Feb 2021 12:09:02 -0500
+Subject: [PATCH] Fix regression in TPS System menu
+
+When backporting 1dbb07f8e41b4809b0f41a7643c37301fcf712d8 from PKI
+10.9+, there was a hidden dependency on
+c8f4fbc561b89603bc651395d3f995d6d4cac9b4, where the TPS variable was
+renamed from lowercase tps. In pulling the patch, this variable was
+renamed to uppercase TPS, breaking usages of lowercase tps in index.js.
+
+Best to use lowercase and avoid pulling in c8f4fbc -- which in turn
+depends on other commits (such as
+f85f8e9d2fc16436757b26b7d10130435c36455b).
+
+Signed-off-by: Alexander Scheel <ascheel@redhat.com>
+(cherry picked from commit abc9e127a4704382d6069e7be8726e2c4436e821)
+---
+ base/tps/shared/webapps/tps/js/tps.js | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/base/tps/shared/webapps/tps/js/tps.js b/base/tps/shared/webapps/tps/js/tps.js
+index f12f7b4..61007fd 100644
+--- a/base/tps/shared/webapps/tps/js/tps.js
++++ b/base/tps/shared/webapps/tps/js/tps.js
+@@ -19,7 +19,7 @@
+  * @author Endi S. Dewata
+  */
+ 
+-var TPS = {
++var tps = {
+     PROFILE_ID_PATTERN: /^[a-zA-Z0-9_]+$/,
+     PROPERTY_NAME_PATTERN: /^[a-zA-Z0-9_\.]+$/,
+     getElementName: function (component) {
+@@ -133,7 +133,7 @@ var PropertiesTable = Table.extend({
+     addEntry: function(entry) {
+         var self = this;
+ 
+-        if (!entry.name.match(TPS.PROPERTY_NAME_PATTERN)) {
++        if (!entry.name.match(tps.PROPERTY_NAME_PATTERN)) {
+             throw "Invalid property name: " + entry.name;
+         }
+ 
+@@ -432,7 +432,7 @@ var ConfigEntryPage = EntryPage.extend({
+     save: function() {
+         var self = this;
+ 
+-        if (!self.entry.profileID.match(TPS.PROFILE_ID_PATTERN)) {
++        if (!self.entry.profileID.match(tps.PROFILE_ID_PATTERN)) {
+             throw "Invalid profile ID: " + self.entry.profileID;
+         }
+ 
+-- 
+1.8.3.1
+
diff --git a/SOURCES/pki-core-Fix-AddProfileCaAuditSigningCert.patch b/SOURCES/pki-core-Fix-AddProfileCaAuditSigningCert.patch
new file mode 100644
index 0000000..ca3f8e2
--- /dev/null
+++ b/SOURCES/pki-core-Fix-AddProfileCaAuditSigningCert.patch
@@ -0,0 +1,127 @@
+From 8f06b5064c30393bf1ce7c4d28e1f3284e8cea07 Mon Sep 17 00:00:00 2001
+From: "Endi S. Dewata" <edewata@redhat.com>
+Date: Fri, 4 Dec 2020 10:15:11 -0600
+Subject: [PATCH] Fix 01-AddProfileCaAuditSigningCert
+
+The upgrade script that is supposed to add caAuditSigningCert
+profile into existing instances failed since the actual profile
+configuration is packaged in pki-ca which is installed after
+the upgrade is done.
+
+To fix the problem, the upgrade script has been modified to
+embed the content of the profile configuration.
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1883639
+(cherry picked from commit 8533fdc51695d5965b7256ffbb73fd928097f7fd)
+---
+ .../10.5.18/01-AddProfileCaAuditSigningCert        | 87 +++++++++++++++++++++-
+ 1 file changed, 86 insertions(+), 1 deletion(-)
+
+diff --git a/base/server/upgrade/10.5.18/01-AddProfileCaAuditSigningCert b/base/server/upgrade/10.5.18/01-AddProfileCaAuditSigningCert
+index 5cec8d9..c142325 100644
+--- a/base/server/upgrade/10.5.18/01-AddProfileCaAuditSigningCert
++++ b/base/server/upgrade/10.5.18/01-AddProfileCaAuditSigningCert
+@@ -17,6 +17,88 @@ logger = logging.getLogger(__name__)
+ 
+ class AddProfileCaAuditSigningCert(pki.server.upgrade.PKIServerUpgradeScriptlet):
+ 
++    caAuditSigningCert = """desc=This certificate profile is for enrolling audit signing certificates.
++visible=true
++enable=true
++enableBy=admin
++auth.instance_id=
++authz.acl=group="Enterprise OCSP Administrators" || group="Enterprise RA Administrators" || group="Enterprise CA Administrators" || group="Enterprise KRA Administrators" || group="Enterprise TKS Administrators" || group="Enterprise TPS Administrators"
++name=Manual Audit Signing Certificate Enrollment
++input.list=i1,i2
++input.i1.class_id=certReqInputImpl
++input.i2.class_id=submitterInfoInputImpl
++output.list=o1
++output.o1.class_id=certOutputImpl
++policyset.list=auditSigningCertSet
++policyset.auditSigningCertSet.list=1,2,3,4,5,6,9
++policyset.auditSigningCertSet.1.constraint.class_id=subjectNameConstraintImpl
++policyset.auditSigningCertSet.1.constraint.name=Subject Name Constraint
++policyset.auditSigningCertSet.1.constraint.params.pattern=CN=.*
++policyset.auditSigningCertSet.1.constraint.params.accept=true
++policyset.auditSigningCertSet.1.default.class_id=userSubjectNameDefaultImpl
++policyset.auditSigningCertSet.1.default.name=Subject Name Default
++policyset.auditSigningCertSet.1.default.params.name=
++policyset.auditSigningCertSet.2.constraint.class_id=validityConstraintImpl
++policyset.auditSigningCertSet.2.constraint.name=Validity Constraint
++policyset.auditSigningCertSet.2.constraint.params.range=720
++policyset.auditSigningCertSet.2.constraint.params.notBeforeCheck=false
++policyset.auditSigningCertSet.2.constraint.params.notAfterCheck=false
++policyset.auditSigningCertSet.2.default.class_id=validityDefaultImpl
++policyset.auditSigningCertSet.2.default.name=Validity Default
++policyset.auditSigningCertSet.2.default.params.range=720
++policyset.auditSigningCertSet.2.default.params.startTime=0
++policyset.auditSigningCertSet.3.constraint.class_id=keyConstraintImpl
++policyset.auditSigningCertSet.3.constraint.name=Key Constraint
++policyset.auditSigningCertSet.3.constraint.params.keyType=-
++policyset.auditSigningCertSet.3.constraint.params.keyParameters=1024,2048,3072,4096,nistp256,nistp521
++policyset.auditSigningCertSet.3.default.class_id=userKeyDefaultImpl
++policyset.auditSigningCertSet.3.default.name=Key Default
++policyset.auditSigningCertSet.4.constraint.class_id=noConstraintImpl
++policyset.auditSigningCertSet.4.constraint.name=No Constraint
++policyset.auditSigningCertSet.4.default.class_id=authorityKeyIdentifierExtDefaultImpl
++policyset.auditSigningCertSet.4.default.name=Authority Key Identifier Default
++policyset.auditSigningCertSet.5.constraint.class_id=noConstraintImpl
++policyset.auditSigningCertSet.5.constraint.name=No Constraint
++policyset.auditSigningCertSet.5.default.class_id=authInfoAccessExtDefaultImpl
++policyset.auditSigningCertSet.5.default.name=AIA Extension Default
++policyset.auditSigningCertSet.5.default.params.authInfoAccessADEnable_0=true
++policyset.auditSigningCertSet.5.default.params.authInfoAccessADLocationType_0=URIName
++policyset.auditSigningCertSet.5.default.params.authInfoAccessADLocation_0=
++policyset.auditSigningCertSet.5.default.params.authInfoAccessADMethod_0=1.3.6.1.5.5.7.48.1
++policyset.auditSigningCertSet.5.default.params.authInfoAccessCritical=false
++policyset.auditSigningCertSet.5.default.params.authInfoAccessNumADs=1
++policyset.auditSigningCertSet.6.constraint.class_id=keyUsageExtConstraintImpl
++policyset.auditSigningCertSet.6.constraint.name=Key Usage Extension Constraint
++policyset.auditSigningCertSet.6.constraint.params.keyUsageCritical=true
++policyset.auditSigningCertSet.6.constraint.params.keyUsageDigitalSignature=true
++policyset.auditSigningCertSet.6.constraint.params.keyUsageNonRepudiation=true
++policyset.auditSigningCertSet.6.constraint.params.keyUsageDataEncipherment=false
++policyset.auditSigningCertSet.6.constraint.params.keyUsageKeyEncipherment=false
++policyset.auditSigningCertSet.6.constraint.params.keyUsageKeyAgreement=false
++policyset.auditSigningCertSet.6.constraint.params.keyUsageKeyCertSign=false
++policyset.auditSigningCertSet.6.constraint.params.keyUsageCrlSign=false
++policyset.auditSigningCertSet.6.constraint.params.keyUsageEncipherOnly=false
++policyset.auditSigningCertSet.6.constraint.params.keyUsageDecipherOnly=false
++policyset.auditSigningCertSet.6.default.class_id=keyUsageExtDefaultImpl
++policyset.auditSigningCertSet.6.default.name=Key Usage Default
++policyset.auditSigningCertSet.6.default.params.keyUsageCritical=true
++policyset.auditSigningCertSet.6.default.params.keyUsageDigitalSignature=true
++policyset.auditSigningCertSet.6.default.params.keyUsageNonRepudiation=true
++policyset.auditSigningCertSet.6.default.params.keyUsageDataEncipherment=false
++policyset.auditSigningCertSet.6.default.params.keyUsageKeyEncipherment=false
++policyset.auditSigningCertSet.6.default.params.keyUsageKeyAgreement=false
++policyset.auditSigningCertSet.6.default.params.keyUsageKeyCertSign=false
++policyset.auditSigningCertSet.6.default.params.keyUsageCrlSign=false
++policyset.auditSigningCertSet.6.default.params.keyUsageEncipherOnly=false
++policyset.auditSigningCertSet.6.default.params.keyUsageDecipherOnly=false
++policyset.auditSigningCertSet.9.constraint.class_id=signingAlgConstraintImpl
++policyset.auditSigningCertSet.9.constraint.name=No Constraint
++policyset.auditSigningCertSet.9.constraint.params.signingAlgsAllowed=SHA256withRSA,SHA512withRSA,SHA256withEC,SHA384withRSA,SHA384withEC,SHA512withEC,SHA256withRSA/PSS,SHA384withRSA/PSS,SHA512withRSA/PSS
++policyset.auditSigningCertSet.9.default.class_id=signingAlgDefaultImpl
++policyset.auditSigningCertSet.9.default.name=Signing Alg
++policyset.auditSigningCertSet.9.default.params.signingAlg=-
++"""
++
+     def __init__(self):
+         super(AddProfileCaAuditSigningCert, self).__init__()
+         self.message = 'Add caAuditSigningCert profile'
+@@ -46,7 +128,10 @@ class AddProfileCaAuditSigningCert(pki.server.upgrade.PKIServerUpgradeScriptlet)
+         if not os.path.exists(path):
+             logger.info('Creating caAuditSigningCert.cfg')
+             self.backup(path)
+-            shutil.copyfile('/usr/share/pki/ca/profiles/ca/caAuditSigningCert.cfg', path)
++
++            with open(path, 'w') as outfile:
++                outfile.write(caAuditSigningCert)
++
+             os.chown(path, instance.uid, instance.gid)
+             os.chmod(path, 0o0660)
+ 
+-- 
+1.8.3.1
+
diff --git a/SOURCES/pki-core-Fix-auditProfileUpgrade.patch b/SOURCES/pki-core-Fix-auditProfileUpgrade.patch
new file mode 100644
index 0000000..0425d6b
--- /dev/null
+++ b/SOURCES/pki-core-Fix-auditProfileUpgrade.patch
@@ -0,0 +1,225 @@
+From df372adab25c9e4081eb89c77c61f0ab80605c0d Mon Sep 17 00:00:00 2001
+From: Christina Fu <cfu@redhat.com>
+Date: Tue, 1 Dec 2020 09:57:43 -0800
+Subject: [PATCH 1/2] Bug1883639 -additional support on upgrade for audit cert
+ profile
+
+It was discovered that upgrading from 10.5.18-7 to 10.5.18-8 the script was
+never executed.
+
+fixes https://bugzilla.redhat.com/show_bug.cgi?id=1883639
+
+(cherry picked from commit 2b922a1d53fc222444c0ee7be6da41609e29cc7c)
+---
+ .../10.5.17/02-AddProfileCaAuditSigningCert        | 52 ----------------------
+ .../10.5.18/01-AddProfileCaAuditSigningCert        | 52 ++++++++++++++++++++++
+ 2 files changed, 52 insertions(+), 52 deletions(-)
+ delete mode 100644 base/server/upgrade/10.5.17/02-AddProfileCaAuditSigningCert
+ create mode 100644 base/server/upgrade/10.5.18/01-AddProfileCaAuditSigningCert
+
+diff --git a/base/server/upgrade/10.5.17/02-AddProfileCaAuditSigningCert b/base/server/upgrade/10.5.17/02-AddProfileCaAuditSigningCert
+deleted file mode 100644
+index 02b8477..0000000
+--- a/base/server/upgrade/10.5.17/02-AddProfileCaAuditSigningCert
++++ /dev/null
+@@ -1,52 +0,0 @@
+-# Authors:
+-#     Christina Fu <cfu@redhat.com>
+-#
+-# Copyright Red Hat, Inc.
+-#
+-# SPDX-License-Identifier: GPL-2.0-or-later
+-
+-from __future__ import absolute_import
+-import logging
+-import os
+-import shutil
+-
+-import pki
+-
+-logger = logging.getLogger(__name__)
+-
+-
+-class AddProfileCaAuditSigningCert(pki.server.upgrade.PKIServerUpgradeScriptlet):
+-
+-    def __init__(self):
+-        super(AddProfileCaAuditSigningCert, self).__init__()
+-        self.message = 'Add caAuditSigningCert profile'
+-
+-    def upgrade_subsystem(self, instance, subsystem):
+-
+-        if subsystem.name != 'ca':
+-            return
+-
+-        path = os.path.join(subsystem.base_dir, 'profiles', 'ca', 'caAuditSigningCert.cfg')
+-
+-        if not os.path.exists(path):
+-            logger.info('Creating caAuditSigningCert.cfg')
+-            self.backup(path)
+-            shutil.copyfile('/usr/share/pki/ca/profiles/ca/caAuditSigningCert.cfg', path)
+-            os.chown(path, instance.uid, instance.gid)
+-            os.chmod(path, 0o0660)
+-
+-        logger.info('Adding caAuditSigningCert into profile.list')
+-        profile_list = subsystem.config.get('profile.list').split(',')
+-        if 'caAuditSigningCert' not in profile_list:
+-            profile_list.append('caAuditSigningCert')
+-            profile_list.sort()
+-            subsystem.config['profile.list'] = ','.join(profile_list)
+-
+-        logger.info('Adding profile.caAuditSigningCert.class_id')
+-        subsystem.config['profile.caAuditSigningCert.class_id'] = 'caEnrollImpl'
+-
+-        logger.info('Adding profile.caAuditSigningCert.config')
+-        subsystem.config['profile.caAuditSigningCert.config'] = path
+-
+-        self.backup(subsystem.cs_conf)
+-        subsystem.save()
+diff --git a/base/server/upgrade/10.5.18/01-AddProfileCaAuditSigningCert b/base/server/upgrade/10.5.18/01-AddProfileCaAuditSigningCert
+new file mode 100644
+index 0000000..02b8477
+--- /dev/null
++++ b/base/server/upgrade/10.5.18/01-AddProfileCaAuditSigningCert
+@@ -0,0 +1,52 @@
++# Authors:
++#     Christina Fu <cfu@redhat.com>
++#
++# Copyright Red Hat, Inc.
++#
++# SPDX-License-Identifier: GPL-2.0-or-later
++
++from __future__ import absolute_import
++import logging
++import os
++import shutil
++
++import pki
++
++logger = logging.getLogger(__name__)
++
++
++class AddProfileCaAuditSigningCert(pki.server.upgrade.PKIServerUpgradeScriptlet):
++
++    def __init__(self):
++        super(AddProfileCaAuditSigningCert, self).__init__()
++        self.message = 'Add caAuditSigningCert profile'
++
++    def upgrade_subsystem(self, instance, subsystem):
++
++        if subsystem.name != 'ca':
++            return
++
++        path = os.path.join(subsystem.base_dir, 'profiles', 'ca', 'caAuditSigningCert.cfg')
++
++        if not os.path.exists(path):
++            logger.info('Creating caAuditSigningCert.cfg')
++            self.backup(path)
++            shutil.copyfile('/usr/share/pki/ca/profiles/ca/caAuditSigningCert.cfg', path)
++            os.chown(path, instance.uid, instance.gid)
++            os.chmod(path, 0o0660)
++
++        logger.info('Adding caAuditSigningCert into profile.list')
++        profile_list = subsystem.config.get('profile.list').split(',')
++        if 'caAuditSigningCert' not in profile_list:
++            profile_list.append('caAuditSigningCert')
++            profile_list.sort()
++            subsystem.config['profile.list'] = ','.join(profile_list)
++
++        logger.info('Adding profile.caAuditSigningCert.class_id')
++        subsystem.config['profile.caAuditSigningCert.class_id'] = 'caEnrollImpl'
++
++        logger.info('Adding profile.caAuditSigningCert.config')
++        subsystem.config['profile.caAuditSigningCert.config'] = path
++
++        self.backup(subsystem.cs_conf)
++        subsystem.save()
+-- 
+1.8.3.1
+
+
+From 3057354b81d16212d54693eef743c4cb3dbe38b5 Mon Sep 17 00:00:00 2001
+From: Christina Fu <cfu@redhat.com>
+Date: Tue, 1 Dec 2020 13:26:20 -0800
+Subject: [PATCH 2/2] Bug1883639-part2-auditProfileUpgrade
+
+This patch addresses the issue where when caSignedLogCert.cfg was renamed
+caAuditSigningCert where
+  * The profileIDMapping and profileSetIDMapping params in the following
+    profile still contains the old names:
+      base/ca/shared/conf/caAuditSigningCert.profile
+  * at renewal time, the profile will no longer be available
+
+The solution provided is to
+  * correct the two mapping param names in caAuditSigningCert.profile
+  * re-enable caSignedLogCert.cfg (but kept invisible)
+
+fixes issue relating to https://bugzilla.redhat.com/show_bug.cgi?id=1883639
+
+(cherry picked from commit de51aaf50bc44bb72a749736fa84268f8caf9ffb)
+---
+ base/ca/shared/conf/caAuditSigningCert.profile            |  4 ++--
+ base/ca/shared/profiles/ca/caSignedLogCert.cfg            |  6 +++---
+ .../upgrade/10.5.18/01-AddProfileCaAuditSigningCert       | 15 +++++++++++++++
+ 3 files changed, 20 insertions(+), 5 deletions(-)
+
+diff --git a/base/ca/shared/conf/caAuditSigningCert.profile b/base/ca/shared/conf/caAuditSigningCert.profile
+index 5983a18..cb9b5e1 100644
+--- a/base/ca/shared/conf/caAuditSigningCert.profile
++++ b/base/ca/shared/conf/caAuditSigningCert.profile
+@@ -4,8 +4,8 @@
+ id=caAuditSigningCert.profile
+ name=CA Audit Signing Certificate Profile
+ description=This profile creates a CA Audit signing certificate that is valid for audit log signing purpose.
+-profileIDMapping=caSignedLogCert
+-profileSetIDMapping=caLogSigningSet
++profileIDMapping=caAuditSigningCert
++profileSetIDMapping=auditSigningCertSet
+ list=2,4,6,8
+ 2.default.class=com.netscape.cms.profile.def.ValidityDefault
+ 2.default.name=Validity Default
+diff --git a/base/ca/shared/profiles/ca/caSignedLogCert.cfg b/base/ca/shared/profiles/ca/caSignedLogCert.cfg
+index 01e21f1..cc058b0 100644
+--- a/base/ca/shared/profiles/ca/caSignedLogCert.cfg
++++ b/base/ca/shared/profiles/ca/caSignedLogCert.cfg
+@@ -1,9 +1,9 @@
+-desc=This profile is for enrolling audit log signing certificates
++desc=(deprecated; use caAuditSigningCert) This profile is for enrolling audit log signing certificates
+ visible=false
+-enable=false
++enable=true
+ enableBy=admin
+ auth.class_id=
+-name=Manual Audit Log Signing Certificate Enrollment
++name=(deprecated; use caAuditSigningCert) Manual Audit Log Signing Certificate Enrollment
+ input.list=i1,i2
+ input.i1.class_id=certReqInputImpl
+ input.i2.class_id=submitterInfoInputImpl
+diff --git a/base/server/upgrade/10.5.18/01-AddProfileCaAuditSigningCert b/base/server/upgrade/10.5.18/01-AddProfileCaAuditSigningCert
+index 02b8477..5cec8d9 100644
+--- a/base/server/upgrade/10.5.18/01-AddProfileCaAuditSigningCert
++++ b/base/server/upgrade/10.5.18/01-AddProfileCaAuditSigningCert
+@@ -26,6 +26,21 @@ class AddProfileCaAuditSigningCert(pki.server.upgrade.PKIServerUpgradeScriptlet)
+         if subsystem.name != 'ca':
+             return
+ 
++        # enable old profile caSignedLogCert to properly deprecate
++        opath = os.path.join(subsystem.base_dir, 'profiles', 'ca', 'caSignedLogCert.cfg')
++        self.backup(opath)
++
++        oconfig = {}
++
++        pki.util.load_properties(opath, oconfig)
++
++        oconfig['enable'] = 'true'
++        oconfig['desc'] = '(deprecated; use caAuditSigningCert) This profile is for enrolling audit log signing certificates'
++        oconfig['name'] = '(deprecated; use caAuditSigningCert) Manual Audit Log Signing Certificate Enrollment'
++
++        pki.util.store_properties(opath, oconfig)
++
++        # now handle new profile
+         path = os.path.join(subsystem.base_dir, 'profiles', 'ca', 'caAuditSigningCert.cfg')
+ 
+         if not os.path.exists(path):
+-- 
+1.8.3.1
+
diff --git a/SOURCES/pki-core-rhel-7-9-rhcs-9-7-bu-2.patch b/SOURCES/pki-core-rhel-7-9-rhcs-9-7-bu-2.patch
new file mode 100644
index 0000000..19a0802
--- /dev/null
+++ b/SOURCES/pki-core-rhel-7-9-rhcs-9-7-bu-2.patch
@@ -0,0 +1,722 @@
+From 6bd383b5f142c4f2795bb3bfb2db167981622a9d Mon Sep 17 00:00:00 2001
+From: jmagne <jmagne@redhat.com>
+Date: Wed, 30 Sep 2020 13:35:25 -0400
+Subject: [PATCH 1/6] Resolve: Bug 1710978 - TPS - Add logging to
+ tdbAddCertificatesForCUID if adding or searching for cert record fails (#559)
+
+Submitted by RHCS-maint.
+
+    This fix provides better logging when the update to the token db sufferes a partial or complete failure.
+
+    Due to the unlikelyness of this happening in practice, this fix provides a simple config based way to simulate
+    the issue, such that the log activity can be easily observed just as if had happened during an actual failure.
+
+    Set the following in the TPS's CS.cfg:
+
+    op.enroll.testAddCertsToDBFailure=true.
+
+    The setting is false by default.
+
+Co-authored-by: Jack Magne <jmagne@test.host.com>
+(cherry picked from commit d7f2b72dd4fe9cd21de70fb8ce1806f66aec3624)
+---
+ .../src/org/dogtagpki/server/tps/TPSTokendb.java   | 76 ++++++++++++++++++----
+ .../server/tps/processor/TPSEnrollProcessor.java   | 14 +++-
+ 2 files changed, 75 insertions(+), 15 deletions(-)
+
+diff --git a/base/tps/src/org/dogtagpki/server/tps/TPSTokendb.java b/base/tps/src/org/dogtagpki/server/tps/TPSTokendb.java
+index 446fa3f..7434502 100644
+--- a/base/tps/src/org/dogtagpki/server/tps/TPSTokendb.java
++++ b/base/tps/src/org/dogtagpki/server/tps/TPSTokendb.java
+@@ -241,25 +241,75 @@ public class TPSTokendb {
+ 
+         CMS.debug(method + " found token " + cuid);
+         CMS.debug(method + " number of certs to update:" + certs.size());
++
++         // Keep track of which certs made it to the database and which didn't,
++         // in case of failure
++         class CnIssuerPair {
++             public final String cn;
++             public final String issuer;
++
++             public CnIssuerPair(String _cn, String _issuer) {
++                 cn = _cn;
++                 issuer = _issuer;
++             }
++ 
++             public String toString() {
++                 return "(cn=" + cn + ", issuerCn=" + issuer + ")";
++             }
++         }
++ 
++         ArrayList<CnIssuerPair> cnIssuerPairsRemaining = new ArrayList<CnIssuerPair>(certs.size());
++         for(TPSCertRecord cert : certs) {
++             String cn = cert.getId();
++             String issuerCn = cert.getIssuedBy();
++             cnIssuerPairsRemaining.add(new CnIssuerPair(cn, issuerCn));
++         }
++
++        boolean testAddCertsFailure = false;
++        //Contrive a very difficult to reproduce testing scenario
++
+         try {
++            IConfigStore configStore = CMS.getConfigStore();
++
++            // get conn ID
++            String config = "op.enroll." + "testAddCertsToDBFailure";
++            testAddCertsFailure = configStore.getBoolean(config,false);
++        } catch (Exception e) {
++           testAddCertsFailure = false;
++        }
++        
++        try {
++            int count = 0;
+             for (TPSCertRecord cert : certs) {
+-                try {
+-                    if (!isCertOnToken(cert, cuid)) {
+-                        CMS.debug(method + " adding cert with serial: " + cert.getSerialNumber());
+-                        tps.certDatabase.addRecord(cert.getId(), cert);
+-                    } else {
+-                        // cert already on token
+-                        CMS.debug(method + "retain and skip adding with serial:" + cert.getSerialNumber());
+-                    }
+-                } catch (Exception e) {
+-                    CMS.debug(method + "Exception after isCertOnToken call"+ e.toString());
+-                    // ignore; go to next;
++                if (!isCertOnToken(cert, cuid)) {
++                    CMS.debug(method + " adding cert with serial: " + cert.getSerialNumber());
++                    // After at least one cert is added correctly, perform the test of a failure
++                    // if so configured.
++                   
++                    if(count > 0 && testAddCertsFailure == true) {
++                        throw new Exception(method + ": " + "Failed to add certificate to token db, as part of a test of failure condition.");
++                    }   
++                    tps.certDatabase.addRecord(cert.getId(), cert);
++                } else {
++                    // cert already on token
++                    CMS.debug(method + "retain and skip adding with serial:" + cert.getSerialNumber());
+                 }
++                
++                // Successfully added cert or verified it was already there, so remove
++                // it from the 'remaining' list
++                cnIssuerPairsRemaining.removeIf(p -> (p.cn == cert.getId() && p.issuer == cert.getIssuedBy()));
++                count ++ ;
+             }
+         } catch (Exception e) {
+             CMS.debug(method + e);
+-            // TODO: what if it throws in the middle of the cert list -- some cert records already updated?
+-            throw new TPSException(e.getMessage());
++
++             String subjectDn = certs.get(0).getSubject();
++             String logMsg = method +  ": " +  "Failed to add or verify the following certs for [" + subjectDn + "] in the Certificate DB: ";
++             for(CnIssuerPair pair : cnIssuerPairsRemaining) {
++                 logMsg += pair + "; ";
++             }
++
++             throw new TPSException(logMsg);
+         }
+     }
+ 
+diff --git a/base/tps/src/org/dogtagpki/server/tps/processor/TPSEnrollProcessor.java b/base/tps/src/org/dogtagpki/server/tps/processor/TPSEnrollProcessor.java
+index f1e773a..5175344 100644
+--- a/base/tps/src/org/dogtagpki/server/tps/processor/TPSEnrollProcessor.java
++++ b/base/tps/src/org/dogtagpki/server/tps/processor/TPSEnrollProcessor.java
+@@ -618,8 +618,18 @@ public class TPSEnrollProcessor extends TPSProcessor {
+         ArrayList<TPSCertRecord> certRecords = certsInfo.toTPSCertRecords(tokenRecord.getId(), tokenRecord.getUserID());
+ 
+         CMS.debug(method + " adding certs to token with tdbAddCertificatesForCUID...");
+-        tps.tdb.tdbAddCertificatesForCUID(tokenRecord.getId(), certRecords);
+-        CMS.debug(method + " tokendb updated with certs to the cuid so that it reflects what's on the token");
++        try {
++            tps.tdb.tdbAddCertificatesForCUID(tokenRecord.getId(), certRecords);
++            CMS.debug(method + " tokendb updated with certs to the cuid so that it reflects what's on the token");
++        } catch(TPSException e) {
++            CMS.debug(method + " Exception occurred in tdbAddCertificatesForCUID: " + e.getMessage());
++            try {
++                auditEnrollment(userid, "enrollment", appletInfo, "failure", channel.getKeyInfoData().toHexStringPlain(), null, null, e.getMessage());
++                tps.tdb.tdbActivity(ActivityDatabase.OP_ENROLLMENT, tokenRecord, session.getIpAddress(), e.getMessage(), "failure");
++            } catch(Exception f) {
++                CMS.debug(method + " Failed to log previous exception: " + f);
++            }
++        }
+ 
+         String finalAppletVersion = appletInfo.getFinalAppletVersion();
+         if(finalAppletVersion == null)
+-- 
+1.8.3.1
+
+
+From dfaecd2ce22313a0144939f5009cb0096511fceb Mon Sep 17 00:00:00 2001
+From: jmagne <jmagne@redhat.com>
+Date: Wed, 30 Sep 2020 13:39:27 -0400
+Subject: [PATCH 2/6] Resolve: Bug 1858860 - TPS - Update Error Codes returned
+ to client (CIW/ESC) to Match CS8. (#564)
+
+This is simply the addition to one very simple patch to the pin reset procedure, that provides
+the proper error code back to the client in 2 very unlikely error scenarios.
+
+RHCS-maint.
+
+Co-authored-by: Jack Magne <jmagne@test.host.com>
+(cherry picked from commit 3c58273ddb5567b86f7aad664f2af5e6560f3928)
+---
+ .../src/org/dogtagpki/server/tps/processor/TPSPinResetProcessor.java  | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/base/tps/src/org/dogtagpki/server/tps/processor/TPSPinResetProcessor.java b/base/tps/src/org/dogtagpki/server/tps/processor/TPSPinResetProcessor.java
+index de5c634..7d3a7cd 100644
+--- a/base/tps/src/org/dogtagpki/server/tps/processor/TPSPinResetProcessor.java
++++ b/base/tps/src/org/dogtagpki/server/tps/processor/TPSPinResetProcessor.java
+@@ -50,7 +50,7 @@ public class TPSPinResetProcessor extends TPSProcessor {
+     @Override
+     public void process(BeginOpMsg beginMsg) throws TPSException, IOException {
+         if (beginMsg == null) {
+-            throw new TPSException("TPSPinResetProcessor.process: invalid input data, not beginMsg provided.",
++            throw new TPSException("TPSPinResetProcessor.process: invalid input data, no beginMsg provided.",
+                     TPSStatus.STATUS_ERROR_MAC_RESET_PIN_PDU);
+         }
+         setBeginMessage(beginMsg);
+@@ -306,7 +306,7 @@ public class TPSPinResetProcessor extends TPSProcessor {
+             logMsg = logMsg + ":" + e.toString();
+             tps.tdb.tdbActivity(ActivityDatabase.OP_PIN_RESET, tokenRecord, session.getIpAddress(), logMsg,
+                     "failure");
+-            throw new TPSException(logMsg);
++            throw new TPSException(logMsg, TPSStatus.STATUS_ERROR_UPDATE_TOKENDB_FAILED);
+         }
+ 
+         CMS.debug(method + ": Token Pin successfully reset!");
+-- 
+1.8.3.1
+
+
+From 6dc155765b9752c9b1e89d442c53b464756df325 Mon Sep 17 00:00:00 2001
+From: Christina Fu <cfu@redhat.com>
+Date: Tue, 8 Sep 2020 17:18:49 -0400
+Subject: [PATCH 3/6] Bug1858861 TPS - Server side key generation is not
+ working for Identity only tokens Missing some commits
+
+  This patch relates to Bug 1494591, where the fix was missing a patch.
+
+  It makes it so that as long as one keyType has serverKeyGen enabled then
+  all key tyes under the same tps profile are considered server-side
+  keygen.
+
+  Code submittd by RHCS-MAINT
+
+fixes https://bugzilla.redhat.com/show_bug.cgi?id=1858861
+
+(cherry picked from commit 103a03062c235cc3e51f98e721ca6d72eb1f5a9d)
+---
+ .../server/tps/cms/TKSRemoteRequestHandler.java    | 50 ++++++++++++++++------
+ 1 file changed, 38 insertions(+), 12 deletions(-)
+
+diff --git a/base/tps/src/org/dogtagpki/server/tps/cms/TKSRemoteRequestHandler.java b/base/tps/src/org/dogtagpki/server/tps/cms/TKSRemoteRequestHandler.java
+index 8155f90..770819d 100644
+--- a/base/tps/src/org/dogtagpki/server/tps/cms/TKSRemoteRequestHandler.java
++++ b/base/tps/src/org/dogtagpki/server/tps/cms/TKSRemoteRequestHandler.java
+@@ -127,9 +127,7 @@ public class TKSRemoteRequestHandler extends RemoteRequestHandler
+                 break;
+             }
+         }
+-
+-
+-
++        CMS.debug(method + " final serverkegGen enabled? " + serverKeygen);
+ 
+         if (keySet == null)
+             keySet = conf.getString("tps.connector." + connid + ".keySet", "defKeySet");
+@@ -264,10 +262,23 @@ public class TKSRemoteRequestHandler extends RemoteRequestHandler
+ 
+         IConfigStore conf = CMS.getConfigStore();
+ 
+-        boolean serverKeygen =
+-                conf.getBoolean("op.enroll." +
+-                        tokenType + ".keyGen.encryption.serverKeygen.enable",
+-                        false);
++        boolean serverKeygen = false;
++
++        //Try out all the currently supported cert types to see if we are doing server side keygen here
++        String[] keygenStrings = { "identity", "signing", "encryption", "authentication", "auth"};
++        for (String keygenString : keygenStrings) {
++            boolean enabled = conf.getBoolean("op.enroll." +
++                    tokenType + ".keyGen." +
++                    keygenString + ".serverKeygen.enable", false);
++
++            CMS.debug(method + " serverkegGen enabled for " + keygenString + " : " + enabled);
++            if (enabled) {
++                serverKeygen = true;
++                break;
++            }
++        }
++        CMS.debug(method + " final serverkegGen enabled? " + serverKeygen);
++
+         if (keySet == null)
+             keySet = conf.getString("tps.connector." + connid + ".keySet", "defKeySet");
+ 
+@@ -427,7 +438,9 @@ public class TKSRemoteRequestHandler extends RemoteRequestHandler
+             String tokenType)
+             throws EBaseException {
+ 
+-        CMS.debug("TKSRemoteRequestHandler: computeSessionKeySCP02(): begins.");
++        String method = "TKSRemoteRequestHandler: computeSessionKeysSCP02(): ";
++
++        CMS.debug(method + " begins.");
+         if (cuid == null || kdd == null || keyInfo == null ||
+                 sequenceCounter == null
+                 || derivationConstant == null) {
+@@ -436,10 +449,23 @@ public class TKSRemoteRequestHandler extends RemoteRequestHandler
+ 
+         IConfigStore conf = CMS.getConfigStore();
+ 
+-        boolean serverKeygen =
+-                conf.getBoolean("op.enroll." +
+-                        tokenType + ".keyGen.encryption.serverKeygen.enable",
+-                        false);
++        boolean serverKeygen = false;
++
++        //Try out all the currently supported cert types to see if we are doing server side keygen here
++        String[] keygenStrings = { "identity", "signing", "encryption", "authentication", "auth"};
++        for (String keygenString : keygenStrings) {
++            boolean enabled = conf.getBoolean("op.enroll." +
++                    tokenType + ".keyGen." +
++                    keygenString + ".serverKeygen.enable", false);
++
++            CMS.debug(method + " serverkegGen enabled for " + keygenString + " : " + enabled);
++            if (enabled) {
++                serverKeygen = true;
++                break;
++            }
++        }
++        CMS.debug(method + " final serverkegGen enabled? " + serverKeygen);
++
+         if (keySet == null)
+             keySet = conf.getString("tps.connector." + connid + ".keySet", "defKeySet");
+ 
+-- 
+1.8.3.1
+
+
+From 9627046fe5d38c447c85ec3a1be75ab86dbdaaac Mon Sep 17 00:00:00 2001
+From: Christina Fu <cfu@redhat.com>
+Date: Tue, 13 Oct 2020 16:19:06 -0700
+Subject: [PATCH 4/6] Bug1883639-add profile caAuditSigningCert
+
+  Existing profiiles caStorageCert.cfg and caTransportCert.cfg
+  should be used for KRA.
+  a caAuditSigningCert profile is added, although I find
+  a misleading profile named caSignedLogCert.cfg  that was intended for
+  the use.  I disabled caSignedLogCert.cfg instead.
+
+  I also removed the SHA1 algorithms from all the *storage* and *audit*
+  profiles while I'm at it.
+
+  The upgrade scripts only adds the new profile caAuditSigningCert.  It
+  does not modify existing profiles or remove those two IPA specific
+  ones.
+
+  fixes https://bugzilla.redhat.com/show_bug.cgi?id=1883639
+
+(cherry picked from commit 73efcea0c74eb4882c003a7fe6cef21fa7627363)
+---
+ base/ca/shared/conf/CS.cfg                         |  4 +-
+ base/ca/shared/profiles/ca/caAuditSigningCert.cfg  | 80 ++++++++++++++++++++++
+ .../profiles/ca/caInternalAuthAuditSigningCert.cfg |  2 +-
+ .../profiles/ca/caInternalAuthDRMstorageCert.cfg   |  2 +-
+ .../profiles/ca/caInternalAuthTransportCert.cfg    |  2 +-
+ base/ca/shared/profiles/ca/caSignedLogCert.cfg     |  4 +-
+ base/ca/shared/profiles/ca/caStorageCert.cfg       |  2 +-
+ base/ca/shared/profiles/ca/caTransportCert.cfg     |  2 +-
+ .../10.5.17/02-AddProfileCaAuditSigningCert        | 52 ++++++++++++++
+ 9 files changed, 142 insertions(+), 8 deletions(-)
+ create mode 100644 base/ca/shared/profiles/ca/caAuditSigningCert.cfg
+ create mode 100644 base/server/upgrade/10.5.17/02-AddProfileCaAuditSigningCert
+
+diff --git a/base/ca/shared/conf/CS.cfg b/base/ca/shared/conf/CS.cfg
+index 2c50831..1eb8881 100644
+--- a/base/ca/shared/conf/CS.cfg
++++ b/base/ca/shared/conf/CS.cfg
+@@ -976,7 +976,7 @@ oidmap.pse.oid=2.16.840.1.113730.1.18
+ oidmap.subject_info_access.class=netscape.security.extensions.SubjectInfoAccessExtension
+ oidmap.subject_info_access.oid=1.3.6.1.5.5.7.1.11
+ os.userid=nobody
+-profile.list=caCMCserverCert,caCMCECserverCert,caCMCECsubsystemCert,caCMCsubsystemCert,caCMCauditSigningCert,caCMCcaCert,caCMCocspCert,caCMCkraTransportCert,caCMCkraStorageCert,caServerKeygen_UserCert,caServerKeygen_DirUserCert,caUserCert,caECUserCert,caUserSMIMEcapCert,caDualCert,caDirBasedDualCert,AdminCert,ECAdminCert,caSignedLogCert,caTPSCert,caRARouterCert,caRouterCert,caServerCert,caECServerCert,caSubsystemCert,caECSubsystemCert,caOtherCert,caCACert,caCMCcaCert,caCrossSignedCACert,caInstallCACert,caRACert,caOCSPCert,caStorageCert,caTransportCert,caDirPinUserCert,caECDirPinUserCert,caDirUserCert,caECDirUserCert,caAgentServerCert,caECAgentServerCert,caAgentFileSigning,caCMCUserCert,caCMCECUserCert,caFullCMCUserCert,caECFullCMCUserCert,caFullCMCUserSignedCert,caECFullCMCUserSignedCert,caFullCMCSharedTokenCert,caECFullCMCSharedTokenCert,caSimpleCMCUserCert,caECSimpleCMCUserCert,caTokenDeviceKeyEnrollment,caTokenUserEncryptionKeyEnrollment,caTokenUserSigningKeyEnrollment,caTempTokenDeviceKeyEnrollment,caTempTokenUserEncryptionKeyEnrollment,caTempTokenUserSigningKeyEnrollment,caAdminCert,caECAdminCert,caInternalAuthServerCert,caECInternalAuthServerCert,caInternalAuthTransportCert,caInternalAuthDRMstorageCert,caInternalAuthSubsystemCert,caECInternalAuthSubsystemCert,caInternalAuthOCSPCert,caInternalAuthAuditSigningCert,DomainController,caDualRAuserCert,caRAagentCert,caRAserverCert,caUUIDdeviceCert,caSSLClientSelfRenewal,caDirUserRenewal,caManualRenewal,caTokenMSLoginEnrollment,caTokenUserSigningKeyRenewal,caTokenUserEncryptionKeyRenewal,caTokenUserAuthKeyRenewal,caJarSigningCert,caIPAserviceCert,caEncUserCert,caSigningUserCert,caTokenUserDelegateAuthKeyEnrollment,caTokenUserDelegateSigningKeyEnrollment
++profile.list=caCMCserverCert,caCMCECserverCert,caCMCECsubsystemCert,caCMCsubsystemCert,caCMCauditSigningCert,caCMCcaCert,caCMCocspCert,caCMCkraTransportCert,caCMCkraStorageCert,caServerKeygen_UserCert,caServerKeygen_DirUserCert,caUserCert,caECUserCert,caUserSMIMEcapCert,caDualCert,caDirBasedDualCert,AdminCert,ECAdminCert,caAuditSigningCert,caSignedLogCert,caTPSCert,caRARouterCert,caRouterCert,caServerCert,caECServerCert,caSubsystemCert,caECSubsystemCert,caOtherCert,caCACert,caCMCcaCert,caCrossSignedCACert,caInstallCACert,caRACert,caOCSPCert,caStorageCert,caTransportCert,caDirPinUserCert,caECDirPinUserCert,caDirUserCert,caECDirUserCert,caAgentServerCert,caECAgentServerCert,caAgentFileSigning,caCMCUserCert,caCMCECUserCert,caFullCMCUserCert,caECFullCMCUserCert,caFullCMCUserSignedCert,caECFullCMCUserSignedCert,caFullCMCSharedTokenCert,caECFullCMCSharedTokenCert,caSimpleCMCUserCert,caECSimpleCMCUserCert,caTokenDeviceKeyEnrollment,caTokenUserEncryptionKeyEnrollment,caTokenUserSigningKeyEnrollment,caTempTokenDeviceKeyEnrollment,caTempTokenUserEncryptionKeyEnrollment,caTempTokenUserSigningKeyEnrollment,caAdminCert,caECAdminCert,caInternalAuthServerCert,caECInternalAuthServerCert,caInternalAuthTransportCert,caInternalAuthDRMstorageCert,caInternalAuthSubsystemCert,caECInternalAuthSubsystemCert,caInternalAuthOCSPCert,caInternalAuthAuditSigningCert,DomainController,caDualRAuserCert,caRAagentCert,caRAserverCert,caUUIDdeviceCert,caSSLClientSelfRenewal,caDirUserRenewal,caManualRenewal,caTokenMSLoginEnrollment,caTokenUserSigningKeyRenewal,caTokenUserEncryptionKeyRenewal,caTokenUserAuthKeyRenewal,caJarSigningCert,caIPAserviceCert,caEncUserCert,caSigningUserCert,caTokenUserDelegateAuthKeyEnrollment,caTokenUserDelegateSigningKeyEnrollment
+ profile.caUUIDdeviceCert.class_id=caEnrollImpl
+ profile.caUUIDdeviceCert.config=[PKI_INSTANCE_PATH]/[PKI_SUBSYSTEM_TYPE]/profiles/ca/caUUIDdeviceCert.cfg
+ profile.caManualRenewal.class_id=caEnrollImpl
+@@ -1087,6 +1087,8 @@ profile.caECServerCert.class_id=caEnrollImpl
+ profile.caECServerCert.config=[PKI_INSTANCE_PATH]/[PKI_SUBSYSTEM_TYPE]/profiles/ca/caECServerCert.cfg
+ profile.caSignedLogCert.class_id=caEnrollImpl
+ profile.caSignedLogCert.config=[PKI_INSTANCE_PATH]/[PKI_SUBSYSTEM_TYPE]/profiles/ca/caSignedLogCert.cfg
++profile.caAuditSigningCert.class_id=caEnrollImpl
++profile.caAuditSigningCert.config=[PKI_INSTANCE_PATH]/[PKI_SUBSYSTEM_TYPE]/profiles/ca/caAuditSigningCert.cfg
+ profile.caSigningUserCert.class_id=caEnrollImpl
+ profile.caSigningUserCert.config=[PKI_INSTANCE_PATH]/[PKI_SUBSYSTEM_TYPE]/profiles/ca/caSigningUserCert.cfg
+ profile.caSimpleCMCUserCert.class_id=caEnrollImpl
+diff --git a/base/ca/shared/profiles/ca/caAuditSigningCert.cfg b/base/ca/shared/profiles/ca/caAuditSigningCert.cfg
+new file mode 100644
+index 0000000..68dfcad
+--- /dev/null
++++ b/base/ca/shared/profiles/ca/caAuditSigningCert.cfg
+@@ -0,0 +1,80 @@
++desc=This certificate profile is for enrolling audit signing certificates.
++visible=true
++enable=true
++enableBy=admin
++auth.instance_id=
++authz.acl=group="Enterprise OCSP Administrators" || group="Enterprise RA Administrators" || group="Enterprise CA Administrators" || group="Enterprise KRA Administrators" || group="Enterprise TKS Administrators" || group="Enterprise TPS Administrators"
++name=Manual Audit Signing Certificate Enrollment
++input.list=i1,i2
++input.i1.class_id=certReqInputImpl
++input.i2.class_id=submitterInfoInputImpl
++output.list=o1
++output.o1.class_id=certOutputImpl
++policyset.list=auditSigningCertSet
++policyset.auditSigningCertSet.list=1,2,3,4,5,6,9
++policyset.auditSigningCertSet.1.constraint.class_id=subjectNameConstraintImpl
++policyset.auditSigningCertSet.1.constraint.name=Subject Name Constraint
++policyset.auditSigningCertSet.1.constraint.params.pattern=CN=.*
++policyset.auditSigningCertSet.1.constraint.params.accept=true
++policyset.auditSigningCertSet.1.default.class_id=userSubjectNameDefaultImpl
++policyset.auditSigningCertSet.1.default.name=Subject Name Default
++policyset.auditSigningCertSet.1.default.params.name=
++policyset.auditSigningCertSet.2.constraint.class_id=validityConstraintImpl
++policyset.auditSigningCertSet.2.constraint.name=Validity Constraint
++policyset.auditSigningCertSet.2.constraint.params.range=720
++policyset.auditSigningCertSet.2.constraint.params.notBeforeCheck=false
++policyset.auditSigningCertSet.2.constraint.params.notAfterCheck=false
++policyset.auditSigningCertSet.2.default.class_id=validityDefaultImpl
++policyset.auditSigningCertSet.2.default.name=Validity Default
++policyset.auditSigningCertSet.2.default.params.range=720
++policyset.auditSigningCertSet.2.default.params.startTime=0
++policyset.auditSigningCertSet.3.constraint.class_id=keyConstraintImpl
++policyset.auditSigningCertSet.3.constraint.name=Key Constraint
++policyset.auditSigningCertSet.3.constraint.params.keyType=-
++policyset.auditSigningCertSet.3.constraint.params.keyParameters=1024,2048,3072,4096,nistp256,nistp521
++policyset.auditSigningCertSet.3.default.class_id=userKeyDefaultImpl
++policyset.auditSigningCertSet.3.default.name=Key Default
++policyset.auditSigningCertSet.4.constraint.class_id=noConstraintImpl
++policyset.auditSigningCertSet.4.constraint.name=No Constraint
++policyset.auditSigningCertSet.4.default.class_id=authorityKeyIdentifierExtDefaultImpl
++policyset.auditSigningCertSet.4.default.name=Authority Key Identifier Default
++policyset.auditSigningCertSet.5.constraint.class_id=noConstraintImpl
++policyset.auditSigningCertSet.5.constraint.name=No Constraint
++policyset.auditSigningCertSet.5.default.class_id=authInfoAccessExtDefaultImpl
++policyset.auditSigningCertSet.5.default.name=AIA Extension Default
++policyset.auditSigningCertSet.5.default.params.authInfoAccessADEnable_0=true
++policyset.auditSigningCertSet.5.default.params.authInfoAccessADLocationType_0=URIName
++policyset.auditSigningCertSet.5.default.params.authInfoAccessADLocation_0=
++policyset.auditSigningCertSet.5.default.params.authInfoAccessADMethod_0=1.3.6.1.5.5.7.48.1
++policyset.auditSigningCertSet.5.default.params.authInfoAccessCritical=false
++policyset.auditSigningCertSet.5.default.params.authInfoAccessNumADs=1
++policyset.auditSigningCertSet.6.constraint.class_id=keyUsageExtConstraintImpl
++policyset.auditSigningCertSet.6.constraint.name=Key Usage Extension Constraint
++policyset.auditSigningCertSet.6.constraint.params.keyUsageCritical=true
++policyset.auditSigningCertSet.6.constraint.params.keyUsageDigitalSignature=true
++policyset.auditSigningCertSet.6.constraint.params.keyUsageNonRepudiation=true
++policyset.auditSigningCertSet.6.constraint.params.keyUsageDataEncipherment=false
++policyset.auditSigningCertSet.6.constraint.params.keyUsageKeyEncipherment=false
++policyset.auditSigningCertSet.6.constraint.params.keyUsageKeyAgreement=false
++policyset.auditSigningCertSet.6.constraint.params.keyUsageKeyCertSign=false
++policyset.auditSigningCertSet.6.constraint.params.keyUsageCrlSign=false
++policyset.auditSigningCertSet.6.constraint.params.keyUsageEncipherOnly=false
++policyset.auditSigningCertSet.6.constraint.params.keyUsageDecipherOnly=false
++policyset.auditSigningCertSet.6.default.class_id=keyUsageExtDefaultImpl
++policyset.auditSigningCertSet.6.default.name=Key Usage Default
++policyset.auditSigningCertSet.6.default.params.keyUsageCritical=true
++policyset.auditSigningCertSet.6.default.params.keyUsageDigitalSignature=true
++policyset.auditSigningCertSet.6.default.params.keyUsageNonRepudiation=true
++policyset.auditSigningCertSet.6.default.params.keyUsageDataEncipherment=false
++policyset.auditSigningCertSet.6.default.params.keyUsageKeyEncipherment=false
++policyset.auditSigningCertSet.6.default.params.keyUsageKeyAgreement=false
++policyset.auditSigningCertSet.6.default.params.keyUsageKeyCertSign=false
++policyset.auditSigningCertSet.6.default.params.keyUsageCrlSign=false
++policyset.auditSigningCertSet.6.default.params.keyUsageEncipherOnly=false
++policyset.auditSigningCertSet.6.default.params.keyUsageDecipherOnly=false
++policyset.auditSigningCertSet.9.constraint.class_id=signingAlgConstraintImpl
++policyset.auditSigningCertSet.9.constraint.name=No Constraint
++policyset.auditSigningCertSet.9.constraint.params.signingAlgsAllowed=SHA256withRSA,SHA512withRSA,SHA256withEC,SHA384withRSA,SHA384withEC,SHA512withEC,SHA256withRSA/PSS,SHA384withRSA/PSS,SHA512withRSA/PSS
++policyset.auditSigningCertSet.9.default.class_id=signingAlgDefaultImpl
++policyset.auditSigningCertSet.9.default.name=Signing Alg
++policyset.auditSigningCertSet.9.default.params.signingAlg=-
+diff --git a/base/ca/shared/profiles/ca/caInternalAuthAuditSigningCert.cfg b/base/ca/shared/profiles/ca/caInternalAuthAuditSigningCert.cfg
+index 55cfd8c..86f288e 100644
+--- a/base/ca/shared/profiles/ca/caInternalAuthAuditSigningCert.cfg
++++ b/base/ca/shared/profiles/ca/caInternalAuthAuditSigningCert.cfg
+@@ -74,7 +74,7 @@ policyset.auditSigningCertSet.6.default.params.keyUsageEncipherOnly=false
+ policyset.auditSigningCertSet.6.default.params.keyUsageDecipherOnly=false
+ policyset.auditSigningCertSet.9.constraint.class_id=signingAlgConstraintImpl
+ policyset.auditSigningCertSet.9.constraint.name=No Constraint
+-policyset.auditSigningCertSet.9.constraint.params.signingAlgsAllowed=SHA1withRSA,SHA256withRSA,SHA512withRSA,SHA1withDSA,SHA1withEC,SHA256withEC,SHA384withRSA,SHA384withEC,SHA512withEC,SHA256withRSA/PSS,SHA384withRSA/PSS,SHA512withRSA/PSS
++policyset.auditSigningCertSet.9.constraint.params.signingAlgsAllowed=SHA256withRSA,SHA512withRSA,SHA256withEC,SHA384withRSA,SHA384withEC,SHA512withEC,SHA256withRSA/PSS,SHA384withRSA/PSS,SHA512withRSA/PSS
+ policyset.auditSigningCertSet.9.default.class_id=signingAlgDefaultImpl
+ policyset.auditSigningCertSet.9.default.name=Signing Alg
+ policyset.auditSigningCertSet.9.default.params.signingAlg=-
+diff --git a/base/ca/shared/profiles/ca/caInternalAuthDRMstorageCert.cfg b/base/ca/shared/profiles/ca/caInternalAuthDRMstorageCert.cfg
+index ae9593e..23a0850 100644
+--- a/base/ca/shared/profiles/ca/caInternalAuthDRMstorageCert.cfg
++++ b/base/ca/shared/profiles/ca/caInternalAuthDRMstorageCert.cfg
+@@ -80,7 +80,7 @@ policyset.drmStorageCertSet.7.default.params.exKeyUsageCritical=false
+ policyset.drmStorageCertSet.7.default.params.exKeyUsageOIDs=1.3.6.1.5.5.7.3.2
+ policyset.drmStorageCertSet.9.constraint.class_id=signingAlgConstraintImpl
+ policyset.drmStorageCertSet.9.constraint.name=No Constraint
+-policyset.drmStorageCertSet.9.constraint.params.signingAlgsAllowed=SHA1withRSA,SHA256withRSA,SHA512withRSA,SHA1withDSA,SHA1withEC,SHA256withEC,SHA384withRSA,SHA384withEC,SHA512withEC,SHA256withRSA/PSS,SHA384withRSA/PSS,SHA512withRSA/PSS
++policyset.drmStorageCertSet.9.constraint.params.signingAlgsAllowed=SHA256withRSA,SHA512withRSA,SHA256withEC,SHA384withRSA,SHA384withEC,SHA512withEC,SHA256withRSA/PSS,SHA384withRSA/PSS,SHA512withRSA/PSS
+ policyset.drmStorageCertSet.9.default.class_id=signingAlgDefaultImpl
+ policyset.drmStorageCertSet.9.default.name=Signing Alg
+ policyset.drmStorageCertSet.9.default.params.signingAlg=-
+diff --git a/base/ca/shared/profiles/ca/caInternalAuthTransportCert.cfg b/base/ca/shared/profiles/ca/caInternalAuthTransportCert.cfg
+index 359881e..cbeb0eb 100644
+--- a/base/ca/shared/profiles/ca/caInternalAuthTransportCert.cfg
++++ b/base/ca/shared/profiles/ca/caInternalAuthTransportCert.cfg
+@@ -80,7 +80,7 @@ policyset.transportCertSet.7.default.params.exKeyUsageCritical=false
+ policyset.transportCertSet.7.default.params.exKeyUsageOIDs=1.3.6.1.5.5.7.3.2
+ policyset.transportCertSet.8.constraint.class_id=signingAlgConstraintImpl
+ policyset.transportCertSet.8.constraint.name=No Constraint
+-policyset.transportCertSet.8.constraint.params.signingAlgsAllowed=SHA1withRSA,SHA256withRSA,SHA512withRSA,SHA1withDSA,SHA1withEC,SHA256withEC,SHA384withRSA,SHA384withEC,SHA512withEC,SHA256withRSA/PSS,SHA384withRSA/PSS,SHA512withRSA/PSS
++policyset.transportCertSet.8.constraint.params.signingAlgsAllowed=SHA256withRSA,SHA512withRSA,SHA256withEC,SHA384withRSA,SHA384withEC,SHA512withEC,SHA256withRSA/PSS,SHA384withRSA/PSS,SHA512withRSA/PSS
+ policyset.transportCertSet.8.default.class_id=signingAlgDefaultImpl
+ policyset.transportCertSet.8.default.name=Signing Alg
+ policyset.transportCertSet.8.default.params.signingAlg=-
+diff --git a/base/ca/shared/profiles/ca/caSignedLogCert.cfg b/base/ca/shared/profiles/ca/caSignedLogCert.cfg
+index ddd3d1a..01e21f1 100644
+--- a/base/ca/shared/profiles/ca/caSignedLogCert.cfg
++++ b/base/ca/shared/profiles/ca/caSignedLogCert.cfg
+@@ -1,6 +1,6 @@
+ desc=This profile is for enrolling audit log signing certificates
+-visible=true
+-enable=true
++visible=false
++enable=false
+ enableBy=admin
+ auth.class_id=
+ name=Manual Audit Log Signing Certificate Enrollment
+diff --git a/base/ca/shared/profiles/ca/caStorageCert.cfg b/base/ca/shared/profiles/ca/caStorageCert.cfg
+index abb9715..0791b79 100644
+--- a/base/ca/shared/profiles/ca/caStorageCert.cfg
++++ b/base/ca/shared/profiles/ca/caStorageCert.cfg
+@@ -73,7 +73,7 @@ policyset.drmStorageCertSet.6.default.params.keyUsageEncipherOnly=false
+ policyset.drmStorageCertSet.6.default.params.keyUsageDecipherOnly=false
+ policyset.drmStorageCertSet.9.constraint.class_id=signingAlgConstraintImpl
+ policyset.drmStorageCertSet.9.constraint.name=No Constraint
+-policyset.drmStorageCertSet.9.constraint.params.signingAlgsAllowed=SHA1withRSA,SHA256withRSA,SHA512withRSA,SHA1withDSA,SHA1withEC,SHA256withEC,SHA384withRSA,SHA384withEC,SHA512withEC,SHA256withRSA/PSS,SHA384withRSA/PSS,SHA512withRSA/PSS
++policyset.drmStorageCertSet.9.constraint.params.signingAlgsAllowed=SHA256withRSA,SHA512withRSA,SHA256withEC,SHA384withRSA,SHA384withEC,SHA512withEC,SHA256withRSA/PSS,SHA384withRSA/PSS,SHA512withRSA/PSS
+ policyset.drmStorageCertSet.9.default.class_id=signingAlgDefaultImpl
+ policyset.drmStorageCertSet.9.default.name=Signing Alg
+ policyset.drmStorageCertSet.9.default.params.signingAlg=-
+diff --git a/base/ca/shared/profiles/ca/caTransportCert.cfg b/base/ca/shared/profiles/ca/caTransportCert.cfg
+index 51dc084..f6ae711 100644
+--- a/base/ca/shared/profiles/ca/caTransportCert.cfg
++++ b/base/ca/shared/profiles/ca/caTransportCert.cfg
+@@ -79,7 +79,7 @@ policyset.transportCertSet.7.default.params.exKeyUsageCritical=false
+ policyset.transportCertSet.7.default.params.exKeyUsageOIDs=1.3.6.1.5.5.7.3.2
+ policyset.transportCertSet.8.constraint.class_id=signingAlgConstraintImpl
+ policyset.transportCertSet.8.constraint.name=No Constraint
+-policyset.transportCertSet.8.constraint.params.signingAlgsAllowed=SHA1withRSA,SHA256withRSA,SHA512withRSA,MD5withRSA,MD2withRSA,SHA1withDSA,SHA1withEC,SHA256withEC,SHA384withRSA,SHA384withEC,SHA512withEC,SHA256withRSA/PSS,SHA384withRSA/PSS,SHA512withRSA/PSS
++policyset.transportCertSet.8.constraint.params.signingAlgsAllowed=SHA256withRSA,SHA512withRSA,MD5withRSA,MD2withRSA,SHA256withEC,SHA384withRSA,SHA384withEC,SHA512withEC,SHA256withRSA/PSS,SHA384withRSA/PSS,SHA512withRSA/PSS
+ policyset.transportCertSet.8.default.class_id=signingAlgDefaultImpl
+ policyset.transportCertSet.8.default.name=Signing Alg
+ policyset.transportCertSet.8.default.params.signingAlg=-
+diff --git a/base/server/upgrade/10.5.17/02-AddProfileCaAuditSigningCert b/base/server/upgrade/10.5.17/02-AddProfileCaAuditSigningCert
+new file mode 100644
+index 0000000..02b8477
+--- /dev/null
++++ b/base/server/upgrade/10.5.17/02-AddProfileCaAuditSigningCert
+@@ -0,0 +1,52 @@
++# Authors:
++#     Christina Fu <cfu@redhat.com>
++#
++# Copyright Red Hat, Inc.
++#
++# SPDX-License-Identifier: GPL-2.0-or-later
++
++from __future__ import absolute_import
++import logging
++import os
++import shutil
++
++import pki
++
++logger = logging.getLogger(__name__)
++
++
++class AddProfileCaAuditSigningCert(pki.server.upgrade.PKIServerUpgradeScriptlet):
++
++    def __init__(self):
++        super(AddProfileCaAuditSigningCert, self).__init__()
++        self.message = 'Add caAuditSigningCert profile'
++
++    def upgrade_subsystem(self, instance, subsystem):
++
++        if subsystem.name != 'ca':
++            return
++
++        path = os.path.join(subsystem.base_dir, 'profiles', 'ca', 'caAuditSigningCert.cfg')
++
++        if not os.path.exists(path):
++            logger.info('Creating caAuditSigningCert.cfg')
++            self.backup(path)
++            shutil.copyfile('/usr/share/pki/ca/profiles/ca/caAuditSigningCert.cfg', path)
++            os.chown(path, instance.uid, instance.gid)
++            os.chmod(path, 0o0660)
++
++        logger.info('Adding caAuditSigningCert into profile.list')
++        profile_list = subsystem.config.get('profile.list').split(',')
++        if 'caAuditSigningCert' not in profile_list:
++            profile_list.append('caAuditSigningCert')
++            profile_list.sort()
++            subsystem.config['profile.list'] = ','.join(profile_list)
++
++        logger.info('Adding profile.caAuditSigningCert.class_id')
++        subsystem.config['profile.caAuditSigningCert.class_id'] = 'caEnrollImpl'
++
++        logger.info('Adding profile.caAuditSigningCert.config')
++        subsystem.config['profile.caAuditSigningCert.config'] = path
++
++        self.backup(subsystem.cs_conf)
++        subsystem.save()
+-- 
+1.8.3.1
+
+
+From 77eadead2fea96d897f3f09894ce612b9e1ee19d Mon Sep 17 00:00:00 2001
+From: Christina Fu <cfu@redhat.com>
+Date: Wed, 16 Sep 2020 18:47:33 -0400
+Subject: [PATCH 5/6] Bug1858867-TPS does not check token cuid on the user
+ externalReg record during PIN reset
+
+  RHCS-MAINT contribution
+  This patch makes sure that if "tokenCUID" exists for the user reg record,
+  pinReset operation would make sure that it mathes with the current
+  token cuid;
+  If the "tokenCUID" does not exist in the user registration record
+  then any token can be used for pinReset;
+
+fixes https://bugzilla.redhat.com/show_bug.cgi?id=1858867
+
+(cherry picked from commit 1f24b6f0b9d37139b2069564ee6b2f5fe2bae527)
+---
+ .../server/tps/processor/TPSPinResetProcessor.java | 26 ++++++++++++++++++++++
+ 1 file changed, 26 insertions(+)
+
+diff --git a/base/tps/src/org/dogtagpki/server/tps/processor/TPSPinResetProcessor.java b/base/tps/src/org/dogtagpki/server/tps/processor/TPSPinResetProcessor.java
+index 7d3a7cd..af42689 100644
+--- a/base/tps/src/org/dogtagpki/server/tps/processor/TPSPinResetProcessor.java
++++ b/base/tps/src/org/dogtagpki/server/tps/processor/TPSPinResetProcessor.java
+@@ -187,6 +187,32 @@ public class TPSPinResetProcessor extends TPSProcessor {
+             } else {
+                 CMS.debug(method + " --> registrationtype attribute disabled or not found, continuing.");
+             }
++            
++            /*
++             * If cuid is provided on the user registration record, then
++             * we have to compare that with the current token cuid;
++             *
++             * If, the cuid is not provided on the user registration record,
++             * then any token can be used.
++             */
++            if (erAttrs.getTokenCUID() != null) {
++                CMS.debug(method + " checking if token cuid matches record cuid");
++                CMS.debug(method + " erAttrs.getTokenCUID()=" + erAttrs.getTokenCUID());
++                CMS.debug(method + " tokenRecord.getId()=" + tokenRecord.getId());
++                if (!tokenRecord.getId().equalsIgnoreCase(erAttrs.getTokenCUID())) {
++                    logMsg = "isExternalReg: token CUID not matching record:" + tokenRecord.getId() + " : " +
++                            erAttrs.getTokenCUID();
++                    CMS.debug(method + logMsg);
++                    tps.tdb.tdbActivity(ActivityDatabase.OP_PIN_RESET, tokenRecord, session.getIpAddress(), logMsg,
++                            "failure");
++                    throw new TPSException(logMsg, TPSStatus.STATUS_ERROR_NOT_TOKEN_OWNER);
++                } else {
++                    logMsg = "isExternalReg: token CUID matches record";
++                    CMS.debug(method + logMsg);
++                }
++            } else {
++                CMS.debug(method + " no need to check if token cuid matches record");
++            }
+ 
+             session.setExternalRegAttrs(erAttrs);
+             setExternalRegSelectedTokenType(erAttrs);
+-- 
+1.8.3.1
+
+
+From 8c17891db620896e684cf0efd4ead66d8b1b4e1d Mon Sep 17 00:00:00 2001
+From: jmagne <jmagne@redhat.com>
+Date: Mon, 19 Oct 2020 21:26:43 -0400
+Subject: [PATCH 6/6] Enhancment to Bug 1858860 - TPS - Update Error Codes
+ returned to client (CIW/ESC) to Match CS8. (#3360)
+
+This enhancement allows config values to be used to test the unlikely error conditions addressed in the original bug:
+
+    To test one two scenarios, use these settings one at a time:
+
+    op.pinReset.testNoBeginMsg=false
+    op.pinReset.testUpdateDBFailure=false
+
+    The first one will test the error code returned when the beginOp message is missing when atempting
+    a pin Reset operation. The error returned should be error "4".
+
+    The second one will test if the update of the db for the token does not complete properly.
+
+    The error returned in this scenario should be "41".
+
+    The tpsclient utility can be used to test these two scenarios. Once again try them separately
+    because the first error will stop the pin reset procedure before the second scenario can even happen.
+
+Co-authored-by: Jack Magne <jmagne@test.host.com>
+(cherry picked from commit 509d31cf80e13c564b50d41feb11fd9c2eb9db73)
+---
+ .../server/tps/processor/TPSPinResetProcessor.java | 29 +++++++++++++++++++++-
+ 1 file changed, 28 insertions(+), 1 deletion(-)
+
+diff --git a/base/tps/src/org/dogtagpki/server/tps/processor/TPSPinResetProcessor.java b/base/tps/src/org/dogtagpki/server/tps/processor/TPSPinResetProcessor.java
+index af42689..805af20 100644
+--- a/base/tps/src/org/dogtagpki/server/tps/processor/TPSPinResetProcessor.java
++++ b/base/tps/src/org/dogtagpki/server/tps/processor/TPSPinResetProcessor.java
+@@ -49,7 +49,19 @@ public class TPSPinResetProcessor extends TPSProcessor {
+ 
+     @Override
+     public void process(BeginOpMsg beginMsg) throws TPSException, IOException {
+-        if (beginMsg == null) {
++
++        IConfigStore configStore = CMS.getConfigStore();
++
++        // Use this only for testing, not for normal operation.
++        String configName = "op.pinReset.testNoBeginMsg";
++        boolean testPinResetNoBeginMsg = false;
++        try {
++            testPinResetNoBeginMsg = configStore.getBoolean(configName,false);
++        } catch (EBaseException e) {
++            testPinResetNoBeginMsg = false;
++        }
++
++        if (beginMsg == null || testPinResetNoBeginMsg == true) {
+             throw new TPSException("TPSPinResetProcessor.process: invalid input data, no beginMsg provided.",
+                     TPSStatus.STATUS_ERROR_MAC_RESET_PIN_PDU);
+         }
+@@ -324,7 +336,22 @@ public class TPSPinResetProcessor extends TPSProcessor {
+ 
+         statusUpdate(100, "PROGRESS_PIN_RESET_COMPLETE");
+         logMsg = "update token during pin reset";
++
++        IConfigStore configStore = CMS.getConfigStore();
++
++        // Use this only for testing, not for normal operation.
++        String configName = "op.pinReset.testUpdateDBFailure";
++        boolean testUpdateDBFailure = false;
+         try {
++            testUpdateDBFailure = configStore.getBoolean(configName,false);
++        } catch (EBaseException e) {
++            testUpdateDBFailure = false;
++        }
++
++        try {
++            if(testUpdateDBFailure == true) {
++                throw new Exception("Test failure to update DB for Pin Reset!");
++            }
+             tps.tdb.tdbUpdateTokenEntry(tokenRecord);
+             tps.tdb.tdbActivity(ActivityDatabase.OP_PIN_RESET, tokenRecord, session.getIpAddress(), logMsg, "success");
+             CMS.debug(method + ": token record updated!");
+-- 
+1.8.3.1
+
diff --git a/SOURCES/pki-core-rhel-7-9-rhcs-9-7-bu-4.patch b/SOURCES/pki-core-rhel-7-9-rhcs-9-7-bu-4.patch
new file mode 100644
index 0000000..ccd1d41
--- /dev/null
+++ b/SOURCES/pki-core-rhel-7-9-rhcs-9-7-bu-4.patch
@@ -0,0 +1,30810 @@
+From 483419330e3564b1b4c4cc8d5466aa49aa80f328 Mon Sep 17 00:00:00 2001
+From: "Endi S. Dewata" <edewata@redhat.com>
+Date: Mon, 7 Dec 2020 11:02:53 -0600
+Subject: [PATCH 01/17] Fix undefined variable in
+ 01-AddProfileCaAuditSigningCert
+
+The 01-AddProfileCaAuditSigningCert has been modified to use
+self when referring to caAuditSigningCert instance variable.
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1883639
+(cherry picked from commit a73a738453395bf2dde3e8e194dfdc98cacea884)
+---
+ base/server/upgrade/10.5.18/01-AddProfileCaAuditSigningCert | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/base/server/upgrade/10.5.18/01-AddProfileCaAuditSigningCert b/base/server/upgrade/10.5.18/01-AddProfileCaAuditSigningCert
+index c142325..8b6acc1 100644
+--- a/base/server/upgrade/10.5.18/01-AddProfileCaAuditSigningCert
++++ b/base/server/upgrade/10.5.18/01-AddProfileCaAuditSigningCert
+@@ -130,7 +130,7 @@ policyset.auditSigningCertSet.9.default.params.signingAlg=-
+             self.backup(path)
+ 
+             with open(path, 'w') as outfile:
+-                outfile.write(caAuditSigningCert)
++                outfile.write(self.caAuditSigningCert)
+ 
+             os.chown(path, instance.uid, instance.gid)
+             os.chmod(path, 0o0660)
+-- 
+1.8.3.1
+
+
+From 9dd2e2c84e617c4b0c6c6c0d166399a647ad4807 Mon Sep 17 00:00:00 2001
+From: Alexander Scheel <ascheel@redhat.com>
+Date: Wed, 2 Dec 2020 11:29:29 -0500
+Subject: [PATCH 02/17] Added input validation for TPS
+
+The TPSProfileService has been modified to validate the
+profile ID and profile property names received via REST API.
+
+The TPS UI has been modified to validate profile ID and
+profile property names before they are sent to the server.
+
+The TableItem.renderColumn() has been modified to escape
+the value already stored in the database before displaying
+it in the UI.
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1791099
+https://bugzilla.redhat.com/show_bug.cgi?id=1793076
+https://bugzilla.redhat.com/show_bug.cgi?id=1725129
+
+Authored by Endi Dewata; backported by ascheel.
+
+Signed-off-by: Alexander Scheel <ascheel@redhat.com>
+(cherry picked from commit 539268ac20c3c323f91540dff1307e807e95f2a5)
+---
+ base/server/share/webapps/pki/js/pki-ui.js         | 37 ++++++++++++++---
+ base/tps/shared/webapps/tps/js/tps.js              | 47 +++++++++++++++++++++-
+ .../dogtagpki/server/tps/rest/ProfileService.java  | 24 ++++++++++-
+ 3 files changed, 99 insertions(+), 9 deletions(-)
+
+diff --git a/base/server/share/webapps/pki/js/pki-ui.js b/base/server/share/webapps/pki/js/pki-ui.js
+index 005e8e6..76d4a46 100644
+--- a/base/server/share/webapps/pki/js/pki-ui.js
++++ b/base/server/share/webapps/pki/js/pki-ui.js
+@@ -351,7 +351,7 @@ var TableItem = Backbone.View.extend({
+             if (value instanceof Date) value = value.toUTCString();
+ 
+             // replace pattern occurance with attribute value
+-            newContent += content.substring(0, index) + value;
++            newContent += content.substring(0, index) + _.escape(value);
+ 
+             // process the remaining content
+             content = content.substring(index + fullName.length + 3);
+@@ -622,17 +622,34 @@ var Table = Backbone.View.extend({
+ 
+             // save new entry
+             dialog.save();
+-            self.addEntry(dialog.entry);
+-
+-            // redraw table
+-            self.render();
+             dialog.close();
++
++            try {
++                self.addEntry(dialog.entry);
++
++                // redraw table
++                self.render();
++
++            } catch (exception) {
++                // display the error in an error dialog,
++                // then reopen the original dialog
++
++                var errorDialog = new ErrorDialog({
++                    el: $("#error-dialog"),
++                    content: exception
++                });
++                errorDialog.on("close", function() {
++                    dialog.open();
++                });
++                errorDialog.open();
++            }
+         });
+ 
+         dialog.open();
+     },
+     addEntry: function(entry) {
+         var self = this;
++
+         self.entries.push(entry);
+     },
+     remove: function(items) {
+@@ -884,8 +901,16 @@ var EntryPage = Page.extend({
+         });
+ 
+         self.saveAction.click(function(e) {
+-            self.save();
+             e.preventDefault();
++            try {
++                self.save();
++            } catch (exception) {
++                new ErrorDialog({
++                    el: $("#error-dialog"),
++                    title: "ERROR",
++                    content: exception
++                }).open();
++            }
+         });
+ 
+     },
+diff --git a/base/tps/shared/webapps/tps/js/tps.js b/base/tps/shared/webapps/tps/js/tps.js
+index 9d47d30..f12f7b4 100644
+--- a/base/tps/shared/webapps/tps/js/tps.js
++++ b/base/tps/shared/webapps/tps/js/tps.js
+@@ -19,7 +19,34 @@
+  * @author Endi S. Dewata
+  */
+ 
+-var tps = {};
++var TPS = {
++    PROFILE_ID_PATTERN: /^[a-zA-Z0-9_]+$/,
++    PROPERTY_NAME_PATTERN: /^[a-zA-Z0-9_\.]+$/,
++    getElementName: function (component) {
++
++        if (component == "Generals") {
++            return "config";
++
++        } else if (component == "Authentication_Sources") {
++            return "authenticators";
++
++        } else if (component == "Subsystem_Connections") {
++            return "connectors";
++
++        } else if (component == "Profiles") {
++            return "profiles";
++
++        } else if (component == "Profile_Mappings") {
++            return "profile-mappings";
++
++        } else if (component == "Audit_Logging") {
++            return "audit";
++
++        } else {
++            return null;
++        }
++    }
++};
+ 
+ var PropertiesTableItem = TableItem.extend({
+     initialize: function(options) {
+@@ -103,6 +130,15 @@ var PropertiesTable = Table.extend({
+             return entry.name;
+         });
+     },
++    addEntry: function(entry) {
++        var self = this;
++
++        if (!entry.name.match(TPS.PROPERTY_NAME_PATTERN)) {
++            throw "Invalid property name: " + entry.name;
++        }
++
++        PropertiesTable.__super__.addEntry.call(self, entry);
++    },
+     remove: function(items) {
+         var self = this;
+ 
+@@ -393,6 +429,15 @@ var ConfigEntryPage = EntryPage.extend({
+ 
+         self.entry.properties = self.getProperties();
+     },
++    save: function() {
++        var self = this;
++
++        if (!self.entry.profileID.match(TPS.PROFILE_ID_PATTERN)) {
++            throw "Invalid profile ID: " + self.entry.profileID;
++        }
++
++        ConfigEntryPage.__super__.save.call(self);
++    },
+     setProperties: function(properties) {
+         var self = this;
+ 
+diff --git a/base/tps/src/org/dogtagpki/server/tps/rest/ProfileService.java b/base/tps/src/org/dogtagpki/server/tps/rest/ProfileService.java
+index 56eac9b..71bf9ad 100644
+--- a/base/tps/src/org/dogtagpki/server/tps/rest/ProfileService.java
++++ b/base/tps/src/org/dogtagpki/server/tps/rest/ProfileService.java
+@@ -25,6 +25,7 @@ import java.security.Principal;
+ import java.util.HashMap;
+ import java.util.Iterator;
+ import java.util.Map;
++import java.util.regex.Pattern;
+ 
+ import javax.ws.rs.core.Response;
+ 
+@@ -51,6 +52,9 @@ import com.netscape.cms.servlet.base.SubsystemService;
+  */
+ public class ProfileService extends SubsystemService implements ProfileResource {
+ 
++    public static Pattern PROFILE_ID_PATTERN = Pattern.compile("^[a-zA-Z0-9_]+$");
++    public static Pattern PROPERTY_NAME_PATTERN = Pattern.compile("^[a-zA-Z0-9_\\.]+$");
++
+     public ProfileService() {
+         CMS.debug("ProfileService.<init>()");
+     }
+@@ -174,6 +178,17 @@ public class ProfileService extends SubsystemService implements ProfileResource
+ 
+         CMS.debug("ProfileService.addProfile(\"" + profileData.getID() + "\")");
+ 
++        if (!PROFILE_ID_PATTERN.matcher(profileData.getID()).matches()) {
++            throw new BadRequestException("Invalid profile ID: " + profileData.getID());
++        }
++
++        Map<String, String> properties = profileData.getProperties();
++        for (String name : properties.keySet()) {
++            if (!PROPERTY_NAME_PATTERN.matcher(name).matches()) {
++                throw new BadRequestException("Invalid profile property: " + name);
++            }
++        }
++
+         try {
+             TPSSubsystem subsystem = (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID);
+             ProfileDatabase database = subsystem.getProfileDatabase();
+@@ -194,7 +209,6 @@ public class ProfileService extends SubsystemService implements ProfileResource
+             profileData = createProfileData(database.getRecord(profileData.getID()));
+ 
+             //Map<String, String> properties = database.getRecord(profileData.getID()).getProperties();
+-            Map<String, String> properties = profileData.getProperties();
+             if (statusChanged) {
+                 properties.put("Status", status);
+             }
+@@ -232,6 +246,13 @@ public class ProfileService extends SubsystemService implements ProfileResource
+ 
+         CMS.debug("ProfileService.updateProfile(\"" + profileID + "\")");
+ 
++        Map<String, String> properties = profileData.getProperties();
++        for (String name : properties.keySet()) {
++            if (!PROPERTY_NAME_PATTERN.matcher(name).matches()) {
++                throw new BadRequestException("Invalid profile property: " + name);
++            }
++        }
++
+         try {
+             TPSSubsystem subsystem = (TPSSubsystem) CMS.getSubsystem(TPSSubsystem.ID);
+             ProfileDatabase database = subsystem.getProfileDatabase();
+@@ -269,7 +290,6 @@ public class ProfileService extends SubsystemService implements ProfileResource
+             }
+ 
+             // update properties if specified
+-            Map<String, String> properties = profileData.getProperties();
+             if (properties != null) {
+                 record.setProperties(properties);
+                 if (statusChanged) {
+-- 
+1.8.3.1
+
+
+From ade3c122d9d54613a59d8c881f95cc0381887132 Mon Sep 17 00:00:00 2001
+From: Alexander Scheel <ascheel@redhat.com>
+Date: Wed, 2 Dec 2020 11:42:43 -0500
+Subject: [PATCH 03/17] Fix XSS in PathLength attribute in CA agent web page
+
+- The input type is set to number when "integer" is encountered
+- The server error message is html escaped, before it gets displayed in client browser
+
+Resolves: BZ#1710171
+
+Authored by Dinesh Prasanth M K; backported by ascheel.
+
+Signed-off-by: Dinesh Prasanth M K <dmoluguw@redhat.com>
+Signed-off-by: Alexander Scheel <ascheel@redhat.com>
+(cherry picked from commit 542d741d1805e99c30bada34e00dc4c6cd12182d)
+---
+ base/ca/shared/webapps/ca/agent/ca/ProfileReview.template         | 2 +-
+ base/server/cms/src/com/netscape/cms/servlet/base/CMSServlet.java | 4 +++-
+ 2 files changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/base/ca/shared/webapps/ca/agent/ca/ProfileReview.template b/base/ca/shared/webapps/ca/agent/ca/ProfileReview.template
+index 7e3cab0..ae687f0 100644
+--- a/base/ca/shared/webapps/ca/agent/ca/ProfileReview.template
++++ b/base/ca/shared/webapps/ca/agent/ca/ProfileReview.template
+@@ -345,7 +345,7 @@ document.writeln('</FONT>');
+         } else if (recordSet[i].defListSet[j].defSyntax == 'string_list') {
+           document.writeln('<textarea cols=40 rows=5 name="' + recordSet[i].defListSet[j].defId + '">' + recordSet[i].defListSet[j].defVal + '</textarea>');
+         } else if (recordSet[i].defListSet[j].defSyntax == 'integer') {
+-          document.writeln('<input size=6 type=text name="' + recordSet[i].defListSet[j].defId + '" value="' + recordSet[i].defListSet[j].defVal + '">');
++          document.writeln('<input size=6 type=number name="' + recordSet[i].defListSet[j].defId + '" value="' + recordSet[i].defListSet[j].defVal + '">');
+         } else if (recordSet[i].defListSet[j].defSyntax == 'image_url') {
+           document.writeln('<img border=0 src="' + recordSet[i].defListSet[j].defVal + '">');
+           document.writeln('<input type=hidden name="' + recordSet[i].defListSet[j].defId + '" value="' + recordSet[i].defListSet[j].defVal + '">');
+diff --git a/base/server/cms/src/com/netscape/cms/servlet/base/CMSServlet.java b/base/server/cms/src/com/netscape/cms/servlet/base/CMSServlet.java
+index 0c65702..854d656 100644
+--- a/base/server/cms/src/com/netscape/cms/servlet/base/CMSServlet.java
++++ b/base/server/cms/src/com/netscape/cms/servlet/base/CMSServlet.java
+@@ -46,6 +46,8 @@ import javax.servlet.http.HttpServletResponse;
+ 
+ import org.w3c.dom.Node;
+ 
++import org.apache.commons.lang.StringEscapeUtils;
++
+ import com.netscape.certsrv.apps.CMS;
+ import com.netscape.certsrv.apps.ICommandQueue;
+ import com.netscape.certsrv.authentication.AuthToken;
+@@ -762,7 +764,7 @@ public abstract class CMSServlet extends HttpServlet {
+         if (tokenIdx != -1) {
+             finalErrMsg =
+                     mFinalErrorMsg.substring(0, tokenIdx) +
+-                            ex.toString() +
++                            StringEscapeUtils.escapeHtml(ex.toString()) +
+                             mFinalErrorMsg.substring(
+                                     tokenIdx + ERROR_MSG_TOKEN.length());
+         }
+-- 
+1.8.3.1
+
+
+From dedca98da44794311e68e9f6c555a11faf33eb33 Mon Sep 17 00:00:00 2001
+From: Alexander Scheel <ascheel@redhat.com>
+Date: Wed, 2 Dec 2020 11:42:30 -0500
+Subject: [PATCH 04/17] Fix reflected XSS attack when hitting getCookie
+ endpoint
+
+This patch sanitizes the Server generated error message, to escape
+the HTML tags if any present.
+
+Resolves: BZ#1789907
+
+Authored by Dinesh Prasanth M K; backported by ascheel.
+
+Signed-off-by: Dinesh Prasanth M K <dmoluguw@redhat.com>
+Signed-off-by: Alexander Scheel <ascheel@redhat.com>
+(cherry picked from commit 5eec56ba16b62091d5ca9eb334f41045f7173a3b)
+---
+ .../cms/src/com/netscape/cms/servlet/csadmin/GetCookie.java       | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/base/server/cms/src/com/netscape/cms/servlet/csadmin/GetCookie.java b/base/server/cms/src/com/netscape/cms/servlet/csadmin/GetCookie.java
+index f86f249..50772ea 100644
+--- a/base/server/cms/src/com/netscape/cms/servlet/csadmin/GetCookie.java
++++ b/base/server/cms/src/com/netscape/cms/servlet/csadmin/GetCookie.java
+@@ -28,6 +28,8 @@ import javax.servlet.ServletOutputStream;
+ import javax.servlet.http.HttpServletRequest;
+ import javax.servlet.http.HttpServletResponse;
+ 
++import org.apache.commons.lang.StringEscapeUtils;
++
+ import com.netscape.certsrv.apps.CMS;
+ import com.netscape.certsrv.authentication.IAuthToken;
+ import com.netscape.certsrv.base.EBaseException;
+@@ -115,7 +117,7 @@ public class GetCookie extends CMSServlet {
+             u = new URL(url_e);
+         } catch (Exception eee) {
+             throw new ECMSGWException(
+-                    "Unable to parse URL: " + url);
++                    "Unable to parse URL: " + StringEscapeUtils.escapeHtml(url));
+         }
+ 
+         int index2 = url_e.indexOf("subsystem=");
+@@ -139,7 +141,7 @@ public class GetCookie extends CMSServlet {
+             header.addStringValue("host", u.getHost());
+             header.addStringValue("sdhost", CMS.getEESSLHost());
+             header.addStringValue("subsystem", subsystem);
+-            header.addStringValue("url", url_e);
++            header.addStringValue("url", StringEscapeUtils.escapeHtml(url_e));
+             header.addStringValue("errorString", "Failed Authentication");
+             String sdname = cs.getString("securitydomain.name", "");
+             header.addStringValue("sdname", sdname);
+@@ -208,7 +210,7 @@ public class GetCookie extends CMSServlet {
+                         */
+                     }
+ 
+-                    header.addStringValue("url", url);
++                    header.addStringValue("url", StringEscapeUtils.escapeHtml(url));
+                     header.addStringValue("session_id", cookie);
+ 
+                     try {
+-- 
+1.8.3.1
+
+
+From 0c4bbc7576b44b83329470bc364b076216de56be Mon Sep 17 00:00:00 2001
+From: Alexander Scheel <ascheel@redhat.com>
+Date: Fri, 19 Jun 2020 12:28:38 -0400
+Subject: [PATCH 05/17] Replace CMSTemplate custom sanitization with lang2
+
+Signed-off-by: Alexander Scheel <ascheel@redhat.com>
+(cherry picked from commit 70b5ba44645cc12cc643ccd284eb4d4df7a68ccb)
+---
+ .../netscape/cms/servlet/common/CMSTemplate.java   | 244 +--------------------
+ 1 file changed, 8 insertions(+), 236 deletions(-)
+
+diff --git a/base/server/cms/src/com/netscape/cms/servlet/common/CMSTemplate.java b/base/server/cms/src/com/netscape/cms/servlet/common/CMSTemplate.java
+index fe5a14b..7dc28f9 100644
+--- a/base/server/cms/src/com/netscape/cms/servlet/common/CMSTemplate.java
++++ b/base/server/cms/src/com/netscape/cms/servlet/common/CMSTemplate.java
+@@ -30,6 +30,8 @@ import java.io.UnsupportedEncodingException;
+ import java.math.BigInteger;
+ import java.util.Enumeration;
+ 
++import org.apache.commons.lang.StringEscapeUtils;
++
+ import com.netscape.certsrv.apps.CMS;
+ import com.netscape.certsrv.base.EBaseException;
+ import com.netscape.certsrv.base.IArgBlock;
+@@ -335,7 +337,7 @@ public class CMSTemplate extends CMSFile {
+                 ((Character) v).equals(Character.valueOf((char) 0))) {
+             s = "null";
+         } else {
+-            s = "\"" + v.toString() + "\"";
++            s = "\"" + CMSTemplate.escapeJavaScriptString(v.toString()) + "\"";
+         }
+ 
+         return s;
+@@ -345,248 +347,18 @@ public class CMSTemplate extends CMSFile {
+      * Escape the contents of src string in preparation to be enclosed in
+      * double quotes as a JavaScript String Literal within an &lt;script&gt;
+      * portion of an HTML document.
+-     * stevep - performance improvements - about 4 times faster than before.
+      */
+-
+     public static String escapeJavaScriptString(String v) {
+-        int l = v.length();
+-        char in[] = new char[l];
+-        char out[] = new char[l * 4];
+-        int j = 0;
+-
+-        v.getChars(0, l, in, 0);
+-
+-        for (int i = 0; i < l; i++) {
+-            char c = in[i];
+-
+-            if ((c > 0x23) && (c != 0x5c) && (c != 0x3c) && (c != 0x3e) && (c != 0x3b)) {
+-                out[j++] = c;
+-                continue;
+-            }
+-
+-            /* some inputs are coming in as '\' and 'n' */
+-            /* see BZ 500736 for details */
+-            if ((c == 0x5c) && ((i+1)<l) && (in[i+1] == 'n' ||
+-                 in[i+1] == 'r' || in[i+1] == 'f' || in[i+1] == 't' ||
+-                 in[i+1] == '<' || in[i+1] == '>' ||
+-                 in[i+1] == 'x' || in[i+1] == ';' ||
+-                 in[i+1] == '\"' || in[i+1] == '\'' || in[i+1] == '\\')) {
+-                if (in[i+1] == 'x' && ((i+3)<l) && in[i+2] == '3' &&
+-                    (in[i+3] == 'c' || in[i+3] == 'e')) {
+-                    out[j++] = '\\';
+-                    out[j++] = in[i+1];
+-                    out[j++] = in[i+2];
+-                    out[j++] = in[i+3];
+-                    i += 3;
+-                    continue;
+-                } else if (in[i+1] == '<' || in[i+1] == '>') {
+-                    c = in[i+1];
+-                    i++;
+-                } else if (in[i+1] == ';') {
+-                    out[j++] = in[i+1];
+-                    i++;
+-                    continue;
+-                } else {
+-                    out[j++] = '\\';
+-                    out[j++] = in[i+1];
+-                    i++;
+-                    continue;
+-                }
+-            }
+-            if (c == '&') {
+-                int k;
+-                for (k = 0; k < 8 && (i+k) < l; k++) {
+-                    out[j+k] = in[i+k];
+-                    if (in[i+k] == ';') break;
+-                    if (in[i+k] == '<' || in[i+k] == '>') {
+-                        k = 8;
+-                        break;
+-                    }
+-                }
+-                if (k < 8) {
+-                    i += k;
+-                    j += k + 1;
+-                    continue;
+-                }
+-            }
+-
+-            switch (c) {
+-            case '\n':
+-                out[j++] = '\\';
+-                out[j++] = 'n';
+-                break;
+-
+-            case '\\':
+-                out[j++] = '\\';
+-                out[j++] = '\\';
+-                break;
+-
+-            case '\"':
+-                out[j++] = '\\';
+-                out[j++] = '\"';
+-                break;
+-
+-            case '\r':
+-                out[j++] = '\\';
+-                out[j++] = 'r';
+-                break;
+-
+-            case '\f':
+-                out[j++] = '\\';
+-                out[j++] = 'f';
+-                break;
+-
+-            case '\t':
+-                out[j++] = '\\';
+-                out[j++] = 't';
+-                break;
+-
+-            case '<':
+-                out[j++] = '\\';
+-                out[j++] = 'x';
+-                out[j++] = '3';
+-                out[j++] = 'c';
+-                break;
+-
+-            case '>':
+-                out[j++] = '\\';
+-                out[j++] = 'x';
+-                out[j++] = '3';
+-                out[j++] = 'e';
+-                break;
+-
+-            case '&':
+-                out[j++] = '&';
+-                out[j++] = 'a';
+-                out[j++] = 'm';
+-                out[j++] = 'p';
+-                out[j++] = ';';
+-                break;
+-
+-            default:
+-                out[j++] = c;
+-            }
+-        }
+-        return new String(out, 0, j);
++        return StringEscapeUtils.escapeJavaScript(v);
+     }
+ 
+     /**
+-     * Like escapeJavaScriptString(String s) but also escape '[' for
+-     * HTML processing.
++     * Like escapeJavaScriptString(String s) but also escapes for HTML
++     * processing; i.e., first encode for HTML and then encode for
++     * outputting in JavaScript.
+      */
+-
+     public static String escapeJavaScriptStringHTML(String v) {
+-        int l = v.length();
+-        char in[] = new char[l];
+-        char out[] = new char[l * 8];
+-        int j = 0;
+-
+-        v.getChars(0, l, in, 0);
+-
+-        for (int i = 0; i < l; i++) {
+-            char c = in[i];
+-
+-            if (c > 0x5C) {
+-                out[j++] = c;
+-                continue;
+-            }
+-
+-            if ((c == 0x5c) && ((i + 1) < l) && (in[i + 1] == 'n' ||
+-                    in[i + 1] == 'r' || in[i + 1] == 'f' || in[i + 1] == 't' ||
+-                    in[i + 1] == '<' || in[i + 1] == '>' ||
+-                    in[i + 1] == 'x' || in[i + 1] == ';' ||
+-                    in[i + 1] == '\"' || in[i + 1] == '\'' || in[i + 1] == '\\')) {
+-                if (in[i + 1] == 'x' && ((i + 3) < l) && in[i + 2] == '3' &&
+-                        (in[i + 3] == 'c' || in[i + 3] == 'e')) {
+-                    out[j++] = '\\';
+-                    out[j++] = in[i + 1];
+-                    out[j++] = in[i + 2];
+-                    out[j++] = in[i + 3];
+-                    i += 3;
+-
+-                    continue;
+-                } else if (in[i + 1] == '<' || in[i + 1] == '>') {
+-                    c = in[i + 1];
+-                    i++;
+-                } else if (in[i + 1] == ';') {
+-                    out[j++] = in[i + 1];
+-                    i++;
+-                    continue;
+-                } else {
+-                    out[j++] = '\\';
+-                    out[j++] = in[i + 1];
+-                    i++;
+-                    continue;
+-                }
+-            }
+-            if (c == '&') {
+-                int k;
+-                for (k = 0; k < 8 && (i + k) < l; k++) {
+-                    out[j + k] = in[i + k];
+-                    if (in[i + k] == ';')
+-                        break;
+-                    if (in[i + k] == '<' || in[i + k] == '>') {
+-                        k = 8;
+-                        break;
+-                    }
+-                }
+-                if (k < 8) {
+-                    i += k;
+-                    j += k + 1;
+-                    continue;
+-                }
+-            }
+-
+-            switch (c) {
+-            case '\n':
+-                out[j++] = '\\';
+-                out[j++] = 'n';
+-                break;
+-
+-            case '\\':
+-                out[j++] = '\\';
+-                out[j++] = '\\';
+-                break;
+-
+-            case '\"':
+-                out[j++] = '\\';
+-                out[j++] = '\"';
+-                break;
+-
+-            case '\r':
+-                out[j++] = '\\';
+-                out[j++] = 'r';
+-                break;
+-
+-            case '\f':
+-                out[j++] = '\\';
+-                out[j++] = 'f';
+-                break;
+-
+-            case '\t':
+-                out[j++] = '\\';
+-                out[j++] = 't';
+-                break;
+-
+-            case '<':
+-                out[j++] = '&';
+-                out[j++] = 'l';
+-                out[j++] = 't';
+-                out[j++] = ';';
+-                break;
+-
+-            case '>':
+-                out[j++] = '&';
+-                out[j++] = 'g';
+-                out[j++] = 't';
+-                out[j++] = ';';
+-                break;
+-
+-            default:
+-                out[j++] = c;
+-            }
+-        }
+-        return new String(out, 0, j);
++        return StringEscapeUtils.escapeJavaScript(StringEscapeUtils.escapeHtml(v));
+     }
+ 
+     /**
+-- 
+1.8.3.1
+
+
+From fc5c842cefc01a8b00a1fe4229e9de6123ba0c25 Mon Sep 17 00:00:00 2001
+From: Alexander Scheel <ascheel@redhat.com>
+Date: Tue, 21 Jul 2020 10:20:47 -0400
+Subject: [PATCH 06/17] Re-fix sanitization in CMSTemplate
+
+When fixing CVE-2019-10179 originally in
+8884b4344225bd6656876d9e2a58b3268e9a899b,
+I had switched to Apache Commons Lang2's
+sanitization framework. However, I didn't
+enable the HTML sanitization necessary to
+fix this CVE.
+
+Signed-off-by: Alexander Scheel <ascheel@redhat.com>
+(cherry picked from commit 520f8377aaedc12a116757d6b6c005a78664c33a)
+---
+ base/server/cms/src/com/netscape/cms/servlet/common/CMSTemplate.java | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/base/server/cms/src/com/netscape/cms/servlet/common/CMSTemplate.java b/base/server/cms/src/com/netscape/cms/servlet/common/CMSTemplate.java
+index 7dc28f9..8588dfc 100644
+--- a/base/server/cms/src/com/netscape/cms/servlet/common/CMSTemplate.java
++++ b/base/server/cms/src/com/netscape/cms/servlet/common/CMSTemplate.java
+@@ -349,7 +349,7 @@ public class CMSTemplate extends CMSFile {
+      * portion of an HTML document.
+      */
+     public static String escapeJavaScriptString(String v) {
+-        return StringEscapeUtils.escapeJavaScript(v);
++        return escapeJavaScriptStringHTML(v);
+     }
+ 
+     /**
+-- 
+1.8.3.1
+
+
+From 797eddc42a1656cffdbaca1d14c7c5a37ffbc905 Mon Sep 17 00:00:00 2001
+From: jmagne <jmagne@redhat.com>
+Date: Tue, 9 Jun 2020 15:06:21 -0700
+Subject: [PATCH 07/17] Address CVE-2020-1721. (#434)
+
+Co-authored-by: Jack Magne <jmagne@localhost.localdomain>
+(cherry picked from commit 23a414c910e435d6eacc8daa90193ddc6f59c59a)
+---
+ base/ca/shared/webapps/ca/agent/funcs.js     | 14 ++++++++------
+ base/kra/shared/webapps/kra/agent/funcs.js   | 14 ++++++++------
+ base/ocsp/shared/webapps/ocsp/agent/funcs.js | 14 ++++++++------
+ base/tks/shared/webapps/tks/agent/funcs.js   | 14 ++++++++------
+ 4 files changed, 32 insertions(+), 24 deletions(-)
+
+diff --git a/base/ca/shared/webapps/ca/agent/funcs.js b/base/ca/shared/webapps/ca/agent/funcs.js
+index 958612d..20f0821 100644
+--- a/base/ca/shared/webapps/ca/agent/funcs.js
++++ b/base/ca/shared/webapps/ca/agent/funcs.js
+@@ -671,19 +671,21 @@ function writeError(errorDetails)
+                 "request or the values that were entered into the form." +
+                 "The following message supplies more information " +
+                 "about the error that occurred.<p>");
+-        document.write("<blockquote><b><pre>");
+         if (errorDetails != null) {
+-                document.write(errorDetails);
++		var block = document.createElement('div');
++                block.setAttribute('style', 'font-weight: bold; margin-left: 50px;');
++                block.appendChild(document.createTextNode(errorDetails));
++		document.body.appendChild(block);
+         } else {
+                 document.write("Unable to provide details. " +
+                  "Contact Administrator.");
+         }
+-        document.write("</pre></b></blockquote>");
+         if (result.header.errorDescription != null) {
+                 document.write('<p>Additional Information:<p>');
+-                document.write('<blockquote><b>');
+-                document.write(result.header.errorDescription);
+-                document.write('</b></blockquote>');
++		var block = document.createElement('div');
++                block.setAttribute('style', 'font-weight: bold; margin-left: 50px;');
++                block.appendChild(document.createTextNode(result.header.errorDescription));
++		document.body.appendChild(block);
+         }
+         document.write("<p>");
+         document.write("Please consult your local administrator for " +
+diff --git a/base/kra/shared/webapps/kra/agent/funcs.js b/base/kra/shared/webapps/kra/agent/funcs.js
+index daef83d..8799d0d 100644
+--- a/base/kra/shared/webapps/kra/agent/funcs.js
++++ b/base/kra/shared/webapps/kra/agent/funcs.js
+@@ -621,19 +621,21 @@ function writeError(errorDetails)
+                 "request or the values that were entered into the form." +
+                 "The following message supplies more information " +
+                 "about the error that occurred.<p>");
+-        document.write("<blockquote><b><pre>");
+         if (errorDetails != null) {
+-                document.write(errorDetails);
++		var block = document.createElement('div');
++                block.setAttribute('style', 'font-weight: bold; margin-left: 50px;');
++                block.appendChild(document.createTextNode(errorDetails));
++		document.body.appendChild(block);
+         } else {
+                 document.write("Unable to provide details. " +
+                  "Contact Administrator.");
+         }
+-        document.write("</pre></b></blockquote>");
+         if (result.header.errorDescription != null) {
+                 document.write('<p>Additional Information:<p>');
+-                document.write('<blockquote><b>');
+-                document.write(result.header.errorDescription);
+-                document.write('</b></blockquote>');
++		var block = document.createElement('div');
++                block.setAttribute('style', 'font-weight: bold; margin-left: 50px;');
++                block.appendChild(document.createTextNode(result.header.errorDescription));
++		document.body.appendChild(block);
+         }
+         document.write("<p>");
+         document.write("Please consult your local administrator for " +
+diff --git a/base/ocsp/shared/webapps/ocsp/agent/funcs.js b/base/ocsp/shared/webapps/ocsp/agent/funcs.js
+index daef83d..8799d0d 100644
+--- a/base/ocsp/shared/webapps/ocsp/agent/funcs.js
++++ b/base/ocsp/shared/webapps/ocsp/agent/funcs.js
+@@ -621,19 +621,21 @@ function writeError(errorDetails)
+                 "request or the values that were entered into the form." +
+                 "The following message supplies more information " +
+                 "about the error that occurred.<p>");
+-        document.write("<blockquote><b><pre>");
+         if (errorDetails != null) {
+-                document.write(errorDetails);
++		var block = document.createElement('div');
++                block.setAttribute('style', 'font-weight: bold; margin-left: 50px;');
++                block.appendChild(document.createTextNode(errorDetails));
++		document.body.appendChild(block);
+         } else {
+                 document.write("Unable to provide details. " +
+                  "Contact Administrator.");
+         }
+-        document.write("</pre></b></blockquote>");
+         if (result.header.errorDescription != null) {
+                 document.write('<p>Additional Information:<p>');
+-                document.write('<blockquote><b>');
+-                document.write(result.header.errorDescription);
+-                document.write('</b></blockquote>');
++		var block = document.createElement('div');
++                block.setAttribute('style', 'font-weight: bold; margin-left: 50px;');
++                block.appendChild(document.createTextNode(result.header.errorDescription));
++		document.body.appendChild(block);
+         }
+         document.write("<p>");
+         document.write("Please consult your local administrator for " +
+diff --git a/base/tks/shared/webapps/tks/agent/funcs.js b/base/tks/shared/webapps/tks/agent/funcs.js
+index daef83d..8799d0d 100644
+--- a/base/tks/shared/webapps/tks/agent/funcs.js
++++ b/base/tks/shared/webapps/tks/agent/funcs.js
+@@ -621,19 +621,21 @@ function writeError(errorDetails)
+                 "request or the values that were entered into the form." +
+                 "The following message supplies more information " +
+                 "about the error that occurred.<p>");
+-        document.write("<blockquote><b><pre>");
+         if (errorDetails != null) {
+-                document.write(errorDetails);
++		var block = document.createElement('div');
++                block.setAttribute('style', 'font-weight: bold; margin-left: 50px;');
++                block.appendChild(document.createTextNode(errorDetails));
++		document.body.appendChild(block);
+         } else {
+                 document.write("Unable to provide details. " +
+                  "Contact Administrator.");
+         }
+-        document.write("</pre></b></blockquote>");
+         if (result.header.errorDescription != null) {
+                 document.write('<p>Additional Information:<p>');
+-                document.write('<blockquote><b>');
+-                document.write(result.header.errorDescription);
+-                document.write('</b></blockquote>');
++		var block = document.createElement('div');
++                block.setAttribute('style', 'font-weight: bold; margin-left: 50px;');
++                block.appendChild(document.createTextNode(result.header.errorDescription));
++		document.body.appendChild(block);
+         }
+         document.write("<p>");
+         document.write("Please consult your local administrator for " +
+-- 
+1.8.3.1
+
+
+From a4e95bb22a79c48157f7d3902515411cdbf6e302 Mon Sep 17 00:00:00 2001
+From: Alexander Scheel <ascheel@redhat.com>
+Date: Mon, 16 Mar 2020 11:44:53 -0400
+Subject: [PATCH 08/17] Update to jquery v3.4.1
+
+Signed-off-by: Alexander Scheel <ascheel@redhat.com>
+(cherry picked from commit 5b7d14172aaef86278e96e7c7c03db47b8c7f416)
+---
+ base/server/share/webapps/pki/js/jquery.js | 13947 ++++++++++++++-------------
+ 1 file changed, 7378 insertions(+), 6569 deletions(-)
+
+diff --git a/base/server/share/webapps/pki/js/jquery.js b/base/server/share/webapps/pki/js/jquery.js
+index c5c6482..773ad95 100644
+--- a/base/server/share/webapps/pki/js/jquery.js
++++ b/base/server/share/webapps/pki/js/jquery.js
+@@ -1,247 +1,186 @@
+ /*!
+- * jQuery JavaScript Library v1.10.2
+- * http://jquery.com/
++ * jQuery JavaScript Library v3.4.1
++ * https://jquery.com/
+  *
+  * Includes Sizzle.js
+- * http://sizzlejs.com/
++ * https://sizzlejs.com/
+  *
+- * Copyright 2005, 2013 jQuery Foundation, Inc. and other contributors
++ * Copyright JS Foundation and other contributors
+  * Released under the MIT license
+- * http://jquery.org/license
++ * https://jquery.org/license
+  *
+- * Date: 2013-07-03T13:48Z
++ * Date: 2019-05-01T21:04Z
+  */
+-(function( window, undefined ) {
+-
+-// Can't do this because several apps including ASP.NET trace
+-// the stack via arguments.caller.callee and Firefox dies if
+-// you try to trace through "use strict" call chains. (#13335)
+-// Support: Firefox 18+
+-//"use strict";
+-var
+-	// The deferred used on DOM ready
+-	readyList,
+-
+-	// A central reference to the root jQuery(document)
+-	rootjQuery,
+-
+-	// Support: IE<10
+-	// For `typeof xmlNode.method` instead of `xmlNode.method !== undefined`
+-	core_strundefined = typeof undefined,
++( function( global, factory ) {
++
++	"use strict";
++
++	if ( typeof module === "object" && typeof module.exports === "object" ) {
++
++		// For CommonJS and CommonJS-like environments where a proper `window`
++		// is present, execute the factory and get jQuery.
++		// For environments that do not have a `window` with a `document`
++		// (such as Node.js), expose a factory as module.exports.
++		// This accentuates the need for the creation of a real `window`.
++		// e.g. var jQuery = require("jquery")(window);
++		// See ticket #14549 for more info.
++		module.exports = global.document ?
++			factory( global, true ) :
++			function( w ) {
++				if ( !w.document ) {
++					throw new Error( "jQuery requires a window with a document" );
++				}
++				return factory( w );
++			};
++	} else {
++		factory( global );
++	}
+ 
+-	// Use the correct document accordingly with window argument (sandbox)
+-	location = window.location,
+-	document = window.document,
+-	docElem = document.documentElement,
++// Pass this if window is not defined yet
++} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
+ 
+-	// Map over jQuery in case of overwrite
+-	_jQuery = window.jQuery,
++// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1
++// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode
++// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common
++// enough that all such attempts are guarded in a try block.
++"use strict";
+ 
+-	// Map over the $ in case of overwrite
+-	_$ = window.$,
++var arr = [];
+ 
+-	// [[Class]] -> type pairs
+-	class2type = {},
++var document = window.document;
+ 
+-	// List of deleted data cache ids, so we can reuse them
+-	core_deletedIds = [],
++var getProto = Object.getPrototypeOf;
+ 
+-	core_version = "1.10.2",
++var slice = arr.slice;
+ 
+-	// Save a reference to some core methods
+-	core_concat = core_deletedIds.concat,
+-	core_push = core_deletedIds.push,
+-	core_slice = core_deletedIds.slice,
+-	core_indexOf = core_deletedIds.indexOf,
+-	core_toString = class2type.toString,
+-	core_hasOwn = class2type.hasOwnProperty,
+-	core_trim = core_version.trim,
++var concat = arr.concat;
+ 
+-	// Define a local copy of jQuery
+-	jQuery = function( selector, context ) {
+-		// The jQuery object is actually just the init constructor 'enhanced'
+-		return new jQuery.fn.init( selector, context, rootjQuery );
+-	},
++var push = arr.push;
+ 
+-	// Used for matching numbers
+-	core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,
++var indexOf = arr.indexOf;
+ 
+-	// Used for splitting on whitespace
+-	core_rnotwhite = /\S+/g,
++var class2type = {};
+ 
+-	// Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE)
+-	rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
++var toString = class2type.toString;
+ 
+-	// A simple way to check for HTML strings
+-	// Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
+-	// Strict HTML recognition (#11290: must start with <)
+-	rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
++var hasOwn = class2type.hasOwnProperty;
+ 
+-	// Match a standalone tag
+-	rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/,
++var fnToString = hasOwn.toString;
+ 
+-	// JSON RegExp
+-	rvalidchars = /^[\],:{}\s]*$/,
+-	rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
+-	rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,
+-	rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,
++var ObjectFunctionString = fnToString.call( Object );
+ 
+-	// Matches dashed string for camelizing
+-	rmsPrefix = /^-ms-/,
+-	rdashAlpha = /-([\da-z])/gi,
++var support = {};
+ 
+-	// Used by jQuery.camelCase as callback to replace()
+-	fcamelCase = function( all, letter ) {
+-		return letter.toUpperCase();
+-	},
++var isFunction = function isFunction( obj ) {
+ 
+-	// The ready event handler
+-	completed = function( event ) {
++      // Support: Chrome <=57, Firefox <=52
++      // In some browsers, typeof returns "function" for HTML <object> elements
++      // (i.e., `typeof document.createElement( "object" ) === "function"`).
++      // We don't want to classify *any* DOM node as a function.
++      return typeof obj === "function" && typeof obj.nodeType !== "number";
++  };
+ 
+-		// readyState === "complete" is good enough for us to call the dom ready in oldIE
+-		if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) {
+-			detach();
+-			jQuery.ready();
+-		}
+-	},
+-	// Clean-up method for dom ready events
+-	detach = function() {
+-		if ( document.addEventListener ) {
+-			document.removeEventListener( "DOMContentLoaded", completed, false );
+-			window.removeEventListener( "load", completed, false );
+ 
+-		} else {
+-			document.detachEvent( "onreadystatechange", completed );
+-			window.detachEvent( "onload", completed );
+-		}
++var isWindow = function isWindow( obj ) {
++		return obj != null && obj === obj.window;
+ 	};
+ 
+-jQuery.fn = jQuery.prototype = {
+-	// The current version of jQuery being used
+-	jquery: core_version,
+-
+-	constructor: jQuery,
+-	init: function( selector, context, rootjQuery ) {
+-		var match, elem;
+ 
+-		// HANDLE: $(""), $(null), $(undefined), $(false)
+-		if ( !selector ) {
+-			return this;
+-		}
+ 
+-		// Handle HTML strings
+-		if ( typeof selector === "string" ) {
+-			if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
+-				// Assume that strings that start and end with <> are HTML and skip the regex check
+-				match = [ null, selector, null ];
+ 
+-			} else {
+-				match = rquickExpr.exec( selector );
+-			}
++	var preservedScriptAttributes = {
++		type: true,
++		src: true,
++		nonce: true,
++		noModule: true
++	};
+ 
+-			// Match html or make sure no context is specified for #id
+-			if ( match && (match[1] || !context) ) {
++	function DOMEval( code, node, doc ) {
++		doc = doc || document;
+ 
+-				// HANDLE: $(html) -> $(array)
+-				if ( match[1] ) {
+-					context = context instanceof jQuery ? context[0] : context;
++		var i, val,
++			script = doc.createElement( "script" );
+ 
+-					// scripts is true for back-compat
+-					jQuery.merge( this, jQuery.parseHTML(
+-						match[1],
+-						context && context.nodeType ? context.ownerDocument || context : document,
+-						true
+-					) );
++		script.text = code;
++		if ( node ) {
++			for ( i in preservedScriptAttributes ) {
+ 
+-					// HANDLE: $(html, props)
+-					if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
+-						for ( match in context ) {
+-							// Properties of context are called as methods if possible
+-							if ( jQuery.isFunction( this[ match ] ) ) {
+-								this[ match ]( context[ match ] );
++				// Support: Firefox 64+, Edge 18+
++				// Some browsers don't support the "nonce" property on scripts.
++				// On the other hand, just using `getAttribute` is not enough as
++				// the `nonce` attribute is reset to an empty string whenever it
++				// becomes browsing-context connected.
++				// See https://github.com/whatwg/html/issues/2369
++				// See https://html.spec.whatwg.org/#nonce-attributes
++				// The `node.getAttribute` check was added for the sake of
++				// `jQuery.globalEval` so that it can fake a nonce-containing node
++				// via an object.
++				val = node[ i ] || node.getAttribute && node.getAttribute( i );
++				if ( val ) {
++					script.setAttribute( i, val );
++				}
++			}
++		}
++		doc.head.appendChild( script ).parentNode.removeChild( script );
++	}
+ 
+-							// ...and otherwise set as attributes
+-							} else {
+-								this.attr( match, context[ match ] );
+-							}
+-						}
+-					}
+ 
+-					return this;
++function toType( obj ) {
++	if ( obj == null ) {
++		return obj + "";
++	}
+ 
+-				// HANDLE: $(#id)
+-				} else {
+-					elem = document.getElementById( match[2] );
+-
+-					// Check parentNode to catch when Blackberry 4.6 returns
+-					// nodes that are no longer in the document #6963
+-					if ( elem && elem.parentNode ) {
+-						// Handle the case where IE and Opera return items
+-						// by name instead of ID
+-						if ( elem.id !== match[2] ) {
+-							return rootjQuery.find( selector );
+-						}
++	// Support: Android <=2.3 only (functionish RegExp)
++	return typeof obj === "object" || typeof obj === "function" ?
++		class2type[ toString.call( obj ) ] || "object" :
++		typeof obj;
++}
++/* global Symbol */
++// Defining this global in .eslintrc.json would create a danger of using the global
++// unguarded in another place, it seems safer to define global only for this module
+ 
+-						// Otherwise, we inject the element directly into the jQuery object
+-						this.length = 1;
+-						this[0] = elem;
+-					}
+ 
+-					this.context = document;
+-					this.selector = selector;
+-					return this;
+-				}
+ 
+-			// HANDLE: $(expr, $(...))
+-			} else if ( !context || context.jquery ) {
+-				return ( context || rootjQuery ).find( selector );
++var
++	version = "3.4.1",
+ 
+-			// HANDLE: $(expr, context)
+-			// (which is just equivalent to: $(context).find(expr)
+-			} else {
+-				return this.constructor( context ).find( selector );
+-			}
++	// Define a local copy of jQuery
++	jQuery = function( selector, context ) {
+ 
+-		// HANDLE: $(DOMElement)
+-		} else if ( selector.nodeType ) {
+-			this.context = this[0] = selector;
+-			this.length = 1;
+-			return this;
++		// The jQuery object is actually just the init constructor 'enhanced'
++		// Need init if jQuery is called (just allow error to be thrown if not included)
++		return new jQuery.fn.init( selector, context );
++	},
+ 
+-		// HANDLE: $(function)
+-		// Shortcut for document ready
+-		} else if ( jQuery.isFunction( selector ) ) {
+-			return rootjQuery.ready( selector );
+-		}
++	// Support: Android <=4.0 only
++	// Make sure we trim BOM and NBSP
++	rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;
+ 
+-		if ( selector.selector !== undefined ) {
+-			this.selector = selector.selector;
+-			this.context = selector.context;
+-		}
++jQuery.fn = jQuery.prototype = {
+ 
+-		return jQuery.makeArray( selector, this );
+-	},
++	// The current version of jQuery being used
++	jquery: version,
+ 
+-	// Start with an empty selector
+-	selector: "",
++	constructor: jQuery,
+ 
+ 	// The default length of a jQuery object is 0
+ 	length: 0,
+ 
+ 	toArray: function() {
+-		return core_slice.call( this );
++		return slice.call( this );
+ 	},
+ 
+ 	// Get the Nth element in the matched element set OR
+ 	// Get the whole matched element set as a clean array
+ 	get: function( num ) {
+-		return num == null ?
+ 
+-			// Return a 'clean' array
+-			this.toArray() :
++		// Return all the elements in a clean array
++		if ( num == null ) {
++			return slice.call( this );
++		}
+ 
+-			// Return just the object
+-			( num < 0 ? this[ this.length + num ] : this[ num ] );
++		// Return just the one element from the set
++		return num < 0 ? this[ num + this.length ] : this[ num ];
+ 	},
+ 
+ 	// Take an array of elements and push it onto the stack
+@@ -253,28 +192,24 @@ jQuery.fn = jQuery.prototype = {
+ 
+ 		// Add the old object onto the stack (as a reference)
+ 		ret.prevObject = this;
+-		ret.context = this.context;
+ 
+ 		// Return the newly-formed element set
+ 		return ret;
+ 	},
+ 
+ 	// Execute a callback for every element in the matched set.
+-	// (You can seed the arguments with an array of args, but this is
+-	// only used internally.)
+-	each: function( callback, args ) {
+-		return jQuery.each( this, callback, args );
++	each: function( callback ) {
++		return jQuery.each( this, callback );
+ 	},
+ 
+-	ready: function( fn ) {
+-		// Add the callback
+-		jQuery.ready.promise().done( fn );
+-
+-		return this;
++	map: function( callback ) {
++		return this.pushStack( jQuery.map( this, function( elem, i ) {
++			return callback.call( elem, i, elem );
++		} ) );
+ 	},
+ 
+ 	slice: function() {
+-		return this.pushStack( core_slice.apply( this, arguments ) );
++		return this.pushStack( slice.apply( this, arguments ) );
+ 	},
+ 
+ 	first: function() {
+@@ -288,32 +223,23 @@ jQuery.fn = jQuery.prototype = {
+ 	eq: function( i ) {
+ 		var len = this.length,
+ 			j = +i + ( i < 0 ? len : 0 );
+-		return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
+-	},
+-
+-	map: function( callback ) {
+-		return this.pushStack( jQuery.map(this, function( elem, i ) {
+-			return callback.call( elem, i, elem );
+-		}));
++		return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] );
+ 	},
+ 
+ 	end: function() {
+-		return this.prevObject || this.constructor(null);
++		return this.prevObject || this.constructor();
+ 	},
+ 
+ 	// For internal use only.
+ 	// Behaves like an Array's method, not like a jQuery method.
+-	push: core_push,
+-	sort: [].sort,
+-	splice: [].splice
++	push: push,
++	sort: arr.sort,
++	splice: arr.splice
+ };
+ 
+-// Give the init function the jQuery prototype for later instantiation
+-jQuery.fn.init.prototype = jQuery.fn;
+-
+ jQuery.extend = jQuery.fn.extend = function() {
+-	var src, copyIsArray, copy, name, options, clone,
+-		target = arguments[0] || {},
++	var options, name, src, copy, copyIsArray, clone,
++		target = arguments[ 0 ] || {},
+ 		i = 1,
+ 		length = arguments.length,
+ 		deep = false;
+@@ -321,44 +247,52 @@ jQuery.extend = jQuery.fn.extend = function() {
+ 	// Handle a deep copy situation
+ 	if ( typeof target === "boolean" ) {
+ 		deep = target;
+-		target = arguments[1] || {};
+-		// skip the boolean and the target
+-		i = 2;
++
++		// Skip the boolean and the target
++		target = arguments[ i ] || {};
++		i++;
+ 	}
+ 
+ 	// Handle case when target is a string or something (possible in deep copy)
+-	if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
++	if ( typeof target !== "object" && !isFunction( target ) ) {
+ 		target = {};
+ 	}
+ 
+-	// extend jQuery itself if only one argument is passed
+-	if ( length === i ) {
++	// Extend jQuery itself if only one argument is passed
++	if ( i === length ) {
+ 		target = this;
+-		--i;
++		i--;
+ 	}
+ 
+ 	for ( ; i < length; i++ ) {
++
+ 		// Only deal with non-null/undefined values
+-		if ( (options = arguments[ i ]) != null ) {
++		if ( ( options = arguments[ i ] ) != null ) {
++
+ 			// Extend the base object
+ 			for ( name in options ) {
+-				src = target[ name ];
+ 				copy = options[ name ];
+ 
++				// Prevent Object.prototype pollution
+ 				// Prevent never-ending loop
+-				if ( target === copy ) {
++				if ( name === "__proto__" || target === copy ) {
+ 					continue;
+ 				}
+ 
+ 				// Recurse if we're merging plain objects or arrays
+-				if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
+-					if ( copyIsArray ) {
+-						copyIsArray = false;
+-						clone = src && jQuery.isArray(src) ? src : [];
+-
++				if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
++					( copyIsArray = Array.isArray( copy ) ) ) ) {
++					src = target[ name ];
++
++					// Ensure proper type for the source value
++					if ( copyIsArray && !Array.isArray( src ) ) {
++						clone = [];
++					} else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) {
++						clone = {};
+ 					} else {
+-						clone = src && jQuery.isPlainObject(src) ? src : {};
++						clone = src;
+ 					}
++					copyIsArray = false;
+ 
+ 					// Never move original objects, clone them
+ 					target[ name ] = jQuery.extend( deep, clone, copy );
+@@ -375,298 +309,69 @@ jQuery.extend = jQuery.fn.extend = function() {
+ 	return target;
+ };
+ 
+-jQuery.extend({
+-	// Unique for each copy of jQuery on the page
+-	// Non-digits removed to match rinlinejQuery
+-	expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ),
+-
+-	noConflict: function( deep ) {
+-		if ( window.$ === jQuery ) {
+-			window.$ = _$;
+-		}
+-
+-		if ( deep && window.jQuery === jQuery ) {
+-			window.jQuery = _jQuery;
+-		}
+-
+-		return jQuery;
+-	},
+-
+-	// Is the DOM ready to be used? Set to true once it occurs.
+-	isReady: false,
+-
+-	// A counter to track how many items to wait for before
+-	// the ready event fires. See #6781
+-	readyWait: 1,
+-
+-	// Hold (or release) the ready event
+-	holdReady: function( hold ) {
+-		if ( hold ) {
+-			jQuery.readyWait++;
+-		} else {
+-			jQuery.ready( true );
+-		}
+-	},
+-
+-	// Handle when the DOM is ready
+-	ready: function( wait ) {
+-
+-		// Abort if there are pending holds or we're already ready
+-		if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
+-			return;
+-		}
+-
+-		// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+-		if ( !document.body ) {
+-			return setTimeout( jQuery.ready );
+-		}
+-
+-		// Remember that the DOM is ready
+-		jQuery.isReady = true;
+-
+-		// If a normal DOM Ready event fired, decrement, and wait if need be
+-		if ( wait !== true && --jQuery.readyWait > 0 ) {
+-			return;
+-		}
+-
+-		// If there are functions bound, to execute
+-		readyList.resolveWith( document, [ jQuery ] );
+-
+-		// Trigger any bound ready events
+-		if ( jQuery.fn.trigger ) {
+-			jQuery( document ).trigger("ready").off("ready");
+-		}
+-	},
+-
+-	// See test/unit/core.js for details concerning isFunction.
+-	// Since version 1.3, DOM methods and functions like alert
+-	// aren't supported. They return false on IE (#2968).
+-	isFunction: function( obj ) {
+-		return jQuery.type(obj) === "function";
+-	},
++jQuery.extend( {
+ 
+-	isArray: Array.isArray || function( obj ) {
+-		return jQuery.type(obj) === "array";
+-	},
++	// Unique for each copy of jQuery on the page
++	expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),
+ 
+-	isWindow: function( obj ) {
+-		/* jshint eqeqeq: false */
+-		return obj != null && obj == obj.window;
+-	},
++	// Assume jQuery is ready without the ready module
++	isReady: true,
+ 
+-	isNumeric: function( obj ) {
+-		return !isNaN( parseFloat(obj) ) && isFinite( obj );
++	error: function( msg ) {
++		throw new Error( msg );
+ 	},
+ 
+-	type: function( obj ) {
+-		if ( obj == null ) {
+-			return String( obj );
+-		}
+-		return typeof obj === "object" || typeof obj === "function" ?
+-			class2type[ core_toString.call(obj) ] || "object" :
+-			typeof obj;
+-	},
++	noop: function() {},
+ 
+ 	isPlainObject: function( obj ) {
+-		var key;
++		var proto, Ctor;
+ 
+-		// Must be an Object.
+-		// Because of IE, we also have to check the presence of the constructor property.
+-		// Make sure that DOM nodes and window objects don't pass through, as well
+-		if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
++		// Detect obvious negatives
++		// Use toString instead of jQuery.type to catch host objects
++		if ( !obj || toString.call( obj ) !== "[object Object]" ) {
+ 			return false;
+ 		}
+ 
+-		try {
+-			// Not own constructor property must be Object
+-			if ( obj.constructor &&
+-				!core_hasOwn.call(obj, "constructor") &&
+-				!core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
+-				return false;
+-			}
+-		} catch ( e ) {
+-			// IE8,9 Will throw exceptions on certain host objects #9897
+-			return false;
+-		}
++		proto = getProto( obj );
+ 
+-		// Support: IE<9
+-		// Handle iteration over inherited properties before own properties.
+-		if ( jQuery.support.ownLast ) {
+-			for ( key in obj ) {
+-				return core_hasOwn.call( obj, key );
+-			}
++		// Objects with no prototype (e.g., `Object.create( null )`) are plain
++		if ( !proto ) {
++			return true;
+ 		}
+ 
+-		// Own properties are enumerated firstly, so to speed up,
+-		// if last one is own, then all properties are own.
+-		for ( key in obj ) {}
+-
+-		return key === undefined || core_hasOwn.call( obj, key );
++		// Objects with prototype are plain iff they were constructed by a global Object function
++		Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor;
++		return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString;
+ 	},
+ 
+ 	isEmptyObject: function( obj ) {
+ 		var name;
++
+ 		for ( name in obj ) {
+ 			return false;
+ 		}
+ 		return true;
+ 	},
+ 
+-	error: function( msg ) {
+-		throw new Error( msg );
+-	},
+-
+-	// data: string of html
+-	// context (optional): If specified, the fragment will be created in this context, defaults to document
+-	// keepScripts (optional): If true, will include scripts passed in the html string
+-	parseHTML: function( data, context, keepScripts ) {
+-		if ( !data || typeof data !== "string" ) {
+-			return null;
+-		}
+-		if ( typeof context === "boolean" ) {
+-			keepScripts = context;
+-			context = false;
+-		}
+-		context = context || document;
+-
+-		var parsed = rsingleTag.exec( data ),
+-			scripts = !keepScripts && [];
+-
+-		// Single tag
+-		if ( parsed ) {
+-			return [ context.createElement( parsed[1] ) ];
+-		}
+-
+-		parsed = jQuery.buildFragment( [ data ], context, scripts );
+-		if ( scripts ) {
+-			jQuery( scripts ).remove();
+-		}
+-		return jQuery.merge( [], parsed.childNodes );
+-	},
+-
+-	parseJSON: function( data ) {
+-		// Attempt to parse using the native JSON parser first
+-		if ( window.JSON && window.JSON.parse ) {
+-			return window.JSON.parse( data );
+-		}
+-
+-		if ( data === null ) {
+-			return data;
+-		}
+-
+-		if ( typeof data === "string" ) {
+-
+-			// Make sure leading/trailing whitespace is removed (IE can't handle it)
+-			data = jQuery.trim( data );
+-
+-			if ( data ) {
+-				// Make sure the incoming data is actual JSON
+-				// Logic borrowed from http://json.org/json2.js
+-				if ( rvalidchars.test( data.replace( rvalidescape, "@" )
+-					.replace( rvalidtokens, "]" )
+-					.replace( rvalidbraces, "")) ) {
+-
+-					return ( new Function( "return " + data ) )();
+-				}
+-			}
+-		}
+-
+-		jQuery.error( "Invalid JSON: " + data );
+-	},
+-
+-	// Cross-browser xml parsing
+-	parseXML: function( data ) {
+-		var xml, tmp;
+-		if ( !data || typeof data !== "string" ) {
+-			return null;
+-		}
+-		try {
+-			if ( window.DOMParser ) { // Standard
+-				tmp = new DOMParser();
+-				xml = tmp.parseFromString( data , "text/xml" );
+-			} else { // IE
+-				xml = new ActiveXObject( "Microsoft.XMLDOM" );
+-				xml.async = "false";
+-				xml.loadXML( data );
+-			}
+-		} catch( e ) {
+-			xml = undefined;
+-		}
+-		if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) {
+-			jQuery.error( "Invalid XML: " + data );
+-		}
+-		return xml;
+-	},
+-
+-	noop: function() {},
+-
+ 	// Evaluates a script in a global context
+-	// Workarounds based on findings by Jim Driscoll
+-	// http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
+-	globalEval: function( data ) {
+-		if ( data && jQuery.trim( data ) ) {
+-			// We use execScript on Internet Explorer
+-			// We use an anonymous function so that context is window
+-			// rather than jQuery in Firefox
+-			( window.execScript || function( data ) {
+-				window[ "eval" ].call( window, data );
+-			} )( data );
+-		}
++	globalEval: function( code, options ) {
++		DOMEval( code, { nonce: options && options.nonce } );
+ 	},
+ 
+-	// Convert dashed to camelCase; used by the css and data modules
+-	// Microsoft forgot to hump their vendor prefix (#9572)
+-	camelCase: function( string ) {
+-		return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
+-	},
+-
+-	nodeName: function( elem, name ) {
+-		return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
+-	},
+-
+-	// args is for internal usage only
+-	each: function( obj, callback, args ) {
+-		var value,
+-			i = 0,
+-			length = obj.length,
+-			isArray = isArraylike( obj );
+-
+-		if ( args ) {
+-			if ( isArray ) {
+-				for ( ; i < length; i++ ) {
+-					value = callback.apply( obj[ i ], args );
+-
+-					if ( value === false ) {
+-						break;
+-					}
+-				}
+-			} else {
+-				for ( i in obj ) {
+-					value = callback.apply( obj[ i ], args );
++	each: function( obj, callback ) {
++		var length, i = 0;
+ 
+-					if ( value === false ) {
+-						break;
+-					}
++		if ( isArrayLike( obj ) ) {
++			length = obj.length;
++			for ( ; i < length; i++ ) {
++				if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
++					break;
+ 				}
+ 			}
+-
+-		// A special, fast, case for the most common use of each
+ 		} else {
+-			if ( isArray ) {
+-				for ( ; i < length; i++ ) {
+-					value = callback.call( obj[ i ], i, obj[ i ] );
+-
+-					if ( value === false ) {
+-						break;
+-					}
+-				}
+-			} else {
+-				for ( i in obj ) {
+-					value = callback.call( obj[ i ], i, obj[ i ] );
+-
+-					if ( value === false ) {
+-						break;
+-					}
++			for ( i in obj ) {
++				if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
++					break;
+ 				}
+ 			}
+ 		}
+@@ -674,33 +379,25 @@ jQuery.extend({
+ 		return obj;
+ 	},
+ 
+-	// Use native String.trim function wherever possible
+-	trim: core_trim && !core_trim.call("\uFEFF\xA0") ?
+-		function( text ) {
+-			return text == null ?
+-				"" :
+-				core_trim.call( text );
+-		} :
+-
+-		// Otherwise use our own trimming functionality
+-		function( text ) {
+-			return text == null ?
+-				"" :
+-				( text + "" ).replace( rtrim, "" );
+-		},
++	// Support: Android <=4.0 only
++	trim: function( text ) {
++		return text == null ?
++			"" :
++			( text + "" ).replace( rtrim, "" );
++	},
+ 
+ 	// results is for internal usage only
+ 	makeArray: function( arr, results ) {
+ 		var ret = results || [];
+ 
+ 		if ( arr != null ) {
+-			if ( isArraylike( Object(arr) ) ) {
++			if ( isArrayLike( Object( arr ) ) ) {
+ 				jQuery.merge( ret,
+ 					typeof arr === "string" ?
+ 					[ arr ] : arr
+ 				);
+ 			} else {
+-				core_push.call( ret, arr );
++				push.call( ret, arr );
+ 			}
+ 		}
+ 
+@@ -708,81 +405,58 @@ jQuery.extend({
+ 	},
+ 
+ 	inArray: function( elem, arr, i ) {
+-		var len;
+-
+-		if ( arr ) {
+-			if ( core_indexOf ) {
+-				return core_indexOf.call( arr, elem, i );
+-			}
++		return arr == null ? -1 : indexOf.call( arr, elem, i );
++	},
+ 
+-			len = arr.length;
+-			i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
++	// Support: Android <=4.0 only, PhantomJS 1 only
++	// push.apply(_, arraylike) throws on ancient WebKit
++	merge: function( first, second ) {
++		var len = +second.length,
++			j = 0,
++			i = first.length;
+ 
+-			for ( ; i < len; i++ ) {
+-				// Skip accessing in sparse arrays
+-				if ( i in arr && arr[ i ] === elem ) {
+-					return i;
+-				}
+-			}
++		for ( ; j < len; j++ ) {
++			first[ i++ ] = second[ j ];
+ 		}
+ 
+-		return -1;
++		first.length = i;
++
++		return first;
+ 	},
+ 
+-	merge: function( first, second ) {
+-		var l = second.length,
+-			i = first.length,
+-			j = 0;
+-
+-		if ( typeof l === "number" ) {
+-			for ( ; j < l; j++ ) {
+-				first[ i++ ] = second[ j ];
+-			}
+-		} else {
+-			while ( second[j] !== undefined ) {
+-				first[ i++ ] = second[ j++ ];
+-			}
+-		}
+-
+-		first.length = i;
+-
+-		return first;
+-	},
+-
+-	grep: function( elems, callback, inv ) {
+-		var retVal,
+-			ret = [],
++	grep: function( elems, callback, invert ) {
++		var callbackInverse,
++			matches = [],
+ 			i = 0,
+-			length = elems.length;
+-		inv = !!inv;
++			length = elems.length,
++			callbackExpect = !invert;
+ 
+ 		// Go through the array, only saving the items
+ 		// that pass the validator function
+ 		for ( ; i < length; i++ ) {
+-			retVal = !!callback( elems[ i ], i );
+-			if ( inv !== retVal ) {
+-				ret.push( elems[ i ] );
++			callbackInverse = !callback( elems[ i ], i );
++			if ( callbackInverse !== callbackExpect ) {
++				matches.push( elems[ i ] );
+ 			}
+ 		}
+ 
+-		return ret;
++		return matches;
+ 	},
+ 
+ 	// arg is for internal usage only
+ 	map: function( elems, callback, arg ) {
+-		var value,
++		var length, value,
+ 			i = 0,
+-			length = elems.length,
+-			isArray = isArraylike( elems ),
+ 			ret = [];
+ 
+-		// Go through the array, translating each of the items to their
+-		if ( isArray ) {
++		// Go through the array, translating each of the items to their new values
++		if ( isArrayLike( elems ) ) {
++			length = elems.length;
+ 			for ( ; i < length; i++ ) {
+ 				value = callback( elems[ i ], i, arg );
+ 
+ 				if ( value != null ) {
+-					ret[ ret.length ] = value;
++					ret.push( value );
+ 				}
+ 			}
+ 
+@@ -792,234 +466,73 @@ jQuery.extend({
+ 				value = callback( elems[ i ], i, arg );
+ 
+ 				if ( value != null ) {
+-					ret[ ret.length ] = value;
++					ret.push( value );
+ 				}
+ 			}
+ 		}
+ 
+ 		// Flatten any nested arrays
+-		return core_concat.apply( [], ret );
++		return concat.apply( [], ret );
+ 	},
+ 
+ 	// A global GUID counter for objects
+ 	guid: 1,
+ 
+-	// Bind a function to a context, optionally partially applying any
+-	// arguments.
+-	proxy: function( fn, context ) {
+-		var args, proxy, tmp;
+-
+-		if ( typeof context === "string" ) {
+-			tmp = fn[ context ];
+-			context = fn;
+-			fn = tmp;
+-		}
+-
+-		// Quick check to determine if target is callable, in the spec
+-		// this throws a TypeError, but we will just return undefined.
+-		if ( !jQuery.isFunction( fn ) ) {
+-			return undefined;
+-		}
+-
+-		// Simulated bind
+-		args = core_slice.call( arguments, 2 );
+-		proxy = function() {
+-			return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) );
+-		};
+-
+-		// Set the guid of unique handler to the same of original handler, so it can be removed
+-		proxy.guid = fn.guid = fn.guid || jQuery.guid++;
+-
+-		return proxy;
+-	},
+-
+-	// Multifunctional method to get and set values of a collection
+-	// The value/s can optionally be executed if it's a function
+-	access: function( elems, fn, key, value, chainable, emptyGet, raw ) {
+-		var i = 0,
+-			length = elems.length,
+-			bulk = key == null;
+-
+-		// Sets many values
+-		if ( jQuery.type( key ) === "object" ) {
+-			chainable = true;
+-			for ( i in key ) {
+-				jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
+-			}
+-
+-		// Sets one value
+-		} else if ( value !== undefined ) {
+-			chainable = true;
+-
+-			if ( !jQuery.isFunction( value ) ) {
+-				raw = true;
+-			}
+-
+-			if ( bulk ) {
+-				// Bulk operations run against the entire set
+-				if ( raw ) {
+-					fn.call( elems, value );
+-					fn = null;
+-
+-				// ...except when executing function values
+-				} else {
+-					bulk = fn;
+-					fn = function( elem, key, value ) {
+-						return bulk.call( jQuery( elem ), value );
+-					};
+-				}
+-			}
+-
+-			if ( fn ) {
+-				for ( ; i < length; i++ ) {
+-					fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
+-				}
+-			}
+-		}
+-
+-		return chainable ?
+-			elems :
+-
+-			// Gets
+-			bulk ?
+-				fn.call( elems ) :
+-				length ? fn( elems[0], key ) : emptyGet;
+-	},
+-
+-	now: function() {
+-		return ( new Date() ).getTime();
+-	},
+-
+-	// A method for quickly swapping in/out CSS properties to get correct calculations.
+-	// Note: this method belongs to the css module but it's needed here for the support module.
+-	// If support gets modularized, this method should be moved back to the css module.
+-	swap: function( elem, options, callback, args ) {
+-		var ret, name,
+-			old = {};
+-
+-		// Remember the old values, and insert the new ones
+-		for ( name in options ) {
+-			old[ name ] = elem.style[ name ];
+-			elem.style[ name ] = options[ name ];
+-		}
+-
+-		ret = callback.apply( elem, args || [] );
+-
+-		// Revert the old values
+-		for ( name in options ) {
+-			elem.style[ name ] = old[ name ];
+-		}
+-
+-		return ret;
+-	}
+-});
+-
+-jQuery.ready.promise = function( obj ) {
+-	if ( !readyList ) {
+-
+-		readyList = jQuery.Deferred();
+-
+-		// Catch cases where $(document).ready() is called after the browser event has already occurred.
+-		// we once tried to use readyState "interactive" here, but it caused issues like the one
+-		// discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
+-		if ( document.readyState === "complete" ) {
+-			// Handle it asynchronously to allow scripts the opportunity to delay ready
+-			setTimeout( jQuery.ready );
++	// jQuery.support is not used in Core but other projects attach their
++	// properties to it so it needs to exist.
++	support: support
++} );
+ 
+-		// Standards-based browsers support DOMContentLoaded
+-		} else if ( document.addEventListener ) {
+-			// Use the handy event callback
+-			document.addEventListener( "DOMContentLoaded", completed, false );
+-
+-			// A fallback to window.onload, that will always work
+-			window.addEventListener( "load", completed, false );
+-
+-		// If IE event model is used
+-		} else {
+-			// Ensure firing before onload, maybe late but safe also for iframes
+-			document.attachEvent( "onreadystatechange", completed );
+-
+-			// A fallback to window.onload, that will always work
+-			window.attachEvent( "onload", completed );
+-
+-			// If IE and not a frame
+-			// continually check to see if the document is ready
+-			var top = false;
+-
+-			try {
+-				top = window.frameElement == null && document.documentElement;
+-			} catch(e) {}
+-
+-			if ( top && top.doScroll ) {
+-				(function doScrollCheck() {
+-					if ( !jQuery.isReady ) {
+-
+-						try {
+-							// Use the trick by Diego Perini
+-							// http://javascript.nwbox.com/IEContentLoaded/
+-							top.doScroll("left");
+-						} catch(e) {
+-							return setTimeout( doScrollCheck, 50 );
+-						}
+-
+-						// detach all dom ready events
+-						detach();
+-
+-						// and execute any waiting functions
+-						jQuery.ready();
+-					}
+-				})();
+-			}
+-		}
+-	}
+-	return readyList.promise( obj );
+-};
++if ( typeof Symbol === "function" ) {
++	jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ];
++}
+ 
+ // Populate the class2type map
+-jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
++jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ),
++function( i, name ) {
+ 	class2type[ "[object " + name + "]" ] = name.toLowerCase();
+-});
++} );
+ 
+-function isArraylike( obj ) {
+-	var length = obj.length,
+-		type = jQuery.type( obj );
++function isArrayLike( obj ) {
+ 
+-	if ( jQuery.isWindow( obj ) ) {
+-		return false;
+-	}
++	// Support: real iOS 8.2 only (not reproducible in simulator)
++	// `in` check used to prevent JIT error (gh-2145)
++	// hasOwn isn't used here due to false negatives
++	// regarding Nodelist length in IE
++	var length = !!obj && "length" in obj && obj.length,
++		type = toType( obj );
+ 
+-	if ( obj.nodeType === 1 && length ) {
+-		return true;
++	if ( isFunction( obj ) || isWindow( obj ) ) {
++		return false;
+ 	}
+ 
+-	return type === "array" || type !== "function" &&
+-		( length === 0 ||
+-		typeof length === "number" && length > 0 && ( length - 1 ) in obj );
++	return type === "array" || length === 0 ||
++		typeof length === "number" && length > 0 && ( length - 1 ) in obj;
+ }
+-
+-// All jQuery objects should point back to these
+-rootjQuery = jQuery(document);
++var Sizzle =
+ /*!
+- * Sizzle CSS Selector Engine v1.10.2
+- * http://sizzlejs.com/
++ * Sizzle CSS Selector Engine v2.3.4
++ * https://sizzlejs.com/
+  *
+- * Copyright 2013 jQuery Foundation, Inc. and other contributors
++ * Copyright JS Foundation and other contributors
+  * Released under the MIT license
+- * http://jquery.org/license
++ * https://js.foundation/
+  *
+- * Date: 2013-07-03
++ * Date: 2019-04-08
+  */
+-(function( window, undefined ) {
++(function( window ) {
+ 
+ var i,
+ 	support,
+-	cachedruns,
+ 	Expr,
+ 	getText,
+ 	isXML,
++	tokenize,
+ 	compile,
++	select,
+ 	outermostContext,
+ 	sortInput,
++	hasDuplicate,
+ 
+ 	// Local document vars
+ 	setDocument,
+@@ -1032,26 +545,21 @@ var i,
+ 	contains,
+ 
+ 	// Instance-specific data
+-	expando = "sizzle" + -(new Date()),
++	expando = "sizzle" + 1 * new Date(),
+ 	preferredDoc = window.document,
+ 	dirruns = 0,
+ 	done = 0,
+ 	classCache = createCache(),
+ 	tokenCache = createCache(),
+ 	compilerCache = createCache(),
+-	hasDuplicate = false,
++	nonnativeSelectorCache = createCache(),
+ 	sortOrder = function( a, b ) {
+ 		if ( a === b ) {
+ 			hasDuplicate = true;
+-			return 0;
+ 		}
+ 		return 0;
+ 	},
+ 
+-	// General-purpose constants
+-	strundefined = typeof undefined,
+-	MAX_NEGATIVE = 1 << 31,
+-
+ 	// Instance methods
+ 	hasOwn = ({}).hasOwnProperty,
+ 	arr = [],
+@@ -1059,12 +567,13 @@ var i,
+ 	push_native = arr.push,
+ 	push = arr.push,
+ 	slice = arr.slice,
+-	// Use a stripped-down indexOf if we can't use a native one
+-	indexOf = arr.indexOf || function( elem ) {
++	// Use a stripped-down indexOf as it's faster than native
++	// https://jsperf.com/thor-indexof-vs-for/5
++	indexOf = function( list, elem ) {
+ 		var i = 0,
+-			len = this.length;
++			len = list.length;
+ 		for ( ; i < len; i++ ) {
+-			if ( this[i] === elem ) {
++			if ( list[i] === elem ) {
+ 				return i;
+ 			}
+ 		}
+@@ -1075,44 +584,45 @@ var i,
+ 
+ 	// Regular expressions
+ 
+-	// Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
++	// http://www.w3.org/TR/css3-selectors/#whitespace
+ 	whitespace = "[\\x20\\t\\r\\n\\f]",
+-	// http://www.w3.org/TR/css3-syntax/#characters
+-	characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
+-
+-	// Loosely modeled on CSS identifier characters
+-	// An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors
+-	// Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
+-	identifier = characterEncoding.replace( "w", "w#" ),
+-
+-	// Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors
+-	attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +
+-		"*(?:([*^$|!~]?=)" + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]",
+-
+-	// Prefer arguments quoted,
+-	//   then not containing pseudos/brackets,
+-	//   then attribute selectors/non-parenthetical expressions,
+-	//   then anything else
+-	// These preferences are here to reduce the number of selectors
+-	//   needing tokenize in the PSEUDO preFilter
+-	pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)",
++
++	// http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
++	identifier = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+",
++
++	// Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors
++	attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace +
++		// Operator (capture 2)
++		"*([*^$|!~]?=)" + whitespace +
++		// "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
++		"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace +
++		"*\\]",
++
++	pseudos = ":(" + identifier + ")(?:\\((" +
++		// To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
++		// 1. quoted (capture 3; capture 4 or capture 5)
++		"('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
++		// 2. simple (capture 6)
++		"((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
++		// 3. anything else (capture 2)
++		".*" +
++		")\\)|)",
+ 
+ 	// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
++	rwhitespace = new RegExp( whitespace + "+", "g" ),
+ 	rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
+ 
+ 	rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
+ 	rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ),
+-
+-	rsibling = new RegExp( whitespace + "*[+~]" ),
+-	rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*)" + whitespace + "*\\]", "g" ),
++	rdescend = new RegExp( whitespace + "|>" ),
+ 
+ 	rpseudo = new RegExp( pseudos ),
+ 	ridentifier = new RegExp( "^" + identifier + "$" ),
+ 
+ 	matchExpr = {
+-		"ID": new RegExp( "^#(" + characterEncoding + ")" ),
+-		"CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
+-		"TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
++		"ID": new RegExp( "^#(" + identifier + ")" ),
++		"CLASS": new RegExp( "^\\.(" + identifier + ")" ),
++		"TAG": new RegExp( "^(" + identifier + "|[*])" ),
+ 		"ATTR": new RegExp( "^" + attributes ),
+ 		"PSEUDO": new RegExp( "^" + pseudos ),
+ 		"CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
+@@ -1125,31 +635,67 @@ var i,
+ 			whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
+ 	},
+ 
++	rhtml = /HTML$/i,
++	rinputs = /^(?:input|select|textarea|button)$/i,
++	rheader = /^h\d$/i,
++
+ 	rnative = /^[^{]+\{\s*\[native \w/,
+ 
+ 	// Easily-parseable/retrievable ID or TAG or CLASS selectors
+ 	rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
+ 
+-	rinputs = /^(?:input|select|textarea|button)$/i,
+-	rheader = /^h\d$/i,
++	rsibling = /[+~]/,
+ 
+-	rescape = /'|\\/g,
+-
+-	// CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
++	// CSS escapes
++	// http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
+ 	runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
+ 	funescape = function( _, escaped, escapedWhitespace ) {
+ 		var high = "0x" + escaped - 0x10000;
+ 		// NaN means non-codepoint
+-		// Support: Firefox
++		// Support: Firefox<24
+ 		// Workaround erroneous numeric interpretation of +"0x"
+ 		return high !== high || escapedWhitespace ?
+ 			escaped :
+-			// BMP codepoint
+ 			high < 0 ?
++				// BMP codepoint
+ 				String.fromCharCode( high + 0x10000 ) :
+ 				// Supplemental Plane codepoint (surrogate pair)
+ 				String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
+-	};
++	},
++
++	// CSS string/identifier serialization
++	// https://drafts.csswg.org/cssom/#common-serializing-idioms
++	rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,
++	fcssescape = function( ch, asCodePoint ) {
++		if ( asCodePoint ) {
++
++			// U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER
++			if ( ch === "\0" ) {
++				return "\uFFFD";
++			}
++
++			// Control characters and (dependent upon position) numbers get escaped as code points
++			return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " ";
++		}
++
++		// Other potentially-special ASCII characters get backslash-escaped
++		return "\\" + ch;
++	},
++
++	// Used for iframes
++	// See setDocument()
++	// Removing the function wrapper causes a "Permission Denied"
++	// error in IE
++	unloadHandler = function() {
++		setDocument();
++	},
++
++	inDisabledFieldset = addCombinator(
++		function( elem ) {
++			return elem.disabled === true && elem.nodeName.toLowerCase() === "fieldset";
++		},
++		{ dir: "parentNode", next: "legend" }
++	);
+ 
+ // Optimize for push.apply( _, NodeList )
+ try {
+@@ -1181,104 +727,131 @@ try {
+ }
+ 
+ function Sizzle( selector, context, results, seed ) {
+-	var match, elem, m, nodeType,
+-		// QSA vars
+-		i, groups, old, nid, newContext, newSelector;
++	var m, i, elem, nid, match, groups, newSelector,
++		newContext = context && context.ownerDocument,
+ 
+-	if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
+-		setDocument( context );
+-	}
++		// nodeType defaults to 9, since context defaults to document
++		nodeType = context ? context.nodeType : 9;
+ 
+-	context = context || document;
+ 	results = results || [];
+ 
+-	if ( !selector || typeof selector !== "string" ) {
++	// Return early from calls with invalid selector or context
++	if ( typeof selector !== "string" || !selector ||
++		nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) {
++
+ 		return results;
+ 	}
+ 
+-	if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) {
+-		return [];
+-	}
++	// Try to shortcut find operations (as opposed to filters) in HTML documents
++	if ( !seed ) {
+ 
+-	if ( documentIsHTML && !seed ) {
+-
+-		// Shortcuts
+-		if ( (match = rquickExpr.exec( selector )) ) {
+-			// Speed-up: Sizzle("#ID")
+-			if ( (m = match[1]) ) {
+-				if ( nodeType === 9 ) {
+-					elem = context.getElementById( m );
+-					// Check parentNode to catch when Blackberry 4.6 returns
+-					// nodes that are no longer in the document #6963
+-					if ( elem && elem.parentNode ) {
+-						// Handle the case where IE, Opera, and Webkit return items
+-						// by name instead of ID
+-						if ( elem.id === m ) {
+-							results.push( elem );
++		if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
++			setDocument( context );
++		}
++		context = context || document;
++
++		if ( documentIsHTML ) {
++
++			// If the selector is sufficiently simple, try using a "get*By*" DOM method
++			// (excepting DocumentFragment context, where the methods don't exist)
++			if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) {
++
++				// ID selector
++				if ( (m = match[1]) ) {
++
++					// Document context
++					if ( nodeType === 9 ) {
++						if ( (elem = context.getElementById( m )) ) {
++
++							// Support: IE, Opera, Webkit
++							// TODO: identify versions
++							// getElementById can match elements by name instead of ID
++							if ( elem.id === m ) {
++								results.push( elem );
++								return results;
++							}
++						} else {
+ 							return results;
+ 						}
++
++					// Element context
+ 					} else {
+-						return results;
+-					}
+-				} else {
+-					// Context is not a document
+-					if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
+-						contains( context, elem ) && elem.id === m ) {
+-						results.push( elem );
+-						return results;
++
++						// Support: IE, Opera, Webkit
++						// TODO: identify versions
++						// getElementById can match elements by name instead of ID
++						if ( newContext && (elem = newContext.getElementById( m )) &&
++							contains( context, elem ) &&
++							elem.id === m ) {
++
++							results.push( elem );
++							return results;
++						}
+ 					}
+-				}
+ 
+-			// Speed-up: Sizzle("TAG")
+-			} else if ( match[2] ) {
+-				push.apply( results, context.getElementsByTagName( selector ) );
+-				return results;
++				// Type selector
++				} else if ( match[2] ) {
++					push.apply( results, context.getElementsByTagName( selector ) );
++					return results;
+ 
+-			// Speed-up: Sizzle(".CLASS")
+-			} else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) {
+-				push.apply( results, context.getElementsByClassName( m ) );
+-				return results;
++				// Class selector
++				} else if ( (m = match[3]) && support.getElementsByClassName &&
++					context.getElementsByClassName ) {
++
++					push.apply( results, context.getElementsByClassName( m ) );
++					return results;
++				}
+ 			}
+-		}
+ 
+-		// QSA path
+-		if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
+-			nid = old = expando;
+-			newContext = context;
+-			newSelector = nodeType === 9 && selector;
++			// Take advantage of querySelectorAll
++			if ( support.qsa &&
++				!nonnativeSelectorCache[ selector + " " ] &&
++				(!rbuggyQSA || !rbuggyQSA.test( selector )) &&
+ 
+-			// qSA works strangely on Element-rooted queries
+-			// We can work around this by specifying an extra ID on the root
+-			// and working up from there (Thanks to Andrew Dupont for the technique)
+-			// IE 8 doesn't work on object elements
+-			if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
+-				groups = tokenize( selector );
++				// Support: IE 8 only
++				// Exclude object elements
++				(nodeType !== 1 || context.nodeName.toLowerCase() !== "object") ) {
+ 
+-				if ( (old = context.getAttribute("id")) ) {
+-					nid = old.replace( rescape, "\\$&" );
+-				} else {
+-					context.setAttribute( "id", nid );
+-				}
+-				nid = "[id='" + nid + "'] ";
++				newSelector = selector;
++				newContext = context;
+ 
+-				i = groups.length;
+-				while ( i-- ) {
+-					groups[i] = nid + toSelector( groups[i] );
++				// qSA considers elements outside a scoping root when evaluating child or
++				// descendant combinators, which is not what we want.
++				// In such cases, we work around the behavior by prefixing every selector in the
++				// list with an ID selector referencing the scope context.
++				// Thanks to Andrew Dupont for this technique.
++				if ( nodeType === 1 && rdescend.test( selector ) ) {
++
++					// Capture the context ID, setting it first if necessary
++					if ( (nid = context.getAttribute( "id" )) ) {
++						nid = nid.replace( rcssescape, fcssescape );
++					} else {
++						context.setAttribute( "id", (nid = expando) );
++					}
++
++					// Prefix every selector in the list
++					groups = tokenize( selector );
++					i = groups.length;
++					while ( i-- ) {
++						groups[i] = "#" + nid + " " + toSelector( groups[i] );
++					}
++					newSelector = groups.join( "," );
++
++					// Expand context for sibling selectors
++					newContext = rsibling.test( selector ) && testContext( context.parentNode ) ||
++						context;
+ 				}
+-				newContext = rsibling.test( selector ) && context.parentNode || context;
+-				newSelector = groups.join(",");
+-			}
+ 
+-			if ( newSelector ) {
+ 				try {
+ 					push.apply( results,
+ 						newContext.querySelectorAll( newSelector )
+ 					);
+ 					return results;
+-				} catch(qsaError) {
++				} catch ( qsaError ) {
++					nonnativeSelectorCache( selector, true );
+ 				} finally {
+-					if ( !old ) {
+-						context.removeAttribute("id");
++					if ( nid === expando ) {
++						context.removeAttribute( "id" );
+ 					}
+ 				}
+ 			}
+@@ -1291,7 +864,7 @@ function Sizzle( selector, context, results, seed ) {
+ 
+ /**
+  * Create key-value caches of limited size
+- * @returns {Function(string, Object)} Returns the Object data after storing it on itself with
++ * @returns {function(string, object)} Returns the Object data after storing it on itself with
+  *	property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
+  *	deleting the oldest entry
+  */
+@@ -1300,11 +873,11 @@ function createCache() {
+ 
+ 	function cache( key, value ) {
+ 		// Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
+-		if ( keys.push( key += " " ) > Expr.cacheLength ) {
++		if ( keys.push( key + " " ) > Expr.cacheLength ) {
+ 			// Only keep the most recent entries
+ 			delete cache[ keys.shift() ];
+ 		}
+-		return (cache[ key ] = value);
++		return (cache[ key + " " ] = value);
+ 	}
+ 	return cache;
+ }
+@@ -1320,22 +893,22 @@ function markFunction( fn ) {
+ 
+ /**
+  * Support testing using an element
+- * @param {Function} fn Passed the created div and expects a boolean result
++ * @param {Function} fn Passed the created element and returns a boolean result
+  */
+ function assert( fn ) {
+-	var div = document.createElement("div");
++	var el = document.createElement("fieldset");
+ 
+ 	try {
+-		return !!fn( div );
++		return !!fn( el );
+ 	} catch (e) {
+ 		return false;
+ 	} finally {
+ 		// Remove from its parent by default
+-		if ( div.parentNode ) {
+-			div.parentNode.removeChild( div );
++		if ( el.parentNode ) {
++			el.parentNode.removeChild( el );
+ 		}
+ 		// release memory in IE
+-		div = null;
++		el = null;
+ 	}
+ }
+ 
+@@ -1346,7 +919,7 @@ function assert( fn ) {
+  */
+ function addHandle( attrs, handler ) {
+ 	var arr = attrs.split("|"),
+-		i = attrs.length;
++		i = arr.length;
+ 
+ 	while ( i-- ) {
+ 		Expr.attrHandle[ arr[i] ] = handler;
+@@ -1362,8 +935,7 @@ function addHandle( attrs, handler ) {
+ function siblingCheck( a, b ) {
+ 	var cur = b && a,
+ 		diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
+-			( ~b.sourceIndex || MAX_NEGATIVE ) -
+-			( ~a.sourceIndex || MAX_NEGATIVE );
++			a.sourceIndex - b.sourceIndex;
+ 
+ 	// Use IE sourceIndex if available on both nodes
+ 	if ( diff ) {
+@@ -1405,6 +977,62 @@ function createButtonPseudo( type ) {
+ }
+ 
+ /**
++ * Returns a function to use in pseudos for :enabled/:disabled
++ * @param {Boolean} disabled true for :disabled; false for :enabled
++ */
++function createDisabledPseudo( disabled ) {
++
++	// Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable
++	return function( elem ) {
++
++		// Only certain elements can match :enabled or :disabled
++		// https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled
++		// https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled
++		if ( "form" in elem ) {
++
++			// Check for inherited disabledness on relevant non-disabled elements:
++			// * listed form-associated elements in a disabled fieldset
++			//   https://html.spec.whatwg.org/multipage/forms.html#category-listed
++			//   https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled
++			// * option elements in a disabled optgroup
++			//   https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled
++			// All such elements have a "form" property.
++			if ( elem.parentNode && elem.disabled === false ) {
++
++				// Option elements defer to a parent optgroup if present
++				if ( "label" in elem ) {
++					if ( "label" in elem.parentNode ) {
++						return elem.parentNode.disabled === disabled;
++					} else {
++						return elem.disabled === disabled;
++					}
++				}
++
++				// Support: IE 6 - 11
++				// Use the isDisabled shortcut property to check for disabled fieldset ancestors
++				return elem.isDisabled === disabled ||
++
++					// Where there is no isDisabled, check manually
++					/* jshint -W018 */
++					elem.isDisabled !== !disabled &&
++						inDisabledFieldset( elem ) === disabled;
++			}
++
++			return elem.disabled === disabled;
++
++		// Try to winnow out elements that can't be disabled before trusting the disabled property.
++		// Some victims get caught in our net (label, legend, menu, track), but it shouldn't
++		// even exist on them, let alone have a boolean value.
++		} else if ( "label" in elem ) {
++			return elem.disabled === disabled;
++		}
++
++		// Remaining elements are neither :enabled nor :disabled
++		return false;
++	};
++}
++
++/**
+  * Returns a function to use in pseudos for positionals
+  * @param {Function} fn
+  */
+@@ -1427,131 +1055,170 @@ function createPositionalPseudo( fn ) {
+ }
+ 
+ /**
+- * Detect xml
+- * @param {Element|Object} elem An element or a document
++ * Checks a node for validity as a Sizzle context
++ * @param {Element|Object=} context
++ * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
+  */
+-isXML = Sizzle.isXML = function( elem ) {
+-	// documentElement is verified for cases where it doesn't yet exist
+-	// (such as loading iframes in IE - #4833)
+-	var documentElement = elem && (elem.ownerDocument || elem).documentElement;
+-	return documentElement ? documentElement.nodeName !== "HTML" : false;
+-};
++function testContext( context ) {
++	return context && typeof context.getElementsByTagName !== "undefined" && context;
++}
+ 
+ // Expose support vars for convenience
+ support = Sizzle.support = {};
+ 
+ /**
++ * Detects XML nodes
++ * @param {Element|Object} elem An element or a document
++ * @returns {Boolean} True iff elem is a non-HTML XML node
++ */
++isXML = Sizzle.isXML = function( elem ) {
++	var namespace = elem.namespaceURI,
++		docElem = (elem.ownerDocument || elem).documentElement;
++
++	// Support: IE <=8
++	// Assume HTML when documentElement doesn't yet exist, such as inside loading iframes
++	// https://bugs.jquery.com/ticket/4833
++	return !rhtml.test( namespace || docElem && docElem.nodeName || "HTML" );
++};
++
++/**
+  * Sets document-related variables once based on the current document
+  * @param {Element|Object} [doc] An element or document object to use to set the document
+  * @returns {Object} Returns the current document
+  */
+ setDocument = Sizzle.setDocument = function( node ) {
+-	var doc = node ? node.ownerDocument || node : preferredDoc,
+-		parent = doc.defaultView;
++	var hasCompare, subWindow,
++		doc = node ? node.ownerDocument || node : preferredDoc;
+ 
+-	// If no document and documentElement is available, return
++	// Return early if doc is invalid or already selected
+ 	if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
+ 		return document;
+ 	}
+ 
+-	// Set our document
++	// Update global variables
+ 	document = doc;
+-	docElem = doc.documentElement;
+-
+-	// Support tests
+-	documentIsHTML = !isXML( doc );
+-
+-	// Support: IE>8
+-	// If iframe document is assigned to "document" variable and if iframe has been reloaded,
+-	// IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936
+-	// IE6-8 do not support the defaultView property so parent will be undefined
+-	if ( parent && parent.attachEvent && parent !== parent.top ) {
+-		parent.attachEvent( "onbeforeunload", function() {
+-			setDocument();
+-		});
++	docElem = document.documentElement;
++	documentIsHTML = !isXML( document );
++
++	// Support: IE 9-11, Edge
++	// Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936)
++	if ( preferredDoc !== document &&
++		(subWindow = document.defaultView) && subWindow.top !== subWindow ) {
++
++		// Support: IE 11, Edge
++		if ( subWindow.addEventListener ) {
++			subWindow.addEventListener( "unload", unloadHandler, false );
++
++		// Support: IE 9 - 10 only
++		} else if ( subWindow.attachEvent ) {
++			subWindow.attachEvent( "onunload", unloadHandler );
++		}
+ 	}
+ 
+ 	/* Attributes
+ 	---------------------------------------------------------------------- */
+ 
+ 	// Support: IE<8
+-	// Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans)
+-	support.attributes = assert(function( div ) {
+-		div.className = "i";
+-		return !div.getAttribute("className");
++	// Verify that getAttribute really returns attributes and not properties
++	// (excepting IE8 booleans)
++	support.attributes = assert(function( el ) {
++		el.className = "i";
++		return !el.getAttribute("className");
+ 	});
+ 
+ 	/* getElement(s)By*
+ 	---------------------------------------------------------------------- */
+ 
+ 	// Check if getElementsByTagName("*") returns only elements
+-	support.getElementsByTagName = assert(function( div ) {
+-		div.appendChild( doc.createComment("") );
+-		return !div.getElementsByTagName("*").length;
++	support.getElementsByTagName = assert(function( el ) {
++		el.appendChild( document.createComment("") );
++		return !el.getElementsByTagName("*").length;
+ 	});
+ 
+-	// Check if getElementsByClassName can be trusted
+-	support.getElementsByClassName = assert(function( div ) {
+-		div.innerHTML = "<div class='a'></div><div class='a i'></div>";
+-
+-		// Support: Safari<4
+-		// Catch class over-caching
+-		div.firstChild.className = "i";
+-		// Support: Opera<10
+-		// Catch gEBCN failure to find non-leading classes
+-		return div.getElementsByClassName("i").length === 2;
+-	});
++	// Support: IE<9
++	support.getElementsByClassName = rnative.test( document.getElementsByClassName );
+ 
+ 	// Support: IE<10
+ 	// Check if getElementById returns elements by name
+-	// The broken getElementById methods don't pick up programatically-set names,
++	// The broken getElementById methods don't pick up programmatically-set names,
+ 	// so use a roundabout getElementsByName test
+-	support.getById = assert(function( div ) {
+-		docElem.appendChild( div ).id = expando;
+-		return !doc.getElementsByName || !doc.getElementsByName( expando ).length;
++	support.getById = assert(function( el ) {
++		docElem.appendChild( el ).id = expando;
++		return !document.getElementsByName || !document.getElementsByName( expando ).length;
+ 	});
+ 
+-	// ID find and filter
++	// ID filter and find
+ 	if ( support.getById ) {
+-		Expr.find["ID"] = function( id, context ) {
+-			if ( typeof context.getElementById !== strundefined && documentIsHTML ) {
+-				var m = context.getElementById( id );
+-				// Check parentNode to catch when Blackberry 4.6 returns
+-				// nodes that are no longer in the document #6963
+-				return m && m.parentNode ? [m] : [];
+-			}
+-		};
+ 		Expr.filter["ID"] = function( id ) {
+ 			var attrId = id.replace( runescape, funescape );
+ 			return function( elem ) {
+ 				return elem.getAttribute("id") === attrId;
+ 			};
+ 		};
++		Expr.find["ID"] = function( id, context ) {
++			if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
++				var elem = context.getElementById( id );
++				return elem ? [ elem ] : [];
++			}
++		};
+ 	} else {
+-		// Support: IE6/7
+-		// getElementById is not reliable as a find shortcut
+-		delete Expr.find["ID"];
+-
+ 		Expr.filter["ID"] =  function( id ) {
+ 			var attrId = id.replace( runescape, funescape );
+ 			return function( elem ) {
+-				var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
++				var node = typeof elem.getAttributeNode !== "undefined" &&
++					elem.getAttributeNode("id");
+ 				return node && node.value === attrId;
+ 			};
+ 		};
++
++		// Support: IE 6 - 7 only
++		// getElementById is not reliable as a find shortcut
++		Expr.find["ID"] = function( id, context ) {
++			if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
++				var node, i, elems,
++					elem = context.getElementById( id );
++
++				if ( elem ) {
++
++					// Verify the id attribute
++					node = elem.getAttributeNode("id");
++					if ( node && node.value === id ) {
++						return [ elem ];
++					}
++
++					// Fall back on getElementsByName
++					elems = context.getElementsByName( id );
++					i = 0;
++					while ( (elem = elems[i++]) ) {
++						node = elem.getAttributeNode("id");
++						if ( node && node.value === id ) {
++							return [ elem ];
++						}
++					}
++				}
++
++				return [];
++			}
++		};
+ 	}
+ 
+ 	// Tag
+ 	Expr.find["TAG"] = support.getElementsByTagName ?
+ 		function( tag, context ) {
+-			if ( typeof context.getElementsByTagName !== strundefined ) {
++			if ( typeof context.getElementsByTagName !== "undefined" ) {
+ 				return context.getElementsByTagName( tag );
++
++			// DocumentFragment nodes don't have gEBTN
++			} else if ( support.qsa ) {
++				return context.querySelectorAll( tag );
+ 			}
+ 		} :
++
+ 		function( tag, context ) {
+ 			var elem,
+ 				tmp = [],
+ 				i = 0,
++				// By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too
+ 				results = context.getElementsByTagName( tag );
+ 
+ 			// Filter out possible comments
+@@ -1569,7 +1236,7 @@ setDocument = Sizzle.setDocument = function( node ) {
+ 
+ 	// Class
+ 	Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
+-		if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) {
++		if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) {
+ 			return context.getElementsByClassName( className );
+ 		}
+ 	};
+@@ -1586,74 +1253,105 @@ setDocument = Sizzle.setDocument = function( node ) {
+ 	// We allow this because of a bug in IE8/9 that throws an error
+ 	// whenever `document.activeElement` is accessed on an iframe
+ 	// So, we allow :focus to pass through QSA all the time to avoid the IE error
+-	// See http://bugs.jquery.com/ticket/13378
++	// See https://bugs.jquery.com/ticket/13378
+ 	rbuggyQSA = [];
+ 
+-	if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) {
++	if ( (support.qsa = rnative.test( document.querySelectorAll )) ) {
+ 		// Build QSA regex
+ 		// Regex strategy adopted from Diego Perini
+-		assert(function( div ) {
++		assert(function( el ) {
+ 			// Select is set to empty string on purpose
+ 			// This is to test IE's treatment of not explicitly
+ 			// setting a boolean content attribute,
+ 			// since its presence should be enough
+-			// http://bugs.jquery.com/ticket/12359
+-			div.innerHTML = "<select><option selected=''></option></select>";
++			// https://bugs.jquery.com/ticket/12359
++			docElem.appendChild( el ).innerHTML = "<a id='" + expando + "'></a>" +
++				"<select id='" + expando + "-\r\\' msallowcapture=''>" +
++				"<option selected=''></option></select>";
++
++			// Support: IE8, Opera 11-12.16
++			// Nothing should be selected when empty strings follow ^= or $= or *=
++			// The test attribute must be unknown in Opera but "safe" for WinRT
++			// https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section
++			if ( el.querySelectorAll("[msallowcapture^='']").length ) {
++				rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
++			}
+ 
+ 			// Support: IE8
+ 			// Boolean attributes and "value" are not treated correctly
+-			if ( !div.querySelectorAll("[selected]").length ) {
++			if ( !el.querySelectorAll("[selected]").length ) {
+ 				rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
+ 			}
+ 
++			// Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+
++			if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) {
++				rbuggyQSA.push("~=");
++			}
++
+ 			// Webkit/Opera - :checked should return selected option elements
+ 			// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
+ 			// IE8 throws error here and will not see later tests
+-			if ( !div.querySelectorAll(":checked").length ) {
++			if ( !el.querySelectorAll(":checked").length ) {
+ 				rbuggyQSA.push(":checked");
+ 			}
++
++			// Support: Safari 8+, iOS 8+
++			// https://bugs.webkit.org/show_bug.cgi?id=136851
++			// In-page `selector#id sibling-combinator selector` fails
++			if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) {
++				rbuggyQSA.push(".#.+[+~]");
++			}
+ 		});
+ 
+-		assert(function( div ) {
++		assert(function( el ) {
++			el.innerHTML = "<a href='' disabled='disabled'></a>" +
++				"<select disabled='disabled'><option/></select>";
+ 
+-			// Support: Opera 10-12/IE8
+-			// ^= $= *= and empty values
+-			// Should not select anything
+ 			// Support: Windows 8 Native Apps
+-			// The type attribute is restricted during .innerHTML assignment
+-			var input = doc.createElement("input");
++			// The type and name attributes are restricted during .innerHTML assignment
++			var input = document.createElement("input");
+ 			input.setAttribute( "type", "hidden" );
+-			div.appendChild( input ).setAttribute( "t", "" );
++			el.appendChild( input ).setAttribute( "name", "D" );
+ 
+-			if ( div.querySelectorAll("[t^='']").length ) {
+-				rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
++			// Support: IE8
++			// Enforce case-sensitivity of name attribute
++			if ( el.querySelectorAll("[name=d]").length ) {
++				rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" );
+ 			}
+ 
+ 			// FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
+ 			// IE8 throws error here and will not see later tests
+-			if ( !div.querySelectorAll(":enabled").length ) {
++			if ( el.querySelectorAll(":enabled").length !== 2 ) {
++				rbuggyQSA.push( ":enabled", ":disabled" );
++			}
++
++			// Support: IE9-11+
++			// IE's :disabled selector does not pick up the children of disabled fieldsets
++			docElem.appendChild( el ).disabled = true;
++			if ( el.querySelectorAll(":disabled").length !== 2 ) {
+ 				rbuggyQSA.push( ":enabled", ":disabled" );
+ 			}
+ 
+ 			// Opera 10-11 does not throw on post-comma invalid pseudos
+-			div.querySelectorAll("*,:x");
++			el.querySelectorAll("*,:x");
+ 			rbuggyQSA.push(",.*:");
+ 		});
+ 	}
+ 
+-	if ( (support.matchesSelector = rnative.test( (matches = docElem.webkitMatchesSelector ||
++	if ( (support.matchesSelector = rnative.test( (matches = docElem.matches ||
++		docElem.webkitMatchesSelector ||
+ 		docElem.mozMatchesSelector ||
+ 		docElem.oMatchesSelector ||
+ 		docElem.msMatchesSelector) )) ) {
+ 
+-		assert(function( div ) {
++		assert(function( el ) {
+ 			// Check to see if it's possible to do matchesSelector
+ 			// on a disconnected node (IE 9)
+-			support.disconnectedMatch = matches.call( div, "div" );
++			support.disconnectedMatch = matches.call( el, "*" );
+ 
+ 			// This should fail with an exception
+ 			// Gecko does not error, returns false instead
+-			matches.call( div, "[s!='']:x" );
++			matches.call( el, "[s!='']:x" );
+ 			rbuggyMatches.push( "!=", pseudos );
+ 		});
+ 	}
+@@ -1663,11 +1361,12 @@ setDocument = Sizzle.setDocument = function( node ) {
+ 
+ 	/* Contains
+ 	---------------------------------------------------------------------- */
++	hasCompare = rnative.test( docElem.compareDocumentPosition );
+ 
+ 	// Element contains another
+-	// Purposefully does not implement inclusive descendent
++	// Purposefully self-exclusive
+ 	// As in, an element does not contain itself
+-	contains = rnative.test( docElem.contains ) || docElem.compareDocumentPosition ?
++	contains = hasCompare || rnative.test( docElem.contains ) ?
+ 		function( a, b ) {
+ 			var adown = a.nodeType === 9 ? a.documentElement : a,
+ 				bup = b && b.parentNode;
+@@ -1692,7 +1391,7 @@ setDocument = Sizzle.setDocument = function( node ) {
+ 	---------------------------------------------------------------------- */
+ 
+ 	// Document order sorting
+-	sortOrder = docElem.compareDocumentPosition ?
++	sortOrder = hasCompare ?
+ 	function( a, b ) {
+ 
+ 		// Flag for duplicate removal
+@@ -1701,34 +1400,46 @@ setDocument = Sizzle.setDocument = function( node ) {
+ 			return 0;
+ 		}
+ 
+-		var compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b );
+-
++		// Sort on method existence if only one input has compareDocumentPosition
++		var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
+ 		if ( compare ) {
+-			// Disconnected nodes
+-			if ( compare & 1 ||
+-				(!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {
++			return compare;
++		}
+ 
+-				// Choose the first element that is related to our preferred document
+-				if ( a === doc || contains(preferredDoc, a) ) {
+-					return -1;
+-				}
+-				if ( b === doc || contains(preferredDoc, b) ) {
+-					return 1;
+-				}
++		// Calculate position if both inputs belong to the same document
++		compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ?
++			a.compareDocumentPosition( b ) :
+ 
+-				// Maintain original order
+-				return sortInput ?
+-					( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
+-					0;
++			// Otherwise we know they are disconnected
++			1;
++
++		// Disconnected nodes
++		if ( compare & 1 ||
++			(!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {
++
++			// Choose the first element that is related to our preferred document
++			if ( a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) {
++				return -1;
++			}
++			if ( b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) {
++				return 1;
+ 			}
+ 
+-			return compare & 4 ? -1 : 1;
++			// Maintain original order
++			return sortInput ?
++				( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
++				0;
+ 		}
+ 
+-		// Not directly comparable, sort on existence of method
+-		return a.compareDocumentPosition ? -1 : 1;
++		return compare & 4 ? -1 : 1;
+ 	} :
+ 	function( a, b ) {
++		// Exit early if the nodes are identical
++		if ( a === b ) {
++			hasDuplicate = true;
++			return 0;
++		}
++
+ 		var cur,
+ 			i = 0,
+ 			aup = a.parentNode,
+@@ -1736,19 +1447,14 @@ setDocument = Sizzle.setDocument = function( node ) {
+ 			ap = [ a ],
+ 			bp = [ b ];
+ 
+-		// Exit early if the nodes are identical
+-		if ( a === b ) {
+-			hasDuplicate = true;
+-			return 0;
+-
+ 		// Parentless nodes are either documents or disconnected
+-		} else if ( !aup || !bup ) {
+-			return a === doc ? -1 :
+-				b === doc ? 1 :
++		if ( !aup || !bup ) {
++			return a === document ? -1 :
++				b === document ? 1 :
+ 				aup ? -1 :
+ 				bup ? 1 :
+ 				sortInput ?
+-				( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
++				( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
+ 				0;
+ 
+ 		// If the nodes are siblings, we can do a quick check
+@@ -1781,7 +1487,7 @@ setDocument = Sizzle.setDocument = function( node ) {
+ 			0;
+ 	};
+ 
+-	return doc;
++	return document;
+ };
+ 
+ Sizzle.matches = function( expr, elements ) {
+@@ -1794,10 +1500,8 @@ Sizzle.matchesSelector = function( elem, expr ) {
+ 		setDocument( elem );
+ 	}
+ 
+-	// Make sure that attribute selectors are quoted
+-	expr = expr.replace( rattributeQuotes, "='$1']" );
+-
+ 	if ( support.matchesSelector && documentIsHTML &&
++		!nonnativeSelectorCache[ expr + " " ] &&
+ 		( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
+ 		( !rbuggyQSA     || !rbuggyQSA.test( expr ) ) ) {
+ 
+@@ -1811,10 +1515,12 @@ Sizzle.matchesSelector = function( elem, expr ) {
+ 					elem.document && elem.document.nodeType !== 11 ) {
+ 				return ret;
+ 			}
+-		} catch(e) {}
++		} catch (e) {
++			nonnativeSelectorCache( expr, true );
++		}
+ 	}
+ 
+-	return Sizzle( expr, document, null, [elem] ).length > 0;
++	return Sizzle( expr, document, null, [ elem ] ).length > 0;
+ };
+ 
+ Sizzle.contains = function( context, elem ) {
+@@ -1837,13 +1543,17 @@ Sizzle.attr = function( elem, name ) {
+ 			fn( elem, name, !documentIsHTML ) :
+ 			undefined;
+ 
+-	return val === undefined ?
++	return val !== undefined ?
++		val :
+ 		support.attributes || !documentIsHTML ?
+ 			elem.getAttribute( name ) :
+ 			(val = elem.getAttributeNode(name)) && val.specified ?
+ 				val.value :
+-				null :
+-		val;
++				null;
++};
++
++Sizzle.escape = function( sel ) {
++	return (sel + "").replace( rcssescape, fcssescape );
+ };
+ 
+ Sizzle.error = function( msg ) {
+@@ -1876,6 +1586,10 @@ Sizzle.uniqueSort = function( results ) {
+ 		}
+ 	}
+ 
++	// Clear input after sorting to release objects
++	// See https://github.com/jquery/sizzle/pull/225
++	sortInput = null;
++
+ 	return results;
+ };
+ 
+@@ -1891,13 +1605,13 @@ getText = Sizzle.getText = function( elem ) {
+ 
+ 	if ( !nodeType ) {
+ 		// If no nodeType, this is expected to be an array
+-		for ( ; (node = elem[i]); i++ ) {
++		while ( (node = elem[i++]) ) {
+ 			// Do not traverse comment nodes
+ 			ret += getText( node );
+ 		}
+ 	} else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
+ 		// Use textContent for elements
+-		// innerText usage removed for consistency of new lines (see #11153)
++		// innerText usage removed for consistency of new lines (jQuery #11153)
+ 		if ( typeof elem.textContent === "string" ) {
+ 			return elem.textContent;
+ 		} else {
+@@ -1939,7 +1653,7 @@ Expr = Sizzle.selectors = {
+ 			match[1] = match[1].replace( runescape, funescape );
+ 
+ 			// Move the given value to match[3] whether quoted or unquoted
+-			match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape );
++			match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape );
+ 
+ 			if ( match[2] === "~=" ) {
+ 				match[3] = " " + match[3] + " ";
+@@ -1982,15 +1696,15 @@ Expr = Sizzle.selectors = {
+ 
+ 		"PSEUDO": function( match ) {
+ 			var excess,
+-				unquoted = !match[5] && match[2];
++				unquoted = !match[6] && match[2];
+ 
+ 			if ( matchExpr["CHILD"].test( match[0] ) ) {
+ 				return null;
+ 			}
+ 
+ 			// Accept quoted arguments as-is
+-			if ( match[3] && match[4] !== undefined ) {
+-				match[2] = match[4];
++			if ( match[3] ) {
++				match[2] = match[4] || match[5] || "";
+ 
+ 			// Strip excess characters from unquoted arguments
+ 			} else if ( unquoted && rpseudo.test( unquoted ) &&
+@@ -2026,7 +1740,7 @@ Expr = Sizzle.selectors = {
+ 			return pattern ||
+ 				(pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
+ 				classCache( className, function( elem ) {
+-					return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" );
++					return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" );
+ 				});
+ 		},
+ 
+@@ -2048,7 +1762,7 @@ Expr = Sizzle.selectors = {
+ 					operator === "^=" ? check && result.indexOf( check ) === 0 :
+ 					operator === "*=" ? check && result.indexOf( check ) > -1 :
+ 					operator === "$=" ? check && result.slice( -check.length ) === check :
+-					operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
++					operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 :
+ 					operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
+ 					false;
+ 			};
+@@ -2067,11 +1781,12 @@ Expr = Sizzle.selectors = {
+ 				} :
+ 
+ 				function( elem, context, xml ) {
+-					var cache, outerCache, node, diff, nodeIndex, start,
++					var cache, uniqueCache, outerCache, node, nodeIndex, start,
+ 						dir = simple !== forward ? "nextSibling" : "previousSibling",
+ 						parent = elem.parentNode,
+ 						name = ofType && elem.nodeName.toLowerCase(),
+-						useCache = !xml && !ofType;
++						useCache = !xml && !ofType,
++						diff = false;
+ 
+ 					if ( parent ) {
+ 
+@@ -2080,7 +1795,10 @@ Expr = Sizzle.selectors = {
+ 							while ( dir ) {
+ 								node = elem;
+ 								while ( (node = node[ dir ]) ) {
+-									if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) {
++									if ( ofType ?
++										node.nodeName.toLowerCase() === name :
++										node.nodeType === 1 ) {
++
+ 										return false;
+ 									}
+ 								}
+@@ -2094,11 +1812,21 @@ Expr = Sizzle.selectors = {
+ 
+ 						// non-xml :nth-child(...) stores cache data on `parent`
+ 						if ( forward && useCache ) {
++
+ 							// Seek `elem` from a previously-cached index
+-							outerCache = parent[ expando ] || (parent[ expando ] = {});
+-							cache = outerCache[ type ] || [];
+-							nodeIndex = cache[0] === dirruns && cache[1];
+-							diff = cache[0] === dirruns && cache[2];
++
++							// ...in a gzip-friendly way
++							node = parent;
++							outerCache = node[ expando ] || (node[ expando ] = {});
++
++							// Support: IE <9 only
++							// Defend against cloned attroperties (jQuery gh-1709)
++							uniqueCache = outerCache[ node.uniqueID ] ||
++								(outerCache[ node.uniqueID ] = {});
++
++							cache = uniqueCache[ type ] || [];
++							nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
++							diff = nodeIndex && cache[ 2 ];
+ 							node = nodeIndex && parent.childNodes[ nodeIndex ];
+ 
+ 							while ( (node = ++nodeIndex && node && node[ dir ] ||
+@@ -2108,29 +1836,55 @@ Expr = Sizzle.selectors = {
+ 
+ 								// When found, cache indexes on `parent` and break
+ 								if ( node.nodeType === 1 && ++diff && node === elem ) {
+-									outerCache[ type ] = [ dirruns, nodeIndex, diff ];
++									uniqueCache[ type ] = [ dirruns, nodeIndex, diff ];
+ 									break;
+ 								}
+ 							}
+ 
+-						// Use previously-cached element index if available
+-						} else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) {
+-							diff = cache[1];
+-
+-						// xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...)
+ 						} else {
+-							// Use the same loop as above to seek `elem` from the start
+-							while ( (node = ++nodeIndex && node && node[ dir ] ||
+-								(diff = nodeIndex = 0) || start.pop()) ) {
++							// Use previously-cached element index if available
++							if ( useCache ) {
++								// ...in a gzip-friendly way
++								node = elem;
++								outerCache = node[ expando ] || (node[ expando ] = {});
+ 
+-								if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) {
+-									// Cache the index of each encountered element
+-									if ( useCache ) {
+-										(node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ];
+-									}
++								// Support: IE <9 only
++								// Defend against cloned attroperties (jQuery gh-1709)
++								uniqueCache = outerCache[ node.uniqueID ] ||
++									(outerCache[ node.uniqueID ] = {});
++
++								cache = uniqueCache[ type ] || [];
++								nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
++								diff = nodeIndex;
++							}
+ 
+-									if ( node === elem ) {
+-										break;
++							// xml :nth-child(...)
++							// or :nth-last-child(...) or :nth(-last)?-of-type(...)
++							if ( diff === false ) {
++								// Use the same loop as above to seek `elem` from the start
++								while ( (node = ++nodeIndex && node && node[ dir ] ||
++									(diff = nodeIndex = 0) || start.pop()) ) {
++
++									if ( ( ofType ?
++										node.nodeName.toLowerCase() === name :
++										node.nodeType === 1 ) &&
++										++diff ) {
++
++										// Cache the index of each encountered element
++										if ( useCache ) {
++											outerCache = node[ expando ] || (node[ expando ] = {});
++
++											// Support: IE <9 only
++											// Defend against cloned attroperties (jQuery gh-1709)
++											uniqueCache = outerCache[ node.uniqueID ] ||
++												(outerCache[ node.uniqueID ] = {});
++
++											uniqueCache[ type ] = [ dirruns, diff ];
++										}
++
++										if ( node === elem ) {
++											break;
++										}
+ 									}
+ 								}
+ 							}
+@@ -2168,7 +1922,7 @@ Expr = Sizzle.selectors = {
+ 							matched = fn( seed, argument ),
+ 							i = matched.length;
+ 						while ( i-- ) {
+-							idx = indexOf.call( seed, matched[i] );
++							idx = indexOf( seed, matched[i] );
+ 							seed[ idx ] = !( matches[ idx ] = matched[i] );
+ 						}
+ 					}) :
+@@ -2207,6 +1961,8 @@ Expr = Sizzle.selectors = {
+ 				function( elem, context, xml ) {
+ 					input[0] = elem;
+ 					matcher( input, null, xml, results );
++					// Don't keep the element (issue #299)
++					input[0] = null;
+ 					return !results.pop();
+ 				};
+ 		}),
+@@ -2218,8 +1974,9 @@ Expr = Sizzle.selectors = {
+ 		}),
+ 
+ 		"contains": markFunction(function( text ) {
++			text = text.replace( runescape, funescape );
+ 			return function( elem ) {
+-				return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
++				return ( elem.textContent || getText( elem ) ).indexOf( text ) > -1;
+ 			};
+ 		}),
+ 
+@@ -2266,13 +2023,8 @@ Expr = Sizzle.selectors = {
+ 		},
+ 
+ 		// Boolean properties
+-		"enabled": function( elem ) {
+-			return elem.disabled === false;
+-		},
+-
+-		"disabled": function( elem ) {
+-			return elem.disabled === true;
+-		},
++		"enabled": createDisabledPseudo( false ),
++		"disabled": createDisabledPseudo( true ),
+ 
+ 		"checked": function( elem ) {
+ 			// In CSS3, :checked should return both checked and selected elements
+@@ -2294,12 +2046,11 @@ Expr = Sizzle.selectors = {
+ 		// Contents
+ 		"empty": function( elem ) {
+ 			// http://www.w3.org/TR/selectors/#empty-pseudo
+-			// :empty is only affected by element nodes and content nodes(including text(3), cdata(4)),
+-			//   not comment, processing instructions, or others
+-			// Thanks to Diego Perini for the nodeName shortcut
+-			//   Greater than "@" means alpha characters (specifically not starting with "#" or "?")
++			// :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
++			//   but not by others (comment: 8; processing instruction: 7; etc.)
++			// nodeType < 6 works because attributes (2) do not appear as children
+ 			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
+-				if ( elem.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4 ) {
++				if ( elem.nodeType < 6 ) {
+ 					return false;
+ 				}
+ 			}
+@@ -2326,11 +2077,12 @@ Expr = Sizzle.selectors = {
+ 
+ 		"text": function( elem ) {
+ 			var attr;
+-			// IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
+-			// use getAttribute instead to test this case
+ 			return elem.nodeName.toLowerCase() === "input" &&
+ 				elem.type === "text" &&
+-				( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === elem.type );
++
++				// Support: IE<8
++				// New HTML5 attribute values (e.g., "search") appear with elem.type === "text"
++				( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" );
+ 		},
+ 
+ 		// Position-in-collection
+@@ -2363,7 +2115,11 @@ Expr = Sizzle.selectors = {
+ 		}),
+ 
+ 		"lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
+-			var i = argument < 0 ? argument + length : argument;
++			var i = argument < 0 ?
++				argument + length :
++				argument > length ?
++					length :
++					argument;
+ 			for ( ; --i >= 0; ) {
+ 				matchIndexes.push( i );
+ 			}
+@@ -2395,7 +2151,7 @@ function setFilters() {}
+ setFilters.prototype = Expr.filters = Expr.pseudos;
+ Expr.setFilters = new setFilters();
+ 
+-function tokenize( selector, parseOnly ) {
++tokenize = Sizzle.tokenize = function( selector, parseOnly ) {
+ 	var matched, match, tokens, type,
+ 		soFar, groups, preFilters,
+ 		cached = tokenCache[ selector + " " ];
+@@ -2416,7 +2172,7 @@ function tokenize( selector, parseOnly ) {
+ 				// Don't consume trailing commas as valid
+ 				soFar = soFar.slice( match[0].length ) || soFar;
+ 			}
+-			groups.push( tokens = [] );
++			groups.push( (tokens = []) );
+ 		}
+ 
+ 		matched = false;
+@@ -2460,7 +2216,7 @@ function tokenize( selector, parseOnly ) {
+ 			Sizzle.error( selector ) :
+ 			// Cache the tokens
+ 			tokenCache( selector, groups ).slice( 0 );
+-}
++};
+ 
+ function toSelector( tokens ) {
+ 	var i = 0,
+@@ -2474,7 +2230,9 @@ function toSelector( tokens ) {
+ 
+ function addCombinator( matcher, combinator, base ) {
+ 	var dir = combinator.dir,
+-		checkNonElements = base && dir === "parentNode",
++		skip = combinator.next,
++		key = skip || dir,
++		checkNonElements = base && key === "parentNode",
+ 		doneName = done++;
+ 
+ 	return combinator.first ?
+@@ -2485,14 +2243,15 @@ function addCombinator( matcher, combinator, base ) {
+ 					return matcher( elem, context, xml );
+ 				}
+ 			}
++			return false;
+ 		} :
+ 
+ 		// Check against all ancestor/preceding elements
+ 		function( elem, context, xml ) {
+-			var data, cache, outerCache,
+-				dirkey = dirruns + " " + doneName;
++			var oldCache, uniqueCache, outerCache,
++				newCache = [ dirruns, doneName ];
+ 
+-			// We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
++			// We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching
+ 			if ( xml ) {
+ 				while ( (elem = elem[ dir ]) ) {
+ 					if ( elem.nodeType === 1 || checkNonElements ) {
+@@ -2505,20 +2264,31 @@ function addCombinator( matcher, combinator, base ) {
+ 				while ( (elem = elem[ dir ]) ) {
+ 					if ( elem.nodeType === 1 || checkNonElements ) {
+ 						outerCache = elem[ expando ] || (elem[ expando ] = {});
+-						if ( (cache = outerCache[ dir ]) && cache[0] === dirkey ) {
+-							if ( (data = cache[1]) === true || data === cachedruns ) {
+-								return data === true;
+-							}
++
++						// Support: IE <9 only
++						// Defend against cloned attroperties (jQuery gh-1709)
++						uniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {});
++
++						if ( skip && skip === elem.nodeName.toLowerCase() ) {
++							elem = elem[ dir ] || elem;
++						} else if ( (oldCache = uniqueCache[ key ]) &&
++							oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {
++
++							// Assign to newCache so results back-propagate to previous elements
++							return (newCache[ 2 ] = oldCache[ 2 ]);
+ 						} else {
+-							cache = outerCache[ dir ] = [ dirkey ];
+-							cache[1] = matcher( elem, context, xml ) || cachedruns;
+-							if ( cache[1] === true ) {
++							// Reuse newcache so results back-propagate to previous elements
++							uniqueCache[ key ] = newCache;
++
++							// A match means we're done; a fail means we have to keep checking
++							if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) {
+ 								return true;
+ 							}
+ 						}
+ 					}
+ 				}
+ 			}
++			return false;
+ 		};
+ }
+ 
+@@ -2536,6 +2306,15 @@ function elementMatcher( matchers ) {
+ 		matchers[0];
+ }
+ 
++function multipleContexts( selector, contexts, results ) {
++	var i = 0,
++		len = contexts.length;
++	for ( ; i < len; i++ ) {
++		Sizzle( selector, contexts[i], results );
++	}
++	return results;
++}
++
+ function condense( unmatched, map, filter, context, xml ) {
+ 	var elem,
+ 		newUnmatched = [],
+@@ -2627,7 +2406,7 @@ function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postS
+ 				i = matcherOut.length;
+ 				while ( i-- ) {
+ 					if ( (elem = matcherOut[i]) &&
+-						(temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
++						(temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) {
+ 
+ 						seed[temp] = !(results[temp] = elem);
+ 					}
+@@ -2662,13 +2441,16 @@ function matcherFromTokens( tokens ) {
+ 			return elem === checkContext;
+ 		}, implicitRelative, true ),
+ 		matchAnyContext = addCombinator( function( elem ) {
+-			return indexOf.call( checkContext, elem ) > -1;
++			return indexOf( checkContext, elem ) > -1;
+ 		}, implicitRelative, true ),
+ 		matchers = [ function( elem, context, xml ) {
+-			return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
++			var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
+ 				(checkContext = context).nodeType ?
+ 					matchContext( elem, context, xml ) :
+ 					matchAnyContext( elem, context, xml ) );
++			// Avoid hanging onto element (issue #299)
++			checkContext = null;
++			return ret;
+ 		} ];
+ 
+ 	for ( ; i < len; i++ ) {
+@@ -2706,42 +2488,43 @@ function matcherFromTokens( tokens ) {
+ }
+ 
+ function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
+-	// A counter to specify which element is currently being matched
+-	var matcherCachedRuns = 0,
+-		bySet = setMatchers.length > 0,
++	var bySet = setMatchers.length > 0,
+ 		byElement = elementMatchers.length > 0,
+-		superMatcher = function( seed, context, xml, results, expandContext ) {
++		superMatcher = function( seed, context, xml, results, outermost ) {
+ 			var elem, j, matcher,
+-				setMatched = [],
+ 				matchedCount = 0,
+ 				i = "0",
+ 				unmatched = seed && [],
+-				outermost = expandContext != null,
++				setMatched = [],
+ 				contextBackup = outermostContext,
+-				// We must always have either seed elements or context
+-				elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ),
++				// We must always have either seed elements or outermost context
++				elems = seed || byElement && Expr.find["TAG"]( "*", outermost ),
+ 				// Use integer dirruns iff this is the outermost matcher
+-				dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1);
++				dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1),
++				len = elems.length;
+ 
+ 			if ( outermost ) {
+-				outermostContext = context !== document && context;
+-				cachedruns = matcherCachedRuns;
++				outermostContext = context === document || context || outermost;
+ 			}
+ 
+ 			// Add elements passing elementMatchers directly to results
+-			// Keep `i` a string if there are no elements so `matchedCount` will be "00" below
+-			for ( ; (elem = elems[i]) != null; i++ ) {
++			// Support: IE<9, Safari
++			// Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id
++			for ( ; i !== len && (elem = elems[i]) != null; i++ ) {
+ 				if ( byElement && elem ) {
+ 					j = 0;
++					if ( !context && elem.ownerDocument !== document ) {
++						setDocument( elem );
++						xml = !documentIsHTML;
++					}
+ 					while ( (matcher = elementMatchers[j++]) ) {
+-						if ( matcher( elem, context, xml ) ) {
++						if ( matcher( elem, context || document, xml) ) {
+ 							results.push( elem );
+ 							break;
+ 						}
+ 					}
+ 					if ( outermost ) {
+ 						dirruns = dirrunsUnique;
+-						cachedruns = ++matcherCachedRuns;
+ 					}
+ 				}
+ 
+@@ -2759,8 +2542,17 @@ function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
+ 				}
+ 			}
+ 
+-			// Apply set filters to unmatched elements
++			// `i` is now the count of elements visited above, and adding it to `matchedCount`
++			// makes the latter nonnegative.
+ 			matchedCount += i;
++
++			// Apply set filters to unmatched elements
++			// NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount`
++			// equals `i`), unless we didn't visit _any_ elements in the above loop because we have
++			// no element matchers and no seed.
++			// Incrementing an initially-string "0" `i` allows `i` to remain a string only in that
++			// case, which will result in a "00" `matchedCount` that differs from `i` but is also
++			// numerically zero.
+ 			if ( bySet && i !== matchedCount ) {
+ 				j = 0;
+ 				while ( (matcher = setMatchers[j++]) ) {
+@@ -2806,7 +2598,7 @@ function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
+ 		superMatcher;
+ }
+ 
+-compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) {
++compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {
+ 	var i,
+ 		setMatchers = [],
+ 		elementMatchers = [],
+@@ -2814,12 +2606,12 @@ compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) {
+ 
+ 	if ( !cached ) {
+ 		// Generate a function of recursive functions that can be used to check each element
+-		if ( !group ) {
+-			group = tokenize( selector );
++		if ( !match ) {
++			match = tokenize( selector );
+ 		}
+-		i = group.length;
++		i = match.length;
+ 		while ( i-- ) {
+-			cached = matcherFromTokens( group[i] );
++			cached = matcherFromTokens( match[i] );
+ 			if ( cached[ expando ] ) {
+ 				setMatchers.push( cached );
+ 			} else {
+@@ -2829,108 +2621,117 @@ compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) {
+ 
+ 		// Cache the compiled function
+ 		cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
++
++		// Save selector and tokenization
++		cached.selector = selector;
+ 	}
+ 	return cached;
+ };
+ 
+-function multipleContexts( selector, contexts, results ) {
+-	var i = 0,
+-		len = contexts.length;
+-	for ( ; i < len; i++ ) {
+-		Sizzle( selector, contexts[i], results );
+-	}
+-	return results;
+-}
+-
+-function select( selector, context, results, seed ) {
++/**
++ * A low-level selection function that works with Sizzle's compiled
++ *  selector functions
++ * @param {String|Function} selector A selector or a pre-compiled
++ *  selector function built with Sizzle.compile
++ * @param {Element} context
++ * @param {Array} [results]
++ * @param {Array} [seed] A set of elements to match against
++ */
++select = Sizzle.select = function( selector, context, results, seed ) {
+ 	var i, tokens, token, type, find,
+-		match = tokenize( selector );
++		compiled = typeof selector === "function" && selector,
++		match = !seed && tokenize( (selector = compiled.selector || selector) );
+ 
+-	if ( !seed ) {
+-		// Try to minimize operations if there is only one group
+-		if ( match.length === 1 ) {
++	results = results || [];
+ 
+-			// Take a shortcut and set the context if the root selector is an ID
+-			tokens = match[0] = match[0].slice( 0 );
+-			if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
+-					support.getById && context.nodeType === 9 && documentIsHTML &&
+-					Expr.relative[ tokens[1].type ] ) {
++	// Try to minimize operations if there is only one selector in the list and no seed
++	// (the latter of which guarantees us context)
++	if ( match.length === 1 ) {
+ 
+-				context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
+-				if ( !context ) {
+-					return results;
+-				}
+-				selector = selector.slice( tokens.shift().value.length );
++		// Reduce context if the leading compound selector is an ID
++		tokens = match[0] = match[0].slice( 0 );
++		if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
++				context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[1].type ] ) {
++
++			context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
++			if ( !context ) {
++				return results;
++
++			// Precompiled matchers will still verify ancestry, so step up a level
++			} else if ( compiled ) {
++				context = context.parentNode;
+ 			}
+ 
+-			// Fetch a seed set for right-to-left matching
+-			i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
+-			while ( i-- ) {
+-				token = tokens[i];
++			selector = selector.slice( tokens.shift().value.length );
++		}
+ 
+-				// Abort if we hit a combinator
+-				if ( Expr.relative[ (type = token.type) ] ) {
+-					break;
+-				}
+-				if ( (find = Expr.find[ type ]) ) {
+-					// Search, expanding context for leading sibling combinators
+-					if ( (seed = find(
+-						token.matches[0].replace( runescape, funescape ),
+-						rsibling.test( tokens[0].type ) && context.parentNode || context
+-					)) ) {
+-
+-						// If seed is empty or no tokens remain, we can return early
+-						tokens.splice( i, 1 );
+-						selector = seed.length && toSelector( tokens );
+-						if ( !selector ) {
+-							push.apply( results, seed );
+-							return results;
+-						}
++		// Fetch a seed set for right-to-left matching
++		i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
++		while ( i-- ) {
++			token = tokens[i];
+ 
+-						break;
++			// Abort if we hit a combinator
++			if ( Expr.relative[ (type = token.type) ] ) {
++				break;
++			}
++			if ( (find = Expr.find[ type ]) ) {
++				// Search, expanding context for leading sibling combinators
++				if ( (seed = find(
++					token.matches[0].replace( runescape, funescape ),
++					rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context
++				)) ) {
++
++					// If seed is empty or no tokens remain, we can return early
++					tokens.splice( i, 1 );
++					selector = seed.length && toSelector( tokens );
++					if ( !selector ) {
++						push.apply( results, seed );
++						return results;
+ 					}
++
++					break;
+ 				}
+ 			}
+ 		}
+ 	}
+ 
+-	// Compile and execute a filtering function
++	// Compile and execute a filtering function if one is not provided
+ 	// Provide `match` to avoid retokenization if we modified the selector above
+-	compile( selector, match )(
++	( compiled || compile( selector, match ) )(
+ 		seed,
+ 		context,
+ 		!documentIsHTML,
+ 		results,
+-		rsibling.test( selector )
++		!context || rsibling.test( selector ) && testContext( context.parentNode ) || context
+ 	);
+ 	return results;
+-}
++};
+ 
+ // One-time assignments
+ 
+ // Sort stability
+ support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;
+ 
+-// Support: Chrome<14
++// Support: Chrome 14-35+
+ // Always assume duplicates if they aren't passed to the comparison function
+-support.detectDuplicates = hasDuplicate;
++support.detectDuplicates = !!hasDuplicate;
+ 
+ // Initialize against the default document
+ setDocument();
+ 
+ // Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
+ // Detached nodes confoundingly follow *each other*
+-support.sortDetached = assert(function( div1 ) {
++support.sortDetached = assert(function( el ) {
+ 	// Should return 1, but returns 4 (following)
+-	return div1.compareDocumentPosition( document.createElement("div") ) & 1;
++	return el.compareDocumentPosition( document.createElement("fieldset") ) & 1;
+ });
+ 
+ // Support: IE<8
+ // Prevent attribute/property "interpolation"
+-// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
+-if ( !assert(function( div ) {
+-	div.innerHTML = "<a href='#'></a>";
+-	return div.firstChild.getAttribute("href") === "#" ;
++// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
++if ( !assert(function( el ) {
++	el.innerHTML = "<a href='#'></a>";
++	return el.firstChild.getAttribute("href") === "#" ;
+ }) ) {
+ 	addHandle( "type|href|height|width", function( elem, name, isXML ) {
+ 		if ( !isXML ) {
+@@ -2941,10 +2742,10 @@ if ( !assert(function( div ) {
+ 
+ // Support: IE<9
+ // Use defaultValue in place of getAttribute("value")
+-if ( !support.attributes || !assert(function( div ) {
+-	div.innerHTML = "<input/>";
+-	div.firstChild.setAttribute( "value", "" );
+-	return div.firstChild.getAttribute( "value" ) === "";
++if ( !support.attributes || !assert(function( el ) {
++	el.innerHTML = "<input/>";
++	el.firstChild.setAttribute( "value", "" );
++	return el.firstChild.getAttribute( "value" ) === "";
+ }) ) {
+ 	addHandle( "value", function( elem, name, isXML ) {
+ 		if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
+@@ -2955,5354 +2756,6133 @@ if ( !support.attributes || !assert(function( div ) {
+ 
+ // Support: IE<9
+ // Use getAttributeNode to fetch booleans when getAttribute lies
+-if ( !assert(function( div ) {
+-	return div.getAttribute("disabled") == null;
++if ( !assert(function( el ) {
++	return el.getAttribute("disabled") == null;
+ }) ) {
+ 	addHandle( booleans, function( elem, name, isXML ) {
+ 		var val;
+ 		if ( !isXML ) {
+-			return (val = elem.getAttributeNode( name )) && val.specified ?
+-				val.value :
+-				elem[ name ] === true ? name.toLowerCase() : null;
++			return elem[ name ] === true ? name.toLowerCase() :
++					(val = elem.getAttributeNode( name )) && val.specified ?
++					val.value :
++				null;
+ 		}
+ 	});
+ }
+ 
++return Sizzle;
++
++})( window );
++
++
++
+ jQuery.find = Sizzle;
+ jQuery.expr = Sizzle.selectors;
+-jQuery.expr[":"] = jQuery.expr.pseudos;
+-jQuery.unique = Sizzle.uniqueSort;
++
++// Deprecated
++jQuery.expr[ ":" ] = jQuery.expr.pseudos;
++jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort;
+ jQuery.text = Sizzle.getText;
+ jQuery.isXMLDoc = Sizzle.isXML;
+ jQuery.contains = Sizzle.contains;
++jQuery.escapeSelector = Sizzle.escape;
+ 
+ 
+-})( window );
+-// String to Object options format cache
+-var optionsCache = {};
+-
+-// Convert String-formatted options into Object-formatted ones and store in cache
+-function createOptions( options ) {
+-	var object = optionsCache[ options ] = {};
+-	jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) {
+-		object[ flag ] = true;
+-	});
+-	return object;
+-}
+ 
+-/*
+- * Create a callback list using the following parameters:
+- *
+- *	options: an optional list of space-separated options that will change how
+- *			the callback list behaves or a more traditional option object
+- *
+- * By default a callback list will act like an event callback list and can be
+- * "fired" multiple times.
+- *
+- * Possible options:
+- *
+- *	once:			will ensure the callback list can only be fired once (like a Deferred)
+- *
+- *	memory:			will keep track of previous values and will call any callback added
+- *					after the list has been fired right away with the latest "memorized"
+- *					values (like a Deferred)
+- *
+- *	unique:			will ensure a callback can only be added once (no duplicate in the list)
+- *
+- *	stopOnFalse:	interrupt callings when a callback returns false
+- *
+- */
+-jQuery.Callbacks = function( options ) {
+ 
+-	// Convert options from String-formatted to Object-formatted if needed
+-	// (we check in cache first)
+-	options = typeof options === "string" ?
+-		( optionsCache[ options ] || createOptions( options ) ) :
+-		jQuery.extend( {}, options );
++var dir = function( elem, dir, until ) {
++	var matched = [],
++		truncate = until !== undefined;
+ 
+-	var // Flag to know if list is currently firing
+-		firing,
+-		// Last fire value (for non-forgettable lists)
+-		memory,
+-		// Flag to know if list was already fired
+-		fired,
+-		// End of the loop when firing
+-		firingLength,
+-		// Index of currently firing callback (modified by remove if needed)
+-		firingIndex,
+-		// First callback to fire (used internally by add and fireWith)
+-		firingStart,
+-		// Actual callback list
+-		list = [],
+-		// Stack of fire calls for repeatable lists
+-		stack = !options.once && [],
+-		// Fire callbacks
+-		fire = function( data ) {
+-			memory = options.memory && data;
+-			fired = true;
+-			firingIndex = firingStart || 0;
+-			firingStart = 0;
+-			firingLength = list.length;
+-			firing = true;
+-			for ( ; list && firingIndex < firingLength; firingIndex++ ) {
+-				if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
+-					memory = false; // To prevent further calls using add
+-					break;
+-				}
+-			}
+-			firing = false;
+-			if ( list ) {
+-				if ( stack ) {
+-					if ( stack.length ) {
+-						fire( stack.shift() );
+-					}
+-				} else if ( memory ) {
+-					list = [];
+-				} else {
+-					self.disable();
+-				}
+-			}
+-		},
+-		// Actual Callbacks object
+-		self = {
+-			// Add a callback or a collection of callbacks to the list
+-			add: function() {
+-				if ( list ) {
+-					// First, we save the current length
+-					var start = list.length;
+-					(function add( args ) {
+-						jQuery.each( args, function( _, arg ) {
+-							var type = jQuery.type( arg );
+-							if ( type === "function" ) {
+-								if ( !options.unique || !self.has( arg ) ) {
+-									list.push( arg );
+-								}
+-							} else if ( arg && arg.length && type !== "string" ) {
+-								// Inspect recursively
+-								add( arg );
+-							}
+-						});
+-					})( arguments );
+-					// Do we need to add the callbacks to the
+-					// current firing batch?
+-					if ( firing ) {
+-						firingLength = list.length;
+-					// With memory, if we're not firing then
+-					// we should call right away
+-					} else if ( memory ) {
+-						firingStart = start;
+-						fire( memory );
+-					}
+-				}
+-				return this;
+-			},
+-			// Remove a callback from the list
+-			remove: function() {
+-				if ( list ) {
+-					jQuery.each( arguments, function( _, arg ) {
+-						var index;
+-						while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
+-							list.splice( index, 1 );
+-							// Handle firing indexes
+-							if ( firing ) {
+-								if ( index <= firingLength ) {
+-									firingLength--;
+-								}
+-								if ( index <= firingIndex ) {
+-									firingIndex--;
+-								}
+-							}
+-						}
+-					});
+-				}
+-				return this;
+-			},
+-			// Check if a given callback is in the list.
+-			// If no argument is given, return whether or not list has callbacks attached.
+-			has: function( fn ) {
+-				return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
+-			},
+-			// Remove all callbacks from the list
+-			empty: function() {
+-				list = [];
+-				firingLength = 0;
+-				return this;
+-			},
+-			// Have the list do nothing anymore
+-			disable: function() {
+-				list = stack = memory = undefined;
+-				return this;
+-			},
+-			// Is it disabled?
+-			disabled: function() {
+-				return !list;
+-			},
+-			// Lock the list in its current state
+-			lock: function() {
+-				stack = undefined;
+-				if ( !memory ) {
+-					self.disable();
+-				}
+-				return this;
+-			},
+-			// Is it locked?
+-			locked: function() {
+-				return !stack;
+-			},
+-			// Call all callbacks with the given context and arguments
+-			fireWith: function( context, args ) {
+-				if ( list && ( !fired || stack ) ) {
+-					args = args || [];
+-					args = [ context, args.slice ? args.slice() : args ];
+-					if ( firing ) {
+-						stack.push( args );
+-					} else {
+-						fire( args );
+-					}
+-				}
+-				return this;
+-			},
+-			// Call all the callbacks with the given arguments
+-			fire: function() {
+-				self.fireWith( this, arguments );
+-				return this;
+-			},
+-			// To know if the callbacks have already been called at least once
+-			fired: function() {
+-				return !!fired;
++	while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) {
++		if ( elem.nodeType === 1 ) {
++			if ( truncate && jQuery( elem ).is( until ) ) {
++				break;
+ 			}
+-		};
+-
+-	return self;
++			matched.push( elem );
++		}
++	}
++	return matched;
+ };
+-jQuery.extend({
+ 
+-	Deferred: function( func ) {
+-		var tuples = [
+-				// action, add listener, listener list, final state
+-				[ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
+-				[ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
+-				[ "notify", "progress", jQuery.Callbacks("memory") ]
+-			],
+-			state = "pending",
+-			promise = {
+-				state: function() {
+-					return state;
+-				},
+-				always: function() {
+-					deferred.done( arguments ).fail( arguments );
+-					return this;
+-				},
+-				then: function( /* fnDone, fnFail, fnProgress */ ) {
+-					var fns = arguments;
+-					return jQuery.Deferred(function( newDefer ) {
+-						jQuery.each( tuples, function( i, tuple ) {
+-							var action = tuple[ 0 ],
+-								fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
+-							// deferred[ done | fail | progress ] for forwarding actions to newDefer
+-							deferred[ tuple[1] ](function() {
+-								var returned = fn && fn.apply( this, arguments );
+-								if ( returned && jQuery.isFunction( returned.promise ) ) {
+-									returned.promise()
+-										.done( newDefer.resolve )
+-										.fail( newDefer.reject )
+-										.progress( newDefer.notify );
+-								} else {
+-									newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
+-								}
+-							});
+-						});
+-						fns = null;
+-					}).promise();
+-				},
+-				// Get a promise for this deferred
+-				// If obj is provided, the promise aspect is added to the object
+-				promise: function( obj ) {
+-					return obj != null ? jQuery.extend( obj, promise ) : promise;
+-				}
+-			},
+-			deferred = {};
+ 
+-		// Keep pipe for back-compat
+-		promise.pipe = promise.then;
++var siblings = function( n, elem ) {
++	var matched = [];
+ 
+-		// Add list-specific methods
+-		jQuery.each( tuples, function( i, tuple ) {
+-			var list = tuple[ 2 ],
+-				stateString = tuple[ 3 ];
++	for ( ; n; n = n.nextSibling ) {
++		if ( n.nodeType === 1 && n !== elem ) {
++			matched.push( n );
++		}
++	}
+ 
+-			// promise[ done | fail | progress ] = list.add
+-			promise[ tuple[1] ] = list.add;
++	return matched;
++};
+ 
+-			// Handle state
+-			if ( stateString ) {
+-				list.add(function() {
+-					// state = [ resolved | rejected ]
+-					state = stateString;
+ 
+-				// [ reject_list | resolve_list ].disable; progress_list.lock
+-				}, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
+-			}
++var rneedsContext = jQuery.expr.match.needsContext;
+ 
+-			// deferred[ resolve | reject | notify ]
+-			deferred[ tuple[0] ] = function() {
+-				deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
+-				return this;
+-			};
+-			deferred[ tuple[0] + "With" ] = list.fireWith;
+-		});
+ 
+-		// Make the deferred a promise
+-		promise.promise( deferred );
+ 
+-		// Call given func if any
+-		if ( func ) {
+-			func.call( deferred, deferred );
+-		}
++function nodeName( elem, name ) {
+ 
+-		// All done!
+-		return deferred;
+-	},
++  return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
+ 
+-	// Deferred helper
+-	when: function( subordinate /* , ..., subordinateN */ ) {
+-		var i = 0,
+-			resolveValues = core_slice.call( arguments ),
+-			length = resolveValues.length,
+-
+-			// the count of uncompleted subordinates
+-			remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
+-
+-			// the master Deferred. If resolveValues consist of only a single Deferred, just use that.
+-			deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
+-
+-			// Update function for both resolve and progress values
+-			updateFunc = function( i, contexts, values ) {
+-				return function( value ) {
+-					contexts[ i ] = this;
+-					values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value;
+-					if( values === progressValues ) {
+-						deferred.notifyWith( contexts, values );
+-					} else if ( !( --remaining ) ) {
+-						deferred.resolveWith( contexts, values );
+-					}
+-				};
+-			},
+-
+-			progressValues, progressContexts, resolveContexts;
++};
++var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i );
+ 
+-		// add listeners to Deferred subordinates; treat others as resolved
+-		if ( length > 1 ) {
+-			progressValues = new Array( length );
+-			progressContexts = new Array( length );
+-			resolveContexts = new Array( length );
+-			for ( ; i < length; i++ ) {
+-				if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
+-					resolveValues[ i ].promise()
+-						.done( updateFunc( i, resolveContexts, resolveValues ) )
+-						.fail( deferred.reject )
+-						.progress( updateFunc( i, progressContexts, progressValues ) );
+-				} else {
+-					--remaining;
+-				}
+-			}
+-		}
+ 
+-		// if we're not waiting on anything, resolve the master
+-		if ( !remaining ) {
+-			deferred.resolveWith( resolveContexts, resolveValues );
+-		}
+ 
+-		return deferred.promise();
++// Implement the identical functionality for filter and not
++function winnow( elements, qualifier, not ) {
++	if ( isFunction( qualifier ) ) {
++		return jQuery.grep( elements, function( elem, i ) {
++			return !!qualifier.call( elem, i, elem ) !== not;
++		} );
+ 	}
+-});
+-jQuery.support = (function( support ) {
+-
+-	var all, a, input, select, fragment, opt, eventName, isSupported, i,
+-		div = document.createElement("div");
+-
+-	// Setup
+-	div.setAttribute( "className", "t" );
+-	div.innerHTML = "  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>";
+ 
+-	// Finish early in limited (non-browser) environments
+-	all = div.getElementsByTagName("*") || [];
+-	a = div.getElementsByTagName("a")[ 0 ];
+-	if ( !a || !a.style || !all.length ) {
+-		return support;
++	// Single element
++	if ( qualifier.nodeType ) {
++		return jQuery.grep( elements, function( elem ) {
++			return ( elem === qualifier ) !== not;
++		} );
+ 	}
+ 
+-	// First batch of tests
+-	select = document.createElement("select");
+-	opt = select.appendChild( document.createElement("option") );
+-	input = div.getElementsByTagName("input")[ 0 ];
+-
+-	a.style.cssText = "top:1px;float:left;opacity:.5";
+-
+-	// Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
+-	support.getSetAttribute = div.className !== "t";
++	// Arraylike of elements (jQuery, arguments, Array)
++	if ( typeof qualifier !== "string" ) {
++		return jQuery.grep( elements, function( elem ) {
++			return ( indexOf.call( qualifier, elem ) > -1 ) !== not;
++		} );
++	}
+ 
+-	// IE strips leading whitespace when .innerHTML is used
+-	support.leadingWhitespace = div.firstChild.nodeType === 3;
++	// Filtered directly for both simple and complex selectors
++	return jQuery.filter( qualifier, elements, not );
++}
+ 
+-	// Make sure that tbody elements aren't automatically inserted
+-	// IE will insert them into empty tables
+-	support.tbody = !div.getElementsByTagName("tbody").length;
++jQuery.filter = function( expr, elems, not ) {
++	var elem = elems[ 0 ];
+ 
+-	// Make sure that link elements get serialized correctly by innerHTML
+-	// This requires a wrapper element in IE
+-	support.htmlSerialize = !!div.getElementsByTagName("link").length;
++	if ( not ) {
++		expr = ":not(" + expr + ")";
++	}
+ 
+-	// Get the style information from getAttribute
+-	// (IE uses .cssText instead)
+-	support.style = /top/.test( a.getAttribute("style") );
++	if ( elems.length === 1 && elem.nodeType === 1 ) {
++		return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [];
++	}
+ 
+-	// Make sure that URLs aren't manipulated
+-	// (IE normalizes it by default)
+-	support.hrefNormalized = a.getAttribute("href") === "/a";
++	return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
++		return elem.nodeType === 1;
++	} ) );
++};
+ 
+-	// Make sure that element opacity exists
+-	// (IE uses filter instead)
+-	// Use a regex to work around a WebKit issue. See #5145
+-	support.opacity = /^0.5/.test( a.style.opacity );
++jQuery.fn.extend( {
++	find: function( selector ) {
++		var i, ret,
++			len = this.length,
++			self = this;
+ 
+-	// Verify style float existence
+-	// (IE uses styleFloat instead of cssFloat)
+-	support.cssFloat = !!a.style.cssFloat;
++		if ( typeof selector !== "string" ) {
++			return this.pushStack( jQuery( selector ).filter( function() {
++				for ( i = 0; i < len; i++ ) {
++					if ( jQuery.contains( self[ i ], this ) ) {
++						return true;
++					}
++				}
++			} ) );
++		}
+ 
+-	// Check the default checkbox/radio value ("" on WebKit; "on" elsewhere)
+-	support.checkOn = !!input.value;
++		ret = this.pushStack( [] );
+ 
+-	// Make sure that a selected-by-default option has a working selected property.
+-	// (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
+-	support.optSelected = opt.selected;
++		for ( i = 0; i < len; i++ ) {
++			jQuery.find( selector, self[ i ], ret );
++		}
+ 
+-	// Tests for enctype support on a form (#6743)
+-	support.enctype = !!document.createElement("form").enctype;
++		return len > 1 ? jQuery.uniqueSort( ret ) : ret;
++	},
++	filter: function( selector ) {
++		return this.pushStack( winnow( this, selector || [], false ) );
++	},
++	not: function( selector ) {
++		return this.pushStack( winnow( this, selector || [], true ) );
++	},
++	is: function( selector ) {
++		return !!winnow(
++			this,
+ 
+-	// Makes sure cloning an html5 element does not cause problems
+-	// Where outerHTML is undefined, this still works
+-	support.html5Clone = document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav></:nav>";
++			// If this is a positional/relative selector, check membership in the returned set
++			// so $("p:first").is("p:last") won't return true for a doc with two "p".
++			typeof selector === "string" && rneedsContext.test( selector ) ?
++				jQuery( selector ) :
++				selector || [],
++			false
++		).length;
++	}
++} );
+ 
+-	// Will be defined later
+-	support.inlineBlockNeedsLayout = false;
+-	support.shrinkWrapBlocks = false;
+-	support.pixelPosition = false;
+-	support.deleteExpando = true;
+-	support.noCloneEvent = true;
+-	support.reliableMarginRight = true;
+-	support.boxSizingReliable = true;
+ 
+-	// Make sure checked status is properly cloned
+-	input.checked = true;
+-	support.noCloneChecked = input.cloneNode( true ).checked;
++// Initialize a jQuery object
+ 
+-	// Make sure that the options inside disabled selects aren't marked as disabled
+-	// (WebKit marks them as disabled)
+-	select.disabled = true;
+-	support.optDisabled = !opt.disabled;
+ 
+-	// Support: IE<9
+-	try {
+-		delete div.test;
+-	} catch( e ) {
+-		support.deleteExpando = false;
+-	}
++// A central reference to the root jQuery(document)
++var rootjQuery,
+ 
+-	// Check if we can trust getAttribute("value")
+-	input = document.createElement("input");
+-	input.setAttribute( "value", "" );
+-	support.input = input.getAttribute( "value" ) === "";
++	// A simple way to check for HTML strings
++	// Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
++	// Strict HTML recognition (#11290: must start with <)
++	// Shortcut simple #id case for speed
++	rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,
+ 
+-	// Check if an input maintains its value after becoming a radio
+-	input.value = "t";
+-	input.setAttribute( "type", "radio" );
+-	support.radioValue = input.value === "t";
++	init = jQuery.fn.init = function( selector, context, root ) {
++		var match, elem;
+ 
+-	// #11217 - WebKit loses check when the name is after the checked attribute
+-	input.setAttribute( "checked", "t" );
+-	input.setAttribute( "name", "t" );
++		// HANDLE: $(""), $(null), $(undefined), $(false)
++		if ( !selector ) {
++			return this;
++		}
+ 
+-	fragment = document.createDocumentFragment();
+-	fragment.appendChild( input );
++		// Method init() accepts an alternate rootjQuery
++		// so migrate can support jQuery.sub (gh-2101)
++		root = root || rootjQuery;
+ 
+-	// Check if a disconnected checkbox will retain its checked
+-	// value of true after appended to the DOM (IE6/7)
+-	support.appendChecked = input.checked;
++		// Handle HTML strings
++		if ( typeof selector === "string" ) {
++			if ( selector[ 0 ] === "<" &&
++				selector[ selector.length - 1 ] === ">" &&
++				selector.length >= 3 ) {
+ 
+-	// WebKit doesn't clone checked state correctly in fragments
+-	support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
++				// Assume that strings that start and end with <> are HTML and skip the regex check
++				match = [ null, selector, null ];
+ 
+-	// Support: IE<9
+-	// Opera does not clone events (and typeof div.attachEvent === undefined).
+-	// IE9-10 clones events bound via attachEvent, but they don't trigger with .click()
+-	if ( div.attachEvent ) {
+-		div.attachEvent( "onclick", function() {
+-			support.noCloneEvent = false;
+-		});
++			} else {
++				match = rquickExpr.exec( selector );
++			}
+ 
+-		div.cloneNode( true ).click();
+-	}
++			// Match html or make sure no context is specified for #id
++			if ( match && ( match[ 1 ] || !context ) ) {
+ 
+-	// Support: IE<9 (lack submit/change bubble), Firefox 17+ (lack focusin event)
+-	// Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP)
+-	for ( i in { submit: true, change: true, focusin: true }) {
+-		div.setAttribute( eventName = "on" + i, "t" );
++				// HANDLE: $(html) -> $(array)
++				if ( match[ 1 ] ) {
++					context = context instanceof jQuery ? context[ 0 ] : context;
+ 
+-		support[ i + "Bubbles" ] = eventName in window || div.attributes[ eventName ].expando === false;
+-	}
++					// Option to run scripts is true for back-compat
++					// Intentionally let the error be thrown if parseHTML is not present
++					jQuery.merge( this, jQuery.parseHTML(
++						match[ 1 ],
++						context && context.nodeType ? context.ownerDocument || context : document,
++						true
++					) );
+ 
+-	div.style.backgroundClip = "content-box";
+-	div.cloneNode( true ).style.backgroundClip = "";
+-	support.clearCloneStyle = div.style.backgroundClip === "content-box";
++					// HANDLE: $(html, props)
++					if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) {
++						for ( match in context ) {
+ 
+-	// Support: IE<9
+-	// Iteration over object's inherited properties before its own.
+-	for ( i in jQuery( support ) ) {
+-		break;
+-	}
+-	support.ownLast = i !== "0";
++							// Properties of context are called as methods if possible
++							if ( isFunction( this[ match ] ) ) {
++								this[ match ]( context[ match ] );
+ 
+-	// Run tests that need a body at doc ready
+-	jQuery(function() {
+-		var container, marginDiv, tds,
+-			divReset = "padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",
+-			body = document.getElementsByTagName("body")[0];
++							// ...and otherwise set as attributes
++							} else {
++								this.attr( match, context[ match ] );
++							}
++						}
++					}
+ 
+-		if ( !body ) {
+-			// Return for frameset docs that don't have a body
+-			return;
+-		}
++					return this;
+ 
+-		container = document.createElement("div");
+-		container.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px";
++				// HANDLE: $(#id)
++				} else {
++					elem = document.getElementById( match[ 2 ] );
+ 
+-		body.appendChild( container ).appendChild( div );
++					if ( elem ) {
+ 
+-		// Support: IE8
+-		// Check if table cells still have offsetWidth/Height when they are set
+-		// to display:none and there are still other visible table cells in a
+-		// table row; if so, offsetWidth/Height are not reliable for use when
+-		// determining if an element has been hidden directly using
+-		// display:none (it is still safe to use offsets if a parent element is
+-		// hidden; don safety goggles and see bug #4512 for more information).
+-		div.innerHTML = "<table><tr><td></td><td>t</td></tr></table>";
+-		tds = div.getElementsByTagName("td");
+-		tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none";
+-		isSupported = ( tds[ 0 ].offsetHeight === 0 );
++						// Inject the element directly into the jQuery object
++						this[ 0 ] = elem;
++						this.length = 1;
++					}
++					return this;
++				}
+ 
+-		tds[ 0 ].style.display = "";
+-		tds[ 1 ].style.display = "none";
++			// HANDLE: $(expr, $(...))
++			} else if ( !context || context.jquery ) {
++				return ( context || root ).find( selector );
+ 
+-		// Support: IE8
+-		// Check if empty table cells still have offsetWidth/Height
+-		support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
++			// HANDLE: $(expr, context)
++			// (which is just equivalent to: $(context).find(expr)
++			} else {
++				return this.constructor( context ).find( selector );
++			}
+ 
+-		// Check box-sizing and margin behavior.
+-		div.innerHTML = "";
+-		div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;";
++		// HANDLE: $(DOMElement)
++		} else if ( selector.nodeType ) {
++			this[ 0 ] = selector;
++			this.length = 1;
++			return this;
+ 
+-		// Workaround failing boxSizing test due to offsetWidth returning wrong value
+-		// with some non-1 values of body zoom, ticket #13543
+-		jQuery.swap( body, body.style.zoom != null ? { zoom: 1 } : {}, function() {
+-			support.boxSizing = div.offsetWidth === 4;
+-		});
++		// HANDLE: $(function)
++		// Shortcut for document ready
++		} else if ( isFunction( selector ) ) {
++			return root.ready !== undefined ?
++				root.ready( selector ) :
+ 
+-		// Use window.getComputedStyle because jsdom on node.js will break without it.
+-		if ( window.getComputedStyle ) {
+-			support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%";
+-			support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px";
+-
+-			// Check if div with explicit width and no margin-right incorrectly
+-			// gets computed margin-right based on width of container. (#3333)
+-			// Fails in WebKit before Feb 2011 nightlies
+-			// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+-			marginDiv = div.appendChild( document.createElement("div") );
+-			marginDiv.style.cssText = div.style.cssText = divReset;
+-			marginDiv.style.marginRight = marginDiv.style.width = "0";
+-			div.style.width = "1px";
+-
+-			support.reliableMarginRight =
+-				!parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight );
+-		}
+-
+-		if ( typeof div.style.zoom !== core_strundefined ) {
+-			// Support: IE<8
+-			// Check if natively block-level elements act like inline-block
+-			// elements when setting their display to 'inline' and giving
+-			// them layout
+-			div.innerHTML = "";
+-			div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1";
+-			support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 );
+-
+-			// Support: IE6
+-			// Check if elements with layout shrink-wrap their children
+-			div.style.display = "block";
+-			div.innerHTML = "<div></div>";
+-			div.firstChild.style.width = "5px";
+-			support.shrinkWrapBlocks = ( div.offsetWidth !== 3 );
+-
+-			if ( support.inlineBlockNeedsLayout ) {
+-				// Prevent IE 6 from affecting layout for positioned elements #11048
+-				// Prevent IE from shrinking the body in IE 7 mode #12869
+-				// Support: IE<8
+-				body.style.zoom = 1;
+-			}
++				// Execute immediately if ready is not present
++				selector( jQuery );
+ 		}
+ 
+-		body.removeChild( container );
+-
+-		// Null elements to avoid leaks in IE
+-		container = div = tds = marginDiv = null;
+-	});
++		return jQuery.makeArray( selector, this );
++	};
+ 
+-	// Null elements to avoid leaks in IE
+-	all = select = fragment = opt = a = input = null;
++// Give the init function the jQuery prototype for later instantiation
++init.prototype = jQuery.fn;
+ 
+-	return support;
+-})({});
++// Initialize central reference
++rootjQuery = jQuery( document );
+ 
+-var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/,
+-	rmultiDash = /([A-Z])/g;
+ 
+-function internalData( elem, name, data, pvt /* Internal Use Only */ ){
+-	if ( !jQuery.acceptData( elem ) ) {
+-		return;
+-	}
++var rparentsprev = /^(?:parents|prev(?:Until|All))/,
+ 
+-	var ret, thisCache,
+-		internalKey = jQuery.expando,
++	// Methods guaranteed to produce a unique set when starting from a unique set
++	guaranteedUnique = {
++		children: true,
++		contents: true,
++		next: true,
++		prev: true
++	};
+ 
+-		// We have to handle DOM nodes and JS objects differently because IE6-7
+-		// can't GC object references properly across the DOM-JS boundary
+-		isNode = elem.nodeType,
++jQuery.fn.extend( {
++	has: function( target ) {
++		var targets = jQuery( target, this ),
++			l = targets.length;
+ 
+-		// Only DOM nodes need the global jQuery cache; JS object data is
+-		// attached directly to the object so GC can occur automatically
+-		cache = isNode ? jQuery.cache : elem,
++		return this.filter( function() {
++			var i = 0;
++			for ( ; i < l; i++ ) {
++				if ( jQuery.contains( this, targets[ i ] ) ) {
++					return true;
++				}
++			}
++		} );
++	},
+ 
+-		// Only defining an ID for JS objects if its cache already exists allows
+-		// the code to shortcut on the same path as a DOM node with no cache
+-		id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey;
++	closest: function( selectors, context ) {
++		var cur,
++			i = 0,
++			l = this.length,
++			matched = [],
++			targets = typeof selectors !== "string" && jQuery( selectors );
+ 
+-	// Avoid doing any more work than we need to when trying to get data on an
+-	// object that has no data at all
+-	if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && data === undefined && typeof name === "string" ) {
+-		return;
+-	}
++		// Positional selectors never match, since there's no _selection_ context
++		if ( !rneedsContext.test( selectors ) ) {
++			for ( ; i < l; i++ ) {
++				for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) {
+ 
+-	if ( !id ) {
+-		// Only DOM nodes need a new unique ID for each element since their data
+-		// ends up in the global cache
+-		if ( isNode ) {
+-			id = elem[ internalKey ] = core_deletedIds.pop() || jQuery.guid++;
+-		} else {
+-			id = internalKey;
+-		}
+-	}
++					// Always skip document fragments
++					if ( cur.nodeType < 11 && ( targets ?
++						targets.index( cur ) > -1 :
+ 
+-	if ( !cache[ id ] ) {
+-		// Avoid exposing jQuery metadata on plain JS objects when the object
+-		// is serialized using JSON.stringify
+-		cache[ id ] = isNode ? {} : { toJSON: jQuery.noop };
+-	}
++						// Don't pass non-elements to Sizzle
++						cur.nodeType === 1 &&
++							jQuery.find.matchesSelector( cur, selectors ) ) ) {
+ 
+-	// An object can be passed to jQuery.data instead of a key/value pair; this gets
+-	// shallow copied over onto the existing cache
+-	if ( typeof name === "object" || typeof name === "function" ) {
+-		if ( pvt ) {
+-			cache[ id ] = jQuery.extend( cache[ id ], name );
+-		} else {
+-			cache[ id ].data = jQuery.extend( cache[ id ].data, name );
++						matched.push( cur );
++						break;
++					}
++				}
++			}
+ 		}
+-	}
+ 
+-	thisCache = cache[ id ];
++		return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched );
++	},
+ 
+-	// jQuery data() is stored in a separate object inside the object's internal data
+-	// cache in order to avoid key collisions between internal data and user-defined
+-	// data.
+-	if ( !pvt ) {
+-		if ( !thisCache.data ) {
+-			thisCache.data = {};
+-		}
++	// Determine the position of an element within the set
++	index: function( elem ) {
+ 
+-		thisCache = thisCache.data;
+-	}
++		// No argument, return index in parent
++		if ( !elem ) {
++			return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
++		}
+ 
+-	if ( data !== undefined ) {
+-		thisCache[ jQuery.camelCase( name ) ] = data;
+-	}
++		// Index in selector
++		if ( typeof elem === "string" ) {
++			return indexOf.call( jQuery( elem ), this[ 0 ] );
++		}
+ 
+-	// Check for both converted-to-camel and non-converted data property names
+-	// If a data property was specified
+-	if ( typeof name === "string" ) {
++		// Locate the position of the desired element
++		return indexOf.call( this,
+ 
+-		// First Try to find as-is property data
+-		ret = thisCache[ name ];
++			// If it receives a jQuery object, the first element is used
++			elem.jquery ? elem[ 0 ] : elem
++		);
++	},
+ 
+-		// Test for null|undefined property data
+-		if ( ret == null ) {
++	add: function( selector, context ) {
++		return this.pushStack(
++			jQuery.uniqueSort(
++				jQuery.merge( this.get(), jQuery( selector, context ) )
++			)
++		);
++	},
+ 
+-			// Try to find the camelCased property
+-			ret = thisCache[ jQuery.camelCase( name ) ];
+-		}
+-	} else {
+-		ret = thisCache;
++	addBack: function( selector ) {
++		return this.add( selector == null ?
++			this.prevObject : this.prevObject.filter( selector )
++		);
+ 	}
++} );
+ 
+-	return ret;
++function sibling( cur, dir ) {
++	while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {}
++	return cur;
+ }
+ 
+-function internalRemoveData( elem, name, pvt ) {
+-	if ( !jQuery.acceptData( elem ) ) {
+-		return;
+-	}
+-
+-	var thisCache, i,
+-		isNode = elem.nodeType,
++jQuery.each( {
++	parent: function( elem ) {
++		var parent = elem.parentNode;
++		return parent && parent.nodeType !== 11 ? parent : null;
++	},
++	parents: function( elem ) {
++		return dir( elem, "parentNode" );
++	},
++	parentsUntil: function( elem, i, until ) {
++		return dir( elem, "parentNode", until );
++	},
++	next: function( elem ) {
++		return sibling( elem, "nextSibling" );
++	},
++	prev: function( elem ) {
++		return sibling( elem, "previousSibling" );
++	},
++	nextAll: function( elem ) {
++		return dir( elem, "nextSibling" );
++	},
++	prevAll: function( elem ) {
++		return dir( elem, "previousSibling" );
++	},
++	nextUntil: function( elem, i, until ) {
++		return dir( elem, "nextSibling", until );
++	},
++	prevUntil: function( elem, i, until ) {
++		return dir( elem, "previousSibling", until );
++	},
++	siblings: function( elem ) {
++		return siblings( ( elem.parentNode || {} ).firstChild, elem );
++	},
++	children: function( elem ) {
++		return siblings( elem.firstChild );
++	},
++	contents: function( elem ) {
++		if ( typeof elem.contentDocument !== "undefined" ) {
++			return elem.contentDocument;
++		}
+ 
+-		// See jQuery.data for more information
+-		cache = isNode ? jQuery.cache : elem,
+-		id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
++		// Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only
++		// Treat the template element as a regular one in browsers that
++		// don't support it.
++		if ( nodeName( elem, "template" ) ) {
++			elem = elem.content || elem;
++		}
+ 
+-	// If there is already no cache entry for this object, there is no
+-	// purpose in continuing
+-	if ( !cache[ id ] ) {
+-		return;
++		return jQuery.merge( [], elem.childNodes );
+ 	}
++}, function( name, fn ) {
++	jQuery.fn[ name ] = function( until, selector ) {
++		var matched = jQuery.map( this, fn, until );
+ 
+-	if ( name ) {
+-
+-		thisCache = pvt ? cache[ id ] : cache[ id ].data;
+-
+-		if ( thisCache ) {
+-
+-			// Support array or space separated string names for data keys
+-			if ( !jQuery.isArray( name ) ) {
++		if ( name.slice( -5 ) !== "Until" ) {
++			selector = until;
++		}
+ 
+-				// try the string as a key before any manipulation
+-				if ( name in thisCache ) {
+-					name = [ name ];
+-				} else {
++		if ( selector && typeof selector === "string" ) {
++			matched = jQuery.filter( selector, matched );
++		}
+ 
+-					// split the camel cased version by spaces unless a key with the spaces exists
+-					name = jQuery.camelCase( name );
+-					if ( name in thisCache ) {
+-						name = [ name ];
+-					} else {
+-						name = name.split(" ");
+-					}
+-				}
+-			} else {
+-				// If "name" is an array of keys...
+-				// When data is initially created, via ("key", "val") signature,
+-				// keys will be converted to camelCase.
+-				// Since there is no way to tell _how_ a key was added, remove
+-				// both plain key and camelCase key. #12786
+-				// This will only penalize the array argument path.
+-				name = name.concat( jQuery.map( name, jQuery.camelCase ) );
+-			}
++		if ( this.length > 1 ) {
+ 
+-			i = name.length;
+-			while ( i-- ) {
+-				delete thisCache[ name[i] ];
++			// Remove duplicates
++			if ( !guaranteedUnique[ name ] ) {
++				jQuery.uniqueSort( matched );
+ 			}
+ 
+-			// If there is no data left in the cache, we want to continue
+-			// and let the cache object itself get destroyed
+-			if ( pvt ? !isEmptyDataObject(thisCache) : !jQuery.isEmptyObject(thisCache) ) {
+-				return;
++			// Reverse order for parents* and prev-derivatives
++			if ( rparentsprev.test( name ) ) {
++				matched.reverse();
+ 			}
+ 		}
+-	}
+ 
+-	// See jQuery.data for more information
+-	if ( !pvt ) {
+-		delete cache[ id ].data;
++		return this.pushStack( matched );
++	};
++} );
++var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g );
+ 
+-		// Don't destroy the parent cache unless the internal data object
+-		// had been the only thing left in it
+-		if ( !isEmptyDataObject( cache[ id ] ) ) {
+-			return;
+-		}
+-	}
+ 
+-	// Destroy the cache
+-	if ( isNode ) {
+-		jQuery.cleanData( [ elem ], true );
+ 
+-	// Use delete when supported for expandos or `cache` is not a window per isWindow (#10080)
+-	/* jshint eqeqeq: false */
+-	} else if ( jQuery.support.deleteExpando || cache != cache.window ) {
+-		/* jshint eqeqeq: true */
+-		delete cache[ id ];
+-
+-	// When all else fails, null
+-	} else {
+-		cache[ id ] = null;
+-	}
++// Convert String-formatted options into Object-formatted ones
++function createOptions( options ) {
++	var object = {};
++	jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) {
++		object[ flag ] = true;
++	} );
++	return object;
+ }
+ 
+-jQuery.extend({
+-	cache: {},
+-
+-	// The following elements throw uncatchable exceptions if you
+-	// attempt to add expando properties to them.
+-	noData: {
+-		"applet": true,
+-		"embed": true,
+-		// Ban all objects except for Flash (which handle expandos)
+-		"object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
+-	},
+-
+-	hasData: function( elem ) {
+-		elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
+-		return !!elem && !isEmptyDataObject( elem );
+-	},
++/*
++ * Create a callback list using the following parameters:
++ *
++ *	options: an optional list of space-separated options that will change how
++ *			the callback list behaves or a more traditional option object
++ *
++ * By default a callback list will act like an event callback list and can be
++ * "fired" multiple times.
++ *
++ * Possible options:
++ *
++ *	once:			will ensure the callback list can only be fired once (like a Deferred)
++ *
++ *	memory:			will keep track of previous values and will call any callback added
++ *					after the list has been fired right away with the latest "memorized"
++ *					values (like a Deferred)
++ *
++ *	unique:			will ensure a callback can only be added once (no duplicate in the list)
++ *
++ *	stopOnFalse:	interrupt callings when a callback returns false
++ *
++ */
++jQuery.Callbacks = function( options ) {
+ 
+-	data: function( elem, name, data ) {
+-		return internalData( elem, name, data );
+-	},
++	// Convert options from String-formatted to Object-formatted if needed
++	// (we check in cache first)
++	options = typeof options === "string" ?
++		createOptions( options ) :
++		jQuery.extend( {}, options );
+ 
+-	removeData: function( elem, name ) {
+-		return internalRemoveData( elem, name );
+-	},
++	var // Flag to know if list is currently firing
++		firing,
+ 
+-	// For internal use only.
+-	_data: function( elem, name, data ) {
+-		return internalData( elem, name, data, true );
+-	},
++		// Last fire value for non-forgettable lists
++		memory,
+ 
+-	_removeData: function( elem, name ) {
+-		return internalRemoveData( elem, name, true );
+-	},
++		// Flag to know if list was already fired
++		fired,
+ 
+-	// A method for determining if a DOM node can handle the data expando
+-	acceptData: function( elem ) {
+-		// Do not set data on non-element because it will not be cleared (#8335).
+-		if ( elem.nodeType && elem.nodeType !== 1 && elem.nodeType !== 9 ) {
+-			return false;
+-		}
++		// Flag to prevent firing
++		locked,
+ 
+-		var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ];
++		// Actual callback list
++		list = [],
+ 
+-		// nodes accept data unless otherwise specified; rejection can be conditional
+-		return !noData || noData !== true && elem.getAttribute("classid") === noData;
+-	}
+-});
++		// Queue of execution data for repeatable lists
++		queue = [],
+ 
+-jQuery.fn.extend({
+-	data: function( key, value ) {
+-		var attrs, name,
+-			data = null,
+-			i = 0,
+-			elem = this[0];
++		// Index of currently firing callback (modified by add/remove as needed)
++		firingIndex = -1,
+ 
+-		// Special expections of .data basically thwart jQuery.access,
+-		// so implement the relevant behavior ourselves
++		// Fire callbacks
++		fire = function() {
+ 
+-		// Gets all values
+-		if ( key === undefined ) {
+-			if ( this.length ) {
+-				data = jQuery.data( elem );
++			// Enforce single-firing
++			locked = locked || options.once;
+ 
+-				if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
+-					attrs = elem.attributes;
+-					for ( ; i < attrs.length; i++ ) {
+-						name = attrs[i].name;
++			// Execute callbacks for all pending executions,
++			// respecting firingIndex overrides and runtime changes
++			fired = firing = true;
++			for ( ; queue.length; firingIndex = -1 ) {
++				memory = queue.shift();
++				while ( ++firingIndex < list.length ) {
+ 
+-						if ( name.indexOf("data-") === 0 ) {
+-							name = jQuery.camelCase( name.slice(5) );
++					// Run callback and check for early termination
++					if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&
++						options.stopOnFalse ) {
+ 
+-							dataAttr( elem, name, data[ name ] );
+-						}
++						// Jump to end and forget the data so .add doesn't re-fire
++						firingIndex = list.length;
++						memory = false;
+ 					}
+-					jQuery._data( elem, "parsedAttrs", true );
+ 				}
+ 			}
+ 
+-			return data;
+-		}
+-
+-		// Sets multiple values
+-		if ( typeof key === "object" ) {
+-			return this.each(function() {
+-				jQuery.data( this, key );
+-			});
+-		}
++			// Forget the data if we're done with it
++			if ( !options.memory ) {
++				memory = false;
++			}
+ 
+-		return arguments.length > 1 ?
++			firing = false;
+ 
+-			// Sets one value
+-			this.each(function() {
+-				jQuery.data( this, key, value );
+-			}) :
++			// Clean up if we're done firing for good
++			if ( locked ) {
+ 
+-			// Gets one value
+-			// Try to fetch any internally stored data first
+-			elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : null;
+-	},
++				// Keep an empty list if we have data for future add calls
++				if ( memory ) {
++					list = [];
+ 
+-	removeData: function( key ) {
+-		return this.each(function() {
+-			jQuery.removeData( this, key );
+-		});
+-	}
+-});
++				// Otherwise, this object is spent
++				} else {
++					list = "";
++				}
++			}
++		},
+ 
+-function dataAttr( elem, key, data ) {
+-	// If nothing was found internally, try to fetch any
+-	// data from the HTML5 data-* attribute
+-	if ( data === undefined && elem.nodeType === 1 ) {
++		// Actual Callbacks object
++		self = {
+ 
+-		var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
++			// Add a callback or a collection of callbacks to the list
++			add: function() {
++				if ( list ) {
+ 
+-		data = elem.getAttribute( name );
++					// If we have memory from a past run, we should fire after adding
++					if ( memory && !firing ) {
++						firingIndex = list.length - 1;
++						queue.push( memory );
++					}
+ 
+-		if ( typeof data === "string" ) {
+-			try {
+-				data = data === "true" ? true :
+-					data === "false" ? false :
+-					data === "null" ? null :
+-					// Only convert to a number if it doesn't change the string
+-					+data + "" === data ? +data :
+-					rbrace.test( data ) ? jQuery.parseJSON( data ) :
+-						data;
+-			} catch( e ) {}
++					( function add( args ) {
++						jQuery.each( args, function( _, arg ) {
++							if ( isFunction( arg ) ) {
++								if ( !options.unique || !self.has( arg ) ) {
++									list.push( arg );
++								}
++							} else if ( arg && arg.length && toType( arg ) !== "string" ) {
+ 
+-			// Make sure we set the data so it isn't changed later
+-			jQuery.data( elem, key, data );
++								// Inspect recursively
++								add( arg );
++							}
++						} );
++					} )( arguments );
+ 
+-		} else {
+-			data = undefined;
+-		}
+-	}
++					if ( memory && !firing ) {
++						fire();
++					}
++				}
++				return this;
++			},
+ 
+-	return data;
+-}
++			// Remove a callback from the list
++			remove: function() {
++				jQuery.each( arguments, function( _, arg ) {
++					var index;
++					while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
++						list.splice( index, 1 );
++
++						// Handle firing indexes
++						if ( index <= firingIndex ) {
++							firingIndex--;
++						}
++					}
++				} );
++				return this;
++			},
+ 
+-// checks a cache object for emptiness
+-function isEmptyDataObject( obj ) {
+-	var name;
+-	for ( name in obj ) {
++			// Check if a given callback is in the list.
++			// If no argument is given, return whether or not list has callbacks attached.
++			has: function( fn ) {
++				return fn ?
++					jQuery.inArray( fn, list ) > -1 :
++					list.length > 0;
++			},
+ 
+-		// if the public data object is empty, the private is still empty
+-		if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
+-			continue;
+-		}
+-		if ( name !== "toJSON" ) {
+-			return false;
+-		}
+-	}
++			// Remove all callbacks from the list
++			empty: function() {
++				if ( list ) {
++					list = [];
++				}
++				return this;
++			},
+ 
+-	return true;
+-}
+-jQuery.extend({
+-	queue: function( elem, type, data ) {
+-		var queue;
++			// Disable .fire and .add
++			// Abort any current/pending executions
++			// Clear all callbacks and values
++			disable: function() {
++				locked = queue = [];
++				list = memory = "";
++				return this;
++			},
++			disabled: function() {
++				return !list;
++			},
+ 
+-		if ( elem ) {
+-			type = ( type || "fx" ) + "queue";
+-			queue = jQuery._data( elem, type );
++			// Disable .fire
++			// Also disable .add unless we have memory (since it would have no effect)
++			// Abort any pending executions
++			lock: function() {
++				locked = queue = [];
++				if ( !memory && !firing ) {
++					list = memory = "";
++				}
++				return this;
++			},
++			locked: function() {
++				return !!locked;
++			},
+ 
+-			// Speed up dequeue by getting out quickly if this is just a lookup
+-			if ( data ) {
+-				if ( !queue || jQuery.isArray(data) ) {
+-					queue = jQuery._data( elem, type, jQuery.makeArray(data) );
+-				} else {
+-					queue.push( data );
++			// Call all callbacks with the given context and arguments
++			fireWith: function( context, args ) {
++				if ( !locked ) {
++					args = args || [];
++					args = [ context, args.slice ? args.slice() : args ];
++					queue.push( args );
++					if ( !firing ) {
++						fire();
++					}
+ 				}
++				return this;
++			},
++
++			// Call all the callbacks with the given arguments
++			fire: function() {
++				self.fireWith( this, arguments );
++				return this;
++			},
++
++			// To know if the callbacks have already been called at least once
++			fired: function() {
++				return !!fired;
+ 			}
+-			return queue || [];
+-		}
+-	},
++		};
+ 
+-	dequeue: function( elem, type ) {
+-		type = type || "fx";
++	return self;
++};
+ 
+-		var queue = jQuery.queue( elem, type ),
+-			startLength = queue.length,
+-			fn = queue.shift(),
+-			hooks = jQuery._queueHooks( elem, type ),
+-			next = function() {
+-				jQuery.dequeue( elem, type );
+-			};
+ 
+-		// If the fx queue is dequeued, always remove the progress sentinel
+-		if ( fn === "inprogress" ) {
+-			fn = queue.shift();
+-			startLength--;
+-		}
++function Identity( v ) {
++	return v;
++}
++function Thrower( ex ) {
++	throw ex;
++}
+ 
+-		if ( fn ) {
++function adoptValue( value, resolve, reject, noValue ) {
++	var method;
+ 
+-			// Add a progress sentinel to prevent the fx queue from being
+-			// automatically dequeued
+-			if ( type === "fx" ) {
+-				queue.unshift( "inprogress" );
+-			}
++	try {
+ 
+-			// clear up the last queue stop function
+-			delete hooks.stop;
+-			fn.call( elem, next, hooks );
+-		}
++		// Check for promise aspect first to privilege synchronous behavior
++		if ( value && isFunction( ( method = value.promise ) ) ) {
++			method.call( value ).done( resolve ).fail( reject );
+ 
+-		if ( !startLength && hooks ) {
+-			hooks.empty.fire();
++		// Other thenables
++		} else if ( value && isFunction( ( method = value.then ) ) ) {
++			method.call( value, resolve, reject );
++
++		// Other non-thenables
++		} else {
++
++			// Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer:
++			// * false: [ value ].slice( 0 ) => resolve( value )
++			// * true: [ value ].slice( 1 ) => resolve()
++			resolve.apply( undefined, [ value ].slice( noValue ) );
+ 		}
+-	},
+ 
+-	// not intended for public consumption - generates a queueHooks object, or returns the current one
+-	_queueHooks: function( elem, type ) {
+-		var key = type + "queueHooks";
+-		return jQuery._data( elem, key ) || jQuery._data( elem, key, {
+-			empty: jQuery.Callbacks("once memory").add(function() {
+-				jQuery._removeData( elem, type + "queue" );
+-				jQuery._removeData( elem, key );
+-			})
+-		});
++	// For Promises/A+, convert exceptions into rejections
++	// Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in
++	// Deferred#then to conditionally suppress rejection.
++	} catch ( value ) {
++
++		// Support: Android 4.0 only
++		// Strict mode functions invoked without .call/.apply get global-object context
++		reject.apply( undefined, [ value ] );
+ 	}
+-});
++}
+ 
+-jQuery.fn.extend({
+-	queue: function( type, data ) {
+-		var setter = 2;
++jQuery.extend( {
+ 
+-		if ( typeof type !== "string" ) {
+-			data = type;
+-			type = "fx";
+-			setter--;
+-		}
++	Deferred: function( func ) {
++		var tuples = [
+ 
+-		if ( arguments.length < setter ) {
+-			return jQuery.queue( this[0], type );
+-		}
++				// action, add listener, callbacks,
++				// ... .then handlers, argument index, [final state]
++				[ "notify", "progress", jQuery.Callbacks( "memory" ),
++					jQuery.Callbacks( "memory" ), 2 ],
++				[ "resolve", "done", jQuery.Callbacks( "once memory" ),
++					jQuery.Callbacks( "once memory" ), 0, "resolved" ],
++				[ "reject", "fail", jQuery.Callbacks( "once memory" ),
++					jQuery.Callbacks( "once memory" ), 1, "rejected" ]
++			],
++			state = "pending",
++			promise = {
++				state: function() {
++					return state;
++				},
++				always: function() {
++					deferred.done( arguments ).fail( arguments );
++					return this;
++				},
++				"catch": function( fn ) {
++					return promise.then( null, fn );
++				},
+ 
+-		return data === undefined ?
+-			this :
+-			this.each(function() {
+-				var queue = jQuery.queue( this, type, data );
++				// Keep pipe for back-compat
++				pipe: function( /* fnDone, fnFail, fnProgress */ ) {
++					var fns = arguments;
+ 
+-				// ensure a hooks for this queue
+-				jQuery._queueHooks( this, type );
++					return jQuery.Deferred( function( newDefer ) {
++						jQuery.each( tuples, function( i, tuple ) {
+ 
+-				if ( type === "fx" && queue[0] !== "inprogress" ) {
+-					jQuery.dequeue( this, type );
+-				}
+-			});
+-	},
+-	dequeue: function( type ) {
+-		return this.each(function() {
+-			jQuery.dequeue( this, type );
+-		});
+-	},
+-	// Based off of the plugin by Clint Helfers, with permission.
+-	// http://blindsignals.com/index.php/2009/07/jquery-delay/
+-	delay: function( time, type ) {
+-		time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
+-		type = type || "fx";
++							// Map tuples (progress, done, fail) to arguments (done, fail, progress)
++							var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ];
+ 
+-		return this.queue( type, function( next, hooks ) {
+-			var timeout = setTimeout( next, time );
+-			hooks.stop = function() {
+-				clearTimeout( timeout );
+-			};
+-		});
+-	},
+-	clearQueue: function( type ) {
+-		return this.queue( type || "fx", [] );
+-	},
+-	// Get a promise resolved when queues of a certain type
+-	// are emptied (fx is the type by default)
+-	promise: function( type, obj ) {
+-		var tmp,
+-			count = 1,
+-			defer = jQuery.Deferred(),
+-			elements = this,
+-			i = this.length,
+-			resolve = function() {
+-				if ( !( --count ) ) {
+-					defer.resolveWith( elements, [ elements ] );
+-				}
+-			};
++							// deferred.progress(function() { bind to newDefer or newDefer.notify })
++							// deferred.done(function() { bind to newDefer or newDefer.resolve })
++							// deferred.fail(function() { bind to newDefer or newDefer.reject })
++							deferred[ tuple[ 1 ] ]( function() {
++								var returned = fn && fn.apply( this, arguments );
++								if ( returned && isFunction( returned.promise ) ) {
++									returned.promise()
++										.progress( newDefer.notify )
++										.done( newDefer.resolve )
++										.fail( newDefer.reject );
++								} else {
++									newDefer[ tuple[ 0 ] + "With" ](
++										this,
++										fn ? [ returned ] : arguments
++									);
++								}
++							} );
++						} );
++						fns = null;
++					} ).promise();
++				},
++				then: function( onFulfilled, onRejected, onProgress ) {
++					var maxDepth = 0;
++					function resolve( depth, deferred, handler, special ) {
++						return function() {
++							var that = this,
++								args = arguments,
++								mightThrow = function() {
++									var returned, then;
++
++									// Support: Promises/A+ section 2.3.3.3.3
++									// https://promisesaplus.com/#point-59
++									// Ignore double-resolution attempts
++									if ( depth < maxDepth ) {
++										return;
++									}
+ 
+-		if ( typeof type !== "string" ) {
+-			obj = type;
+-			type = undefined;
+-		}
+-		type = type || "fx";
++									returned = handler.apply( that, args );
+ 
+-		while( i-- ) {
+-			tmp = jQuery._data( elements[ i ], type + "queueHooks" );
+-			if ( tmp && tmp.empty ) {
+-				count++;
+-				tmp.empty.add( resolve );
+-			}
+-		}
+-		resolve();
+-		return defer.promise( obj );
+-	}
+-});
+-var nodeHook, boolHook,
+-	rclass = /[\t\r\n\f]/g,
+-	rreturn = /\r/g,
+-	rfocusable = /^(?:input|select|textarea|button|object)$/i,
+-	rclickable = /^(?:a|area)$/i,
+-	ruseDefault = /^(?:checked|selected)$/i,
+-	getSetAttribute = jQuery.support.getSetAttribute,
+-	getSetInput = jQuery.support.input;
+-
+-jQuery.fn.extend({
+-	attr: function( name, value ) {
+-		return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );
+-	},
++									// Support: Promises/A+ section 2.3.1
++									// https://promisesaplus.com/#point-48
++									if ( returned === deferred.promise() ) {
++										throw new TypeError( "Thenable self-resolution" );
++									}
+ 
+-	removeAttr: function( name ) {
+-		return this.each(function() {
+-			jQuery.removeAttr( this, name );
+-		});
+-	},
++									// Support: Promises/A+ sections 2.3.3.1, 3.5
++									// https://promisesaplus.com/#point-54
++									// https://promisesaplus.com/#point-75
++									// Retrieve `then` only once
++									then = returned &&
++
++										// Support: Promises/A+ section 2.3.4
++										// https://promisesaplus.com/#point-64
++										// Only check objects and functions for thenability
++										( typeof returned === "object" ||
++											typeof returned === "function" ) &&
++										returned.then;
++
++									// Handle a returned thenable
++									if ( isFunction( then ) ) {
++
++										// Special processors (notify) just wait for resolution
++										if ( special ) {
++											then.call(
++												returned,
++												resolve( maxDepth, deferred, Identity, special ),
++												resolve( maxDepth, deferred, Thrower, special )
++											);
++
++										// Normal processors (resolve) also hook into progress
++										} else {
++
++											// ...and disregard older resolution values
++											maxDepth++;
++
++											then.call(
++												returned,
++												resolve( maxDepth, deferred, Identity, special ),
++												resolve( maxDepth, deferred, Thrower, special ),
++												resolve( maxDepth, deferred, Identity,
++													deferred.notifyWith )
++											);
++										}
++
++									// Handle all other returned values
++									} else {
++
++										// Only substitute handlers pass on context
++										// and multiple values (non-spec behavior)
++										if ( handler !== Identity ) {
++											that = undefined;
++											args = [ returned ];
++										}
++
++										// Process the value(s)
++										// Default process is resolve
++										( special || deferred.resolveWith )( that, args );
++									}
++								},
++
++								// Only normal processors (resolve) catch and reject exceptions
++								process = special ?
++									mightThrow :
++									function() {
++										try {
++											mightThrow();
++										} catch ( e ) {
++
++											if ( jQuery.Deferred.exceptionHook ) {
++												jQuery.Deferred.exceptionHook( e,
++													process.stackTrace );
++											}
++
++											// Support: Promises/A+ section 2.3.3.3.4.1
++											// https://promisesaplus.com/#point-61
++											// Ignore post-resolution exceptions
++											if ( depth + 1 >= maxDepth ) {
++
++												// Only substitute handlers pass on context
++												// and multiple values (non-spec behavior)
++												if ( handler !== Thrower ) {
++													that = undefined;
++													args = [ e ];
++												}
++
++												deferred.rejectWith( that, args );
++											}
++										}
++									};
++
++							// Support: Promises/A+ section 2.3.3.3.1
++							// https://promisesaplus.com/#point-57
++							// Re-resolve promises immediately to dodge false rejection from
++							// subsequent errors
++							if ( depth ) {
++								process();
++							} else {
+ 
+-	prop: function( name, value ) {
+-		return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );
+-	},
++								// Call an optional hook to record the stack, in case of exception
++								// since it's otherwise lost when execution goes async
++								if ( jQuery.Deferred.getStackHook ) {
++									process.stackTrace = jQuery.Deferred.getStackHook();
++								}
++								window.setTimeout( process );
++							}
++						};
++					}
+ 
+-	removeProp: function( name ) {
+-		name = jQuery.propFix[ name ] || name;
+-		return this.each(function() {
+-			// try/catch handles cases where IE balks (such as removing a property on window)
+-			try {
+-				this[ name ] = undefined;
+-				delete this[ name ];
+-			} catch( e ) {}
+-		});
+-	},
++					return jQuery.Deferred( function( newDefer ) {
++
++						// progress_handlers.add( ... )
++						tuples[ 0 ][ 3 ].add(
++							resolve(
++								0,
++								newDefer,
++								isFunction( onProgress ) ?
++									onProgress :
++									Identity,
++								newDefer.notifyWith
++							)
++						);
++
++						// fulfilled_handlers.add( ... )
++						tuples[ 1 ][ 3 ].add(
++							resolve(
++								0,
++								newDefer,
++								isFunction( onFulfilled ) ?
++									onFulfilled :
++									Identity
++							)
++						);
++
++						// rejected_handlers.add( ... )
++						tuples[ 2 ][ 3 ].add(
++							resolve(
++								0,
++								newDefer,
++								isFunction( onRejected ) ?
++									onRejected :
++									Thrower
++							)
++						);
++					} ).promise();
++				},
+ 
+-	addClass: function( value ) {
+-		var classes, elem, cur, clazz, j,
+-			i = 0,
+-			len = this.length,
+-			proceed = typeof value === "string" && value;
++				// Get a promise for this deferred
++				// If obj is provided, the promise aspect is added to the object
++				promise: function( obj ) {
++					return obj != null ? jQuery.extend( obj, promise ) : promise;
++				}
++			},
++			deferred = {};
+ 
+-		if ( jQuery.isFunction( value ) ) {
+-			return this.each(function( j ) {
+-				jQuery( this ).addClass( value.call( this, j, this.className ) );
+-			});
+-		}
++		// Add list-specific methods
++		jQuery.each( tuples, function( i, tuple ) {
++			var list = tuple[ 2 ],
++				stateString = tuple[ 5 ];
+ 
+-		if ( proceed ) {
+-			// The disjunction here is for better compressibility (see removeClass)
+-			classes = ( value || "" ).match( core_rnotwhite ) || [];
++			// promise.progress = list.add
++			// promise.done = list.add
++			// promise.fail = list.add
++			promise[ tuple[ 1 ] ] = list.add;
+ 
+-			for ( ; i < len; i++ ) {
+-				elem = this[ i ];
+-				cur = elem.nodeType === 1 && ( elem.className ?
+-					( " " + elem.className + " " ).replace( rclass, " " ) :
+-					" "
+-				);
++			// Handle state
++			if ( stateString ) {
++				list.add(
++					function() {
+ 
+-				if ( cur ) {
+-					j = 0;
+-					while ( (clazz = classes[j++]) ) {
+-						if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
+-							cur += clazz + " ";
+-						}
+-					}
+-					elem.className = jQuery.trim( cur );
++						// state = "resolved" (i.e., fulfilled)
++						// state = "rejected"
++						state = stateString;
++					},
+ 
+-				}
++					// rejected_callbacks.disable
++					// fulfilled_callbacks.disable
++					tuples[ 3 - i ][ 2 ].disable,
++
++					// rejected_handlers.disable
++					// fulfilled_handlers.disable
++					tuples[ 3 - i ][ 3 ].disable,
++
++					// progress_callbacks.lock
++					tuples[ 0 ][ 2 ].lock,
++
++					// progress_handlers.lock
++					tuples[ 0 ][ 3 ].lock
++				);
+ 			}
+-		}
+ 
+-		return this;
+-	},
++			// progress_handlers.fire
++			// fulfilled_handlers.fire
++			// rejected_handlers.fire
++			list.add( tuple[ 3 ].fire );
+ 
+-	removeClass: function( value ) {
+-		var classes, elem, cur, clazz, j,
+-			i = 0,
+-			len = this.length,
+-			proceed = arguments.length === 0 || typeof value === "string" && value;
++			// deferred.notify = function() { deferred.notifyWith(...) }
++			// deferred.resolve = function() { deferred.resolveWith(...) }
++			// deferred.reject = function() { deferred.rejectWith(...) }
++			deferred[ tuple[ 0 ] ] = function() {
++				deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments );
++				return this;
++			};
+ 
+-		if ( jQuery.isFunction( value ) ) {
+-			return this.each(function( j ) {
+-				jQuery( this ).removeClass( value.call( this, j, this.className ) );
+-			});
+-		}
+-		if ( proceed ) {
+-			classes = ( value || "" ).match( core_rnotwhite ) || [];
++			// deferred.notifyWith = list.fireWith
++			// deferred.resolveWith = list.fireWith
++			// deferred.rejectWith = list.fireWith
++			deferred[ tuple[ 0 ] + "With" ] = list.fireWith;
++		} );
+ 
+-			for ( ; i < len; i++ ) {
+-				elem = this[ i ];
+-				// This expression is here for better compressibility (see addClass)
+-				cur = elem.nodeType === 1 && ( elem.className ?
+-					( " " + elem.className + " " ).replace( rclass, " " ) :
+-					""
+-				);
++		// Make the deferred a promise
++		promise.promise( deferred );
+ 
+-				if ( cur ) {
+-					j = 0;
+-					while ( (clazz = classes[j++]) ) {
+-						// Remove *all* instances
+-						while ( cur.indexOf( " " + clazz + " " ) >= 0 ) {
+-							cur = cur.replace( " " + clazz + " ", " " );
+-						}
+-					}
+-					elem.className = value ? jQuery.trim( cur ) : "";
+-				}
+-			}
++		// Call given func if any
++		if ( func ) {
++			func.call( deferred, deferred );
+ 		}
+ 
+-		return this;
++		// All done!
++		return deferred;
+ 	},
+ 
+-	toggleClass: function( value, stateVal ) {
+-		var type = typeof value;
++	// Deferred helper
++	when: function( singleValue ) {
++		var
+ 
+-		if ( typeof stateVal === "boolean" && type === "string" ) {
+-			return stateVal ? this.addClass( value ) : this.removeClass( value );
+-		}
++			// count of uncompleted subordinates
++			remaining = arguments.length,
+ 
+-		if ( jQuery.isFunction( value ) ) {
+-			return this.each(function( i ) {
+-				jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
+-			});
+-		}
++			// count of unprocessed arguments
++			i = remaining,
+ 
+-		return this.each(function() {
+-			if ( type === "string" ) {
+-				// toggle individual class names
+-				var className,
+-					i = 0,
+-					self = jQuery( this ),
+-					classNames = value.match( core_rnotwhite ) || [];
++			// subordinate fulfillment data
++			resolveContexts = Array( i ),
++			resolveValues = slice.call( arguments ),
+ 
+-				while ( (className = classNames[ i++ ]) ) {
+-					// check each className given, space separated list
+-					if ( self.hasClass( className ) ) {
+-						self.removeClass( className );
+-					} else {
+-						self.addClass( className );
++			// the master Deferred
++			master = jQuery.Deferred(),
++
++			// subordinate callback factory
++			updateFunc = function( i ) {
++				return function( value ) {
++					resolveContexts[ i ] = this;
++					resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
++					if ( !( --remaining ) ) {
++						master.resolveWith( resolveContexts, resolveValues );
+ 					}
+-				}
++				};
++			};
+ 
+-			// Toggle whole class name
+-			} else if ( type === core_strundefined || type === "boolean" ) {
+-				if ( this.className ) {
+-					// store className if set
+-					jQuery._data( this, "__className__", this.className );
+-				}
++		// Single- and empty arguments are adopted like Promise.resolve
++		if ( remaining <= 1 ) {
++			adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject,
++				!remaining );
+ 
+-				// If the element has a class name or if we're passed "false",
+-				// then remove the whole classname (if there was one, the above saved it).
+-				// Otherwise bring back whatever was previously saved (if anything),
+-				// falling back to the empty string if nothing was stored.
+-				this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
+-			}
+-		});
+-	},
++			// Use .then() to unwrap secondary thenables (cf. gh-3000)
++			if ( master.state() === "pending" ||
++				isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) {
+ 
+-	hasClass: function( selector ) {
+-		var className = " " + selector + " ",
+-			i = 0,
+-			l = this.length;
+-		for ( ; i < l; i++ ) {
+-			if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) {
+-				return true;
++				return master.then();
+ 			}
+ 		}
+ 
+-		return false;
+-	},
+-
+-	val: function( value ) {
+-		var ret, hooks, isFunction,
+-			elem = this[0];
++		// Multiple arguments are aggregated like Promise.all array elements
++		while ( i-- ) {
++			adoptValue( resolveValues[ i ], updateFunc( i ), master.reject );
++		}
+ 
+-		if ( !arguments.length ) {
+-			if ( elem ) {
+-				hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
++		return master.promise();
++	}
++} );
+ 
+-				if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
+-					return ret;
+-				}
+ 
+-				ret = elem.value;
++// These usually indicate a programmer mistake during development,
++// warn about them ASAP rather than swallowing them by default.
++var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;
+ 
+-				return typeof ret === "string" ?
+-					// handle most common string cases
+-					ret.replace(rreturn, "") :
+-					// handle cases where value is null/undef or number
+-					ret == null ? "" : ret;
+-			}
++jQuery.Deferred.exceptionHook = function( error, stack ) {
+ 
+-			return;
+-		}
++	// Support: IE 8 - 9 only
++	// Console exists when dev tools are open, which can happen at any time
++	if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) {
++		window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack );
++	}
++};
+ 
+-		isFunction = jQuery.isFunction( value );
+ 
+-		return this.each(function( i ) {
+-			var val;
+ 
+-			if ( this.nodeType !== 1 ) {
+-				return;
+-			}
+ 
+-			if ( isFunction ) {
+-				val = value.call( this, i, jQuery( this ).val() );
+-			} else {
+-				val = value;
+-			}
++jQuery.readyException = function( error ) {
++	window.setTimeout( function() {
++		throw error;
++	} );
++};
+ 
+-			// Treat null/undefined as ""; convert numbers to string
+-			if ( val == null ) {
+-				val = "";
+-			} else if ( typeof val === "number" ) {
+-				val += "";
+-			} else if ( jQuery.isArray( val ) ) {
+-				val = jQuery.map(val, function ( value ) {
+-					return value == null ? "" : value + "";
+-				});
+-			}
+ 
+-			hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
+ 
+-			// If set returns undefined, fall back to normal setting
+-			if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
+-				this.value = val;
+-			}
+-		});
+-	}
+-});
+ 
+-jQuery.extend({
+-	valHooks: {
+-		option: {
+-			get: function( elem ) {
+-				// Use proper attribute retrieval(#6932, #12072)
+-				var val = jQuery.find.attr( elem, "value" );
+-				return val != null ?
+-					val :
+-					elem.text;
+-			}
+-		},
+-		select: {
+-			get: function( elem ) {
+-				var value, option,
+-					options = elem.options,
+-					index = elem.selectedIndex,
+-					one = elem.type === "select-one" || index < 0,
+-					values = one ? null : [],
+-					max = one ? index + 1 : options.length,
+-					i = index < 0 ?
+-						max :
+-						one ? index : 0;
++// The deferred used on DOM ready
++var readyList = jQuery.Deferred();
+ 
+-				// Loop through all the selected options
+-				for ( ; i < max; i++ ) {
+-					option = options[ i ];
++jQuery.fn.ready = function( fn ) {
+ 
+-					// oldIE doesn't update selected after form reset (#2551)
+-					if ( ( option.selected || i === index ) &&
+-							// Don't return options that are disabled or in a disabled optgroup
+-							( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) &&
+-							( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {
++	readyList
++		.then( fn )
+ 
+-						// Get the specific value for the option
+-						value = jQuery( option ).val();
++		// Wrap jQuery.readyException in a function so that the lookup
++		// happens at the time of error handling instead of callback
++		// registration.
++		.catch( function( error ) {
++			jQuery.readyException( error );
++		} );
+ 
+-						// We don't need an array for one selects
+-						if ( one ) {
+-							return value;
+-						}
++	return this;
++};
+ 
+-						// Multi-Selects return an array
+-						values.push( value );
+-					}
+-				}
++jQuery.extend( {
+ 
+-				return values;
+-			},
++	// Is the DOM ready to be used? Set to true once it occurs.
++	isReady: false,
+ 
+-			set: function( elem, value ) {
+-				var optionSet, option,
+-					options = elem.options,
+-					values = jQuery.makeArray( value ),
+-					i = options.length;
++	// A counter to track how many items to wait for before
++	// the ready event fires. See #6781
++	readyWait: 1,
+ 
+-				while ( i-- ) {
+-					option = options[ i ];
+-					if ( (option.selected = jQuery.inArray( jQuery(option).val(), values ) >= 0) ) {
+-						optionSet = true;
+-					}
+-				}
++	// Handle when the DOM is ready
++	ready: function( wait ) {
+ 
+-				// force browsers to behave consistently when non-matching value is set
+-				if ( !optionSet ) {
+-					elem.selectedIndex = -1;
+-				}
+-				return values;
+-			}
++		// Abort if there are pending holds or we're already ready
++		if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
++			return;
+ 		}
+-	},
+ 
+-	attr: function( elem, name, value ) {
+-		var hooks, ret,
+-			nType = elem.nodeType;
++		// Remember that the DOM is ready
++		jQuery.isReady = true;
+ 
+-		// don't get/set attributes on text, comment and attribute nodes
+-		if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
++		// If a normal DOM Ready event fired, decrement, and wait if need be
++		if ( wait !== true && --jQuery.readyWait > 0 ) {
+ 			return;
+ 		}
+ 
+-		// Fallback to prop when attributes are not supported
+-		if ( typeof elem.getAttribute === core_strundefined ) {
+-			return jQuery.prop( elem, name, value );
+-		}
+-
+-		// All attributes are lowercase
+-		// Grab necessary hook if one is defined
+-		if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
+-			name = name.toLowerCase();
+-			hooks = jQuery.attrHooks[ name ] ||
+-				( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook );
+-		}
++		// If there are functions bound, to execute
++		readyList.resolveWith( document, [ jQuery ] );
++	}
++} );
+ 
+-		if ( value !== undefined ) {
++jQuery.ready.then = readyList.then;
+ 
+-			if ( value === null ) {
+-				jQuery.removeAttr( elem, name );
++// The ready event handler and self cleanup method
++function completed() {
++	document.removeEventListener( "DOMContentLoaded", completed );
++	window.removeEventListener( "load", completed );
++	jQuery.ready();
++}
+ 
+-			} else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
+-				return ret;
++// Catch cases where $(document).ready() is called
++// after the browser event has already occurred.
++// Support: IE <=9 - 10 only
++// Older IE sometimes signals "interactive" too soon
++if ( document.readyState === "complete" ||
++	( document.readyState !== "loading" && !document.documentElement.doScroll ) ) {
+ 
+-			} else {
+-				elem.setAttribute( name, value + "" );
+-				return value;
+-			}
++	// Handle it asynchronously to allow scripts the opportunity to delay ready
++	window.setTimeout( jQuery.ready );
+ 
+-		} else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
+-			return ret;
++} else {
+ 
+-		} else {
+-			ret = jQuery.find.attr( elem, name );
++	// Use the handy event callback
++	document.addEventListener( "DOMContentLoaded", completed );
+ 
+-			// Non-existent attributes return null, we normalize to undefined
+-			return ret == null ?
+-				undefined :
+-				ret;
+-		}
+-	},
++	// A fallback to window.onload, that will always work
++	window.addEventListener( "load", completed );
++}
+ 
+-	removeAttr: function( elem, value ) {
+-		var name, propName,
+-			i = 0,
+-			attrNames = value && value.match( core_rnotwhite );
+ 
+-		if ( attrNames && elem.nodeType === 1 ) {
+-			while ( (name = attrNames[i++]) ) {
+-				propName = jQuery.propFix[ name ] || name;
+-
+-				// Boolean attributes get special treatment (#10870)
+-				if ( jQuery.expr.match.bool.test( name ) ) {
+-					// Set corresponding property to false
+-					if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) {
+-						elem[ propName ] = false;
+-					// Support: IE<9
+-					// Also clear defaultChecked/defaultSelected (if appropriate)
+-					} else {
+-						elem[ jQuery.camelCase( "default-" + name ) ] =
+-							elem[ propName ] = false;
+-					}
+ 
+-				// See #9699 for explanation of this approach (setting first, then removal)
+-				} else {
+-					jQuery.attr( elem, name, "" );
+-				}
+ 
+-				elem.removeAttribute( getSetAttribute ? name : propName );
+-			}
+-		}
+-	},
++// Multifunctional method to get and set values of a collection
++// The value/s can optionally be executed if it's a function
++var access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
++	var i = 0,
++		len = elems.length,
++		bulk = key == null;
+ 
+-	attrHooks: {
+-		type: {
+-			set: function( elem, value ) {
+-				if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
+-					// Setting the type on a radio button after the value resets the value in IE6-9
+-					// Reset value to default in case type is set after value during creation
+-					var val = elem.value;
+-					elem.setAttribute( "type", value );
+-					if ( val ) {
+-						elem.value = val;
+-					}
+-					return value;
+-				}
+-			}
++	// Sets many values
++	if ( toType( key ) === "object" ) {
++		chainable = true;
++		for ( i in key ) {
++			access( elems, fn, i, key[ i ], true, emptyGet, raw );
+ 		}
+-	},
+-
+-	propFix: {
+-		"for": "htmlFor",
+-		"class": "className"
+-	},
+ 
+-	prop: function( elem, name, value ) {
+-		var ret, hooks, notxml,
+-			nType = elem.nodeType;
++	// Sets one value
++	} else if ( value !== undefined ) {
++		chainable = true;
+ 
+-		// don't get/set properties on text, comment and attribute nodes
+-		if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+-			return;
++		if ( !isFunction( value ) ) {
++			raw = true;
+ 		}
+ 
+-		notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+-
+-		if ( notxml ) {
+-			// Fix name and attach hooks
+-			name = jQuery.propFix[ name ] || name;
+-			hooks = jQuery.propHooks[ name ];
+-		}
++		if ( bulk ) {
+ 
+-		if ( value !== undefined ) {
+-			return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ?
+-				ret :
+-				( elem[ name ] = value );
++			// Bulk operations run against the entire set
++			if ( raw ) {
++				fn.call( elems, value );
++				fn = null;
+ 
+-		} else {
+-			return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ?
+-				ret :
+-				elem[ name ];
++			// ...except when executing function values
++			} else {
++				bulk = fn;
++				fn = function( elem, key, value ) {
++					return bulk.call( jQuery( elem ), value );
++				};
++			}
+ 		}
+-	},
+ 
+-	propHooks: {
+-		tabIndex: {
+-			get: function( elem ) {
+-				// elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
+-				// http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
+-				// Use proper attribute retrieval(#12072)
+-				var tabindex = jQuery.find.attr( elem, "tabindex" );
+-
+-				return tabindex ?
+-					parseInt( tabindex, 10 ) :
+-					rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
+-						0 :
+-						-1;
++		if ( fn ) {
++			for ( ; i < len; i++ ) {
++				fn(
++					elems[ i ], key, raw ?
++					value :
++					value.call( elems[ i ], i, fn( elems[ i ], key ) )
++				);
+ 			}
+ 		}
+ 	}
+-});
+-
+-// Hooks for boolean attributes
+-boolHook = {
+-	set: function( elem, value, name ) {
+-		if ( value === false ) {
+-			// Remove boolean attributes when set to false
+-			jQuery.removeAttr( elem, name );
+-		} else if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) {
+-			// IE<8 needs the *property* name
+-			elem.setAttribute( !getSetAttribute && jQuery.propFix[ name ] || name, name );
+ 
+-		// Use defaultChecked and defaultSelected for oldIE
+-		} else {
+-			elem[ jQuery.camelCase( "default-" + name ) ] = elem[ name ] = true;
+-		}
++	if ( chainable ) {
++		return elems;
++	}
+ 
+-		return name;
++	// Gets
++	if ( bulk ) {
++		return fn.call( elems );
+ 	}
++
++	return len ? fn( elems[ 0 ], key ) : emptyGet;
+ };
+-jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) {
+-	var getter = jQuery.expr.attrHandle[ name ] || jQuery.find.attr;
+-
+-	jQuery.expr.attrHandle[ name ] = getSetInput && getSetAttribute || !ruseDefault.test( name ) ?
+-		function( elem, name, isXML ) {
+-			var fn = jQuery.expr.attrHandle[ name ],
+-				ret = isXML ?
+-					undefined :
+-					/* jshint eqeqeq: false */
+-					(jQuery.expr.attrHandle[ name ] = undefined) !=
+-						getter( elem, name, isXML ) ?
+-
+-						name.toLowerCase() :
+-						null;
+-			jQuery.expr.attrHandle[ name ] = fn;
+-			return ret;
+-		} :
+-		function( elem, name, isXML ) {
+-			return isXML ?
+-				undefined :
+-				elem[ jQuery.camelCase( "default-" + name ) ] ?
+-					name.toLowerCase() :
+-					null;
+-		};
+-});
+ 
+-// fix oldIE attroperties
+-if ( !getSetInput || !getSetAttribute ) {
+-	jQuery.attrHooks.value = {
+-		set: function( elem, value, name ) {
+-			if ( jQuery.nodeName( elem, "input" ) ) {
+-				// Does not return so that setAttribute is also used
+-				elem.defaultValue = value;
+-			} else {
+-				// Use nodeHook if defined (#1954); otherwise setAttribute is fine
+-				return nodeHook && nodeHook.set( elem, value, name );
+-			}
+-		}
+-	};
++
++// Matches dashed string for camelizing
++var rmsPrefix = /^-ms-/,
++	rdashAlpha = /-([a-z])/g;
++
++// Used by camelCase as callback to replace()
++function fcamelCase( all, letter ) {
++	return letter.toUpperCase();
+ }
+ 
+-// IE6/7 do not support getting/setting some attributes with get/setAttribute
+-if ( !getSetAttribute ) {
+-
+-	// Use this for any attribute in IE6/7
+-	// This fixes almost every IE6/7 issue
+-	nodeHook = {
+-		set: function( elem, value, name ) {
+-			// Set the existing or create a new attribute node
+-			var ret = elem.getAttributeNode( name );
+-			if ( !ret ) {
+-				elem.setAttributeNode(
+-					(ret = elem.ownerDocument.createAttribute( name ))
+-				);
+-			}
++// Convert dashed to camelCase; used by the css and data modules
++// Support: IE <=9 - 11, Edge 12 - 15
++// Microsoft forgot to hump their vendor prefix (#9572)
++function camelCase( string ) {
++	return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
++}
++var acceptData = function( owner ) {
++
++	// Accepts only:
++	//  - Node
++	//    - Node.ELEMENT_NODE
++	//    - Node.DOCUMENT_NODE
++	//  - Object
++	//    - Any
++	return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType );
++};
+ 
+-			ret.value = value += "";
+ 
+-			// Break association with cloned elements by also using setAttribute (#9646)
+-			return name === "value" || value === elem.getAttribute( name ) ?
+-				value :
+-				undefined;
+-		}
+-	};
+-	jQuery.expr.attrHandle.id = jQuery.expr.attrHandle.name = jQuery.expr.attrHandle.coords =
+-		// Some attributes are constructed with empty-string values when not defined
+-		function( elem, name, isXML ) {
+-			var ret;
+-			return isXML ?
+-				undefined :
+-				(ret = elem.getAttributeNode( name )) && ret.value !== "" ?
+-					ret.value :
+-					null;
+-		};
+-	jQuery.valHooks.button = {
+-		get: function( elem, name ) {
+-			var ret = elem.getAttributeNode( name );
+-			return ret && ret.specified ?
+-				ret.value :
+-				undefined;
+-		},
+-		set: nodeHook.set
+-	};
+ 
+-	// Set contenteditable to false on removals(#10429)
+-	// Setting to empty string throws an error as an invalid value
+-	jQuery.attrHooks.contenteditable = {
+-		set: function( elem, value, name ) {
+-			nodeHook.set( elem, value === "" ? false : value, name );
+-		}
+-	};
+ 
+-	// Set width and height to auto instead of 0 on empty string( Bug #8150 )
+-	// This is for removals
+-	jQuery.each([ "width", "height" ], function( i, name ) {
+-		jQuery.attrHooks[ name ] = {
+-			set: function( elem, value ) {
+-				if ( value === "" ) {
+-					elem.setAttribute( name, "auto" );
+-					return value;
+-				}
+-			}
+-		};
+-	});
++function Data() {
++	this.expando = jQuery.expando + Data.uid++;
+ }
+ 
++Data.uid = 1;
+ 
+-// Some attributes require a special call on IE
+-// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
+-if ( !jQuery.support.hrefNormalized ) {
+-	// href/src property should get the full normalized URL (#10299/#12915)
+-	jQuery.each([ "href", "src" ], function( i, name ) {
+-		jQuery.propHooks[ name ] = {
+-			get: function( elem ) {
+-				return elem.getAttribute( name, 4 );
+-			}
+-		};
+-	});
+-}
++Data.prototype = {
+ 
+-if ( !jQuery.support.style ) {
+-	jQuery.attrHooks.style = {
+-		get: function( elem ) {
+-			// Return undefined in the case of empty string
+-			// Note: IE uppercases css property names, but if we were to .toLowerCase()
+-			// .cssText, that would destroy case senstitivity in URL's, like in "background"
+-			return elem.style.cssText || undefined;
+-		},
+-		set: function( elem, value ) {
+-			return ( elem.style.cssText = value + "" );
+-		}
+-	};
+-}
++	cache: function( owner ) {
+ 
+-// Safari mis-reports the default selected property of an option
+-// Accessing the parent's selectedIndex property fixes it
+-if ( !jQuery.support.optSelected ) {
+-	jQuery.propHooks.selected = {
+-		get: function( elem ) {
+-			var parent = elem.parentNode;
++		// Check if the owner object already has a cache
++		var value = owner[ this.expando ];
+ 
+-			if ( parent ) {
+-				parent.selectedIndex;
++		// If not, create one
++		if ( !value ) {
++			value = {};
+ 
+-				// Make sure that it also works with optgroups, see #5701
+-				if ( parent.parentNode ) {
+-					parent.parentNode.selectedIndex;
++			// We can accept data for non-element nodes in modern browsers,
++			// but we should not, see #8335.
++			// Always return an empty object.
++			if ( acceptData( owner ) ) {
++
++				// If it is a node unlikely to be stringify-ed or looped over
++				// use plain assignment
++				if ( owner.nodeType ) {
++					owner[ this.expando ] = value;
++
++				// Otherwise secure it in a non-enumerable property
++				// configurable must be true to allow the property to be
++				// deleted when data is removed
++				} else {
++					Object.defineProperty( owner, this.expando, {
++						value: value,
++						configurable: true
++					} );
+ 				}
+ 			}
+-			return null;
+ 		}
+-	};
+-}
+ 
+-jQuery.each([
+-	"tabIndex",
+-	"readOnly",
+-	"maxLength",
+-	"cellSpacing",
+-	"cellPadding",
+-	"rowSpan",
+-	"colSpan",
+-	"useMap",
+-	"frameBorder",
+-	"contentEditable"
+-], function() {
+-	jQuery.propFix[ this.toLowerCase() ] = this;
+-});
++		return value;
++	},
++	set: function( owner, data, value ) {
++		var prop,
++			cache = this.cache( owner );
+ 
+-// IE6/7 call enctype encoding
+-if ( !jQuery.support.enctype ) {
+-	jQuery.propFix.enctype = "encoding";
+-}
++		// Handle: [ owner, key, value ] args
++		// Always use camelCase key (gh-2257)
++		if ( typeof data === "string" ) {
++			cache[ camelCase( data ) ] = value;
+ 
+-// Radios and checkboxes getter/setter
+-jQuery.each([ "radio", "checkbox" ], function() {
+-	jQuery.valHooks[ this ] = {
+-		set: function( elem, value ) {
+-			if ( jQuery.isArray( value ) ) {
+-				return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
++		// Handle: [ owner, { properties } ] args
++		} else {
++
++			// Copy the properties one-by-one to the cache object
++			for ( prop in data ) {
++				cache[ camelCase( prop ) ] = data[ prop ];
+ 			}
+ 		}
+-	};
+-	if ( !jQuery.support.checkOn ) {
+-		jQuery.valHooks[ this ].get = function( elem ) {
+-			// Support: Webkit
+-			// "" is returned instead of "on" if a value isn't specified
+-			return elem.getAttribute("value") === null ? "on" : elem.value;
+-		};
+-	}
+-});
+-var rformElems = /^(?:input|select|textarea)$/i,
+-	rkeyEvent = /^key/,
+-	rmouseEvent = /^(?:mouse|contextmenu)|click/,
+-	rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
+-	rtypenamespace = /^([^.]*)(?:\.(.+)|)$/;
+-
+-function returnTrue() {
+-	return true;
+-}
++		return cache;
++	},
++	get: function( owner, key ) {
++		return key === undefined ?
++			this.cache( owner ) :
+ 
+-function returnFalse() {
+-	return false;
+-}
++			// Always use camelCase key (gh-2257)
++			owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ];
++	},
++	access: function( owner, key, value ) {
+ 
+-function safeActiveElement() {
+-	try {
+-		return document.activeElement;
+-	} catch ( err ) { }
+-}
++		// In cases where either:
++		//
++		//   1. No key was specified
++		//   2. A string key was specified, but no value provided
++		//
++		// Take the "read" path and allow the get method to determine
++		// which value to return, respectively either:
++		//
++		//   1. The entire cache object
++		//   2. The data stored at the key
++		//
++		if ( key === undefined ||
++				( ( key && typeof key === "string" ) && value === undefined ) ) {
+ 
+-/*
+- * Helper functions for managing events -- not part of the public interface.
+- * Props to Dean Edwards' addEvent library for many of the ideas.
+- */
+-jQuery.event = {
++			return this.get( owner, key );
++		}
+ 
+-	global: {},
++		// When the key is not a string, or both a key and value
++		// are specified, set or extend (existing objects) with either:
++		//
++		//   1. An object of properties
++		//   2. A key and value
++		//
++		this.set( owner, key, value );
+ 
+-	add: function( elem, types, handler, data, selector ) {
+-		var tmp, events, t, handleObjIn,
+-			special, eventHandle, handleObj,
+-			handlers, type, namespaces, origType,
+-			elemData = jQuery._data( elem );
++		// Since the "set" path can have two possible entry points
++		// return the expected data based on which path was taken[*]
++		return value !== undefined ? value : key;
++	},
++	remove: function( owner, key ) {
++		var i,
++			cache = owner[ this.expando ];
+ 
+-		// Don't attach events to noData or text/comment nodes (but allow plain objects)
+-		if ( !elemData ) {
++		if ( cache === undefined ) {
+ 			return;
+ 		}
+ 
+-		// Caller can pass in an object of custom data in lieu of the handler
+-		if ( handler.handler ) {
+-			handleObjIn = handler;
+-			handler = handleObjIn.handler;
+-			selector = handleObjIn.selector;
+-		}
++		if ( key !== undefined ) {
+ 
+-		// Make sure that the handler has a unique ID, used to find/remove it later
+-		if ( !handler.guid ) {
+-			handler.guid = jQuery.guid++;
+-		}
++			// Support array or space separated string of keys
++			if ( Array.isArray( key ) ) {
+ 
+-		// Init the element's event structure and main handler, if this is the first
+-		if ( !(events = elemData.events) ) {
+-			events = elemData.events = {};
+-		}
+-		if ( !(eventHandle = elemData.handle) ) {
+-			eventHandle = elemData.handle = function( e ) {
+-				// Discard the second event of a jQuery.event.trigger() and
+-				// when an event is called after a page has unloaded
+-				return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ?
+-					jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
+-					undefined;
+-			};
+-			// Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
+-			eventHandle.elem = elem;
++				// If key is an array of keys...
++				// We always set camelCase keys, so remove that.
++				key = key.map( camelCase );
++			} else {
++				key = camelCase( key );
++
++				// If a key with the spaces exists, use it.
++				// Otherwise, create an array by matching non-whitespace
++				key = key in cache ?
++					[ key ] :
++					( key.match( rnothtmlwhite ) || [] );
++			}
++
++			i = key.length;
++
++			while ( i-- ) {
++				delete cache[ key[ i ] ];
++			}
+ 		}
+ 
+-		// Handle multiple events separated by a space
+-		types = ( types || "" ).match( core_rnotwhite ) || [""];
+-		t = types.length;
+-		while ( t-- ) {
+-			tmp = rtypenamespace.exec( types[t] ) || [];
+-			type = origType = tmp[1];
+-			namespaces = ( tmp[2] || "" ).split( "." ).sort();
++		// Remove the expando if there's no more data
++		if ( key === undefined || jQuery.isEmptyObject( cache ) ) {
+ 
+-			// There *must* be a type, no attaching namespace-only handlers
+-			if ( !type ) {
+-				continue;
++			// Support: Chrome <=35 - 45
++			// Webkit & Blink performance suffers when deleting properties
++			// from DOM nodes, so set to undefined instead
++			// https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted)
++			if ( owner.nodeType ) {
++				owner[ this.expando ] = undefined;
++			} else {
++				delete owner[ this.expando ];
+ 			}
++		}
++	},
++	hasData: function( owner ) {
++		var cache = owner[ this.expando ];
++		return cache !== undefined && !jQuery.isEmptyObject( cache );
++	}
++};
++var dataPriv = new Data();
+ 
+-			// If event changes its type, use the special event handlers for the changed type
+-			special = jQuery.event.special[ type ] || {};
++var dataUser = new Data();
+ 
+-			// If selector defined, determine special event api type, otherwise given type
+-			type = ( selector ? special.delegateType : special.bindType ) || type;
+ 
+-			// Update special based on newly reset type
+-			special = jQuery.event.special[ type ] || {};
+ 
+-			// handleObj is passed to all event handlers
+-			handleObj = jQuery.extend({
+-				type: type,
+-				origType: origType,
+-				data: data,
+-				handler: handler,
+-				guid: handler.guid,
+-				selector: selector,
+-				needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
+-				namespace: namespaces.join(".")
+-			}, handleObjIn );
++//	Implementation Summary
++//
++//	1. Enforce API surface and semantic compatibility with 1.9.x branch
++//	2. Improve the module's maintainability by reducing the storage
++//		paths to a single mechanism.
++//	3. Use the same single mechanism to support "private" and "user" data.
++//	4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData)
++//	5. Avoid exposing implementation details on user objects (eg. expando properties)
++//	6. Provide a clear path for implementation upgrade to WeakMap in 2014
+ 
+-			// Init the event handler queue if we're the first
+-			if ( !(handlers = events[ type ]) ) {
+-				handlers = events[ type ] = [];
+-				handlers.delegateCount = 0;
++var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
++	rmultiDash = /[A-Z]/g;
+ 
+-				// Only use addEventListener/attachEvent if the special events handler returns false
+-				if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
+-					// Bind the global event handler to the element
+-					if ( elem.addEventListener ) {
+-						elem.addEventListener( type, eventHandle, false );
++function getData( data ) {
++	if ( data === "true" ) {
++		return true;
++	}
+ 
+-					} else if ( elem.attachEvent ) {
+-						elem.attachEvent( "on" + type, eventHandle );
+-					}
+-				}
+-			}
++	if ( data === "false" ) {
++		return false;
++	}
+ 
+-			if ( special.add ) {
+-				special.add.call( elem, handleObj );
++	if ( data === "null" ) {
++		return null;
++	}
+ 
+-				if ( !handleObj.handler.guid ) {
+-					handleObj.handler.guid = handler.guid;
+-				}
+-			}
++	// Only convert to a number if it doesn't change the string
++	if ( data === +data + "" ) {
++		return +data;
++	}
+ 
+-			// Add to the element's handler list, delegates in front
+-			if ( selector ) {
+-				handlers.splice( handlers.delegateCount++, 0, handleObj );
+-			} else {
+-				handlers.push( handleObj );
+-			}
++	if ( rbrace.test( data ) ) {
++		return JSON.parse( data );
++	}
+ 
+-			// Keep track of which events have ever been used, for event optimization
+-			jQuery.event.global[ type ] = true;
++	return data;
++}
++
++function dataAttr( elem, key, data ) {
++	var name;
++
++	// If nothing was found internally, try to fetch any
++	// data from the HTML5 data-* attribute
++	if ( data === undefined && elem.nodeType === 1 ) {
++		name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase();
++		data = elem.getAttribute( name );
++
++		if ( typeof data === "string" ) {
++			try {
++				data = getData( data );
++			} catch ( e ) {}
++
++			// Make sure we set the data so it isn't changed later
++			dataUser.set( elem, key, data );
++		} else {
++			data = undefined;
+ 		}
++	}
++	return data;
++}
+ 
+-		// Nullify elem to prevent memory leaks in IE
+-		elem = null;
++jQuery.extend( {
++	hasData: function( elem ) {
++		return dataUser.hasData( elem ) || dataPriv.hasData( elem );
+ 	},
+ 
+-	// Detach an event or set of events from an element
+-	remove: function( elem, types, handler, selector, mappedTypes ) {
+-		var j, handleObj, tmp,
+-			origCount, t, events,
+-			special, handlers, type,
+-			namespaces, origType,
+-			elemData = jQuery.hasData( elem ) && jQuery._data( elem );
++	data: function( elem, name, data ) {
++		return dataUser.access( elem, name, data );
++	},
+ 
+-		if ( !elemData || !(events = elemData.events) ) {
+-			return;
+-		}
++	removeData: function( elem, name ) {
++		dataUser.remove( elem, name );
++	},
+ 
+-		// Once for each type.namespace in types; type may be omitted
+-		types = ( types || "" ).match( core_rnotwhite ) || [""];
+-		t = types.length;
+-		while ( t-- ) {
+-			tmp = rtypenamespace.exec( types[t] ) || [];
+-			type = origType = tmp[1];
+-			namespaces = ( tmp[2] || "" ).split( "." ).sort();
++	// TODO: Now that all calls to _data and _removeData have been replaced
++	// with direct calls to dataPriv methods, these can be deprecated.
++	_data: function( elem, name, data ) {
++		return dataPriv.access( elem, name, data );
++	},
+ 
+-			// Unbind all events (on this namespace, if provided) for the element
+-			if ( !type ) {
+-				for ( type in events ) {
+-					jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
+-				}
+-				continue;
+-			}
++	_removeData: function( elem, name ) {
++		dataPriv.remove( elem, name );
++	}
++} );
+ 
+-			special = jQuery.event.special[ type ] || {};
+-			type = ( selector ? special.delegateType : special.bindType ) || type;
+-			handlers = events[ type ] || [];
+-			tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" );
++jQuery.fn.extend( {
++	data: function( key, value ) {
++		var i, name, data,
++			elem = this[ 0 ],
++			attrs = elem && elem.attributes;
+ 
+-			// Remove matching events
+-			origCount = j = handlers.length;
+-			while ( j-- ) {
+-				handleObj = handlers[ j ];
++		// Gets all values
++		if ( key === undefined ) {
++			if ( this.length ) {
++				data = dataUser.get( elem );
+ 
+-				if ( ( mappedTypes || origType === handleObj.origType ) &&
+-					( !handler || handler.guid === handleObj.guid ) &&
+-					( !tmp || tmp.test( handleObj.namespace ) ) &&
+-					( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
+-					handlers.splice( j, 1 );
++				if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) {
++					i = attrs.length;
++					while ( i-- ) {
+ 
+-					if ( handleObj.selector ) {
+-						handlers.delegateCount--;
+-					}
+-					if ( special.remove ) {
+-						special.remove.call( elem, handleObj );
++						// Support: IE 11 only
++						// The attrs elements can be null (#14894)
++						if ( attrs[ i ] ) {
++							name = attrs[ i ].name;
++							if ( name.indexOf( "data-" ) === 0 ) {
++								name = camelCase( name.slice( 5 ) );
++								dataAttr( elem, name, data[ name ] );
++							}
++						}
+ 					}
++					dataPriv.set( elem, "hasDataAttrs", true );
+ 				}
+ 			}
+ 
+-			// Remove generic event handler if we removed something and no more handlers exist
+-			// (avoids potential for endless recursion during removal of special event handlers)
+-			if ( origCount && !handlers.length ) {
+-				if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
+-					jQuery.removeEvent( elem, type, elemData.handle );
+-				}
+-
+-				delete events[ type ];
+-			}
++			return data;
+ 		}
+ 
+-		// Remove the expando if it's no longer used
+-		if ( jQuery.isEmptyObject( events ) ) {
+-			delete elemData.handle;
+-
+-			// removeData also checks for emptiness and clears the expando if empty
+-			// so use it instead of delete
+-			jQuery._removeData( elem, "events" );
++		// Sets multiple values
++		if ( typeof key === "object" ) {
++			return this.each( function() {
++				dataUser.set( this, key );
++			} );
+ 		}
+-	},
+-
+-	trigger: function( event, data, elem, onlyHandlers ) {
+-		var handle, ontype, cur,
+-			bubbleType, special, tmp, i,
+-			eventPath = [ elem || document ],
+-			type = core_hasOwn.call( event, "type" ) ? event.type : event,
+-			namespaces = core_hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : [];
+ 
+-		cur = tmp = elem = elem || document;
++		return access( this, function( value ) {
++			var data;
+ 
+-		// Don't do events on text and comment nodes
+-		if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
+-			return;
+-		}
++			// The calling jQuery object (element matches) is not empty
++			// (and therefore has an element appears at this[ 0 ]) and the
++			// `value` parameter was not undefined. An empty jQuery object
++			// will result in `undefined` for elem = this[ 0 ] which will
++			// throw an exception if an attempt to read a data cache is made.
++			if ( elem && value === undefined ) {
+ 
+-		// focus/blur morphs to focusin/out; ensure we're not firing them right now
+-		if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
+-			return;
+-		}
++				// Attempt to get data from the cache
++				// The key will always be camelCased in Data
++				data = dataUser.get( elem, key );
++				if ( data !== undefined ) {
++					return data;
++				}
+ 
+-		if ( type.indexOf(".") >= 0 ) {
+-			// Namespaced trigger; create a regexp to match event type in handle()
+-			namespaces = type.split(".");
+-			type = namespaces.shift();
+-			namespaces.sort();
+-		}
+-		ontype = type.indexOf(":") < 0 && "on" + type;
++				// Attempt to "discover" the data in
++				// HTML5 custom data-* attrs
++				data = dataAttr( elem, key );
++				if ( data !== undefined ) {
++					return data;
++				}
+ 
+-		// Caller can pass in a jQuery.Event object, Object, or just an event type string
+-		event = event[ jQuery.expando ] ?
+-			event :
+-			new jQuery.Event( type, typeof event === "object" && event );
++				// We tried really hard, but the data doesn't exist.
++				return;
++			}
+ 
+-		// Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
+-		event.isTrigger = onlyHandlers ? 2 : 3;
+-		event.namespace = namespaces.join(".");
+-		event.namespace_re = event.namespace ?
+-			new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) :
+-			null;
++			// Set the data...
++			this.each( function() {
+ 
+-		// Clean up the event in case it is being reused
+-		event.result = undefined;
+-		if ( !event.target ) {
+-			event.target = elem;
+-		}
++				// We always store the camelCased key
++				dataUser.set( this, key, value );
++			} );
++		}, null, value, arguments.length > 1, null, true );
++	},
+ 
+-		// Clone any incoming data and prepend the event, creating the handler arg list
+-		data = data == null ?
+-			[ event ] :
+-			jQuery.makeArray( data, [ event ] );
++	removeData: function( key ) {
++		return this.each( function() {
++			dataUser.remove( this, key );
++		} );
++	}
++} );
+ 
+-		// Allow special events to draw outside the lines
+-		special = jQuery.event.special[ type ] || {};
+-		if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
+-			return;
+-		}
+ 
+-		// Determine event propagation path in advance, per W3C events spec (#9951)
+-		// Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
+-		if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
++jQuery.extend( {
++	queue: function( elem, type, data ) {
++		var queue;
+ 
+-			bubbleType = special.delegateType || type;
+-			if ( !rfocusMorph.test( bubbleType + type ) ) {
+-				cur = cur.parentNode;
+-			}
+-			for ( ; cur; cur = cur.parentNode ) {
+-				eventPath.push( cur );
+-				tmp = cur;
+-			}
++		if ( elem ) {
++			type = ( type || "fx" ) + "queue";
++			queue = dataPriv.get( elem, type );
+ 
+-			// Only add window if we got to document (e.g., not plain obj or detached DOM)
+-			if ( tmp === (elem.ownerDocument || document) ) {
+-				eventPath.push( tmp.defaultView || tmp.parentWindow || window );
++			// Speed up dequeue by getting out quickly if this is just a lookup
++			if ( data ) {
++				if ( !queue || Array.isArray( data ) ) {
++					queue = dataPriv.access( elem, type, jQuery.makeArray( data ) );
++				} else {
++					queue.push( data );
++				}
+ 			}
++			return queue || [];
+ 		}
++	},
+ 
+-		// Fire handlers on the event path
+-		i = 0;
+-		while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) {
+-
+-			event.type = i > 1 ?
+-				bubbleType :
+-				special.bindType || type;
++	dequeue: function( elem, type ) {
++		type = type || "fx";
+ 
+-			// jQuery handler
+-			handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
+-			if ( handle ) {
+-				handle.apply( cur, data );
+-			}
++		var queue = jQuery.queue( elem, type ),
++			startLength = queue.length,
++			fn = queue.shift(),
++			hooks = jQuery._queueHooks( elem, type ),
++			next = function() {
++				jQuery.dequeue( elem, type );
++			};
+ 
+-			// Native handler
+-			handle = ontype && cur[ ontype ];
+-			if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) {
+-				event.preventDefault();
+-			}
++		// If the fx queue is dequeued, always remove the progress sentinel
++		if ( fn === "inprogress" ) {
++			fn = queue.shift();
++			startLength--;
+ 		}
+-		event.type = type;
+-
+-		// If nobody prevented the default action, do it now
+-		if ( !onlyHandlers && !event.isDefaultPrevented() ) {
+-
+-			if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) &&
+-				jQuery.acceptData( elem ) ) {
+ 
+-				// Call a native DOM method on the target with the same name name as the event.
+-				// Can't use an .isFunction() check here because IE6/7 fails that test.
+-				// Don't do default actions on window, that's where global variables be (#6170)
+-				if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) {
+-
+-					// Don't re-trigger an onFOO event when we call its FOO() method
+-					tmp = elem[ ontype ];
+-
+-					if ( tmp ) {
+-						elem[ ontype ] = null;
+-					}
+-
+-					// Prevent re-triggering of the same event, since we already bubbled it above
+-					jQuery.event.triggered = type;
+-					try {
+-						elem[ type ]();
+-					} catch ( e ) {
+-						// IE<9 dies on focus/blur to hidden element (#1486,#12518)
+-						// only reproducible on winXP IE8 native, not IE9 in IE8 mode
+-					}
+-					jQuery.event.triggered = undefined;
++		if ( fn ) {
+ 
+-					if ( tmp ) {
+-						elem[ ontype ] = tmp;
+-					}
+-				}
++			// Add a progress sentinel to prevent the fx queue from being
++			// automatically dequeued
++			if ( type === "fx" ) {
++				queue.unshift( "inprogress" );
+ 			}
++
++			// Clear up the last queue stop function
++			delete hooks.stop;
++			fn.call( elem, next, hooks );
+ 		}
+ 
+-		return event.result;
++		if ( !startLength && hooks ) {
++			hooks.empty.fire();
++		}
+ 	},
+ 
+-	dispatch: function( event ) {
+-
+-		// Make a writable jQuery.Event from the native event object
+-		event = jQuery.event.fix( event );
+-
+-		var i, ret, handleObj, matched, j,
+-			handlerQueue = [],
+-			args = core_slice.call( arguments ),
+-			handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [],
+-			special = jQuery.event.special[ event.type ] || {};
++	// Not public - generate a queueHooks object, or return the current one
++	_queueHooks: function( elem, type ) {
++		var key = type + "queueHooks";
++		return dataPriv.get( elem, key ) || dataPriv.access( elem, key, {
++			empty: jQuery.Callbacks( "once memory" ).add( function() {
++				dataPriv.remove( elem, [ type + "queue", key ] );
++			} )
++		} );
++	}
++} );
+ 
+-		// Use the fix-ed jQuery.Event rather than the (read-only) native event
+-		args[0] = event;
+-		event.delegateTarget = this;
++jQuery.fn.extend( {
++	queue: function( type, data ) {
++		var setter = 2;
+ 
+-		// Call the preDispatch hook for the mapped type, and let it bail if desired
+-		if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
+-			return;
++		if ( typeof type !== "string" ) {
++			data = type;
++			type = "fx";
++			setter--;
+ 		}
+ 
+-		// Determine handlers
+-		handlerQueue = jQuery.event.handlers.call( this, event, handlers );
+-
+-		// Run delegates first; they may want to stop propagation beneath us
+-		i = 0;
+-		while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) {
+-			event.currentTarget = matched.elem;
+-
+-			j = 0;
+-			while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) {
++		if ( arguments.length < setter ) {
++			return jQuery.queue( this[ 0 ], type );
++		}
+ 
+-				// Triggered event must either 1) have no namespace, or
+-				// 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
+-				if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) {
++		return data === undefined ?
++			this :
++			this.each( function() {
++				var queue = jQuery.queue( this, type, data );
+ 
+-					event.handleObj = handleObj;
+-					event.data = handleObj.data;
++				// Ensure a hooks for this queue
++				jQuery._queueHooks( this, type );
+ 
+-					ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
+-							.apply( matched.elem, args );
++				if ( type === "fx" && queue[ 0 ] !== "inprogress" ) {
++					jQuery.dequeue( this, type );
++				}
++			} );
++	},
++	dequeue: function( type ) {
++		return this.each( function() {
++			jQuery.dequeue( this, type );
++		} );
++	},
++	clearQueue: function( type ) {
++		return this.queue( type || "fx", [] );
++	},
+ 
+-					if ( ret !== undefined ) {
+-						if ( (event.result = ret) === false ) {
+-							event.preventDefault();
+-							event.stopPropagation();
+-						}
+-					}
++	// Get a promise resolved when queues of a certain type
++	// are emptied (fx is the type by default)
++	promise: function( type, obj ) {
++		var tmp,
++			count = 1,
++			defer = jQuery.Deferred(),
++			elements = this,
++			i = this.length,
++			resolve = function() {
++				if ( !( --count ) ) {
++					defer.resolveWith( elements, [ elements ] );
+ 				}
+-			}
+-		}
++			};
+ 
+-		// Call the postDispatch hook for the mapped type
+-		if ( special.postDispatch ) {
+-			special.postDispatch.call( this, event );
++		if ( typeof type !== "string" ) {
++			obj = type;
++			type = undefined;
+ 		}
++		type = type || "fx";
+ 
+-		return event.result;
+-	},
++		while ( i-- ) {
++			tmp = dataPriv.get( elements[ i ], type + "queueHooks" );
++			if ( tmp && tmp.empty ) {
++				count++;
++				tmp.empty.add( resolve );
++			}
++		}
++		resolve();
++		return defer.promise( obj );
++	}
++} );
++var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source;
+ 
+-	handlers: function( event, handlers ) {
+-		var sel, handleObj, matches, i,
+-			handlerQueue = [],
+-			delegateCount = handlers.delegateCount,
+-			cur = event.target;
++var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" );
+ 
+-		// Find delegate handlers
+-		// Black-hole SVG <use> instance trees (#13180)
+-		// Avoid non-left-click bubbling in Firefox (#3861)
+-		if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) {
+ 
+-			/* jshint eqeqeq: false */
+-			for ( ; cur != this; cur = cur.parentNode || this ) {
+-				/* jshint eqeqeq: true */
++var cssExpand = [ "Top", "Right", "Bottom", "Left" ];
+ 
+-				// Don't check non-elements (#13208)
+-				// Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
+-				if ( cur.nodeType === 1 && (cur.disabled !== true || event.type !== "click") ) {
+-					matches = [];
+-					for ( i = 0; i < delegateCount; i++ ) {
+-						handleObj = handlers[ i ];
++var documentElement = document.documentElement;
+ 
+-						// Don't conflict with Object.prototype properties (#13203)
+-						sel = handleObj.selector + " ";
+ 
+-						if ( matches[ sel ] === undefined ) {
+-							matches[ sel ] = handleObj.needsContext ?
+-								jQuery( sel, this ).index( cur ) >= 0 :
+-								jQuery.find( sel, this, null, [ cur ] ).length;
+-						}
+-						if ( matches[ sel ] ) {
+-							matches.push( handleObj );
+-						}
+-					}
+-					if ( matches.length ) {
+-						handlerQueue.push({ elem: cur, handlers: matches });
+-					}
+-				}
+-			}
+-		}
+ 
+-		// Add the remaining (directly-bound) handlers
+-		if ( delegateCount < handlers.length ) {
+-			handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) });
+-		}
++	var isAttached = function( elem ) {
++			return jQuery.contains( elem.ownerDocument, elem );
++		},
++		composed = { composed: true };
++
++	// Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only
++	// Check attachment across shadow DOM boundaries when possible (gh-3504)
++	// Support: iOS 10.0-10.2 only
++	// Early iOS 10 versions support `attachShadow` but not `getRootNode`,
++	// leading to errors. We need to check for `getRootNode`.
++	if ( documentElement.getRootNode ) {
++		isAttached = function( elem ) {
++			return jQuery.contains( elem.ownerDocument, elem ) ||
++				elem.getRootNode( composed ) === elem.ownerDocument;
++		};
++	}
++var isHiddenWithinTree = function( elem, el ) {
+ 
+-		return handlerQueue;
+-	},
++		// isHiddenWithinTree might be called from jQuery#filter function;
++		// in that case, element will be second argument
++		elem = el || elem;
+ 
+-	fix: function( event ) {
+-		if ( event[ jQuery.expando ] ) {
+-			return event;
+-		}
++		// Inline style trumps all
++		return elem.style.display === "none" ||
++			elem.style.display === "" &&
+ 
+-		// Create a writable copy of the event object and normalize some properties
+-		var i, prop, copy,
+-			type = event.type,
+-			originalEvent = event,
+-			fixHook = this.fixHooks[ type ];
++			// Otherwise, check computed style
++			// Support: Firefox <=43 - 45
++			// Disconnected elements can have computed display: none, so first confirm that elem is
++			// in the document.
++			isAttached( elem ) &&
+ 
+-		if ( !fixHook ) {
+-			this.fixHooks[ type ] = fixHook =
+-				rmouseEvent.test( type ) ? this.mouseHooks :
+-				rkeyEvent.test( type ) ? this.keyHooks :
+-				{};
+-		}
+-		copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
++			jQuery.css( elem, "display" ) === "none";
++	};
+ 
+-		event = new jQuery.Event( originalEvent );
++var swap = function( elem, options, callback, args ) {
++	var ret, name,
++		old = {};
+ 
+-		i = copy.length;
+-		while ( i-- ) {
+-			prop = copy[ i ];
+-			event[ prop ] = originalEvent[ prop ];
+-		}
++	// Remember the old values, and insert the new ones
++	for ( name in options ) {
++		old[ name ] = elem.style[ name ];
++		elem.style[ name ] = options[ name ];
++	}
+ 
+-		// Support: IE<9
+-		// Fix target property (#1925)
+-		if ( !event.target ) {
+-			event.target = originalEvent.srcElement || document;
+-		}
++	ret = callback.apply( elem, args || [] );
+ 
+-		// Support: Chrome 23+, Safari?
+-		// Target should not be a text node (#504, #13143)
+-		if ( event.target.nodeType === 3 ) {
+-			event.target = event.target.parentNode;
+-		}
++	// Revert the old values
++	for ( name in options ) {
++		elem.style[ name ] = old[ name ];
++	}
+ 
+-		// Support: IE<9
+-		// For mouse/key events, metaKey==false if it's undefined (#3368, #11328)
+-		event.metaKey = !!event.metaKey;
++	return ret;
++};
+ 
+-		return fixHook.filter ? fixHook.filter( event, originalEvent ) : event;
+-	},
+ 
+-	// Includes some event props shared by KeyEvent and MouseEvent
+-	props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
+ 
+-	fixHooks: {},
+ 
+-	keyHooks: {
+-		props: "char charCode key keyCode".split(" "),
+-		filter: function( event, original ) {
++function adjustCSS( elem, prop, valueParts, tween ) {
++	var adjusted, scale,
++		maxIterations = 20,
++		currentValue = tween ?
++			function() {
++				return tween.cur();
++			} :
++			function() {
++				return jQuery.css( elem, prop, "" );
++			},
++		initial = currentValue(),
++		unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
+ 
+-			// Add which for key events
+-			if ( event.which == null ) {
+-				event.which = original.charCode != null ? original.charCode : original.keyCode;
+-			}
++		// Starting value computation is required for potential unit mismatches
++		initialInUnit = elem.nodeType &&
++			( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) &&
++			rcssNum.exec( jQuery.css( elem, prop ) );
+ 
+-			return event;
+-		}
+-	},
++	if ( initialInUnit && initialInUnit[ 3 ] !== unit ) {
+ 
+-	mouseHooks: {
+-		props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
+-		filter: function( event, original ) {
+-			var body, eventDoc, doc,
+-				button = original.button,
+-				fromElement = original.fromElement;
++		// Support: Firefox <=54
++		// Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144)
++		initial = initial / 2;
+ 
+-			// Calculate pageX/Y if missing and clientX/Y available
+-			if ( event.pageX == null && original.clientX != null ) {
+-				eventDoc = event.target.ownerDocument || document;
+-				doc = eventDoc.documentElement;
+-				body = eventDoc.body;
++		// Trust units reported by jQuery.css
++		unit = unit || initialInUnit[ 3 ];
+ 
+-				event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
+-				event.pageY = original.clientY + ( doc && doc.scrollTop  || body && body.scrollTop  || 0 ) - ( doc && doc.clientTop  || body && body.clientTop  || 0 );
+-			}
++		// Iteratively approximate from a nonzero starting point
++		initialInUnit = +initial || 1;
+ 
+-			// Add relatedTarget, if necessary
+-			if ( !event.relatedTarget && fromElement ) {
+-				event.relatedTarget = fromElement === event.target ? original.toElement : fromElement;
+-			}
++		while ( maxIterations-- ) {
+ 
+-			// Add which for click: 1 === left; 2 === middle; 3 === right
+-			// Note: button is not normalized, so don't use it
+-			if ( !event.which && button !== undefined ) {
+-				event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
++			// Evaluate and update our best guess (doubling guesses that zero out).
++			// Finish if the scale equals or crosses 1 (making the old*new product non-positive).
++			jQuery.style( elem, prop, initialInUnit + unit );
++			if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) {
++				maxIterations = 0;
+ 			}
++			initialInUnit = initialInUnit / scale;
+ 
+-			return event;
+ 		}
+-	},
+-
+-	special: {
+-		load: {
+-			// Prevent triggered image.load events from bubbling to window.load
+-			noBubble: true
+-		},
+-		focus: {
+-			// Fire native event if possible so blur/focus sequence is correct
+-			trigger: function() {
+-				if ( this !== safeActiveElement() && this.focus ) {
+-					try {
+-						this.focus();
+-						return false;
+-					} catch ( e ) {
+-						// Support: IE<9
+-						// If we error on focus to hidden element (#1486, #12518),
+-						// let .trigger() run the handlers
+-					}
+-				}
+-			},
+-			delegateType: "focusin"
+-		},
+-		blur: {
+-			trigger: function() {
+-				if ( this === safeActiveElement() && this.blur ) {
+-					this.blur();
+-					return false;
+-				}
+-			},
+-			delegateType: "focusout"
+-		},
+-		click: {
+-			// For checkbox, fire native event so checked state will be right
+-			trigger: function() {
+-				if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) {
+-					this.click();
+-					return false;
+-				}
+-			},
+ 
+-			// For cross-browser consistency, don't fire native .click() on links
+-			_default: function( event ) {
+-				return jQuery.nodeName( event.target, "a" );
+-			}
+-		},
++		initialInUnit = initialInUnit * 2;
++		jQuery.style( elem, prop, initialInUnit + unit );
+ 
+-		beforeunload: {
+-			postDispatch: function( event ) {
++		// Make sure we update the tween properties later on
++		valueParts = valueParts || [];
++	}
+ 
+-				// Even when returnValue equals to undefined Firefox will still show alert
+-				if ( event.result !== undefined ) {
+-					event.originalEvent.returnValue = event.result;
+-				}
+-			}
+-		}
+-	},
++	if ( valueParts ) {
++		initialInUnit = +initialInUnit || +initial || 0;
+ 
+-	simulate: function( type, elem, event, bubble ) {
+-		// Piggyback on a donor event to simulate a different one.
+-		// Fake originalEvent to avoid donor's stopPropagation, but if the
+-		// simulated event prevents default then we do the same on the donor.
+-		var e = jQuery.extend(
+-			new jQuery.Event(),
+-			event,
+-			{
+-				type: type,
+-				isSimulated: true,
+-				originalEvent: {}
+-			}
+-		);
+-		if ( bubble ) {
+-			jQuery.event.trigger( e, null, elem );
+-		} else {
+-			jQuery.event.dispatch.call( elem, e );
+-		}
+-		if ( e.isDefaultPrevented() ) {
+-			event.preventDefault();
++		// Apply relative offset (+=/-=) if specified
++		adjusted = valueParts[ 1 ] ?
++			initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] :
++			+valueParts[ 2 ];
++		if ( tween ) {
++			tween.unit = unit;
++			tween.start = initialInUnit;
++			tween.end = adjusted;
+ 		}
+ 	}
+-};
+-
+-jQuery.removeEvent = document.removeEventListener ?
+-	function( elem, type, handle ) {
+-		if ( elem.removeEventListener ) {
+-			elem.removeEventListener( type, handle, false );
+-		}
+-	} :
+-	function( elem, type, handle ) {
+-		var name = "on" + type;
++	return adjusted;
++}
+ 
+-		if ( elem.detachEvent ) {
+ 
+-			// #8545, #7054, preventing memory leaks for custom events in IE6-8
+-			// detachEvent needed property on element, by name of that event, to properly expose it to GC
+-			if ( typeof elem[ name ] === core_strundefined ) {
+-				elem[ name ] = null;
+-			}
++var defaultDisplayMap = {};
+ 
+-			elem.detachEvent( name, handle );
+-		}
+-	};
++function getDefaultDisplay( elem ) {
++	var temp,
++		doc = elem.ownerDocument,
++		nodeName = elem.nodeName,
++		display = defaultDisplayMap[ nodeName ];
+ 
+-jQuery.Event = function( src, props ) {
+-	// Allow instantiation without the 'new' keyword
+-	if ( !(this instanceof jQuery.Event) ) {
+-		return new jQuery.Event( src, props );
++	if ( display ) {
++		return display;
+ 	}
+ 
+-	// Event object
+-	if ( src && src.type ) {
+-		this.originalEvent = src;
+-		this.type = src.type;
+-
+-		// Events bubbling up the document may have been marked as prevented
+-		// by a handler lower down the tree; reflect the correct value.
+-		this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false ||
+-			src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse;
++	temp = doc.body.appendChild( doc.createElement( nodeName ) );
++	display = jQuery.css( temp, "display" );
+ 
+-	// Event type
+-	} else {
+-		this.type = src;
+-	}
++	temp.parentNode.removeChild( temp );
+ 
+-	// Put explicitly provided properties onto the event object
+-	if ( props ) {
+-		jQuery.extend( this, props );
++	if ( display === "none" ) {
++		display = "block";
+ 	}
++	defaultDisplayMap[ nodeName ] = display;
+ 
+-	// Create a timestamp if incoming event doesn't have one
+-	this.timeStamp = src && src.timeStamp || jQuery.now();
+-
+-	// Mark it as fixed
+-	this[ jQuery.expando ] = true;
+-};
+-
+-// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
+-// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
+-jQuery.Event.prototype = {
+-	isDefaultPrevented: returnFalse,
+-	isPropagationStopped: returnFalse,
+-	isImmediatePropagationStopped: returnFalse,
++	return display;
++}
+ 
+-	preventDefault: function() {
+-		var e = this.originalEvent;
++function showHide( elements, show ) {
++	var display, elem,
++		values = [],
++		index = 0,
++		length = elements.length;
+ 
+-		this.isDefaultPrevented = returnTrue;
+-		if ( !e ) {
+-			return;
++	// Determine new display value for elements that need to change
++	for ( ; index < length; index++ ) {
++		elem = elements[ index ];
++		if ( !elem.style ) {
++			continue;
+ 		}
+ 
+-		// If preventDefault exists, run it on the original event
+-		if ( e.preventDefault ) {
+-			e.preventDefault();
++		display = elem.style.display;
++		if ( show ) {
+ 
+-		// Support: IE
+-		// Otherwise set the returnValue property of the original event to false
++			// Since we force visibility upon cascade-hidden elements, an immediate (and slow)
++			// check is required in this first loop unless we have a nonempty display value (either
++			// inline or about-to-be-restored)
++			if ( display === "none" ) {
++				values[ index ] = dataPriv.get( elem, "display" ) || null;
++				if ( !values[ index ] ) {
++					elem.style.display = "";
++				}
++			}
++			if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) {
++				values[ index ] = getDefaultDisplay( elem );
++			}
+ 		} else {
+-			e.returnValue = false;
+-		}
+-	},
+-	stopPropagation: function() {
+-		var e = this.originalEvent;
++			if ( display !== "none" ) {
++				values[ index ] = "none";
+ 
+-		this.isPropagationStopped = returnTrue;
+-		if ( !e ) {
+-			return;
+-		}
+-		// If stopPropagation exists, run it on the original event
+-		if ( e.stopPropagation ) {
+-			e.stopPropagation();
++				// Remember what we're overwriting
++				dataPriv.set( elem, "display", display );
++			}
+ 		}
++	}
+ 
+-		// Support: IE
+-		// Set the cancelBubble property of the original event to true
+-		e.cancelBubble = true;
+-	},
+-	stopImmediatePropagation: function() {
+-		this.isImmediatePropagationStopped = returnTrue;
+-		this.stopPropagation();
++	// Set the display of the elements in a second loop to avoid constant reflow
++	for ( index = 0; index < length; index++ ) {
++		if ( values[ index ] != null ) {
++			elements[ index ].style.display = values[ index ];
++		}
+ 	}
+-};
+ 
+-// Create mouseenter/leave events using mouseover/out and event-time checks
+-jQuery.each({
+-	mouseenter: "mouseover",
+-	mouseleave: "mouseout"
+-}, function( orig, fix ) {
+-	jQuery.event.special[ orig ] = {
+-		delegateType: fix,
+-		bindType: fix,
++	return elements;
++}
+ 
+-		handle: function( event ) {
+-			var ret,
+-				target = this,
+-				related = event.relatedTarget,
+-				handleObj = event.handleObj;
++jQuery.fn.extend( {
++	show: function() {
++		return showHide( this, true );
++	},
++	hide: function() {
++		return showHide( this );
++	},
++	toggle: function( state ) {
++		if ( typeof state === "boolean" ) {
++			return state ? this.show() : this.hide();
++		}
+ 
+-			// For mousenter/leave call the handler if related is outside the target.
+-			// NB: No relatedTarget if the mouse left/entered the browser window
+-			if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
+-				event.type = handleObj.origType;
+-				ret = handleObj.handler.apply( this, arguments );
+-				event.type = fix;
++		return this.each( function() {
++			if ( isHiddenWithinTree( this ) ) {
++				jQuery( this ).show();
++			} else {
++				jQuery( this ).hide();
+ 			}
+-			return ret;
+-		}
+-	};
+-});
++		} );
++	}
++} );
++var rcheckableType = ( /^(?:checkbox|radio)$/i );
+ 
+-// IE submit delegation
+-if ( !jQuery.support.submitBubbles ) {
++var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i );
+ 
+-	jQuery.event.special.submit = {
+-		setup: function() {
+-			// Only need this for delegated form submit events
+-			if ( jQuery.nodeName( this, "form" ) ) {
+-				return false;
+-			}
++var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i );
+ 
+-			// Lazy-add a submit handler when a descendant form may potentially be submitted
+-			jQuery.event.add( this, "click._submit keypress._submit", function( e ) {
+-				// Node name check avoids a VML-related crash in IE (#9807)
+-				var elem = e.target,
+-					form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined;
+-				if ( form && !jQuery._data( form, "submitBubbles" ) ) {
+-					jQuery.event.add( form, "submit._submit", function( event ) {
+-						event._submit_bubble = true;
+-					});
+-					jQuery._data( form, "submitBubbles", true );
+-				}
+-			});
+-			// return undefined since we don't need an event listener
+-		},
+ 
+-		postDispatch: function( event ) {
+-			// If form was submitted by the user, bubble the event up the tree
+-			if ( event._submit_bubble ) {
+-				delete event._submit_bubble;
+-				if ( this.parentNode && !event.isTrigger ) {
+-					jQuery.event.simulate( "submit", this.parentNode, event, true );
+-				}
+-			}
+-		},
+ 
+-		teardown: function() {
+-			// Only need this for delegated form submit events
+-			if ( jQuery.nodeName( this, "form" ) ) {
+-				return false;
+-			}
++// We have to close these tags to support XHTML (#13200)
++var wrapMap = {
+ 
+-			// Remove delegated handlers; cleanData eventually reaps submit handlers attached above
+-			jQuery.event.remove( this, "._submit" );
+-		}
+-	};
+-}
++	// Support: IE <=9 only
++	option: [ 1, "<select multiple='multiple'>", "</select>" ],
+ 
+-// IE change delegation and checkbox/radio fix
+-if ( !jQuery.support.changeBubbles ) {
++	// XHTML parsers do not magically insert elements in the
++	// same way that tag soup parsers do. So we cannot shorten
++	// this by omitting <tbody> or other required elements.
++	thead: [ 1, "<table>", "</table>" ],
++	col: [ 2, "<table><colgroup>", "</colgroup></table>" ],
++	tr: [ 2, "<table><tbody>", "</tbody></table>" ],
++	td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
+ 
+-	jQuery.event.special.change = {
++	_default: [ 0, "", "" ]
++};
+ 
+-		setup: function() {
++// Support: IE <=9 only
++wrapMap.optgroup = wrapMap.option;
+ 
+-			if ( rformElems.test( this.nodeName ) ) {
+-				// IE doesn't fire change on a check/radio until blur; trigger it on click
+-				// after a propertychange. Eat the blur-change in special.change.handle.
+-				// This still fires onchange a second time for check/radio after blur.
+-				if ( this.type === "checkbox" || this.type === "radio" ) {
+-					jQuery.event.add( this, "propertychange._change", function( event ) {
+-						if ( event.originalEvent.propertyName === "checked" ) {
+-							this._just_changed = true;
+-						}
+-					});
+-					jQuery.event.add( this, "click._change", function( event ) {
+-						if ( this._just_changed && !event.isTrigger ) {
+-							this._just_changed = false;
+-						}
+-						// Allow triggered, simulated change events (#11500)
+-						jQuery.event.simulate( "change", this, event, true );
+-					});
+-				}
+-				return false;
+-			}
+-			// Delegated event; lazy-add a change handler on descendant inputs
+-			jQuery.event.add( this, "beforeactivate._change", function( e ) {
+-				var elem = e.target;
++wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
++wrapMap.th = wrapMap.td;
+ 
+-				if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) {
+-					jQuery.event.add( elem, "change._change", function( event ) {
+-						if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
+-							jQuery.event.simulate( "change", this.parentNode, event, true );
+-						}
+-					});
+-					jQuery._data( elem, "changeBubbles", true );
+-				}
+-			});
+-		},
+ 
+-		handle: function( event ) {
+-			var elem = event.target;
++function getAll( context, tag ) {
+ 
+-			// Swallow native change events from checkbox/radio, we already triggered them above
+-			if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) {
+-				return event.handleObj.handler.apply( this, arguments );
+-			}
+-		},
++	// Support: IE <=9 - 11 only
++	// Use typeof to avoid zero-argument method invocation on host objects (#15151)
++	var ret;
+ 
+-		teardown: function() {
+-			jQuery.event.remove( this, "._change" );
++	if ( typeof context.getElementsByTagName !== "undefined" ) {
++		ret = context.getElementsByTagName( tag || "*" );
+ 
+-			return !rformElems.test( this.nodeName );
+-		}
+-	};
+-}
++	} else if ( typeof context.querySelectorAll !== "undefined" ) {
++		ret = context.querySelectorAll( tag || "*" );
+ 
+-// Create "bubbling" focus and blur events
+-if ( !jQuery.support.focusinBubbles ) {
+-	jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
++	} else {
++		ret = [];
++	}
+ 
+-		// Attach a single capturing handler while someone wants focusin/focusout
+-		var attaches = 0,
+-			handler = function( event ) {
+-				jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
+-			};
++	if ( tag === undefined || tag && nodeName( context, tag ) ) {
++		return jQuery.merge( [ context ], ret );
++	}
+ 
+-		jQuery.event.special[ fix ] = {
+-			setup: function() {
+-				if ( attaches++ === 0 ) {
+-					document.addEventListener( orig, handler, true );
+-				}
+-			},
+-			teardown: function() {
+-				if ( --attaches === 0 ) {
+-					document.removeEventListener( orig, handler, true );
+-				}
+-			}
+-		};
+-	});
++	return ret;
+ }
+ 
+-jQuery.fn.extend({
+ 
+-	on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
+-		var type, origFn;
++// Mark scripts as having already been evaluated
++function setGlobalEval( elems, refElements ) {
++	var i = 0,
++		l = elems.length;
+ 
+-		// Types can be a map of types/handlers
+-		if ( typeof types === "object" ) {
+-			// ( types-Object, selector, data )
+-			if ( typeof selector !== "string" ) {
+-				// ( types-Object, data )
+-				data = data || selector;
+-				selector = undefined;
+-			}
+-			for ( type in types ) {
+-				this.on( type, selector, data, types[ type ], one );
+-			}
+-			return this;
+-		}
++	for ( ; i < l; i++ ) {
++		dataPriv.set(
++			elems[ i ],
++			"globalEval",
++			!refElements || dataPriv.get( refElements[ i ], "globalEval" )
++		);
++	}
++}
+ 
+-		if ( data == null && fn == null ) {
+-			// ( types, fn )
+-			fn = selector;
+-			data = selector = undefined;
+-		} else if ( fn == null ) {
+-			if ( typeof selector === "string" ) {
+-				// ( types, selector, fn )
+-				fn = data;
+-				data = undefined;
+-			} else {
+-				// ( types, data, fn )
+-				fn = data;
+-				data = selector;
+-				selector = undefined;
+-			}
+-		}
+-		if ( fn === false ) {
+-			fn = returnFalse;
+-		} else if ( !fn ) {
+-			return this;
+-		}
+ 
+-		if ( one === 1 ) {
+-			origFn = fn;
+-			fn = function( event ) {
+-				// Can use an empty set, since event contains the info
+-				jQuery().off( event );
+-				return origFn.apply( this, arguments );
+-			};
+-			// Use same guid so caller can remove using origFn
+-			fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
+-		}
+-		return this.each( function() {
+-			jQuery.event.add( this, types, fn, data, selector );
+-		});
+-	},
+-	one: function( types, selector, data, fn ) {
+-		return this.on( types, selector, data, fn, 1 );
+-	},
+-	off: function( types, selector, fn ) {
+-		var handleObj, type;
+-		if ( types && types.preventDefault && types.handleObj ) {
+-			// ( event )  dispatched jQuery.Event
+-			handleObj = types.handleObj;
+-			jQuery( types.delegateTarget ).off(
+-				handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
+-				handleObj.selector,
+-				handleObj.handler
+-			);
+-			return this;
+-		}
+-		if ( typeof types === "object" ) {
+-			// ( types-object [, selector] )
+-			for ( type in types ) {
+-				this.off( type, selector, types[ type ] );
+-			}
+-			return this;
+-		}
+-		if ( selector === false || typeof selector === "function" ) {
+-			// ( types [, fn] )
+-			fn = selector;
+-			selector = undefined;
+-		}
+-		if ( fn === false ) {
+-			fn = returnFalse;
+-		}
+-		return this.each(function() {
+-			jQuery.event.remove( this, types, fn, selector );
+-		});
+-	},
++var rhtml = /<|&#?\w+;/;
+ 
+-	trigger: function( type, data ) {
+-		return this.each(function() {
+-			jQuery.event.trigger( type, data, this );
+-		});
+-	},
+-	triggerHandler: function( type, data ) {
+-		var elem = this[0];
+-		if ( elem ) {
+-			return jQuery.event.trigger( type, data, elem, true );
+-		}
+-	}
+-});
+-var isSimple = /^.[^:#\[\.,]*$/,
+-	rparentsprev = /^(?:parents|prev(?:Until|All))/,
+-	rneedsContext = jQuery.expr.match.needsContext,
+-	// methods guaranteed to produce a unique set when starting from a unique set
+-	guaranteedUnique = {
+-		children: true,
+-		contents: true,
+-		next: true,
+-		prev: true
+-	};
++function buildFragment( elems, context, scripts, selection, ignored ) {
++	var elem, tmp, tag, wrap, attached, j,
++		fragment = context.createDocumentFragment(),
++		nodes = [],
++		i = 0,
++		l = elems.length;
+ 
+-jQuery.fn.extend({
+-	find: function( selector ) {
+-		var i,
+-			ret = [],
+-			self = this,
+-			len = self.length;
++	for ( ; i < l; i++ ) {
++		elem = elems[ i ];
+ 
+-		if ( typeof selector !== "string" ) {
+-			return this.pushStack( jQuery( selector ).filter(function() {
+-				for ( i = 0; i < len; i++ ) {
+-					if ( jQuery.contains( self[ i ], this ) ) {
+-						return true;
+-					}
+-				}
+-			}) );
+-		}
++		if ( elem || elem === 0 ) {
+ 
+-		for ( i = 0; i < len; i++ ) {
+-			jQuery.find( selector, self[ i ], ret );
+-		}
++			// Add nodes directly
++			if ( toType( elem ) === "object" ) {
+ 
+-		// Needed because $( selector, context ) becomes $( context ).find( selector )
+-		ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret );
+-		ret.selector = this.selector ? this.selector + " " + selector : selector;
+-		return ret;
+-	},
++				// Support: Android <=4.0 only, PhantomJS 1 only
++				// push.apply(_, arraylike) throws on ancient WebKit
++				jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );
+ 
+-	has: function( target ) {
+-		var i,
+-			targets = jQuery( target, this ),
+-			len = targets.length;
++			// Convert non-html into a text node
++			} else if ( !rhtml.test( elem ) ) {
++				nodes.push( context.createTextNode( elem ) );
+ 
+-		return this.filter(function() {
+-			for ( i = 0; i < len; i++ ) {
+-				if ( jQuery.contains( this, targets[i] ) ) {
+-					return true;
+-				}
+-			}
+-		});
+-	},
++			// Convert html into DOM nodes
++			} else {
++				tmp = tmp || fragment.appendChild( context.createElement( "div" ) );
+ 
+-	not: function( selector ) {
+-		return this.pushStack( winnow(this, selector || [], true) );
+-	},
++				// Deserialize a standard representation
++				tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase();
++				wrap = wrapMap[ tag ] || wrapMap._default;
++				tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ];
+ 
+-	filter: function( selector ) {
+-		return this.pushStack( winnow(this, selector || [], false) );
+-	},
++				// Descend through wrappers to the right content
++				j = wrap[ 0 ];
++				while ( j-- ) {
++					tmp = tmp.lastChild;
++				}
+ 
+-	is: function( selector ) {
+-		return !!winnow(
+-			this,
++				// Support: Android <=4.0 only, PhantomJS 1 only
++				// push.apply(_, arraylike) throws on ancient WebKit
++				jQuery.merge( nodes, tmp.childNodes );
+ 
+-			// If this is a positional/relative selector, check membership in the returned set
+-			// so $("p:first").is("p:last") won't return true for a doc with two "p".
+-			typeof selector === "string" && rneedsContext.test( selector ) ?
+-				jQuery( selector ) :
+-				selector || [],
+-			false
+-		).length;
+-	},
++				// Remember the top-level container
++				tmp = fragment.firstChild;
+ 
+-	closest: function( selectors, context ) {
+-		var cur,
+-			i = 0,
+-			l = this.length,
+-			ret = [],
+-			pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ?
+-				jQuery( selectors, context || this.context ) :
+-				0;
++				// Ensure the created nodes are orphaned (#12392)
++				tmp.textContent = "";
++			}
++		}
++	}
+ 
+-		for ( ; i < l; i++ ) {
+-			for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) {
+-				// Always skip document fragments
+-				if ( cur.nodeType < 11 && (pos ?
+-					pos.index(cur) > -1 :
++	// Remove wrapper from fragment
++	fragment.textContent = "";
+ 
+-					// Don't pass non-elements to Sizzle
+-					cur.nodeType === 1 &&
+-						jQuery.find.matchesSelector(cur, selectors)) ) {
++	i = 0;
++	while ( ( elem = nodes[ i++ ] ) ) {
+ 
+-					cur = ret.push( cur );
+-					break;
+-				}
++		// Skip elements already in the context collection (trac-4087)
++		if ( selection && jQuery.inArray( elem, selection ) > -1 ) {
++			if ( ignored ) {
++				ignored.push( elem );
+ 			}
++			continue;
+ 		}
+ 
+-		return this.pushStack( ret.length > 1 ? jQuery.unique( ret ) : ret );
+-	},
++		attached = isAttached( elem );
+ 
+-	// Determine the position of an element within
+-	// the matched set of elements
+-	index: function( elem ) {
++		// Append to fragment
++		tmp = getAll( fragment.appendChild( elem ), "script" );
+ 
+-		// No argument, return index in parent
+-		if ( !elem ) {
+-			return ( this[0] && this[0].parentNode ) ? this.first().prevAll().length : -1;
++		// Preserve script evaluation history
++		if ( attached ) {
++			setGlobalEval( tmp );
+ 		}
+ 
+-		// index in selector
+-		if ( typeof elem === "string" ) {
+-			return jQuery.inArray( this[0], jQuery( elem ) );
++		// Capture executables
++		if ( scripts ) {
++			j = 0;
++			while ( ( elem = tmp[ j++ ] ) ) {
++				if ( rscriptType.test( elem.type || "" ) ) {
++					scripts.push( elem );
++				}
++			}
+ 		}
++	}
+ 
+-		// Locate the position of the desired element
+-		return jQuery.inArray(
+-			// If it receives a jQuery object, the first element is used
+-			elem.jquery ? elem[0] : elem, this );
+-	},
++	return fragment;
++}
+ 
+-	add: function( selector, context ) {
+-		var set = typeof selector === "string" ?
+-				jQuery( selector, context ) :
+-				jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),
+-			all = jQuery.merge( this.get(), set );
+ 
+-		return this.pushStack( jQuery.unique(all) );
+-	},
++( function() {
++	var fragment = document.createDocumentFragment(),
++		div = fragment.appendChild( document.createElement( "div" ) ),
++		input = document.createElement( "input" );
+ 
+-	addBack: function( selector ) {
+-		return this.add( selector == null ?
+-			this.prevObject : this.prevObject.filter(selector)
+-		);
+-	}
+-});
++	// Support: Android 4.0 - 4.3 only
++	// Check state lost if the name is set (#11217)
++	// Support: Windows Web Apps (WWA)
++	// `name` and `type` must use .setAttribute for WWA (#14901)
++	input.setAttribute( "type", "radio" );
++	input.setAttribute( "checked", "checked" );
++	input.setAttribute( "name", "t" );
+ 
+-function sibling( cur, dir ) {
+-	do {
+-		cur = cur[ dir ];
+-	} while ( cur && cur.nodeType !== 1 );
++	div.appendChild( input );
+ 
+-	return cur;
+-}
++	// Support: Android <=4.1 only
++	// Older WebKit doesn't clone checked state correctly in fragments
++	support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;
+ 
+-jQuery.each({
+-	parent: function( elem ) {
+-		var parent = elem.parentNode;
+-		return parent && parent.nodeType !== 11 ? parent : null;
+-	},
+-	parents: function( elem ) {
+-		return jQuery.dir( elem, "parentNode" );
+-	},
+-	parentsUntil: function( elem, i, until ) {
+-		return jQuery.dir( elem, "parentNode", until );
+-	},
+-	next: function( elem ) {
+-		return sibling( elem, "nextSibling" );
+-	},
+-	prev: function( elem ) {
+-		return sibling( elem, "previousSibling" );
+-	},
+-	nextAll: function( elem ) {
+-		return jQuery.dir( elem, "nextSibling" );
+-	},
+-	prevAll: function( elem ) {
+-		return jQuery.dir( elem, "previousSibling" );
+-	},
+-	nextUntil: function( elem, i, until ) {
+-		return jQuery.dir( elem, "nextSibling", until );
+-	},
+-	prevUntil: function( elem, i, until ) {
+-		return jQuery.dir( elem, "previousSibling", until );
+-	},
+-	siblings: function( elem ) {
+-		return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );
+-	},
+-	children: function( elem ) {
+-		return jQuery.sibling( elem.firstChild );
+-	},
+-	contents: function( elem ) {
+-		return jQuery.nodeName( elem, "iframe" ) ?
+-			elem.contentDocument || elem.contentWindow.document :
+-			jQuery.merge( [], elem.childNodes );
+-	}
+-}, function( name, fn ) {
+-	jQuery.fn[ name ] = function( until, selector ) {
+-		var ret = jQuery.map( this, fn, until );
++	// Support: IE <=11 only
++	// Make sure textarea (and checkbox) defaultValue is properly cloned
++	div.innerHTML = "<textarea>x</textarea>";
++	support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;
++} )();
+ 
+-		if ( name.slice( -5 ) !== "Until" ) {
+-			selector = until;
+-		}
+ 
+-		if ( selector && typeof selector === "string" ) {
+-			ret = jQuery.filter( selector, ret );
+-		}
++var
++	rkeyEvent = /^key/,
++	rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/,
++	rtypenamespace = /^([^.]*)(?:\.(.+)|)/;
+ 
+-		if ( this.length > 1 ) {
+-			// Remove duplicates
+-			if ( !guaranteedUnique[ name ] ) {
+-				ret = jQuery.unique( ret );
+-			}
++function returnTrue() {
++	return true;
++}
+ 
+-			// Reverse order for parents* and prev-derivatives
+-			if ( rparentsprev.test( name ) ) {
+-				ret = ret.reverse();
+-			}
+-		}
++function returnFalse() {
++	return false;
++}
+ 
+-		return this.pushStack( ret );
+-	};
+-});
++// Support: IE <=9 - 11+
++// focus() and blur() are asynchronous, except when they are no-op.
++// So expect focus to be synchronous when the element is already active,
++// and blur to be synchronous when the element is not already active.
++// (focus and blur are always synchronous in other supported browsers,
++// this just defines when we can count on it).
++function expectSync( elem, type ) {
++	return ( elem === safeActiveElement() ) === ( type === "focus" );
++}
+ 
+-jQuery.extend({
+-	filter: function( expr, elems, not ) {
+-		var elem = elems[ 0 ];
++// Support: IE <=9 only
++// Accessing document.activeElement can throw unexpectedly
++// https://bugs.jquery.com/ticket/13393
++function safeActiveElement() {
++	try {
++		return document.activeElement;
++	} catch ( err ) { }
++}
+ 
+-		if ( not ) {
+-			expr = ":not(" + expr + ")";
+-		}
++function on( elem, types, selector, data, fn, one ) {
++	var origFn, type;
+ 
+-		return elems.length === 1 && elem.nodeType === 1 ?
+-			jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] :
+-			jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
+-				return elem.nodeType === 1;
+-			}));
+-	},
++	// Types can be a map of types/handlers
++	if ( typeof types === "object" ) {
+ 
+-	dir: function( elem, dir, until ) {
+-		var matched = [],
+-			cur = elem[ dir ];
++		// ( types-Object, selector, data )
++		if ( typeof selector !== "string" ) {
+ 
+-		while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
+-			if ( cur.nodeType === 1 ) {
+-				matched.push( cur );
+-			}
+-			cur = cur[dir];
++			// ( types-Object, data )
++			data = data || selector;
++			selector = undefined;
+ 		}
+-		return matched;
+-	},
++		for ( type in types ) {
++			on( elem, type, selector, data, types[ type ], one );
++		}
++		return elem;
++	}
+ 
+-	sibling: function( n, elem ) {
+-		var r = [];
++	if ( data == null && fn == null ) {
+ 
+-		for ( ; n; n = n.nextSibling ) {
+-			if ( n.nodeType === 1 && n !== elem ) {
+-				r.push( n );
+-			}
+-		}
++		// ( types, fn )
++		fn = selector;
++		data = selector = undefined;
++	} else if ( fn == null ) {
++		if ( typeof selector === "string" ) {
++
++			// ( types, selector, fn )
++			fn = data;
++			data = undefined;
++		} else {
+ 
+-		return r;
++			// ( types, data, fn )
++			fn = data;
++			data = selector;
++			selector = undefined;
++		}
++	}
++	if ( fn === false ) {
++		fn = returnFalse;
++	} else if ( !fn ) {
++		return elem;
+ 	}
+-});
+ 
+-// Implement the identical functionality for filter and not
+-function winnow( elements, qualifier, not ) {
+-	if ( jQuery.isFunction( qualifier ) ) {
+-		return jQuery.grep( elements, function( elem, i ) {
+-			/* jshint -W018 */
+-			return !!qualifier.call( elem, i, elem ) !== not;
+-		});
++	if ( one === 1 ) {
++		origFn = fn;
++		fn = function( event ) {
++
++			// Can use an empty set, since event contains the info
++			jQuery().off( event );
++			return origFn.apply( this, arguments );
++		};
+ 
++		// Use same guid so caller can remove using origFn
++		fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
+ 	}
++	return elem.each( function() {
++		jQuery.event.add( this, types, fn, data, selector );
++	} );
++}
+ 
+-	if ( qualifier.nodeType ) {
+-		return jQuery.grep( elements, function( elem ) {
+-			return ( elem === qualifier ) !== not;
+-		});
++/*
++ * Helper functions for managing events -- not part of the public interface.
++ * Props to Dean Edwards' addEvent library for many of the ideas.
++ */
++jQuery.event = {
+ 
+-	}
++	global: {},
++
++	add: function( elem, types, handler, data, selector ) {
++
++		var handleObjIn, eventHandle, tmp,
++			events, t, handleObj,
++			special, handlers, type, namespaces, origType,
++			elemData = dataPriv.get( elem );
+ 
+-	if ( typeof qualifier === "string" ) {
+-		if ( isSimple.test( qualifier ) ) {
+-			return jQuery.filter( qualifier, elements, not );
++		// Don't attach events to noData or text/comment nodes (but allow plain objects)
++		if ( !elemData ) {
++			return;
+ 		}
+ 
+-		qualifier = jQuery.filter( qualifier, elements );
+-	}
++		// Caller can pass in an object of custom data in lieu of the handler
++		if ( handler.handler ) {
++			handleObjIn = handler;
++			handler = handleObjIn.handler;
++			selector = handleObjIn.selector;
++		}
+ 
+-	return jQuery.grep( elements, function( elem ) {
+-		return ( jQuery.inArray( elem, qualifier ) >= 0 ) !== not;
+-	});
+-}
+-function createSafeFragment( document ) {
+-	var list = nodeNames.split( "|" ),
+-		safeFrag = document.createDocumentFragment();
+-
+-	if ( safeFrag.createElement ) {
+-		while ( list.length ) {
+-			safeFrag.createElement(
+-				list.pop()
+-			);
++		// Ensure that invalid selectors throw exceptions at attach time
++		// Evaluate against documentElement in case elem is a non-element node (e.g., document)
++		if ( selector ) {
++			jQuery.find.matchesSelector( documentElement, selector );
+ 		}
+-	}
+-	return safeFrag;
+-}
+ 
+-var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" +
+-		"header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
+-	rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g,
+-	rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"),
+-	rleadingWhitespace = /^\s+/,
+-	rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
+-	rtagName = /<([\w:]+)/,
+-	rtbody = /<tbody/i,
+-	rhtml = /<|&#?\w+;/,
+-	rnoInnerhtml = /<(?:script|style|link)/i,
+-	manipulation_rcheckableType = /^(?:checkbox|radio)$/i,
+-	// checked="checked" or checked
+-	rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
+-	rscriptType = /^$|\/(?:java|ecma)script/i,
+-	rscriptTypeMasked = /^true\/(.*)/,
+-	rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,
+-
+-	// We have to close these tags to support XHTML (#13200)
+-	wrapMap = {
+-		option: [ 1, "<select multiple='multiple'>", "</select>" ],
+-		legend: [ 1, "<fieldset>", "</fieldset>" ],
+-		area: [ 1, "<map>", "</map>" ],
+-		param: [ 1, "<object>", "</object>" ],
+-		thead: [ 1, "<table>", "</table>" ],
+-		tr: [ 2, "<table><tbody>", "</tbody></table>" ],
+-		col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
+-		td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
+-
+-		// IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags,
+-		// unless wrapped in a div with non-breaking characters in front of it.
+-		_default: jQuery.support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X<div>", "</div>"  ]
+-	},
+-	safeFragment = createSafeFragment( document ),
+-	fragmentDiv = safeFragment.appendChild( document.createElement("div") );
++		// Make sure that the handler has a unique ID, used to find/remove it later
++		if ( !handler.guid ) {
++			handler.guid = jQuery.guid++;
++		}
+ 
+-wrapMap.optgroup = wrapMap.option;
+-wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
+-wrapMap.th = wrapMap.td;
++		// Init the element's event structure and main handler, if this is the first
++		if ( !( events = elemData.events ) ) {
++			events = elemData.events = {};
++		}
++		if ( !( eventHandle = elemData.handle ) ) {
++			eventHandle = elemData.handle = function( e ) {
+ 
+-jQuery.fn.extend({
+-	text: function( value ) {
+-		return jQuery.access( this, function( value ) {
+-			return value === undefined ?
+-				jQuery.text( this ) :
+-				this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) );
+-		}, null, value, arguments.length );
+-	},
++				// Discard the second event of a jQuery.event.trigger() and
++				// when an event is called after a page has unloaded
++				return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ?
++					jQuery.event.dispatch.apply( elem, arguments ) : undefined;
++			};
++		}
+ 
+-	append: function() {
+-		return this.domManip( arguments, function( elem ) {
+-			if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
+-				var target = manipulationTarget( this, elem );
+-				target.appendChild( elem );
+-			}
+-		});
+-	},
++		// Handle multiple events separated by a space
++		types = ( types || "" ).match( rnothtmlwhite ) || [ "" ];
++		t = types.length;
++		while ( t-- ) {
++			tmp = rtypenamespace.exec( types[ t ] ) || [];
++			type = origType = tmp[ 1 ];
++			namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort();
+ 
+-	prepend: function() {
+-		return this.domManip( arguments, function( elem ) {
+-			if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
+-				var target = manipulationTarget( this, elem );
+-				target.insertBefore( elem, target.firstChild );
++			// There *must* be a type, no attaching namespace-only handlers
++			if ( !type ) {
++				continue;
+ 			}
+-		});
+-	},
+ 
+-	before: function() {
+-		return this.domManip( arguments, function( elem ) {
+-			if ( this.parentNode ) {
+-				this.parentNode.insertBefore( elem, this );
+-			}
+-		});
+-	},
++			// If event changes its type, use the special event handlers for the changed type
++			special = jQuery.event.special[ type ] || {};
+ 
+-	after: function() {
+-		return this.domManip( arguments, function( elem ) {
+-			if ( this.parentNode ) {
+-				this.parentNode.insertBefore( elem, this.nextSibling );
+-			}
+-		});
+-	},
++			// If selector defined, determine special event api type, otherwise given type
++			type = ( selector ? special.delegateType : special.bindType ) || type;
+ 
+-	// keepData is for internal use only--do not document
+-	remove: function( selector, keepData ) {
+-		var elem,
+-			elems = selector ? jQuery.filter( selector, this ) : this,
+-			i = 0;
++			// Update special based on newly reset type
++			special = jQuery.event.special[ type ] || {};
++
++			// handleObj is passed to all event handlers
++			handleObj = jQuery.extend( {
++				type: type,
++				origType: origType,
++				data: data,
++				handler: handler,
++				guid: handler.guid,
++				selector: selector,
++				needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
++				namespace: namespaces.join( "." )
++			}, handleObjIn );
+ 
+-		for ( ; (elem = elems[i]) != null; i++ ) {
++			// Init the event handler queue if we're the first
++			if ( !( handlers = events[ type ] ) ) {
++				handlers = events[ type ] = [];
++				handlers.delegateCount = 0;
+ 
+-			if ( !keepData && elem.nodeType === 1 ) {
+-				jQuery.cleanData( getAll( elem ) );
+-			}
++				// Only use addEventListener if the special events handler returns false
++				if ( !special.setup ||
++					special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
+ 
+-			if ( elem.parentNode ) {
+-				if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) {
+-					setGlobalEval( getAll( elem, "script" ) );
++					if ( elem.addEventListener ) {
++						elem.addEventListener( type, eventHandle );
++					}
+ 				}
+-				elem.parentNode.removeChild( elem );
+ 			}
+-		}
+-
+-		return this;
+-	},
+ 
+-	empty: function() {
+-		var elem,
+-			i = 0;
++			if ( special.add ) {
++				special.add.call( elem, handleObj );
+ 
+-		for ( ; (elem = this[i]) != null; i++ ) {
+-			// Remove element nodes and prevent memory leaks
+-			if ( elem.nodeType === 1 ) {
+-				jQuery.cleanData( getAll( elem, false ) );
++				if ( !handleObj.handler.guid ) {
++					handleObj.handler.guid = handler.guid;
++				}
+ 			}
+ 
+-			// Remove any remaining nodes
+-			while ( elem.firstChild ) {
+-				elem.removeChild( elem.firstChild );
++			// Add to the element's handler list, delegates in front
++			if ( selector ) {
++				handlers.splice( handlers.delegateCount++, 0, handleObj );
++			} else {
++				handlers.push( handleObj );
+ 			}
+ 
+-			// If this is a select, ensure that it displays empty (#12336)
+-			// Support: IE<9
+-			if ( elem.options && jQuery.nodeName( elem, "select" ) ) {
+-				elem.options.length = 0;
+-			}
++			// Keep track of which events have ever been used, for event optimization
++			jQuery.event.global[ type ] = true;
+ 		}
+ 
+-		return this;
+ 	},
+ 
+-	clone: function( dataAndEvents, deepDataAndEvents ) {
+-		dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
+-		deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
+-
+-		return this.map( function () {
+-			return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
+-		});
+-	},
++	// Detach an event or set of events from an element
++	remove: function( elem, types, handler, selector, mappedTypes ) {
+ 
+-	html: function( value ) {
+-		return jQuery.access( this, function( value ) {
+-			var elem = this[0] || {},
+-				i = 0,
+-				l = this.length;
++		var j, origCount, tmp,
++			events, t, handleObj,
++			special, handlers, type, namespaces, origType,
++			elemData = dataPriv.hasData( elem ) && dataPriv.get( elem );
+ 
+-			if ( value === undefined ) {
+-				return elem.nodeType === 1 ?
+-					elem.innerHTML.replace( rinlinejQuery, "" ) :
+-					undefined;
+-			}
++		if ( !elemData || !( events = elemData.events ) ) {
++			return;
++		}
+ 
+-			// See if we can take a shortcut and just use innerHTML
+-			if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
+-				( jQuery.support.htmlSerialize || !rnoshimcache.test( value )  ) &&
+-				( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) &&
+-				!wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) {
++		// Once for each type.namespace in types; type may be omitted
++		types = ( types || "" ).match( rnothtmlwhite ) || [ "" ];
++		t = types.length;
++		while ( t-- ) {
++			tmp = rtypenamespace.exec( types[ t ] ) || [];
++			type = origType = tmp[ 1 ];
++			namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort();
+ 
+-				value = value.replace( rxhtmlTag, "<$1></$2>" );
++			// Unbind all events (on this namespace, if provided) for the element
++			if ( !type ) {
++				for ( type in events ) {
++					jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
++				}
++				continue;
++			}
+ 
+-				try {
+-					for (; i < l; i++ ) {
+-						// Remove element nodes and prevent memory leaks
+-						elem = this[i] || {};
+-						if ( elem.nodeType === 1 ) {
+-							jQuery.cleanData( getAll( elem, false ) );
+-							elem.innerHTML = value;
+-						}
+-					}
++			special = jQuery.event.special[ type ] || {};
++			type = ( selector ? special.delegateType : special.bindType ) || type;
++			handlers = events[ type ] || [];
++			tmp = tmp[ 2 ] &&
++				new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" );
+ 
+-					elem = 0;
++			// Remove matching events
++			origCount = j = handlers.length;
++			while ( j-- ) {
++				handleObj = handlers[ j ];
+ 
+-				// If using innerHTML throws an exception, use the fallback method
+-				} catch(e) {}
+-			}
++				if ( ( mappedTypes || origType === handleObj.origType ) &&
++					( !handler || handler.guid === handleObj.guid ) &&
++					( !tmp || tmp.test( handleObj.namespace ) ) &&
++					( !selector || selector === handleObj.selector ||
++						selector === "**" && handleObj.selector ) ) {
++					handlers.splice( j, 1 );
+ 
+-			if ( elem ) {
+-				this.empty().append( value );
++					if ( handleObj.selector ) {
++						handlers.delegateCount--;
++					}
++					if ( special.remove ) {
++						special.remove.call( elem, handleObj );
++					}
++				}
+ 			}
+-		}, null, value, arguments.length );
+-	},
+-
+-	replaceWith: function() {
+-		var
+-			// Snapshot the DOM in case .domManip sweeps something relevant into its fragment
+-			args = jQuery.map( this, function( elem ) {
+-				return [ elem.nextSibling, elem.parentNode ];
+-			}),
+-			i = 0;
+ 
+-		// Make the changes, replacing each context element with the new content
+-		this.domManip( arguments, function( elem ) {
+-			var next = args[ i++ ],
+-				parent = args[ i++ ];
++			// Remove generic event handler if we removed something and no more handlers exist
++			// (avoids potential for endless recursion during removal of special event handlers)
++			if ( origCount && !handlers.length ) {
++				if ( !special.teardown ||
++					special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
+ 
+-			if ( parent ) {
+-				// Don't use the snapshot next if it has moved (#13810)
+-				if ( next && next.parentNode !== parent ) {
+-					next = this.nextSibling;
++					jQuery.removeEvent( elem, type, elemData.handle );
+ 				}
+-				jQuery( this ).remove();
+-				parent.insertBefore( elem, next );
+-			}
+-		// Allow new content to include elements from the context set
+-		}, true );
+ 
+-		// Force removal if there was no new content (e.g., from empty arguments)
+-		return i ? this : this.remove();
+-	},
++				delete events[ type ];
++			}
++		}
+ 
+-	detach: function( selector ) {
+-		return this.remove( selector, true );
++		// Remove data and the expando if it's no longer used
++		if ( jQuery.isEmptyObject( events ) ) {
++			dataPriv.remove( elem, "handle events" );
++		}
+ 	},
+ 
+-	domManip: function( args, callback, allowIntersection ) {
++	dispatch: function( nativeEvent ) {
+ 
+-		// Flatten any nested arrays
+-		args = core_concat.apply( [], args );
++		// Make a writable jQuery.Event from the native event object
++		var event = jQuery.event.fix( nativeEvent );
+ 
+-		var first, node, hasScripts,
+-			scripts, doc, fragment,
+-			i = 0,
+-			l = this.length,
+-			set = this,
+-			iNoClone = l - 1,
+-			value = args[0],
+-			isFunction = jQuery.isFunction( value );
+-
+-		// We can't cloneNode fragments that contain checked, in WebKit
+-		if ( isFunction || !( l <= 1 || typeof value !== "string" || jQuery.support.checkClone || !rchecked.test( value ) ) ) {
+-			return this.each(function( index ) {
+-				var self = set.eq( index );
+-				if ( isFunction ) {
+-					args[0] = value.call( this, index, self.html() );
+-				}
+-				self.domManip( args, callback, allowIntersection );
+-			});
+-		}
++		var i, j, ret, matched, handleObj, handlerQueue,
++			args = new Array( arguments.length ),
++			handlers = ( dataPriv.get( this, "events" ) || {} )[ event.type ] || [],
++			special = jQuery.event.special[ event.type ] || {};
+ 
+-		if ( l ) {
+-			fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, !allowIntersection && this );
+-			first = fragment.firstChild;
++		// Use the fix-ed jQuery.Event rather than the (read-only) native event
++		args[ 0 ] = event;
+ 
+-			if ( fragment.childNodes.length === 1 ) {
+-				fragment = first;
+-			}
++		for ( i = 1; i < arguments.length; i++ ) {
++			args[ i ] = arguments[ i ];
++		}
+ 
+-			if ( first ) {
+-				scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
+-				hasScripts = scripts.length;
++		event.delegateTarget = this;
+ 
+-				// Use the original fragment for the last item instead of the first because it can end up
+-				// being emptied incorrectly in certain situations (#8070).
+-				for ( ; i < l; i++ ) {
+-					node = fragment;
++		// Call the preDispatch hook for the mapped type, and let it bail if desired
++		if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
++			return;
++		}
+ 
+-					if ( i !== iNoClone ) {
+-						node = jQuery.clone( node, true, true );
++		// Determine handlers
++		handlerQueue = jQuery.event.handlers.call( this, event, handlers );
+ 
+-						// Keep references to cloned scripts for later restoration
+-						if ( hasScripts ) {
+-							jQuery.merge( scripts, getAll( node, "script" ) );
+-						}
+-					}
++		// Run delegates first; they may want to stop propagation beneath us
++		i = 0;
++		while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) {
++			event.currentTarget = matched.elem;
+ 
+-					callback.call( this[i], node, i );
+-				}
++			j = 0;
++			while ( ( handleObj = matched.handlers[ j++ ] ) &&
++				!event.isImmediatePropagationStopped() ) {
+ 
+-				if ( hasScripts ) {
+-					doc = scripts[ scripts.length - 1 ].ownerDocument;
++				// If the event is namespaced, then each handler is only invoked if it is
++				// specially universal or its namespaces are a superset of the event's.
++				if ( !event.rnamespace || handleObj.namespace === false ||
++					event.rnamespace.test( handleObj.namespace ) ) {
+ 
+-					// Reenable scripts
+-					jQuery.map( scripts, restoreScript );
++					event.handleObj = handleObj;
++					event.data = handleObj.data;
+ 
+-					// Evaluate executable scripts on first document insertion
+-					for ( i = 0; i < hasScripts; i++ ) {
+-						node = scripts[ i ];
+-						if ( rscriptType.test( node.type || "" ) &&
+-							!jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) {
++					ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle ||
++						handleObj.handler ).apply( matched.elem, args );
+ 
+-							if ( node.src ) {
+-								// Hope ajax is available...
+-								jQuery._evalUrl( node.src );
+-							} else {
+-								jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) );
+-							}
++					if ( ret !== undefined ) {
++						if ( ( event.result = ret ) === false ) {
++							event.preventDefault();
++							event.stopPropagation();
+ 						}
+ 					}
+ 				}
+-
+-				// Fix #11809: Avoid leaking memory
+-				fragment = first = null;
+ 			}
+ 		}
+ 
+-		return this;
+-	}
+-});
++		// Call the postDispatch hook for the mapped type
++		if ( special.postDispatch ) {
++			special.postDispatch.call( this, event );
++		}
+ 
+-// Support: IE<8
+-// Manipulating tables requires a tbody
+-function manipulationTarget( elem, content ) {
+-	return jQuery.nodeName( elem, "table" ) &&
+-		jQuery.nodeName( content.nodeType === 1 ? content : content.firstChild, "tr" ) ?
++		return event.result;
++	},
+ 
+-		elem.getElementsByTagName("tbody")[0] ||
+-			elem.appendChild( elem.ownerDocument.createElement("tbody") ) :
+-		elem;
+-}
++	handlers: function( event, handlers ) {
++		var i, handleObj, sel, matchedHandlers, matchedSelectors,
++			handlerQueue = [],
++			delegateCount = handlers.delegateCount,
++			cur = event.target;
+ 
+-// Replace/restore the type attribute of script elements for safe DOM manipulation
+-function disableScript( elem ) {
+-	elem.type = (jQuery.find.attr( elem, "type" ) !== null) + "/" + elem.type;
+-	return elem;
+-}
+-function restoreScript( elem ) {
+-	var match = rscriptTypeMasked.exec( elem.type );
+-	if ( match ) {
+-		elem.type = match[1];
+-	} else {
+-		elem.removeAttribute("type");
+-	}
+-	return elem;
+-}
++		// Find delegate handlers
++		if ( delegateCount &&
+ 
+-// Mark scripts as having already been evaluated
+-function setGlobalEval( elems, refElements ) {
+-	var elem,
+-		i = 0;
+-	for ( ; (elem = elems[i]) != null; i++ ) {
+-		jQuery._data( elem, "globalEval", !refElements || jQuery._data( refElements[i], "globalEval" ) );
+-	}
+-}
++			// Support: IE <=9
++			// Black-hole SVG <use> instance trees (trac-13180)
++			cur.nodeType &&
+ 
+-function cloneCopyEvent( src, dest ) {
++			// Support: Firefox <=42
++			// Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861)
++			// https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click
++			// Support: IE 11 only
++			// ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343)
++			!( event.type === "click" && event.button >= 1 ) ) {
+ 
+-	if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
+-		return;
+-	}
++			for ( ; cur !== this; cur = cur.parentNode || this ) {
+ 
+-	var type, i, l,
+-		oldData = jQuery._data( src ),
+-		curData = jQuery._data( dest, oldData ),
+-		events = oldData.events;
++				// Don't check non-elements (#13208)
++				// Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
++				if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) {
++					matchedHandlers = [];
++					matchedSelectors = {};
++					for ( i = 0; i < delegateCount; i++ ) {
++						handleObj = handlers[ i ];
+ 
+-	if ( events ) {
+-		delete curData.handle;
+-		curData.events = {};
++						// Don't conflict with Object.prototype properties (#13203)
++						sel = handleObj.selector + " ";
+ 
+-		for ( type in events ) {
+-			for ( i = 0, l = events[ type ].length; i < l; i++ ) {
+-				jQuery.event.add( dest, type, events[ type ][ i ] );
++						if ( matchedSelectors[ sel ] === undefined ) {
++							matchedSelectors[ sel ] = handleObj.needsContext ?
++								jQuery( sel, this ).index( cur ) > -1 :
++								jQuery.find( sel, this, null, [ cur ] ).length;
++						}
++						if ( matchedSelectors[ sel ] ) {
++							matchedHandlers.push( handleObj );
++						}
++					}
++					if ( matchedHandlers.length ) {
++						handlerQueue.push( { elem: cur, handlers: matchedHandlers } );
++					}
++				}
+ 			}
+ 		}
+-	}
+ 
+-	// make the cloned public data object a copy from the original
+-	if ( curData.data ) {
+-		curData.data = jQuery.extend( {}, curData.data );
+-	}
+-}
+-
+-function fixCloneNodeIssues( src, dest ) {
+-	var nodeName, e, data;
++		// Add the remaining (directly-bound) handlers
++		cur = this;
++		if ( delegateCount < handlers.length ) {
++			handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } );
++		}
+ 
+-	// We do not need to do anything for non-Elements
+-	if ( dest.nodeType !== 1 ) {
+-		return;
+-	}
++		return handlerQueue;
++	},
+ 
+-	nodeName = dest.nodeName.toLowerCase();
++	addProp: function( name, hook ) {
++		Object.defineProperty( jQuery.Event.prototype, name, {
++			enumerable: true,
++			configurable: true,
+ 
+-	// IE6-8 copies events bound via attachEvent when using cloneNode.
+-	if ( !jQuery.support.noCloneEvent && dest[ jQuery.expando ] ) {
+-		data = jQuery._data( dest );
++			get: isFunction( hook ) ?
++				function() {
++					if ( this.originalEvent ) {
++							return hook( this.originalEvent );
++					}
++				} :
++				function() {
++					if ( this.originalEvent ) {
++							return this.originalEvent[ name ];
++					}
++				},
+ 
+-		for ( e in data.events ) {
+-			jQuery.removeEvent( dest, e, data.handle );
+-		}
++			set: function( value ) {
++				Object.defineProperty( this, name, {
++					enumerable: true,
++					configurable: true,
++					writable: true,
++					value: value
++				} );
++			}
++		} );
++	},
+ 
+-		// Event data gets referenced instead of copied if the expando gets copied too
+-		dest.removeAttribute( jQuery.expando );
+-	}
++	fix: function( originalEvent ) {
++		return originalEvent[ jQuery.expando ] ?
++			originalEvent :
++			new jQuery.Event( originalEvent );
++	},
+ 
+-	// IE blanks contents when cloning scripts, and tries to evaluate newly-set text
+-	if ( nodeName === "script" && dest.text !== src.text ) {
+-		disableScript( dest ).text = src.text;
+-		restoreScript( dest );
++	special: {
++		load: {
+ 
+-	// IE6-10 improperly clones children of object elements using classid.
+-	// IE10 throws NoModificationAllowedError if parent is null, #12132.
+-	} else if ( nodeName === "object" ) {
+-		if ( dest.parentNode ) {
+-			dest.outerHTML = src.outerHTML;
+-		}
++			// Prevent triggered image.load events from bubbling to window.load
++			noBubble: true
++		},
++		click: {
+ 
+-		// This path appears unavoidable for IE9. When cloning an object
+-		// element in IE9, the outerHTML strategy above is not sufficient.
+-		// If the src has innerHTML and the destination does not,
+-		// copy the src.innerHTML into the dest.innerHTML. #10324
+-		if ( jQuery.support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) {
+-			dest.innerHTML = src.innerHTML;
+-		}
++			// Utilize native event to ensure correct state for checkable inputs
++			setup: function( data ) {
+ 
+-	} else if ( nodeName === "input" && manipulation_rcheckableType.test( src.type ) ) {
+-		// IE6-8 fails to persist the checked state of a cloned checkbox
+-		// or radio button. Worse, IE6-7 fail to give the cloned element
+-		// a checked appearance if the defaultChecked value isn't also set
++				// For mutual compressibility with _default, replace `this` access with a local var.
++				// `|| data` is dead code meant only to preserve the variable through minification.
++				var el = this || data;
+ 
+-		dest.defaultChecked = dest.checked = src.checked;
++				// Claim the first handler
++				if ( rcheckableType.test( el.type ) &&
++					el.click && nodeName( el, "input" ) ) {
+ 
+-		// IE6-7 get confused and end up setting the value of a cloned
+-		// checkbox/radio button to an empty string instead of "on"
+-		if ( dest.value !== src.value ) {
+-			dest.value = src.value;
+-		}
++					// dataPriv.set( el, "click", ... )
++					leverageNative( el, "click", returnTrue );
++				}
+ 
+-	// IE6-8 fails to return the selected option to the default selected
+-	// state when cloning options
+-	} else if ( nodeName === "option" ) {
+-		dest.defaultSelected = dest.selected = src.defaultSelected;
++				// Return false to allow normal processing in the caller
++				return false;
++			},
++			trigger: function( data ) {
+ 
+-	// IE6-8 fails to set the defaultValue to the correct value when
+-	// cloning other types of input fields
+-	} else if ( nodeName === "input" || nodeName === "textarea" ) {
+-		dest.defaultValue = src.defaultValue;
+-	}
+-}
++				// For mutual compressibility with _default, replace `this` access with a local var.
++				// `|| data` is dead code meant only to preserve the variable through minification.
++				var el = this || data;
+ 
+-jQuery.each({
+-	appendTo: "append",
+-	prependTo: "prepend",
+-	insertBefore: "before",
+-	insertAfter: "after",
+-	replaceAll: "replaceWith"
+-}, function( name, original ) {
+-	jQuery.fn[ name ] = function( selector ) {
+-		var elems,
+-			i = 0,
+-			ret = [],
+-			insert = jQuery( selector ),
+-			last = insert.length - 1;
++				// Force setup before triggering a click
++				if ( rcheckableType.test( el.type ) &&
++					el.click && nodeName( el, "input" ) ) {
+ 
+-		for ( ; i <= last; i++ ) {
+-			elems = i === last ? this : this.clone(true);
+-			jQuery( insert[i] )[ original ]( elems );
++					leverageNative( el, "click" );
++				}
+ 
+-			// Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get()
+-			core_push.apply( ret, elems.get() );
+-		}
++				// Return non-false to allow normal event-path propagation
++				return true;
++			},
+ 
+-		return this.pushStack( ret );
+-	};
+-});
++			// For cross-browser consistency, suppress native .click() on links
++			// Also prevent it if we're currently inside a leveraged native-event stack
++			_default: function( event ) {
++				var target = event.target;
++				return rcheckableType.test( target.type ) &&
++					target.click && nodeName( target, "input" ) &&
++					dataPriv.get( target, "click" ) ||
++					nodeName( target, "a" );
++			}
++		},
+ 
+-function getAll( context, tag ) {
+-	var elems, elem,
+-		i = 0,
+-		found = typeof context.getElementsByTagName !== core_strundefined ? context.getElementsByTagName( tag || "*" ) :
+-			typeof context.querySelectorAll !== core_strundefined ? context.querySelectorAll( tag || "*" ) :
+-			undefined;
++		beforeunload: {
++			postDispatch: function( event ) {
+ 
+-	if ( !found ) {
+-		for ( found = [], elems = context.childNodes || context; (elem = elems[i]) != null; i++ ) {
+-			if ( !tag || jQuery.nodeName( elem, tag ) ) {
+-				found.push( elem );
+-			} else {
+-				jQuery.merge( found, getAll( elem, tag ) );
++				// Support: Firefox 20+
++				// Firefox doesn't alert if the returnValue field is not set.
++				if ( event.result !== undefined && event.originalEvent ) {
++					event.originalEvent.returnValue = event.result;
++				}
+ 			}
+ 		}
+ 	}
++};
+ 
+-	return tag === undefined || tag && jQuery.nodeName( context, tag ) ?
+-		jQuery.merge( [ context ], found ) :
+-		found;
+-}
++// Ensure the presence of an event listener that handles manually-triggered
++// synthetic events by interrupting progress until reinvoked in response to
++// *native* events that it fires directly, ensuring that state changes have
++// already occurred before other listeners are invoked.
++function leverageNative( el, type, expectSync ) {
+ 
+-// Used in buildFragment, fixes the defaultChecked property
+-function fixDefaultChecked( elem ) {
+-	if ( manipulation_rcheckableType.test( elem.type ) ) {
+-		elem.defaultChecked = elem.checked;
++	// Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add
++	if ( !expectSync ) {
++		if ( dataPriv.get( el, type ) === undefined ) {
++			jQuery.event.add( el, type, returnTrue );
++		}
++		return;
+ 	}
+-}
+-
+-jQuery.extend({
+-	clone: function( elem, dataAndEvents, deepDataAndEvents ) {
+-		var destElements, node, clone, i, srcElements,
+-			inPage = jQuery.contains( elem.ownerDocument, elem );
+ 
+-		if ( jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) {
+-			clone = elem.cloneNode( true );
++	// Register the controller as a special universal handler for all event namespaces
++	dataPriv.set( el, type, false );
++	jQuery.event.add( el, type, {
++		namespace: false,
++		handler: function( event ) {
++			var notAsync, result,
++				saved = dataPriv.get( this, type );
++
++			if ( ( event.isTrigger & 1 ) && this[ type ] ) {
++
++				// Interrupt processing of the outer synthetic .trigger()ed event
++				// Saved data should be false in such cases, but might be a leftover capture object
++				// from an async native handler (gh-4350)
++				if ( !saved.length ) {
++
++					// Store arguments for use when handling the inner native event
++					// There will always be at least one argument (an event object), so this array
++					// will not be confused with a leftover capture object.
++					saved = slice.call( arguments );
++					dataPriv.set( this, type, saved );
++
++					// Trigger the native event and capture its result
++					// Support: IE <=9 - 11+
++					// focus() and blur() are asynchronous
++					notAsync = expectSync( this, type );
++					this[ type ]();
++					result = dataPriv.get( this, type );
++					if ( saved !== result || notAsync ) {
++						dataPriv.set( this, type, false );
++					} else {
++						result = {};
++					}
++					if ( saved !== result ) {
+ 
+-		// IE<=8 does not properly clone detached, unknown element nodes
+-		} else {
+-			fragmentDiv.innerHTML = elem.outerHTML;
+-			fragmentDiv.removeChild( clone = fragmentDiv.firstChild );
+-		}
++						// Cancel the outer synthetic event
++						event.stopImmediatePropagation();
++						event.preventDefault();
++						return result.value;
++					}
+ 
+-		if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) &&
+-				(elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {
++				// If this is an inner synthetic event for an event with a bubbling surrogate
++				// (focus or blur), assume that the surrogate already propagated from triggering the
++				// native event and prevent that from happening again here.
++				// This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the
++				// bubbling surrogate propagates *after* the non-bubbling base), but that seems
++				// less bad than duplication.
++				} else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) {
++					event.stopPropagation();
++				}
+ 
+-			// We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2
+-			destElements = getAll( clone );
+-			srcElements = getAll( elem );
++			// If this is a native event triggered above, everything is now in order
++			// Fire an inner synthetic event with the original arguments
++			} else if ( saved.length ) {
+ 
+-			// Fix all IE cloning issues
+-			for ( i = 0; (node = srcElements[i]) != null; ++i ) {
+-				// Ensure that the destination node is not null; Fixes #9587
+-				if ( destElements[i] ) {
+-					fixCloneNodeIssues( node, destElements[i] );
+-				}
+-			}
+-		}
++				// ...and capture the result
++				dataPriv.set( this, type, {
++					value: jQuery.event.trigger(
+ 
+-		// Copy the events from the original to the clone
+-		if ( dataAndEvents ) {
+-			if ( deepDataAndEvents ) {
+-				srcElements = srcElements || getAll( elem );
+-				destElements = destElements || getAll( clone );
++						// Support: IE <=9 - 11+
++						// Extend with the prototype to reset the above stopImmediatePropagation()
++						jQuery.extend( saved[ 0 ], jQuery.Event.prototype ),
++						saved.slice( 1 ),
++						this
++					)
++				} );
+ 
+-				for ( i = 0; (node = srcElements[i]) != null; i++ ) {
+-					cloneCopyEvent( node, destElements[i] );
+-				}
+-			} else {
+-				cloneCopyEvent( elem, clone );
++				// Abort handling of the native event
++				event.stopImmediatePropagation();
+ 			}
+ 		}
++	} );
++}
+ 
+-		// Preserve script evaluation history
+-		destElements = getAll( clone, "script" );
+-		if ( destElements.length > 0 ) {
+-			setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
+-		}
++jQuery.removeEvent = function( elem, type, handle ) {
+ 
+-		destElements = srcElements = node = null;
++	// This "if" is needed for plain objects
++	if ( elem.removeEventListener ) {
++		elem.removeEventListener( type, handle );
++	}
++};
+ 
+-		// Return the cloned set
+-		return clone;
+-	},
++jQuery.Event = function( src, props ) {
+ 
+-	buildFragment: function( elems, context, scripts, selection ) {
+-		var j, elem, contains,
+-			tmp, tag, tbody, wrap,
+-			l = elems.length,
++	// Allow instantiation without the 'new' keyword
++	if ( !( this instanceof jQuery.Event ) ) {
++		return new jQuery.Event( src, props );
++	}
+ 
+-			// Ensure a safe fragment
+-			safe = createSafeFragment( context ),
++	// Event object
++	if ( src && src.type ) {
++		this.originalEvent = src;
++		this.type = src.type;
+ 
+-			nodes = [],
+-			i = 0;
++		// Events bubbling up the document may have been marked as prevented
++		// by a handler lower down the tree; reflect the correct value.
++		this.isDefaultPrevented = src.defaultPrevented ||
++				src.defaultPrevented === undefined &&
+ 
+-		for ( ; i < l; i++ ) {
+-			elem = elems[ i ];
++				// Support: Android <=2.3 only
++				src.returnValue === false ?
++			returnTrue :
++			returnFalse;
+ 
+-			if ( elem || elem === 0 ) {
++		// Create target properties
++		// Support: Safari <=6 - 7 only
++		// Target should not be a text node (#504, #13143)
++		this.target = ( src.target && src.target.nodeType === 3 ) ?
++			src.target.parentNode :
++			src.target;
+ 
+-				// Add nodes directly
+-				if ( jQuery.type( elem ) === "object" ) {
+-					jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );
++		this.currentTarget = src.currentTarget;
++		this.relatedTarget = src.relatedTarget;
+ 
+-				// Convert non-html into a text node
+-				} else if ( !rhtml.test( elem ) ) {
+-					nodes.push( context.createTextNode( elem ) );
++	// Event type
++	} else {
++		this.type = src;
++	}
+ 
+-				// Convert html into DOM nodes
+-				} else {
+-					tmp = tmp || safe.appendChild( context.createElement("div") );
++	// Put explicitly provided properties onto the event object
++	if ( props ) {
++		jQuery.extend( this, props );
++	}
+ 
+-					// Deserialize a standard representation
+-					tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase();
+-					wrap = wrapMap[ tag ] || wrapMap._default;
++	// Create a timestamp if incoming event doesn't have one
++	this.timeStamp = src && src.timeStamp || Date.now();
+ 
+-					tmp.innerHTML = wrap[1] + elem.replace( rxhtmlTag, "<$1></$2>" ) + wrap[2];
++	// Mark it as fixed
++	this[ jQuery.expando ] = true;
++};
+ 
+-					// Descend through wrappers to the right content
+-					j = wrap[0];
+-					while ( j-- ) {
+-						tmp = tmp.lastChild;
+-					}
++// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
++// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
++jQuery.Event.prototype = {
++	constructor: jQuery.Event,
++	isDefaultPrevented: returnFalse,
++	isPropagationStopped: returnFalse,
++	isImmediatePropagationStopped: returnFalse,
++	isSimulated: false,
+ 
+-					// Manually add leading whitespace removed by IE
+-					if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
+-						nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) );
+-					}
++	preventDefault: function() {
++		var e = this.originalEvent;
+ 
+-					// Remove IE's autoinserted <tbody> from table fragments
+-					if ( !jQuery.support.tbody ) {
++		this.isDefaultPrevented = returnTrue;
+ 
+-						// String was a <table>, *may* have spurious <tbody>
+-						elem = tag === "table" && !rtbody.test( elem ) ?
+-							tmp.firstChild :
++		if ( e && !this.isSimulated ) {
++			e.preventDefault();
++		}
++	},
++	stopPropagation: function() {
++		var e = this.originalEvent;
+ 
+-							// String was a bare <thead> or <tfoot>
+-							wrap[1] === "<table>" && !rtbody.test( elem ) ?
+-								tmp :
+-								0;
++		this.isPropagationStopped = returnTrue;
+ 
+-						j = elem && elem.childNodes.length;
+-						while ( j-- ) {
+-							if ( jQuery.nodeName( (tbody = elem.childNodes[j]), "tbody" ) && !tbody.childNodes.length ) {
+-								elem.removeChild( tbody );
+-							}
+-						}
+-					}
++		if ( e && !this.isSimulated ) {
++			e.stopPropagation();
++		}
++	},
++	stopImmediatePropagation: function() {
++		var e = this.originalEvent;
+ 
+-					jQuery.merge( nodes, tmp.childNodes );
++		this.isImmediatePropagationStopped = returnTrue;
+ 
+-					// Fix #12392 for WebKit and IE > 9
+-					tmp.textContent = "";
++		if ( e && !this.isSimulated ) {
++			e.stopImmediatePropagation();
++		}
+ 
+-					// Fix #12392 for oldIE
+-					while ( tmp.firstChild ) {
+-						tmp.removeChild( tmp.firstChild );
+-					}
++		this.stopPropagation();
++	}
++};
+ 
+-					// Remember the top-level container for proper cleanup
+-					tmp = safe.lastChild;
+-				}
++// Includes all common event props including KeyEvent and MouseEvent specific props
++jQuery.each( {
++	altKey: true,
++	bubbles: true,
++	cancelable: true,
++	changedTouches: true,
++	ctrlKey: true,
++	detail: true,
++	eventPhase: true,
++	metaKey: true,
++	pageX: true,
++	pageY: true,
++	shiftKey: true,
++	view: true,
++	"char": true,
++	code: true,
++	charCode: true,
++	key: true,
++	keyCode: true,
++	button: true,
++	buttons: true,
++	clientX: true,
++	clientY: true,
++	offsetX: true,
++	offsetY: true,
++	pointerId: true,
++	pointerType: true,
++	screenX: true,
++	screenY: true,
++	targetTouches: true,
++	toElement: true,
++	touches: true,
++
++	which: function( event ) {
++		var button = event.button;
++
++		// Add which for key events
++		if ( event.which == null && rkeyEvent.test( event.type ) ) {
++			return event.charCode != null ? event.charCode : event.keyCode;
++		}
++
++		// Add which for click: 1 === left; 2 === middle; 3 === right
++		if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) {
++			if ( button & 1 ) {
++				return 1;
++			}
++
++			if ( button & 2 ) {
++				return 3;
++			}
++
++			if ( button & 4 ) {
++				return 2;
+ 			}
+-		}
+ 
+-		// Fix #11356: Clear elements from fragment
+-		if ( tmp ) {
+-			safe.removeChild( tmp );
++			return 0;
+ 		}
+ 
+-		// Reset defaultChecked for any radios and checkboxes
+-		// about to be appended to the DOM in IE 6/7 (#8060)
+-		if ( !jQuery.support.appendChecked ) {
+-			jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked );
+-		}
++		return event.which;
++	}
++}, jQuery.event.addProp );
+ 
+-		i = 0;
+-		while ( (elem = nodes[ i++ ]) ) {
++jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) {
++	jQuery.event.special[ type ] = {
+ 
+-			// #4087 - If origin and destination elements are the same, and this is
+-			// that element, do not do anything
+-			if ( selection && jQuery.inArray( elem, selection ) !== -1 ) {
+-				continue;
+-			}
++		// Utilize native event if possible so blur/focus sequence is correct
++		setup: function() {
+ 
+-			contains = jQuery.contains( elem.ownerDocument, elem );
++			// Claim the first handler
++			// dataPriv.set( this, "focus", ... )
++			// dataPriv.set( this, "blur", ... )
++			leverageNative( this, type, expectSync );
+ 
+-			// Append to fragment
+-			tmp = getAll( safe.appendChild( elem ), "script" );
++			// Return false to allow normal processing in the caller
++			return false;
++		},
++		trigger: function() {
+ 
+-			// Preserve script evaluation history
+-			if ( contains ) {
+-				setGlobalEval( tmp );
+-			}
++			// Force setup before trigger
++			leverageNative( this, type );
+ 
+-			// Capture executables
+-			if ( scripts ) {
+-				j = 0;
+-				while ( (elem = tmp[ j++ ]) ) {
+-					if ( rscriptType.test( elem.type || "" ) ) {
+-						scripts.push( elem );
+-					}
+-				}
+-			}
+-		}
++			// Return non-false to allow normal event-path propagation
++			return true;
++		},
+ 
+-		tmp = null;
++		delegateType: delegateType
++	};
++} );
+ 
+-		return safe;
+-	},
++// Create mouseenter/leave events using mouseover/out and event-time checks
++// so that event delegation works in jQuery.
++// Do the same for pointerenter/pointerleave and pointerover/pointerout
++//
++// Support: Safari 7 only
++// Safari sends mouseenter too often; see:
++// https://bugs.chromium.org/p/chromium/issues/detail?id=470258
++// for the description of the bug (it existed in older Chrome versions as well).
++jQuery.each( {
++	mouseenter: "mouseover",
++	mouseleave: "mouseout",
++	pointerenter: "pointerover",
++	pointerleave: "pointerout"
++}, function( orig, fix ) {
++	jQuery.event.special[ orig ] = {
++		delegateType: fix,
++		bindType: fix,
+ 
+-	cleanData: function( elems, /* internal */ acceptData ) {
+-		var elem, type, id, data,
+-			i = 0,
+-			internalKey = jQuery.expando,
+-			cache = jQuery.cache,
+-			deleteExpando = jQuery.support.deleteExpando,
+-			special = jQuery.event.special;
++		handle: function( event ) {
++			var ret,
++				target = this,
++				related = event.relatedTarget,
++				handleObj = event.handleObj;
+ 
+-		for ( ; (elem = elems[i]) != null; i++ ) {
++			// For mouseenter/leave call the handler if related is outside the target.
++			// NB: No relatedTarget if the mouse left/entered the browser window
++			if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) {
++				event.type = handleObj.origType;
++				ret = handleObj.handler.apply( this, arguments );
++				event.type = fix;
++			}
++			return ret;
++		}
++	};
++} );
+ 
+-			if ( acceptData || jQuery.acceptData( elem ) ) {
++jQuery.fn.extend( {
+ 
+-				id = elem[ internalKey ];
+-				data = id && cache[ id ];
++	on: function( types, selector, data, fn ) {
++		return on( this, types, selector, data, fn );
++	},
++	one: function( types, selector, data, fn ) {
++		return on( this, types, selector, data, fn, 1 );
++	},
++	off: function( types, selector, fn ) {
++		var handleObj, type;
++		if ( types && types.preventDefault && types.handleObj ) {
+ 
+-				if ( data ) {
+-					if ( data.events ) {
+-						for ( type in data.events ) {
+-							if ( special[ type ] ) {
+-								jQuery.event.remove( elem, type );
++			// ( event )  dispatched jQuery.Event
++			handleObj = types.handleObj;
++			jQuery( types.delegateTarget ).off(
++				handleObj.namespace ?
++					handleObj.origType + "." + handleObj.namespace :
++					handleObj.origType,
++				handleObj.selector,
++				handleObj.handler
++			);
++			return this;
++		}
++		if ( typeof types === "object" ) {
+ 
+-							// This is a shortcut to avoid jQuery.event.remove's overhead
+-							} else {
+-								jQuery.removeEvent( elem, type, data.handle );
+-							}
+-						}
+-					}
++			// ( types-object [, selector] )
++			for ( type in types ) {
++				this.off( type, selector, types[ type ] );
++			}
++			return this;
++		}
++		if ( selector === false || typeof selector === "function" ) {
+ 
+-					// Remove cache only if it was not already removed by jQuery.event.remove
+-					if ( cache[ id ] ) {
++			// ( types [, fn] )
++			fn = selector;
++			selector = undefined;
++		}
++		if ( fn === false ) {
++			fn = returnFalse;
++		}
++		return this.each( function() {
++			jQuery.event.remove( this, types, fn, selector );
++		} );
++	}
++} );
+ 
+-						delete cache[ id ];
+ 
+-						// IE does not allow us to delete expando properties from nodes,
+-						// nor does it have a removeAttribute function on Document nodes;
+-						// we must handle all of these cases
+-						if ( deleteExpando ) {
+-							delete elem[ internalKey ];
++var
+ 
+-						} else if ( typeof elem.removeAttribute !== core_strundefined ) {
+-							elem.removeAttribute( internalKey );
++	/* eslint-disable max-len */
+ 
+-						} else {
+-							elem[ internalKey ] = null;
+-						}
++	// See https://github.com/eslint/eslint/issues/3229
++	rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi,
+ 
+-						core_deletedIds.push( id );
+-					}
+-				}
+-			}
+-		}
+-	},
++	/* eslint-enable */
+ 
+-	_evalUrl: function( url ) {
+-		return jQuery.ajax({
+-			url: url,
+-			type: "GET",
+-			dataType: "script",
+-			async: false,
+-			global: false,
+-			"throws": true
+-		});
+-	}
+-});
+-jQuery.fn.extend({
+-	wrapAll: function( html ) {
+-		if ( jQuery.isFunction( html ) ) {
+-			return this.each(function(i) {
+-				jQuery(this).wrapAll( html.call(this, i) );
+-			});
+-		}
++	// Support: IE <=10 - 11, Edge 12 - 13 only
++	// In IE/Edge using regex groups here causes severe slowdowns.
++	// See https://connect.microsoft.com/IE/feedback/details/1736512/
++	rnoInnerhtml = /<script|<style|<link/i,
+ 
+-		if ( this[0] ) {
+-			// The elements to wrap the target around
+-			var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
++	// checked="checked" or checked
++	rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
++	rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;
+ 
+-			if ( this[0].parentNode ) {
+-				wrap.insertBefore( this[0] );
+-			}
++// Prefer a tbody over its parent table for containing new rows
++function manipulationTarget( elem, content ) {
++	if ( nodeName( elem, "table" ) &&
++		nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) {
+ 
+-			wrap.map(function() {
+-				var elem = this;
++		return jQuery( elem ).children( "tbody" )[ 0 ] || elem;
++	}
+ 
+-				while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
+-					elem = elem.firstChild;
+-				}
++	return elem;
++}
+ 
+-				return elem;
+-			}).append( this );
+-		}
++// Replace/restore the type attribute of script elements for safe DOM manipulation
++function disableScript( elem ) {
++	elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type;
++	return elem;
++}
++function restoreScript( elem ) {
++	if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) {
++		elem.type = elem.type.slice( 5 );
++	} else {
++		elem.removeAttribute( "type" );
++	}
+ 
+-		return this;
+-	},
++	return elem;
++}
+ 
+-	wrapInner: function( html ) {
+-		if ( jQuery.isFunction( html ) ) {
+-			return this.each(function(i) {
+-				jQuery(this).wrapInner( html.call(this, i) );
+-			});
+-		}
++function cloneCopyEvent( src, dest ) {
++	var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events;
+ 
+-		return this.each(function() {
+-			var self = jQuery( this ),
+-				contents = self.contents();
++	if ( dest.nodeType !== 1 ) {
++		return;
++	}
+ 
+-			if ( contents.length ) {
+-				contents.wrapAll( html );
++	// 1. Copy private data: events, handlers, etc.
++	if ( dataPriv.hasData( src ) ) {
++		pdataOld = dataPriv.access( src );
++		pdataCur = dataPriv.set( dest, pdataOld );
++		events = pdataOld.events;
+ 
+-			} else {
+-				self.append( html );
++		if ( events ) {
++			delete pdataCur.handle;
++			pdataCur.events = {};
++
++			for ( type in events ) {
++				for ( i = 0, l = events[ type ].length; i < l; i++ ) {
++					jQuery.event.add( dest, type, events[ type ][ i ] );
++				}
+ 			}
+-		});
+-	},
++		}
++	}
+ 
+-	wrap: function( html ) {
+-		var isFunction = jQuery.isFunction( html );
++	// 2. Copy user data
++	if ( dataUser.hasData( src ) ) {
++		udataOld = dataUser.access( src );
++		udataCur = jQuery.extend( {}, udataOld );
+ 
+-		return this.each(function(i) {
+-			jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );
+-		});
+-	},
++		dataUser.set( dest, udataCur );
++	}
++}
+ 
+-	unwrap: function() {
+-		return this.parent().each(function() {
+-			if ( !jQuery.nodeName( this, "body" ) ) {
+-				jQuery( this ).replaceWith( this.childNodes );
+-			}
+-		}).end();
++// Fix IE bugs, see support tests
++function fixInput( src, dest ) {
++	var nodeName = dest.nodeName.toLowerCase();
++
++	// Fails to persist the checked state of a cloned checkbox or radio button.
++	if ( nodeName === "input" && rcheckableType.test( src.type ) ) {
++		dest.checked = src.checked;
++
++	// Fails to return the selected option to the default selected state when cloning options
++	} else if ( nodeName === "input" || nodeName === "textarea" ) {
++		dest.defaultValue = src.defaultValue;
+ 	}
+-});
+-var iframe, getStyles, curCSS,
+-	ralpha = /alpha\([^)]*\)/i,
+-	ropacity = /opacity\s*=\s*([^)]*)/,
+-	rposition = /^(top|right|bottom|left)$/,
+-	// swappable if display is none or starts with table except "table", "table-cell", or "table-caption"
+-	// see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
+-	rdisplayswap = /^(none|table(?!-c[ea]).+)/,
+-	rmargin = /^margin/,
+-	rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ),
+-	rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ),
+-	rrelNum = new RegExp( "^([+-])=(" + core_pnum + ")", "i" ),
+-	elemdisplay = { BODY: "block" },
++}
+ 
+-	cssShow = { position: "absolute", visibility: "hidden", display: "block" },
+-	cssNormalTransform = {
+-		letterSpacing: 0,
+-		fontWeight: 400
+-	},
++function domManip( collection, args, callback, ignored ) {
+ 
+-	cssExpand = [ "Top", "Right", "Bottom", "Left" ],
+-	cssPrefixes = [ "Webkit", "O", "Moz", "ms" ];
++	// Flatten any nested arrays
++	args = concat.apply( [], args );
+ 
+-// return a css property mapped to a potentially vendor prefixed property
+-function vendorPropName( style, name ) {
++	var fragment, first, scripts, hasScripts, node, doc,
++		i = 0,
++		l = collection.length,
++		iNoClone = l - 1,
++		value = args[ 0 ],
++		valueIsFunction = isFunction( value );
+ 
+-	// shortcut for names that are not vendor prefixed
+-	if ( name in style ) {
+-		return name;
++	// We can't cloneNode fragments that contain checked, in WebKit
++	if ( valueIsFunction ||
++			( l > 1 && typeof value === "string" &&
++				!support.checkClone && rchecked.test( value ) ) ) {
++		return collection.each( function( index ) {
++			var self = collection.eq( index );
++			if ( valueIsFunction ) {
++				args[ 0 ] = value.call( this, index, self.html() );
++			}
++			domManip( self, args, callback, ignored );
++		} );
+ 	}
+ 
+-	// check for vendor prefixed names
+-	var capName = name.charAt(0).toUpperCase() + name.slice(1),
+-		origName = name,
+-		i = cssPrefixes.length;
++	if ( l ) {
++		fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored );
++		first = fragment.firstChild;
+ 
+-	while ( i-- ) {
+-		name = cssPrefixes[ i ] + capName;
+-		if ( name in style ) {
+-			return name;
++		if ( fragment.childNodes.length === 1 ) {
++			fragment = first;
+ 		}
+-	}
+ 
+-	return origName;
+-}
++		// Require either new content or an interest in ignored elements to invoke the callback
++		if ( first || ignored ) {
++			scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
++			hasScripts = scripts.length;
+ 
+-function isHidden( elem, el ) {
+-	// isHidden might be called from jQuery#filter function;
+-	// in that case, element will be second argument
+-	elem = el || elem;
+-	return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem );
+-}
++			// Use the original fragment for the last item
++			// instead of the first because it can end up
++			// being emptied incorrectly in certain situations (#8070).
++			for ( ; i < l; i++ ) {
++				node = fragment;
+ 
+-function showHide( elements, show ) {
+-	var display, elem, hidden,
+-		values = [],
+-		index = 0,
+-		length = elements.length;
++				if ( i !== iNoClone ) {
++					node = jQuery.clone( node, true, true );
+ 
+-	for ( ; index < length; index++ ) {
+-		elem = elements[ index ];
+-		if ( !elem.style ) {
+-			continue;
+-		}
++					// Keep references to cloned scripts for later restoration
++					if ( hasScripts ) {
+ 
+-		values[ index ] = jQuery._data( elem, "olddisplay" );
+-		display = elem.style.display;
+-		if ( show ) {
+-			// Reset the inline display of this element to learn if it is
+-			// being hidden by cascaded rules or not
+-			if ( !values[ index ] && display === "none" ) {
+-				elem.style.display = "";
+-			}
++						// Support: Android <=4.0 only, PhantomJS 1 only
++						// push.apply(_, arraylike) throws on ancient WebKit
++						jQuery.merge( scripts, getAll( node, "script" ) );
++					}
++				}
+ 
+-			// Set elements which have been overridden with display: none
+-			// in a stylesheet to whatever the default browser style is
+-			// for such an element
+-			if ( elem.style.display === "" && isHidden( elem ) ) {
+-				values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) );
++				callback.call( collection[ i ], node, i );
+ 			}
+-		} else {
+ 
+-			if ( !values[ index ] ) {
+-				hidden = isHidden( elem );
++			if ( hasScripts ) {
++				doc = scripts[ scripts.length - 1 ].ownerDocument;
++
++				// Reenable scripts
++				jQuery.map( scripts, restoreScript );
+ 
+-				if ( display && display !== "none" || !hidden ) {
+-					jQuery._data( elem, "olddisplay", hidden ? display : jQuery.css( elem, "display" ) );
++				// Evaluate executable scripts on first document insertion
++				for ( i = 0; i < hasScripts; i++ ) {
++					node = scripts[ i ];
++					if ( rscriptType.test( node.type || "" ) &&
++						!dataPriv.access( node, "globalEval" ) &&
++						jQuery.contains( doc, node ) ) {
++
++						if ( node.src && ( node.type || "" ).toLowerCase()  !== "module" ) {
++
++							// Optional AJAX dependency, but won't run scripts if not present
++							if ( jQuery._evalUrl && !node.noModule ) {
++								jQuery._evalUrl( node.src, {
++									nonce: node.nonce || node.getAttribute( "nonce" )
++								} );
++							}
++						} else {
++							DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc );
++						}
++					}
+ 				}
+ 			}
+ 		}
+ 	}
+ 
+-	// Set the display of most of the elements in a second loop
+-	// to avoid the constant reflow
+-	for ( index = 0; index < length; index++ ) {
+-		elem = elements[ index ];
+-		if ( !elem.style ) {
+-			continue;
++	return collection;
++}
++
++function remove( elem, selector, keepData ) {
++	var node,
++		nodes = selector ? jQuery.filter( selector, elem ) : elem,
++		i = 0;
++
++	for ( ; ( node = nodes[ i ] ) != null; i++ ) {
++		if ( !keepData && node.nodeType === 1 ) {
++			jQuery.cleanData( getAll( node ) );
+ 		}
+-		if ( !show || elem.style.display === "none" || elem.style.display === "" ) {
+-			elem.style.display = show ? values[ index ] || "" : "none";
++
++		if ( node.parentNode ) {
++			if ( keepData && isAttached( node ) ) {
++				setGlobalEval( getAll( node, "script" ) );
++			}
++			node.parentNode.removeChild( node );
+ 		}
+ 	}
+ 
+-	return elements;
++	return elem;
+ }
+ 
+-jQuery.fn.extend({
+-	css: function( name, value ) {
+-		return jQuery.access( this, function( elem, name, value ) {
+-			var len, styles,
+-				map = {},
+-				i = 0;
++jQuery.extend( {
++	htmlPrefilter: function( html ) {
++		return html.replace( rxhtmlTag, "<$1></$2>" );
++	},
+ 
+-			if ( jQuery.isArray( name ) ) {
+-				styles = getStyles( elem );
+-				len = name.length;
++	clone: function( elem, dataAndEvents, deepDataAndEvents ) {
++		var i, l, srcElements, destElements,
++			clone = elem.cloneNode( true ),
++			inPage = isAttached( elem );
+ 
+-				for ( ; i < len; i++ ) {
+-					map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
+-				}
++		// Fix IE cloning issues
++		if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) &&
++				!jQuery.isXMLDoc( elem ) ) {
+ 
+-				return map;
+-			}
++			// We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2
++			destElements = getAll( clone );
++			srcElements = getAll( elem );
+ 
+-			return value !== undefined ?
+-				jQuery.style( elem, name, value ) :
+-				jQuery.css( elem, name );
+-		}, name, value, arguments.length > 1 );
+-	},
+-	show: function() {
+-		return showHide( this, true );
+-	},
+-	hide: function() {
+-		return showHide( this );
+-	},
+-	toggle: function( state ) {
+-		if ( typeof state === "boolean" ) {
+-			return state ? this.show() : this.hide();
++			for ( i = 0, l = srcElements.length; i < l; i++ ) {
++				fixInput( srcElements[ i ], destElements[ i ] );
++			}
+ 		}
+ 
+-		return this.each(function() {
+-			if ( isHidden( this ) ) {
+-				jQuery( this ).show();
+-			} else {
+-				jQuery( this ).hide();
+-			}
+-		});
+-	}
+-});
++		// Copy the events from the original to the clone
++		if ( dataAndEvents ) {
++			if ( deepDataAndEvents ) {
++				srcElements = srcElements || getAll( elem );
++				destElements = destElements || getAll( clone );
+ 
+-jQuery.extend({
+-	// Add in style property hooks for overriding the default
+-	// behavior of getting and setting a style property
+-	cssHooks: {
+-		opacity: {
+-			get: function( elem, computed ) {
+-				if ( computed ) {
+-					// We should always get a number back from opacity
+-					var ret = curCSS( elem, "opacity" );
+-					return ret === "" ? "1" : ret;
++				for ( i = 0, l = srcElements.length; i < l; i++ ) {
++					cloneCopyEvent( srcElements[ i ], destElements[ i ] );
+ 				}
++			} else {
++				cloneCopyEvent( elem, clone );
+ 			}
+ 		}
+-	},
+ 
+-	// Don't automatically add "px" to these possibly-unitless properties
+-	cssNumber: {
+-		"columnCount": true,
+-		"fillOpacity": true,
+-		"fontWeight": true,
+-		"lineHeight": true,
+-		"opacity": true,
+-		"order": true,
+-		"orphans": true,
+-		"widows": true,
+-		"zIndex": true,
+-		"zoom": true
+-	},
++		// Preserve script evaluation history
++		destElements = getAll( clone, "script" );
++		if ( destElements.length > 0 ) {
++			setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
++		}
+ 
+-	// Add in properties whose names you wish to fix before
+-	// setting or getting the value
+-	cssProps: {
+-		// normalize float css property
+-		"float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat"
++		// Return the cloned set
++		return clone;
+ 	},
+ 
+-	// Get and set the style property on a DOM Node
+-	style: function( elem, name, value, extra ) {
+-		// Don't set styles on text and comment nodes
+-		if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
+-			return;
+-		}
+-
+-		// Make sure that we're working with the right name
+-		var ret, type, hooks,
+-			origName = jQuery.camelCase( name ),
+-			style = elem.style;
++	cleanData: function( elems ) {
++		var data, elem, type,
++			special = jQuery.event.special,
++			i = 0;
+ 
+-		name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) );
++		for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) {
++			if ( acceptData( elem ) ) {
++				if ( ( data = elem[ dataPriv.expando ] ) ) {
++					if ( data.events ) {
++						for ( type in data.events ) {
++							if ( special[ type ] ) {
++								jQuery.event.remove( elem, type );
+ 
+-		// gets hook for the prefixed version
+-		// followed by the unprefixed version
+-		hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
++							// This is a shortcut to avoid jQuery.event.remove's overhead
++							} else {
++								jQuery.removeEvent( elem, type, data.handle );
++							}
++						}
++					}
+ 
+-		// Check if we're setting a value
+-		if ( value !== undefined ) {
+-			type = typeof value;
++					// Support: Chrome <=35 - 45+
++					// Assign undefined instead of using delete, see Data#remove
++					elem[ dataPriv.expando ] = undefined;
++				}
++				if ( elem[ dataUser.expando ] ) {
+ 
+-			// convert relative number strings (+= or -=) to relative numbers. #7345
+-			if ( type === "string" && (ret = rrelNum.exec( value )) ) {
+-				value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) );
+-				// Fixes bug #9237
+-				type = "number";
+-			}
+-
+-			// Make sure that NaN and null values aren't set. See: #7116
+-			if ( value == null || type === "number" && isNaN( value ) ) {
+-				return;
++					// Support: Chrome <=35 - 45+
++					// Assign undefined instead of using delete, see Data#remove
++					elem[ dataUser.expando ] = undefined;
++				}
+ 			}
++		}
++	}
++} );
+ 
+-			// If a number was passed in, add 'px' to the (except for certain CSS properties)
+-			if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
+-				value += "px";
+-			}
++jQuery.fn.extend( {
++	detach: function( selector ) {
++		return remove( this, selector, true );
++	},
+ 
+-			// Fixes #8908, it can be done more correctly by specifing setters in cssHooks,
+-			// but it would mean to define eight (for every problematic property) identical functions
+-			if ( !jQuery.support.clearCloneStyle && value === "" && name.indexOf("background") === 0 ) {
+-				style[ name ] = "inherit";
+-			}
++	remove: function( selector ) {
++		return remove( this, selector );
++	},
+ 
+-			// If a hook was provided, use that value, otherwise just set the specified value
+-			if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) {
++	text: function( value ) {
++		return access( this, function( value ) {
++			return value === undefined ?
++				jQuery.text( this ) :
++				this.empty().each( function() {
++					if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
++						this.textContent = value;
++					}
++				} );
++		}, null, value, arguments.length );
++	},
+ 
+-				// Wrapped to prevent IE from throwing errors when 'invalid' values are provided
+-				// Fixes bug #5509
+-				try {
+-					style[ name ] = value;
+-				} catch(e) {}
++	append: function() {
++		return domManip( this, arguments, function( elem ) {
++			if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
++				var target = manipulationTarget( this, elem );
++				target.appendChild( elem );
+ 			}
++		} );
++	},
+ 
+-		} else {
+-			// If a hook was provided get the non-computed value from there
+-			if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
+-				return ret;
++	prepend: function() {
++		return domManip( this, arguments, function( elem ) {
++			if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
++				var target = manipulationTarget( this, elem );
++				target.insertBefore( elem, target.firstChild );
+ 			}
++		} );
++	},
+ 
+-			// Otherwise just get the value from the style object
+-			return style[ name ];
+-		}
++	before: function() {
++		return domManip( this, arguments, function( elem ) {
++			if ( this.parentNode ) {
++				this.parentNode.insertBefore( elem, this );
++			}
++		} );
+ 	},
+ 
+-	css: function( elem, name, extra, styles ) {
+-		var num, val, hooks,
+-			origName = jQuery.camelCase( name );
++	after: function() {
++		return domManip( this, arguments, function( elem ) {
++			if ( this.parentNode ) {
++				this.parentNode.insertBefore( elem, this.nextSibling );
++			}
++		} );
++	},
+ 
+-		// Make sure that we're working with the right name
+-		name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) );
++	empty: function() {
++		var elem,
++			i = 0;
+ 
+-		// gets hook for the prefixed version
+-		// followed by the unprefixed version
+-		hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
++		for ( ; ( elem = this[ i ] ) != null; i++ ) {
++			if ( elem.nodeType === 1 ) {
+ 
+-		// If a hook was provided get the computed value from there
+-		if ( hooks && "get" in hooks ) {
+-			val = hooks.get( elem, true, extra );
+-		}
++				// Prevent memory leaks
++				jQuery.cleanData( getAll( elem, false ) );
+ 
+-		// Otherwise, if a way to get the computed value exists, use that
+-		if ( val === undefined ) {
+-			val = curCSS( elem, name, styles );
++				// Remove any remaining nodes
++				elem.textContent = "";
++			}
+ 		}
+ 
+-		//convert "normal" to computed value
+-		if ( val === "normal" && name in cssNormalTransform ) {
+-			val = cssNormalTransform[ name ];
+-		}
++		return this;
++	},
+ 
+-		// Return, converting to number if forced or a qualifier was provided and val looks numeric
+-		if ( extra === "" || extra ) {
+-			num = parseFloat( val );
+-			return extra === true || jQuery.isNumeric( num ) ? num || 0 : val;
+-		}
+-		return val;
+-	}
+-});
++	clone: function( dataAndEvents, deepDataAndEvents ) {
++		dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
++		deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
+ 
+-// NOTE: we've included the "window" in window.getComputedStyle
+-// because jsdom on node.js will break without it.
+-if ( window.getComputedStyle ) {
+-	getStyles = function( elem ) {
+-		return window.getComputedStyle( elem, null );
+-	};
++		return this.map( function() {
++			return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
++		} );
++	},
++
++	html: function( value ) {
++		return access( this, function( value ) {
++			var elem = this[ 0 ] || {},
++				i = 0,
++				l = this.length;
+ 
+-	curCSS = function( elem, name, _computed ) {
+-		var width, minWidth, maxWidth,
+-			computed = _computed || getStyles( elem ),
++			if ( value === undefined && elem.nodeType === 1 ) {
++				return elem.innerHTML;
++			}
+ 
+-			// getPropertyValue is only needed for .css('filter') in IE9, see #12537
+-			ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined,
+-			style = elem.style;
++			// See if we can take a shortcut and just use innerHTML
++			if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
++				!wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) {
+ 
+-		if ( computed ) {
++				value = jQuery.htmlPrefilter( value );
++
++				try {
++					for ( ; i < l; i++ ) {
++						elem = this[ i ] || {};
++
++						// Remove element nodes and prevent memory leaks
++						if ( elem.nodeType === 1 ) {
++							jQuery.cleanData( getAll( elem, false ) );
++							elem.innerHTML = value;
++						}
++					}
++
++					elem = 0;
+ 
+-			if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {
+-				ret = jQuery.style( elem, name );
++				// If using innerHTML throws an exception, use the fallback method
++				} catch ( e ) {}
+ 			}
+ 
+-			// A tribute to the "awesome hack by Dean Edwards"
+-			// Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right
+-			// Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels
+-			// this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values
+-			if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) {
++			if ( elem ) {
++				this.empty().append( value );
++			}
++		}, null, value, arguments.length );
++	},
+ 
+-				// Remember the original values
+-				width = style.width;
+-				minWidth = style.minWidth;
+-				maxWidth = style.maxWidth;
++	replaceWith: function() {
++		var ignored = [];
+ 
+-				// Put in the new values to get a computed value out
+-				style.minWidth = style.maxWidth = style.width = ret;
+-				ret = computed.width;
++		// Make the changes, replacing each non-ignored context element with the new content
++		return domManip( this, arguments, function( elem ) {
++			var parent = this.parentNode;
+ 
+-				// Revert the changed values
+-				style.width = width;
+-				style.minWidth = minWidth;
+-				style.maxWidth = maxWidth;
++			if ( jQuery.inArray( this, ignored ) < 0 ) {
++				jQuery.cleanData( getAll( this ) );
++				if ( parent ) {
++					parent.replaceChild( elem, this );
++				}
+ 			}
++
++		// Force callback invocation
++		}, ignored );
++	}
++} );
++
++jQuery.each( {
++	appendTo: "append",
++	prependTo: "prepend",
++	insertBefore: "before",
++	insertAfter: "after",
++	replaceAll: "replaceWith"
++}, function( name, original ) {
++	jQuery.fn[ name ] = function( selector ) {
++		var elems,
++			ret = [],
++			insert = jQuery( selector ),
++			last = insert.length - 1,
++			i = 0;
++
++		for ( ; i <= last; i++ ) {
++			elems = i === last ? this : this.clone( true );
++			jQuery( insert[ i ] )[ original ]( elems );
++
++			// Support: Android <=4.0 only, PhantomJS 1 only
++			// .get() because push.apply(_, arraylike) throws on ancient WebKit
++			push.apply( ret, elems.get() );
+ 		}
+ 
+-		return ret;
+-	};
+-} else if ( document.documentElement.currentStyle ) {
+-	getStyles = function( elem ) {
+-		return elem.currentStyle;
++		return this.pushStack( ret );
+ 	};
++} );
++var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" );
+ 
+-	curCSS = function( elem, name, _computed ) {
+-		var left, rs, rsLeft,
+-			computed = _computed || getStyles( elem ),
+-			ret = computed ? computed[ name ] : undefined,
+-			style = elem.style;
++var getStyles = function( elem ) {
+ 
+-		// Avoid setting ret to empty string here
+-		// so we don't default to auto
+-		if ( ret == null && style && style[ name ] ) {
+-			ret = style[ name ];
++		// Support: IE <=11 only, Firefox <=30 (#15098, #14150)
++		// IE throws on elements created in popups
++		// FF meanwhile throws on frame elements through "defaultView.getComputedStyle"
++		var view = elem.ownerDocument.defaultView;
++
++		if ( !view || !view.opener ) {
++			view = window;
+ 		}
+ 
+-		// From the awesome hack by Dean Edwards
+-		// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
++		return view.getComputedStyle( elem );
++	};
++
++var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" );
+ 
+-		// If we're not dealing with a regular pixel number
+-		// but a number that has a weird ending, we need to convert it to pixels
+-		// but not position css attributes, as those are proportional to the parent element instead
+-		// and we can't measure the parent instead because it might trigger a "stacking dolls" problem
+-		if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) {
+ 
+-			// Remember the original values
+-			left = style.left;
+-			rs = elem.runtimeStyle;
+-			rsLeft = rs && rs.left;
+ 
+-			// Put in the new values to get a computed value out
+-			if ( rsLeft ) {
+-				rs.left = elem.currentStyle.left;
+-			}
+-			style.left = name === "fontSize" ? "1em" : ret;
+-			ret = style.pixelLeft + "px";
++( function() {
+ 
+-			// Revert the changed values
+-			style.left = left;
+-			if ( rsLeft ) {
+-				rs.left = rsLeft;
+-			}
++	// Executing both pixelPosition & boxSizingReliable tests require only one layout
++	// so they're executed at the same time to save the second computation.
++	function computeStyleTests() {
++
++		// This is a singleton, we need to execute it only once
++		if ( !div ) {
++			return;
+ 		}
+ 
+-		return ret === "" ? "auto" : ret;
+-	};
+-}
++		container.style.cssText = "position:absolute;left:-11111px;width:60px;" +
++			"margin-top:1px;padding:0;border:0";
++		div.style.cssText =
++			"position:relative;display:block;box-sizing:border-box;overflow:scroll;" +
++			"margin:auto;border:1px;padding:1px;" +
++			"width:60%;top:1%";
++		documentElement.appendChild( container ).appendChild( div );
+ 
+-function setPositiveNumber( elem, value, subtract ) {
+-	var matches = rnumsplit.exec( value );
+-	return matches ?
+-		// Guard against undefined "subtract", e.g., when used as in cssHooks
+-		Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) :
+-		value;
+-}
++		var divStyle = window.getComputedStyle( div );
++		pixelPositionVal = divStyle.top !== "1%";
+ 
+-function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
+-	var i = extra === ( isBorderBox ? "border" : "content" ) ?
+-		// If we already have the right measurement, avoid augmentation
+-		4 :
+-		// Otherwise initialize for horizontal or vertical properties
+-		name === "width" ? 1 : 0,
++		// Support: Android 4.0 - 4.3 only, Firefox <=3 - 44
++		reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12;
+ 
+-		val = 0;
++		// Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3
++		// Some styles come back with percentage values, even though they shouldn't
++		div.style.right = "60%";
++		pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36;
+ 
+-	for ( ; i < 4; i += 2 ) {
+-		// both box models exclude margin, so add it if we want it
+-		if ( extra === "margin" ) {
+-			val += jQuery.css( elem, extra + cssExpand[ i ], true, styles );
+-		}
++		// Support: IE 9 - 11 only
++		// Detect misreporting of content dimensions for box-sizing:border-box elements
++		boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36;
+ 
+-		if ( isBorderBox ) {
+-			// border-box includes padding, so remove it if we want content
+-			if ( extra === "content" ) {
+-				val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
+-			}
++		// Support: IE 9 only
++		// Detect overflow:scroll screwiness (gh-3699)
++		// Support: Chrome <=64
++		// Don't get tricked when zoom affects offsetWidth (gh-4029)
++		div.style.position = "absolute";
++		scrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12;
+ 
+-			// at this point, extra isn't border nor margin, so remove border
+-			if ( extra !== "margin" ) {
+-				val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
+-			}
+-		} else {
+-			// at this point, extra isn't content, so add padding
+-			val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
++		documentElement.removeChild( container );
+ 
+-			// at this point, extra isn't content nor padding, so add border
+-			if ( extra !== "padding" ) {
+-				val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
+-			}
+-		}
++		// Nullify the div so it wouldn't be stored in the memory and
++		// it will also be a sign that checks already performed
++		div = null;
+ 	}
+ 
+-	return val;
+-}
++	function roundPixelMeasures( measure ) {
++		return Math.round( parseFloat( measure ) );
++	}
+ 
+-function getWidthOrHeight( elem, name, extra ) {
++	var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal,
++		reliableMarginLeftVal,
++		container = document.createElement( "div" ),
++		div = document.createElement( "div" );
+ 
+-	// Start with offset property, which is equivalent to the border-box value
+-	var valueIsBorderBox = true,
+-		val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
+-		styles = getStyles( elem ),
+-		isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box";
++	// Finish early in limited (non-browser) environments
++	if ( !div.style ) {
++		return;
++	}
+ 
+-	// some non-html elements return undefined for offsetWidth, so check for null/undefined
+-	// svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285
+-	// MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668
+-	if ( val <= 0 || val == null ) {
+-		// Fall back to computed then uncomputed css if necessary
+-		val = curCSS( elem, name, styles );
+-		if ( val < 0 || val == null ) {
+-			val = elem.style[ name ];
+-		}
++	// Support: IE <=9 - 11 only
++	// Style of cloned element affects source element cloned (#8908)
++	div.style.backgroundClip = "content-box";
++	div.cloneNode( true ).style.backgroundClip = "";
++	support.clearCloneStyle = div.style.backgroundClip === "content-box";
+ 
+-		// Computed unit is not pixels. Stop here and return.
+-		if ( rnumnonpx.test(val) ) {
+-			return val;
++	jQuery.extend( support, {
++		boxSizingReliable: function() {
++			computeStyleTests();
++			return boxSizingReliableVal;
++		},
++		pixelBoxStyles: function() {
++			computeStyleTests();
++			return pixelBoxStylesVal;
++		},
++		pixelPosition: function() {
++			computeStyleTests();
++			return pixelPositionVal;
++		},
++		reliableMarginLeft: function() {
++			computeStyleTests();
++			return reliableMarginLeftVal;
++		},
++		scrollboxSize: function() {
++			computeStyleTests();
++			return scrollboxSizeVal;
+ 		}
++	} );
++} )();
+ 
+-		// we need the check for style in case a browser which returns unreliable values
+-		// for getComputedStyle silently falls back to the reliable elem.style
+-		valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] );
+ 
+-		// Normalize "", auto, and prepare for extra
+-		val = parseFloat( val ) || 0;
+-	}
++function curCSS( elem, name, computed ) {
++	var width, minWidth, maxWidth, ret,
+ 
+-	// use the active box-sizing model to add/subtract irrelevant styles
+-	return ( val +
+-		augmentWidthOrHeight(
+-			elem,
+-			name,
+-			extra || ( isBorderBox ? "border" : "content" ),
+-			valueIsBorderBox,
+-			styles
+-		)
+-	) + "px";
+-}
++		// Support: Firefox 51+
++		// Retrieving style before computed somehow
++		// fixes an issue with getting wrong values
++		// on detached elements
++		style = elem.style;
+ 
+-// Try to determine the default display value of an element
+-function css_defaultDisplay( nodeName ) {
+-	var doc = document,
+-		display = elemdisplay[ nodeName ];
++	computed = computed || getStyles( elem );
+ 
+-	if ( !display ) {
+-		display = actualDisplay( nodeName, doc );
++	// getPropertyValue is needed for:
++	//   .css('filter') (IE 9 only, #12537)
++	//   .css('--customProperty) (#3144)
++	if ( computed ) {
++		ret = computed.getPropertyValue( name ) || computed[ name ];
+ 
+-		// If the simple way fails, read from inside an iframe
+-		if ( display === "none" || !display ) {
+-			// Use the already-created iframe if possible
+-			iframe = ( iframe ||
+-				jQuery("<iframe frameborder='0' width='0' height='0'/>")
+-				.css( "cssText", "display:block !important" )
+-			).appendTo( doc.documentElement );
++		if ( ret === "" && !isAttached( elem ) ) {
++			ret = jQuery.style( elem, name );
++		}
+ 
+-			// Always write a new HTML skeleton so Webkit and Firefox don't choke on reuse
+-			doc = ( iframe[0].contentWindow || iframe[0].contentDocument ).document;
+-			doc.write("<!doctype html><html><body>");
+-			doc.close();
++		// A tribute to the "awesome hack by Dean Edwards"
++		// Android Browser returns percentage for some values,
++		// but width seems to be reliably pixels.
++		// This is against the CSSOM draft spec:
++		// https://drafts.csswg.org/cssom/#resolved-values
++		if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) {
+ 
+-			display = actualDisplay( nodeName, doc );
+-			iframe.detach();
+-		}
++			// Remember the original values
++			width = style.width;
++			minWidth = style.minWidth;
++			maxWidth = style.maxWidth;
+ 
+-		// Store the correct default display
+-		elemdisplay[ nodeName ] = display;
++			// Put in the new values to get a computed value out
++			style.minWidth = style.maxWidth = style.width = ret;
++			ret = computed.width;
++
++			// Revert the changed values
++			style.width = width;
++			style.minWidth = minWidth;
++			style.maxWidth = maxWidth;
++		}
+ 	}
+ 
+-	return display;
+-}
++	return ret !== undefined ?
+ 
+-// Called ONLY from within css_defaultDisplay
+-function actualDisplay( name, doc ) {
+-	var elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ),
+-		display = jQuery.css( elem[0], "display" );
+-	elem.remove();
+-	return display;
++		// Support: IE <=9 - 11 only
++		// IE returns zIndex value as an integer.
++		ret + "" :
++		ret;
+ }
+ 
+-jQuery.each([ "height", "width" ], function( i, name ) {
+-	jQuery.cssHooks[ name ] = {
+-		get: function( elem, computed, extra ) {
+-			if ( computed ) {
+-				// certain elements can have dimension info if we invisibly show them
+-				// however, it must have a current display style that would benefit from this
+-				return elem.offsetWidth === 0 && rdisplayswap.test( jQuery.css( elem, "display" ) ) ?
+-					jQuery.swap( elem, cssShow, function() {
+-						return getWidthOrHeight( elem, name, extra );
+-					}) :
+-					getWidthOrHeight( elem, name, extra );
++
++function addGetHookIf( conditionFn, hookFn ) {
++
++	// Define the hook, we'll check on the first run if it's really needed.
++	return {
++		get: function() {
++			if ( conditionFn() ) {
++
++				// Hook not needed (or it's not possible to use it due
++				// to missing dependency), remove it.
++				delete this.get;
++				return;
+ 			}
+-		},
+ 
+-		set: function( elem, value, extra ) {
+-			var styles = extra && getStyles( elem );
+-			return setPositiveNumber( elem, value, extra ?
+-				augmentWidthOrHeight(
+-					elem,
+-					name,
+-					extra,
+-					jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
+-					styles
+-				) : 0
+-			);
++			// Hook needed; redefine it so that the support test is not executed again.
++			return ( this.get = hookFn ).apply( this, arguments );
+ 		}
+ 	};
+-});
+-
+-if ( !jQuery.support.opacity ) {
+-	jQuery.cssHooks.opacity = {
+-		get: function( elem, computed ) {
+-			// IE uses filters for opacity
+-			return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ?
+-				( 0.01 * parseFloat( RegExp.$1 ) ) + "" :
+-				computed ? "1" : "";
+-		},
+-
+-		set: function( elem, value ) {
+-			var style = elem.style,
+-				currentStyle = elem.currentStyle,
+-				opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "",
+-				filter = currentStyle && currentStyle.filter || style.filter || "";
++}
+ 
+-			// IE has trouble with opacity if it does not have layout
+-			// Force it by setting the zoom level
+-			style.zoom = 1;
+ 
+-			// if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652
+-			// if value === "", then remove inline opacity #12685
+-			if ( ( value >= 1 || value === "" ) &&
+-					jQuery.trim( filter.replace( ralpha, "" ) ) === "" &&
+-					style.removeAttribute ) {
++var cssPrefixes = [ "Webkit", "Moz", "ms" ],
++	emptyStyle = document.createElement( "div" ).style,
++	vendorProps = {};
+ 
+-				// Setting style.filter to null, "" & " " still leave "filter:" in the cssText
+-				// if "filter:" is present at all, clearType is disabled, we want to avoid this
+-				// style.removeAttribute is IE Only, but so apparently is this code path...
+-				style.removeAttribute( "filter" );
++// Return a vendor-prefixed property or undefined
++function vendorPropName( name ) {
+ 
+-				// if there is no filter style applied in a css rule or unset inline opacity, we are done
+-				if ( value === "" || currentStyle && !currentStyle.filter ) {
+-					return;
+-				}
+-			}
++	// Check for vendor prefixed names
++	var capName = name[ 0 ].toUpperCase() + name.slice( 1 ),
++		i = cssPrefixes.length;
+ 
+-			// otherwise, set new filter values
+-			style.filter = ralpha.test( filter ) ?
+-				filter.replace( ralpha, opacity ) :
+-				filter + " " + opacity;
++	while ( i-- ) {
++		name = cssPrefixes[ i ] + capName;
++		if ( name in emptyStyle ) {
++			return name;
+ 		}
+-	};
++	}
+ }
+ 
+-// These hooks cannot be added until DOM ready because the support test
+-// for it is not run until after DOM ready
+-jQuery(function() {
+-	if ( !jQuery.support.reliableMarginRight ) {
+-		jQuery.cssHooks.marginRight = {
+-			get: function( elem, computed ) {
+-				if ( computed ) {
+-					// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+-					// Work around by temporarily setting element display to inline-block
+-					return jQuery.swap( elem, { "display": "inline-block" },
+-						curCSS, [ elem, "marginRight" ] );
+-				}
+-			}
+-		};
+-	}
++// Return a potentially-mapped jQuery.cssProps or vendor prefixed property
++function finalPropName( name ) {
++	var final = jQuery.cssProps[ name ] || vendorProps[ name ];
+ 
+-	// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
+-	// getComputedStyle returns percent when specified for top/left/bottom/right
+-	// rather than make the css module depend on the offset module, we just check for it here
+-	if ( !jQuery.support.pixelPosition && jQuery.fn.position ) {
+-		jQuery.each( [ "top", "left" ], function( i, prop ) {
+-			jQuery.cssHooks[ prop ] = {
+-				get: function( elem, computed ) {
+-					if ( computed ) {
+-						computed = curCSS( elem, prop );
+-						// if curCSS returns percentage, fallback to offset
+-						return rnumnonpx.test( computed ) ?
+-							jQuery( elem ).position()[ prop ] + "px" :
+-							computed;
+-					}
+-				}
+-			};
+-		});
++	if ( final ) {
++		return final;
++	}
++	if ( name in emptyStyle ) {
++		return name;
+ 	}
++	return vendorProps[ name ] = vendorPropName( name ) || name;
++}
+ 
+-});
+ 
+-if ( jQuery.expr && jQuery.expr.filters ) {
+-	jQuery.expr.filters.hidden = function( elem ) {
+-		// Support: Opera <= 12.12
+-		// Opera reports offsetWidths and offsetHeights less than zero on some elements
+-		return elem.offsetWidth <= 0 && elem.offsetHeight <= 0 ||
+-			(!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || jQuery.css( elem, "display" )) === "none");
+-	};
++var
+ 
+-	jQuery.expr.filters.visible = function( elem ) {
+-		return !jQuery.expr.filters.hidden( elem );
++	// Swappable if display is none or starts with table
++	// except "table", "table-cell", or "table-caption"
++	// See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
++	rdisplayswap = /^(none|table(?!-c[ea]).+)/,
++	rcustomProp = /^--/,
++	cssShow = { position: "absolute", visibility: "hidden", display: "block" },
++	cssNormalTransform = {
++		letterSpacing: "0",
++		fontWeight: "400"
+ 	};
+-}
+ 
+-// These hooks are used by animate to expand properties
+-jQuery.each({
+-	margin: "",
+-	padding: "",
+-	border: "Width"
+-}, function( prefix, suffix ) {
+-	jQuery.cssHooks[ prefix + suffix ] = {
+-		expand: function( value ) {
+-			var i = 0,
+-				expanded = {},
++function setPositiveNumber( elem, value, subtract ) {
+ 
+-				// assumes a single number if not a string
+-				parts = typeof value === "string" ? value.split(" ") : [ value ];
++	// Any relative (+/-) values have already been
++	// normalized at this point
++	var matches = rcssNum.exec( value );
++	return matches ?
+ 
+-			for ( ; i < 4; i++ ) {
+-				expanded[ prefix + cssExpand[ i ] + suffix ] =
+-					parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
+-			}
++		// Guard against undefined "subtract", e.g., when used as in cssHooks
++		Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) :
++		value;
++}
+ 
+-			return expanded;
+-		}
+-	};
++function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) {
++	var i = dimension === "width" ? 1 : 0,
++		extra = 0,
++		delta = 0;
+ 
+-	if ( !rmargin.test( prefix ) ) {
+-		jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
++	// Adjustment may not be necessary
++	if ( box === ( isBorderBox ? "border" : "content" ) ) {
++		return 0;
+ 	}
+-});
+-var r20 = /%20/g,
+-	rbracket = /\[\]$/,
+-	rCRLF = /\r?\n/g,
+-	rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
+-	rsubmittable = /^(?:input|select|textarea|keygen)/i;
+ 
+-jQuery.fn.extend({
+-	serialize: function() {
+-		return jQuery.param( this.serializeArray() );
+-	},
+-	serializeArray: function() {
+-		return this.map(function(){
+-			// Can add propHook for "elements" to filter or add form elements
+-			var elements = jQuery.prop( this, "elements" );
+-			return elements ? jQuery.makeArray( elements ) : this;
+-		})
+-		.filter(function(){
+-			var type = this.type;
+-			// Use .is(":disabled") so that fieldset[disabled] works
+-			return this.name && !jQuery( this ).is( ":disabled" ) &&
+-				rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
+-				( this.checked || !manipulation_rcheckableType.test( type ) );
+-		})
+-		.map(function( i, elem ){
+-			var val = jQuery( this ).val();
+-
+-			return val == null ?
+-				null :
+-				jQuery.isArray( val ) ?
+-					jQuery.map( val, function( val ){
+-						return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+-					}) :
+-					{ name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+-		}).get();
+-	}
+-});
+-
+-//Serialize an array of form elements or a set of
+-//key/values into a query string
+-jQuery.param = function( a, traditional ) {
+-	var prefix,
+-		s = [],
+-		add = function( key, value ) {
+-			// If value is a function, invoke it and return its value
+-			value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value );
+-			s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
+-		};
+-
+-	// Set traditional to true for jQuery <= 1.3.2 behavior.
+-	if ( traditional === undefined ) {
+-		traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;
+-	}
+-
+-	// If an array was passed in, assume that it is an array of form elements.
+-	if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
+-		// Serialize the form elements
+-		jQuery.each( a, function() {
+-			add( this.name, this.value );
+-		});
++	for ( ; i < 4; i += 2 ) {
+ 
+-	} else {
+-		// If traditional, encode the "old" way (the way 1.3.2 or older
+-		// did it), otherwise encode params recursively.
+-		for ( prefix in a ) {
+-			buildParams( prefix, a[ prefix ], traditional, add );
++		// Both box models exclude margin
++		if ( box === "margin" ) {
++			delta += jQuery.css( elem, box + cssExpand[ i ], true, styles );
+ 		}
+-	}
+ 
+-	// Return the resulting serialization
+-	return s.join( "&" ).replace( r20, "+" );
+-};
++		// If we get here with a content-box, we're seeking "padding" or "border" or "margin"
++		if ( !isBorderBox ) {
+ 
+-function buildParams( prefix, obj, traditional, add ) {
+-	var name;
++			// Add padding
++			delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
+ 
+-	if ( jQuery.isArray( obj ) ) {
+-		// Serialize array item.
+-		jQuery.each( obj, function( i, v ) {
+-			if ( traditional || rbracket.test( prefix ) ) {
+-				// Treat each array item as a scalar.
+-				add( prefix, v );
++			// For "border" or "margin", add border
++			if ( box !== "padding" ) {
++				delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
+ 
++			// But still keep track of it otherwise
+ 			} else {
+-				// Item is non-scalar (array or object), encode its numeric index.
+-				buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add );
++				extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
+ 			}
+-		});
+ 
+-	} else if ( !traditional && jQuery.type( obj ) === "object" ) {
+-		// Serialize object item.
+-		for ( name in obj ) {
+-			buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
+-		}
++		// If we get here with a border-box (content + padding + border), we're seeking "content" or
++		// "padding" or "margin"
++		} else {
+ 
+-	} else {
+-		// Serialize scalar item.
+-		add( prefix, obj );
+-	}
+-}
+-jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
+-	"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
+-	"change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {
++			// For "content", subtract padding
++			if ( box === "content" ) {
++				delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
++			}
+ 
+-	// Handle event binding
+-	jQuery.fn[ name ] = function( data, fn ) {
+-		return arguments.length > 0 ?
+-			this.on( name, null, data, fn ) :
+-			this.trigger( name );
+-	};
+-});
++			// For "content" or "padding", subtract border
++			if ( box !== "margin" ) {
++				delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
++			}
++		}
++	}
+ 
+-jQuery.fn.extend({
+-	hover: function( fnOver, fnOut ) {
+-		return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
+-	},
++	// Account for positive content-box scroll gutter when requested by providing computedVal
++	if ( !isBorderBox && computedVal >= 0 ) {
+ 
+-	bind: function( types, data, fn ) {
+-		return this.on( types, null, data, fn );
+-	},
+-	unbind: function( types, fn ) {
+-		return this.off( types, null, fn );
+-	},
++		// offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border
++		// Assuming integer scroll gutter, subtract the rest and round down
++		delta += Math.max( 0, Math.ceil(
++			elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] -
++			computedVal -
++			delta -
++			extra -
++			0.5
+ 
+-	delegate: function( selector, types, data, fn ) {
+-		return this.on( types, selector, data, fn );
+-	},
+-	undelegate: function( selector, types, fn ) {
+-		// ( namespace ) or ( selector, types [, fn] )
+-		return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn );
++		// If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter
++		// Use an explicit zero to avoid NaN (gh-3964)
++		) ) || 0;
+ 	}
+-});
+-var
+-	// Document location
+-	ajaxLocParts,
+-	ajaxLocation,
+-	ajax_nonce = jQuery.now(),
+ 
+-	ajax_rquery = /\?/,
+-	rhash = /#.*$/,
+-	rts = /([?&])_=[^&]*/,
+-	rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL
+-	// #7653, #8125, #8152: local protocol detection
+-	rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
+-	rnoContent = /^(?:GET|HEAD)$/,
+-	rprotocol = /^\/\//,
+-	rurl = /^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,
++	return delta;
++}
+ 
+-	// Keep a copy of the old load method
+-	_load = jQuery.fn.load,
++function getWidthOrHeight( elem, dimension, extra ) {
+ 
+-	/* Prefilters
+-	 * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
+-	 * 2) These are called:
+-	 *    - BEFORE asking for a transport
+-	 *    - AFTER param serialization (s.data is a string if s.processData is true)
+-	 * 3) key is the dataType
+-	 * 4) the catchall symbol "*" can be used
+-	 * 5) execution will start with transport dataType and THEN continue down to "*" if needed
+-	 */
+-	prefilters = {},
++	// Start with computed style
++	var styles = getStyles( elem ),
+ 
+-	/* Transports bindings
+-	 * 1) key is the dataType
+-	 * 2) the catchall symbol "*" can be used
+-	 * 3) selection will start with transport dataType and THEN go to "*" if needed
+-	 */
+-	transports = {},
++		// To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322).
++		// Fake content-box until we know it's needed to know the true value.
++		boxSizingNeeded = !support.boxSizingReliable() || extra,
++		isBorderBox = boxSizingNeeded &&
++			jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
++		valueIsBorderBox = isBorderBox,
+ 
+-	// Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
+-	allTypes = "*/".concat("*");
++		val = curCSS( elem, dimension, styles ),
++		offsetProp = "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 );
+ 
+-// #8138, IE may throw an exception when accessing
+-// a field from window.location if document.domain has been set
+-try {
+-	ajaxLocation = location.href;
+-} catch( e ) {
+-	// Use the href attribute of an A element
+-	// since IE will modify it given document.location
+-	ajaxLocation = document.createElement( "a" );
+-	ajaxLocation.href = "";
+-	ajaxLocation = ajaxLocation.href;
+-}
++	// Support: Firefox <=54
++	// Return a confounding non-pixel value or feign ignorance, as appropriate.
++	if ( rnumnonpx.test( val ) ) {
++		if ( !extra ) {
++			return val;
++		}
++		val = "auto";
++	}
+ 
+-// Segment location into parts
+-ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
+ 
+-// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
+-function addToPrefiltersOrTransports( structure ) {
++	// Fall back to offsetWidth/offsetHeight when value is "auto"
++	// This happens for inline elements with no explicit setting (gh-3571)
++	// Support: Android <=4.1 - 4.3 only
++	// Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602)
++	// Support: IE 9-11 only
++	// Also use offsetWidth/offsetHeight for when box sizing is unreliable
++	// We use getClientRects() to check for hidden/disconnected.
++	// In those cases, the computed value can be trusted to be border-box
++	if ( ( !support.boxSizingReliable() && isBorderBox ||
++		val === "auto" ||
++		!parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) &&
++		elem.getClientRects().length ) {
+ 
+-	// dataTypeExpression is optional and defaults to "*"
+-	return function( dataTypeExpression, func ) {
++		isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box";
+ 
+-		if ( typeof dataTypeExpression !== "string" ) {
+-			func = dataTypeExpression;
+-			dataTypeExpression = "*";
++		// Where available, offsetWidth/offsetHeight approximate border box dimensions.
++		// Where not available (e.g., SVG), assume unreliable box-sizing and interpret the
++		// retrieved value as a content box dimension.
++		valueIsBorderBox = offsetProp in elem;
++		if ( valueIsBorderBox ) {
++			val = elem[ offsetProp ];
+ 		}
++	}
+ 
+-		var dataType,
+-			i = 0,
+-			dataTypes = dataTypeExpression.toLowerCase().match( core_rnotwhite ) || [];
++	// Normalize "" and auto
++	val = parseFloat( val ) || 0;
+ 
+-		if ( jQuery.isFunction( func ) ) {
+-			// For each dataType in the dataTypeExpression
+-			while ( (dataType = dataTypes[i++]) ) {
+-				// Prepend if requested
+-				if ( dataType[0] === "+" ) {
+-					dataType = dataType.slice( 1 ) || "*";
+-					(structure[ dataType ] = structure[ dataType ] || []).unshift( func );
++	// Adjust for the element's box model
++	return ( val +
++		boxModelAdjustment(
++			elem,
++			dimension,
++			extra || ( isBorderBox ? "border" : "content" ),
++			valueIsBorderBox,
++			styles,
+ 
+-				// Otherwise append
+-				} else {
+-					(structure[ dataType ] = structure[ dataType ] || []).push( func );
+-				}
+-			}
+-		}
+-	};
++			// Provide the current computed size to request scroll gutter calculation (gh-3589)
++			val
++		)
++	) + "px";
+ }
+ 
+-// Base inspection function for prefilters and transports
+-function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {
++jQuery.extend( {
+ 
+-	var inspected = {},
+-		seekingTransport = ( structure === transports );
++	// Add in style property hooks for overriding the default
++	// behavior of getting and setting a style property
++	cssHooks: {
++		opacity: {
++			get: function( elem, computed ) {
++				if ( computed ) {
+ 
+-	function inspect( dataType ) {
+-		var selected;
+-		inspected[ dataType ] = true;
+-		jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
+-			var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
+-			if( typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[ dataTypeOrTransport ] ) {
+-				options.dataTypes.unshift( dataTypeOrTransport );
+-				inspect( dataTypeOrTransport );
+-				return false;
+-			} else if ( seekingTransport ) {
+-				return !( selected = dataTypeOrTransport );
++					// We should always get a number back from opacity
++					var ret = curCSS( elem, "opacity" );
++					return ret === "" ? "1" : ret;
++				}
+ 			}
+-		});
+-		return selected;
+-	}
++		}
++	},
+ 
+-	return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
+-}
++	// Don't automatically add "px" to these possibly-unitless properties
++	cssNumber: {
++		"animationIterationCount": true,
++		"columnCount": true,
++		"fillOpacity": true,
++		"flexGrow": true,
++		"flexShrink": true,
++		"fontWeight": true,
++		"gridArea": true,
++		"gridColumn": true,
++		"gridColumnEnd": true,
++		"gridColumnStart": true,
++		"gridRow": true,
++		"gridRowEnd": true,
++		"gridRowStart": true,
++		"lineHeight": true,
++		"opacity": true,
++		"order": true,
++		"orphans": true,
++		"widows": true,
++		"zIndex": true,
++		"zoom": true
++	},
+ 
+-// A special extend for ajax options
+-// that takes "flat" options (not to be deep extended)
+-// Fixes #9887
+-function ajaxExtend( target, src ) {
+-	var deep, key,
+-		flatOptions = jQuery.ajaxSettings.flatOptions || {};
++	// Add in properties whose names you wish to fix before
++	// setting or getting the value
++	cssProps: {},
+ 
+-	for ( key in src ) {
+-		if ( src[ key ] !== undefined ) {
+-			( flatOptions[ key ] ? target : ( deep || (deep = {}) ) )[ key ] = src[ key ];
++	// Get and set the style property on a DOM Node
++	style: function( elem, name, value, extra ) {
++
++		// Don't set styles on text and comment nodes
++		if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
++			return;
+ 		}
+-	}
+-	if ( deep ) {
+-		jQuery.extend( true, target, deep );
+-	}
+ 
+-	return target;
+-}
++		// Make sure that we're working with the right name
++		var ret, type, hooks,
++			origName = camelCase( name ),
++			isCustomProp = rcustomProp.test( name ),
++			style = elem.style;
+ 
+-jQuery.fn.load = function( url, params, callback ) {
+-	if ( typeof url !== "string" && _load ) {
+-		return _load.apply( this, arguments );
+-	}
++		// Make sure that we're working with the right name. We don't
++		// want to query the value if it is a CSS custom property
++		// since they are user-defined.
++		if ( !isCustomProp ) {
++			name = finalPropName( origName );
++		}
+ 
+-	var selector, response, type,
+-		self = this,
+-		off = url.indexOf(" ");
++		// Gets hook for the prefixed version, then unprefixed version
++		hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
+ 
+-	if ( off >= 0 ) {
+-		selector = url.slice( off, url.length );
+-		url = url.slice( 0, off );
+-	}
++		// Check if we're setting a value
++		if ( value !== undefined ) {
++			type = typeof value;
+ 
+-	// If it's a function
+-	if ( jQuery.isFunction( params ) ) {
++			// Convert "+=" or "-=" to relative numbers (#7345)
++			if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) {
++				value = adjustCSS( elem, name, ret );
+ 
+-		// We assume that it's the callback
+-		callback = params;
+-		params = undefined;
++				// Fixes bug #9237
++				type = "number";
++			}
+ 
+-	// Otherwise, build a param string
+-	} else if ( params && typeof params === "object" ) {
+-		type = "POST";
+-	}
++			// Make sure that null and NaN values aren't set (#7116)
++			if ( value == null || value !== value ) {
++				return;
++			}
+ 
+-	// If we have elements to modify, make the request
+-	if ( self.length > 0 ) {
+-		jQuery.ajax({
+-			url: url,
++			// If a number was passed in, add the unit (except for certain CSS properties)
++			// The isCustomProp check can be removed in jQuery 4.0 when we only auto-append
++			// "px" to a few hardcoded values.
++			if ( type === "number" && !isCustomProp ) {
++				value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" );
++			}
+ 
+-			// if "type" variable is undefined, then "GET" method will be used
+-			type: type,
+-			dataType: "html",
+-			data: params
+-		}).done(function( responseText ) {
++			// background-* props affect original clone's values
++			if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) {
++				style[ name ] = "inherit";
++			}
+ 
+-			// Save response for use in complete callback
+-			response = arguments;
++			// If a hook was provided, use that value, otherwise just set the specified value
++			if ( !hooks || !( "set" in hooks ) ||
++				( value = hooks.set( elem, value, extra ) ) !== undefined ) {
+ 
+-			self.html( selector ?
++				if ( isCustomProp ) {
++					style.setProperty( name, value );
++				} else {
++					style[ name ] = value;
++				}
++			}
+ 
+-				// If a selector was specified, locate the right elements in a dummy div
+-				// Exclude scripts to avoid IE 'Permission Denied' errors
+-				jQuery("<div>").append( jQuery.parseHTML( responseText ) ).find( selector ) :
++		} else {
+ 
+-				// Otherwise use the full result
+-				responseText );
++			// If a hook was provided get the non-computed value from there
++			if ( hooks && "get" in hooks &&
++				( ret = hooks.get( elem, false, extra ) ) !== undefined ) {
+ 
+-		}).complete( callback && function( jqXHR, status ) {
+-			self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] );
+-		});
+-	}
++				return ret;
++			}
+ 
+-	return this;
+-};
++			// Otherwise just get the value from the style object
++			return style[ name ];
++		}
++	},
+ 
+-// Attach a bunch of functions for handling common AJAX events
+-jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ){
+-	jQuery.fn[ type ] = function( fn ){
+-		return this.on( type, fn );
+-	};
+-});
++	css: function( elem, name, extra, styles ) {
++		var val, num, hooks,
++			origName = camelCase( name ),
++			isCustomProp = rcustomProp.test( name );
+ 
+-jQuery.extend({
++		// Make sure that we're working with the right name. We don't
++		// want to modify the value if it is a CSS custom property
++		// since they are user-defined.
++		if ( !isCustomProp ) {
++			name = finalPropName( origName );
++		}
+ 
+-	// Counter for holding the number of active queries
+-	active: 0,
++		// Try prefixed name followed by the unprefixed name
++		hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
+ 
+-	// Last-Modified header cache for next request
+-	lastModified: {},
+-	etag: {},
++		// If a hook was provided get the computed value from there
++		if ( hooks && "get" in hooks ) {
++			val = hooks.get( elem, true, extra );
++		}
+ 
+-	ajaxSettings: {
+-		url: ajaxLocation,
+-		type: "GET",
+-		isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
+-		global: true,
+-		processData: true,
+-		async: true,
+-		contentType: "application/x-www-form-urlencoded; charset=UTF-8",
+-		/*
+-		timeout: 0,
+-		data: null,
+-		dataType: null,
+-		username: null,
+-		password: null,
+-		cache: null,
+-		throws: false,
+-		traditional: false,
+-		headers: {},
+-		*/
++		// Otherwise, if a way to get the computed value exists, use that
++		if ( val === undefined ) {
++			val = curCSS( elem, name, styles );
++		}
+ 
+-		accepts: {
+-			"*": allTypes,
+-			text: "text/plain",
+-			html: "text/html",
+-			xml: "application/xml, text/xml",
+-			json: "application/json, text/javascript"
++		// Convert "normal" to computed value
++		if ( val === "normal" && name in cssNormalTransform ) {
++			val = cssNormalTransform[ name ];
++		}
++
++		// Make numeric if forced or a qualifier was provided and val looks numeric
++		if ( extra === "" || extra ) {
++			num = parseFloat( val );
++			return extra === true || isFinite( num ) ? num || 0 : val;
++		}
++
++		return val;
++	}
++} );
++
++jQuery.each( [ "height", "width" ], function( i, dimension ) {
++	jQuery.cssHooks[ dimension ] = {
++		get: function( elem, computed, extra ) {
++			if ( computed ) {
++
++				// Certain elements can have dimension info if we invisibly show them
++				// but it must have a current display style that would benefit
++				return rdisplayswap.test( jQuery.css( elem, "display" ) ) &&
++
++					// Support: Safari 8+
++					// Table columns in Safari have non-zero offsetWidth & zero
++					// getBoundingClientRect().width unless display is changed.
++					// Support: IE <=11 only
++					// Running getBoundingClientRect on a disconnected node
++					// in IE throws an error.
++					( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ?
++						swap( elem, cssShow, function() {
++							return getWidthOrHeight( elem, dimension, extra );
++						} ) :
++						getWidthOrHeight( elem, dimension, extra );
++			}
+ 		},
+ 
+-		contents: {
+-			xml: /xml/,
+-			html: /html/,
+-			json: /json/
++		set: function( elem, value, extra ) {
++			var matches,
++				styles = getStyles( elem ),
++
++				// Only read styles.position if the test has a chance to fail
++				// to avoid forcing a reflow.
++				scrollboxSizeBuggy = !support.scrollboxSize() &&
++					styles.position === "absolute",
++
++				// To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991)
++				boxSizingNeeded = scrollboxSizeBuggy || extra,
++				isBorderBox = boxSizingNeeded &&
++					jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
++				subtract = extra ?
++					boxModelAdjustment(
++						elem,
++						dimension,
++						extra,
++						isBorderBox,
++						styles
++					) :
++					0;
++
++			// Account for unreliable border-box dimensions by comparing offset* to computed and
++			// faking a content-box to get border and padding (gh-3699)
++			if ( isBorderBox && scrollboxSizeBuggy ) {
++				subtract -= Math.ceil(
++					elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] -
++					parseFloat( styles[ dimension ] ) -
++					boxModelAdjustment( elem, dimension, "border", false, styles ) -
++					0.5
++				);
++			}
++
++			// Convert to pixels if value adjustment is needed
++			if ( subtract && ( matches = rcssNum.exec( value ) ) &&
++				( matches[ 3 ] || "px" ) !== "px" ) {
++
++				elem.style[ dimension ] = value;
++				value = jQuery.css( elem, dimension );
++			}
++
++			return setPositiveNumber( elem, value, subtract );
++		}
++	};
++} );
++
++jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft,
++	function( elem, computed ) {
++		if ( computed ) {
++			return ( parseFloat( curCSS( elem, "marginLeft" ) ) ||
++				elem.getBoundingClientRect().left -
++					swap( elem, { marginLeft: 0 }, function() {
++						return elem.getBoundingClientRect().left;
++					} )
++				) + "px";
++		}
++	}
++);
++
++// These hooks are used by animate to expand properties
++jQuery.each( {
++	margin: "",
++	padding: "",
++	border: "Width"
++}, function( prefix, suffix ) {
++	jQuery.cssHooks[ prefix + suffix ] = {
++		expand: function( value ) {
++			var i = 0,
++				expanded = {},
++
++				// Assumes a single number if not a string
++				parts = typeof value === "string" ? value.split( " " ) : [ value ];
++
++			for ( ; i < 4; i++ ) {
++				expanded[ prefix + cssExpand[ i ] + suffix ] =
++					parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
++			}
++
++			return expanded;
++		}
++	};
++
++	if ( prefix !== "margin" ) {
++		jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
++	}
++} );
++
++jQuery.fn.extend( {
++	css: function( name, value ) {
++		return access( this, function( elem, name, value ) {
++			var styles, len,
++				map = {},
++				i = 0;
++
++			if ( Array.isArray( name ) ) {
++				styles = getStyles( elem );
++				len = name.length;
++
++				for ( ; i < len; i++ ) {
++					map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
++				}
++
++				return map;
++			}
++
++			return value !== undefined ?
++				jQuery.style( elem, name, value ) :
++				jQuery.css( elem, name );
++		}, name, value, arguments.length > 1 );
++	}
++} );
++
++
++function Tween( elem, options, prop, end, easing ) {
++	return new Tween.prototype.init( elem, options, prop, end, easing );
++}
++jQuery.Tween = Tween;
++
++Tween.prototype = {
++	constructor: Tween,
++	init: function( elem, options, prop, end, easing, unit ) {
++		this.elem = elem;
++		this.prop = prop;
++		this.easing = easing || jQuery.easing._default;
++		this.options = options;
++		this.start = this.now = this.cur();
++		this.end = end;
++		this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
++	},
++	cur: function() {
++		var hooks = Tween.propHooks[ this.prop ];
++
++		return hooks && hooks.get ?
++			hooks.get( this ) :
++			Tween.propHooks._default.get( this );
++	},
++	run: function( percent ) {
++		var eased,
++			hooks = Tween.propHooks[ this.prop ];
++
++		if ( this.options.duration ) {
++			this.pos = eased = jQuery.easing[ this.easing ](
++				percent, this.options.duration * percent, 0, 1, this.options.duration
++			);
++		} else {
++			this.pos = eased = percent;
++		}
++		this.now = ( this.end - this.start ) * eased + this.start;
++
++		if ( this.options.step ) {
++			this.options.step.call( this.elem, this.now, this );
++		}
++
++		if ( hooks && hooks.set ) {
++			hooks.set( this );
++		} else {
++			Tween.propHooks._default.set( this );
++		}
++		return this;
++	}
++};
++
++Tween.prototype.init.prototype = Tween.prototype;
++
++Tween.propHooks = {
++	_default: {
++		get: function( tween ) {
++			var result;
++
++			// Use a property on the element directly when it is not a DOM element,
++			// or when there is no matching style property that exists.
++			if ( tween.elem.nodeType !== 1 ||
++				tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) {
++				return tween.elem[ tween.prop ];
++			}
++
++			// Passing an empty string as a 3rd parameter to .css will automatically
++			// attempt a parseFloat and fallback to a string if the parse fails.
++			// Simple values such as "10px" are parsed to Float;
++			// complex values such as "rotate(1rad)" are returned as-is.
++			result = jQuery.css( tween.elem, tween.prop, "" );
++
++			// Empty strings, null, undefined and "auto" are converted to 0.
++			return !result || result === "auto" ? 0 : result;
+ 		},
++		set: function( tween ) {
++
++			// Use step hook for back compat.
++			// Use cssHook if its there.
++			// Use .style if available and use plain properties where available.
++			if ( jQuery.fx.step[ tween.prop ] ) {
++				jQuery.fx.step[ tween.prop ]( tween );
++			} else if ( tween.elem.nodeType === 1 && (
++					jQuery.cssHooks[ tween.prop ] ||
++					tween.elem.style[ finalPropName( tween.prop ) ] != null ) ) {
++				jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
++			} else {
++				tween.elem[ tween.prop ] = tween.now;
++			}
++		}
++	}
++};
++
++// Support: IE <=9 only
++// Panic based approach to setting things on disconnected nodes
++Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
++	set: function( tween ) {
++		if ( tween.elem.nodeType && tween.elem.parentNode ) {
++			tween.elem[ tween.prop ] = tween.now;
++		}
++	}
++};
++
++jQuery.easing = {
++	linear: function( p ) {
++		return p;
++	},
++	swing: function( p ) {
++		return 0.5 - Math.cos( p * Math.PI ) / 2;
++	},
++	_default: "swing"
++};
++
++jQuery.fx = Tween.prototype.init;
++
++// Back compat <1.8 extension point
++jQuery.fx.step = {};
++
++
++
++
++var
++	fxNow, inProgress,
++	rfxtypes = /^(?:toggle|show|hide)$/,
++	rrun = /queueHooks$/;
++
++function schedule() {
++	if ( inProgress ) {
++		if ( document.hidden === false && window.requestAnimationFrame ) {
++			window.requestAnimationFrame( schedule );
++		} else {
++			window.setTimeout( schedule, jQuery.fx.interval );
++		}
++
++		jQuery.fx.tick();
++	}
++}
++
++// Animations created synchronously will run synchronously
++function createFxNow() {
++	window.setTimeout( function() {
++		fxNow = undefined;
++	} );
++	return ( fxNow = Date.now() );
++}
++
++// Generate parameters to create a standard animation
++function genFx( type, includeWidth ) {
++	var which,
++		i = 0,
++		attrs = { height: type };
++
++	// If we include width, step value is 1 to do all cssExpand values,
++	// otherwise step value is 2 to skip over Left and Right
++	includeWidth = includeWidth ? 1 : 0;
++	for ( ; i < 4; i += 2 - includeWidth ) {
++		which = cssExpand[ i ];
++		attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
++	}
++
++	if ( includeWidth ) {
++		attrs.opacity = attrs.width = type;
++	}
++
++	return attrs;
++}
++
++function createTween( value, prop, animation ) {
++	var tween,
++		collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ),
++		index = 0,
++		length = collection.length;
++	for ( ; index < length; index++ ) {
++		if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) {
++
++			// We're done with this property
++			return tween;
++		}
++	}
++}
++
++function defaultPrefilter( elem, props, opts ) {
++	var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display,
++		isBox = "width" in props || "height" in props,
++		anim = this,
++		orig = {},
++		style = elem.style,
++		hidden = elem.nodeType && isHiddenWithinTree( elem ),
++		dataShow = dataPriv.get( elem, "fxshow" );
++
++	// Queue-skipping animations hijack the fx hooks
++	if ( !opts.queue ) {
++		hooks = jQuery._queueHooks( elem, "fx" );
++		if ( hooks.unqueued == null ) {
++			hooks.unqueued = 0;
++			oldfire = hooks.empty.fire;
++			hooks.empty.fire = function() {
++				if ( !hooks.unqueued ) {
++					oldfire();
++				}
++			};
++		}
++		hooks.unqueued++;
++
++		anim.always( function() {
++
++			// Ensure the complete handler is called before this completes
++			anim.always( function() {
++				hooks.unqueued--;
++				if ( !jQuery.queue( elem, "fx" ).length ) {
++					hooks.empty.fire();
++				}
++			} );
++		} );
++	}
++
++	// Detect show/hide animations
++	for ( prop in props ) {
++		value = props[ prop ];
++		if ( rfxtypes.test( value ) ) {
++			delete props[ prop ];
++			toggle = toggle || value === "toggle";
++			if ( value === ( hidden ? "hide" : "show" ) ) {
++
++				// Pretend to be hidden if this is a "show" and
++				// there is still data from a stopped show/hide
++				if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) {
++					hidden = true;
++
++				// Ignore all other no-op show/hide data
++				} else {
++					continue;
++				}
++			}
++			orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );
++		}
++	}
++
++	// Bail out if this is a no-op like .hide().hide()
++	propTween = !jQuery.isEmptyObject( props );
++	if ( !propTween && jQuery.isEmptyObject( orig ) ) {
++		return;
++	}
++
++	// Restrict "overflow" and "display" styles during box animations
++	if ( isBox && elem.nodeType === 1 ) {
++
++		// Support: IE <=9 - 11, Edge 12 - 15
++		// Record all 3 overflow attributes because IE does not infer the shorthand
++		// from identically-valued overflowX and overflowY and Edge just mirrors
++		// the overflowX value there.
++		opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
++
++		// Identify a display type, preferring old show/hide data over the CSS cascade
++		restoreDisplay = dataShow && dataShow.display;
++		if ( restoreDisplay == null ) {
++			restoreDisplay = dataPriv.get( elem, "display" );
++		}
++		display = jQuery.css( elem, "display" );
++		if ( display === "none" ) {
++			if ( restoreDisplay ) {
++				display = restoreDisplay;
++			} else {
++
++				// Get nonempty value(s) by temporarily forcing visibility
++				showHide( [ elem ], true );
++				restoreDisplay = elem.style.display || restoreDisplay;
++				display = jQuery.css( elem, "display" );
++				showHide( [ elem ] );
++			}
++		}
++
++		// Animate inline elements as inline-block
++		if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) {
++			if ( jQuery.css( elem, "float" ) === "none" ) {
++
++				// Restore the original display value at the end of pure show/hide animations
++				if ( !propTween ) {
++					anim.done( function() {
++						style.display = restoreDisplay;
++					} );
++					if ( restoreDisplay == null ) {
++						display = style.display;
++						restoreDisplay = display === "none" ? "" : display;
++					}
++				}
++				style.display = "inline-block";
++			}
++		}
++	}
++
++	if ( opts.overflow ) {
++		style.overflow = "hidden";
++		anim.always( function() {
++			style.overflow = opts.overflow[ 0 ];
++			style.overflowX = opts.overflow[ 1 ];
++			style.overflowY = opts.overflow[ 2 ];
++		} );
++	}
++
++	// Implement show/hide animations
++	propTween = false;
++	for ( prop in orig ) {
++
++		// General show/hide setup for this element animation
++		if ( !propTween ) {
++			if ( dataShow ) {
++				if ( "hidden" in dataShow ) {
++					hidden = dataShow.hidden;
++				}
++			} else {
++				dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } );
++			}
++
++			// Store hidden/visible for toggle so `.stop().toggle()` "reverses"
++			if ( toggle ) {
++				dataShow.hidden = !hidden;
++			}
++
++			// Show elements before animating them
++			if ( hidden ) {
++				showHide( [ elem ], true );
++			}
++
++			/* eslint-disable no-loop-func */
++
++			anim.done( function() {
++
++			/* eslint-enable no-loop-func */
++
++				// The final step of a "hide" animation is actually hiding the element
++				if ( !hidden ) {
++					showHide( [ elem ] );
++				}
++				dataPriv.remove( elem, "fxshow" );
++				for ( prop in orig ) {
++					jQuery.style( elem, prop, orig[ prop ] );
++				}
++			} );
++		}
++
++		// Per-property setup
++		propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );
++		if ( !( prop in dataShow ) ) {
++			dataShow[ prop ] = propTween.start;
++			if ( hidden ) {
++				propTween.end = propTween.start;
++				propTween.start = 0;
++			}
++		}
++	}
++}
++
++function propFilter( props, specialEasing ) {
++	var index, name, easing, value, hooks;
++
++	// camelCase, specialEasing and expand cssHook pass
++	for ( index in props ) {
++		name = camelCase( index );
++		easing = specialEasing[ name ];
++		value = props[ index ];
++		if ( Array.isArray( value ) ) {
++			easing = value[ 1 ];
++			value = props[ index ] = value[ 0 ];
++		}
++
++		if ( index !== name ) {
++			props[ name ] = value;
++			delete props[ index ];
++		}
++
++		hooks = jQuery.cssHooks[ name ];
++		if ( hooks && "expand" in hooks ) {
++			value = hooks.expand( value );
++			delete props[ name ];
++
++			// Not quite $.extend, this won't overwrite existing keys.
++			// Reusing 'index' because we have the correct "name"
++			for ( index in value ) {
++				if ( !( index in props ) ) {
++					props[ index ] = value[ index ];
++					specialEasing[ index ] = easing;
++				}
++			}
++		} else {
++			specialEasing[ name ] = easing;
++		}
++	}
++}
++
++function Animation( elem, properties, options ) {
++	var result,
++		stopped,
++		index = 0,
++		length = Animation.prefilters.length,
++		deferred = jQuery.Deferred().always( function() {
++
++			// Don't match elem in the :animated selector
++			delete tick.elem;
++		} ),
++		tick = function() {
++			if ( stopped ) {
++				return false;
++			}
++			var currentTime = fxNow || createFxNow(),
++				remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
++
++				// Support: Android 2.3 only
++				// Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497)
++				temp = remaining / animation.duration || 0,
++				percent = 1 - temp,
++				index = 0,
++				length = animation.tweens.length;
++
++			for ( ; index < length; index++ ) {
++				animation.tweens[ index ].run( percent );
++			}
++
++			deferred.notifyWith( elem, [ animation, percent, remaining ] );
++
++			// If there's more to do, yield
++			if ( percent < 1 && length ) {
++				return remaining;
++			}
++
++			// If this was an empty animation, synthesize a final progress notification
++			if ( !length ) {
++				deferred.notifyWith( elem, [ animation, 1, 0 ] );
++			}
++
++			// Resolve the animation and report its conclusion
++			deferred.resolveWith( elem, [ animation ] );
++			return false;
++		},
++		animation = deferred.promise( {
++			elem: elem,
++			props: jQuery.extend( {}, properties ),
++			opts: jQuery.extend( true, {
++				specialEasing: {},
++				easing: jQuery.easing._default
++			}, options ),
++			originalProperties: properties,
++			originalOptions: options,
++			startTime: fxNow || createFxNow(),
++			duration: options.duration,
++			tweens: [],
++			createTween: function( prop, end ) {
++				var tween = jQuery.Tween( elem, animation.opts, prop, end,
++						animation.opts.specialEasing[ prop ] || animation.opts.easing );
++				animation.tweens.push( tween );
++				return tween;
++			},
++			stop: function( gotoEnd ) {
++				var index = 0,
++
++					// If we are going to the end, we want to run all the tweens
++					// otherwise we skip this part
++					length = gotoEnd ? animation.tweens.length : 0;
++				if ( stopped ) {
++					return this;
++				}
++				stopped = true;
++				for ( ; index < length; index++ ) {
++					animation.tweens[ index ].run( 1 );
++				}
++
++				// Resolve when we played the last frame; otherwise, reject
++				if ( gotoEnd ) {
++					deferred.notifyWith( elem, [ animation, 1, 0 ] );
++					deferred.resolveWith( elem, [ animation, gotoEnd ] );
++				} else {
++					deferred.rejectWith( elem, [ animation, gotoEnd ] );
++				}
++				return this;
++			}
++		} ),
++		props = animation.props;
++
++	propFilter( props, animation.opts.specialEasing );
++
++	for ( ; index < length; index++ ) {
++		result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts );
++		if ( result ) {
++			if ( isFunction( result.stop ) ) {
++				jQuery._queueHooks( animation.elem, animation.opts.queue ).stop =
++					result.stop.bind( result );
++			}
++			return result;
++		}
++	}
++
++	jQuery.map( props, createTween, animation );
++
++	if ( isFunction( animation.opts.start ) ) {
++		animation.opts.start.call( elem, animation );
++	}
++
++	// Attach callbacks from options
++	animation
++		.progress( animation.opts.progress )
++		.done( animation.opts.done, animation.opts.complete )
++		.fail( animation.opts.fail )
++		.always( animation.opts.always );
++
++	jQuery.fx.timer(
++		jQuery.extend( tick, {
++			elem: elem,
++			anim: animation,
++			queue: animation.opts.queue
++		} )
++	);
++
++	return animation;
++}
++
++jQuery.Animation = jQuery.extend( Animation, {
++
++	tweeners: {
++		"*": [ function( prop, value ) {
++			var tween = this.createTween( prop, value );
++			adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween );
++			return tween;
++		} ]
++	},
++
++	tweener: function( props, callback ) {
++		if ( isFunction( props ) ) {
++			callback = props;
++			props = [ "*" ];
++		} else {
++			props = props.match( rnothtmlwhite );
++		}
++
++		var prop,
++			index = 0,
++			length = props.length;
++
++		for ( ; index < length; index++ ) {
++			prop = props[ index ];
++			Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || [];
++			Animation.tweeners[ prop ].unshift( callback );
++		}
++	},
++
++	prefilters: [ defaultPrefilter ],
++
++	prefilter: function( callback, prepend ) {
++		if ( prepend ) {
++			Animation.prefilters.unshift( callback );
++		} else {
++			Animation.prefilters.push( callback );
++		}
++	}
++} );
++
++jQuery.speed = function( speed, easing, fn ) {
++	var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
++		complete: fn || !fn && easing ||
++			isFunction( speed ) && speed,
++		duration: speed,
++		easing: fn && easing || easing && !isFunction( easing ) && easing
++	};
++
++	// Go to the end state if fx are off
++	if ( jQuery.fx.off ) {
++		opt.duration = 0;
++
++	} else {
++		if ( typeof opt.duration !== "number" ) {
++			if ( opt.duration in jQuery.fx.speeds ) {
++				opt.duration = jQuery.fx.speeds[ opt.duration ];
++
++			} else {
++				opt.duration = jQuery.fx.speeds._default;
++			}
++		}
++	}
++
++	// Normalize opt.queue - true/undefined/null -> "fx"
++	if ( opt.queue == null || opt.queue === true ) {
++		opt.queue = "fx";
++	}
++
++	// Queueing
++	opt.old = opt.complete;
++
++	opt.complete = function() {
++		if ( isFunction( opt.old ) ) {
++			opt.old.call( this );
++		}
++
++		if ( opt.queue ) {
++			jQuery.dequeue( this, opt.queue );
++		}
++	};
++
++	return opt;
++};
++
++jQuery.fn.extend( {
++	fadeTo: function( speed, to, easing, callback ) {
++
++		// Show any hidden elements after setting opacity to 0
++		return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show()
++
++			// Animate to the value specified
++			.end().animate( { opacity: to }, speed, easing, callback );
++	},
++	animate: function( prop, speed, easing, callback ) {
++		var empty = jQuery.isEmptyObject( prop ),
++			optall = jQuery.speed( speed, easing, callback ),
++			doAnimation = function() {
++
++				// Operate on a copy of prop so per-property easing won't be lost
++				var anim = Animation( this, jQuery.extend( {}, prop ), optall );
++
++				// Empty animations, or finishing resolves immediately
++				if ( empty || dataPriv.get( this, "finish" ) ) {
++					anim.stop( true );
++				}
++			};
++			doAnimation.finish = doAnimation;
++
++		return empty || optall.queue === false ?
++			this.each( doAnimation ) :
++			this.queue( optall.queue, doAnimation );
++	},
++	stop: function( type, clearQueue, gotoEnd ) {
++		var stopQueue = function( hooks ) {
++			var stop = hooks.stop;
++			delete hooks.stop;
++			stop( gotoEnd );
++		};
++
++		if ( typeof type !== "string" ) {
++			gotoEnd = clearQueue;
++			clearQueue = type;
++			type = undefined;
++		}
++		if ( clearQueue && type !== false ) {
++			this.queue( type || "fx", [] );
++		}
++
++		return this.each( function() {
++			var dequeue = true,
++				index = type != null && type + "queueHooks",
++				timers = jQuery.timers,
++				data = dataPriv.get( this );
++
++			if ( index ) {
++				if ( data[ index ] && data[ index ].stop ) {
++					stopQueue( data[ index ] );
++				}
++			} else {
++				for ( index in data ) {
++					if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
++						stopQueue( data[ index ] );
++					}
++				}
++			}
++
++			for ( index = timers.length; index--; ) {
++				if ( timers[ index ].elem === this &&
++					( type == null || timers[ index ].queue === type ) ) {
++
++					timers[ index ].anim.stop( gotoEnd );
++					dequeue = false;
++					timers.splice( index, 1 );
++				}
++			}
++
++			// Start the next in the queue if the last step wasn't forced.
++			// Timers currently will call their complete callbacks, which
++			// will dequeue but only if they were gotoEnd.
++			if ( dequeue || !gotoEnd ) {
++				jQuery.dequeue( this, type );
++			}
++		} );
++	},
++	finish: function( type ) {
++		if ( type !== false ) {
++			type = type || "fx";
++		}
++		return this.each( function() {
++			var index,
++				data = dataPriv.get( this ),
++				queue = data[ type + "queue" ],
++				hooks = data[ type + "queueHooks" ],
++				timers = jQuery.timers,
++				length = queue ? queue.length : 0;
++
++			// Enable finishing flag on private data
++			data.finish = true;
++
++			// Empty the queue first
++			jQuery.queue( this, type, [] );
++
++			if ( hooks && hooks.stop ) {
++				hooks.stop.call( this, true );
++			}
++
++			// Look for any active animations, and finish them
++			for ( index = timers.length; index--; ) {
++				if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
++					timers[ index ].anim.stop( true );
++					timers.splice( index, 1 );
++				}
++			}
++
++			// Look for any animations in the old queue and finish them
++			for ( index = 0; index < length; index++ ) {
++				if ( queue[ index ] && queue[ index ].finish ) {
++					queue[ index ].finish.call( this );
++				}
++			}
++
++			// Turn off finishing flag
++			delete data.finish;
++		} );
++	}
++} );
++
++jQuery.each( [ "toggle", "show", "hide" ], function( i, name ) {
++	var cssFn = jQuery.fn[ name ];
++	jQuery.fn[ name ] = function( speed, easing, callback ) {
++		return speed == null || typeof speed === "boolean" ?
++			cssFn.apply( this, arguments ) :
++			this.animate( genFx( name, true ), speed, easing, callback );
++	};
++} );
++
++// Generate shortcuts for custom animations
++jQuery.each( {
++	slideDown: genFx( "show" ),
++	slideUp: genFx( "hide" ),
++	slideToggle: genFx( "toggle" ),
++	fadeIn: { opacity: "show" },
++	fadeOut: { opacity: "hide" },
++	fadeToggle: { opacity: "toggle" }
++}, function( name, props ) {
++	jQuery.fn[ name ] = function( speed, easing, callback ) {
++		return this.animate( props, speed, easing, callback );
++	};
++} );
++
++jQuery.timers = [];
++jQuery.fx.tick = function() {
++	var timer,
++		i = 0,
++		timers = jQuery.timers;
++
++	fxNow = Date.now();
++
++	for ( ; i < timers.length; i++ ) {
++		timer = timers[ i ];
++
++		// Run the timer and safely remove it when done (allowing for external removal)
++		if ( !timer() && timers[ i ] === timer ) {
++			timers.splice( i--, 1 );
++		}
++	}
++
++	if ( !timers.length ) {
++		jQuery.fx.stop();
++	}
++	fxNow = undefined;
++};
++
++jQuery.fx.timer = function( timer ) {
++	jQuery.timers.push( timer );
++	jQuery.fx.start();
++};
++
++jQuery.fx.interval = 13;
++jQuery.fx.start = function() {
++	if ( inProgress ) {
++		return;
++	}
++
++	inProgress = true;
++	schedule();
++};
++
++jQuery.fx.stop = function() {
++	inProgress = null;
++};
++
++jQuery.fx.speeds = {
++	slow: 600,
++	fast: 200,
++
++	// Default speed
++	_default: 400
++};
++
++
++// Based off of the plugin by Clint Helfers, with permission.
++// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/
++jQuery.fn.delay = function( time, type ) {
++	time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
++	type = type || "fx";
++
++	return this.queue( type, function( next, hooks ) {
++		var timeout = window.setTimeout( next, time );
++		hooks.stop = function() {
++			window.clearTimeout( timeout );
++		};
++	} );
++};
++
++
++( function() {
++	var input = document.createElement( "input" ),
++		select = document.createElement( "select" ),
++		opt = select.appendChild( document.createElement( "option" ) );
++
++	input.type = "checkbox";
++
++	// Support: Android <=4.3 only
++	// Default value for a checkbox should be "on"
++	support.checkOn = input.value !== "";
++
++	// Support: IE <=11 only
++	// Must access selectedIndex to make default options select
++	support.optSelected = opt.selected;
++
++	// Support: IE <=11 only
++	// An input loses its value after becoming a radio
++	input = document.createElement( "input" );
++	input.value = "t";
++	input.type = "radio";
++	support.radioValue = input.value === "t";
++} )();
++
++
++var boolHook,
++	attrHandle = jQuery.expr.attrHandle;
++
++jQuery.fn.extend( {
++	attr: function( name, value ) {
++		return access( this, jQuery.attr, name, value, arguments.length > 1 );
++	},
++
++	removeAttr: function( name ) {
++		return this.each( function() {
++			jQuery.removeAttr( this, name );
++		} );
++	}
++} );
++
++jQuery.extend( {
++	attr: function( elem, name, value ) {
++		var ret, hooks,
++			nType = elem.nodeType;
++
++		// Don't get/set attributes on text, comment and attribute nodes
++		if ( nType === 3 || nType === 8 || nType === 2 ) {
++			return;
++		}
++
++		// Fallback to prop when attributes are not supported
++		if ( typeof elem.getAttribute === "undefined" ) {
++			return jQuery.prop( elem, name, value );
++		}
++
++		// Attribute hooks are determined by the lowercase version
++		// Grab necessary hook if one is defined
++		if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
++			hooks = jQuery.attrHooks[ name.toLowerCase() ] ||
++				( jQuery.expr.match.bool.test( name ) ? boolHook : undefined );
++		}
++
++		if ( value !== undefined ) {
++			if ( value === null ) {
++				jQuery.removeAttr( elem, name );
++				return;
++			}
++
++			if ( hooks && "set" in hooks &&
++				( ret = hooks.set( elem, value, name ) ) !== undefined ) {
++				return ret;
++			}
++
++			elem.setAttribute( name, value + "" );
++			return value;
++		}
++
++		if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {
++			return ret;
++		}
++
++		ret = jQuery.find.attr( elem, name );
++
++		// Non-existent attributes return null, we normalize to undefined
++		return ret == null ? undefined : ret;
++	},
++
++	attrHooks: {
++		type: {
++			set: function( elem, value ) {
++				if ( !support.radioValue && value === "radio" &&
++					nodeName( elem, "input" ) ) {
++					var val = elem.value;
++					elem.setAttribute( "type", value );
++					if ( val ) {
++						elem.value = val;
++					}
++					return value;
++				}
++			}
++		}
++	},
++
++	removeAttr: function( elem, value ) {
++		var name,
++			i = 0,
++
++			// Attribute names can contain non-HTML whitespace characters
++			// https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
++			attrNames = value && value.match( rnothtmlwhite );
++
++		if ( attrNames && elem.nodeType === 1 ) {
++			while ( ( name = attrNames[ i++ ] ) ) {
++				elem.removeAttribute( name );
++			}
++		}
++	}
++} );
++
++// Hooks for boolean attributes
++boolHook = {
++	set: function( elem, value, name ) {
++		if ( value === false ) {
++
++			// Remove boolean attributes when set to false
++			jQuery.removeAttr( elem, name );
++		} else {
++			elem.setAttribute( name, name );
++		}
++		return name;
++	}
++};
++
++jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) {
++	var getter = attrHandle[ name ] || jQuery.find.attr;
++
++	attrHandle[ name ] = function( elem, name, isXML ) {
++		var ret, handle,
++			lowercaseName = name.toLowerCase();
++
++		if ( !isXML ) {
++
++			// Avoid an infinite loop by temporarily removing this function from the getter
++			handle = attrHandle[ lowercaseName ];
++			attrHandle[ lowercaseName ] = ret;
++			ret = getter( elem, name, isXML ) != null ?
++				lowercaseName :
++				null;
++			attrHandle[ lowercaseName ] = handle;
++		}
++		return ret;
++	};
++} );
++
++
++
++
++var rfocusable = /^(?:input|select|textarea|button)$/i,
++	rclickable = /^(?:a|area)$/i;
++
++jQuery.fn.extend( {
++	prop: function( name, value ) {
++		return access( this, jQuery.prop, name, value, arguments.length > 1 );
++	},
++
++	removeProp: function( name ) {
++		return this.each( function() {
++			delete this[ jQuery.propFix[ name ] || name ];
++		} );
++	}
++} );
++
++jQuery.extend( {
++	prop: function( elem, name, value ) {
++		var ret, hooks,
++			nType = elem.nodeType;
++
++		// Don't get/set properties on text, comment and attribute nodes
++		if ( nType === 3 || nType === 8 || nType === 2 ) {
++			return;
++		}
++
++		if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
++
++			// Fix name and attach hooks
++			name = jQuery.propFix[ name ] || name;
++			hooks = jQuery.propHooks[ name ];
++		}
++
++		if ( value !== undefined ) {
++			if ( hooks && "set" in hooks &&
++				( ret = hooks.set( elem, value, name ) ) !== undefined ) {
++				return ret;
++			}
++
++			return ( elem[ name ] = value );
++		}
++
++		if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {
++			return ret;
++		}
++
++		return elem[ name ];
++	},
++
++	propHooks: {
++		tabIndex: {
++			get: function( elem ) {
++
++				// Support: IE <=9 - 11 only
++				// elem.tabIndex doesn't always return the
++				// correct value when it hasn't been explicitly set
++				// https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
++				// Use proper attribute retrieval(#12072)
++				var tabindex = jQuery.find.attr( elem, "tabindex" );
++
++				if ( tabindex ) {
++					return parseInt( tabindex, 10 );
++				}
++
++				if (
++					rfocusable.test( elem.nodeName ) ||
++					rclickable.test( elem.nodeName ) &&
++					elem.href
++				) {
++					return 0;
++				}
++
++				return -1;
++			}
++		}
++	},
++
++	propFix: {
++		"for": "htmlFor",
++		"class": "className"
++	}
++} );
++
++// Support: IE <=11 only
++// Accessing the selectedIndex property
++// forces the browser to respect setting selected
++// on the option
++// The getter ensures a default option is selected
++// when in an optgroup
++// eslint rule "no-unused-expressions" is disabled for this code
++// since it considers such accessions noop
++if ( !support.optSelected ) {
++	jQuery.propHooks.selected = {
++		get: function( elem ) {
++
++			/* eslint no-unused-expressions: "off" */
++
++			var parent = elem.parentNode;
++			if ( parent && parent.parentNode ) {
++				parent.parentNode.selectedIndex;
++			}
++			return null;
++		},
++		set: function( elem ) {
++
++			/* eslint no-unused-expressions: "off" */
++
++			var parent = elem.parentNode;
++			if ( parent ) {
++				parent.selectedIndex;
++
++				if ( parent.parentNode ) {
++					parent.parentNode.selectedIndex;
++				}
++			}
++		}
++	};
++}
++
++jQuery.each( [
++	"tabIndex",
++	"readOnly",
++	"maxLength",
++	"cellSpacing",
++	"cellPadding",
++	"rowSpan",
++	"colSpan",
++	"useMap",
++	"frameBorder",
++	"contentEditable"
++], function() {
++	jQuery.propFix[ this.toLowerCase() ] = this;
++} );
++
++
++
++
++	// Strip and collapse whitespace according to HTML spec
++	// https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace
++	function stripAndCollapse( value ) {
++		var tokens = value.match( rnothtmlwhite ) || [];
++		return tokens.join( " " );
++	}
++
++
++function getClass( elem ) {
++	return elem.getAttribute && elem.getAttribute( "class" ) || "";
++}
++
++function classesToArray( value ) {
++	if ( Array.isArray( value ) ) {
++		return value;
++	}
++	if ( typeof value === "string" ) {
++		return value.match( rnothtmlwhite ) || [];
++	}
++	return [];
++}
++
++jQuery.fn.extend( {
++	addClass: function( value ) {
++		var classes, elem, cur, curValue, clazz, j, finalValue,
++			i = 0;
++
++		if ( isFunction( value ) ) {
++			return this.each( function( j ) {
++				jQuery( this ).addClass( value.call( this, j, getClass( this ) ) );
++			} );
++		}
++
++		classes = classesToArray( value );
++
++		if ( classes.length ) {
++			while ( ( elem = this[ i++ ] ) ) {
++				curValue = getClass( elem );
++				cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );
++
++				if ( cur ) {
++					j = 0;
++					while ( ( clazz = classes[ j++ ] ) ) {
++						if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
++							cur += clazz + " ";
++						}
++					}
++
++					// Only assign if different to avoid unneeded rendering.
++					finalValue = stripAndCollapse( cur );
++					if ( curValue !== finalValue ) {
++						elem.setAttribute( "class", finalValue );
++					}
++				}
++			}
++		}
++
++		return this;
++	},
++
++	removeClass: function( value ) {
++		var classes, elem, cur, curValue, clazz, j, finalValue,
++			i = 0;
++
++		if ( isFunction( value ) ) {
++			return this.each( function( j ) {
++				jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) );
++			} );
++		}
++
++		if ( !arguments.length ) {
++			return this.attr( "class", "" );
++		}
++
++		classes = classesToArray( value );
++
++		if ( classes.length ) {
++			while ( ( elem = this[ i++ ] ) ) {
++				curValue = getClass( elem );
++
++				// This expression is here for better compressibility (see addClass)
++				cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );
++
++				if ( cur ) {
++					j = 0;
++					while ( ( clazz = classes[ j++ ] ) ) {
++
++						// Remove *all* instances
++						while ( cur.indexOf( " " + clazz + " " ) > -1 ) {
++							cur = cur.replace( " " + clazz + " ", " " );
++						}
++					}
++
++					// Only assign if different to avoid unneeded rendering.
++					finalValue = stripAndCollapse( cur );
++					if ( curValue !== finalValue ) {
++						elem.setAttribute( "class", finalValue );
++					}
++				}
++			}
++		}
++
++		return this;
++	},
++
++	toggleClass: function( value, stateVal ) {
++		var type = typeof value,
++			isValidValue = type === "string" || Array.isArray( value );
++
++		if ( typeof stateVal === "boolean" && isValidValue ) {
++			return stateVal ? this.addClass( value ) : this.removeClass( value );
++		}
++
++		if ( isFunction( value ) ) {
++			return this.each( function( i ) {
++				jQuery( this ).toggleClass(
++					value.call( this, i, getClass( this ), stateVal ),
++					stateVal
++				);
++			} );
++		}
++
++		return this.each( function() {
++			var className, i, self, classNames;
++
++			if ( isValidValue ) {
++
++				// Toggle individual class names
++				i = 0;
++				self = jQuery( this );
++				classNames = classesToArray( value );
++
++				while ( ( className = classNames[ i++ ] ) ) {
++
++					// Check each className given, space separated list
++					if ( self.hasClass( className ) ) {
++						self.removeClass( className );
++					} else {
++						self.addClass( className );
++					}
++				}
++
++			// Toggle whole class name
++			} else if ( value === undefined || type === "boolean" ) {
++				className = getClass( this );
++				if ( className ) {
++
++					// Store className if set
++					dataPriv.set( this, "__className__", className );
++				}
++
++				// If the element has a class name or if we're passed `false`,
++				// then remove the whole classname (if there was one, the above saved it).
++				// Otherwise bring back whatever was previously saved (if anything),
++				// falling back to the empty string if nothing was stored.
++				if ( this.setAttribute ) {
++					this.setAttribute( "class",
++						className || value === false ?
++						"" :
++						dataPriv.get( this, "__className__" ) || ""
++					);
++				}
++			}
++		} );
++	},
++
++	hasClass: function( selector ) {
++		var className, elem,
++			i = 0;
++
++		className = " " + selector + " ";
++		while ( ( elem = this[ i++ ] ) ) {
++			if ( elem.nodeType === 1 &&
++				( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) {
++					return true;
++			}
++		}
++
++		return false;
++	}
++} );
++
++
++
++
++var rreturn = /\r/g;
++
++jQuery.fn.extend( {
++	val: function( value ) {
++		var hooks, ret, valueIsFunction,
++			elem = this[ 0 ];
++
++		if ( !arguments.length ) {
++			if ( elem ) {
++				hooks = jQuery.valHooks[ elem.type ] ||
++					jQuery.valHooks[ elem.nodeName.toLowerCase() ];
++
++				if ( hooks &&
++					"get" in hooks &&
++					( ret = hooks.get( elem, "value" ) ) !== undefined
++				) {
++					return ret;
++				}
++
++				ret = elem.value;
++
++				// Handle most common string cases
++				if ( typeof ret === "string" ) {
++					return ret.replace( rreturn, "" );
++				}
++
++				// Handle cases where value is null/undef or number
++				return ret == null ? "" : ret;
++			}
++
++			return;
++		}
++
++		valueIsFunction = isFunction( value );
++
++		return this.each( function( i ) {
++			var val;
++
++			if ( this.nodeType !== 1 ) {
++				return;
++			}
++
++			if ( valueIsFunction ) {
++				val = value.call( this, i, jQuery( this ).val() );
++			} else {
++				val = value;
++			}
++
++			// Treat null/undefined as ""; convert numbers to string
++			if ( val == null ) {
++				val = "";
++
++			} else if ( typeof val === "number" ) {
++				val += "";
++
++			} else if ( Array.isArray( val ) ) {
++				val = jQuery.map( val, function( value ) {
++					return value == null ? "" : value + "";
++				} );
++			}
++
++			hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
++
++			// If set returns undefined, fall back to normal setting
++			if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) {
++				this.value = val;
++			}
++		} );
++	}
++} );
++
++jQuery.extend( {
++	valHooks: {
++		option: {
++			get: function( elem ) {
++
++				var val = jQuery.find.attr( elem, "value" );
++				return val != null ?
++					val :
++
++					// Support: IE <=10 - 11 only
++					// option.text throws exceptions (#14686, #14858)
++					// Strip and collapse whitespace
++					// https://html.spec.whatwg.org/#strip-and-collapse-whitespace
++					stripAndCollapse( jQuery.text( elem ) );
++			}
++		},
++		select: {
++			get: function( elem ) {
++				var value, option, i,
++					options = elem.options,
++					index = elem.selectedIndex,
++					one = elem.type === "select-one",
++					values = one ? null : [],
++					max = one ? index + 1 : options.length;
++
++				if ( index < 0 ) {
++					i = max;
++
++				} else {
++					i = one ? index : 0;
++				}
++
++				// Loop through all the selected options
++				for ( ; i < max; i++ ) {
++					option = options[ i ];
++
++					// Support: IE <=9 only
++					// IE8-9 doesn't update selected after form reset (#2551)
++					if ( ( option.selected || i === index ) &&
++
++							// Don't return options that are disabled or in a disabled optgroup
++							!option.disabled &&
++							( !option.parentNode.disabled ||
++								!nodeName( option.parentNode, "optgroup" ) ) ) {
++
++						// Get the specific value for the option
++						value = jQuery( option ).val();
++
++						// We don't need an array for one selects
++						if ( one ) {
++							return value;
++						}
++
++						// Multi-Selects return an array
++						values.push( value );
++					}
++				}
++
++				return values;
++			},
++
++			set: function( elem, value ) {
++				var optionSet, option,
++					options = elem.options,
++					values = jQuery.makeArray( value ),
++					i = options.length;
++
++				while ( i-- ) {
++					option = options[ i ];
++
++					/* eslint-disable no-cond-assign */
++
++					if ( option.selected =
++						jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1
++					) {
++						optionSet = true;
++					}
++
++					/* eslint-enable no-cond-assign */
++				}
++
++				// Force browsers to behave consistently when non-matching value is set
++				if ( !optionSet ) {
++					elem.selectedIndex = -1;
++				}
++				return values;
++			}
++		}
++	}
++} );
++
++// Radios and checkboxes getter/setter
++jQuery.each( [ "radio", "checkbox" ], function() {
++	jQuery.valHooks[ this ] = {
++		set: function( elem, value ) {
++			if ( Array.isArray( value ) ) {
++				return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 );
++			}
++		}
++	};
++	if ( !support.checkOn ) {
++		jQuery.valHooks[ this ].get = function( elem ) {
++			return elem.getAttribute( "value" ) === null ? "on" : elem.value;
++		};
++	}
++} );
++
++
++
++
++// Return jQuery for attributes-only inclusion
++
++
++support.focusin = "onfocusin" in window;
++
++
++var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
++	stopPropagationCallback = function( e ) {
++		e.stopPropagation();
++	};
++
++jQuery.extend( jQuery.event, {
++
++	trigger: function( event, data, elem, onlyHandlers ) {
++
++		var i, cur, tmp, bubbleType, ontype, handle, special, lastElement,
++			eventPath = [ elem || document ],
++			type = hasOwn.call( event, "type" ) ? event.type : event,
++			namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : [];
++
++		cur = lastElement = tmp = elem = elem || document;
++
++		// Don't do events on text and comment nodes
++		if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
++			return;
++		}
++
++		// focus/blur morphs to focusin/out; ensure we're not firing them right now
++		if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
++			return;
++		}
+ 
+-		responseFields: {
+-			xml: "responseXML",
+-			text: "responseText",
+-			json: "responseJSON"
+-		},
++		if ( type.indexOf( "." ) > -1 ) {
+ 
+-		// Data converters
+-		// Keys separate source (or catchall "*") and destination types with a single space
+-		converters: {
++			// Namespaced trigger; create a regexp to match event type in handle()
++			namespaces = type.split( "." );
++			type = namespaces.shift();
++			namespaces.sort();
++		}
++		ontype = type.indexOf( ":" ) < 0 && "on" + type;
+ 
+-			// Convert anything to text
+-			"* text": String,
++		// Caller can pass in a jQuery.Event object, Object, or just an event type string
++		event = event[ jQuery.expando ] ?
++			event :
++			new jQuery.Event( type, typeof event === "object" && event );
+ 
+-			// Text to html (true = no transformation)
+-			"text html": true,
++		// Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
++		event.isTrigger = onlyHandlers ? 2 : 3;
++		event.namespace = namespaces.join( "." );
++		event.rnamespace = event.namespace ?
++			new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) :
++			null;
+ 
+-			// Evaluate text as a json expression
+-			"text json": jQuery.parseJSON,
++		// Clean up the event in case it is being reused
++		event.result = undefined;
++		if ( !event.target ) {
++			event.target = elem;
++		}
+ 
+-			// Parse text as xml
+-			"text xml": jQuery.parseXML
+-		},
++		// Clone any incoming data and prepend the event, creating the handler arg list
++		data = data == null ?
++			[ event ] :
++			jQuery.makeArray( data, [ event ] );
+ 
+-		// For options that shouldn't be deep extended:
+-		// you can add your own custom options here if
+-		// and when you create one that shouldn't be
+-		// deep extended (see ajaxExtend)
+-		flatOptions: {
+-			url: true,
+-			context: true
++		// Allow special events to draw outside the lines
++		special = jQuery.event.special[ type ] || {};
++		if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
++			return;
+ 		}
+-	},
+ 
+-	// Creates a full fledged settings object into target
+-	// with both ajaxSettings and settings fields.
+-	// If target is omitted, writes into ajaxSettings.
+-	ajaxSetup: function( target, settings ) {
+-		return settings ?
++		// Determine event propagation path in advance, per W3C events spec (#9951)
++		// Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
++		if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) {
+ 
+-			// Building a settings object
+-			ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :
++			bubbleType = special.delegateType || type;
++			if ( !rfocusMorph.test( bubbleType + type ) ) {
++				cur = cur.parentNode;
++			}
++			for ( ; cur; cur = cur.parentNode ) {
++				eventPath.push( cur );
++				tmp = cur;
++			}
+ 
+-			// Extending ajaxSettings
+-			ajaxExtend( jQuery.ajaxSettings, target );
+-	},
++			// Only add window if we got to document (e.g., not plain obj or detached DOM)
++			if ( tmp === ( elem.ownerDocument || document ) ) {
++				eventPath.push( tmp.defaultView || tmp.parentWindow || window );
++			}
++		}
+ 
+-	ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
+-	ajaxTransport: addToPrefiltersOrTransports( transports ),
++		// Fire handlers on the event path
++		i = 0;
++		while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) {
++			lastElement = cur;
++			event.type = i > 1 ?
++				bubbleType :
++				special.bindType || type;
+ 
+-	// Main method
+-	ajax: function( url, options ) {
++			// jQuery handler
++			handle = ( dataPriv.get( cur, "events" ) || {} )[ event.type ] &&
++				dataPriv.get( cur, "handle" );
++			if ( handle ) {
++				handle.apply( cur, data );
++			}
+ 
+-		// If url is an object, simulate pre-1.5 signature
+-		if ( typeof url === "object" ) {
+-			options = url;
+-			url = undefined;
++			// Native handler
++			handle = ontype && cur[ ontype ];
++			if ( handle && handle.apply && acceptData( cur ) ) {
++				event.result = handle.apply( cur, data );
++				if ( event.result === false ) {
++					event.preventDefault();
++				}
++			}
+ 		}
++		event.type = type;
+ 
+-		// Force options to be an object
+-		options = options || {};
++		// If nobody prevented the default action, do it now
++		if ( !onlyHandlers && !event.isDefaultPrevented() ) {
+ 
+-		var // Cross-domain detection vars
+-			parts,
+-			// Loop variable
+-			i,
+-			// URL without anti-cache param
+-			cacheURL,
+-			// Response headers as string
+-			responseHeadersString,
+-			// timeout handle
+-			timeoutTimer,
++			if ( ( !special._default ||
++				special._default.apply( eventPath.pop(), data ) === false ) &&
++				acceptData( elem ) ) {
+ 
+-			// To know if global events are to be dispatched
+-			fireGlobals,
++				// Call a native DOM method on the target with the same name as the event.
++				// Don't do default actions on window, that's where global variables be (#6170)
++				if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) {
+ 
+-			transport,
+-			// Response headers
+-			responseHeaders,
+-			// Create the final options object
+-			s = jQuery.ajaxSetup( {}, options ),
+-			// Callbacks context
+-			callbackContext = s.context || s,
+-			// Context for global events is callbackContext if it is a DOM node or jQuery collection
+-			globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ?
+-				jQuery( callbackContext ) :
+-				jQuery.event,
+-			// Deferreds
+-			deferred = jQuery.Deferred(),
+-			completeDeferred = jQuery.Callbacks("once memory"),
+-			// Status-dependent callbacks
+-			statusCode = s.statusCode || {},
+-			// Headers (they are sent all at once)
+-			requestHeaders = {},
+-			requestHeadersNames = {},
+-			// The jqXHR state
+-			state = 0,
+-			// Default abort message
+-			strAbort = "canceled",
+-			// Fake xhr
+-			jqXHR = {
+-				readyState: 0,
++					// Don't re-trigger an onFOO event when we call its FOO() method
++					tmp = elem[ ontype ];
+ 
+-				// Builds headers hashtable if needed
+-				getResponseHeader: function( key ) {
+-					var match;
+-					if ( state === 2 ) {
+-						if ( !responseHeaders ) {
+-							responseHeaders = {};
+-							while ( (match = rheaders.exec( responseHeadersString )) ) {
+-								responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
+-							}
+-						}
+-						match = responseHeaders[ key.toLowerCase() ];
++					if ( tmp ) {
++						elem[ ontype ] = null;
+ 					}
+-					return match == null ? null : match;
+-				},
+ 
+-				// Raw string
+-				getAllResponseHeaders: function() {
+-					return state === 2 ? responseHeadersString : null;
+-				},
++					// Prevent re-triggering of the same event, since we already bubbled it above
++					jQuery.event.triggered = type;
+ 
+-				// Caches the header
+-				setRequestHeader: function( name, value ) {
+-					var lname = name.toLowerCase();
+-					if ( !state ) {
+-						name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
+-						requestHeaders[ name ] = value;
++					if ( event.isPropagationStopped() ) {
++						lastElement.addEventListener( type, stopPropagationCallback );
+ 					}
+-					return this;
+-				},
+ 
+-				// Overrides response content-type header
+-				overrideMimeType: function( type ) {
+-					if ( !state ) {
+-						s.mimeType = type;
+-					}
+-					return this;
+-				},
++					elem[ type ]();
+ 
+-				// Status-dependent callbacks
+-				statusCode: function( map ) {
+-					var code;
+-					if ( map ) {
+-						if ( state < 2 ) {
+-							for ( code in map ) {
+-								// Lazy-add the new callback in a way that preserves old ones
+-								statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
+-							}
+-						} else {
+-							// Execute the appropriate callbacks
+-							jqXHR.always( map[ jqXHR.status ] );
+-						}
++					if ( event.isPropagationStopped() ) {
++						lastElement.removeEventListener( type, stopPropagationCallback );
+ 					}
+-					return this;
+-				},
+ 
+-				// Cancel the request
+-				abort: function( statusText ) {
+-					var finalText = statusText || strAbort;
+-					if ( transport ) {
+-						transport.abort( finalText );
++					jQuery.event.triggered = undefined;
++
++					if ( tmp ) {
++						elem[ ontype ] = tmp;
+ 					}
+-					done( 0, finalText );
+-					return this;
+ 				}
+-			};
++			}
++		}
+ 
+-		// Attach deferreds
+-		deferred.promise( jqXHR ).complete = completeDeferred.add;
+-		jqXHR.success = jqXHR.done;
+-		jqXHR.error = jqXHR.fail;
++		return event.result;
++	},
+ 
+-		// Remove hash character (#7531: and string promotion)
+-		// Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
+-		// Handle falsy url in the settings object (#10093: consistency with old signature)
+-		// We also use the url parameter if available
+-		s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
++	// Piggyback on a donor event to simulate a different one
++	// Used only for `focus(in | out)` events
++	simulate: function( type, elem, event ) {
++		var e = jQuery.extend(
++			new jQuery.Event(),
++			event,
++			{
++				type: type,
++				isSimulated: true
++			}
++		);
+ 
+-		// Alias method option to type as per ticket #12004
+-		s.type = options.method || options.type || s.method || s.type;
++		jQuery.event.trigger( e, null, elem );
++	}
+ 
+-		// Extract dataTypes list
+-		s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( core_rnotwhite ) || [""];
++} );
+ 
+-		// A cross-domain request is in order when we have a protocol:host:port mismatch
+-		if ( s.crossDomain == null ) {
+-			parts = rurl.exec( s.url.toLowerCase() );
+-			s.crossDomain = !!( parts &&
+-				( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||
+-					( parts[ 3 ] || ( parts[ 1 ] === "http:" ? "80" : "443" ) ) !==
+-						( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? "80" : "443" ) ) )
+-			);
+-		}
++jQuery.fn.extend( {
+ 
+-		// Convert data if not already a string
+-		if ( s.data && s.processData && typeof s.data !== "string" ) {
+-			s.data = jQuery.param( s.data, s.traditional );
++	trigger: function( type, data ) {
++		return this.each( function() {
++			jQuery.event.trigger( type, data, this );
++		} );
++	},
++	triggerHandler: function( type, data ) {
++		var elem = this[ 0 ];
++		if ( elem ) {
++			return jQuery.event.trigger( type, data, elem, true );
+ 		}
++	}
++} );
+ 
+-		// Apply prefilters
+-		inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
+ 
+-		// If request was aborted inside a prefilter, stop there
+-		if ( state === 2 ) {
+-			return jqXHR;
+-		}
++// Support: Firefox <=44
++// Firefox doesn't have focus(in | out) events
++// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787
++//
++// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1
++// focus(in | out) events fire after focus & blur events,
++// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order
++// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857
++if ( !support.focusin ) {
++	jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) {
+ 
+-		// We can fire global events as of now if asked to
+-		fireGlobals = s.global;
++		// Attach a single capturing handler on the document while someone wants focusin/focusout
++		var handler = function( event ) {
++			jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) );
++		};
+ 
+-		// Watch for a new set of requests
+-		if ( fireGlobals && jQuery.active++ === 0 ) {
+-			jQuery.event.trigger("ajaxStart");
+-		}
++		jQuery.event.special[ fix ] = {
++			setup: function() {
++				var doc = this.ownerDocument || this,
++					attaches = dataPriv.access( doc, fix );
+ 
+-		// Uppercase the type
+-		s.type = s.type.toUpperCase();
++				if ( !attaches ) {
++					doc.addEventListener( orig, handler, true );
++				}
++				dataPriv.access( doc, fix, ( attaches || 0 ) + 1 );
++			},
++			teardown: function() {
++				var doc = this.ownerDocument || this,
++					attaches = dataPriv.access( doc, fix ) - 1;
+ 
+-		// Determine if request has content
+-		s.hasContent = !rnoContent.test( s.type );
++				if ( !attaches ) {
++					doc.removeEventListener( orig, handler, true );
++					dataPriv.remove( doc, fix );
++
++				} else {
++					dataPriv.access( doc, fix, attaches );
++				}
++			}
++		};
++	} );
++}
++var location = window.location;
++
++var nonce = Date.now();
++
++var rquery = ( /\?/ );
++
++
++
++// Cross-browser xml parsing
++jQuery.parseXML = function( data ) {
++	var xml;
++	if ( !data || typeof data !== "string" ) {
++		return null;
++	}
++
++	// Support: IE 9 - 11 only
++	// IE throws on parseFromString with invalid input.
++	try {
++		xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" );
++	} catch ( e ) {
++		xml = undefined;
++	}
++
++	if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) {
++		jQuery.error( "Invalid XML: " + data );
++	}
++	return xml;
++};
++
++
++var
++	rbracket = /\[\]$/,
++	rCRLF = /\r?\n/g,
++	rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
++	rsubmittable = /^(?:input|select|textarea|keygen)/i;
+ 
+-		// Save the URL in case we're toying with the If-Modified-Since
+-		// and/or If-None-Match header later on
+-		cacheURL = s.url;
++function buildParams( prefix, obj, traditional, add ) {
++	var name;
+ 
+-		// More options handling for requests with no content
+-		if ( !s.hasContent ) {
++	if ( Array.isArray( obj ) ) {
+ 
+-			// If data is available, append data to url
+-			if ( s.data ) {
+-				cacheURL = ( s.url += ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
+-				// #9682: remove data so that it's not used in an eventual retry
+-				delete s.data;
+-			}
++		// Serialize array item.
++		jQuery.each( obj, function( i, v ) {
++			if ( traditional || rbracket.test( prefix ) ) {
+ 
+-			// Add anti-cache in url if needed
+-			if ( s.cache === false ) {
+-				s.url = rts.test( cacheURL ) ?
++				// Treat each array item as a scalar.
++				add( prefix, v );
+ 
+-					// If there is already a '_' parameter, set its value
+-					cacheURL.replace( rts, "$1_=" + ajax_nonce++ ) :
++			} else {
+ 
+-					// Otherwise add one to the end
+-					cacheURL + ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ajax_nonce++;
++				// Item is non-scalar (array or object), encode its numeric index.
++				buildParams(
++					prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]",
++					v,
++					traditional,
++					add
++				);
+ 			}
+-		}
++		} );
+ 
+-		// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+-		if ( s.ifModified ) {
+-			if ( jQuery.lastModified[ cacheURL ] ) {
+-				jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
+-			}
+-			if ( jQuery.etag[ cacheURL ] ) {
+-				jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
+-			}
+-		}
++	} else if ( !traditional && toType( obj ) === "object" ) {
+ 
+-		// Set the correct header, if data is being sent
+-		if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
+-			jqXHR.setRequestHeader( "Content-Type", s.contentType );
++		// Serialize object item.
++		for ( name in obj ) {
++			buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
+ 		}
+ 
+-		// Set the Accepts header for the server, depending on the dataType
+-		jqXHR.setRequestHeader(
+-			"Accept",
+-			s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
+-				s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
+-				s.accepts[ "*" ]
+-		);
++	} else {
+ 
+-		// Check for headers option
+-		for ( i in s.headers ) {
+-			jqXHR.setRequestHeader( i, s.headers[ i ] );
+-		}
++		// Serialize scalar item.
++		add( prefix, obj );
++	}
++}
+ 
+-		// Allow custom headers/mimetypes and early abort
+-		if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
+-			// Abort if not done already and return
+-			return jqXHR.abort();
+-		}
++// Serialize an array of form elements or a set of
++// key/values into a query string
++jQuery.param = function( a, traditional ) {
++	var prefix,
++		s = [],
++		add = function( key, valueOrFunction ) {
+ 
+-		// aborting is no longer a cancellation
+-		strAbort = "abort";
++			// If value is a function, invoke it and use its return value
++			var value = isFunction( valueOrFunction ) ?
++				valueOrFunction() :
++				valueOrFunction;
+ 
+-		// Install callbacks on deferreds
+-		for ( i in { success: 1, error: 1, complete: 1 } ) {
+-			jqXHR[ i ]( s[ i ] );
+-		}
++			s[ s.length ] = encodeURIComponent( key ) + "=" +
++				encodeURIComponent( value == null ? "" : value );
++		};
+ 
+-		// Get transport
+-		transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
++	if ( a == null ) {
++		return "";
++	}
+ 
+-		// If no transport, we auto-abort
+-		if ( !transport ) {
+-			done( -1, "No Transport" );
+-		} else {
+-			jqXHR.readyState = 1;
++	// If an array was passed in, assume that it is an array of form elements.
++	if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
+ 
+-			// Send global event
+-			if ( fireGlobals ) {
+-				globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
+-			}
+-			// Timeout
+-			if ( s.async && s.timeout > 0 ) {
+-				timeoutTimer = setTimeout(function() {
+-					jqXHR.abort("timeout");
+-				}, s.timeout );
+-			}
++		// Serialize the form elements
++		jQuery.each( a, function() {
++			add( this.name, this.value );
++		} );
+ 
+-			try {
+-				state = 1;
+-				transport.send( requestHeaders, done );
+-			} catch ( e ) {
+-				// Propagate exception as error if not done
+-				if ( state < 2 ) {
+-					done( -1, e );
+-				// Simply rethrow otherwise
+-				} else {
+-					throw e;
+-				}
+-			}
++	} else {
++
++		// If traditional, encode the "old" way (the way 1.3.2 or older
++		// did it), otherwise encode params recursively.
++		for ( prefix in a ) {
++			buildParams( prefix, a[ prefix ], traditional, add );
+ 		}
++	}
+ 
+-		// Callback for when everything is done
+-		function done( status, nativeStatusText, responses, headers ) {
+-			var isSuccess, success, error, response, modified,
+-				statusText = nativeStatusText;
++	// Return the resulting serialization
++	return s.join( "&" );
++};
+ 
+-			// Called once
+-			if ( state === 2 ) {
+-				return;
+-			}
++jQuery.fn.extend( {
++	serialize: function() {
++		return jQuery.param( this.serializeArray() );
++	},
++	serializeArray: function() {
++		return this.map( function() {
++
++			// Can add propHook for "elements" to filter or add form elements
++			var elements = jQuery.prop( this, "elements" );
++			return elements ? jQuery.makeArray( elements ) : this;
++		} )
++		.filter( function() {
++			var type = this.type;
+ 
+-			// State is "done" now
+-			state = 2;
++			// Use .is( ":disabled" ) so that fieldset[disabled] works
++			return this.name && !jQuery( this ).is( ":disabled" ) &&
++				rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
++				( this.checked || !rcheckableType.test( type ) );
++		} )
++		.map( function( i, elem ) {
++			var val = jQuery( this ).val();
+ 
+-			// Clear timeout if it exists
+-			if ( timeoutTimer ) {
+-				clearTimeout( timeoutTimer );
++			if ( val == null ) {
++				return null;
+ 			}
+ 
+-			// Dereference transport for early garbage collection
+-			// (no matter how long the jqXHR object will be used)
+-			transport = undefined;
++			if ( Array.isArray( val ) ) {
++				return jQuery.map( val, function( val ) {
++					return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
++				} );
++			}
+ 
+-			// Cache response headers
+-			responseHeadersString = headers || "";
++			return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
++		} ).get();
++	}
++} );
+ 
+-			// Set readyState
+-			jqXHR.readyState = status > 0 ? 4 : 0;
+ 
+-			// Determine if successful
+-			isSuccess = status >= 200 && status < 300 || status === 304;
++var
++	r20 = /%20/g,
++	rhash = /#.*$/,
++	rantiCache = /([?&])_=[^&]*/,
++	rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg,
+ 
+-			// Get response data
+-			if ( responses ) {
+-				response = ajaxHandleResponses( s, jqXHR, responses );
+-			}
++	// #7653, #8125, #8152: local protocol detection
++	rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
++	rnoContent = /^(?:GET|HEAD)$/,
++	rprotocol = /^\/\//,
+ 
+-			// Convert no matter what (that way responseXXX fields are always set)
+-			response = ajaxConvert( s, response, jqXHR, isSuccess );
++	/* Prefilters
++	 * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
++	 * 2) These are called:
++	 *    - BEFORE asking for a transport
++	 *    - AFTER param serialization (s.data is a string if s.processData is true)
++	 * 3) key is the dataType
++	 * 4) the catchall symbol "*" can be used
++	 * 5) execution will start with transport dataType and THEN continue down to "*" if needed
++	 */
++	prefilters = {},
+ 
+-			// If successful, handle type chaining
+-			if ( isSuccess ) {
++	/* Transports bindings
++	 * 1) key is the dataType
++	 * 2) the catchall symbol "*" can be used
++	 * 3) selection will start with transport dataType and THEN go to "*" if needed
++	 */
++	transports = {},
+ 
+-				// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+-				if ( s.ifModified ) {
+-					modified = jqXHR.getResponseHeader("Last-Modified");
+-					if ( modified ) {
+-						jQuery.lastModified[ cacheURL ] = modified;
+-					}
+-					modified = jqXHR.getResponseHeader("etag");
+-					if ( modified ) {
+-						jQuery.etag[ cacheURL ] = modified;
+-					}
+-				}
++	// Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
++	allTypes = "*/".concat( "*" ),
+ 
+-				// if no content
+-				if ( status === 204 || s.type === "HEAD" ) {
+-					statusText = "nocontent";
++	// Anchor tag for parsing the document origin
++	originAnchor = document.createElement( "a" );
++	originAnchor.href = location.href;
+ 
+-				// if not modified
+-				} else if ( status === 304 ) {
+-					statusText = "notmodified";
++// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
++function addToPrefiltersOrTransports( structure ) {
+ 
+-				// If we have data, let's convert it
+-				} else {
+-					statusText = response.state;
+-					success = response.data;
+-					error = response.error;
+-					isSuccess = !error;
+-				}
+-			} else {
+-				// We extract error from statusText
+-				// then normalize statusText and status for non-aborts
+-				error = statusText;
+-				if ( status || !statusText ) {
+-					statusText = "error";
+-					if ( status < 0 ) {
+-						status = 0;
+-					}
+-				}
+-			}
++	// dataTypeExpression is optional and defaults to "*"
++	return function( dataTypeExpression, func ) {
+ 
+-			// Set data for the fake xhr object
+-			jqXHR.status = status;
+-			jqXHR.statusText = ( nativeStatusText || statusText ) + "";
++		if ( typeof dataTypeExpression !== "string" ) {
++			func = dataTypeExpression;
++			dataTypeExpression = "*";
++		}
+ 
+-			// Success/Error
+-			if ( isSuccess ) {
+-				deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
+-			} else {
+-				deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
+-			}
++		var dataType,
++			i = 0,
++			dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || [];
+ 
+-			// Status-dependent callbacks
+-			jqXHR.statusCode( statusCode );
+-			statusCode = undefined;
++		if ( isFunction( func ) ) {
+ 
+-			if ( fireGlobals ) {
+-				globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
+-					[ jqXHR, s, isSuccess ? success : error ] );
+-			}
++			// For each dataType in the dataTypeExpression
++			while ( ( dataType = dataTypes[ i++ ] ) ) {
+ 
+-			// Complete
+-			completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
++				// Prepend if requested
++				if ( dataType[ 0 ] === "+" ) {
++					dataType = dataType.slice( 1 ) || "*";
++					( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func );
+ 
+-			if ( fireGlobals ) {
+-				globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
+-				// Handle the global AJAX counter
+-				if ( !( --jQuery.active ) ) {
+-					jQuery.event.trigger("ajaxStop");
++				// Otherwise append
++				} else {
++					( structure[ dataType ] = structure[ dataType ] || [] ).push( func );
+ 				}
+ 			}
+ 		}
++	};
++}
+ 
+-		return jqXHR;
+-	},
++// Base inspection function for prefilters and transports
++function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {
+ 
+-	getJSON: function( url, data, callback ) {
+-		return jQuery.get( url, data, callback, "json" );
+-	},
++	var inspected = {},
++		seekingTransport = ( structure === transports );
+ 
+-	getScript: function( url, callback ) {
+-		return jQuery.get( url, undefined, callback, "script" );
++	function inspect( dataType ) {
++		var selected;
++		inspected[ dataType ] = true;
++		jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
++			var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
++			if ( typeof dataTypeOrTransport === "string" &&
++				!seekingTransport && !inspected[ dataTypeOrTransport ] ) {
++
++				options.dataTypes.unshift( dataTypeOrTransport );
++				inspect( dataTypeOrTransport );
++				return false;
++			} else if ( seekingTransport ) {
++				return !( selected = dataTypeOrTransport );
++			}
++		} );
++		return selected;
+ 	}
+-});
+ 
+-jQuery.each( [ "get", "post" ], function( i, method ) {
+-	jQuery[ method ] = function( url, data, callback, type ) {
+-		// shift arguments if data argument was omitted
+-		if ( jQuery.isFunction( data ) ) {
+-			type = type || callback;
+-			callback = data;
+-			data = undefined;
++	return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
++}
++
++// A special extend for ajax options
++// that takes "flat" options (not to be deep extended)
++// Fixes #9887
++function ajaxExtend( target, src ) {
++	var key, deep,
++		flatOptions = jQuery.ajaxSettings.flatOptions || {};
++
++	for ( key in src ) {
++		if ( src[ key ] !== undefined ) {
++			( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ];
+ 		}
++	}
++	if ( deep ) {
++		jQuery.extend( true, target, deep );
++	}
+ 
+-		return jQuery.ajax({
+-			url: url,
+-			type: method,
+-			dataType: type,
+-			data: data,
+-			success: callback
+-		});
+-	};
+-});
++	return target;
++}
+ 
+ /* Handles responses to an ajax request:
+  * - finds the right dataType (mediates between content-type and expected dataType)
+  * - returns the corresponding response
+  */
+ function ajaxHandleResponses( s, jqXHR, responses ) {
+-	var firstDataType, ct, finalDataType, type,
++
++	var ct, type, finalDataType, firstDataType,
+ 		contents = s.contents,
+ 		dataTypes = s.dataTypes;
+ 
+ 	// Remove auto dataType and get content-type in the process
+-	while( dataTypes[ 0 ] === "*" ) {
++	while ( dataTypes[ 0 ] === "*" ) {
+ 		dataTypes.shift();
+ 		if ( ct === undefined ) {
+-			ct = s.mimeType || jqXHR.getResponseHeader("Content-Type");
++			ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" );
+ 		}
+ 	}
+ 
+@@ -8320,9 +8900,10 @@ function ajaxHandleResponses( s, jqXHR, responses ) {
+ 	if ( dataTypes[ 0 ] in responses ) {
+ 		finalDataType = dataTypes[ 0 ];
+ 	} else {
++
+ 		// Try convertible dataTypes
+ 		for ( type in responses ) {
+-			if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
++			if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) {
+ 				finalDataType = type;
+ 				break;
+ 			}
+@@ -8330,6 +8911,7 @@ function ajaxHandleResponses( s, jqXHR, responses ) {
+ 				firstDataType = type;
+ 			}
+ 		}
++
+ 		// Or just use first one
+ 		finalDataType = finalDataType || firstDataType;
+ 	}
+@@ -8351,6 +8933,7 @@ function ajaxHandleResponses( s, jqXHR, responses ) {
+ function ajaxConvert( s, response, jqXHR, isSuccess ) {
+ 	var conv2, current, conv, tmp, prev,
+ 		converters = {},
++
+ 		// Work with a copy of dataTypes in case we need to modify it for conversion
+ 		dataTypes = s.dataTypes.slice();
+ 
+@@ -8403,6 +8986,7 @@ function ajaxConvert( s, response, jqXHR, isSuccess ) {
+ 							conv = converters[ prev + " " + tmp[ 0 ] ] ||
+ 								converters[ "* " + tmp[ 0 ] ];
+ 							if ( conv ) {
++
+ 								// Condense equivalence converters
+ 								if ( conv === true ) {
+ 									conv = converters[ conv2 ];
+@@ -8422,13 +9006,16 @@ function ajaxConvert( s, response, jqXHR, isSuccess ) {
+ 				if ( conv !== true ) {
+ 
+ 					// Unless errors are allowed to bubble, catch and return them
+-					if ( conv && s[ "throws" ] ) {
++					if ( conv && s.throws ) {
+ 						response = conv( response );
+ 					} else {
+ 						try {
+ 							response = conv( response );
+ 						} catch ( e ) {
+-							return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current };
++							return {
++								state: "parsererror",
++								error: conv ? e : "No conversion from " + prev + " to " + current
++							};
+ 						}
+ 					}
+ 				}
+@@ -8438,1177 +9025,1189 @@ function ajaxConvert( s, response, jqXHR, isSuccess ) {
+ 
+ 	return { state: "success", data: response };
+ }
+-// Install script dataType
+-jQuery.ajaxSetup({
+-	accepts: {
+-		script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
++
++jQuery.extend( {
++
++	// Counter for holding the number of active queries
++	active: 0,
++
++	// Last-Modified header cache for next request
++	lastModified: {},
++	etag: {},
++
++	ajaxSettings: {
++		url: location.href,
++		type: "GET",
++		isLocal: rlocalProtocol.test( location.protocol ),
++		global: true,
++		processData: true,
++		async: true,
++		contentType: "application/x-www-form-urlencoded; charset=UTF-8",
++
++		/*
++		timeout: 0,
++		data: null,
++		dataType: null,
++		username: null,
++		password: null,
++		cache: null,
++		throws: false,
++		traditional: false,
++		headers: {},
++		*/
++
++		accepts: {
++			"*": allTypes,
++			text: "text/plain",
++			html: "text/html",
++			xml: "application/xml, text/xml",
++			json: "application/json, text/javascript"
++		},
++
++		contents: {
++			xml: /\bxml\b/,
++			html: /\bhtml/,
++			json: /\bjson\b/
++		},
++
++		responseFields: {
++			xml: "responseXML",
++			text: "responseText",
++			json: "responseJSON"
++		},
++
++		// Data converters
++		// Keys separate source (or catchall "*") and destination types with a single space
++		converters: {
++
++			// Convert anything to text
++			"* text": String,
++
++			// Text to html (true = no transformation)
++			"text html": true,
++
++			// Evaluate text as a json expression
++			"text json": JSON.parse,
++
++			// Parse text as xml
++			"text xml": jQuery.parseXML
++		},
++
++		// For options that shouldn't be deep extended:
++		// you can add your own custom options here if
++		// and when you create one that shouldn't be
++		// deep extended (see ajaxExtend)
++		flatOptions: {
++			url: true,
++			context: true
++		}
+ 	},
+-	contents: {
+-		script: /(?:java|ecma)script/
++
++	// Creates a full fledged settings object into target
++	// with both ajaxSettings and settings fields.
++	// If target is omitted, writes into ajaxSettings.
++	ajaxSetup: function( target, settings ) {
++		return settings ?
++
++			// Building a settings object
++			ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :
++
++			// Extending ajaxSettings
++			ajaxExtend( jQuery.ajaxSettings, target );
+ 	},
+-	converters: {
+-		"text script": function( text ) {
+-			jQuery.globalEval( text );
+-			return text;
++
++	ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
++	ajaxTransport: addToPrefiltersOrTransports( transports ),
++
++	// Main method
++	ajax: function( url, options ) {
++
++		// If url is an object, simulate pre-1.5 signature
++		if ( typeof url === "object" ) {
++			options = url;
++			url = undefined;
+ 		}
+-	}
+-});
+ 
+-// Handle cache's special case and global
+-jQuery.ajaxPrefilter( "script", function( s ) {
+-	if ( s.cache === undefined ) {
+-		s.cache = false;
+-	}
+-	if ( s.crossDomain ) {
+-		s.type = "GET";
+-		s.global = false;
+-	}
+-});
++		// Force options to be an object
++		options = options || {};
++
++		var transport,
++
++			// URL without anti-cache param
++			cacheURL,
++
++			// Response headers
++			responseHeadersString,
++			responseHeaders,
++
++			// timeout handle
++			timeoutTimer,
++
++			// Url cleanup var
++			urlAnchor,
++
++			// Request state (becomes false upon send and true upon completion)
++			completed,
++
++			// To know if global events are to be dispatched
++			fireGlobals,
++
++			// Loop variable
++			i,
++
++			// uncached part of the url
++			uncached,
++
++			// Create the final options object
++			s = jQuery.ajaxSetup( {}, options ),
++
++			// Callbacks context
++			callbackContext = s.context || s,
++
++			// Context for global events is callbackContext if it is a DOM node or jQuery collection
++			globalEventContext = s.context &&
++				( callbackContext.nodeType || callbackContext.jquery ) ?
++					jQuery( callbackContext ) :
++					jQuery.event,
++
++			// Deferreds
++			deferred = jQuery.Deferred(),
++			completeDeferred = jQuery.Callbacks( "once memory" ),
++
++			// Status-dependent callbacks
++			statusCode = s.statusCode || {},
++
++			// Headers (they are sent all at once)
++			requestHeaders = {},
++			requestHeadersNames = {},
++
++			// Default abort message
++			strAbort = "canceled",
++
++			// Fake xhr
++			jqXHR = {
++				readyState: 0,
++
++				// Builds headers hashtable if needed
++				getResponseHeader: function( key ) {
++					var match;
++					if ( completed ) {
++						if ( !responseHeaders ) {
++							responseHeaders = {};
++							while ( ( match = rheaders.exec( responseHeadersString ) ) ) {
++								responseHeaders[ match[ 1 ].toLowerCase() + " " ] =
++									( responseHeaders[ match[ 1 ].toLowerCase() + " " ] || [] )
++										.concat( match[ 2 ] );
++							}
++						}
++						match = responseHeaders[ key.toLowerCase() + " " ];
++					}
++					return match == null ? null : match.join( ", " );
++				},
++
++				// Raw string
++				getAllResponseHeaders: function() {
++					return completed ? responseHeadersString : null;
++				},
++
++				// Caches the header
++				setRequestHeader: function( name, value ) {
++					if ( completed == null ) {
++						name = requestHeadersNames[ name.toLowerCase() ] =
++							requestHeadersNames[ name.toLowerCase() ] || name;
++						requestHeaders[ name ] = value;
++					}
++					return this;
++				},
++
++				// Overrides response content-type header
++				overrideMimeType: function( type ) {
++					if ( completed == null ) {
++						s.mimeType = type;
++					}
++					return this;
++				},
++
++				// Status-dependent callbacks
++				statusCode: function( map ) {
++					var code;
++					if ( map ) {
++						if ( completed ) {
++
++							// Execute the appropriate callbacks
++							jqXHR.always( map[ jqXHR.status ] );
++						} else {
++
++							// Lazy-add the new callbacks in a way that preserves old ones
++							for ( code in map ) {
++								statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
++							}
++						}
++					}
++					return this;
++				},
++
++				// Cancel the request
++				abort: function( statusText ) {
++					var finalText = statusText || strAbort;
++					if ( transport ) {
++						transport.abort( finalText );
++					}
++					done( 0, finalText );
++					return this;
++				}
++			};
++
++		// Attach deferreds
++		deferred.promise( jqXHR );
++
++		// Add protocol if not provided (prefilters might expect it)
++		// Handle falsy url in the settings object (#10093: consistency with old signature)
++		// We also use the url parameter if available
++		s.url = ( ( url || s.url || location.href ) + "" )
++			.replace( rprotocol, location.protocol + "//" );
++
++		// Alias method option to type as per ticket #12004
++		s.type = options.method || options.type || s.method || s.type;
+ 
+-// Bind script tag hack transport
+-jQuery.ajaxTransport( "script", function(s) {
++		// Extract dataTypes list
++		s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ];
+ 
+-	// This transport only deals with cross domain requests
+-	if ( s.crossDomain ) {
++		// A cross-domain request is in order when the origin doesn't match the current origin.
++		if ( s.crossDomain == null ) {
++			urlAnchor = document.createElement( "a" );
+ 
+-		var script,
+-			head = document.head || jQuery("head")[0] || document.documentElement;
++			// Support: IE <=8 - 11, Edge 12 - 15
++			// IE throws exception on accessing the href property if url is malformed,
++			// e.g. http://example.com:80x/
++			try {
++				urlAnchor.href = s.url;
+ 
+-		return {
++				// Support: IE <=8 - 11 only
++				// Anchor's host property isn't correctly set when s.url is relative
++				urlAnchor.href = urlAnchor.href;
++				s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !==
++					urlAnchor.protocol + "//" + urlAnchor.host;
++			} catch ( e ) {
+ 
+-			send: function( _, callback ) {
++				// If there is an error parsing the URL, assume it is crossDomain,
++				// it can be rejected by the transport if it is invalid
++				s.crossDomain = true;
++			}
++		}
+ 
+-				script = document.createElement("script");
++		// Convert data if not already a string
++		if ( s.data && s.processData && typeof s.data !== "string" ) {
++			s.data = jQuery.param( s.data, s.traditional );
++		}
+ 
+-				script.async = true;
++		// Apply prefilters
++		inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
+ 
+-				if ( s.scriptCharset ) {
+-					script.charset = s.scriptCharset;
+-				}
++		// If request was aborted inside a prefilter, stop there
++		if ( completed ) {
++			return jqXHR;
++		}
++
++		// We can fire global events as of now if asked to
++		// Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118)
++		fireGlobals = jQuery.event && s.global;
+ 
+-				script.src = s.url;
++		// Watch for a new set of requests
++		if ( fireGlobals && jQuery.active++ === 0 ) {
++			jQuery.event.trigger( "ajaxStart" );
++		}
+ 
+-				// Attach handlers for all browsers
+-				script.onload = script.onreadystatechange = function( _, isAbort ) {
++		// Uppercase the type
++		s.type = s.type.toUpperCase();
+ 
+-					if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {
++		// Determine if request has content
++		s.hasContent = !rnoContent.test( s.type );
+ 
+-						// Handle memory leak in IE
+-						script.onload = script.onreadystatechange = null;
++		// Save the URL in case we're toying with the If-Modified-Since
++		// and/or If-None-Match header later on
++		// Remove hash to simplify url manipulation
++		cacheURL = s.url.replace( rhash, "" );
+ 
+-						// Remove the script
+-						if ( script.parentNode ) {
+-							script.parentNode.removeChild( script );
+-						}
++		// More options handling for requests with no content
++		if ( !s.hasContent ) {
+ 
+-						// Dereference the script
+-						script = null;
++			// Remember the hash so we can put it back
++			uncached = s.url.slice( cacheURL.length );
+ 
+-						// Callback if not abort
+-						if ( !isAbort ) {
+-							callback( 200, "success" );
+-						}
+-					}
+-				};
++			// If data is available and should be processed, append data to url
++			if ( s.data && ( s.processData || typeof s.data === "string" ) ) {
++				cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data;
+ 
+-				// Circumvent IE6 bugs with base elements (#2709 and #4378) by prepending
+-				// Use native DOM manipulation to avoid our domManip AJAX trickery
+-				head.insertBefore( script, head.firstChild );
+-			},
++				// #9682: remove data so that it's not used in an eventual retry
++				delete s.data;
++			}
+ 
+-			abort: function() {
+-				if ( script ) {
+-					script.onload( undefined, true );
+-				}
++			// Add or update anti-cache param if needed
++			if ( s.cache === false ) {
++				cacheURL = cacheURL.replace( rantiCache, "$1" );
++				uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce++ ) + uncached;
+ 			}
+-		};
+-	}
+-});
+-var oldCallbacks = [],
+-	rjsonp = /(=)\?(?=&|$)|\?\?/;
+ 
+-// Default jsonp settings
+-jQuery.ajaxSetup({
+-	jsonp: "callback",
+-	jsonpCallback: function() {
+-		var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( ajax_nonce++ ) );
+-		this[ callback ] = true;
+-		return callback;
+-	}
+-});
++			// Put hash and anti-cache on the URL that will be requested (gh-1732)
++			s.url = cacheURL + uncached;
+ 
+-// Detect, normalize options and install callbacks for jsonp requests
+-jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
++		// Change '%20' to '+' if this is encoded form body content (gh-2658)
++		} else if ( s.data && s.processData &&
++			( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) {
++			s.data = s.data.replace( r20, "+" );
++		}
+ 
+-	var callbackName, overwritten, responseContainer,
+-		jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?
+-			"url" :
+-			typeof s.data === "string" && !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && rjsonp.test( s.data ) && "data"
++		// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
++		if ( s.ifModified ) {
++			if ( jQuery.lastModified[ cacheURL ] ) {
++				jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
++			}
++			if ( jQuery.etag[ cacheURL ] ) {
++				jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
++			}
++		}
++
++		// Set the correct header, if data is being sent
++		if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
++			jqXHR.setRequestHeader( "Content-Type", s.contentType );
++		}
++
++		// Set the Accepts header for the server, depending on the dataType
++		jqXHR.setRequestHeader(
++			"Accept",
++			s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ?
++				s.accepts[ s.dataTypes[ 0 ] ] +
++					( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
++				s.accepts[ "*" ]
+ 		);
+ 
+-	// Handle iff the expected data type is "jsonp" or we have a parameter to set
+-	if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) {
++		// Check for headers option
++		for ( i in s.headers ) {
++			jqXHR.setRequestHeader( i, s.headers[ i ] );
++		}
+ 
+-		// Get callback name, remembering preexisting value associated with it
+-		callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ?
+-			s.jsonpCallback() :
+-			s.jsonpCallback;
++		// Allow custom headers/mimetypes and early abort
++		if ( s.beforeSend &&
++			( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) {
+ 
+-		// Insert callback into url or form data
+-		if ( jsonProp ) {
+-			s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName );
+-		} else if ( s.jsonp !== false ) {
+-			s.url += ( ajax_rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
++			// Abort if not done already and return
++			return jqXHR.abort();
+ 		}
+ 
+-		// Use data converter to retrieve json after script execution
+-		s.converters["script json"] = function() {
+-			if ( !responseContainer ) {
+-				jQuery.error( callbackName + " was not called" );
+-			}
+-			return responseContainer[ 0 ];
+-		};
++		// Aborting is no longer a cancellation
++		strAbort = "abort";
+ 
+-		// force json dataType
+-		s.dataTypes[ 0 ] = "json";
++		// Install callbacks on deferreds
++		completeDeferred.add( s.complete );
++		jqXHR.done( s.success );
++		jqXHR.fail( s.error );
+ 
+-		// Install callback
+-		overwritten = window[ callbackName ];
+-		window[ callbackName ] = function() {
+-			responseContainer = arguments;
+-		};
++		// Get transport
++		transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
+ 
+-		// Clean-up function (fires after converters)
+-		jqXHR.always(function() {
+-			// Restore preexisting value
+-			window[ callbackName ] = overwritten;
++		// If no transport, we auto-abort
++		if ( !transport ) {
++			done( -1, "No Transport" );
++		} else {
++			jqXHR.readyState = 1;
+ 
+-			// Save back as free
+-			if ( s[ callbackName ] ) {
+-				// make sure that re-using the options doesn't screw things around
+-				s.jsonpCallback = originalSettings.jsonpCallback;
++			// Send global event
++			if ( fireGlobals ) {
++				globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
++			}
+ 
+-				// save the callback name for future use
+-				oldCallbacks.push( callbackName );
++			// If request was aborted inside ajaxSend, stop there
++			if ( completed ) {
++				return jqXHR;
+ 			}
+ 
+-			// Call if it was a function and we have a response
+-			if ( responseContainer && jQuery.isFunction( overwritten ) ) {
+-				overwritten( responseContainer[ 0 ] );
++			// Timeout
++			if ( s.async && s.timeout > 0 ) {
++				timeoutTimer = window.setTimeout( function() {
++					jqXHR.abort( "timeout" );
++				}, s.timeout );
+ 			}
+ 
+-			responseContainer = overwritten = undefined;
+-		});
++			try {
++				completed = false;
++				transport.send( requestHeaders, done );
++			} catch ( e ) {
+ 
+-		// Delegate to script
+-		return "script";
+-	}
+-});
+-var xhrCallbacks, xhrSupported,
+-	xhrId = 0,
+-	// #5280: Internet Explorer will keep connections alive if we don't abort on unload
+-	xhrOnUnloadAbort = window.ActiveXObject && function() {
+-		// Abort all pending requests
+-		var key;
+-		for ( key in xhrCallbacks ) {
+-			xhrCallbacks[ key ]( undefined, true );
++				// Rethrow post-completion exceptions
++				if ( completed ) {
++					throw e;
++				}
++
++				// Propagate others as results
++				done( -1, e );
++			}
+ 		}
+-	};
+ 
+-// Functions to create xhrs
+-function createStandardXHR() {
+-	try {
+-		return new window.XMLHttpRequest();
+-	} catch( e ) {}
+-}
++		// Callback for when everything is done
++		function done( status, nativeStatusText, responses, headers ) {
++			var isSuccess, success, error, response, modified,
++				statusText = nativeStatusText;
+ 
+-function createActiveXHR() {
+-	try {
+-		return new window.ActiveXObject("Microsoft.XMLHTTP");
+-	} catch( e ) {}
+-}
++			// Ignore repeat invocations
++			if ( completed ) {
++				return;
++			}
+ 
+-// Create the request object
+-// (This is still attached to ajaxSettings for backward compatibility)
+-jQuery.ajaxSettings.xhr = window.ActiveXObject ?
+-	/* Microsoft failed to properly
+-	 * implement the XMLHttpRequest in IE7 (can't request local files),
+-	 * so we use the ActiveXObject when it is available
+-	 * Additionally XMLHttpRequest can be disabled in IE7/IE8 so
+-	 * we need a fallback.
+-	 */
+-	function() {
+-		return !this.isLocal && createStandardXHR() || createActiveXHR();
+-	} :
+-	// For all other browsers, use the standard XMLHttpRequest object
+-	createStandardXHR;
++			completed = true;
++
++			// Clear timeout if it exists
++			if ( timeoutTimer ) {
++				window.clearTimeout( timeoutTimer );
++			}
++
++			// Dereference transport for early garbage collection
++			// (no matter how long the jqXHR object will be used)
++			transport = undefined;
+ 
+-// Determine support properties
+-xhrSupported = jQuery.ajaxSettings.xhr();
+-jQuery.support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
+-xhrSupported = jQuery.support.ajax = !!xhrSupported;
++			// Cache response headers
++			responseHeadersString = headers || "";
+ 
+-// Create transport if the browser can provide an xhr
+-if ( xhrSupported ) {
++			// Set readyState
++			jqXHR.readyState = status > 0 ? 4 : 0;
+ 
+-	jQuery.ajaxTransport(function( s ) {
+-		// Cross domain only allowed if supported through XMLHttpRequest
+-		if ( !s.crossDomain || jQuery.support.cors ) {
++			// Determine if successful
++			isSuccess = status >= 200 && status < 300 || status === 304;
+ 
+-			var callback;
++			// Get response data
++			if ( responses ) {
++				response = ajaxHandleResponses( s, jqXHR, responses );
++			}
+ 
+-			return {
+-				send: function( headers, complete ) {
++			// Convert no matter what (that way responseXXX fields are always set)
++			response = ajaxConvert( s, response, jqXHR, isSuccess );
+ 
+-					// Get a new xhr
+-					var handle, i,
+-						xhr = s.xhr();
++			// If successful, handle type chaining
++			if ( isSuccess ) {
+ 
+-					// Open the socket
+-					// Passing null username, generates a login popup on Opera (#2865)
+-					if ( s.username ) {
+-						xhr.open( s.type, s.url, s.async, s.username, s.password );
+-					} else {
+-						xhr.open( s.type, s.url, s.async );
++				// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
++				if ( s.ifModified ) {
++					modified = jqXHR.getResponseHeader( "Last-Modified" );
++					if ( modified ) {
++						jQuery.lastModified[ cacheURL ] = modified;
+ 					}
+-
+-					// Apply custom fields if provided
+-					if ( s.xhrFields ) {
+-						for ( i in s.xhrFields ) {
+-							xhr[ i ] = s.xhrFields[ i ];
+-						}
++					modified = jqXHR.getResponseHeader( "etag" );
++					if ( modified ) {
++						jQuery.etag[ cacheURL ] = modified;
+ 					}
++				}
+ 
+-					// Override mime type if needed
+-					if ( s.mimeType && xhr.overrideMimeType ) {
+-						xhr.overrideMimeType( s.mimeType );
+-					}
++				// if no content
++				if ( status === 204 || s.type === "HEAD" ) {
++					statusText = "nocontent";
++
++				// if not modified
++				} else if ( status === 304 ) {
++					statusText = "notmodified";
++
++				// If we have data, let's convert it
++				} else {
++					statusText = response.state;
++					success = response.data;
++					error = response.error;
++					isSuccess = !error;
++				}
++			} else {
+ 
+-					// X-Requested-With header
+-					// For cross-domain requests, seeing as conditions for a preflight are
+-					// akin to a jigsaw puzzle, we simply never set it to be sure.
+-					// (it can always be set on a per-request basis or even using ajaxSetup)
+-					// For same-domain requests, won't change header if already provided.
+-					if ( !s.crossDomain && !headers["X-Requested-With"] ) {
+-						headers["X-Requested-With"] = "XMLHttpRequest";
++				// Extract error from statusText and normalize for non-aborts
++				error = statusText;
++				if ( status || !statusText ) {
++					statusText = "error";
++					if ( status < 0 ) {
++						status = 0;
+ 					}
++				}
++			}
+ 
+-					// Need an extra try/catch for cross domain requests in Firefox 3
+-					try {
+-						for ( i in headers ) {
+-							xhr.setRequestHeader( i, headers[ i ] );
+-						}
+-					} catch( err ) {}
++			// Set data for the fake xhr object
++			jqXHR.status = status;
++			jqXHR.statusText = ( nativeStatusText || statusText ) + "";
++
++			// Success/Error
++			if ( isSuccess ) {
++				deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
++			} else {
++				deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
++			}
++
++			// Status-dependent callbacks
++			jqXHR.statusCode( statusCode );
++			statusCode = undefined;
++
++			if ( fireGlobals ) {
++				globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
++					[ jqXHR, s, isSuccess ? success : error ] );
++			}
++
++			// Complete
++			completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
++
++			if ( fireGlobals ) {
++				globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
++
++				// Handle the global AJAX counter
++				if ( !( --jQuery.active ) ) {
++					jQuery.event.trigger( "ajaxStop" );
++				}
++			}
++		}
++
++		return jqXHR;
++	},
++
++	getJSON: function( url, data, callback ) {
++		return jQuery.get( url, data, callback, "json" );
++	},
++
++	getScript: function( url, callback ) {
++		return jQuery.get( url, undefined, callback, "script" );
++	}
++} );
+ 
+-					// Do send the request
+-					// This may raise an exception which is actually
+-					// handled in jQuery.ajax (so no try/catch here)
+-					xhr.send( ( s.hasContent && s.data ) || null );
++jQuery.each( [ "get", "post" ], function( i, method ) {
++	jQuery[ method ] = function( url, data, callback, type ) {
+ 
+-					// Listener
+-					callback = function( _, isAbort ) {
+-						var status, responseHeaders, statusText, responses;
++		// Shift arguments if data argument was omitted
++		if ( isFunction( data ) ) {
++			type = type || callback;
++			callback = data;
++			data = undefined;
++		}
+ 
+-						// Firefox throws exceptions when accessing properties
+-						// of an xhr when a network error occurred
+-						// http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE)
+-						try {
++		// The url can be an options object (which then must have .url)
++		return jQuery.ajax( jQuery.extend( {
++			url: url,
++			type: method,
++			dataType: type,
++			data: data,
++			success: callback
++		}, jQuery.isPlainObject( url ) && url ) );
++	};
++} );
+ 
+-							// Was never called and is aborted or complete
+-							if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
+ 
+-								// Only called once
+-								callback = undefined;
++jQuery._evalUrl = function( url, options ) {
++	return jQuery.ajax( {
++		url: url,
+ 
+-								// Do not keep as active anymore
+-								if ( handle ) {
+-									xhr.onreadystatechange = jQuery.noop;
+-									if ( xhrOnUnloadAbort ) {
+-										delete xhrCallbacks[ handle ];
+-									}
+-								}
++		// Make this explicit, since user can override this through ajaxSetup (#11264)
++		type: "GET",
++		dataType: "script",
++		cache: true,
++		async: false,
++		global: false,
++
++		// Only evaluate the response if it is successful (gh-4126)
++		// dataFilter is not invoked for failure responses, so using it instead
++		// of the default converter is kludgy but it works.
++		converters: {
++			"text script": function() {}
++		},
++		dataFilter: function( response ) {
++			jQuery.globalEval( response, options );
++		}
++	} );
++};
+ 
+-								// If it's an abort
+-								if ( isAbort ) {
+-									// Abort it manually if needed
+-									if ( xhr.readyState !== 4 ) {
+-										xhr.abort();
+-									}
+-								} else {
+-									responses = {};
+-									status = xhr.status;
+-									responseHeaders = xhr.getAllResponseHeaders();
+-
+-									// When requesting binary data, IE6-9 will throw an exception
+-									// on any attempt to access responseText (#11426)
+-									if ( typeof xhr.responseText === "string" ) {
+-										responses.text = xhr.responseText;
+-									}
+ 
+-									// Firefox throws an exception when accessing
+-									// statusText for faulty cross-domain requests
+-									try {
+-										statusText = xhr.statusText;
+-									} catch( e ) {
+-										// We normalize with Webkit giving an empty statusText
+-										statusText = "";
+-									}
++jQuery.fn.extend( {
++	wrapAll: function( html ) {
++		var wrap;
+ 
+-									// Filter status for non standard behaviors
++		if ( this[ 0 ] ) {
++			if ( isFunction( html ) ) {
++				html = html.call( this[ 0 ] );
++			}
+ 
+-									// If the request is local and we have data: assume a success
+-									// (success with no data won't get notified, that's the best we
+-									// can do given current implementations)
+-									if ( !status && s.isLocal && !s.crossDomain ) {
+-										status = responses.text ? 200 : 404;
+-									// IE - #1450: sometimes returns 1223 when it should be 204
+-									} else if ( status === 1223 ) {
+-										status = 204;
+-									}
+-								}
+-							}
+-						} catch( firefoxAccessException ) {
+-							if ( !isAbort ) {
+-								complete( -1, firefoxAccessException );
+-							}
+-						}
++			// The elements to wrap the target around
++			wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true );
+ 
+-						// Call complete if needed
+-						if ( responses ) {
+-							complete( status, statusText, responses, responseHeaders );
+-						}
+-					};
++			if ( this[ 0 ].parentNode ) {
++				wrap.insertBefore( this[ 0 ] );
++			}
+ 
+-					if ( !s.async ) {
+-						// if we're in sync mode we fire the callback
+-						callback();
+-					} else if ( xhr.readyState === 4 ) {
+-						// (IE6 & IE7) if it's in cache and has been
+-						// retrieved directly we need to fire the callback
+-						setTimeout( callback );
+-					} else {
+-						handle = ++xhrId;
+-						if ( xhrOnUnloadAbort ) {
+-							// Create the active xhrs callbacks list if needed
+-							// and attach the unload handler
+-							if ( !xhrCallbacks ) {
+-								xhrCallbacks = {};
+-								jQuery( window ).unload( xhrOnUnloadAbort );
+-							}
+-							// Add to list of active xhrs callbacks
+-							xhrCallbacks[ handle ] = callback;
+-						}
+-						xhr.onreadystatechange = callback;
+-					}
+-				},
++			wrap.map( function() {
++				var elem = this;
+ 
+-				abort: function() {
+-					if ( callback ) {
+-						callback( undefined, true );
+-					}
++				while ( elem.firstElementChild ) {
++					elem = elem.firstElementChild;
+ 				}
+-			};
++
++				return elem;
++			} ).append( this );
+ 		}
+-	});
+-}
+-var fxNow, timerId,
+-	rfxtypes = /^(?:toggle|show|hide)$/,
+-	rfxnum = new RegExp( "^(?:([+-])=|)(" + core_pnum + ")([a-z%]*)$", "i" ),
+-	rrun = /queueHooks$/,
+-	animationPrefilters = [ defaultPrefilter ],
+-	tweeners = {
+-		"*": [function( prop, value ) {
+-			var tween = this.createTween( prop, value ),
+-				target = tween.cur(),
+-				parts = rfxnum.exec( value ),
+-				unit = parts && parts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
+-
+-				// Starting value computation is required for potential unit mismatches
+-				start = ( jQuery.cssNumber[ prop ] || unit !== "px" && +target ) &&
+-					rfxnum.exec( jQuery.css( tween.elem, prop ) ),
+-				scale = 1,
+-				maxIterations = 20;
+-
+-			if ( start && start[ 3 ] !== unit ) {
+-				// Trust units reported by jQuery.css
+-				unit = unit || start[ 3 ];
+-
+-				// Make sure we update the tween properties later on
+-				parts = parts || [];
+-
+-				// Iteratively approximate from a nonzero starting point
+-				start = +target || 1;
+ 
+-				do {
+-					// If previous iteration zeroed out, double until we get *something*
+-					// Use a string for doubling factor so we don't accidentally see scale as unchanged below
+-					scale = scale || ".5";
++		return this;
++	},
+ 
+-					// Adjust and apply
+-					start = start / scale;
+-					jQuery.style( tween.elem, prop, start + unit );
++	wrapInner: function( html ) {
++		if ( isFunction( html ) ) {
++			return this.each( function( i ) {
++				jQuery( this ).wrapInner( html.call( this, i ) );
++			} );
++		}
+ 
+-				// Update scale, tolerating zero or NaN from tween.cur()
+-				// And breaking the loop if scale is unchanged or perfect, or if we've just had enough
+-				} while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations );
+-			}
++		return this.each( function() {
++			var self = jQuery( this ),
++				contents = self.contents();
+ 
+-			// Update tween properties
+-			if ( parts ) {
+-				start = tween.start = +start || +target || 0;
+-				tween.unit = unit;
+-				// If a +=/-= token was provided, we're doing a relative animation
+-				tween.end = parts[ 1 ] ?
+-					start + ( parts[ 1 ] + 1 ) * parts[ 2 ] :
+-					+parts[ 2 ];
+-			}
++			if ( contents.length ) {
++				contents.wrapAll( html );
+ 
+-			return tween;
+-		}]
+-	};
++			} else {
++				self.append( html );
++			}
++		} );
++	},
+ 
+-// Animations created synchronously will run synchronously
+-function createFxNow() {
+-	setTimeout(function() {
+-		fxNow = undefined;
+-	});
+-	return ( fxNow = jQuery.now() );
+-}
++	wrap: function( html ) {
++		var htmlIsFunction = isFunction( html );
+ 
+-function createTween( value, prop, animation ) {
+-	var tween,
+-		collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ),
+-		index = 0,
+-		length = collection.length;
+-	for ( ; index < length; index++ ) {
+-		if ( (tween = collection[ index ].call( animation, prop, value )) ) {
++		return this.each( function( i ) {
++			jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html );
++		} );
++	},
+ 
+-			// we're done with this property
+-			return tween;
+-		}
++	unwrap: function( selector ) {
++		this.parent( selector ).not( "body" ).each( function() {
++			jQuery( this ).replaceWith( this.childNodes );
++		} );
++		return this;
+ 	}
+-}
++} );
+ 
+-function Animation( elem, properties, options ) {
+-	var result,
+-		stopped,
+-		index = 0,
+-		length = animationPrefilters.length,
+-		deferred = jQuery.Deferred().always( function() {
+-			// don't match elem in the :animated selector
+-			delete tick.elem;
+-		}),
+-		tick = function() {
+-			if ( stopped ) {
+-				return false;
+-			}
+-			var currentTime = fxNow || createFxNow(),
+-				remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
+-				// archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497)
+-				temp = remaining / animation.duration || 0,
+-				percent = 1 - temp,
+-				index = 0,
+-				length = animation.tweens.length;
+ 
+-			for ( ; index < length ; index++ ) {
+-				animation.tweens[ index ].run( percent );
+-			}
++jQuery.expr.pseudos.hidden = function( elem ) {
++	return !jQuery.expr.pseudos.visible( elem );
++};
++jQuery.expr.pseudos.visible = function( elem ) {
++	return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );
++};
+ 
+-			deferred.notifyWith( elem, [ animation, percent, remaining ]);
+ 
+-			if ( percent < 1 && length ) {
+-				return remaining;
+-			} else {
+-				deferred.resolveWith( elem, [ animation ] );
+-				return false;
+-			}
+-		},
+-		animation = deferred.promise({
+-			elem: elem,
+-			props: jQuery.extend( {}, properties ),
+-			opts: jQuery.extend( true, { specialEasing: {} }, options ),
+-			originalProperties: properties,
+-			originalOptions: options,
+-			startTime: fxNow || createFxNow(),
+-			duration: options.duration,
+-			tweens: [],
+-			createTween: function( prop, end ) {
+-				var tween = jQuery.Tween( elem, animation.opts, prop, end,
+-						animation.opts.specialEasing[ prop ] || animation.opts.easing );
+-				animation.tweens.push( tween );
+-				return tween;
+-			},
+-			stop: function( gotoEnd ) {
+-				var index = 0,
+-					// if we are going to the end, we want to run all the tweens
+-					// otherwise we skip this part
+-					length = gotoEnd ? animation.tweens.length : 0;
+-				if ( stopped ) {
+-					return this;
+-				}
+-				stopped = true;
+-				for ( ; index < length ; index++ ) {
+-					animation.tweens[ index ].run( 1 );
+-				}
+ 
+-				// resolve when we played the last frame
+-				// otherwise, reject
+-				if ( gotoEnd ) {
+-					deferred.resolveWith( elem, [ animation, gotoEnd ] );
+-				} else {
+-					deferred.rejectWith( elem, [ animation, gotoEnd ] );
+-				}
+-				return this;
+-			}
+-		}),
+-		props = animation.props;
+ 
+-	propFilter( props, animation.opts.specialEasing );
++jQuery.ajaxSettings.xhr = function() {
++	try {
++		return new window.XMLHttpRequest();
++	} catch ( e ) {}
++};
+ 
+-	for ( ; index < length ; index++ ) {
+-		result = animationPrefilters[ index ].call( animation, elem, props, animation.opts );
+-		if ( result ) {
+-			return result;
+-		}
+-	}
++var xhrSuccessStatus = {
+ 
+-	jQuery.map( props, createTween, animation );
++		// File protocol always yields status code 0, assume 200
++		0: 200,
+ 
+-	if ( jQuery.isFunction( animation.opts.start ) ) {
+-		animation.opts.start.call( elem, animation );
+-	}
++		// Support: IE <=9 only
++		// #1450: sometimes IE returns 1223 when it should be 204
++		1223: 204
++	},
++	xhrSupported = jQuery.ajaxSettings.xhr();
+ 
+-	jQuery.fx.timer(
+-		jQuery.extend( tick, {
+-			elem: elem,
+-			anim: animation,
+-			queue: animation.opts.queue
+-		})
+-	);
++support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
++support.ajax = xhrSupported = !!xhrSupported;
+ 
+-	// attach callbacks from options
+-	return animation.progress( animation.opts.progress )
+-		.done( animation.opts.done, animation.opts.complete )
+-		.fail( animation.opts.fail )
+-		.always( animation.opts.always );
+-}
++jQuery.ajaxTransport( function( options ) {
++	var callback, errorCallback;
+ 
+-function propFilter( props, specialEasing ) {
+-	var index, name, easing, value, hooks;
++	// Cross domain only allowed if supported through XMLHttpRequest
++	if ( support.cors || xhrSupported && !options.crossDomain ) {
++		return {
++			send: function( headers, complete ) {
++				var i,
++					xhr = options.xhr();
++
++				xhr.open(
++					options.type,
++					options.url,
++					options.async,
++					options.username,
++					options.password
++				);
+ 
+-	// camelCase, specialEasing and expand cssHook pass
+-	for ( index in props ) {
+-		name = jQuery.camelCase( index );
+-		easing = specialEasing[ name ];
+-		value = props[ index ];
+-		if ( jQuery.isArray( value ) ) {
+-			easing = value[ 1 ];
+-			value = props[ index ] = value[ 0 ];
+-		}
++				// Apply custom fields if provided
++				if ( options.xhrFields ) {
++					for ( i in options.xhrFields ) {
++						xhr[ i ] = options.xhrFields[ i ];
++					}
++				}
+ 
+-		if ( index !== name ) {
+-			props[ name ] = value;
+-			delete props[ index ];
+-		}
++				// Override mime type if needed
++				if ( options.mimeType && xhr.overrideMimeType ) {
++					xhr.overrideMimeType( options.mimeType );
++				}
+ 
+-		hooks = jQuery.cssHooks[ name ];
+-		if ( hooks && "expand" in hooks ) {
+-			value = hooks.expand( value );
+-			delete props[ name ];
++				// X-Requested-With header
++				// For cross-domain requests, seeing as conditions for a preflight are
++				// akin to a jigsaw puzzle, we simply never set it to be sure.
++				// (it can always be set on a per-request basis or even using ajaxSetup)
++				// For same-domain requests, won't change header if already provided.
++				if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) {
++					headers[ "X-Requested-With" ] = "XMLHttpRequest";
++				}
+ 
+-			// not quite $.extend, this wont overwrite keys already present.
+-			// also - reusing 'index' from above because we have the correct "name"
+-			for ( index in value ) {
+-				if ( !( index in props ) ) {
+-					props[ index ] = value[ index ];
+-					specialEasing[ index ] = easing;
++				// Set headers
++				for ( i in headers ) {
++					xhr.setRequestHeader( i, headers[ i ] );
+ 				}
+-			}
+-		} else {
+-			specialEasing[ name ] = easing;
+-		}
+-	}
+-}
+ 
+-jQuery.Animation = jQuery.extend( Animation, {
++				// Callback
++				callback = function( type ) {
++					return function() {
++						if ( callback ) {
++							callback = errorCallback = xhr.onload =
++								xhr.onerror = xhr.onabort = xhr.ontimeout =
++									xhr.onreadystatechange = null;
+ 
+-	tweener: function( props, callback ) {
+-		if ( jQuery.isFunction( props ) ) {
+-			callback = props;
+-			props = [ "*" ];
+-		} else {
+-			props = props.split(" ");
+-		}
++							if ( type === "abort" ) {
++								xhr.abort();
++							} else if ( type === "error" ) {
+ 
+-		var prop,
+-			index = 0,
+-			length = props.length;
++								// Support: IE <=9 only
++								// On a manual native abort, IE9 throws
++								// errors on any property access that is not readyState
++								if ( typeof xhr.status !== "number" ) {
++									complete( 0, "error" );
++								} else {
++									complete(
+ 
+-		for ( ; index < length ; index++ ) {
+-			prop = props[ index ];
+-			tweeners[ prop ] = tweeners[ prop ] || [];
+-			tweeners[ prop ].unshift( callback );
+-		}
+-	},
++										// File: protocol always yields status 0; see #8605, #14207
++										xhr.status,
++										xhr.statusText
++									);
++								}
++							} else {
++								complete(
++									xhrSuccessStatus[ xhr.status ] || xhr.status,
++									xhr.statusText,
++
++									// Support: IE <=9 only
++									// IE9 has no XHR2 but throws on binary (trac-11426)
++									// For XHR2 non-text, let the caller handle it (gh-2498)
++									( xhr.responseType || "text" ) !== "text"  ||
++									typeof xhr.responseText !== "string" ?
++										{ binary: xhr.response } :
++										{ text: xhr.responseText },
++									xhr.getAllResponseHeaders()
++								);
++							}
++						}
++					};
++				};
+ 
+-	prefilter: function( callback, prepend ) {
+-		if ( prepend ) {
+-			animationPrefilters.unshift( callback );
+-		} else {
+-			animationPrefilters.push( callback );
+-		}
+-	}
+-});
++				// Listen to events
++				xhr.onload = callback();
++				errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" );
+ 
+-function defaultPrefilter( elem, props, opts ) {
+-	/* jshint validthis: true */
+-	var prop, value, toggle, tween, hooks, oldfire,
+-		anim = this,
+-		orig = {},
+-		style = elem.style,
+-		hidden = elem.nodeType && isHidden( elem ),
+-		dataShow = jQuery._data( elem, "fxshow" );
++				// Support: IE 9 only
++				// Use onreadystatechange to replace onabort
++				// to handle uncaught aborts
++				if ( xhr.onabort !== undefined ) {
++					xhr.onabort = errorCallback;
++				} else {
++					xhr.onreadystatechange = function() {
++
++						// Check readyState before timeout as it changes
++						if ( xhr.readyState === 4 ) {
++
++							// Allow onerror to be called first,
++							// but that will not handle a native abort
++							// Also, save errorCallback to a variable
++							// as xhr.onerror cannot be accessed
++							window.setTimeout( function() {
++								if ( callback ) {
++									errorCallback();
++								}
++							} );
++						}
++					};
++				}
+ 
+-	// handle queue: false promises
+-	if ( !opts.queue ) {
+-		hooks = jQuery._queueHooks( elem, "fx" );
+-		if ( hooks.unqueued == null ) {
+-			hooks.unqueued = 0;
+-			oldfire = hooks.empty.fire;
+-			hooks.empty.fire = function() {
+-				if ( !hooks.unqueued ) {
+-					oldfire();
++				// Create the abort callback
++				callback = callback( "abort" );
++
++				try {
++
++					// Do send the request (this may raise an exception)
++					xhr.send( options.hasContent && options.data || null );
++				} catch ( e ) {
++
++					// #14683: Only rethrow if this hasn't been notified as an error yet
++					if ( callback ) {
++						throw e;
++					}
+ 				}
+-			};
+-		}
+-		hooks.unqueued++;
++			},
+ 
+-		anim.always(function() {
+-			// doing this makes sure that the complete handler will be called
+-			// before this completes
+-			anim.always(function() {
+-				hooks.unqueued--;
+-				if ( !jQuery.queue( elem, "fx" ).length ) {
+-					hooks.empty.fire();
++			abort: function() {
++				if ( callback ) {
++					callback();
+ 				}
+-			});
+-		});
++			}
++		};
+ 	}
++} );
+ 
+-	// height/width overflow pass
+-	if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {
+-		// Make sure that nothing sneaks out
+-		// Record all 3 overflow attributes because IE does not
+-		// change the overflow attribute when overflowX and
+-		// overflowY are set to the same value
+-		opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
+ 
+-		// Set display property to inline-block for height/width
+-		// animations on inline elements that are having width/height animated
+-		if ( jQuery.css( elem, "display" ) === "inline" &&
+-				jQuery.css( elem, "float" ) === "none" ) {
+ 
+-			// inline-level elements accept inline-block;
+-			// block-level elements need to be inline with layout
+-			if ( !jQuery.support.inlineBlockNeedsLayout || css_defaultDisplay( elem.nodeName ) === "inline" ) {
+-				style.display = "inline-block";
+ 
+-			} else {
+-				style.zoom = 1;
+-			}
+-		}
++// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432)
++jQuery.ajaxPrefilter( function( s ) {
++	if ( s.crossDomain ) {
++		s.contents.script = false;
+ 	}
++} );
+ 
+-	if ( opts.overflow ) {
+-		style.overflow = "hidden";
+-		if ( !jQuery.support.shrinkWrapBlocks ) {
+-			anim.always(function() {
+-				style.overflow = opts.overflow[ 0 ];
+-				style.overflowX = opts.overflow[ 1 ];
+-				style.overflowY = opts.overflow[ 2 ];
+-			});
++// Install script dataType
++jQuery.ajaxSetup( {
++	accepts: {
++		script: "text/javascript, application/javascript, " +
++			"application/ecmascript, application/x-ecmascript"
++	},
++	contents: {
++		script: /\b(?:java|ecma)script\b/
++	},
++	converters: {
++		"text script": function( text ) {
++			jQuery.globalEval( text );
++			return text;
+ 		}
+ 	}
++} );
+ 
+-
+-	// show/hide pass
+-	for ( prop in props ) {
+-		value = props[ prop ];
+-		if ( rfxtypes.exec( value ) ) {
+-			delete props[ prop ];
+-			toggle = toggle || value === "toggle";
+-			if ( value === ( hidden ? "hide" : "show" ) ) {
+-				continue;
+-			}
+-			orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );
+-		}
++// Handle cache's special case and crossDomain
++jQuery.ajaxPrefilter( "script", function( s ) {
++	if ( s.cache === undefined ) {
++		s.cache = false;
++	}
++	if ( s.crossDomain ) {
++		s.type = "GET";
+ 	}
++} );
+ 
+-	if ( !jQuery.isEmptyObject( orig ) ) {
+-		if ( dataShow ) {
+-			if ( "hidden" in dataShow ) {
+-				hidden = dataShow.hidden;
+-			}
+-		} else {
+-			dataShow = jQuery._data( elem, "fxshow", {} );
+-		}
++// Bind script tag hack transport
++jQuery.ajaxTransport( "script", function( s ) {
+ 
+-		// store state if its toggle - enables .stop().toggle() to "reverse"
+-		if ( toggle ) {
+-			dataShow.hidden = !hidden;
+-		}
+-		if ( hidden ) {
+-			jQuery( elem ).show();
+-		} else {
+-			anim.done(function() {
+-				jQuery( elem ).hide();
+-			});
+-		}
+-		anim.done(function() {
+-			var prop;
+-			jQuery._removeData( elem, "fxshow" );
+-			for ( prop in orig ) {
+-				jQuery.style( elem, prop, orig[ prop ] );
+-			}
+-		});
+-		for ( prop in orig ) {
+-			tween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );
++	// This transport only deals with cross domain or forced-by-attrs requests
++	if ( s.crossDomain || s.scriptAttrs ) {
++		var script, callback;
++		return {
++			send: function( _, complete ) {
++				script = jQuery( "<script>" )
++					.attr( s.scriptAttrs || {} )
++					.prop( { charset: s.scriptCharset, src: s.url } )
++					.on( "load error", callback = function( evt ) {
++						script.remove();
++						callback = null;
++						if ( evt ) {
++							complete( evt.type === "error" ? 404 : 200, evt.type );
++						}
++					} );
+ 
+-			if ( !( prop in dataShow ) ) {
+-				dataShow[ prop ] = tween.start;
+-				if ( hidden ) {
+-					tween.end = tween.start;
+-					tween.start = prop === "width" || prop === "height" ? 1 : 0;
++				// Use native DOM manipulation to avoid our domManip AJAX trickery
++				document.head.appendChild( script[ 0 ] );
++			},
++			abort: function() {
++				if ( callback ) {
++					callback();
+ 				}
+ 			}
+-		}
++		};
+ 	}
+-}
++} );
+ 
+-function Tween( elem, options, prop, end, easing ) {
+-	return new Tween.prototype.init( elem, options, prop, end, easing );
+-}
+-jQuery.Tween = Tween;
+-
+-Tween.prototype = {
+-	constructor: Tween,
+-	init: function( elem, options, prop, end, easing, unit ) {
+-		this.elem = elem;
+-		this.prop = prop;
+-		this.easing = easing || "swing";
+-		this.options = options;
+-		this.start = this.now = this.cur();
+-		this.end = end;
+-		this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
+-	},
+-	cur: function() {
+-		var hooks = Tween.propHooks[ this.prop ];
+ 
+-		return hooks && hooks.get ?
+-			hooks.get( this ) :
+-			Tween.propHooks._default.get( this );
+-	},
+-	run: function( percent ) {
+-		var eased,
+-			hooks = Tween.propHooks[ this.prop ];
+ 
+-		if ( this.options.duration ) {
+-			this.pos = eased = jQuery.easing[ this.easing ](
+-				percent, this.options.duration * percent, 0, 1, this.options.duration
+-			);
+-		} else {
+-			this.pos = eased = percent;
+-		}
+-		this.now = ( this.end - this.start ) * eased + this.start;
+ 
+-		if ( this.options.step ) {
+-			this.options.step.call( this.elem, this.now, this );
+-		}
++var oldCallbacks = [],
++	rjsonp = /(=)\?(?=&|$)|\?\?/;
+ 
+-		if ( hooks && hooks.set ) {
+-			hooks.set( this );
+-		} else {
+-			Tween.propHooks._default.set( this );
+-		}
+-		return this;
++// Default jsonp settings
++jQuery.ajaxSetup( {
++	jsonp: "callback",
++	jsonpCallback: function() {
++		var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) );
++		this[ callback ] = true;
++		return callback;
+ 	}
+-};
+-
+-Tween.prototype.init.prototype = Tween.prototype;
++} );
+ 
+-Tween.propHooks = {
+-	_default: {
+-		get: function( tween ) {
+-			var result;
++// Detect, normalize options and install callbacks for jsonp requests
++jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
+ 
+-			if ( tween.elem[ tween.prop ] != null &&
+-				(!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) {
+-				return tween.elem[ tween.prop ];
+-			}
++	var callbackName, overwritten, responseContainer,
++		jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?
++			"url" :
++			typeof s.data === "string" &&
++				( s.contentType || "" )
++					.indexOf( "application/x-www-form-urlencoded" ) === 0 &&
++				rjsonp.test( s.data ) && "data"
++		);
+ 
+-			// passing an empty string as a 3rd parameter to .css will automatically
+-			// attempt a parseFloat and fallback to a string if the parse fails
+-			// so, simple values such as "10px" are parsed to Float.
+-			// complex values such as "rotate(1rad)" are returned as is.
+-			result = jQuery.css( tween.elem, tween.prop, "" );
+-			// Empty strings, null, undefined and "auto" are converted to 0.
+-			return !result || result === "auto" ? 0 : result;
+-		},
+-		set: function( tween ) {
+-			// use step hook for back compat - use cssHook if its there - use .style if its
+-			// available and use plain properties where available
+-			if ( jQuery.fx.step[ tween.prop ] ) {
+-				jQuery.fx.step[ tween.prop ]( tween );
+-			} else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) {
+-				jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
+-			} else {
+-				tween.elem[ tween.prop ] = tween.now;
+-			}
+-		}
+-	}
+-};
++	// Handle iff the expected data type is "jsonp" or we have a parameter to set
++	if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) {
+ 
+-// Support: IE <=9
+-// Panic based approach to setting things on disconnected nodes
++		// Get callback name, remembering preexisting value associated with it
++		callbackName = s.jsonpCallback = isFunction( s.jsonpCallback ) ?
++			s.jsonpCallback() :
++			s.jsonpCallback;
+ 
+-Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
+-	set: function( tween ) {
+-		if ( tween.elem.nodeType && tween.elem.parentNode ) {
+-			tween.elem[ tween.prop ] = tween.now;
++		// Insert callback into url or form data
++		if ( jsonProp ) {
++			s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName );
++		} else if ( s.jsonp !== false ) {
++			s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
+ 		}
+-	}
+-};
+-
+-jQuery.each([ "toggle", "show", "hide" ], function( i, name ) {
+-	var cssFn = jQuery.fn[ name ];
+-	jQuery.fn[ name ] = function( speed, easing, callback ) {
+-		return speed == null || typeof speed === "boolean" ?
+-			cssFn.apply( this, arguments ) :
+-			this.animate( genFx( name, true ), speed, easing, callback );
+-	};
+-});
+-
+-jQuery.fn.extend({
+-	fadeTo: function( speed, to, easing, callback ) {
+-
+-		// show any hidden elements after setting opacity to 0
+-		return this.filter( isHidden ).css( "opacity", 0 ).show()
+ 
+-			// animate to the value specified
+-			.end().animate({ opacity: to }, speed, easing, callback );
+-	},
+-	animate: function( prop, speed, easing, callback ) {
+-		var empty = jQuery.isEmptyObject( prop ),
+-			optall = jQuery.speed( speed, easing, callback ),
+-			doAnimation = function() {
+-				// Operate on a copy of prop so per-property easing won't be lost
+-				var anim = Animation( this, jQuery.extend( {}, prop ), optall );
++		// Use data converter to retrieve json after script execution
++		s.converters[ "script json" ] = function() {
++			if ( !responseContainer ) {
++				jQuery.error( callbackName + " was not called" );
++			}
++			return responseContainer[ 0 ];
++		};
+ 
+-				// Empty animations, or finishing resolves immediately
+-				if ( empty || jQuery._data( this, "finish" ) ) {
+-					anim.stop( true );
+-				}
+-			};
+-			doAnimation.finish = doAnimation;
++		// Force json dataType
++		s.dataTypes[ 0 ] = "json";
+ 
+-		return empty || optall.queue === false ?
+-			this.each( doAnimation ) :
+-			this.queue( optall.queue, doAnimation );
+-	},
+-	stop: function( type, clearQueue, gotoEnd ) {
+-		var stopQueue = function( hooks ) {
+-			var stop = hooks.stop;
+-			delete hooks.stop;
+-			stop( gotoEnd );
++		// Install callback
++		overwritten = window[ callbackName ];
++		window[ callbackName ] = function() {
++			responseContainer = arguments;
+ 		};
+ 
+-		if ( typeof type !== "string" ) {
+-			gotoEnd = clearQueue;
+-			clearQueue = type;
+-			type = undefined;
+-		}
+-		if ( clearQueue && type !== false ) {
+-			this.queue( type || "fx", [] );
+-		}
++		// Clean-up function (fires after converters)
++		jqXHR.always( function() {
+ 
+-		return this.each(function() {
+-			var dequeue = true,
+-				index = type != null && type + "queueHooks",
+-				timers = jQuery.timers,
+-				data = jQuery._data( this );
++			// If previous value didn't exist - remove it
++			if ( overwritten === undefined ) {
++				jQuery( window ).removeProp( callbackName );
+ 
+-			if ( index ) {
+-				if ( data[ index ] && data[ index ].stop ) {
+-					stopQueue( data[ index ] );
+-				}
++			// Otherwise restore preexisting value
+ 			} else {
+-				for ( index in data ) {
+-					if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
+-						stopQueue( data[ index ] );
+-					}
+-				}
+-			}
+-
+-			for ( index = timers.length; index--; ) {
+-				if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
+-					timers[ index ].anim.stop( gotoEnd );
+-					dequeue = false;
+-					timers.splice( index, 1 );
+-				}
+-			}
+-
+-			// start the next in the queue if the last step wasn't forced
+-			// timers currently will call their complete callbacks, which will dequeue
+-			// but only if they were gotoEnd
+-			if ( dequeue || !gotoEnd ) {
+-				jQuery.dequeue( this, type );
++				window[ callbackName ] = overwritten;
+ 			}
+-		});
+-	},
+-	finish: function( type ) {
+-		if ( type !== false ) {
+-			type = type || "fx";
+-		}
+-		return this.each(function() {
+-			var index,
+-				data = jQuery._data( this ),
+-				queue = data[ type + "queue" ],
+-				hooks = data[ type + "queueHooks" ],
+-				timers = jQuery.timers,
+-				length = queue ? queue.length : 0;
+-
+-			// enable finishing flag on private data
+-			data.finish = true;
+-
+-			// empty the queue first
+-			jQuery.queue( this, type, [] );
+ 
+-			if ( hooks && hooks.stop ) {
+-				hooks.stop.call( this, true );
+-			}
++			// Save back as free
++			if ( s[ callbackName ] ) {
+ 
+-			// look for any active animations, and finish them
+-			for ( index = timers.length; index--; ) {
+-				if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
+-					timers[ index ].anim.stop( true );
+-					timers.splice( index, 1 );
+-				}
+-			}
++				// Make sure that re-using the options doesn't screw things around
++				s.jsonpCallback = originalSettings.jsonpCallback;
+ 
+-			// look for any animations in the old queue and finish them
+-			for ( index = 0; index < length; index++ ) {
+-				if ( queue[ index ] && queue[ index ].finish ) {
+-					queue[ index ].finish.call( this );
+-				}
++				// Save the callback name for future use
++				oldCallbacks.push( callbackName );
+ 			}
+ 
+-			// turn off finishing flag
+-			delete data.finish;
+-		});
+-	}
+-});
++			// Call if it was a function and we have a response
++			if ( responseContainer && isFunction( overwritten ) ) {
++				overwritten( responseContainer[ 0 ] );
++			}
+ 
+-// Generate parameters to create a standard animation
+-function genFx( type, includeWidth ) {
+-	var which,
+-		attrs = { height: type },
+-		i = 0;
++			responseContainer = overwritten = undefined;
++		} );
+ 
+-	// if we include width, step value is 1 to do all cssExpand values,
+-	// if we don't include width, step value is 2 to skip over Left and Right
+-	includeWidth = includeWidth? 1 : 0;
+-	for( ; i < 4 ; i += 2 - includeWidth ) {
+-		which = cssExpand[ i ];
+-		attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
++		// Delegate to script
++		return "script";
+ 	}
++} );
+ 
+-	if ( includeWidth ) {
+-		attrs.opacity = attrs.width = type;
+-	}
+ 
+-	return attrs;
+-}
+ 
+-// Generate shortcuts for custom animations
+-jQuery.each({
+-	slideDown: genFx("show"),
+-	slideUp: genFx("hide"),
+-	slideToggle: genFx("toggle"),
+-	fadeIn: { opacity: "show" },
+-	fadeOut: { opacity: "hide" },
+-	fadeToggle: { opacity: "toggle" }
+-}, function( name, props ) {
+-	jQuery.fn[ name ] = function( speed, easing, callback ) {
+-		return this.animate( props, speed, easing, callback );
+-	};
+-});
+ 
+-jQuery.speed = function( speed, easing, fn ) {
+-	var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
+-		complete: fn || !fn && easing ||
+-			jQuery.isFunction( speed ) && speed,
+-		duration: speed,
+-		easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
+-	};
++// Support: Safari 8 only
++// In Safari 8 documents created via document.implementation.createHTMLDocument
++// collapse sibling forms: the second one becomes a child of the first one.
++// Because of that, this security measure has to be disabled in Safari 8.
++// https://bugs.webkit.org/show_bug.cgi?id=137337
++support.createHTMLDocument = ( function() {
++	var body = document.implementation.createHTMLDocument( "" ).body;
++	body.innerHTML = "<form></form><form></form>";
++	return body.childNodes.length === 2;
++} )();
+ 
+-	opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
+-		opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
+ 
+-	// normalize opt.queue - true/undefined/null -> "fx"
+-	if ( opt.queue == null || opt.queue === true ) {
+-		opt.queue = "fx";
++// Argument "data" should be string of html
++// context (optional): If specified, the fragment will be created in this context,
++// defaults to document
++// keepScripts (optional): If true, will include scripts passed in the html string
++jQuery.parseHTML = function( data, context, keepScripts ) {
++	if ( typeof data !== "string" ) {
++		return [];
++	}
++	if ( typeof context === "boolean" ) {
++		keepScripts = context;
++		context = false;
+ 	}
+ 
+-	// Queueing
+-	opt.old = opt.complete;
++	var base, parsed, scripts;
+ 
+-	opt.complete = function() {
+-		if ( jQuery.isFunction( opt.old ) ) {
+-			opt.old.call( this );
+-		}
++	if ( !context ) {
+ 
+-		if ( opt.queue ) {
+-			jQuery.dequeue( this, opt.queue );
++		// Stop scripts or inline event handlers from being executed immediately
++		// by using document.implementation
++		if ( support.createHTMLDocument ) {
++			context = document.implementation.createHTMLDocument( "" );
++
++			// Set the base href for the created document
++			// so any parsed elements with URLs
++			// are based on the document's URL (gh-2965)
++			base = context.createElement( "base" );
++			base.href = document.location.href;
++			context.head.appendChild( base );
++		} else {
++			context = document;
+ 		}
+-	};
++	}
+ 
+-	return opt;
+-};
++	parsed = rsingleTag.exec( data );
++	scripts = !keepScripts && [];
+ 
+-jQuery.easing = {
+-	linear: function( p ) {
+-		return p;
+-	},
+-	swing: function( p ) {
+-		return 0.5 - Math.cos( p*Math.PI ) / 2;
++	// Single tag
++	if ( parsed ) {
++		return [ context.createElement( parsed[ 1 ] ) ];
+ 	}
+-};
+-
+-jQuery.timers = [];
+-jQuery.fx = Tween.prototype.init;
+-jQuery.fx.tick = function() {
+-	var timer,
+-		timers = jQuery.timers,
+-		i = 0;
+ 
+-	fxNow = jQuery.now();
++	parsed = buildFragment( [ data ], context, scripts );
+ 
+-	for ( ; i < timers.length; i++ ) {
+-		timer = timers[ i ];
+-		// Checks the timer has not already been removed
+-		if ( !timer() && timers[ i ] === timer ) {
+-			timers.splice( i--, 1 );
+-		}
++	if ( scripts && scripts.length ) {
++		jQuery( scripts ).remove();
+ 	}
+ 
+-	if ( !timers.length ) {
+-		jQuery.fx.stop();
+-	}
+-	fxNow = undefined;
++	return jQuery.merge( [], parsed.childNodes );
+ };
+ 
+-jQuery.fx.timer = function( timer ) {
+-	if ( timer() && jQuery.timers.push( timer ) ) {
+-		jQuery.fx.start();
++
++/**
++ * Load a url into a page
++ */
++jQuery.fn.load = function( url, params, callback ) {
++	var selector, type, response,
++		self = this,
++		off = url.indexOf( " " );
++
++	if ( off > -1 ) {
++		selector = stripAndCollapse( url.slice( off ) );
++		url = url.slice( 0, off );
+ 	}
+-};
+ 
+-jQuery.fx.interval = 13;
++	// If it's a function
++	if ( isFunction( params ) ) {
+ 
+-jQuery.fx.start = function() {
+-	if ( !timerId ) {
+-		timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval );
++		// We assume that it's the callback
++		callback = params;
++		params = undefined;
++
++	// Otherwise, build a param string
++	} else if ( params && typeof params === "object" ) {
++		type = "POST";
+ 	}
+-};
+ 
+-jQuery.fx.stop = function() {
+-	clearInterval( timerId );
+-	timerId = null;
+-};
++	// If we have elements to modify, make the request
++	if ( self.length > 0 ) {
++		jQuery.ajax( {
++			url: url,
+ 
+-jQuery.fx.speeds = {
+-	slow: 600,
+-	fast: 200,
+-	// Default speed
+-	_default: 400
+-};
++			// If "type" variable is undefined, then "GET" method will be used.
++			// Make value of this field explicit since
++			// user can override it through ajaxSetup method
++			type: type || "GET",
++			dataType: "html",
++			data: params
++		} ).done( function( responseText ) {
+ 
+-// Back Compat <1.8 extension point
+-jQuery.fx.step = {};
++			// Save response for use in complete callback
++			response = arguments;
+ 
+-if ( jQuery.expr && jQuery.expr.filters ) {
+-	jQuery.expr.filters.animated = function( elem ) {
+-		return jQuery.grep(jQuery.timers, function( fn ) {
+-			return elem === fn.elem;
+-		}).length;
+-	};
+-}
+-jQuery.fn.offset = function( options ) {
+-	if ( arguments.length ) {
+-		return options === undefined ?
+-			this :
+-			this.each(function( i ) {
+-				jQuery.offset.setOffset( this, options, i );
+-			});
+-	}
++			self.html( selector ?
+ 
+-	var docElem, win,
+-		box = { top: 0, left: 0 },
+-		elem = this[ 0 ],
+-		doc = elem && elem.ownerDocument;
++				// If a selector was specified, locate the right elements in a dummy div
++				// Exclude scripts to avoid IE 'Permission Denied' errors
++				jQuery( "<div>" ).append( jQuery.parseHTML( responseText ) ).find( selector ) :
+ 
+-	if ( !doc ) {
+-		return;
++				// Otherwise use the full result
++				responseText );
++
++		// If the request succeeds, this function gets "data", "status", "jqXHR"
++		// but they are ignored because response was set above.
++		// If it fails, this function gets "jqXHR", "status", "error"
++		} ).always( callback && function( jqXHR, status ) {
++			self.each( function() {
++				callback.apply( this, response || [ jqXHR.responseText, status, jqXHR ] );
++			} );
++		} );
+ 	}
+ 
+-	docElem = doc.documentElement;
++	return this;
++};
+ 
+-	// Make sure it's not a disconnected DOM node
+-	if ( !jQuery.contains( docElem, elem ) ) {
+-		return box;
+-	}
+ 
+-	// If we don't have gBCR, just use 0,0 rather than error
+-	// BlackBerry 5, iOS 3 (original iPhone)
+-	if ( typeof elem.getBoundingClientRect !== core_strundefined ) {
+-		box = elem.getBoundingClientRect();
+-	}
+-	win = getWindow( doc );
+-	return {
+-		top: box.top  + ( win.pageYOffset || docElem.scrollTop )  - ( docElem.clientTop  || 0 ),
+-		left: box.left + ( win.pageXOffset || docElem.scrollLeft ) - ( docElem.clientLeft || 0 )
++
++
++// Attach a bunch of functions for handling common AJAX events
++jQuery.each( [
++	"ajaxStart",
++	"ajaxStop",
++	"ajaxComplete",
++	"ajaxError",
++	"ajaxSuccess",
++	"ajaxSend"
++], function( i, type ) {
++	jQuery.fn[ type ] = function( fn ) {
++		return this.on( type, fn );
+ 	};
++} );
++
++
++
++
++jQuery.expr.pseudos.animated = function( elem ) {
++	return jQuery.grep( jQuery.timers, function( fn ) {
++		return elem === fn.elem;
++	} ).length;
+ };
+ 
+-jQuery.offset = {
+ 
++
++
++jQuery.offset = {
+ 	setOffset: function( elem, options, i ) {
+-		var position = jQuery.css( elem, "position" );
++		var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
++			position = jQuery.css( elem, "position" ),
++			curElem = jQuery( elem ),
++			props = {};
+ 
+-		// set position first, in-case top/left are set even on static elem
++		// Set position first, in-case top/left are set even on static elem
+ 		if ( position === "static" ) {
+ 			elem.style.position = "relative";
+ 		}
+ 
+-		var curElem = jQuery( elem ),
+-			curOffset = curElem.offset(),
+-			curCSSTop = jQuery.css( elem, "top" ),
+-			curCSSLeft = jQuery.css( elem, "left" ),
+-			calculatePosition = ( position === "absolute" || position === "fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1,
+-			props = {}, curPosition = {}, curTop, curLeft;
++		curOffset = curElem.offset();
++		curCSSTop = jQuery.css( elem, "top" );
++		curCSSLeft = jQuery.css( elem, "left" );
++		calculatePosition = ( position === "absolute" || position === "fixed" ) &&
++			( curCSSTop + curCSSLeft ).indexOf( "auto" ) > -1;
+ 
+-		// need to be able to calculate position if either top or left is auto and position is either absolute or fixed
++		// Need to be able to calculate position if either
++		// top or left is auto and position is either absolute or fixed
+ 		if ( calculatePosition ) {
+ 			curPosition = curElem.position();
+ 			curTop = curPosition.top;
+ 			curLeft = curPosition.left;
++
+ 		} else {
+ 			curTop = parseFloat( curCSSTop ) || 0;
+ 			curLeft = parseFloat( curCSSLeft ) || 0;
+ 		}
+ 
+-		if ( jQuery.isFunction( options ) ) {
+-			options = options.call( elem, i, curOffset );
++		if ( isFunction( options ) ) {
++
++			// Use jQuery.extend here to allow modification of coordinates argument (gh-1848)
++			options = options.call( elem, i, jQuery.extend( {}, curOffset ) );
+ 		}
+ 
+ 		if ( options.top != null ) {
+@@ -9620,122 +10219,201 @@ jQuery.offset = {
+ 
+ 		if ( "using" in options ) {
+ 			options.using.call( elem, props );
++
+ 		} else {
+ 			curElem.css( props );
+ 		}
+ 	}
+ };
+ 
++jQuery.fn.extend( {
++
++	// offset() relates an element's border box to the document origin
++	offset: function( options ) {
++
++		// Preserve chaining for setter
++		if ( arguments.length ) {
++			return options === undefined ?
++				this :
++				this.each( function( i ) {
++					jQuery.offset.setOffset( this, options, i );
++				} );
++		}
++
++		var rect, win,
++			elem = this[ 0 ];
++
++		if ( !elem ) {
++			return;
++		}
++
++		// Return zeros for disconnected and hidden (display: none) elements (gh-2310)
++		// Support: IE <=11 only
++		// Running getBoundingClientRect on a
++		// disconnected node in IE throws an error
++		if ( !elem.getClientRects().length ) {
++			return { top: 0, left: 0 };
++		}
+ 
+-jQuery.fn.extend({
++		// Get document-relative position by adding viewport scroll to viewport-relative gBCR
++		rect = elem.getBoundingClientRect();
++		win = elem.ownerDocument.defaultView;
++		return {
++			top: rect.top + win.pageYOffset,
++			left: rect.left + win.pageXOffset
++		};
++	},
+ 
++	// position() relates an element's margin box to its offset parent's padding box
++	// This corresponds to the behavior of CSS absolute positioning
+ 	position: function() {
+ 		if ( !this[ 0 ] ) {
+ 			return;
+ 		}
+ 
+-		var offsetParent, offset,
+-			parentOffset = { top: 0, left: 0 },
+-			elem = this[ 0 ];
++		var offsetParent, offset, doc,
++			elem = this[ 0 ],
++			parentOffset = { top: 0, left: 0 };
+ 
+-		// fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is it's only offset parent
++		// position:fixed elements are offset from the viewport, which itself always has zero offset
+ 		if ( jQuery.css( elem, "position" ) === "fixed" ) {
+-			// we assume that getBoundingClientRect is available when computed position is fixed
++
++			// Assume position:fixed implies availability of getBoundingClientRect
+ 			offset = elem.getBoundingClientRect();
+-		} else {
+-			// Get *real* offsetParent
+-			offsetParent = this.offsetParent();
+ 
+-			// Get correct offsets
++		} else {
+ 			offset = this.offset();
+-			if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) {
+-				parentOffset = offsetParent.offset();
++
++			// Account for the *real* offset parent, which can be the document or its root element
++			// when a statically positioned element is identified
++			doc = elem.ownerDocument;
++			offsetParent = elem.offsetParent || doc.documentElement;
++			while ( offsetParent &&
++				( offsetParent === doc.body || offsetParent === doc.documentElement ) &&
++				jQuery.css( offsetParent, "position" ) === "static" ) {
++
++				offsetParent = offsetParent.parentNode;
+ 			}
++			if ( offsetParent && offsetParent !== elem && offsetParent.nodeType === 1 ) {
+ 
+-			// Add offsetParent borders
+-			parentOffset.top  += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true );
+-			parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true );
++				// Incorporate borders into its offset, since they are outside its content origin
++				parentOffset = jQuery( offsetParent ).offset();
++				parentOffset.top += jQuery.css( offsetParent, "borderTopWidth", true );
++				parentOffset.left += jQuery.css( offsetParent, "borderLeftWidth", true );
++			}
+ 		}
+ 
+ 		// Subtract parent offsets and element margins
+-		// note: when an element has margin: auto the offsetLeft and marginLeft
+-		// are the same in Safari causing offset.left to incorrectly be 0
+ 		return {
+-			top:  offset.top  - parentOffset.top - jQuery.css( elem, "marginTop", true ),
+-			left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true)
++			top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
++			left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true )
+ 		};
+ 	},
+ 
++	// This method will return documentElement in the following cases:
++	// 1) For the element inside the iframe without offsetParent, this method will return
++	//    documentElement of the parent window
++	// 2) For the hidden or detached element
++	// 3) For body or html element, i.e. in case of the html node - it will return itself
++	//
++	// but those exceptions were never presented as a real life use-cases
++	// and might be considered as more preferable results.
++	//
++	// This logic, however, is not guaranteed and can change at any point in the future
+ 	offsetParent: function() {
+-		return this.map(function() {
+-			var offsetParent = this.offsetParent || docElem;
+-			while ( offsetParent && ( !jQuery.nodeName( offsetParent, "html" ) && jQuery.css( offsetParent, "position") === "static" ) ) {
++		return this.map( function() {
++			var offsetParent = this.offsetParent;
++
++			while ( offsetParent && jQuery.css( offsetParent, "position" ) === "static" ) {
+ 				offsetParent = offsetParent.offsetParent;
+ 			}
+-			return offsetParent || docElem;
+-		});
+-	}
+-});
+ 
++			return offsetParent || documentElement;
++		} );
++	}
++} );
+ 
+ // Create scrollLeft and scrollTop methods
+-jQuery.each( {scrollLeft: "pageXOffset", scrollTop: "pageYOffset"}, function( method, prop ) {
+-	var top = /Y/.test( prop );
++jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) {
++	var top = "pageYOffset" === prop;
+ 
+ 	jQuery.fn[ method ] = function( val ) {
+-		return jQuery.access( this, function( elem, method, val ) {
+-			var win = getWindow( elem );
++		return access( this, function( elem, method, val ) {
++
++			// Coalesce documents and windows
++			var win;
++			if ( isWindow( elem ) ) {
++				win = elem;
++			} else if ( elem.nodeType === 9 ) {
++				win = elem.defaultView;
++			}
+ 
+ 			if ( val === undefined ) {
+-				return win ? (prop in win) ? win[ prop ] :
+-					win.document.documentElement[ method ] :
+-					elem[ method ];
++				return win ? win[ prop ] : elem[ method ];
+ 			}
+ 
+ 			if ( win ) {
+ 				win.scrollTo(
+-					!top ? val : jQuery( win ).scrollLeft(),
+-					top ? val : jQuery( win ).scrollTop()
++					!top ? val : win.pageXOffset,
++					top ? val : win.pageYOffset
+ 				);
+ 
+ 			} else {
+ 				elem[ method ] = val;
+ 			}
+-		}, method, val, arguments.length, null );
++		}, method, val, arguments.length );
+ 	};
+-});
++} );
++
++// Support: Safari <=7 - 9.1, Chrome <=37 - 49
++// Add the top/left cssHooks using jQuery.fn.position
++// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
++// Blink bug: https://bugs.chromium.org/p/chromium/issues/detail?id=589347
++// getComputedStyle returns percent when specified for top/left/bottom/right;
++// rather than make the css module depend on the offset module, just check for it here
++jQuery.each( [ "top", "left" ], function( i, prop ) {
++	jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition,
++		function( elem, computed ) {
++			if ( computed ) {
++				computed = curCSS( elem, prop );
++
++				// If curCSS returns percentage, fallback to offset
++				return rnumnonpx.test( computed ) ?
++					jQuery( elem ).position()[ prop ] + "px" :
++					computed;
++			}
++		}
++	);
++} );
++
+ 
+-function getWindow( elem ) {
+-	return jQuery.isWindow( elem ) ?
+-		elem :
+-		elem.nodeType === 9 ?
+-			elem.defaultView || elem.parentWindow :
+-			false;
+-}
+ // Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
+ jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
+-	jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) {
+-		// margin is only for outerHeight, outerWidth
++	jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name },
++		function( defaultExtra, funcName ) {
++
++		// Margin is only for outerHeight, outerWidth
+ 		jQuery.fn[ funcName ] = function( margin, value ) {
+ 			var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
+ 				extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
+ 
+-			return jQuery.access( this, function( elem, type, value ) {
++			return access( this, function( elem, type, value ) {
+ 				var doc;
+ 
+-				if ( jQuery.isWindow( elem ) ) {
+-					// As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there
+-					// isn't a whole lot we can do. See pull request at this URL for discussion:
+-					// https://github.com/jquery/jquery/pull/764
+-					return elem.document.documentElement[ "client" + name ];
++				if ( isWindow( elem ) ) {
++
++					// $( window ).outerWidth/Height return w/h including scrollbars (gh-1729)
++					return funcName.indexOf( "outer" ) === 0 ?
++						elem[ "inner" + name ] :
++						elem.document.documentElement[ "client" + name ];
+ 				}
+ 
+ 				// Get document width or height
+ 				if ( elem.nodeType === 9 ) {
+ 					doc = elem.documentElement;
+ 
+-					// Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], whichever is greatest
+-					// unfortunately, this causes bug #3838 in IE6/8 only, but there is currently no good, small way to fix it.
++					// Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height],
++					// whichever is greatest
+ 					return Math.max(
+ 						elem.body[ "scroll" + name ], doc[ "scroll" + name ],
+ 						elem.body[ "offset" + name ], doc[ "offset" + name ],
+@@ -9744,46 +10422,177 @@ jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
+ 				}
+ 
+ 				return value === undefined ?
++
+ 					// Get width or height on the element, requesting but not forcing parseFloat
+ 					jQuery.css( elem, type, extra ) :
+ 
+ 					// Set width or height on the element
+ 					jQuery.style( elem, type, value, extra );
+-			}, type, chainable ? margin : undefined, chainable, null );
++			}, type, chainable ? margin : undefined, chainable );
+ 		};
+-	});
+-});
+-// Limit scope pollution from any deprecated API
+-// (function() {
++	} );
++} );
++
++
++jQuery.each( ( "blur focus focusin focusout resize scroll click dblclick " +
++	"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
++	"change select submit keydown keypress keyup contextmenu" ).split( " " ),
++	function( i, name ) {
++
++	// Handle event binding
++	jQuery.fn[ name ] = function( data, fn ) {
++		return arguments.length > 0 ?
++			this.on( name, null, data, fn ) :
++			this.trigger( name );
++	};
++} );
+ 
+-// The number of elements contained in the matched element set
+-jQuery.fn.size = function() {
+-	return this.length;
++jQuery.fn.extend( {
++	hover: function( fnOver, fnOut ) {
++		return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
++	}
++} );
++
++
++
++
++jQuery.fn.extend( {
++
++	bind: function( types, data, fn ) {
++		return this.on( types, null, data, fn );
++	},
++	unbind: function( types, fn ) {
++		return this.off( types, null, fn );
++	},
++
++	delegate: function( selector, types, data, fn ) {
++		return this.on( types, selector, data, fn );
++	},
++	undelegate: function( selector, types, fn ) {
++
++		// ( namespace ) or ( selector, types [, fn] )
++		return arguments.length === 1 ?
++			this.off( selector, "**" ) :
++			this.off( types, selector || "**", fn );
++	}
++} );
++
++// Bind a function to a context, optionally partially applying any
++// arguments.
++// jQuery.proxy is deprecated to promote standards (specifically Function#bind)
++// However, it is not slated for removal any time soon
++jQuery.proxy = function( fn, context ) {
++	var tmp, args, proxy;
++
++	if ( typeof context === "string" ) {
++		tmp = fn[ context ];
++		context = fn;
++		fn = tmp;
++	}
++
++	// Quick check to determine if target is callable, in the spec
++	// this throws a TypeError, but we will just return undefined.
++	if ( !isFunction( fn ) ) {
++		return undefined;
++	}
++
++	// Simulated bind
++	args = slice.call( arguments, 2 );
++	proxy = function() {
++		return fn.apply( context || this, args.concat( slice.call( arguments ) ) );
++	};
++
++	// Set the guid of unique handler to the same of original handler, so it can be removed
++	proxy.guid = fn.guid = fn.guid || jQuery.guid++;
++
++	return proxy;
++};
++
++jQuery.holdReady = function( hold ) {
++	if ( hold ) {
++		jQuery.readyWait++;
++	} else {
++		jQuery.ready( true );
++	}
++};
++jQuery.isArray = Array.isArray;
++jQuery.parseJSON = JSON.parse;
++jQuery.nodeName = nodeName;
++jQuery.isFunction = isFunction;
++jQuery.isWindow = isWindow;
++jQuery.camelCase = camelCase;
++jQuery.type = toType;
++
++jQuery.now = Date.now;
++
++jQuery.isNumeric = function( obj ) {
++
++	// As of jQuery 3.0, isNumeric is limited to
++	// strings and numbers (primitives or objects)
++	// that can be coerced to finite numbers (gh-2662)
++	var type = jQuery.type( obj );
++	return ( type === "number" || type === "string" ) &&
++
++		// parseFloat NaNs numeric-cast false positives ("")
++		// ...but misinterprets leading-number strings, particularly hex literals ("0x...")
++		// subtraction forces infinities to NaN
++		!isNaN( obj - parseFloat( obj ) );
+ };
+ 
+-jQuery.fn.andSelf = jQuery.fn.addBack;
+ 
+-// })();
+-if ( typeof module === "object" && module && typeof module.exports === "object" ) {
+-	// Expose jQuery as module.exports in loaders that implement the Node
+-	// module pattern (including browserify). Do not create the global, since
+-	// the user will be storing it themselves locally, and globals are frowned
+-	// upon in the Node module world.
+-	module.exports = jQuery;
+-} else {
+-	// Otherwise expose jQuery to the global object as usual
+-	window.jQuery = window.$ = jQuery;
+ 
+-	// Register as a named AMD module, since jQuery can be concatenated with other
+-	// files that may use define, but not via a proper concatenation script that
+-	// understands anonymous AMD modules. A named AMD is safest and most robust
+-	// way to register. Lowercase jquery is used because AMD module names are
+-	// derived from file names, and jQuery is normally delivered in a lowercase
+-	// file name. Do this after creating the global so that if an AMD module wants
+-	// to call noConflict to hide this version of jQuery, it will work.
+-	if ( typeof define === "function" && define.amd ) {
+-		define( "jquery", [], function () { return jQuery; } );
++
++// Register as a named AMD module, since jQuery can be concatenated with other
++// files that may use define, but not via a proper concatenation script that
++// understands anonymous AMD modules. A named AMD is safest and most robust
++// way to register. Lowercase jquery is used because AMD module names are
++// derived from file names, and jQuery is normally delivered in a lowercase
++// file name. Do this after creating the global so that if an AMD module wants
++// to call noConflict to hide this version of jQuery, it will work.
++
++// Note that for maximum portability, libraries that are not jQuery should
++// declare themselves as anonymous modules, and avoid setting a global if an
++// AMD loader is present. jQuery is a special case. For more information, see
++// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon
++
++if ( typeof define === "function" && define.amd ) {
++	define( "jquery", [], function() {
++		return jQuery;
++	} );
++}
++
++
++
++
++var
++
++	// Map over jQuery in case of overwrite
++	_jQuery = window.jQuery,
++
++	// Map over the $ in case of overwrite
++	_$ = window.$;
++
++jQuery.noConflict = function( deep ) {
++	if ( window.$ === jQuery ) {
++		window.$ = _$;
++	}
++
++	if ( deep && window.jQuery === jQuery ) {
++		window.jQuery = _jQuery;
+ 	}
++
++	return jQuery;
++};
++
++// Expose jQuery and $ identifiers, even in AMD
++// (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
++// and CommonJS for browser emulators (#13566)
++if ( !noGlobal ) {
++	window.jQuery = window.$ = jQuery;
+ }
+ 
+-})( window );
++
++
++
++return jQuery;
++} );
+-- 
+1.8.3.1
+
+
+From 93ec2d60deff4eca166d4262c096713688eec10e Mon Sep 17 00:00:00 2001
+From: Alexander Scheel <ascheel@redhat.com>
+Date: Mon, 16 Mar 2020 11:45:01 -0400
+Subject: [PATCH 09/17] Update to jquery-i18n-properties v1.2.7
+
+Signed-off-by: Alexander Scheel <ascheel@redhat.com>
+(cherry picked from commit e1999f5aabe5da5366b78a3cfed0eeff35f5a0cc)
+---
+ .../share/webapps/pki/js/jquery.i18n.properties.js | 902 ++++++++++-----------
+ 1 file changed, 447 insertions(+), 455 deletions(-)
+
+diff --git a/base/server/share/webapps/pki/js/jquery.i18n.properties.js b/base/server/share/webapps/pki/js/jquery.i18n.properties.js
+index 9383f59..2494923 100644
+--- a/base/server/share/webapps/pki/js/jquery.i18n.properties.js
++++ b/base/server/share/webapps/pki/js/jquery.i18n.properties.js
+@@ -4,470 +4,462 @@
+  * Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and
+  * MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses.
+  *
+- * @version     1.0.x
+- * @author      Nuno Fernandes
+- * @url         www.codingwithcoffee.com
++ * @version     1.2.7
++ * @url         https://github.com/jquery-i18n-properties/jquery-i18n-properties
+  * @inspiration Localisation assistance for jQuery (http://keith-wood.name/localisation.html)
+  *              by Keith Wood (kbwood{at}iinet.com.au) June 2007
+  *
+  *****************************************************************************/
+ 
+-(function($) {
+-$.i18n = {};
++(function ($) {
+ 
+-/** Map holding bundle keys (if mode: 'map') */
+-$.i18n.map = {};
++    $.i18n = {};
+ 
+-/**
+- * Load and parse message bundle files (.properties),
+- * making bundles keys available as javascript variables.
+- *
+- * i18n files are named <name>.js, or <name>_<language>.js or <name>_<language>_<country>.js
+- * Where:
+- *      The <language> argument is a valid ISO Language Code. These codes are the lower-case,
+- *      two-letter codes as defined by ISO-639. You can find a full list of these codes at a
+- *      number of sites, such as: http://www.loc.gov/standards/iso639-2/englangn.html
+- *      The <country> argument is a valid ISO Country Code. These codes are the upper-case,
+- *      two-letter codes as defined by ISO-3166. You can find a full list of these codes at a
+- *      number of sites, such as: http://www.iso.ch/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html
+- *
+- * Sample usage for a bundles/Messages.properties bundle:
+- * $.i18n.properties({
+- *      name:      'Messages',
+- *      language:  'en_US',
+- *      path:      'bundles'
+- * });
+- * @param  name			(string/string[], optional) names of file to load (eg, 'Messages' or ['Msg1','Msg2']). Defaults to "Messages"
+- * @param  language		(string, optional) language/country code (eg, 'en', 'en_US', 'pt_PT'). if not specified, language reported by the browser will be used instead.
+- * @param  path			(string, optional) path of directory that contains file to load
+- * @param  mode			(string, optional) whether bundles keys are available as JavaScript variables/functions or as a map (eg, 'vars' or 'map')
+- * @param  cache        (boolean, optional) whether bundles should be cached by the browser, or forcibly reloaded on each page load. Defaults to false (i.e. forcibly reloaded)
+- * @param  encoding 	(string, optional) the encoding to request for bundles. Property file resource bundles are specified to be in ISO-8859-1 format. Defaults to UTF-8 for backward compatibility.
+- * @param  callback     (function, optional) callback function to be called after script is terminated
+- */
+-$.i18n.properties = function(settings) {
+-	// set up settings
+-    var defaults = {
+-        name:           'Messages',
+-        language:       '',
+-        path:           '',
+-        mode:           'vars',
+-        cache:			false,
+-        encoding:       'UTF-8',
+-        callback:       null
++    /** Map holding bundle keys (if mode: 'map') */
++    $.i18n.map = {};
++
++    var debug = function (message) {
++        window.console && console.log('i18n::' + message);
+     };
+-    settings = $.extend(defaults, settings);
+-    if(settings.language === null || settings.language == '') {
+-	   settings.language = $.i18n.browserLang();
+-	}
+-	if(settings.language === null) {settings.language='';}
+-
+-	// load and parse bundle files
+-	var files = getFiles(settings.name);
+-	for(i=0; i<files.length; i++) {
+-		// 1. load base (eg, Messages.properties)
+-		loadAndParseFile(settings.path + files[i] + '.properties', settings);
+-        // 2. with language code (eg, Messages_pt.properties)
+-		if(settings.language.length >= 2) {
+-            loadAndParseFile(settings.path + files[i] + '_' + settings.language.substring(0, 2) +'.properties', settings);
+-		}
+-		// 3. with language code and country code (eg, Messages_pt_PT.properties)
+-        if(settings.language.length >= 5) {
+-            loadAndParseFile(settings.path + files[i] + '_' + settings.language.substring(0, 5) +'.properties', settings);
++
++    /**
++     * Load and parse message bundle files (.properties),
++     * making bundles keys available as javascript variables.
++     *
++     * i18n files are named <name>.js, or <name>_<language>.js or <name>_<language>_<country>.js
++     * Where:
++     *      The <language> argument is a valid ISO Language Code. These codes are the lower-case,
++     *      two-letter codes as defined by ISO-639. You can find a full list of these codes at a
++     *      number of sites, such as: http://www.loc.gov/standards/iso639-2/englangn.html
++     *      The <country> argument is a valid ISO Country Code. These codes are the upper-case,
++     *      two-letter codes as defined by ISO-3166. You can find a full list of these codes at a
++     *      number of sites, such as: http://www.iso.ch/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html
++     *
++     * Sample usage for a bundles/Messages.properties bundle:
++     * $.i18n.properties({
++     *      name:      'Messages',
++     *      language:  'en_US',
++     *      path:      'bundles'
++     * });
++     * @param  name      (string/string[], optional) names of file to load (eg, 'Messages' or ['Msg1','Msg2']). Defaults to "Messages"
++     * @param  language    (string, optional) language/country code (eg, 'en', 'en_US', 'pt_BR'). if not specified, language reported by the browser will be used instead.
++     * @param  path      (string, optional) path of directory that contains file to load
++     * @param  mode      (string, optional) whether bundles keys are available as JavaScript variables/functions or as a map (eg, 'vars' or 'map')
++     * @param  debug     (boolean, optional) whether debug statements are logged at the console
++     * @param  cache        (boolean, optional) whether bundles should be cached by the browser, or forcibly reloaded on each page load. Defaults to false (i.e. forcibly reloaded)
++     * @param  encoding  (string, optional) the encoding to request for bundles. Property file resource bundles are specified to be in ISO-8859-1 format. Defaults to UTF-8 for backward compatibility.
++     * @param  callback     (function, optional) callback function to be called after script is terminated
++     */
++    $.i18n.properties = function (settings) {
++
++        var defaults = {
++            name: 'Messages',
++            language: '',
++            path: '',
++            mode: 'vars',
++            cache: false,
++            debug: false,
++            encoding: 'UTF-8',
++            async: false,
++            callback: null
++        };
++        settings = $.extend(defaults, settings);
++
++        // Ensure a trailing slash
++        if (!settings.path.match(/\/$/)) settings.path += '/';
++
++        // Try to ensure that we have at a least a two letter language code
++        settings.language = this.normaliseLanguageCode(settings.language);
++
++        // Ensure an array
++        var files = (settings.name && settings.name.constructor === Array) ? settings.name : [settings.name];
++
++        // A locale is at least a language code which means at least two files per name. If
++        // we also have a country code, thats an extra file per name.
++        settings.totalFiles = (files.length * 2) + ((settings.language.length >= 5) ? files.length : 0);
++        if (settings.debug) {
++            debug('totalFiles: ' + settings.totalFiles);
+         }
+-	}
+-
+-	// call callback
+-	if(settings.callback){ settings.callback(); }
+-};
+-
+-
+-/**
+- * When configured with mode: 'map', allows access to bundle values by specifying its key.
+- * Eg, jQuery.i18n.prop('com.company.bundles.menu_add')
+- */
+-$.i18n.prop = function(key /* Add parameters as function arguments as necessary  */) {
+-	var value = $.i18n.map[key];
+-	if (value == null)
+-		return '[' + key + ']';
+-
+-//	if(arguments.length < 2) // No arguments.
+-//    //if(key == 'spv.lbl.modified') {alert(value);}
+-//		return value;
+-
+-//	if (!$.isArray(placeHolderValues)) {
+-//		// If placeHolderValues is not an array, make it into one.
+-//		placeHolderValues = [placeHolderValues];
+-//		for (var i=2; i<arguments.length; i++)
+-//			placeHolderValues.push(arguments[i]);
+-//	}
+-
+-	// Place holder replacement
+-	/**
+-	 * Tested with:
+-	 *   test.t1=asdf ''{0}''
+-	 *   test.t2=asdf '{0}' '{1}'{1}'zxcv
+-	 *   test.t3=This is \"a quote" 'a''{0}''s'd{fgh{ij'
+-	 *   test.t4="'''{'0}''" {0}{a}
+-	 *   test.t5="'''{0}'''" {1}
+-	 *   test.t6=a {1} b {0} c
+-	 *   test.t7=a 'quoted \\ s\ttringy' \t\t x
+-	 *
+-	 * Produces:
+-	 *   test.t1, p1 ==> asdf 'p1'
+-	 *   test.t2, p1 ==> asdf {0} {1}{1}zxcv
+-	 *   test.t3, p1 ==> This is "a quote" a'{0}'sd{fgh{ij
+-	 *   test.t4, p1 ==> "'{0}'" p1{a}
+-	 *   test.t5, p1 ==> "'{0}'" {1}
+-	 *   test.t6, p1 ==> a {1} b p1 c
+-	 *   test.t6, p1, p2 ==> a p2 b p1 c
+-	 *   test.t6, p1, p2, p3 ==> a p2 b p1 c
+-	 *   test.t7 ==> a quoted \ s	tringy 		 x
+-	 */
+-
+-	var i;
+-	if (typeof(value) == 'string') {
+-        // Handle escape characters. Done separately from the tokenizing loop below because escape characters are
+-		// active in quoted strings.
+-        i = 0;
+-        while ((i = value.indexOf('\\', i)) != -1) {
+-		   if (value[i+1] == 't')
+-			   value = value.substring(0, i) + '\t' + value.substring((i++) + 2); // tab
+-		   else if (value[i+1] == 'r')
+-			   value = value.substring(0, i) + '\r' + value.substring((i++) + 2); // return
+-		   else if (value[i+1] == 'n')
+-			   value = value.substring(0, i) + '\n' + value.substring((i++) + 2); // line feed
+-		   else if (value[i+1] == 'f')
+-			   value = value.substring(0, i) + '\f' + value.substring((i++) + 2); // form feed
+-		   else if (value[i+1] == '\\')
+-			   value = value.substring(0, i) + '\\' + value.substring((i++) + 2); // \
+-		   else
+-			   value = value.substring(0, i) + value.substring(i+1); // Quietly drop the character
++
++        settings.filesLoaded = 0;
++
++        files.forEach(function (file) {
++
++            var defaultFileName, shortFileName, longFileName, fileNames;
++            // 1. load base (eg, Messages.properties)
++            defaultFileName = settings.path + file + '.properties';
++            // 2. with language code (eg, Messages_pt.properties)
++            var shortCode = settings.language.substring(0, 2);
++            shortFileName = settings.path + file + '_' + shortCode + '.properties';
++            // 3. with language code and country code (eg, Messages_pt_BR.properties)
++            if (settings.language.length >= 5) {
++                var longCode = settings.language.substring(0, 5);
++                longFileName = settings.path + file + '_' + longCode + '.properties';
++                fileNames = [defaultFileName, shortFileName, longFileName];
++            } else {
++                fileNames = [defaultFileName, shortFileName];
++            }
++            loadAndParseFiles(fileNames, settings);
++        });
++
++        // call callback
++        if (settings.callback && !settings.async) {
++            settings.callback();
+         }
++    }; // properties
++
++    /**
++     * When configured with mode: 'map', allows access to bundle values by specifying its key.
++     * Eg, jQuery.i18n.prop('com.company.bundles.menu_add')
++     */
++    $.i18n.prop = function (key /* Add parameters as function arguments as necessary  */) {
++
++        var value = $.i18n.map[key];
++        if (value === null) {
++            return '[' + key + ']';
++        }
++
++        var phvList;
++        if (arguments.length == 2 && $.isArray(arguments[1])) {
++            // An array was passed as the only parameter, so assume it is the list of place holder values.
++            phvList = arguments[1];
++        }
++
++        // Place holder replacement
++        /**
++        * Tested with:
++        *   test.t1=asdf ''{0}''
++        *   test.t2=asdf '{0}' '{1}'{1}'zxcv
++        *   test.t3=This is \"a quote" 'a''{0}''s'd{fgh{ij'
++        *   test.t4="'''{'0}''" {0}{a}
++        *   test.t5="'''{0}'''" {1}
++        *   test.t6=a {1} b {0} c
++        *   test.t7=a 'quoted \\ s\ttringy' \t\t x
++        *
++        * Produces:
++        *   test.t1, p1 ==> asdf 'p1'
++        *   test.t2, p1 ==> asdf {0} {1}{1}zxcv
++        *   test.t3, p1 ==> This is "a quote" a'{0}'sd{fgh{ij
++        *   test.t4, p1 ==> "'{0}'" p1{a}
++        *   test.t5, p1 ==> "'{0}'" {1}
++        *   test.t6, p1 ==> a {1} b p1 c
++        *   test.t6, p1, p2 ==> a p2 b p1 c
++        *   test.t6, p1, p2, p3 ==> a p2 b p1 c
++        *   test.t7 ==> a quoted \ s	tringy 		 x
++        */
++
++        var i;
++        if (typeof(value) == 'string') {
++            // Handle escape characters. Done separately from the tokenizing loop below because escape characters are
++            // active in quoted strings.
++            i = 0;
++            while ((i = value.indexOf('\\', i)) != -1) {
++                if (value.charAt(i + 1) == 't') {
++                    value = value.substring(0, i) + '\t' + value.substring((i++) + 2); // tab
++                } else if (value.charAt(i + 1) == 'r') {
++                    value = value.substring(0, i) + '\r' + value.substring((i++) + 2); // return
++                } else if (value.charAt(i + 1) == 'n') {
++                    value = value.substring(0, i) + '\n' + value.substring((i++) + 2); // line feed
++                } else if (value.charAt(i + 1) == 'f') {
++                    value = value.substring(0, i) + '\f' + value.substring((i++) + 2); // form feed
++                } else if (value.charAt(i + 1) == '\\') {
++                    value = value.substring(0, i) + '\\' + value.substring((i++) + 2); // \
++                } else {
++                    value = value.substring(0, i) + value.substring(i + 1); // Quietly drop the character
++                }
++            }
++
++            // Lazily convert the string to a list of tokens.
++            var arr = [], j, index;
++            i = 0;
++            while (i < value.length) {
++                if (value.charAt(i) == '\'') {
++                    // Handle quotes
++                    if (i == value.length - 1) {
++                        value = value.substring(0, i); // Silently drop the trailing quote
++                    } else if (value.charAt(i + 1) == '\'') {
++                        value = value.substring(0, i) + value.substring(++i); // Escaped quote
++                    } else {
++                        // Quoted string
++                        j = i + 2;
++                        while ((j = value.indexOf('\'', j)) != -1) {
++                            if (j == value.length - 1 || value.charAt(j + 1) != '\'') {
++                                // Found start and end quotes. Remove them
++                                value = value.substring(0, i) + value.substring(i + 1, j) + value.substring(j + 1);
++                                i = j - 1;
++                                break;
++                            } else {
++                                // Found a double quote, reduce to a single quote.
++                                value = value.substring(0, j) + value.substring(++j);
++                            }
++                        }
++
++                        if (j == -1) {
++                            // There is no end quote. Drop the start quote
++                            value = value.substring(0, i) + value.substring(i + 1);
++                        }
++                    }
++                } else if (value.charAt(i) == '{') {
++                    // Beginning of an unquoted place holder.
++                    j = value.indexOf('}', i + 1);
++                    if (j == -1) {
++                        i++; // No end. Process the rest of the line. Java would throw an exception
++                    } else {
++                        // Add 1 to the index so that it aligns with the function arguments.
++                        index = parseInt(value.substring(i + 1, j));
++                        if (!isNaN(index) && index >= 0) {
++                            // Put the line thus far (if it isn't empty) into the array
++                            var s = value.substring(0, i);
++                            if (s !== "") {
++                                arr.push(s);
++                            }
++                            // Put the parameter reference into the array
++                            arr.push(index);
++                            // Start the processing over again starting from the rest of the line.
++                            i = 0;
++                            value = value.substring(j + 1);
++                        } else {
++                            i = j + 1; // Invalid parameter. Leave as is.
++                        }
++                    }
++                } else {
++                    i++;
++                }
++            } // while
++
++            // Put the remainder of the no-empty line into the array.
++            if (value !== "") {
++                arr.push(value);
++            }
++            value = arr;
++
++            // Make the array the value for the entry.
++            $.i18n.map[key] = arr;
++        }
++
++        if (value.length === 0) {
++            return "";
++        }
++        if (value.length == 1 && typeof(value[0]) == "string") {
++            return value[0];
++        }
++
++        var str = "";
++        for (i = 0, j = value.length; i < j; i++) {
++            if (typeof(value[i]) == "string") {
++                str += value[i];
++            } else if (phvList && value[i] < phvList.length) {
++                // Must be a number
++                str += phvList[value[i]];
++            } else if (!phvList && value[i] + 1 < arguments.length) {
++                str += arguments[value[i] + 1];
++            } else {
++                str += "{" + value[i] + "}";
++            }
++        }
++
++        return str;
++    };
++
++    function callbackIfComplete(settings) {
++
++        if (settings.debug) {
++            debug('callbackIfComplete()');
++            debug('totalFiles: ' + settings.totalFiles);
++            debug('filesLoaded: ' + settings.filesLoaded);
++        }
++
++        if (settings.async) {
++            if (settings.filesLoaded === settings.totalFiles) {
++                if (settings.callback) {
++                    settings.callback();
++                }
++            }
++        }
++    }
+ 
+-		// Lazily convert the string to a list of tokens.
+-		var arr = [], j, index;
+-		i = 0;
+-		while (i < value.length) {
+-			if (value[i] == '\'') {
+-				// Handle quotes
+-				if (i == value.length-1)
+-					value = value.substring(0, i); // Silently drop the trailing quote
+-				else if (value[i+1] == '\'')
+-					value = value.substring(0, i) + value.substring(++i); // Escaped quote
+-				else {
+-					// Quoted string
+-					j = i + 2;
+-					while ((j = value.indexOf('\'', j)) != -1) {
+-						if (j == value.length-1 || value[j+1] != '\'') {
+-							// Found start and end quotes. Remove them
+-							value = value.substring(0,i) + value.substring(i+1, j) + value.substring(j+1);
+-							i = j - 1;
+-							break;
+-						}
+-						else {
+-							// Found a double quote, reduce to a single quote.
+-							value = value.substring(0,j) + value.substring(++j);
+-						}
+-					}
+-
+-					if (j == -1) {
+-						// There is no end quote. Drop the start quote
+-						value = value.substring(0,i) + value.substring(i+1);
+-					}
+-				}
+-			}
+-			else if (value[i] == '{') {
+-				// Beginning of an unquoted place holder.
+-				j = value.indexOf('}', i+1);
+-				if (j == -1)
+-					i++; // No end. Process the rest of the line. Java would throw an exception
+-				else {
+-					// Add 1 to the index so that it aligns with the function arguments.
+-					index = parseInt(value.substring(i+1, j));
+-					if (!isNaN(index) && index >= 0) {
+-						// Put the line thus far (if it isn't empty) into the array
+-						var s = value.substring(0, i);
+-						if (s != "")
+-							arr.push(s);
+-						// Put the parameter reference into the array
+-						arr.push(index);
+-						// Start the processing over again starting from the rest of the line.
+-						i = 0;
+-						value = value.substring(j+1);
+-					}
+-					else
+-						i = j + 1; // Invalid parameter. Leave as is.
+-				}
+-			}
+-			else
+-				i++;
+-		}
+-
+-		// Put the remainder of the no-empty line into the array.
+-		if (value != "")
+-			arr.push(value);
+-		value = arr;
+-
+-		// Make the array the value for the entry.
+-		$.i18n.map[key] = arr;
+-	}
+-
+-	if (value.length == 0)
+-		return "";
+-	if (value.lengh == 1 && typeof(value[0]) == "string")
+-		return value[0];
+-
+-	var s = "";
+-	for (i=0; i<value.length; i++) {
+-		if (typeof(value[i]) == "string")
+-			s += value[i];
+-		// Must be a number
+-		else if (value[i] + 1 < arguments.length)
+-			s += arguments[value[i] + 1];
+-		else
+-			s += "{"+ value[i] +"}";
+-	}
+-
+-	return s;
+-};
+-
+-/** Language reported by browser, normalized code */
+-$.i18n.browserLang = function() {
+-	return normaliseLanguageCode(navigator.language /* Mozilla */ || navigator.userLanguage /* IE */);
+-}
+-
+-
+-/** Load and parse .properties files */
+-function loadAndParseFile(filename, settings) {
+-	$.ajax({
+-        url:        filename,
+-        async:      false,
+-        cache:		settings.cache,
+-        contentType:'text/plain;charset='+ settings.encoding,
+-        dataType:   'text',
+-        success:    function(data, status) {
+-					parseData(data, settings.mode);
+-					}
+-    });
+-}
+-
+-/** Parse .properties files */
+-function parseData(data, mode) {
+-   var parsed = '';
+-   var parameters = data.split( /\n/ );
+-   var regPlaceHolder = /(\{\d+\})/g;
+-   var regRepPlaceHolder = /\{(\d+)\}/g;
+-   var unicodeRE = /(\\u.{4})/ig;
+-   for(var i=0; i<parameters.length; i++ ) {
+-       parameters[i] = parameters[i].replace( /^\s\s*/, '' ).replace( /\s\s*$/, '' ); // trim
+-       if(parameters[i].length > 0 && parameters[i].match("^#")!="#") { // skip comments
+-           var pair = parameters[i].split('=');
+-           if(pair.length > 0) {
+-               /** Process key & value */
+-               var name = unescape(pair[0]).replace( /^\s\s*/, '' ).replace( /\s\s*$/, '' ); // trim
+-               var value = pair.length == 1 ? "" : pair[1];
+-               // process multi-line values
+-               while(value.match(/\\$/)=="\\") {
+-			value = value.substring(0, value.length - 1);
+-			value += parameters[++i].replace( /\s\s*$/, '' ); // right trim
+-               }
+-               // Put values with embedded '='s back together
+-               for(var s=2;s<pair.length;s++){ value +='=' + pair[s]; }
+-               value = value.replace( /^\s\s*/, '' ).replace( /\s\s*$/, '' ); // trim
+-
+-               /** Mode: bundle keys in a map */
+-               if(mode == 'map' || mode == 'both') {
+-                   // handle unicode chars possibly left out
+-                   var unicodeMatches = value.match(unicodeRE);
+-                   if(unicodeMatches) {
+-                     for(var u=0; u<unicodeMatches.length; u++) {
+-                        value = value.replace( unicodeMatches[u], unescapeUnicode(unicodeMatches[u]));
+-                     }
+-                   }
+-                   // add to map
+-                   $.i18n.map[name] = value;
+-               }
+-
+-               /** Mode: bundle keys as vars/functions */
+-               if(mode == 'vars' || mode == 'both') {
+-                   value = value.replace( /"/g, '\\"' ); // escape quotation mark (")
+-
+-                   // make sure namespaced key exists (eg, 'some.key')
+-                   checkKeyNamespace(name);
+-
+-                   // value with variable substitutions
+-                   if(regPlaceHolder.test(value)) {
+-                       var parts = value.split(regPlaceHolder);
+-                       // process function args
+-                       var first = true;
+-                       var fnArgs = '';
+-                       var usedArgs = [];
+-                       for(var p=0; p<parts.length; p++) {
+-                           if(regPlaceHolder.test(parts[p]) && (usedArgs.length == 0 || usedArgs.indexOf(parts[p]) == -1)) {
+-                               if(!first) {fnArgs += ',';}
+-                               fnArgs += parts[p].replace(regRepPlaceHolder, 'v$1');
+-                               usedArgs.push(parts[p]);
+-                               first = false;
+-                           }
+-                       }
+-                       parsed += name + '=function(' + fnArgs + '){';
+-                       // process function body
+-                       var fnExpr = '"' + value.replace(regRepPlaceHolder, '"+v$1+"') + '"';
+-                       parsed += 'return ' + fnExpr + ';' + '};';
+-
+-                   // simple value
+-                   }else{
+-                       parsed += name+'="'+value+'";';
+-                   }
+-               } // END: Mode: bundle keys as vars/functions
+-           } // END: if(pair.length > 0)
+-       } // END: skip comments
+-   }
+-   eval(parsed);
+-}
+-
+-/** Make sure namespace exists (for keys with dots in name) */
+-// TODO key parts that start with numbers quietly fail. i.e. month.short.1=Jan
+-function checkKeyNamespace(key) {
+-	var regDot = /\./;
+-	if(regDot.test(key)) {
+-		var fullname = '';
+-		var names = key.split( /\./ );
+-		for(var i=0; i<names.length; i++) {
+-			if(i>0) {fullname += '.';}
+-			fullname += names[i];
+-			if(eval('typeof '+fullname+' == "undefined"')) {
+-				eval(fullname + '={};');
+-			}
+-		}
+-	}
+-}
+-
+-/** Make sure filename is an array */
+-function getFiles(names) {
+-	return (names && names.constructor == Array) ? names : [names];
+-}
+-
+-/** Ensure language code is in the format aa_AA. */
+-function normaliseLanguageCode(lang) {
+-    lang = lang.toLowerCase();
+-    if(lang.length > 3) {
+-        lang = lang.substring(0, 3) + lang.substring(3).toUpperCase();
++    function loadAndParseFiles(fileNames, settings) {
++
++        if (settings.debug) debug('loadAndParseFiles');
++
++	    if (fileNames !== null && fileNames.length > 0) {
++		    loadAndParseFile(fileNames[0], settings, function () {
++			    fileNames.shift();
++			    loadAndParseFiles(fileNames,settings);
++		    });
++	    } else {
++            callbackIfComplete(settings);
++        }
++    }
++
++    /** Load and parse .properties files */
++    function loadAndParseFile(filename, settings, nextFile) {
++
++        if (settings.debug) {
++            debug('loadAndParseFile(\'' + filename +'\')');
++            debug('totalFiles: ' + settings.totalFiles);
++            debug('filesLoaded: ' + settings.filesLoaded);
++        }
++
++  	    if (filename !== null && typeof filename !== 'undefined') {
++            $.ajax({
++                url: filename,
++                async: settings.async,
++                cache: settings.cache,
++                dataType: 'text',
++                success: function (data, status) {
++
++                    if (settings.debug) {
++                        debug('Succeeded in downloading ' + filename + '.');
++                        debug(data);
++                    }
++
++                    parseData(data, settings);
++                    nextFile();
++                },
++                error: function (jqXHR, textStatus, errorThrown) {
++
++                    if (settings.debug) {
++                        debug('Failed to download or parse ' + filename + '. errorThrown: ' + errorThrown);
++                    }
++                    if (jqXHR.status === 404) {
++                        settings.totalFiles -= 1;
++                    }
++                    nextFile();
++                }
++            });
++        }
++    }
++
++    /** Parse .properties files */
++    function parseData(data, settings) {
++
++        var parsed = '';
++        var lines = data.split(/\n/);
++        var regPlaceHolder = /(\{\d+})/g;
++        var regRepPlaceHolder = /\{(\d+)}/g;
++        var unicodeRE = /(\\u.{4})/ig;
++        lines.forEach(function (line, i) {
++
++            line = line.trim();
++            if (line.length > 0 && line.match("^#") != "#") { // skip comments
++                var pair = line.split('=');
++                if (pair.length > 0) {
++                    /** Process key & value */
++                    var name = decodeURI(pair[0]).trim();
++                    var value = pair.length == 1 ? "" : pair[1];
++                    // process multi-line values
++                    while (value.match(/\\$/) === "\\") {
++                        value = value.substring(0, value.length - 1);
++                        value += lines[++i].trimRight();
++                    }
++                    // Put values with embedded '='s back together
++                    for (var s = 2; s < pair.length; s++) {
++                        value += '=' + pair[s];
++                    }
++                    value = value.trim();
++
++                    /** Mode: bundle keys in a map */
++                    if (settings.mode == 'map' || settings.mode == 'both') {
++                        // handle unicode chars possibly left out
++                        var unicodeMatches = value.match(unicodeRE);
++                        if (unicodeMatches) {
++                            unicodeMatches.forEach(function (match) {
++                                value = value.replace(match, unescapeUnicode(match));
++                            });
++                        }
++                        // add to map
++                        $.i18n.map[name] = value;
++                    }
++
++                    /** Mode: bundle keys as vars/functions */
++                    if (settings.mode == 'vars' || settings.mode == 'both') {
++                        value = value.replace(/"/g, '\\"'); // escape quotation mark (")
++
++                        // make sure namespaced key exists (eg, 'some.key')
++                        checkKeyNamespace(name);
++
++                        // value with variable substitutions
++                        if (regPlaceHolder.test(value)) {
++                            var parts = value.split(regPlaceHolder);
++                            // process function args
++                            var first = true;
++                            var fnArgs = '';
++                            var usedArgs = [];
++                            parts.forEach(function (part) {
++
++                                if (regPlaceHolder.test(part) && (usedArgs.length === 0 || usedArgs.indexOf(part) == -1)) {
++                                    if (!first) {
++                                        fnArgs += ',';
++                                    }
++                                    fnArgs += part.replace(regRepPlaceHolder, 'v$1');
++                                    usedArgs.push(part);
++                                    first = false;
++                                }
++                            });
++                            parsed += name + '=function(' + fnArgs + '){';
++                            // process function body
++                            var fnExpr = '"' + value.replace(regRepPlaceHolder, '"+v$1+"') + '"';
++                            parsed += 'return ' + fnExpr + ';' + '};';
++                            // simple value
++                        } else {
++                            parsed += name + '="' + value + '";';
++                        }
++                    } // END: Mode: bundle keys as vars/functions
++                } // END: if(pair.length > 0)
++            } // END: skip comments
++        });
++        eval(parsed);
++        settings.filesLoaded += 1;
++    }
++
++    /** Make sure namespace exists (for keys with dots in name) */
++    // TODO key parts that start with numbers quietly fail. i.e. month.short.1=Jan
++    function checkKeyNamespace(key) {
++
++        var regDot = /\./;
++        if (regDot.test(key)) {
++            var fullname = '';
++            var names = key.split(/\./);
++            names.forEach(function (name, i) {
++
++                if (i > 0) {
++                    fullname += '.';
++                }
++                fullname += name;
++                if (eval('typeof ' + fullname + ' == "undefined"')) {
++                    eval(fullname + '={};');
++                }
++            });
++        }
++    }
++
++    /** Ensure language code is in the format aa_AA. */
++    $.i18n.normaliseLanguageCode = function (lang) {
++
++        if (!lang || lang.length < 2) {
++            lang = (navigator.languages && navigator.languages.length > 0) ? navigator.languages[0]
++                                        : (navigator.language || navigator.userLanguage /* IE */ || 'en');
++        }
++
++        lang = lang.toLowerCase();
++        lang = lang.replace(/-/,"_"); // some browsers report language as en-US instead of en_US
++        if (lang.length > 3) {
++            lang = lang.substring(0, 3) + lang.substring(3).toUpperCase();
++        }
++        return lang;
++    };
++
++    /** Unescape unicode chars ('\u00e3') */
++    function unescapeUnicode(str) {
++
++        // unescape unicode codes
++        var codes = [];
++        var code = parseInt(str.substr(2), 16);
++        if (code >= 0 && code < Math.pow(2, 16)) {
++            codes.push(code);
++        }
++        // convert codes to text
++        return codes.reduce(function (acc, val) { return acc + String.fromCharCode(val); }, '');
+     }
+-    return lang;
+-}
+-
+-/** Unescape unicode chars ('\u00e3') */
+-function unescapeUnicode(str) {
+-  // unescape unicode codes
+-  var codes = [];
+-  var code = parseInt(str.substr(2), 16);
+-  if (code >= 0 && code < Math.pow(2, 16)) {
+-     codes.push(code);
+-  }
+-  // convert codes to text
+-  var unescaped = '';
+-  for (var i = 0; i < codes.length; ++i) {
+-    unescaped += String.fromCharCode(codes[i]);
+-  }
+-  return unescaped;
+-}
+-
+-/* Cross-Browser Split 1.0.1
+-(c) Steven Levithan <stevenlevithan.com>; MIT License
+-An ECMA-compliant, uniform cross-browser split method */
+-var cbSplit;
+-// avoid running twice, which would break `cbSplit._nativeSplit`'s reference to the native `split`
+-if (!cbSplit) {
+-  cbSplit = function(str, separator, limit) {
+-      // if `separator` is not a regex, use the native `split`
+-      if (Object.prototype.toString.call(separator) !== "[object RegExp]") {
+-        if(typeof cbSplit._nativeSplit == "undefined")
+-          return str.split(separator, limit);
+-        else
+-          return cbSplit._nativeSplit.call(str, separator, limit);
+-      }
+-
+-      var output = [],
+-          lastLastIndex = 0,
+-          flags = (separator.ignoreCase ? "i" : "") +
+-                  (separator.multiline  ? "m" : "") +
+-                  (separator.sticky     ? "y" : ""),
+-          separator = RegExp(separator.source, flags + "g"), // make `global` and avoid `lastIndex` issues by working with a copy
+-          separator2, match, lastIndex, lastLength;
+-
+-      str = str + ""; // type conversion
+-      if (!cbSplit._compliantExecNpcg) {
+-          separator2 = RegExp("^" + separator.source + "$(?!\\s)", flags); // doesn't need /g or /y, but they don't hurt
+-      }
+-
+-      /* behavior for `limit`: if it's...
+-      - `undefined`: no limit.
+-      - `NaN` or zero: return an empty array.
+-      - a positive number: use `Math.floor(limit)`.
+-      - a negative number: no limit.
+-      - other: type-convert, then use the above rules. */
+-      if (limit === undefined || +limit < 0) {
+-          limit = Infinity;
+-      } else {
+-          limit = Math.floor(+limit);
+-          if (!limit) {
+-              return [];
+-          }
+-      }
+-
+-      while (match = separator.exec(str)) {
+-          lastIndex = match.index + match[0].length; // `separator.lastIndex` is not reliable cross-browser
+-
+-          if (lastIndex > lastLastIndex) {
+-              output.push(str.slice(lastLastIndex, match.index));
+-
+-              // fix browsers whose `exec` methods don't consistently return `undefined` for nonparticipating capturing groups
+-              if (!cbSplit._compliantExecNpcg && match.length > 1) {
+-                  match[0].replace(separator2, function () {
+-                      for (var i = 1; i < arguments.length - 2; i++) {
+-                          if (arguments[i] === undefined) {
+-                              match[i] = undefined;
+-                          }
+-                      }
+-                  });
+-              }
+-
+-              if (match.length > 1 && match.index < str.length) {
+-                  Array.prototype.push.apply(output, match.slice(1));
+-              }
+-
+-              lastLength = match[0].length;
+-              lastLastIndex = lastIndex;
+-
+-              if (output.length >= limit) {
+-                  break;
+-              }
+-          }
+-
+-          if (separator.lastIndex === match.index) {
+-              separator.lastIndex++; // avoid an infinite loop
+-          }
+-      }
+-
+-      if (lastLastIndex === str.length) {
+-          if (lastLength || !separator.test("")) {
+-              output.push("");
+-          }
+-      } else {
+-          output.push(str.slice(lastLastIndex));
+-      }
+-
+-      return output.length > limit ? output.slice(0, limit) : output;
+-  };
+-
+-  cbSplit._compliantExecNpcg = /()??/.exec("")[1] === undefined; // NPCG: nonparticipating capturing group
+-  cbSplit._nativeSplit = String.prototype.split;
+-
+-} // end `if (!cbSplit)`
+-String.prototype.split = function (separator, limit) {
+-    return cbSplit(this, separator, limit);
+-};
+-
+-})(jQuery);
++}) (jQuery);
+-- 
+1.8.3.1
+
+
+From 8fdd9d90185a9f4ba3f2cb8264510c73dc9b535a Mon Sep 17 00:00:00 2001
+From: Alexander Scheel <ascheel@redhat.com>
+Date: Mon, 16 Mar 2020 11:46:27 -0400
+Subject: [PATCH 10/17] Update to backbone v1.4.0
+
+Signed-off-by: Alexander Scheel <ascheel@redhat.com>
+(cherry picked from commit 14849481d81eafa04627f6b4e1eaafbc4e054d55)
+---
+ base/server/share/webapps/pki/js/backbone.js | 1483 +++++++++++++++++---------
+ 1 file changed, 999 insertions(+), 484 deletions(-)
+
+diff --git a/base/server/share/webapps/pki/js/backbone.js b/base/server/share/webapps/pki/js/backbone.js
+index 3fec403..3e09d0d 100644
+--- a/base/server/share/webapps/pki/js/backbone.js
++++ b/base/server/share/webapps/pki/js/backbone.js
+@@ -1,49 +1,54 @@
+-//     Backbone.js 1.1.0
++//     Backbone.js 1.4.0
+ 
+-//     (c) 2010-2011 Jeremy Ashkenas, DocumentCloud Inc.
+-//     (c) 2011-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
++//     (c) 2010-2019 Jeremy Ashkenas and DocumentCloud
+ //     Backbone may be freely distributed under the MIT license.
+ //     For all details and documentation:
+ //     http://backbonejs.org
+ 
+-(function(){
++(function(factory) {
++
++  // Establish the root object, `window` (`self`) in the browser, or `global` on the server.
++  // We use `self` instead of `window` for `WebWorker` support.
++  var root = typeof self == 'object' && self.self === self && self ||
++            typeof global == 'object' && global.global === global && global;
++
++  // Set up Backbone appropriately for the environment. Start with AMD.
++  if (typeof define === 'function' && define.amd) {
++    define(['underscore', 'jquery', 'exports'], function(_, $, exports) {
++      // Export global even in AMD case in case this script is loaded with
++      // others that may still expect a global Backbone.
++      root.Backbone = factory(root, exports, _, $);
++    });
++
++  // Next for Node.js or CommonJS. jQuery may not be needed as a module.
++  } else if (typeof exports !== 'undefined') {
++    var _ = require('underscore'), $;
++    try { $ = require('jquery'); } catch (e) {}
++    factory(root, exports, _, $);
++
++  // Finally, as a browser global.
++  } else {
++    root.Backbone = factory(root, {}, root._, root.jQuery || root.Zepto || root.ender || root.$);
++  }
++
++})(function(root, Backbone, _, $) {
+ 
+   // Initial Setup
+   // -------------
+ 
+-  // Save a reference to the global object (`window` in the browser, `exports`
+-  // on the server).
+-  var root = this;
+-
+   // Save the previous value of the `Backbone` variable, so that it can be
+   // restored later on, if `noConflict` is used.
+   var previousBackbone = root.Backbone;
+ 
+-  // Create local references to array methods we'll want to use later.
+-  var array = [];
+-  var push = array.push;
+-  var slice = array.slice;
+-  var splice = array.splice;
+-
+-  // The top-level namespace. All public Backbone classes and modules will
+-  // be attached to this. Exported for both the browser and the server.
+-  var Backbone;
+-  if (typeof exports !== 'undefined') {
+-    Backbone = exports;
+-  } else {
+-    Backbone = root.Backbone = {};
+-  }
++  // Create a local reference to a common array method we'll want to use later.
++  var slice = Array.prototype.slice;
+ 
+   // Current version of the library. Keep in sync with `package.json`.
+-  Backbone.VERSION = '1.1.0';
+-
+-  // Require Underscore, if we're on the server, and it's not already present.
+-  var _ = root._;
+-  if (!_ && (typeof require !== 'undefined')) _ = require('underscore');
++  Backbone.VERSION = '1.4.0';
+ 
+   // For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns
+   // the `$` variable.
+-  Backbone.$ = root.jQuery || root.Zepto || root.ender || root.$;
++  Backbone.$ = $;
+ 
+   // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable
+   // to its previous owner. Returns a reference to this Backbone object.
+@@ -58,7 +63,7 @@
+   Backbone.emulateHTTP = false;
+ 
+   // Turn on `emulateJSON` to support legacy servers that can't deal with direct
+-  // `application/json` requests ... will encode the body as
++  // `application/json` requests ... this will encode the body as
+   // `application/x-www-form-urlencoded` instead and will send the model in a
+   // form param named `model`.
+   Backbone.emulateJSON = false;
+@@ -67,8 +72,8 @@
+   // ---------------
+ 
+   // A module that can be mixed in to *any object* in order to provide it with
+-  // custom events. You may bind with `on` or remove with `off` callback
+-  // functions to an event; `trigger`-ing an event fires all callbacks in
++  // a custom event channel. You may bind a callback to an event with `on` or
++  // remove with `off`; `trigger`-ing an event fires all callbacks in
+   // succession.
+   //
+   //     var object = {};
+@@ -76,123 +81,248 @@
+   //     object.on('expand', function(){ alert('expanded'); });
+   //     object.trigger('expand');
+   //
+-  var Events = Backbone.Events = {
+-
+-    // Bind an event to a `callback` function. Passing `"all"` will bind
+-    // the callback to all events fired.
+-    on: function(name, callback, context) {
+-      if (!eventsApi(this, 'on', name, [callback, context]) || !callback) return this;
+-      this._events || (this._events = {});
+-      var events = this._events[name] || (this._events[name] = []);
+-      events.push({callback: callback, context: context, ctx: context || this});
+-      return this;
+-    },
++  var Events = Backbone.Events = {};
+ 
+-    // Bind an event to only be triggered a single time. After the first time
+-    // the callback is invoked, it will be removed.
+-    once: function(name, callback, context) {
+-      if (!eventsApi(this, 'once', name, [callback, context]) || !callback) return this;
+-      var self = this;
+-      var once = _.once(function() {
+-        self.off(name, once);
+-        callback.apply(this, arguments);
+-      });
+-      once._callback = callback;
+-      return this.on(name, once, context);
+-    },
+-
+-    // Remove one or many callbacks. If `context` is null, removes all
+-    // callbacks with that function. If `callback` is null, removes all
+-    // callbacks for the event. If `name` is null, removes all bound
+-    // callbacks for all events.
+-    off: function(name, callback, context) {
+-      var retain, ev, events, names, i, l, j, k;
+-      if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this;
+-      if (!name && !callback && !context) {
+-        this._events = {};
+-        return this;
++  // Regular expression used to split event strings.
++  var eventSplitter = /\s+/;
++
++  // A private global variable to share between listeners and listenees.
++  var _listening;
++
++  // Iterates over the standard `event, callback` (as well as the fancy multiple
++  // space-separated events `"change blur", callback` and jQuery-style event
++  // maps `{event: callback}`).
++  var eventsApi = function(iteratee, events, name, callback, opts) {
++    var i = 0, names;
++    if (name && typeof name === 'object') {
++      // Handle event maps.
++      if (callback !== void 0 && 'context' in opts && opts.context === void 0) opts.context = callback;
++      for (names = _.keys(name); i < names.length ; i++) {
++        events = eventsApi(iteratee, events, names[i], name[names[i]], opts);
+       }
+-      names = name ? [name] : _.keys(this._events);
+-      for (i = 0, l = names.length; i < l; i++) {
+-        name = names[i];
+-        if (events = this._events[name]) {
+-          this._events[name] = retain = [];
+-          if (callback || context) {
+-            for (j = 0, k = events.length; j < k; j++) {
+-              ev = events[j];
+-              if ((callback && callback !== ev.callback && callback !== ev.callback._callback) ||
+-                  (context && context !== ev.context)) {
+-                retain.push(ev);
+-              }
+-            }
+-          }
+-          if (!retain.length) delete this._events[name];
+-        }
++    } else if (name && eventSplitter.test(name)) {
++      // Handle space-separated event names by delegating them individually.
++      for (names = name.split(eventSplitter); i < names.length; i++) {
++        events = iteratee(events, names[i], callback, opts);
+       }
++    } else {
++      // Finally, standard events.
++      events = iteratee(events, name, callback, opts);
++    }
++    return events;
++  };
+ 
+-      return this;
+-    },
++  // Bind an event to a `callback` function. Passing `"all"` will bind
++  // the callback to all events fired.
++  Events.on = function(name, callback, context) {
++    this._events = eventsApi(onApi, this._events || {}, name, callback, {
++      context: context,
++      ctx: this,
++      listening: _listening
++    });
+ 
+-    // Trigger one or many events, firing all bound callbacks. Callbacks are
+-    // passed the same arguments as `trigger` is, apart from the event name
+-    // (unless you're listening on `"all"`, which will cause your callback to
+-    // receive the true name of the event as the first argument).
+-    trigger: function(name) {
+-      if (!this._events) return this;
+-      var args = slice.call(arguments, 1);
+-      if (!eventsApi(this, 'trigger', name, args)) return this;
+-      var events = this._events[name];
+-      var allEvents = this._events.all;
+-      if (events) triggerEvents(events, args);
+-      if (allEvents) triggerEvents(allEvents, arguments);
+-      return this;
+-    },
++    if (_listening) {
++      var listeners = this._listeners || (this._listeners = {});
++      listeners[_listening.id] = _listening;
++      // Allow the listening to use a counter, instead of tracking
++      // callbacks for library interop
++      _listening.interop = false;
++    }
+ 
+-    // Tell this object to stop listening to either specific events ... or
+-    // to every object it's currently listening to.
+-    stopListening: function(obj, name, callback) {
+-      var listeningTo = this._listeningTo;
+-      if (!listeningTo) return this;
+-      var remove = !name && !callback;
+-      if (!callback && typeof name === 'object') callback = this;
+-      if (obj) (listeningTo = {})[obj._listenId] = obj;
+-      for (var id in listeningTo) {
+-        obj = listeningTo[id];
+-        obj.off(name, callback, this);
+-        if (remove || _.isEmpty(obj._events)) delete this._listeningTo[id];
+-      }
+-      return this;
++    return this;
++  };
++
++  // Inversion-of-control versions of `on`. Tell *this* object to listen to
++  // an event in another object... keeping track of what it's listening to
++  // for easier unbinding later.
++  Events.listenTo = function(obj, name, callback) {
++    if (!obj) return this;
++    var id = obj._listenId || (obj._listenId = _.uniqueId('l'));
++    var listeningTo = this._listeningTo || (this._listeningTo = {});
++    var listening = _listening = listeningTo[id];
++
++    // This object is not listening to any other events on `obj` yet.
++    // Setup the necessary references to track the listening callbacks.
++    if (!listening) {
++      this._listenId || (this._listenId = _.uniqueId('l'));
++      listening = _listening = listeningTo[id] = new Listening(this, obj);
++    }
++
++    // Bind callbacks on obj.
++    var error = tryCatchOn(obj, name, callback, this);
++    _listening = void 0;
++
++    if (error) throw error;
++    // If the target obj is not Backbone.Events, track events manually.
++    if (listening.interop) listening.on(name, callback);
++
++    return this;
++  };
++
++  // The reducing API that adds a callback to the `events` object.
++  var onApi = function(events, name, callback, options) {
++    if (callback) {
++      var handlers = events[name] || (events[name] = []);
++      var context = options.context, ctx = options.ctx, listening = options.listening;
++      if (listening) listening.count++;
++
++      handlers.push({callback: callback, context: context, ctx: context || ctx, listening: listening});
+     }
++    return events;
++  };
+ 
++  // An try-catch guarded #on function, to prevent poisoning the global
++  // `_listening` variable.
++  var tryCatchOn = function(obj, name, callback, context) {
++    try {
++      obj.on(name, callback, context);
++    } catch (e) {
++      return e;
++    }
+   };
+ 
+-  // Regular expression used to split event strings.
+-  var eventSplitter = /\s+/;
++  // Remove one or many callbacks. If `context` is null, removes all
++  // callbacks with that function. If `callback` is null, removes all
++  // callbacks for the event. If `name` is null, removes all bound
++  // callbacks for all events.
++  Events.off = function(name, callback, context) {
++    if (!this._events) return this;
++    this._events = eventsApi(offApi, this._events, name, callback, {
++      context: context,
++      listeners: this._listeners
++    });
++
++    return this;
++  };
++
++  // Tell this object to stop listening to either specific events ... or
++  // to every object it's currently listening to.
++  Events.stopListening = function(obj, name, callback) {
++    var listeningTo = this._listeningTo;
++    if (!listeningTo) return this;
++
++    var ids = obj ? [obj._listenId] : _.keys(listeningTo);
++    for (var i = 0; i < ids.length; i++) {
++      var listening = listeningTo[ids[i]];
++
++      // If listening doesn't exist, this object is not currently
++      // listening to obj. Break out early.
++      if (!listening) break;
++
++      listening.obj.off(name, callback, this);
++      if (listening.interop) listening.off(name, callback);
++    }
++    if (_.isEmpty(listeningTo)) this._listeningTo = void 0;
++
++    return this;
++  };
++
++  // The reducing API that removes a callback from the `events` object.
++  var offApi = function(events, name, callback, options) {
++    if (!events) return;
+ 
+-  // Implement fancy features of the Events API such as multiple event
+-  // names `"change blur"` and jQuery-style event maps `{change: action}`
+-  // in terms of the existing API.
+-  var eventsApi = function(obj, action, name, rest) {
+-    if (!name) return true;
++    var context = options.context, listeners = options.listeners;
++    var i = 0, names;
+ 
+-    // Handle event maps.
+-    if (typeof name === 'object') {
+-      for (var key in name) {
+-        obj[action].apply(obj, [key, name[key]].concat(rest));
++    // Delete all event listeners and "drop" events.
++    if (!name && !context && !callback) {
++      for (names = _.keys(listeners); i < names.length; i++) {
++        listeners[names[i]].cleanup();
+       }
+-      return false;
++      return;
+     }
+ 
+-    // Handle space separated event names.
+-    if (eventSplitter.test(name)) {
+-      var names = name.split(eventSplitter);
+-      for (var i = 0, l = names.length; i < l; i++) {
+-        obj[action].apply(obj, [names[i]].concat(rest));
++    names = name ? [name] : _.keys(events);
++    for (; i < names.length; i++) {
++      name = names[i];
++      var handlers = events[name];
++
++      // Bail out if there are no events stored.
++      if (!handlers) break;
++
++      // Find any remaining events.
++      var remaining = [];
++      for (var j = 0; j < handlers.length; j++) {
++        var handler = handlers[j];
++        if (
++          callback && callback !== handler.callback &&
++            callback !== handler.callback._callback ||
++              context && context !== handler.context
++        ) {
++          remaining.push(handler);
++        } else {
++          var listening = handler.listening;
++          if (listening) listening.off(name, callback);
++        }
++      }
++
++      // Replace events if there are any remaining.  Otherwise, clean up.
++      if (remaining.length) {
++        events[name] = remaining;
++      } else {
++        delete events[name];
+       }
+-      return false;
+     }
+ 
+-    return true;
++    return events;
++  };
++
++  // Bind an event to only be triggered a single time. After the first time
++  // the callback is invoked, its listener will be removed. If multiple events
++  // are passed in using the space-separated syntax, the handler will fire
++  // once for each event, not once for a combination of all events.
++  Events.once = function(name, callback, context) {
++    // Map the event into a `{event: once}` object.
++    var events = eventsApi(onceMap, {}, name, callback, this.off.bind(this));
++    if (typeof name === 'string' && context == null) callback = void 0;
++    return this.on(events, callback, context);
++  };
++
++  // Inversion-of-control versions of `once`.
++  Events.listenToOnce = function(obj, name, callback) {
++    // Map the event into a `{event: once}` object.
++    var events = eventsApi(onceMap, {}, name, callback, this.stopListening.bind(this, obj));
++    return this.listenTo(obj, events);
++  };
++
++  // Reduces the event callbacks into a map of `{event: onceWrapper}`.
++  // `offer` unbinds the `onceWrapper` after it has been called.
++  var onceMap = function(map, name, callback, offer) {
++    if (callback) {
++      var once = map[name] = _.once(function() {
++        offer(name, once);
++        callback.apply(this, arguments);
++      });
++      once._callback = callback;
++    }
++    return map;
++  };
++
++  // Trigger one or many events, firing all bound callbacks. Callbacks are
++  // passed the same arguments as `trigger` is, apart from the event name
++  // (unless you're listening on `"all"`, which will cause your callback to
++  // receive the true name of the event as the first argument).
++  Events.trigger = function(name) {
++    if (!this._events) return this;
++
++    var length = Math.max(0, arguments.length - 1);
++    var args = Array(length);
++    for (var i = 0; i < length; i++) args[i] = arguments[i + 1];
++
++    eventsApi(triggerApi, this._events, name, void 0, args);
++    return this;
++  };
++
++  // Handles triggering the appropriate event callbacks.
++  var triggerApi = function(objEvents, name, callback, args) {
++    if (objEvents) {
++      var events = objEvents[name];
++      var allEvents = objEvents.all;
++      if (events && allEvents) allEvents = allEvents.slice();
++      if (events) triggerEvents(events, args);
++      if (allEvents) triggerEvents(allEvents, [name].concat(args));
++    }
++    return objEvents;
+   };
+ 
+   // A difficult-to-believe, but optimized internal dispatch function for
+@@ -205,25 +335,47 @@
+       case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return;
+       case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return;
+       case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return;
+-      default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args);
++      default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return;
+     }
+   };
+ 
+-  var listenMethods = {listenTo: 'on', listenToOnce: 'once'};
+-
+-  // Inversion-of-control versions of `on` and `once`. Tell *this* object to
+-  // listen to an event in another object ... keeping track of what it's
+-  // listening to.
+-  _.each(listenMethods, function(implementation, method) {
+-    Events[method] = function(obj, name, callback) {
+-      var listeningTo = this._listeningTo || (this._listeningTo = {});
+-      var id = obj._listenId || (obj._listenId = _.uniqueId('l'));
+-      listeningTo[id] = obj;
+-      if (!callback && typeof name === 'object') callback = this;
+-      obj[implementation](name, callback, this);
+-      return this;
+-    };
+-  });
++  // A listening class that tracks and cleans up memory bindings
++  // when all callbacks have been offed.
++  var Listening = function(listener, obj) {
++    this.id = listener._listenId;
++    this.listener = listener;
++    this.obj = obj;
++    this.interop = true;
++    this.count = 0;
++    this._events = void 0;
++  };
++
++  Listening.prototype.on = Events.on;
++
++  // Offs a callback (or several).
++  // Uses an optimized counter if the listenee uses Backbone.Events.
++  // Otherwise, falls back to manual tracking to support events
++  // library interop.
++  Listening.prototype.off = function(name, callback) {
++    var cleanup;
++    if (this.interop) {
++      this._events = eventsApi(offApi, this._events, name, callback, {
++        context: void 0,
++        listeners: void 0
++      });
++      cleanup = !this._events;
++    } else {
++      this.count--;
++      cleanup = this.count === 0;
++    }
++    if (cleanup) this.cleanup();
++  };
++
++  // Cleans up memory bindings between the listener and the listenee.
++  Listening.prototype.cleanup = function() {
++    delete this.listener._listeningTo[this.obj._listenId];
++    if (!this.interop) delete this.obj._listeners[this.id];
++  };
+ 
+   // Aliases for backwards compatibility.
+   Events.bind   = Events.on;
+@@ -246,11 +398,13 @@
+   var Model = Backbone.Model = function(attributes, options) {
+     var attrs = attributes || {};
+     options || (options = {});
+-    this.cid = _.uniqueId('c');
++    this.preinitialize.apply(this, arguments);
++    this.cid = _.uniqueId(this.cidPrefix);
+     this.attributes = {};
+     if (options.collection) this.collection = options.collection;
+     if (options.parse) attrs = this.parse(attrs, options) || {};
+-    attrs = _.defaults({}, attrs, _.result(this, 'defaults'));
++    var defaults = _.result(this, 'defaults');
++    attrs = _.defaults(_.extend({}, defaults, attrs), defaults);
+     this.set(attrs, options);
+     this.changed = {};
+     this.initialize.apply(this, arguments);
+@@ -269,6 +423,14 @@
+     // CouchDB users may want to set this to `"_id"`.
+     idAttribute: 'id',
+ 
++    // The prefix is used to create the client id which is used to identify models locally.
++    // You may want to override this if you're experiencing name clashes with model ids.
++    cidPrefix: 'c',
++
++    // preinitialize is an empty function by default. You can override it with a function
++    // or object.  preinitialize will run before any instantiation logic is run in the Model.
++    preinitialize: function(){},
++
+     // Initialize is an empty function by default. Override it with your own
+     // initialization logic.
+     initialize: function(){},
+@@ -300,14 +462,19 @@
+       return this.get(attr) != null;
+     },
+ 
++    // Special-cased proxy to underscore's `_.matches` method.
++    matches: function(attrs) {
++      return !!_.iteratee(attrs, this)(this.attributes);
++    },
++
+     // Set a hash of model attributes on the object, firing `"change"`. This is
+     // the core primitive operation of a model, updating the data and notifying
+     // anyone who needs to know about the change in state. The heart of the beast.
+     set: function(key, val, options) {
+-      var attr, attrs, unset, changes, silent, changing, prev, current;
+       if (key == null) return this;
+ 
+       // Handle both `"key", value` and `{key: value}` -style arguments.
++      var attrs;
+       if (typeof key === 'object') {
+         attrs = key;
+         options = val;
+@@ -321,37 +488,40 @@
+       if (!this._validate(attrs, options)) return false;
+ 
+       // Extract attributes and options.
+-      unset           = options.unset;
+-      silent          = options.silent;
+-      changes         = [];
+-      changing        = this._changing;
+-      this._changing  = true;
++      var unset      = options.unset;
++      var silent     = options.silent;
++      var changes    = [];
++      var changing   = this._changing;
++      this._changing = true;
+ 
+       if (!changing) {
+         this._previousAttributes = _.clone(this.attributes);
+         this.changed = {};
+       }
+-      current = this.attributes, prev = this._previousAttributes;
+ 
+-      // Check for changes of `id`.
+-      if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];
++      var current = this.attributes;
++      var changed = this.changed;
++      var prev    = this._previousAttributes;
+ 
+       // For each `set` attribute, update or delete the current value.
+-      for (attr in attrs) {
++      for (var attr in attrs) {
+         val = attrs[attr];
+         if (!_.isEqual(current[attr], val)) changes.push(attr);
+         if (!_.isEqual(prev[attr], val)) {
+-          this.changed[attr] = val;
++          changed[attr] = val;
+         } else {
+-          delete this.changed[attr];
++          delete changed[attr];
+         }
+         unset ? delete current[attr] : current[attr] = val;
+       }
+ 
++      // Update the `id`.
++      if (this.idAttribute in attrs) this.id = this.get(this.idAttribute);
++
+       // Trigger all relevant attribute changes.
+       if (!silent) {
+-        if (changes.length) this._pending = true;
+-        for (var i = 0, l = changes.length; i < l; i++) {
++        if (changes.length) this._pending = options;
++        for (var i = 0; i < changes.length; i++) {
+           this.trigger('change:' + changes[i], this, current[changes[i]], options);
+         }
+       }
+@@ -361,6 +531,7 @@
+       if (changing) return this;
+       if (!silent) {
+         while (this._pending) {
++          options = this._pending;
+           this._pending = false;
+           this.trigger('change', this, options);
+         }
+@@ -398,13 +569,16 @@
+     // determining if there *would be* a change.
+     changedAttributes: function(diff) {
+       if (!diff) return this.hasChanged() ? _.clone(this.changed) : false;
+-      var val, changed = false;
+       var old = this._changing ? this._previousAttributes : this.attributes;
++      var changed = {};
++      var hasChanged;
+       for (var attr in diff) {
+-        if (_.isEqual(old[attr], (val = diff[attr]))) continue;
+-        (changed || (changed = {}))[attr] = val;
++        var val = diff[attr];
++        if (_.isEqual(old[attr], val)) continue;
++        changed[attr] = val;
++        hasChanged = true;
+       }
+-      return changed;
++      return hasChanged ? changed : false;
+     },
+ 
+     // Get the previous value of an attribute, recorded at the time the last
+@@ -420,17 +594,16 @@
+       return _.clone(this._previousAttributes);
+     },
+ 
+-    // Fetch the model from the server. If the server's representation of the
+-    // model differs from its current attributes, they will be overridden,
+-    // triggering a `"change"` event.
++    // Fetch the model from the server, merging the response with the model's
++    // local attributes. Any changed attributes will trigger a "change" event.
+     fetch: function(options) {
+-      options = options ? _.clone(options) : {};
+-      if (options.parse === void 0) options.parse = true;
++      options = _.extend({parse: true}, options);
+       var model = this;
+       var success = options.success;
+       options.success = function(resp) {
+-        if (!model.set(model.parse(resp, options), options)) return false;
+-        if (success) success(model, resp, options);
++        var serverAttrs = options.parse ? model.parse(resp, options) : resp;
++        if (!model.set(serverAttrs, options)) return false;
++        if (success) success.call(options.context, model, resp, options);
+         model.trigger('sync', model, resp, options);
+       };
+       wrapError(this, options);
+@@ -441,9 +614,8 @@
+     // If the server returns an attributes hash that differs, the model's
+     // state will be `set` again.
+     save: function(key, val, options) {
+-      var attrs, method, xhr, attributes = this.attributes;
+-
+       // Handle both `"key", value` and `{key: value}` -style arguments.
++      var attrs;
+       if (key == null || typeof key === 'object') {
+         attrs = key;
+         options = val;
+@@ -451,46 +623,43 @@
+         (attrs = {})[key] = val;
+       }
+ 
+-      options = _.extend({validate: true}, options);
++      options = _.extend({validate: true, parse: true}, options);
++      var wait = options.wait;
+ 
+       // If we're not waiting and attributes exist, save acts as
+       // `set(attr).save(null, opts)` with validation. Otherwise, check if
+       // the model will be valid when the attributes, if any, are set.
+-      if (attrs && !options.wait) {
++      if (attrs && !wait) {
+         if (!this.set(attrs, options)) return false;
+-      } else {
+-        if (!this._validate(attrs, options)) return false;
+-      }
+-
+-      // Set temporary attributes if `{wait: true}`.
+-      if (attrs && options.wait) {
+-        this.attributes = _.extend({}, attributes, attrs);
++      } else if (!this._validate(attrs, options)) {
++        return false;
+       }
+ 
+       // After a successful server-side save, the client is (optionally)
+       // updated with the server-side state.
+-      if (options.parse === void 0) options.parse = true;
+       var model = this;
+       var success = options.success;
++      var attributes = this.attributes;
+       options.success = function(resp) {
+         // Ensure attributes are restored during synchronous saves.
+         model.attributes = attributes;
+-        var serverAttrs = model.parse(resp, options);
+-        if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs);
+-        if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) {
+-          return false;
+-        }
+-        if (success) success(model, resp, options);
++        var serverAttrs = options.parse ? model.parse(resp, options) : resp;
++        if (wait) serverAttrs = _.extend({}, attrs, serverAttrs);
++        if (serverAttrs && !model.set(serverAttrs, options)) return false;
++        if (success) success.call(options.context, model, resp, options);
+         model.trigger('sync', model, resp, options);
+       };
+       wrapError(this, options);
+ 
+-      method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update');
+-      if (method === 'patch') options.attrs = attrs;
+-      xhr = this.sync(method, this, options);
++      // Set temporary attributes if `{wait: true}` to properly find new ids.
++      if (attrs && wait) this.attributes = _.extend({}, attributes, attrs);
++
++      var method = this.isNew() ? 'create' : options.patch ? 'patch' : 'update';
++      if (method === 'patch' && !options.attrs) options.attrs = attrs;
++      var xhr = this.sync(method, this, options);
+ 
+       // Restore attributes.
+-      if (attrs && options.wait) this.attributes = attributes;
++      this.attributes = attributes;
+ 
+       return xhr;
+     },
+@@ -502,25 +671,27 @@
+       options = options ? _.clone(options) : {};
+       var model = this;
+       var success = options.success;
++      var wait = options.wait;
+ 
+       var destroy = function() {
++        model.stopListening();
+         model.trigger('destroy', model, model.collection, options);
+       };
+ 
+       options.success = function(resp) {
+-        if (options.wait || model.isNew()) destroy();
+-        if (success) success(model, resp, options);
++        if (wait) destroy();
++        if (success) success.call(options.context, model, resp, options);
+         if (!model.isNew()) model.trigger('sync', model, resp, options);
+       };
+ 
++      var xhr = false;
+       if (this.isNew()) {
+-        options.success();
+-        return false;
++        _.defer(options.success);
++      } else {
++        wrapError(this, options);
++        xhr = this.sync('delete', this, options);
+       }
+-      wrapError(this, options);
+-
+-      var xhr = this.sync('delete', this, options);
+-      if (!options.wait) destroy();
++      if (!wait) destroy();
+       return xhr;
+     },
+ 
+@@ -528,9 +699,13 @@
+     // using Backbone's restful methods, override this to change the endpoint
+     // that will be called.
+     url: function() {
+-      var base = _.result(this, 'urlRoot') || _.result(this.collection, 'url') || urlError();
++      var base =
++        _.result(this, 'urlRoot') ||
++        _.result(this.collection, 'url') ||
++        urlError();
+       if (this.isNew()) return base;
+-      return base + (base.charAt(base.length - 1) === '/' ? '' : '/') + encodeURIComponent(this.id);
++      var id = this.get(this.idAttribute);
++      return base.replace(/[^\/]$/, '$&/') + encodeURIComponent(id);
+     },
+ 
+     // **parse** converts a response into the hash of attributes to be `set` on
+@@ -546,12 +721,12 @@
+ 
+     // A model is new if it has never been saved to the server, and lacks an id.
+     isNew: function() {
+-      return this.id == null;
++      return !this.has(this.idAttribute);
+     },
+ 
+     // Check if the model is currently in a valid state.
+     isValid: function(options) {
+-      return this._validate({}, _.extend(options || {}, { validate: true }));
++      return this._validate({}, _.extend({}, options, {validate: true}));
+     },
+ 
+     // Run validation against the next complete set of model attributes,
+@@ -567,23 +742,11 @@
+ 
+   });
+ 
+-  // Underscore methods that we want to implement on the Model.
+-  var modelMethods = ['keys', 'values', 'pairs', 'invert', 'pick', 'omit'];
+-
+-  // Mix in each Underscore method as a proxy to `Model#attributes`.
+-  _.each(modelMethods, function(method) {
+-    Model.prototype[method] = function() {
+-      var args = slice.call(arguments);
+-      args.unshift(this.attributes);
+-      return _[method].apply(_, args);
+-    };
+-  });
+-
+   // Backbone.Collection
+   // -------------------
+ 
+   // If models tend to represent a single row of data, a Backbone Collection is
+-  // more analagous to a table full of data ... or a small slice or page of that
++  // more analogous to a table full of data ... or a small slice or page of that
+   // table, or a collection of rows that belong together for a particular reason
+   // -- all of the messages in this particular folder, all of the documents
+   // belonging to this particular author, and so on. Collections maintain
+@@ -594,6 +757,7 @@
+   // its models in sort order, as they're added and removed.
+   var Collection = Backbone.Collection = function(models, options) {
+     options || (options = {});
++    this.preinitialize.apply(this, arguments);
+     if (options.model) this.model = options.model;
+     if (options.comparator !== void 0) this.comparator = options.comparator;
+     this._reset();
+@@ -605,6 +769,17 @@
+   var setOptions = {add: true, remove: true, merge: true};
+   var addOptions = {add: true, remove: false};
+ 
++  // Splices `insert` into `array` at index `at`.
++  var splice = function(array, insert, at) {
++    at = Math.min(Math.max(at, 0), array.length);
++    var tail = Array(array.length - at);
++    var length = insert.length;
++    var i;
++    for (i = 0; i < tail.length; i++) tail[i] = array[i + at];
++    for (i = 0; i < length; i++) array[i + at] = insert[i];
++    for (i = 0; i < tail.length; i++) array[i + length + at] = tail[i];
++  };
++
+   // Define the Collection's inheritable methods.
+   _.extend(Collection.prototype, Events, {
+ 
+@@ -612,6 +787,11 @@
+     // This should be overridden in most cases.
+     model: Model,
+ 
++
++    // preinitialize is an empty function by default. You can override it with a function
++    // or object.  preinitialize will run before any instantiation logic is run in the Collection.
++    preinitialize: function(){},
++
+     // Initialize is an empty function by default. Override it with your own
+     // initialization logic.
+     initialize: function(){},
+@@ -619,7 +799,7 @@
+     // The JSON representation of a Collection is an array of the
+     // models' attributes.
+     toJSON: function(options) {
+-      return this.map(function(model){ return model.toJSON(options); });
++      return this.map(function(model) { return model.toJSON(options); });
+     },
+ 
+     // Proxy `Backbone.sync` by default.
+@@ -627,32 +807,24 @@
+       return Backbone.sync.apply(this, arguments);
+     },
+ 
+-    // Add a model, or list of models to the set.
++    // Add a model, or list of models to the set. `models` may be Backbone
++    // Models or raw JavaScript objects to be converted to Models, or any
++    // combination of the two.
+     add: function(models, options) {
+       return this.set(models, _.extend({merge: false}, options, addOptions));
+     },
+ 
+     // Remove a model, or a list of models from the set.
+     remove: function(models, options) {
++      options = _.extend({}, options);
+       var singular = !_.isArray(models);
+-      models = singular ? [models] : _.clone(models);
+-      options || (options = {});
+-      var i, l, index, model;
+-      for (i = 0, l = models.length; i < l; i++) {
+-        model = models[i] = this.get(models[i]);
+-        if (!model) continue;
+-        delete this._byId[model.id];
+-        delete this._byId[model.cid];
+-        index = this.indexOf(model);
+-        this.models.splice(index, 1);
+-        this.length--;
+-        if (!options.silent) {
+-          options.index = index;
+-          model.trigger('remove', model, this, options);
+-        }
+-        this._removeReference(model);
++      models = singular ? [models] : models.slice();
++      var removed = this._removeModels(models, options);
++      if (!options.silent && removed.length) {
++        options.changes = {added: [], merged: [], removed: removed};
++        this.trigger('update', this, options);
+       }
+-      return singular ? models[0] : models;
++      return singular ? removed[0] : removed;
+     },
+ 
+     // Update a collection by `set`-ing a new list of models, adding new ones,
+@@ -660,90 +832,114 @@
+     // already exist in the collection, as necessary. Similar to **Model#set**,
+     // the core operation for updating the data contained by the collection.
+     set: function(models, options) {
+-      options = _.defaults({}, options, setOptions);
+-      if (options.parse) models = this.parse(models, options);
++      if (models == null) return;
++
++      options = _.extend({}, setOptions, options);
++      if (options.parse && !this._isModel(models)) {
++        models = this.parse(models, options) || [];
++      }
++
+       var singular = !_.isArray(models);
+-      models = singular ? (models ? [models] : []) : _.clone(models);
+-      var i, l, id, model, attrs, existing, sort;
++      models = singular ? [models] : models.slice();
++
+       var at = options.at;
+-      var targetModel = this.model;
+-      var sortable = this.comparator && (at == null) && options.sort !== false;
++      if (at != null) at = +at;
++      if (at > this.length) at = this.length;
++      if (at < 0) at += this.length + 1;
++
++      var set = [];
++      var toAdd = [];
++      var toMerge = [];
++      var toRemove = [];
++      var modelMap = {};
++
++      var add = options.add;
++      var merge = options.merge;
++      var remove = options.remove;
++
++      var sort = false;
++      var sortable = this.comparator && at == null && options.sort !== false;
+       var sortAttr = _.isString(this.comparator) ? this.comparator : null;
+-      var toAdd = [], toRemove = [], modelMap = {};
+-      var add = options.add, merge = options.merge, remove = options.remove;
+-      var order = !sortable && add && remove ? [] : false;
+ 
+       // Turn bare objects into model references, and prevent invalid models
+       // from being added.
+-      for (i = 0, l = models.length; i < l; i++) {
+-        attrs = models[i];
+-        if (attrs instanceof Model) {
+-          id = model = attrs;
+-        } else {
+-          id = attrs[targetModel.prototype.idAttribute];
+-        }
++      var model, i;
++      for (i = 0; i < models.length; i++) {
++        model = models[i];
+ 
+         // If a duplicate is found, prevent it from being added and
+         // optionally merge it into the existing model.
+-        if (existing = this.get(id)) {
+-          if (remove) modelMap[existing.cid] = true;
+-          if (merge) {
+-            attrs = attrs === model ? model.attributes : attrs;
++        var existing = this.get(model);
++        if (existing) {
++          if (merge && model !== existing) {
++            var attrs = this._isModel(model) ? model.attributes : model;
+             if (options.parse) attrs = existing.parse(attrs, options);
+             existing.set(attrs, options);
+-            if (sortable && !sort && existing.hasChanged(sortAttr)) sort = true;
++            toMerge.push(existing);
++            if (sortable && !sort) sort = existing.hasChanged(sortAttr);
++          }
++          if (!modelMap[existing.cid]) {
++            modelMap[existing.cid] = true;
++            set.push(existing);
+           }
+           models[i] = existing;
+ 
+         // If this is a new, valid model, push it to the `toAdd` list.
+         } else if (add) {
+-          model = models[i] = this._prepareModel(attrs, options);
+-          if (!model) continue;
+-          toAdd.push(model);
+-
+-          // Listen to added models' events, and index models for lookup by
+-          // `id` and by `cid`.
+-          model.on('all', this._onModelEvent, this);
+-          this._byId[model.cid] = model;
+-          if (model.id != null) this._byId[model.id] = model;
++          model = models[i] = this._prepareModel(model, options);
++          if (model) {
++            toAdd.push(model);
++            this._addReference(model, options);
++            modelMap[model.cid] = true;
++            set.push(model);
++          }
+         }
+-        if (order) order.push(existing || model);
+       }
+ 
+-      // Remove nonexistent models if appropriate.
++      // Remove stale models.
+       if (remove) {
+-        for (i = 0, l = this.length; i < l; ++i) {
+-          if (!modelMap[(model = this.models[i]).cid]) toRemove.push(model);
++        for (i = 0; i < this.length; i++) {
++          model = this.models[i];
++          if (!modelMap[model.cid]) toRemove.push(model);
+         }
+-        if (toRemove.length) this.remove(toRemove, options);
++        if (toRemove.length) this._removeModels(toRemove, options);
+       }
+ 
+       // See if sorting is needed, update `length` and splice in new models.
+-      if (toAdd.length || (order && order.length)) {
++      var orderChanged = false;
++      var replace = !sortable && add && remove;
++      if (set.length && replace) {
++        orderChanged = this.length !== set.length || _.some(this.models, function(m, index) {
++          return m !== set[index];
++        });
++        this.models.length = 0;
++        splice(this.models, set, 0);
++        this.length = this.models.length;
++      } else if (toAdd.length) {
+         if (sortable) sort = true;
+-        this.length += toAdd.length;
+-        if (at != null) {
+-          for (i = 0, l = toAdd.length; i < l; i++) {
+-            this.models.splice(at + i, 0, toAdd[i]);
+-          }
+-        } else {
+-          if (order) this.models.length = 0;
+-          var orderedModels = order || toAdd;
+-          for (i = 0, l = orderedModels.length; i < l; i++) {
+-            this.models.push(orderedModels[i]);
+-          }
+-        }
++        splice(this.models, toAdd, at == null ? this.length : at);
++        this.length = this.models.length;
+       }
+ 
+       // Silently sort the collection if appropriate.
+       if (sort) this.sort({silent: true});
+ 
+-      // Unless silenced, it's time to fire all appropriate add/sort events.
++      // Unless silenced, it's time to fire all appropriate add/sort/update events.
+       if (!options.silent) {
+-        for (i = 0, l = toAdd.length; i < l; i++) {
+-          (model = toAdd[i]).trigger('add', model, this, options);
++        for (i = 0; i < toAdd.length; i++) {
++          if (at != null) options.index = at + i;
++          model = toAdd[i];
++          model.trigger('add', model, this, options);
++        }
++        if (sort || orderChanged) this.trigger('sort', this, options);
++        if (toAdd.length || toRemove.length || toMerge.length) {
++          options.changes = {
++            added: toAdd,
++            removed: toRemove,
++            merged: toMerge
++          };
++          this.trigger('update', this, options);
+         }
+-        if (sort || (order && order.length)) this.trigger('sort', this, options);
+       }
+ 
+       // Return the added (or merged) model (or models).
+@@ -755,9 +951,9 @@
+     // any granular `add` or `remove` events. Fires `reset` when finished.
+     // Useful for bulk operations and optimizations.
+     reset: function(models, options) {
+-      options || (options = {});
+-      for (var i = 0, l = this.models.length; i < l; i++) {
+-        this._removeReference(this.models[i]);
++      options = options ? _.clone(options) : {};
++      for (var i = 0; i < this.models.length; i++) {
++        this._removeReference(this.models[i], options);
+       }
+       options.previousModels = this.models;
+       this._reset();
+@@ -774,8 +970,7 @@
+     // Remove a model from the end of the collection.
+     pop: function(options) {
+       var model = this.at(this.length - 1);
+-      this.remove(model, options);
+-      return model;
++      return this.remove(model, options);
+     },
+ 
+     // Add a model to the beginning of the collection.
+@@ -786,8 +981,7 @@
+     // Remove a model from the beginning of the collection.
+     shift: function(options) {
+       var model = this.at(0);
+-      this.remove(model, options);
+-      return model;
++      return this.remove(model, options);
+     },
+ 
+     // Slice out a sub-array of models from the collection.
+@@ -795,27 +989,30 @@
+       return slice.apply(this.models, arguments);
+     },
+ 
+-    // Get a model from the set by id.
++    // Get a model from the set by id, cid, model object with id or cid
++    // properties, or an attributes object that is transformed through modelId.
+     get: function(obj) {
+       if (obj == null) return void 0;
+-      return this._byId[obj.id] || this._byId[obj.cid] || this._byId[obj];
++      return this._byId[obj] ||
++        this._byId[this.modelId(this._isModel(obj) ? obj.attributes : obj)] ||
++        obj.cid && this._byId[obj.cid];
++    },
++
++    // Returns `true` if the model is in the collection.
++    has: function(obj) {
++      return this.get(obj) != null;
+     },
+ 
+     // Get the model at the given index.
+     at: function(index) {
++      if (index < 0) index += this.length;
+       return this.models[index];
+     },
+ 
+     // Return models with matching attributes. Useful for simple cases of
+     // `filter`.
+     where: function(attrs, first) {
+-      if (_.isEmpty(attrs)) return first ? void 0 : [];
+-      return this[first ? 'find' : 'filter'](function(model) {
+-        for (var key in attrs) {
+-          if (attrs[key] !== model.get(key)) return false;
+-        }
+-        return true;
+-      });
++      return this[first ? 'find' : 'filter'](attrs);
+     },
+ 
+     // Return the first model with matching attributes. Useful for simple cases
+@@ -828,37 +1025,39 @@
+     // normal circumstances, as the set will maintain sort order as each item
+     // is added.
+     sort: function(options) {
+-      if (!this.comparator) throw new Error('Cannot sort a set without a comparator');
++      var comparator = this.comparator;
++      if (!comparator) throw new Error('Cannot sort a set without a comparator');
+       options || (options = {});
+ 
++      var length = comparator.length;
++      if (_.isFunction(comparator)) comparator = comparator.bind(this);
++
+       // Run sort based on type of `comparator`.
+-      if (_.isString(this.comparator) || this.comparator.length === 1) {
+-        this.models = this.sortBy(this.comparator, this);
++      if (length === 1 || _.isString(comparator)) {
++        this.models = this.sortBy(comparator);
+       } else {
+-        this.models.sort(_.bind(this.comparator, this));
++        this.models.sort(comparator);
+       }
+-
+       if (!options.silent) this.trigger('sort', this, options);
+       return this;
+     },
+ 
+     // Pluck an attribute from each model in the collection.
+     pluck: function(attr) {
+-      return _.invoke(this.models, 'get', attr);
++      return this.map(attr + '');
+     },
+ 
+     // Fetch the default set of models for this collection, resetting the
+     // collection when they arrive. If `reset: true` is passed, the response
+     // data will be passed through the `reset` method instead of `set`.
+     fetch: function(options) {
+-      options = options ? _.clone(options) : {};
+-      if (options.parse === void 0) options.parse = true;
++      options = _.extend({parse: true}, options);
+       var success = options.success;
+       var collection = this;
+       options.success = function(resp) {
+         var method = options.reset ? 'reset' : 'set';
+         collection[method](resp, options);
+-        if (success) success(collection, resp, options);
++        if (success) success.call(options.context, collection, resp, options);
+         collection.trigger('sync', collection, resp, options);
+       };
+       wrapError(this, options);
+@@ -870,13 +1069,15 @@
+     // wait for the server to agree.
+     create: function(model, options) {
+       options = options ? _.clone(options) : {};
+-      if (!(model = this._prepareModel(model, options))) return false;
+-      if (!options.wait) this.add(model, options);
++      var wait = options.wait;
++      model = this._prepareModel(model, options);
++      if (!model) return false;
++      if (!wait) this.add(model, options);
+       var collection = this;
+       var success = options.success;
+-      options.success = function(model, resp, options) {
+-        if (options.wait) collection.add(model, options);
+-        if (success) success(model, resp, options);
++      options.success = function(m, resp, callbackOpts) {
++        if (wait) collection.add(m, callbackOpts);
++        if (success) success.call(callbackOpts.context, m, resp, callbackOpts);
+       };
+       model.save(null, options);
+       return model;
+@@ -890,7 +1091,30 @@
+ 
+     // Create a new collection with an identical list of models as this one.
+     clone: function() {
+-      return new this.constructor(this.models);
++      return new this.constructor(this.models, {
++        model: this.model,
++        comparator: this.comparator
++      });
++    },
++
++    // Define how to uniquely identify models in the collection.
++    modelId: function(attrs) {
++      return attrs[this.model.prototype.idAttribute || 'id'];
++    },
++
++    // Get an iterator of all models in this collection.
++    values: function() {
++      return new CollectionIterator(this, ITERATOR_VALUES);
++    },
++
++    // Get an iterator of all model IDs in this collection.
++    keys: function() {
++      return new CollectionIterator(this, ITERATOR_KEYS);
++    },
++
++    // Get an iterator of all [ID, model] tuples in this collection.
++    entries: function() {
++      return new CollectionIterator(this, ITERATOR_KEYSVALUES);
+     },
+ 
+     // Private method to reset all internal state. Called when the collection
+@@ -904,7 +1128,7 @@
+     // Prepare a hash of attributes (or other model) to be added to this
+     // collection.
+     _prepareModel: function(attrs, options) {
+-      if (attrs instanceof Model) {
++      if (this._isModel(attrs)) {
+         if (!attrs.collection) attrs.collection = this;
+         return attrs;
+       }
+@@ -916,8 +1140,53 @@
+       return false;
+     },
+ 
++    // Internal method called by both remove and set.
++    _removeModels: function(models, options) {
++      var removed = [];
++      for (var i = 0; i < models.length; i++) {
++        var model = this.get(models[i]);
++        if (!model) continue;
++
++        var index = this.indexOf(model);
++        this.models.splice(index, 1);
++        this.length--;
++
++        // Remove references before triggering 'remove' event to prevent an
++        // infinite loop. #3693
++        delete this._byId[model.cid];
++        var id = this.modelId(model.attributes);
++        if (id != null) delete this._byId[id];
++
++        if (!options.silent) {
++          options.index = index;
++          model.trigger('remove', model, this, options);
++        }
++
++        removed.push(model);
++        this._removeReference(model, options);
++      }
++      return removed;
++    },
++
++    // Method for checking whether an object should be considered a model for
++    // the purposes of adding to the collection.
++    _isModel: function(model) {
++      return model instanceof Model;
++    },
++
++    // Internal method to create a model's ties to a collection.
++    _addReference: function(model, options) {
++      this._byId[model.cid] = model;
++      var id = this.modelId(model.attributes);
++      if (id != null) this._byId[id] = model;
++      model.on('all', this._onModelEvent, this);
++    },
++
+     // Internal method to sever a model's ties to a collection.
+-    _removeReference: function(model) {
++    _removeReference: function(model, options) {
++      delete this._byId[model.cid];
++      var id = this.modelId(model.attributes);
++      if (id != null) delete this._byId[id];
+       if (this === model.collection) delete model.collection;
+       model.off('all', this._onModelEvent, this);
+     },
+@@ -927,48 +1196,88 @@
+     // events simply proxy through. "add" and "remove" events that originate
+     // in other collections are ignored.
+     _onModelEvent: function(event, model, collection, options) {
+-      if ((event === 'add' || event === 'remove') && collection !== this) return;
+-      if (event === 'destroy') this.remove(model, options);
+-      if (model && event === 'change:' + model.idAttribute) {
+-        delete this._byId[model.previous(model.idAttribute)];
+-        if (model.id != null) this._byId[model.id] = model;
++      if (model) {
++        if ((event === 'add' || event === 'remove') && collection !== this) return;
++        if (event === 'destroy') this.remove(model, options);
++        if (event === 'change') {
++          var prevId = this.modelId(model.previousAttributes());
++          var id = this.modelId(model.attributes);
++          if (prevId !== id) {
++            if (prevId != null) delete this._byId[prevId];
++            if (id != null) this._byId[id] = model;
++          }
++        }
+       }
+       this.trigger.apply(this, arguments);
+     }
+ 
+   });
+ 
+-  // Underscore methods that we want to implement on the Collection.
+-  // 90% of the core usefulness of Backbone Collections is actually implemented
+-  // right here:
+-  var methods = ['forEach', 'each', 'map', 'collect', 'reduce', 'foldl',
+-    'inject', 'reduceRight', 'foldr', 'find', 'detect', 'filter', 'select',
+-    'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke',
+-    'max', 'min', 'toArray', 'size', 'first', 'head', 'take', 'initial', 'rest',
+-    'tail', 'drop', 'last', 'without', 'difference', 'indexOf', 'shuffle',
+-    'lastIndexOf', 'isEmpty', 'chain'];
++  // Defining an @@iterator method implements JavaScript's Iterable protocol.
++  // In modern ES2015 browsers, this value is found at Symbol.iterator.
++  /* global Symbol */
++  var $$iterator = typeof Symbol === 'function' && Symbol.iterator;
++  if ($$iterator) {
++    Collection.prototype[$$iterator] = Collection.prototype.values;
++  }
+ 
+-  // Mix in each Underscore method as a proxy to `Collection#models`.
+-  _.each(methods, function(method) {
+-    Collection.prototype[method] = function() {
+-      var args = slice.call(arguments);
+-      args.unshift(this.models);
+-      return _[method].apply(_, args);
+-    };
+-  });
++  // CollectionIterator
++  // ------------------
++
++  // A CollectionIterator implements JavaScript's Iterator protocol, allowing the
++  // use of `for of` loops in modern browsers and interoperation between
++  // Backbone.Collection and other JavaScript functions and third-party libraries
++  // which can operate on Iterables.
++  var CollectionIterator = function(collection, kind) {
++    this._collection = collection;
++    this._kind = kind;
++    this._index = 0;
++  };
+ 
+-  // Underscore methods that take a property name as an argument.
+-  var attributeMethods = ['groupBy', 'countBy', 'sortBy'];
++  // This "enum" defines the three possible kinds of values which can be emitted
++  // by a CollectionIterator that correspond to the values(), keys() and entries()
++  // methods on Collection, respectively.
++  var ITERATOR_VALUES = 1;
++  var ITERATOR_KEYS = 2;
++  var ITERATOR_KEYSVALUES = 3;
+ 
+-  // Use attributes instead of properties.
+-  _.each(attributeMethods, function(method) {
+-    Collection.prototype[method] = function(value, context) {
+-      var iterator = _.isFunction(value) ? value : function(model) {
+-        return model.get(value);
+-      };
+-      return _[method](this.models, iterator, context);
++  // All Iterators should themselves be Iterable.
++  if ($$iterator) {
++    CollectionIterator.prototype[$$iterator] = function() {
++      return this;
+     };
+-  });
++  }
++
++  CollectionIterator.prototype.next = function() {
++    if (this._collection) {
++
++      // Only continue iterating if the iterated collection is long enough.
++      if (this._index < this._collection.length) {
++        var model = this._collection.at(this._index);
++        this._index++;
++
++        // Construct a value depending on what kind of values should be iterated.
++        var value;
++        if (this._kind === ITERATOR_VALUES) {
++          value = model;
++        } else {
++          var id = this._collection.modelId(model.attributes);
++          if (this._kind === ITERATOR_KEYS) {
++            value = id;
++          } else { // ITERATOR_KEYSVALUES
++            value = [id, model];
++          }
++        }
++        return {value: value, done: false};
++      }
++
++      // Once exhausted, remove the reference to the collection so future
++      // calls to the next method always return done.
++      this._collection = void 0;
++    }
++
++    return {value: void 0, done: true};
++  };
+ 
+   // Backbone.View
+   // -------------
+@@ -985,17 +1294,16 @@
+   // if an existing element is not provided...
+   var View = Backbone.View = function(options) {
+     this.cid = _.uniqueId('view');
+-    options || (options = {});
++    this.preinitialize.apply(this, arguments);
+     _.extend(this, _.pick(options, viewOptions));
+     this._ensureElement();
+     this.initialize.apply(this, arguments);
+-    this.delegateEvents();
+   };
+ 
+   // Cached regex to split keys for `delegate`.
+   var delegateEventSplitter = /^(\S+)\s*(.*)$/;
+ 
+-  // List of view options to be merged as properties.
++  // List of view options to be set as properties.
+   var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];
+ 
+   // Set up all inheritable **Backbone.View** properties and methods.
+@@ -1010,6 +1318,10 @@
+       return this.$el.find(selector);
+     },
+ 
++    // preinitialize is an empty function by default. You can override it with a function
++    // or object.  preinitialize will run before any instantiation logic is run in the View
++    preinitialize: function(){},
++
+     // Initialize is an empty function by default. Override it with your own
+     // initialization logic.
+     initialize: function(){},
+@@ -1024,21 +1336,37 @@
+     // Remove this view by taking the element out of the DOM, and removing any
+     // applicable Backbone.Events listeners.
+     remove: function() {
+-      this.$el.remove();
++      this._removeElement();
+       this.stopListening();
+       return this;
+     },
+ 
+-    // Change the view's element (`this.el` property), including event
+-    // re-delegation.
+-    setElement: function(element, delegate) {
+-      if (this.$el) this.undelegateEvents();
+-      this.$el = element instanceof Backbone.$ ? element : Backbone.$(element);
+-      this.el = this.$el[0];
+-      if (delegate !== false) this.delegateEvents();
++    // Remove this view's element from the document and all event listeners
++    // attached to it. Exposed for subclasses using an alternative DOM
++    // manipulation API.
++    _removeElement: function() {
++      this.$el.remove();
++    },
++
++    // Change the view's element (`this.el` property) and re-delegate the
++    // view's events on the new element.
++    setElement: function(element) {
++      this.undelegateEvents();
++      this._setElement(element);
++      this.delegateEvents();
+       return this;
+     },
+ 
++    // Creates the `this.el` and `this.$el` references for this view using the
++    // given `el`. `el` can be a CSS selector or an HTML string, a jQuery
++    // context or an element. Subclasses can override this to utilize an
++    // alternative DOM manipulation API and are only required to set the
++    // `this.el` property.
++    _setElement: function(el) {
++      this.$el = el instanceof Backbone.$ ? el : Backbone.$(el);
++      this.el = this.$el[0];
++    },
++
+     // Set callbacks, where `this.events` is a hash of
+     //
+     // *{"event selector": "callback"}*
+@@ -1052,37 +1380,49 @@
+     // pairs. Callbacks will be bound to the view, with `this` set properly.
+     // Uses event delegation for efficiency.
+     // Omitting the selector binds the event to `this.el`.
+-    // This only works for delegate-able events: not `focus`, `blur`, and
+-    // not `change`, `submit`, and `reset` in Internet Explorer.
+     delegateEvents: function(events) {
+-      if (!(events || (events = _.result(this, 'events')))) return this;
++      events || (events = _.result(this, 'events'));
++      if (!events) return this;
+       this.undelegateEvents();
+       for (var key in events) {
+         var method = events[key];
+-        if (!_.isFunction(method)) method = this[events[key]];
++        if (!_.isFunction(method)) method = this[method];
+         if (!method) continue;
+-
+         var match = key.match(delegateEventSplitter);
+-        var eventName = match[1], selector = match[2];
+-        method = _.bind(method, this);
+-        eventName += '.delegateEvents' + this.cid;
+-        if (selector === '') {
+-          this.$el.on(eventName, method);
+-        } else {
+-          this.$el.on(eventName, selector, method);
+-        }
++        this.delegate(match[1], match[2], method.bind(this));
+       }
+       return this;
+     },
+ 
+-    // Clears all callbacks previously bound to the view with `delegateEvents`.
++    // Add a single event listener to the view's element (or a child element
++    // using `selector`). This only works for delegate-able events: not `focus`,
++    // `blur`, and not `change`, `submit`, and `reset` in Internet Explorer.
++    delegate: function(eventName, selector, listener) {
++      this.$el.on(eventName + '.delegateEvents' + this.cid, selector, listener);
++      return this;
++    },
++
++    // Clears all callbacks previously bound to the view by `delegateEvents`.
+     // You usually don't need to use this, but may wish to if you have multiple
+     // Backbone views attached to the same DOM element.
+     undelegateEvents: function() {
+-      this.$el.off('.delegateEvents' + this.cid);
++      if (this.$el) this.$el.off('.delegateEvents' + this.cid);
++      return this;
++    },
++
++    // A finer-grained `undelegateEvents` for removing a single delegated event.
++    // `selector` and `listener` are both optional.
++    undelegate: function(eventName, selector, listener) {
++      this.$el.off(eventName + '.delegateEvents' + this.cid, selector, listener);
+       return this;
+     },
+ 
++    // Produces a DOM element to be assigned to your view. Exposed for
++    // subclasses using an alternative DOM manipulation API.
++    _createElement: function(tagName) {
++      return document.createElement(tagName);
++    },
++
+     // Ensure that the View has a DOM element to render into.
+     // If `this.el` is a string, pass it through `$()`, take the first
+     // matching element, and re-assign it to `el`. Otherwise, create
+@@ -1092,13 +1432,107 @@
+         var attrs = _.extend({}, _.result(this, 'attributes'));
+         if (this.id) attrs.id = _.result(this, 'id');
+         if (this.className) attrs['class'] = _.result(this, 'className');
+-        var $el = Backbone.$('<' + _.result(this, 'tagName') + '>').attr(attrs);
+-        this.setElement($el, false);
++        this.setElement(this._createElement(_.result(this, 'tagName')));
++        this._setAttributes(attrs);
+       } else {
+-        this.setElement(_.result(this, 'el'), false);
++        this.setElement(_.result(this, 'el'));
+       }
++    },
++
++    // Set attributes from a hash on this view's element.  Exposed for
++    // subclasses using an alternative DOM manipulation API.
++    _setAttributes: function(attributes) {
++      this.$el.attr(attributes);
++    }
++
++  });
++
++  // Proxy Backbone class methods to Underscore functions, wrapping the model's
++  // `attributes` object or collection's `models` array behind the scenes.
++  //
++  // collection.filter(function(model) { return model.get('age') > 10 });
++  // collection.each(this.addView);
++  //
++  // `Function#apply` can be slow so we use the method's arg count, if we know it.
++  var addMethod = function(base, length, method, attribute) {
++    switch (length) {
++      case 1: return function() {
++        return base[method](this[attribute]);
++      };
++      case 2: return function(value) {
++        return base[method](this[attribute], value);
++      };
++      case 3: return function(iteratee, context) {
++        return base[method](this[attribute], cb(iteratee, this), context);
++      };
++      case 4: return function(iteratee, defaultVal, context) {
++        return base[method](this[attribute], cb(iteratee, this), defaultVal, context);
++      };
++      default: return function() {
++        var args = slice.call(arguments);
++        args.unshift(this[attribute]);
++        return base[method].apply(base, args);
++      };
+     }
++  };
++
++  var addUnderscoreMethods = function(Class, base, methods, attribute) {
++    _.each(methods, function(length, method) {
++      if (base[method]) Class.prototype[method] = addMethod(base, length, method, attribute);
++    });
++  };
++
++  // Support `collection.sortBy('attr')` and `collection.findWhere({id: 1})`.
++  var cb = function(iteratee, instance) {
++    if (_.isFunction(iteratee)) return iteratee;
++    if (_.isObject(iteratee) && !instance._isModel(iteratee)) return modelMatcher(iteratee);
++    if (_.isString(iteratee)) return function(model) { return model.get(iteratee); };
++    return iteratee;
++  };
++  var modelMatcher = function(attrs) {
++    var matcher = _.matches(attrs);
++    return function(model) {
++      return matcher(model.attributes);
++    };
++  };
++
++  // Underscore methods that we want to implement on the Collection.
++  // 90% of the core usefulness of Backbone Collections is actually implemented
++  // right here:
++  var collectionMethods = {forEach: 3, each: 3, map: 3, collect: 3, reduce: 0,
++    foldl: 0, inject: 0, reduceRight: 0, foldr: 0, find: 3, detect: 3, filter: 3,
++    select: 3, reject: 3, every: 3, all: 3, some: 3, any: 3, include: 3, includes: 3,
++    contains: 3, invoke: 0, max: 3, min: 3, toArray: 1, size: 1, first: 3,
++    head: 3, take: 3, initial: 3, rest: 3, tail: 3, drop: 3, last: 3,
++    without: 0, difference: 0, indexOf: 3, shuffle: 1, lastIndexOf: 3,
++    isEmpty: 1, chain: 1, sample: 3, partition: 3, groupBy: 3, countBy: 3,
++    sortBy: 3, indexBy: 3, findIndex: 3, findLastIndex: 3};
++
++
++  // Underscore methods that we want to implement on the Model, mapped to the
++  // number of arguments they take.
++  var modelMethods = {keys: 1, values: 1, pairs: 1, invert: 1, pick: 0,
++    omit: 0, chain: 1, isEmpty: 1};
+ 
++  // Mix in each Underscore method as a proxy to `Collection#models`.
++
++  _.each([
++    [Collection, collectionMethods, 'models'],
++    [Model, modelMethods, 'attributes']
++  ], function(config) {
++    var Base = config[0],
++        methods = config[1],
++        attribute = config[2];
++
++    Base.mixin = function(obj) {
++      var mappings = _.reduce(_.functions(obj), function(memo, name) {
++        memo[name] = 0;
++        return memo;
++      }, {});
++      addUnderscoreMethods(Base, obj, mappings, attribute);
++    };
++
++    addUnderscoreMethods(Base, _, methods, attribute);
+   });
+ 
+   // Backbone.sync
+@@ -1165,14 +1599,13 @@
+       params.processData = false;
+     }
+ 
+-    // If we're sending a `PATCH` request, and we're in an old Internet Explorer
+-    // that still has ActiveX enabled by default, override jQuery to use that
+-    // for XHR instead. Remove this line when jQuery supports `PATCH` on IE8.
+-    if (params.type === 'PATCH' && noXhrPatch) {
+-      params.xhr = function() {
+-        return new ActiveXObject("Microsoft.XMLHTTP");
+-      };
+-    }
++    // Pass along `textStatus` and `errorThrown` from jQuery.
++    var error = options.error;
++    options.error = function(xhr, textStatus, errorThrown) {
++      options.textStatus = textStatus;
++      options.errorThrown = errorThrown;
++      if (error) error.call(options.context, xhr, textStatus, errorThrown);
++    };
+ 
+     // Make the request, allowing the user to override any Ajax options.
+     var xhr = options.xhr = Backbone.ajax(_.extend(params, options));
+@@ -1180,15 +1613,13 @@
+     return xhr;
+   };
+ 
+-  var noXhrPatch = typeof window !== 'undefined' && !!window.ActiveXObject && !(window.XMLHttpRequest && (new XMLHttpRequest).dispatchEvent);
+-
+   // Map from CRUD to HTTP for our default `Backbone.sync` implementation.
+   var methodMap = {
+-    'create': 'POST',
+-    'update': 'PUT',
+-    'patch':  'PATCH',
+-    'delete': 'DELETE',
+-    'read':   'GET'
++    create: 'POST',
++    update: 'PUT',
++    patch: 'PATCH',
++    delete: 'DELETE',
++    read: 'GET'
+   };
+ 
+   // Set the default implementation of `Backbone.ajax` to proxy through to `$`.
+@@ -1204,6 +1635,7 @@
+   // matched. Creating a new one sets its `routes` hash, if not set statically.
+   var Router = Backbone.Router = function(options) {
+     options || (options = {});
++    this.preinitialize.apply(this, arguments);
+     if (options.routes) this.routes = options.routes;
+     this._bindRoutes();
+     this.initialize.apply(this, arguments);
+@@ -1219,6 +1651,10 @@
+   // Set up all inheritable **Backbone.Router** properties and methods.
+   _.extend(Router.prototype, Events, {
+ 
++    // preinitialize is an empty function by default. You can override it with a function
++    // or object.  preinitialize will run before any instantiation logic is run in the Router.
++    preinitialize: function(){},
++
+     // Initialize is an empty function by default. Override it with your own
+     // initialization logic.
+     initialize: function(){},
+@@ -1239,14 +1675,21 @@
+       var router = this;
+       Backbone.history.route(route, function(fragment) {
+         var args = router._extractParameters(route, fragment);
+-        callback && callback.apply(router, args);
+-        router.trigger.apply(router, ['route:' + name].concat(args));
+-        router.trigger('route', name, args);
+-        Backbone.history.trigger('route', router, name, args);
++        if (router.execute(callback, args, name) !== false) {
++          router.trigger.apply(router, ['route:' + name].concat(args));
++          router.trigger('route', name, args);
++          Backbone.history.trigger('route', router, name, args);
++        }
+       });
+       return this;
+     },
+ 
++    // Execute a route handler with the provided parameters.  This is an
++    // excellent place to do pre-route setup or post-route cleanup.
++    execute: function(callback, args, name) {
++      if (callback) callback.apply(this, args);
++    },
++
+     // Simple proxy to `Backbone.history` to save a fragment into the history.
+     navigate: function(fragment, options) {
+       Backbone.history.navigate(fragment, options);
+@@ -1269,12 +1712,12 @@
+     // against the current location hash.
+     _routeToRegExp: function(route) {
+       route = route.replace(escapeRegExp, '\\$&')
+-                   .replace(optionalParam, '(?:$1)?')
+-                   .replace(namedParam, function(match, optional) {
+-                     return optional ? match : '([^\/]+)';
+-                   })
+-                   .replace(splatParam, '(.*?)');
+-      return new RegExp('^' + route + '$');
++        .replace(optionalParam, '(?:$1)?')
++        .replace(namedParam, function(match, optional) {
++          return optional ? match : '([^/?]+)';
++        })
++        .replace(splatParam, '([^?]*?)');
++      return new RegExp('^' + route + '(?:\\?([\\s\\S]*))?$');
+     },
+ 
+     // Given a route, and a URL fragment that it matches, return the array of
+@@ -1282,7 +1725,9 @@
+     // treated as `null` to normalize cross-browser behavior.
+     _extractParameters: function(route, fragment) {
+       var params = route.exec(fragment).slice(1);
+-      return _.map(params, function(param) {
++      return _.map(params, function(param, i) {
++        // Don't decode the search params.
++        if (i === params.length - 1) return param || null;
+         return param ? decodeURIComponent(param) : null;
+       });
+     }
+@@ -1299,7 +1744,7 @@
+   // falls back to polling.
+   var History = Backbone.History = function() {
+     this.handlers = [];
+-    _.bindAll(this, 'checkUrl');
++    this.checkUrl = this.checkUrl.bind(this);
+ 
+     // Ensure that `History` can be used outside of the browser.
+     if (typeof window !== 'undefined') {
+@@ -1314,14 +1759,8 @@
+   // Cached regex for stripping leading and trailing slashes.
+   var rootStripper = /^\/+|\/+$/g;
+ 
+-  // Cached regex for detecting MSIE.
+-  var isExplorer = /msie [\w.]+/;
+-
+-  // Cached regex for removing a trailing slash.
+-  var trailingSlash = /\/$/;
+-
+-  // Cached regex for stripping urls of hash and query.
+-  var pathStripper = /[?#].*$/;
++  // Cached regex for stripping urls of hash.
++  var pathStripper = /#.*$/;
+ 
+   // Has the history handling already been started?
+   History.started = false;
+@@ -1333,6 +1772,33 @@
+     // twenty times a second.
+     interval: 50,
+ 
++    // Are we at the app root?
++    atRoot: function() {
++      var path = this.location.pathname.replace(/[^\/]$/, '$&/');
++      return path === this.root && !this.getSearch();
++    },
++
++    // Does the pathname match the root?
++    matchRoot: function() {
++      var path = this.decodeFragment(this.location.pathname);
++      var rootPath = path.slice(0, this.root.length - 1) + '/';
++      return rootPath === this.root;
++    },
++
++    // Unicode characters in `location.pathname` are percent encoded so they're
++    // decoded for comparison. `%25` should not be decoded since it may be part
++    // of an encoded parameter.
++    decodeFragment: function(fragment) {
++      return decodeURI(fragment.replace(/%25/g, '%2525'));
++    },
++
++    // In IE6, the hash fragment and search params are incorrect if the
++    // fragment contains `?`.
++    getSearch: function() {
++      var match = this.location.href.replace(/#.*/, '').match(/\?.+/);
++      return match ? match[0] : '';
++    },
++
+     // Gets the true hash value. Cannot use location.hash directly due to bug
+     // in Firefox where location.hash will always be decoded.
+     getHash: function(window) {
+@@ -1340,14 +1806,19 @@
+       return match ? match[1] : '';
+     },
+ 
+-    // Get the cross-browser normalized URL fragment, either from the URL,
+-    // the hash, or the override.
+-    getFragment: function(fragment, forcePushState) {
++    // Get the pathname and search params, without the root.
++    getPath: function() {
++      var path = this.decodeFragment(
++        this.location.pathname + this.getSearch()
++      ).slice(this.root.length - 1);
++      return path.charAt(0) === '/' ? path.slice(1) : path;
++    },
++
++    // Get the cross-browser normalized URL fragment from the path or hash.
++    getFragment: function(fragment) {
+       if (fragment == null) {
+-        if (this._hasPushState || !this._wantsHashChange || forcePushState) {
+-          fragment = this.location.pathname;
+-          var root = this.root.replace(trailingSlash, '');
+-          if (!fragment.indexOf(root)) fragment = fragment.slice(root.length);
++        if (this._usePushState || !this._wantsHashChange) {
++          fragment = this.getPath();
+         } else {
+           fragment = this.getHash();
+         }
+@@ -1358,7 +1829,7 @@
+     // Start the hash change handling, returning `true` if the current URL matches
+     // an existing route, and `false` otherwise.
+     start: function(options) {
+-      if (History.started) throw new Error("Backbone.history has already been started");
++      if (History.started) throw new Error('Backbone.history has already been started');
+       History.started = true;
+ 
+       // Figure out the initial configuration. Do we need an iframe?
+@@ -1366,65 +1837,93 @@
+       this.options          = _.extend({root: '/'}, this.options, options);
+       this.root             = this.options.root;
+       this._wantsHashChange = this.options.hashChange !== false;
++      this._hasHashChange   = 'onhashchange' in window && (document.documentMode === void 0 || document.documentMode > 7);
++      this._useHashChange   = this._wantsHashChange && this._hasHashChange;
+       this._wantsPushState  = !!this.options.pushState;
+-      this._hasPushState    = !!(this.options.pushState && this.history && this.history.pushState);
+-      var fragment          = this.getFragment();
+-      var docMode           = document.documentMode;
+-      var oldIE             = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7));
++      this._hasPushState    = !!(this.history && this.history.pushState);
++      this._usePushState    = this._wantsPushState && this._hasPushState;
++      this.fragment         = this.getFragment();
+ 
+       // Normalize root to always include a leading and trailing slash.
+       this.root = ('/' + this.root + '/').replace(rootStripper, '/');
+ 
+-      if (oldIE && this._wantsHashChange) {
+-        this.iframe = Backbone.$('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo('body')[0].contentWindow;
+-        this.navigate(fragment);
+-      }
+-
+-      // Depending on whether we're using pushState or hashes, and whether
+-      // 'onhashchange' is supported, determine how we check the URL state.
+-      if (this._hasPushState) {
+-        Backbone.$(window).on('popstate', this.checkUrl);
+-      } else if (this._wantsHashChange && ('onhashchange' in window) && !oldIE) {
+-        Backbone.$(window).on('hashchange', this.checkUrl);
+-      } else if (this._wantsHashChange) {
+-        this._checkUrlInterval = setInterval(this.checkUrl, this.interval);
+-      }
+-
+-      // Determine if we need to change the base url, for a pushState link
+-      // opened by a non-pushState browser.
+-      this.fragment = fragment;
+-      var loc = this.location;
+-      var atRoot = loc.pathname.replace(/[^\/]$/, '$&/') === this.root;
+-
+       // Transition from hashChange to pushState or vice versa if both are
+       // requested.
+       if (this._wantsHashChange && this._wantsPushState) {
+ 
+         // If we've started off with a route from a `pushState`-enabled
+         // browser, but we're currently in a browser that doesn't support it...
+-        if (!this._hasPushState && !atRoot) {
+-          this.fragment = this.getFragment(null, true);
+-          this.location.replace(this.root + this.location.search + '#' + this.fragment);
++        if (!this._hasPushState && !this.atRoot()) {
++          var rootPath = this.root.slice(0, -1) || '/';
++          this.location.replace(rootPath + '#' + this.getPath());
+           // Return immediately as browser will do redirect to new url
+           return true;
+ 
+         // Or if we've started out with a hash-based route, but we're currently
+         // in a browser where it could be `pushState`-based instead...
+-        } else if (this._hasPushState && atRoot && loc.hash) {
+-          this.fragment = this.getHash().replace(routeStripper, '');
+-          this.history.replaceState({}, document.title, this.root + this.fragment + loc.search);
++        } else if (this._hasPushState && this.atRoot()) {
++          this.navigate(this.getHash(), {replace: true});
+         }
+ 
+       }
+ 
++      // Proxy an iframe to handle location events if the browser doesn't
++      // support the `hashchange` event, HTML5 history, or the user wants
++      // `hashChange` but not `pushState`.
++      if (!this._hasHashChange && this._wantsHashChange && !this._usePushState) {
++        this.iframe = document.createElement('iframe');
++        this.iframe.src = 'javascript:0';
++        this.iframe.style.display = 'none';
++        this.iframe.tabIndex = -1;
++        var body = document.body;
++        // Using `appendChild` will throw on IE < 9 if the document is not ready.
++        var iWindow = body.insertBefore(this.iframe, body.firstChild).contentWindow;
++        iWindow.document.open();
++        iWindow.document.close();
++        iWindow.location.hash = '#' + this.fragment;
++      }
++
++      // Add a cross-platform `addEventListener` shim for older browsers.
++      var addEventListener = window.addEventListener || function(eventName, listener) {
++        return attachEvent('on' + eventName, listener);
++      };
++
++      // Depending on whether we're using pushState or hashes, and whether
++      // 'onhashchange' is supported, determine how we check the URL state.
++      if (this._usePushState) {
++        addEventListener('popstate', this.checkUrl, false);
++      } else if (this._useHashChange && !this.iframe) {
++        addEventListener('hashchange', this.checkUrl, false);
++      } else if (this._wantsHashChange) {
++        this._checkUrlInterval = setInterval(this.checkUrl, this.interval);
++      }
++
+       if (!this.options.silent) return this.loadUrl();
+     },
+ 
+     // Disable Backbone.history, perhaps temporarily. Not useful in a real app,
+     // but possibly useful for unit testing Routers.
+     stop: function() {
+-      Backbone.$(window).off('popstate', this.checkUrl).off('hashchange', this.checkUrl);
+-      clearInterval(this._checkUrlInterval);
++      // Add a cross-platform `removeEventListener` shim for older browsers.
++      var removeEventListener = window.removeEventListener || function(eventName, listener) {
++        return detachEvent('on' + eventName, listener);
++      };
++
++      // Remove window listeners.
++      if (this._usePushState) {
++        removeEventListener('popstate', this.checkUrl, false);
++      } else if (this._useHashChange && !this.iframe) {
++        removeEventListener('hashchange', this.checkUrl, false);
++      }
++
++      // Clean up the iframe if necessary.
++      if (this.iframe) {
++        document.body.removeChild(this.iframe);
++        this.iframe = null;
++      }
++
++      // Some environments will throw when clearing an undefined interval.
++      if (this._checkUrlInterval) clearInterval(this._checkUrlInterval);
+       History.started = false;
+     },
+ 
+@@ -1438,9 +1937,13 @@
+     // calls `loadUrl`, normalizing across the hidden iframe.
+     checkUrl: function(e) {
+       var current = this.getFragment();
++
++      // If the user pressed the back button, the iframe's hash will have
++      // changed and we should use that for comparison.
+       if (current === this.fragment && this.iframe) {
+-        current = this.getFragment(this.getHash(this.iframe));
++        current = this.getHash(this.iframe.contentWindow);
+       }
++
+       if (current === this.fragment) return false;
+       if (this.iframe) this.navigate(current);
+       this.loadUrl();
+@@ -1450,8 +1953,10 @@
+     // match, returns `true`. If no defined routes matches the fragment,
+     // returns `false`.
+     loadUrl: function(fragment) {
++      // If the root doesn't match, no routes can match either.
++      if (!this.matchRoot()) return false;
+       fragment = this.fragment = this.getFragment(fragment);
+-      return _.any(this.handlers, function(handler) {
++      return _.some(this.handlers, function(handler) {
+         if (handler.route.test(fragment)) {
+           handler.callback(fragment);
+           return true;
+@@ -1470,31 +1975,45 @@
+       if (!History.started) return false;
+       if (!options || options === true) options = {trigger: !!options};
+ 
+-      var url = this.root + (fragment = this.getFragment(fragment || ''));
++      // Normalize the fragment.
++      fragment = this.getFragment(fragment || '');
++
++      // Don't include a trailing slash on the root.
++      var rootPath = this.root;
++      if (fragment === '' || fragment.charAt(0) === '?') {
++        rootPath = rootPath.slice(0, -1) || '/';
++      }
++      var url = rootPath + fragment;
+ 
+       // Strip the fragment of the query and hash for matching.
+       fragment = fragment.replace(pathStripper, '');
+ 
+-      if (this.fragment === fragment) return;
+-      this.fragment = fragment;
++      // Decode for matching.
++      var decodedFragment = this.decodeFragment(fragment);
+ 
+-      // Don't include a trailing slash on the root.
+-      if (fragment === '' && url !== '/') url = url.slice(0, -1);
++      if (this.fragment === decodedFragment) return;
++      this.fragment = decodedFragment;
+ 
+       // If pushState is available, we use it to set the fragment as a real URL.
+-      if (this._hasPushState) {
++      if (this._usePushState) {
+         this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url);
+ 
+       // If hash changes haven't been explicitly disabled, update the hash
+       // fragment to store history.
+       } else if (this._wantsHashChange) {
+         this._updateHash(this.location, fragment, options.replace);
+-        if (this.iframe && (fragment !== this.getFragment(this.getHash(this.iframe)))) {
++        if (this.iframe && fragment !== this.getHash(this.iframe.contentWindow)) {
++          var iWindow = this.iframe.contentWindow;
++
+           // Opening and closing the iframe tricks IE7 and earlier to push a
+           // history entry on hash-tag change.  When replace is true, we don't
+           // want this.
+-          if(!options.replace) this.iframe.document.open().close();
+-          this._updateHash(this.iframe.location, fragment, options.replace);
++          if (!options.replace) {
++            iWindow.document.open();
++            iWindow.document.close();
++          }
++
++          this._updateHash(iWindow.location, fragment, options.replace);
+         }
+ 
+       // If you've told us that you explicitly don't want fallback hashchange-
+@@ -1525,7 +2044,7 @@
+   // Helpers
+   // -------
+ 
+-  // Helper function to correctly set up the prototype chain, for subclasses.
++  // Helper function to correctly set up the prototype chain for subclasses.
+   // Similar to `goog.inherits`, but uses a hash of prototype properties and
+   // class properties to be extended.
+   var extend = function(protoProps, staticProps) {
+@@ -1534,7 +2053,7 @@
+ 
+     // The constructor function for the new subclass is either defined by you
+     // (the "constructor" property in your `extend` definition), or defaulted
+-    // by us to simply call the parent's constructor.
++    // by us to simply call the parent constructor.
+     if (protoProps && _.has(protoProps, 'constructor')) {
+       child = protoProps.constructor;
+     } else {
+@@ -1545,14 +2064,9 @@
+     _.extend(child, parent, staticProps);
+ 
+     // Set the prototype chain to inherit from `parent`, without calling
+-    // `parent`'s constructor function.
+-    var Surrogate = function(){ this.constructor = child; };
+-    Surrogate.prototype = parent.prototype;
+-    child.prototype = new Surrogate;
+-
+-    // Add prototype properties (instance properties) to the subclass,
+-    // if supplied.
+-    if (protoProps) _.extend(child.prototype, protoProps);
++    // `parent`'s constructor function and add the prototype properties.
++    child.prototype = _.create(parent.prototype, protoProps);
++    child.prototype.constructor = child;
+ 
+     // Set a convenience property in case the parent's prototype is needed
+     // later.
+@@ -1573,9 +2087,10 @@
+   var wrapError = function(model, options) {
+     var error = options.error;
+     options.error = function(resp) {
+-      if (error) error(model, resp, options);
++      if (error) error.call(options.context, model, resp, options);
+       model.trigger('error', model, resp, options);
+     };
+   };
+ 
+-}).call(this);
++  return Backbone;
++});
+-- 
+1.8.3.1
+
+
+From fa36d43150d98f1ad386ccfec910967d45733ced Mon Sep 17 00:00:00 2001
+From: Alexander Scheel <ascheel@redhat.com>
+Date: Mon, 16 Mar 2020 11:55:06 -0400
+Subject: [PATCH 11/17] Upgrade to underscore v1.9.2
+
+Signed-off-by: Alexander Scheel <ascheel@redhat.com>
+(cherry picked from commit 36be451de0cc890694b420f20ec5adcc1450706f)
+---
+ base/server/share/webapps/pki/js/underscore.js | 1672 +++++++++++++++---------
+ 1 file changed, 1044 insertions(+), 628 deletions(-)
+
+diff --git a/base/server/share/webapps/pki/js/underscore.js b/base/server/share/webapps/pki/js/underscore.js
+index a2a0d57..5cdf62e 100644
+--- a/base/server/share/webapps/pki/js/underscore.js
++++ b/base/server/share/webapps/pki/js/underscore.js
+@@ -1,6 +1,6 @@
+-//     Underscore.js 1.5.2
+-//     http://underscorejs.org
+-//     (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
++//     Underscore.js 1.9.2
++//     https://underscorejs.org
++//     (c) 2009-2018 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
+ //     Underscore may be freely distributed under the MIT license.
+ 
+ (function() {
+@@ -8,41 +8,35 @@
+   // Baseline setup
+   // --------------
+ 
+-  // Establish the root object, `window` in the browser, or `exports` on the server.
+-  var root = this;
++  // Establish the root object, `window` (`self`) in the browser, `global`
++  // on the server, or `this` in some virtual machines. We use `self`
++  // instead of `window` for `WebWorker` support.
++  var root = typeof self == 'object' && self.self === self && self ||
++            typeof global == 'object' && global.global === global && global ||
++            this ||
++            {};
+ 
+   // Save the previous value of the `_` variable.
+   var previousUnderscore = root._;
+ 
+-  // Establish the object that gets returned to break out of a loop iteration.
+-  var breaker = {};
+-
+   // Save bytes in the minified (but not gzipped) version:
+-  var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
++  var ArrayProto = Array.prototype, ObjProto = Object.prototype;
++  var SymbolProto = typeof Symbol !== 'undefined' ? Symbol.prototype : null;
+ 
+   // Create quick reference variables for speed access to core prototypes.
+-  var
+-    push             = ArrayProto.push,
+-    slice            = ArrayProto.slice,
+-    concat           = ArrayProto.concat,
+-    toString         = ObjProto.toString,
+-    hasOwnProperty   = ObjProto.hasOwnProperty;
++  var push = ArrayProto.push,
++      slice = ArrayProto.slice,
++      toString = ObjProto.toString,
++      hasOwnProperty = ObjProto.hasOwnProperty;
+ 
+   // All **ECMAScript 5** native function implementations that we hope to use
+   // are declared here.
+-  var
+-    nativeForEach      = ArrayProto.forEach,
+-    nativeMap          = ArrayProto.map,
+-    nativeReduce       = ArrayProto.reduce,
+-    nativeReduceRight  = ArrayProto.reduceRight,
+-    nativeFilter       = ArrayProto.filter,
+-    nativeEvery        = ArrayProto.every,
+-    nativeSome         = ArrayProto.some,
+-    nativeIndexOf      = ArrayProto.indexOf,
+-    nativeLastIndexOf  = ArrayProto.lastIndexOf,
+-    nativeIsArray      = Array.isArray,
+-    nativeKeys         = Object.keys,
+-    nativeBind         = FuncProto.bind;
++  var nativeIsArray = Array.isArray,
++      nativeKeys = Object.keys,
++      nativeCreate = Object.create;
++
++  // Naked function reference for surrogate-prototype-swapping.
++  var Ctor = function(){};
+ 
+   // Create a safe reference to the Underscore object for use below.
+   var _ = function(obj) {
+@@ -52,11 +46,12 @@
+   };
+ 
+   // Export the Underscore object for **Node.js**, with
+-  // backwards-compatibility for the old `require()` API. If we're in
+-  // the browser, add `_` as a global object via a string identifier,
+-  // for Closure Compiler "advanced" mode.
+-  if (typeof exports !== 'undefined') {
+-    if (typeof module !== 'undefined' && module.exports) {
++  // backwards-compatibility for their old module API. If we're in
++  // the browser, add `_` as a global object.
++  // (`nodeType` is checked to ensure that `module`
++  // and `exports` are not HTML elements.)
++  if (typeof exports != 'undefined' && !exports.nodeType) {
++    if (typeof module != 'undefined' && !module.nodeType && module.exports) {
+       exports = module.exports = _;
+     }
+     exports._ = _;
+@@ -65,261 +60,367 @@
+   }
+ 
+   // Current version.
+-  _.VERSION = '1.5.2';
++  _.VERSION = '1.9.2';
++
++  // Internal function that returns an efficient (for current engines) version
++  // of the passed-in callback, to be repeatedly applied in other Underscore
++  // functions.
++  var optimizeCb = function(func, context, argCount) {
++    if (context === void 0) return func;
++    switch (argCount == null ? 3 : argCount) {
++      case 1: return function(value) {
++        return func.call(context, value);
++      };
++      // The 2-argument case is omitted because we’re not using it.
++      case 3: return function(value, index, collection) {
++        return func.call(context, value, index, collection);
++      };
++      case 4: return function(accumulator, value, index, collection) {
++        return func.call(context, accumulator, value, index, collection);
++      };
++    }
++    return function() {
++      return func.apply(context, arguments);
++    };
++  };
++
++  var builtinIteratee;
++
++  // An internal function to generate callbacks that can be applied to each
++  // element in a collection, returning the desired result — either `identity`,
++  // an arbitrary callback, a property matcher, or a property accessor.
++  var cb = function(value, context, argCount) {
++    if (_.iteratee !== builtinIteratee) return _.iteratee(value, context);
++    if (value == null) return _.identity;
++    if (_.isFunction(value)) return optimizeCb(value, context, argCount);
++    if (_.isObject(value) && !_.isArray(value)) return _.matcher(value);
++    return _.property(value);
++  };
++
++  // External wrapper for our callback generator. Users may customize
++  // `_.iteratee` if they want additional predicate/iteratee shorthand styles.
++  // This abstraction hides the internal-only argCount argument.
++  _.iteratee = builtinIteratee = function(value, context) {
++    return cb(value, context, Infinity);
++  };
++
++  // Some functions take a variable number of arguments, or a few expected
++  // arguments at the beginning and then a variable number of values to operate
++  // on. This helper accumulates all remaining arguments past the function’s
++  // argument length (or an explicit `startIndex`), into an array that becomes
++  // the last argument. Similar to ES6’s "rest parameter".
++  var restArguments = function(func, startIndex) {
++    startIndex = startIndex == null ? func.length - 1 : +startIndex;
++    return function() {
++      var length = Math.max(arguments.length - startIndex, 0),
++          rest = Array(length),
++          index = 0;
++      for (; index < length; index++) {
++        rest[index] = arguments[index + startIndex];
++      }
++      switch (startIndex) {
++        case 0: return func.call(this, rest);
++        case 1: return func.call(this, arguments[0], rest);
++        case 2: return func.call(this, arguments[0], arguments[1], rest);
++      }
++      var args = Array(startIndex + 1);
++      for (index = 0; index < startIndex; index++) {
++        args[index] = arguments[index];
++      }
++      args[startIndex] = rest;
++      return func.apply(this, args);
++    };
++  };
++
++  // An internal function for creating a new object that inherits from another.
++  var baseCreate = function(prototype) {
++    if (!_.isObject(prototype)) return {};
++    if (nativeCreate) return nativeCreate(prototype);
++    Ctor.prototype = prototype;
++    var result = new Ctor;
++    Ctor.prototype = null;
++    return result;
++  };
++
++  var shallowProperty = function(key) {
++    return function(obj) {
++      return obj == null ? void 0 : obj[key];
++    };
++  };
++
++  var has = function(obj, path) {
++    return obj != null && hasOwnProperty.call(obj, path);
++  }
++
++  var deepGet = function(obj, path) {
++    var length = path.length;
++    for (var i = 0; i < length; i++) {
++      if (obj == null) return void 0;
++      obj = obj[path[i]];
++    }
++    return length ? obj : void 0;
++  };
++
++  // Helper for collection methods to determine whether a collection
++  // should be iterated as an array or as an object.
++  // Related: https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength
++  // Avoids a very nasty iOS 8 JIT bug on ARM-64. #2094
++  var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
++  var getLength = shallowProperty('length');
++  var isArrayLike = function(collection) {
++    var length = getLength(collection);
++    return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
++  };
+ 
+   // Collection Functions
+   // --------------------
+ 
+   // The cornerstone, an `each` implementation, aka `forEach`.
+-  // Handles objects with the built-in `forEach`, arrays, and raw objects.
+-  // Delegates to **ECMAScript 5**'s native `forEach` if available.
+-  var each = _.each = _.forEach = function(obj, iterator, context) {
+-    if (obj == null) return;
+-    if (nativeForEach && obj.forEach === nativeForEach) {
+-      obj.forEach(iterator, context);
+-    } else if (obj.length === +obj.length) {
+-      for (var i = 0, length = obj.length; i < length; i++) {
+-        if (iterator.call(context, obj[i], i, obj) === breaker) return;
++  // Handles raw objects in addition to array-likes. Treats all
++  // sparse array-likes as if they were dense.
++  _.each = _.forEach = function(obj, iteratee, context) {
++    iteratee = optimizeCb(iteratee, context);
++    var i, length;
++    if (isArrayLike(obj)) {
++      for (i = 0, length = obj.length; i < length; i++) {
++        iteratee(obj[i], i, obj);
+       }
+     } else {
+       var keys = _.keys(obj);
+-      for (var i = 0, length = keys.length; i < length; i++) {
+-        if (iterator.call(context, obj[keys[i]], keys[i], obj) === breaker) return;
++      for (i = 0, length = keys.length; i < length; i++) {
++        iteratee(obj[keys[i]], keys[i], obj);
+       }
+     }
++    return obj;
+   };
+ 
+-  // Return the results of applying the iterator to each element.
+-  // Delegates to **ECMAScript 5**'s native `map` if available.
+-  _.map = _.collect = function(obj, iterator, context) {
+-    var results = [];
+-    if (obj == null) return results;
+-    if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
+-    each(obj, function(value, index, list) {
+-      results.push(iterator.call(context, value, index, list));
+-    });
++  // Return the results of applying the iteratee to each element.
++  _.map = _.collect = function(obj, iteratee, context) {
++    iteratee = cb(iteratee, context);
++    var keys = !isArrayLike(obj) && _.keys(obj),
++        length = (keys || obj).length,
++        results = Array(length);
++    for (var index = 0; index < length; index++) {
++      var currentKey = keys ? keys[index] : index;
++      results[index] = iteratee(obj[currentKey], currentKey, obj);
++    }
+     return results;
+   };
+ 
+-  var reduceError = 'Reduce of empty array with no initial value';
+-
+-  // **Reduce** builds up a single result from a list of values, aka `inject`,
+-  // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
+-  _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
+-    var initial = arguments.length > 2;
+-    if (obj == null) obj = [];
+-    if (nativeReduce && obj.reduce === nativeReduce) {
+-      if (context) iterator = _.bind(iterator, context);
+-      return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
+-    }
+-    each(obj, function(value, index, list) {
++  // Create a reducing function iterating left or right.
++  var createReduce = function(dir) {
++    // Wrap code that reassigns argument variables in a separate function than
++    // the one that accesses `arguments.length` to avoid a perf hit. (#1991)
++    var reducer = function(obj, iteratee, memo, initial) {
++      var keys = !isArrayLike(obj) && _.keys(obj),
++          length = (keys || obj).length,
++          index = dir > 0 ? 0 : length - 1;
+       if (!initial) {
+-        memo = value;
+-        initial = true;
+-      } else {
+-        memo = iterator.call(context, memo, value, index, list);
++        memo = obj[keys ? keys[index] : index];
++        index += dir;
+       }
+-    });
+-    if (!initial) throw new TypeError(reduceError);
+-    return memo;
++      for (; index >= 0 && index < length; index += dir) {
++        var currentKey = keys ? keys[index] : index;
++        memo = iteratee(memo, obj[currentKey], currentKey, obj);
++      }
++      return memo;
++    };
++
++    return function(obj, iteratee, memo, context) {
++      var initial = arguments.length >= 3;
++      return reducer(obj, optimizeCb(iteratee, context, 4), memo, initial);
++    };
+   };
+ 
++  // **Reduce** builds up a single result from a list of values, aka `inject`,
++  // or `foldl`.
++  _.reduce = _.foldl = _.inject = createReduce(1);
++
+   // The right-associative version of reduce, also known as `foldr`.
+-  // Delegates to **ECMAScript 5**'s native `reduceRight` if available.
+-  _.reduceRight = _.foldr = function(obj, iterator, memo, context) {
+-    var initial = arguments.length > 2;
+-    if (obj == null) obj = [];
+-    if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
+-      if (context) iterator = _.bind(iterator, context);
+-      return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
+-    }
+-    var length = obj.length;
+-    if (length !== +length) {
+-      var keys = _.keys(obj);
+-      length = keys.length;
+-    }
+-    each(obj, function(value, index, list) {
+-      index = keys ? keys[--length] : --length;
+-      if (!initial) {
+-        memo = obj[index];
+-        initial = true;
+-      } else {
+-        memo = iterator.call(context, memo, obj[index], index, list);
+-      }
+-    });
+-    if (!initial) throw new TypeError(reduceError);
+-    return memo;
+-  };
++  _.reduceRight = _.foldr = createReduce(-1);
+ 
+   // Return the first value which passes a truth test. Aliased as `detect`.
+-  _.find = _.detect = function(obj, iterator, context) {
+-    var result;
+-    any(obj, function(value, index, list) {
+-      if (iterator.call(context, value, index, list)) {
+-        result = value;
+-        return true;
+-      }
+-    });
+-    return result;
++  _.find = _.detect = function(obj, predicate, context) {
++    var keyFinder = isArrayLike(obj) ? _.findIndex : _.findKey;
++    var key = keyFinder(obj, predicate, context);
++    if (key !== void 0 && key !== -1) return obj[key];
+   };
+ 
+   // Return all the elements that pass a truth test.
+-  // Delegates to **ECMAScript 5**'s native `filter` if available.
+   // Aliased as `select`.
+-  _.filter = _.select = function(obj, iterator, context) {
++  _.filter = _.select = function(obj, predicate, context) {
+     var results = [];
+-    if (obj == null) return results;
+-    if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
+-    each(obj, function(value, index, list) {
+-      if (iterator.call(context, value, index, list)) results.push(value);
++    predicate = cb(predicate, context);
++    _.each(obj, function(value, index, list) {
++      if (predicate(value, index, list)) results.push(value);
+     });
+     return results;
+   };
+ 
+   // Return all the elements for which a truth test fails.
+-  _.reject = function(obj, iterator, context) {
+-    return _.filter(obj, function(value, index, list) {
+-      return !iterator.call(context, value, index, list);
+-    }, context);
++  _.reject = function(obj, predicate, context) {
++    return _.filter(obj, _.negate(cb(predicate)), context);
+   };
+ 
+   // Determine whether all of the elements match a truth test.
+-  // Delegates to **ECMAScript 5**'s native `every` if available.
+   // Aliased as `all`.
+-  _.every = _.all = function(obj, iterator, context) {
+-    iterator || (iterator = _.identity);
+-    var result = true;
+-    if (obj == null) return result;
+-    if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
+-    each(obj, function(value, index, list) {
+-      if (!(result = result && iterator.call(context, value, index, list))) return breaker;
+-    });
+-    return !!result;
++  _.every = _.all = function(obj, predicate, context) {
++    predicate = cb(predicate, context);
++    var keys = !isArrayLike(obj) && _.keys(obj),
++        length = (keys || obj).length;
++    for (var index = 0; index < length; index++) {
++      var currentKey = keys ? keys[index] : index;
++      if (!predicate(obj[currentKey], currentKey, obj)) return false;
++    }
++    return true;
+   };
+ 
+   // Determine if at least one element in the object matches a truth test.
+-  // Delegates to **ECMAScript 5**'s native `some` if available.
+   // Aliased as `any`.
+-  var any = _.some = _.any = function(obj, iterator, context) {
+-    iterator || (iterator = _.identity);
+-    var result = false;
+-    if (obj == null) return result;
+-    if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
+-    each(obj, function(value, index, list) {
+-      if (result || (result = iterator.call(context, value, index, list))) return breaker;
+-    });
+-    return !!result;
++  _.some = _.any = function(obj, predicate, context) {
++    predicate = cb(predicate, context);
++    var keys = !isArrayLike(obj) && _.keys(obj),
++        length = (keys || obj).length;
++    for (var index = 0; index < length; index++) {
++      var currentKey = keys ? keys[index] : index;
++      if (predicate(obj[currentKey], currentKey, obj)) return true;
++    }
++    return false;
+   };
+ 
+-  // Determine if the array or object contains a given value (using `===`).
+-  // Aliased as `include`.
+-  _.contains = _.include = function(obj, target) {
+-    if (obj == null) return false;
+-    if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
+-    return any(obj, function(value) {
+-      return value === target;
+-    });
++  // Determine if the array or object contains a given item (using `===`).
++  // Aliased as `includes` and `include`.
++  _.contains = _.includes = _.include = function(obj, item, fromIndex, guard) {
++    if (!isArrayLike(obj)) obj = _.values(obj);
++    if (typeof fromIndex != 'number' || guard) fromIndex = 0;
++    return _.indexOf(obj, item, fromIndex) >= 0;
+   };
+ 
+   // Invoke a method (with arguments) on every item in a collection.
+-  _.invoke = function(obj, method) {
+-    var args = slice.call(arguments, 2);
+-    var isFunc = _.isFunction(method);
+-    return _.map(obj, function(value) {
+-      return (isFunc ? method : value[method]).apply(value, args);
++  _.invoke = restArguments(function(obj, path, args) {
++    var contextPath, func;
++    if (_.isFunction(path)) {
++      func = path;
++    } else if (_.isArray(path)) {
++      contextPath = path.slice(0, -1);
++      path = path[path.length - 1];
++    }
++    return _.map(obj, function(context) {
++      var method = func;
++      if (!method) {
++        if (contextPath && contextPath.length) {
++          context = deepGet(context, contextPath);
++        }
++        if (context == null) return void 0;
++        method = context[path];
++      }
++      return method == null ? method : method.apply(context, args);
+     });
+-  };
++  });
+ 
+   // Convenience version of a common use case of `map`: fetching a property.
+   _.pluck = function(obj, key) {
+-    return _.map(obj, function(value){ return value[key]; });
++    return _.map(obj, _.property(key));
+   };
+ 
+   // Convenience version of a common use case of `filter`: selecting only objects
+   // containing specific `key:value` pairs.
+-  _.where = function(obj, attrs, first) {
+-    if (_.isEmpty(attrs)) return first ? void 0 : [];
+-    return _[first ? 'find' : 'filter'](obj, function(value) {
+-      for (var key in attrs) {
+-        if (attrs[key] !== value[key]) return false;
+-      }
+-      return true;
+-    });
++  _.where = function(obj, attrs) {
++    return _.filter(obj, _.matcher(attrs));
+   };
+ 
+   // Convenience version of a common use case of `find`: getting the first object
+   // containing specific `key:value` pairs.
+   _.findWhere = function(obj, attrs) {
+-    return _.where(obj, attrs, true);
++    return _.find(obj, _.matcher(attrs));
+   };
+ 
+-  // Return the maximum element or (element-based computation).
+-  // Can't optimize arrays of integers longer than 65,535 elements.
+-  // See [WebKit Bug 80797](https://bugs.webkit.org/show_bug.cgi?id=80797)
+-  _.max = function(obj, iterator, context) {
+-    if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
+-      return Math.max.apply(Math, obj);
++  // Return the maximum element (or element-based computation).
++  _.max = function(obj, iteratee, context) {
++    var result = -Infinity, lastComputed = -Infinity,
++        value, computed;
++    if (iteratee == null || typeof iteratee == 'number' && typeof obj[0] != 'object' && obj != null) {
++      obj = isArrayLike(obj) ? obj : _.values(obj);
++      for (var i = 0, length = obj.length; i < length; i++) {
++        value = obj[i];
++        if (value != null && value > result) {
++          result = value;
++        }
++      }
++    } else {
++      iteratee = cb(iteratee, context);
++      _.each(obj, function(v, index, list) {
++        computed = iteratee(v, index, list);
++        if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
++          result = v;
++          lastComputed = computed;
++        }
++      });
+     }
+-    if (!iterator && _.isEmpty(obj)) return -Infinity;
+-    var result = {computed : -Infinity, value: -Infinity};
+-    each(obj, function(value, index, list) {
+-      var computed = iterator ? iterator.call(context, value, index, list) : value;
+-      computed > result.computed && (result = {value : value, computed : computed});
+-    });
+-    return result.value;
++    return result;
+   };
+ 
+   // Return the minimum element (or element-based computation).
+-  _.min = function(obj, iterator, context) {
+-    if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
+-      return Math.min.apply(Math, obj);
++  _.min = function(obj, iteratee, context) {
++    var result = Infinity, lastComputed = Infinity,
++        value, computed;
++    if (iteratee == null || typeof iteratee == 'number' && typeof obj[0] != 'object' && obj != null) {
++      obj = isArrayLike(obj) ? obj : _.values(obj);
++      for (var i = 0, length = obj.length; i < length; i++) {
++        value = obj[i];
++        if (value != null && value < result) {
++          result = value;
++        }
++      }
++    } else {
++      iteratee = cb(iteratee, context);
++      _.each(obj, function(v, index, list) {
++        computed = iteratee(v, index, list);
++        if (computed < lastComputed || computed === Infinity && result === Infinity) {
++          result = v;
++          lastComputed = computed;
++        }
++      });
+     }
+-    if (!iterator && _.isEmpty(obj)) return Infinity;
+-    var result = {computed : Infinity, value: Infinity};
+-    each(obj, function(value, index, list) {
+-      var computed = iterator ? iterator.call(context, value, index, list) : value;
+-      computed < result.computed && (result = {value : value, computed : computed});
+-    });
+-    return result.value;
++    return result;
+   };
+ 
+-  // Shuffle an array, using the modern version of the
+-  // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
++  // Shuffle a collection.
+   _.shuffle = function(obj) {
+-    var rand;
+-    var index = 0;
+-    var shuffled = [];
+-    each(obj, function(value) {
+-      rand = _.random(index++);
+-      shuffled[index - 1] = shuffled[rand];
+-      shuffled[rand] = value;
+-    });
+-    return shuffled;
++    return _.sample(obj, Infinity);
+   };
+ 
+-  // Sample **n** random values from an array.
+-  // If **n** is not specified, returns a single random element from the array.
++  // Sample **n** random values from a collection using the modern version of the
++  // [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
++  // If **n** is not specified, returns a single random element.
+   // The internal `guard` argument allows it to work with `map`.
+   _.sample = function(obj, n, guard) {
+-    if (arguments.length < 2 || guard) {
++    if (n == null || guard) {
++      if (!isArrayLike(obj)) obj = _.values(obj);
+       return obj[_.random(obj.length - 1)];
+     }
+-    return _.shuffle(obj).slice(0, Math.max(0, n));
+-  };
+-
+-  // An internal function to generate lookup iterators.
+-  var lookupIterator = function(value) {
+-    return _.isFunction(value) ? value : function(obj){ return obj[value]; };
++    var sample = isArrayLike(obj) ? _.clone(obj) : _.values(obj);
++    var length = getLength(sample);
++    n = Math.max(Math.min(n, length), 0);
++    var last = length - 1;
++    for (var index = 0; index < n; index++) {
++      var rand = _.random(index, last);
++      var temp = sample[index];
++      sample[index] = sample[rand];
++      sample[rand] = temp;
++    }
++    return sample.slice(0, n);
+   };
+ 
+-  // Sort the object's values by a criterion produced by an iterator.
+-  _.sortBy = function(obj, value, context) {
+-    var iterator = lookupIterator(value);
+-    return _.pluck(_.map(obj, function(value, index, list) {
++  // Sort the object's values by a criterion produced by an iteratee.
++  _.sortBy = function(obj, iteratee, context) {
++    var index = 0;
++    iteratee = cb(iteratee, context);
++    return _.pluck(_.map(obj, function(value, key, list) {
+       return {
+         value: value,
+-        index: index,
+-        criteria: iterator.call(context, value, index, list)
++        index: index++,
++        criteria: iteratee(value, key, list)
+       };
+     }).sort(function(left, right) {
+       var a = left.criteria;
+@@ -333,13 +434,13 @@
+   };
+ 
+   // An internal function used for aggregate "group by" operations.
+-  var group = function(behavior) {
+-    return function(obj, value, context) {
+-      var result = {};
+-      var iterator = value == null ? _.identity : lookupIterator(value);
+-      each(obj, function(value, index) {
+-        var key = iterator.call(context, value, index, obj);
+-        behavior(result, key, value);
++  var group = function(behavior, partition) {
++    return function(obj, iteratee, context) {
++      var result = partition ? [[], []] : {};
++      iteratee = cb(iteratee, context);
++      _.each(obj, function(value, index) {
++        var key = iteratee(value, index, obj);
++        behavior(result, value, key);
+       });
+       return result;
+     };
+@@ -347,50 +448,48 @@
+ 
+   // Groups the object's values by a criterion. Pass either a string attribute
+   // to group by, or a function that returns the criterion.
+-  _.groupBy = group(function(result, key, value) {
+-    (_.has(result, key) ? result[key] : (result[key] = [])).push(value);
++  _.groupBy = group(function(result, value, key) {
++    if (has(result, key)) result[key].push(value); else result[key] = [value];
+   });
+ 
+   // Indexes the object's values by a criterion, similar to `groupBy`, but for
+   // when you know that your index values will be unique.
+-  _.indexBy = group(function(result, key, value) {
++  _.indexBy = group(function(result, value, key) {
+     result[key] = value;
+   });
+ 
+   // Counts instances of an object that group by a certain criterion. Pass
+   // either a string attribute to count by, or a function that returns the
+   // criterion.
+-  _.countBy = group(function(result, key) {
+-    _.has(result, key) ? result[key]++ : result[key] = 1;
++  _.countBy = group(function(result, value, key) {
++    if (has(result, key)) result[key]++; else result[key] = 1;
+   });
+ 
+-  // Use a comparator function to figure out the smallest index at which
+-  // an object should be inserted so as to maintain order. Uses binary search.
+-  _.sortedIndex = function(array, obj, iterator, context) {
+-    iterator = iterator == null ? _.identity : lookupIterator(iterator);
+-    var value = iterator.call(context, obj);
+-    var low = 0, high = array.length;
+-    while (low < high) {
+-      var mid = (low + high) >>> 1;
+-      iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid;
+-    }
+-    return low;
+-  };
+-
++  var reStrSymbol = /[^\ud800-\udfff]|[\ud800-\udbff][\udc00-\udfff]|[\ud800-\udfff]/g;
+   // Safely create a real, live array from anything iterable.
+   _.toArray = function(obj) {
+     if (!obj) return [];
+     if (_.isArray(obj)) return slice.call(obj);
+-    if (obj.length === +obj.length) return _.map(obj, _.identity);
++    if (_.isString(obj)) {
++      // Keep surrogate pair characters together
++      return obj.match(reStrSymbol);
++    }
++    if (isArrayLike(obj)) return _.map(obj, _.identity);
+     return _.values(obj);
+   };
+ 
+   // Return the number of elements in an object.
+   _.size = function(obj) {
+     if (obj == null) return 0;
+-    return (obj.length === +obj.length) ? obj.length : _.keys(obj).length;
++    return isArrayLike(obj) ? obj.length : _.keys(obj).length;
+   };
+ 
++  // Split a collection into two arrays: one whose elements all satisfy the given
++  // predicate, and one whose elements all do not satisfy the predicate.
++  _.partition = group(function(result, value, pass) {
++    result[pass ? 0 : 1].push(value);
++  }, true);
++
+   // Array Functions
+   // ---------------
+ 
+@@ -398,130 +497,157 @@
+   // values in the array. Aliased as `head` and `take`. The **guard** check
+   // allows it to work with `_.map`.
+   _.first = _.head = _.take = function(array, n, guard) {
+-    if (array == null) return void 0;
+-    return (n == null) || guard ? array[0] : slice.call(array, 0, n);
++    if (array == null || array.length < 1) return n == null ? void 0 : [];
++    if (n == null || guard) return array[0];
++    return _.initial(array, array.length - n);
+   };
+ 
+   // Returns everything but the last entry of the array. Especially useful on
+   // the arguments object. Passing **n** will return all the values in
+-  // the array, excluding the last N. The **guard** check allows it to work with
+-  // `_.map`.
++  // the array, excluding the last N.
+   _.initial = function(array, n, guard) {
+-    return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
++    return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
+   };
+ 
+   // Get the last element of an array. Passing **n** will return the last N
+-  // values in the array. The **guard** check allows it to work with `_.map`.
++  // values in the array.
+   _.last = function(array, n, guard) {
+-    if (array == null) return void 0;
+-    if ((n == null) || guard) {
+-      return array[array.length - 1];
+-    } else {
+-      return slice.call(array, Math.max(array.length - n, 0));
+-    }
++    if (array == null || array.length < 1) return n == null ? void 0 : [];
++    if (n == null || guard) return array[array.length - 1];
++    return _.rest(array, Math.max(0, array.length - n));
+   };
+ 
+   // Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
+   // Especially useful on the arguments object. Passing an **n** will return
+-  // the rest N values in the array. The **guard**
+-  // check allows it to work with `_.map`.
++  // the rest N values in the array.
+   _.rest = _.tail = _.drop = function(array, n, guard) {
+-    return slice.call(array, (n == null) || guard ? 1 : n);
++    return slice.call(array, n == null || guard ? 1 : n);
+   };
+ 
+   // Trim out all falsy values from an array.
+   _.compact = function(array) {
+-    return _.filter(array, _.identity);
++    return _.filter(array, Boolean);
+   };
+ 
+   // Internal implementation of a recursive `flatten` function.
+-  var flatten = function(input, shallow, output) {
+-    if (shallow && _.every(input, _.isArray)) {
+-      return concat.apply(output, input);
+-    }
+-    each(input, function(value) {
+-      if (_.isArray(value) || _.isArguments(value)) {
+-        shallow ? push.apply(output, value) : flatten(value, shallow, output);
+-      } else {
+-        output.push(value);
++  var flatten = function(input, shallow, strict, output) {
++    output = output || [];
++    var idx = output.length;
++    for (var i = 0, length = getLength(input); i < length; i++) {
++      var value = input[i];
++      if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) {
++        // Flatten current level of array or arguments object.
++        if (shallow) {
++          var j = 0, len = value.length;
++          while (j < len) output[idx++] = value[j++];
++        } else {
++          flatten(value, shallow, strict, output);
++          idx = output.length;
++        }
++      } else if (!strict) {
++        output[idx++] = value;
+       }
+-    });
++    }
+     return output;
+   };
+ 
+   // Flatten out an array, either recursively (by default), or just one level.
+   _.flatten = function(array, shallow) {
+-    return flatten(array, shallow, []);
++    return flatten(array, shallow, false);
+   };
+ 
+   // Return a version of the array that does not contain the specified value(s).
+-  _.without = function(array) {
+-    return _.difference(array, slice.call(arguments, 1));
+-  };
++  _.without = restArguments(function(array, otherArrays) {
++    return _.difference(array, otherArrays);
++  });
+ 
+   // Produce a duplicate-free version of the array. If the array has already
+   // been sorted, you have the option of using a faster algorithm.
++  // The faster algorithm will not work with an iteratee if the iteratee
++  // is not a one-to-one function, so providing an iteratee will disable
++  // the faster algorithm.
+   // Aliased as `unique`.
+-  _.uniq = _.unique = function(array, isSorted, iterator, context) {
+-    if (_.isFunction(isSorted)) {
+-      context = iterator;
+-      iterator = isSorted;
++  _.uniq = _.unique = function(array, isSorted, iteratee, context) {
++    if (!_.isBoolean(isSorted)) {
++      context = iteratee;
++      iteratee = isSorted;
+       isSorted = false;
+     }
+-    var initial = iterator ? _.map(array, iterator, context) : array;
+-    var results = [];
++    if (iteratee != null) iteratee = cb(iteratee, context);
++    var result = [];
+     var seen = [];
+-    each(initial, function(value, index) {
+-      if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) {
+-        seen.push(value);
+-        results.push(array[index]);
++    for (var i = 0, length = getLength(array); i < length; i++) {
++      var value = array[i],
++          computed = iteratee ? iteratee(value, i, array) : value;
++      if (isSorted && !iteratee) {
++        if (!i || seen !== computed) result.push(value);
++        seen = computed;
++      } else if (iteratee) {
++        if (!_.contains(seen, computed)) {
++          seen.push(computed);
++          result.push(value);
++        }
++      } else if (!_.contains(result, value)) {
++        result.push(value);
+       }
+-    });
+-    return results;
++    }
++    return result;
+   };
+ 
+   // Produce an array that contains the union: each distinct element from all of
+   // the passed-in arrays.
+-  _.union = function() {
+-    return _.uniq(_.flatten(arguments, true));
+-  };
++  _.union = restArguments(function(arrays) {
++    return _.uniq(flatten(arrays, true, true));
++  });
+ 
+   // Produce an array that contains every item shared between all the
+   // passed-in arrays.
+   _.intersection = function(array) {
+-    var rest = slice.call(arguments, 1);
+-    return _.filter(_.uniq(array), function(item) {
+-      return _.every(rest, function(other) {
+-        return _.indexOf(other, item) >= 0;
+-      });
+-    });
++    var result = [];
++    var argsLength = arguments.length;
++    for (var i = 0, length = getLength(array); i < length; i++) {
++      var item = array[i];
++      if (_.contains(result, item)) continue;
++      var j;
++      for (j = 1; j < argsLength; j++) {
++        if (!_.contains(arguments[j], item)) break;
++      }
++      if (j === argsLength) result.push(item);
++    }
++    return result;
+   };
+ 
+   // Take the difference between one array and a number of other arrays.
+   // Only the elements present in just the first array will remain.
+-  _.difference = function(array) {
+-    var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
+-    return _.filter(array, function(value){ return !_.contains(rest, value); });
++  _.difference = restArguments(function(array, rest) {
++    rest = flatten(rest, true, true);
++    return _.filter(array, function(value){
++      return !_.contains(rest, value);
++    });
++  });
++
++  // Complement of _.zip. Unzip accepts an array of arrays and groups
++  // each array's elements on shared indices.
++  _.unzip = function(array) {
++    var length = array && _.max(array, getLength).length || 0;
++    var result = Array(length);
++
++    for (var index = 0; index < length; index++) {
++      result[index] = _.pluck(array, index);
++    }
++    return result;
+   };
+ 
+   // Zip together multiple lists into a single array -- elements that share
+   // an index go together.
+-  _.zip = function() {
+-    var length = _.max(_.pluck(arguments, "length").concat(0));
+-    var results = new Array(length);
+-    for (var i = 0; i < length; i++) {
+-      results[i] = _.pluck(arguments, '' + i);
+-    }
+-    return results;
+-  };
++  _.zip = restArguments(_.unzip);
+ 
+   // Converts lists into objects. Pass either a single array of `[key, value]`
+   // pairs, or two parallel arrays of the same length -- one of keys, and one of
+-  // the corresponding values.
++  // the corresponding values. Passing by pairs is the reverse of _.pairs.
+   _.object = function(list, values) {
+-    if (list == null) return {};
+     var result = {};
+-    for (var i = 0, length = list.length; i < length; i++) {
++    for (var i = 0, length = getLength(list); i < length; i++) {
+       if (values) {
+         result[list[i]] = values[i];
+       } else {
+@@ -531,127 +657,182 @@
+     return result;
+   };
+ 
+-  // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
+-  // we need this function. Return the position of the first occurrence of an
+-  // item in an array, or -1 if the item is not included in the array.
+-  // Delegates to **ECMAScript 5**'s native `indexOf` if available.
+-  // If the array is large and already in sort order, pass `true`
+-  // for **isSorted** to use binary search.
+-  _.indexOf = function(array, item, isSorted) {
+-    if (array == null) return -1;
+-    var i = 0, length = array.length;
+-    if (isSorted) {
+-      if (typeof isSorted == 'number') {
+-        i = (isSorted < 0 ? Math.max(0, length + isSorted) : isSorted);
+-      } else {
+-        i = _.sortedIndex(array, item);
+-        return array[i] === item ? i : -1;
++  // Generator function to create the findIndex and findLastIndex functions.
++  var createPredicateIndexFinder = function(dir) {
++    return function(array, predicate, context) {
++      predicate = cb(predicate, context);
++      var length = getLength(array);
++      var index = dir > 0 ? 0 : length - 1;
++      for (; index >= 0 && index < length; index += dir) {
++        if (predicate(array[index], index, array)) return index;
+       }
+-    }
+-    if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
+-    for (; i < length; i++) if (array[i] === item) return i;
+-    return -1;
++      return -1;
++    };
+   };
+ 
+-  // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
+-  _.lastIndexOf = function(array, item, from) {
+-    if (array == null) return -1;
+-    var hasIndex = from != null;
+-    if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) {
+-      return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item);
++  // Returns the first index on an array-like that passes a predicate test.
++  _.findIndex = createPredicateIndexFinder(1);
++  _.findLastIndex = createPredicateIndexFinder(-1);
++
++  // Use a comparator function to figure out the smallest index at which
++  // an object should be inserted so as to maintain order. Uses binary search.
++  _.sortedIndex = function(array, obj, iteratee, context) {
++    iteratee = cb(iteratee, context, 1);
++    var value = iteratee(obj);
++    var low = 0, high = getLength(array);
++    while (low < high) {
++      var mid = Math.floor((low + high) / 2);
++      if (iteratee(array[mid]) < value) low = mid + 1; else high = mid;
+     }
+-    var i = (hasIndex ? from : array.length);
+-    while (i--) if (array[i] === item) return i;
+-    return -1;
++    return low;
+   };
+ 
++  // Generator function to create the indexOf and lastIndexOf functions.
++  var createIndexFinder = function(dir, predicateFind, sortedIndex) {
++    return function(array, item, idx) {
++      var i = 0, length = getLength(array);
++      if (typeof idx == 'number') {
++        if (dir > 0) {
++          i = idx >= 0 ? idx : Math.max(idx + length, i);
++        } else {
++          length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1;
++        }
++      } else if (sortedIndex && idx && length) {
++        idx = sortedIndex(array, item);
++        return array[idx] === item ? idx : -1;
++      }
++      if (item !== item) {
++        idx = predicateFind(slice.call(array, i, length), _.isNaN);
++        return idx >= 0 ? idx + i : -1;
++      }
++      for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) {
++        if (array[idx] === item) return idx;
++      }
++      return -1;
++    };
++  };
++
++  // Return the position of the first occurrence of an item in an array,
++  // or -1 if the item is not included in the array.
++  // If the array is large and already in sort order, pass `true`
++  // for **isSorted** to use binary search.
++  _.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex);
++  _.lastIndexOf = createIndexFinder(-1, _.findLastIndex);
++
+   // Generate an integer Array containing an arithmetic progression. A port of
+   // the native Python `range()` function. See
+-  // [the Python documentation](http://docs.python.org/library/functions.html#range).
++  // [the Python documentation](https://docs.python.org/library/functions.html#range).
+   _.range = function(start, stop, step) {
+-    if (arguments.length <= 1) {
++    if (stop == null) {
+       stop = start || 0;
+       start = 0;
+     }
+-    step = arguments[2] || 1;
++    if (!step) {
++      step = stop < start ? -1 : 1;
++    }
+ 
+     var length = Math.max(Math.ceil((stop - start) / step), 0);
+-    var idx = 0;
+-    var range = new Array(length);
++    var range = Array(length);
+ 
+-    while(idx < length) {
+-      range[idx++] = start;
+-      start += step;
++    for (var idx = 0; idx < length; idx++, start += step) {
++      range[idx] = start;
+     }
+ 
+     return range;
+   };
+ 
++  // Chunk a single array into multiple arrays, each containing `count` or fewer
++  // items.
++  _.chunk = function(array, count) {
++    if (count == null || count < 1) return [];
++    var result = [];
++    var i = 0, length = array.length;
++    while (i < length) {
++      result.push(slice.call(array, i, i += count));
++    }
++    return result;
++  };
++
+   // Function (ahem) Functions
+   // ------------------
+ 
+-  // Reusable constructor function for prototype setting.
+-  var ctor = function(){};
++  // Determines whether to execute a function as a constructor
++  // or a normal function with the provided arguments.
++  var executeBound = function(sourceFunc, boundFunc, context, callingContext, args) {
++    if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args);
++    var self = baseCreate(sourceFunc.prototype);
++    var result = sourceFunc.apply(self, args);
++    if (_.isObject(result)) return result;
++    return self;
++  };
+ 
+   // Create a function bound to a given object (assigning `this`, and arguments,
+   // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
+   // available.
+-  _.bind = function(func, context) {
+-    var args, bound;
+-    if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
+-    if (!_.isFunction(func)) throw new TypeError;
+-    args = slice.call(arguments, 2);
+-    return bound = function() {
+-      if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
+-      ctor.prototype = func.prototype;
+-      var self = new ctor;
+-      ctor.prototype = null;
+-      var result = func.apply(self, args.concat(slice.call(arguments)));
+-      if (Object(result) === result) return result;
+-      return self;
+-    };
+-  };
++  _.bind = restArguments(function(func, context, args) {
++    if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function');
++    var bound = restArguments(function(callArgs) {
++      return executeBound(func, bound, context, this, args.concat(callArgs));
++    });
++    return bound;
++  });
+ 
+   // Partially apply a function by creating a version that has had some of its
+-  // arguments pre-filled, without changing its dynamic `this` context.
+-  _.partial = function(func) {
+-    var args = slice.call(arguments, 1);
+-    return function() {
+-      return func.apply(this, args.concat(slice.call(arguments)));
++  // arguments pre-filled, without changing its dynamic `this` context. _ acts
++  // as a placeholder by default, allowing any combination of arguments to be
++  // pre-filled. Set `_.partial.placeholder` for a custom placeholder argument.
++  _.partial = restArguments(function(func, boundArgs) {
++    var placeholder = _.partial.placeholder;
++    var bound = function() {
++      var position = 0, length = boundArgs.length;
++      var args = Array(length);
++      for (var i = 0; i < length; i++) {
++        args[i] = boundArgs[i] === placeholder ? arguments[position++] : boundArgs[i];
++      }
++      while (position < arguments.length) args.push(arguments[position++]);
++      return executeBound(func, bound, this, this, args);
+     };
+-  };
++    return bound;
++  });
+ 
+-  // Bind all of an object's methods to that object. Useful for ensuring that
+-  // all callbacks defined on an object belong to it.
+-  _.bindAll = function(obj) {
+-    var funcs = slice.call(arguments, 1);
+-    if (funcs.length === 0) throw new Error("bindAll must be passed function names");
+-    each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
+-    return obj;
+-  };
++  _.partial.placeholder = _;
++
++  // Bind a number of an object's methods to that object. Remaining arguments
++  // are the method names to be bound. Useful for ensuring that all callbacks
++  // defined on an object belong to it.
++  _.bindAll = restArguments(function(obj, keys) {
++    keys = flatten(keys, false, false);
++    var index = keys.length;
++    if (index < 1) throw new Error('bindAll must be passed function names');
++    while (index--) {
++      var key = keys[index];
++      obj[key] = _.bind(obj[key], obj);
++    }
++  });
+ 
+   // Memoize an expensive function by storing its results.
+   _.memoize = function(func, hasher) {
+-    var memo = {};
+-    hasher || (hasher = _.identity);
+-    return function() {
+-      var key = hasher.apply(this, arguments);
+-      return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
++    var memoize = function(key) {
++      var cache = memoize.cache;
++      var address = '' + (hasher ? hasher.apply(this, arguments) : key);
++      if (!has(cache, address)) cache[address] = func.apply(this, arguments);
++      return cache[address];
+     };
++    memoize.cache = {};
++    return memoize;
+   };
+ 
+   // Delays a function for the given number of milliseconds, and then calls
+   // it with the arguments supplied.
+-  _.delay = function(func, wait) {
+-    var args = slice.call(arguments, 2);
+-    return setTimeout(function(){ return func.apply(null, args); }, wait);
+-  };
++  _.delay = restArguments(function(func, wait, args) {
++    return setTimeout(function() {
++      return func.apply(null, args);
++    }, wait);
++  });
+ 
+   // Defers a function, scheduling it to run after the current call stack has
+   // cleared.
+-  _.defer = function(func) {
+-    return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
+-  };
++  _.defer = _.partial(_.delay, _, 1);
+ 
+   // Returns a function, that, when invoked, will only be triggered at most once
+   // during a given window of time. Normally, the throttled function will run
+@@ -659,31 +840,44 @@
+   // but if you'd like to disable the execution on the leading edge, pass
+   // `{leading: false}`. To disable execution on the trailing edge, ditto.
+   _.throttle = function(func, wait, options) {
+-    var context, args, result;
+-    var timeout = null;
++    var timeout, context, args, result;
+     var previous = 0;
+-    options || (options = {});
++    if (!options) options = {};
++
+     var later = function() {
+-      previous = options.leading === false ? 0 : new Date;
++      previous = options.leading === false ? 0 : _.now();
+       timeout = null;
+       result = func.apply(context, args);
++      if (!timeout) context = args = null;
+     };
+-    return function() {
+-      var now = new Date;
++
++    var throttled = function() {
++      var now = _.now();
+       if (!previous && options.leading === false) previous = now;
+       var remaining = wait - (now - previous);
+       context = this;
+       args = arguments;
+-      if (remaining <= 0) {
+-        clearTimeout(timeout);
+-        timeout = null;
++      if (remaining <= 0 || remaining > wait) {
++        if (timeout) {
++          clearTimeout(timeout);
++          timeout = null;
++        }
+         previous = now;
+         result = func.apply(context, args);
++        if (!timeout) context = args = null;
+       } else if (!timeout && options.trailing !== false) {
+         timeout = setTimeout(later, remaining);
+       }
+       return result;
+     };
++
++    throttled.cancel = function() {
++      clearTimeout(timeout);
++      previous = 0;
++      timeout = context = args = null;
++    };
++
++    return throttled;
+   };
+ 
+   // Returns a function, that, as long as it continues to be invoked, will not
+@@ -691,67 +885,62 @@
+   // N milliseconds. If `immediate` is passed, trigger the function on the
+   // leading edge, instead of the trailing.
+   _.debounce = function(func, wait, immediate) {
+-    var timeout, args, context, timestamp, result;
+-    return function() {
+-      context = this;
+-      args = arguments;
+-      timestamp = new Date();
+-      var later = function() {
+-        var last = (new Date()) - timestamp;
+-        if (last < wait) {
+-          timeout = setTimeout(later, wait - last);
+-        } else {
+-          timeout = null;
+-          if (!immediate) result = func.apply(context, args);
+-        }
+-      };
+-      var callNow = immediate && !timeout;
+-      if (!timeout) {
++    var timeout, result;
++
++    var later = function(context, args) {
++      timeout = null;
++      if (args) result = func.apply(context, args);
++    };
++
++    var debounced = restArguments(function(args) {
++      if (timeout) clearTimeout(timeout);
++      if (immediate) {
++        var callNow = !timeout;
+         timeout = setTimeout(later, wait);
++        if (callNow) result = func.apply(this, args);
++      } else {
++        timeout = _.delay(later, wait, this, args);
+       }
+-      if (callNow) result = func.apply(context, args);
++
+       return result;
+-    };
+-  };
++    });
+ 
+-  // Returns a function that will be executed at most one time, no matter how
+-  // often you call it. Useful for lazy initialization.
+-  _.once = function(func) {
+-    var ran = false, memo;
+-    return function() {
+-      if (ran) return memo;
+-      ran = true;
+-      memo = func.apply(this, arguments);
+-      func = null;
+-      return memo;
++    debounced.cancel = function() {
++      clearTimeout(timeout);
++      timeout = null;
+     };
++
++    return debounced;
+   };
+ 
+   // Returns the first function passed as an argument to the second,
+   // allowing you to adjust arguments, run code before and after, and
+   // conditionally execute the original function.
+   _.wrap = function(func, wrapper) {
++    return _.partial(wrapper, func);
++  };
++
++  // Returns a negated version of the passed-in predicate.
++  _.negate = function(predicate) {
+     return function() {
+-      var args = [func];
+-      push.apply(args, arguments);
+-      return wrapper.apply(this, args);
++      return !predicate.apply(this, arguments);
+     };
+   };
+ 
+   // Returns a function that is the composition of a list of functions, each
+   // consuming the return value of the function that follows.
+   _.compose = function() {
+-    var funcs = arguments;
++    var args = arguments;
++    var start = args.length - 1;
+     return function() {
+-      var args = arguments;
+-      for (var i = funcs.length - 1; i >= 0; i--) {
+-        args = [funcs[i].apply(this, args)];
+-      }
+-      return args[0];
++      var i = start;
++      var result = args[start].apply(this, arguments);
++      while (i--) result = args[i].call(this, result);
++      return result;
+     };
+   };
+ 
+-  // Returns a function that will only be executed after being called N times.
++  // Returns a function that will only be executed on and after the Nth call.
+   _.after = function(times, func) {
+     return function() {
+       if (--times < 1) {
+@@ -760,15 +949,68 @@
+     };
+   };
+ 
++  // Returns a function that will only be executed up to (but not including) the Nth call.
++  _.before = function(times, func) {
++    var memo;
++    return function() {
++      if (--times > 0) {
++        memo = func.apply(this, arguments);
++      }
++      if (times <= 1) func = null;
++      return memo;
++    };
++  };
++
++  // Returns a function that will be executed at most one time, no matter how
++  // often you call it. Useful for lazy initialization.
++  _.once = _.partial(_.before, 2);
++
++  _.restArguments = restArguments;
++
+   // Object Functions
+   // ----------------
+ 
+-  // Retrieve the names of an object's properties.
+-  // Delegates to **ECMAScript 5**'s native `Object.keys`
+-  _.keys = nativeKeys || function(obj) {
+-    if (obj !== Object(obj)) throw new TypeError('Invalid object');
++  // Keys in IE < 9 that won't be iterated by `for key in ...` and thus missed.
++  var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString');
++  var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString',
++    'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString'];
++
++  var collectNonEnumProps = function(obj, keys) {
++    var nonEnumIdx = nonEnumerableProps.length;
++    var constructor = obj.constructor;
++    var proto = _.isFunction(constructor) && constructor.prototype || ObjProto;
++
++    // Constructor is a special case.
++    var prop = 'constructor';
++    if (has(obj, prop) && !_.contains(keys, prop)) keys.push(prop);
++
++    while (nonEnumIdx--) {
++      prop = nonEnumerableProps[nonEnumIdx];
++      if (prop in obj && obj[prop] !== proto[prop] && !_.contains(keys, prop)) {
++        keys.push(prop);
++      }
++    }
++  };
++
++  // Retrieve the names of an object's own properties.
++  // Delegates to **ECMAScript 5**'s native `Object.keys`.
++  _.keys = function(obj) {
++    if (!_.isObject(obj)) return [];
++    if (nativeKeys) return nativeKeys(obj);
+     var keys = [];
+-    for (var key in obj) if (_.has(obj, key)) keys.push(key);
++    for (var key in obj) if (has(obj, key)) keys.push(key);
++    // Ahem, IE < 9.
++    if (hasEnumBug) collectNonEnumProps(obj, keys);
++    return keys;
++  };
++
++  // Retrieve all the property names of an object.
++  _.allKeys = function(obj) {
++    if (!_.isObject(obj)) return [];
++    var keys = [];
++    for (var key in obj) keys.push(key);
++    // Ahem, IE < 9.
++    if (hasEnumBug) collectNonEnumProps(obj, keys);
+     return keys;
+   };
+ 
+@@ -776,18 +1018,33 @@
+   _.values = function(obj) {
+     var keys = _.keys(obj);
+     var length = keys.length;
+-    var values = new Array(length);
++    var values = Array(length);
+     for (var i = 0; i < length; i++) {
+       values[i] = obj[keys[i]];
+     }
+     return values;
+   };
+ 
++  // Returns the results of applying the iteratee to each element of the object.
++  // In contrast to _.map it returns an object.
++  _.mapObject = function(obj, iteratee, context) {
++    iteratee = cb(iteratee, context);
++    var keys = _.keys(obj),
++        length = keys.length,
++        results = {};
++    for (var index = 0; index < length; index++) {
++      var currentKey = keys[index];
++      results[currentKey] = iteratee(obj[currentKey], currentKey, obj);
++    }
++    return results;
++  };
++
+   // Convert an object into a list of `[key, value]` pairs.
++  // The opposite of _.object.
+   _.pairs = function(obj) {
+     var keys = _.keys(obj);
+     var length = keys.length;
+-    var pairs = new Array(length);
++    var pairs = Array(length);
+     for (var i = 0; i < length; i++) {
+       pairs[i] = [keys[i], obj[keys[i]]];
+     }
+@@ -805,7 +1062,7 @@
+   };
+ 
+   // Return a sorted list of the function names available on the object.
+-  // Aliased as `methods`
++  // Aliased as `methods`.
+   _.functions = _.methods = function(obj) {
+     var names = [];
+     for (var key in obj) {
+@@ -814,48 +1071,92 @@
+     return names.sort();
+   };
+ 
+-  // Extend a given object with all the properties in passed-in object(s).
+-  _.extend = function(obj) {
+-    each(slice.call(arguments, 1), function(source) {
+-      if (source) {
+-        for (var prop in source) {
+-          obj[prop] = source[prop];
++  // An internal function for creating assigner functions.
++  var createAssigner = function(keysFunc, defaults) {
++    return function(obj) {
++      var length = arguments.length;
++      if (defaults) obj = Object(obj);
++      if (length < 2 || obj == null) return obj;
++      for (var index = 1; index < length; index++) {
++        var source = arguments[index],
++            keys = keysFunc(source),
++            l = keys.length;
++        for (var i = 0; i < l; i++) {
++          var key = keys[i];
++          if (!defaults || obj[key] === void 0) obj[key] = source[key];
+         }
+       }
+-    });
+-    return obj;
++      return obj;
++    };
+   };
+ 
+-  // Return a copy of the object only containing the whitelisted properties.
+-  _.pick = function(obj) {
+-    var copy = {};
+-    var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
+-    each(keys, function(key) {
+-      if (key in obj) copy[key] = obj[key];
+-    });
+-    return copy;
+-  };
++  // Extend a given object with all the properties in passed-in object(s).
++  _.extend = createAssigner(_.allKeys);
+ 
+-   // Return a copy of the object without the blacklisted properties.
+-  _.omit = function(obj) {
+-    var copy = {};
+-    var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
+-    for (var key in obj) {
+-      if (!_.contains(keys, key)) copy[key] = obj[key];
++  // Assigns a given object with all the own properties in the passed-in object(s).
++  // (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
++  _.extendOwn = _.assign = createAssigner(_.keys);
++
++  // Returns the first key on an object that passes a predicate test.
++  _.findKey = function(obj, predicate, context) {
++    predicate = cb(predicate, context);
++    var keys = _.keys(obj), key;
++    for (var i = 0, length = keys.length; i < length; i++) {
++      key = keys[i];
++      if (predicate(obj[key], key, obj)) return key;
+     }
+-    return copy;
+   };
+ 
++  // Internal pick helper function to determine if `obj` has key `key`.
++  var keyInObj = function(value, key, obj) {
++    return key in obj;
++  };
++
++  // Return a copy of the object only containing the whitelisted properties.
++  _.pick = restArguments(function(obj, keys) {
++    var result = {}, iteratee = keys[0];
++    if (obj == null) return result;
++    if (_.isFunction(iteratee)) {
++      if (keys.length > 1) iteratee = optimizeCb(iteratee, keys[1]);
++      keys = _.allKeys(obj);
++    } else {
++      iteratee = keyInObj;
++      keys = flatten(keys, false, false);
++      obj = Object(obj);
++    }
++    for (var i = 0, length = keys.length; i < length; i++) {
++      var key = keys[i];
++      var value = obj[key];
++      if (iteratee(value, key, obj)) result[key] = value;
++    }
++    return result;
++  });
++
++  // Return a copy of the object without the blacklisted properties.
++  _.omit = restArguments(function(obj, keys) {
++    var iteratee = keys[0], context;
++    if (_.isFunction(iteratee)) {
++      iteratee = _.negate(iteratee);
++      if (keys.length > 1) context = keys[1];
++    } else {
++      keys = _.map(flatten(keys, false, false), String);
++      iteratee = function(value, key) {
++        return !_.contains(keys, key);
++      };
++    }
++    return _.pick(obj, iteratee, context);
++  });
++
+   // Fill in a given object with default properties.
+-  _.defaults = function(obj) {
+-    each(slice.call(arguments, 1), function(source) {
+-      if (source) {
+-        for (var prop in source) {
+-          if (obj[prop] === void 0) obj[prop] = source[prop];
+-        }
+-      }
+-    });
+-    return obj;
++  _.defaults = createAssigner(_.allKeys, true);
++
++  // Creates an object that inherits from the given prototype object.
++  // If additional properties are provided then they will be added to the
++  // created object.
++  _.create = function(prototype, props) {
++    var result = baseCreate(prototype);
++    if (props) _.extendOwn(result, props);
++    return result;
+   };
+ 
+   // Create a (shallow-cloned) duplicate of an object.
+@@ -872,109 +1173,136 @@
+     return obj;
+   };
+ 
++  // Returns whether an object has a given set of `key:value` pairs.
++  _.isMatch = function(object, attrs) {
++    var keys = _.keys(attrs), length = keys.length;
++    if (object == null) return !length;
++    var obj = Object(object);
++    for (var i = 0; i < length; i++) {
++      var key = keys[i];
++      if (attrs[key] !== obj[key] || !(key in obj)) return false;
++    }
++    return true;
++  };
++
++
+   // Internal recursive comparison function for `isEqual`.
+-  var eq = function(a, b, aStack, bStack) {
++  var eq, deepEq;
++  eq = function(a, b, aStack, bStack) {
+     // Identical objects are equal. `0 === -0`, but they aren't identical.
+-    // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
+-    if (a === b) return a !== 0 || 1 / a == 1 / b;
+-    // A strict comparison is necessary because `null == undefined`.
+-    if (a == null || b == null) return a === b;
++    // See the [Harmony `egal` proposal](https://wiki.ecmascript.org/doku.php?id=harmony:egal).
++    if (a === b) return a !== 0 || 1 / a === 1 / b;
++    // `null` or `undefined` only equal to itself (strict comparison).
++    if (a == null || b == null) return false;
++    // `NaN`s are equivalent, but non-reflexive.
++    if (a !== a) return b !== b;
++    // Exhaust primitive checks
++    var type = typeof a;
++    if (type !== 'function' && type !== 'object' && typeof b != 'object') return false;
++    return deepEq(a, b, aStack, bStack);
++  };
++
++  // Internal recursive comparison function for `isEqual`.
++  deepEq = function(a, b, aStack, bStack) {
+     // Unwrap any wrapped objects.
+     if (a instanceof _) a = a._wrapped;
+     if (b instanceof _) b = b._wrapped;
+     // Compare `[[Class]]` names.
+     var className = toString.call(a);
+-    if (className != toString.call(b)) return false;
++    if (className !== toString.call(b)) return false;
+     switch (className) {
+-      // Strings, numbers, dates, and booleans are compared by value.
++      // Strings, numbers, regular expressions, dates, and booleans are compared by value.
++      case '[object RegExp]':
++      // RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i')
+       case '[object String]':
+         // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
+         // equivalent to `new String("5")`.
+-        return a == String(b);
++        return '' + a === '' + b;
+       case '[object Number]':
+-        // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
+-        // other numeric values.
+-        return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
++        // `NaN`s are equivalent, but non-reflexive.
++        // Object(NaN) is equivalent to NaN.
++        if (+a !== +a) return +b !== +b;
++        // An `egal` comparison is performed for other numeric values.
++        return +a === 0 ? 1 / +a === 1 / b : +a === +b;
+       case '[object Date]':
+       case '[object Boolean]':
+         // Coerce dates and booleans to numeric primitive values. Dates are compared by their
+         // millisecond representations. Note that invalid dates with millisecond representations
+         // of `NaN` are not equivalent.
+-        return +a == +b;
+-      // RegExps are compared by their source patterns and flags.
+-      case '[object RegExp]':
+-        return a.source == b.source &&
+-               a.global == b.global &&
+-               a.multiline == b.multiline &&
+-               a.ignoreCase == b.ignoreCase;
++        return +a === +b;
++      case '[object Symbol]':
++        return SymbolProto.valueOf.call(a) === SymbolProto.valueOf.call(b);
++    }
++
++    var areArrays = className === '[object Array]';
++    if (!areArrays) {
++      if (typeof a != 'object' || typeof b != 'object') return false;
++
++      // Objects with different constructors are not equivalent, but `Object`s or `Array`s
++      // from different frames are.
++      var aCtor = a.constructor, bCtor = b.constructor;
++      if (aCtor !== bCtor && !(_.isFunction(aCtor) && aCtor instanceof aCtor &&
++                               _.isFunction(bCtor) && bCtor instanceof bCtor)
++                          && ('constructor' in a && 'constructor' in b)) {
++        return false;
++      }
+     }
+-    if (typeof a != 'object' || typeof b != 'object') return false;
+     // Assume equality for cyclic structures. The algorithm for detecting cyclic
+     // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
++
++    // Initializing stack of traversed objects.
++    // It's done here since we only need them for objects and arrays comparison.
++    aStack = aStack || [];
++    bStack = bStack || [];
+     var length = aStack.length;
+     while (length--) {
+       // Linear search. Performance is inversely proportional to the number of
+       // unique nested structures.
+-      if (aStack[length] == a) return bStack[length] == b;
+-    }
+-    // Objects with different constructors are not equivalent, but `Object`s
+-    // from different frames are.
+-    var aCtor = a.constructor, bCtor = b.constructor;
+-    if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) &&
+-                             _.isFunction(bCtor) && (bCtor instanceof bCtor))) {
+-      return false;
++      if (aStack[length] === a) return bStack[length] === b;
+     }
++
+     // Add the first object to the stack of traversed objects.
+     aStack.push(a);
+     bStack.push(b);
+-    var size = 0, result = true;
++
+     // Recursively compare objects and arrays.
+-    if (className == '[object Array]') {
++    if (areArrays) {
+       // Compare array lengths to determine if a deep comparison is necessary.
+-      size = a.length;
+-      result = size == b.length;
+-      if (result) {
+-        // Deep compare the contents, ignoring non-numeric properties.
+-        while (size--) {
+-          if (!(result = eq(a[size], b[size], aStack, bStack))) break;
+-        }
++      length = a.length;
++      if (length !== b.length) return false;
++      // Deep compare the contents, ignoring non-numeric properties.
++      while (length--) {
++        if (!eq(a[length], b[length], aStack, bStack)) return false;
+       }
+     } else {
+       // Deep compare objects.
+-      for (var key in a) {
+-        if (_.has(a, key)) {
+-          // Count the expected number of properties.
+-          size++;
+-          // Deep compare each member.
+-          if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break;
+-        }
+-      }
+-      // Ensure that both objects contain the same number of properties.
+-      if (result) {
+-        for (key in b) {
+-          if (_.has(b, key) && !(size--)) break;
+-        }
+-        result = !size;
++      var keys = _.keys(a), key;
++      length = keys.length;
++      // Ensure that both objects contain the same number of properties before comparing deep equality.
++      if (_.keys(b).length !== length) return false;
++      while (length--) {
++        // Deep compare each member
++        key = keys[length];
++        if (!(has(b, key) && eq(a[key], b[key], aStack, bStack))) return false;
+       }
+     }
+     // Remove the first object from the stack of traversed objects.
+     aStack.pop();
+     bStack.pop();
+-    return result;
++    return true;
+   };
+ 
+   // Perform a deep comparison to check if two objects are equal.
+   _.isEqual = function(a, b) {
+-    return eq(a, b, [], []);
++    return eq(a, b);
+   };
+ 
+   // Is a given array, string, or object empty?
+   // An "empty" object has no enumerable own-properties.
+   _.isEmpty = function(obj) {
+     if (obj == null) return true;
+-    if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
+-    for (var key in obj) if (_.has(obj, key)) return false;
+-    return true;
++    if (isArrayLike(obj) && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj))) return obj.length === 0;
++    return _.keys(obj).length === 0;
+   };
+ 
+   // Is a given value a DOM element?
+@@ -985,49 +1313,52 @@
+   // Is a given value an array?
+   // Delegates to ECMA5's native Array.isArray
+   _.isArray = nativeIsArray || function(obj) {
+-    return toString.call(obj) == '[object Array]';
++    return toString.call(obj) === '[object Array]';
+   };
+ 
+   // Is a given variable an object?
+   _.isObject = function(obj) {
+-    return obj === Object(obj);
++    var type = typeof obj;
++    return type === 'function' || type === 'object' && !!obj;
+   };
+ 
+-  // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.
+-  each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
++  // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError, isMap, isWeakMap, isSet, isWeakSet.
++  _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error', 'Symbol', 'Map', 'WeakMap', 'Set', 'WeakSet'], function(name) {
+     _['is' + name] = function(obj) {
+-      return toString.call(obj) == '[object ' + name + ']';
++      return toString.call(obj) === '[object ' + name + ']';
+     };
+   });
+ 
+-  // Define a fallback version of the method in browsers (ahem, IE), where
++  // Define a fallback version of the method in browsers (ahem, IE < 9), where
+   // there isn't any inspectable "Arguments" type.
+   if (!_.isArguments(arguments)) {
+     _.isArguments = function(obj) {
+-      return !!(obj && _.has(obj, 'callee'));
++      return has(obj, 'callee');
+     };
+   }
+ 
+-  // Optimize `isFunction` if appropriate.
+-  if (typeof (/./) !== 'function') {
++  // Optimize `isFunction` if appropriate. Work around some typeof bugs in old v8,
++  // IE 11 (#1621), Safari 8 (#1929), and PhantomJS (#2236).
++  var nodelist = root.document && root.document.childNodes;
++  if (typeof /./ != 'function' && typeof Int8Array != 'object' && typeof nodelist != 'function') {
+     _.isFunction = function(obj) {
+-      return typeof obj === 'function';
++      return typeof obj == 'function' || false;
+     };
+   }
+ 
+   // Is a given object a finite number?
+   _.isFinite = function(obj) {
+-    return isFinite(obj) && !isNaN(parseFloat(obj));
++    return !_.isSymbol(obj) && isFinite(obj) && !isNaN(parseFloat(obj));
+   };
+ 
+-  // Is the given value `NaN`? (NaN is the only number which does not equal itself).
++  // Is the given value `NaN`?
+   _.isNaN = function(obj) {
+-    return _.isNumber(obj) && obj != +obj;
++    return _.isNumber(obj) && isNaN(obj);
+   };
+ 
+   // Is a given value a boolean?
+   _.isBoolean = function(obj) {
+-    return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
++    return obj === true || obj === false || toString.call(obj) === '[object Boolean]';
+   };
+ 
+   // Is a given value equal to null?
+@@ -1042,8 +1373,19 @@
+ 
+   // Shortcut function for checking if an object has a given property directly
+   // on itself (in other words, not on a prototype).
+-  _.has = function(obj, key) {
+-    return hasOwnProperty.call(obj, key);
++  _.has = function(obj, path) {
++    if (!_.isArray(path)) {
++      return has(obj, path);
++    }
++    var length = path.length;
++    for (var i = 0; i < length; i++) {
++      var key = path[i];
++      if (obj == null || !hasOwnProperty.call(obj, key)) {
++        return false;
++      }
++      obj = obj[key];
++    }
++    return !!length;
+   };
+ 
+   // Utility Functions
+@@ -1056,15 +1398,55 @@
+     return this;
+   };
+ 
+-  // Keep the identity function around for default iterators.
++  // Keep the identity function around for default iteratees.
+   _.identity = function(value) {
+     return value;
+   };
+ 
++  // Predicate-generating functions. Often useful outside of Underscore.
++  _.constant = function(value) {
++    return function() {
++      return value;
++    };
++  };
++
++  _.noop = function(){};
++
++  // Creates a function that, when passed an object, will traverse that object’s
++  // properties down the given `path`, specified as an array of keys or indexes.
++  _.property = function(path) {
++    if (!_.isArray(path)) {
++      return shallowProperty(path);
++    }
++    return function(obj) {
++      return deepGet(obj, path);
++    };
++  };
++
++  // Generates a function for a given object that returns a given property.
++  _.propertyOf = function(obj) {
++    if (obj == null) {
++      return function(){};
++    }
++    return function(path) {
++      return !_.isArray(path) ? obj[path] : deepGet(obj, path);
++    };
++  };
++
++  // Returns a predicate for checking whether an object has a given set of
++  // `key:value` pairs.
++  _.matcher = _.matches = function(attrs) {
++    attrs = _.extendOwn({}, attrs);
++    return function(obj) {
++      return _.isMatch(obj, attrs);
++    };
++  };
++
+   // Run a function **n** times.
+-  _.times = function(n, iterator, context) {
++  _.times = function(n, iteratee, context) {
+     var accum = Array(Math.max(0, n));
+-    for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i);
++    iteratee = optimizeCb(iteratee, context, 1);
++    for (var i = 0; i < n; i++) accum[i] = iteratee(i);
+     return accum;
+   };
+ 
+@@ -1077,52 +1459,57 @@
+     return min + Math.floor(Math.random() * (max - min + 1));
+   };
+ 
+-  // List of HTML entities for escaping.
+-  var entityMap = {
+-    escape: {
+-      '&': '&amp;',
+-      '<': '&lt;',
+-      '>': '&gt;',
+-      '"': '&quot;',
+-      "'": '&#x27;'
+-    }
++  // A (possibly faster) way to get the current timestamp as an integer.
++  _.now = Date.now || function() {
++    return new Date().getTime();
+   };
+-  entityMap.unescape = _.invert(entityMap.escape);
+ 
+-  // Regexes containing the keys and values listed immediately above.
+-  var entityRegexes = {
+-    escape:   new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'),
+-    unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g')
++  // List of HTML entities for escaping.
++  var escapeMap = {
++    '&': '&amp;',
++    '<': '&lt;',
++    '>': '&gt;',
++    '"': '&quot;',
++    "'": '&#x27;',
++    '`': '&#x60;'
+   };
++  var unescapeMap = _.invert(escapeMap);
+ 
+   // Functions for escaping and unescaping strings to/from HTML interpolation.
+-  _.each(['escape', 'unescape'], function(method) {
+-    _[method] = function(string) {
+-      if (string == null) return '';
+-      return ('' + string).replace(entityRegexes[method], function(match) {
+-        return entityMap[method][match];
+-      });
++  var createEscaper = function(map) {
++    var escaper = function(match) {
++      return map[match];
++    };
++    // Regexes for identifying a key that needs to be escaped.
++    var source = '(?:' + _.keys(map).join('|') + ')';
++    var testRegexp = RegExp(source);
++    var replaceRegexp = RegExp(source, 'g');
++    return function(string) {
++      string = string == null ? '' : '' + string;
++      return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string;
+     };
+-  });
+-
+-  // If the value of the named `property` is a function then invoke it with the
+-  // `object` as context; otherwise, return it.
+-  _.result = function(object, property) {
+-    if (object == null) return void 0;
+-    var value = object[property];
+-    return _.isFunction(value) ? value.call(object) : value;
+   };
+-
+-  // Add your own custom functions to the Underscore object.
+-  _.mixin = function(obj) {
+-    each(_.functions(obj), function(name) {
+-      var func = _[name] = obj[name];
+-      _.prototype[name] = function() {
+-        var args = [this._wrapped];
+-        push.apply(args, arguments);
+-        return result.call(this, func.apply(_, args));
+-      };
+-    });
++  _.escape = createEscaper(escapeMap);
++  _.unescape = createEscaper(unescapeMap);
++
++  // Traverses the children of `obj` along `path`. If a child is a function, it
++  // is invoked with its parent as context. Returns the value of the final
++  // child, or `fallback` if any child is undefined.
++  _.result = function(obj, path, fallback) {
++    if (!_.isArray(path)) path = [path];
++    var length = path.length;
++    if (!length) {
++      return _.isFunction(fallback) ? fallback.call(obj) : fallback;
++    }
++    for (var i = 0; i < length; i++) {
++      var prop = obj == null ? void 0 : obj[path[i]];
++      if (prop === void 0) {
++        prop = fallback;
++        i = length; // Ensure we don't continue iterating.
++      }
++      obj = _.isFunction(prop) ? prop.call(obj) : prop;
++    }
++    return obj;
+   };
+ 
+   // Generate a unique integer id (unique within the entire client session).
+@@ -1136,9 +1523,9 @@
+   // By default, Underscore uses ERB-style template delimiters, change the
+   // following template settings to use alternative delimiters.
+   _.templateSettings = {
+-    evaluate    : /<%([\s\S]+?)%>/g,
+-    interpolate : /<%=([\s\S]+?)%>/g,
+-    escape      : /<%-([\s\S]+?)%>/g
++    evaluate: /<%([\s\S]+?)%>/g,
++    interpolate: /<%=([\s\S]+?)%>/g,
++    escape: /<%-([\s\S]+?)%>/g
+   };
+ 
+   // When customizing `templateSettings`, if you don't want to define an
+@@ -1149,26 +1536,30 @@
+   // Certain characters need to be escaped so that they can be put into a
+   // string literal.
+   var escapes = {
+-    "'":      "'",
+-    '\\':     '\\',
+-    '\r':     'r',
+-    '\n':     'n',
+-    '\t':     't',
++    "'": "'",
++    '\\': '\\',
++    '\r': 'r',
++    '\n': 'n',
+     '\u2028': 'u2028',
+     '\u2029': 'u2029'
+   };
+ 
+-  var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
++  var escapeRegExp = /\\|'|\r|\n|\u2028|\u2029/g;
++
++  var escapeChar = function(match) {
++    return '\\' + escapes[match];
++  };
+ 
+   // JavaScript micro-templating, similar to John Resig's implementation.
+   // Underscore templating handles arbitrary delimiters, preserves whitespace,
+   // and correctly escapes quotes within interpolated code.
+-  _.template = function(text, data, settings) {
+-    var render;
++  // NB: `oldSettings` only exists for backwards compatibility.
++  _.template = function(text, settings, oldSettings) {
++    if (!settings && oldSettings) settings = oldSettings;
+     settings = _.defaults({}, settings, _.templateSettings);
+ 
+     // Combine delimiters into one regular expression via alternation.
+-    var matcher = new RegExp([
++    var matcher = RegExp([
+       (settings.escape || noMatch).source,
+       (settings.interpolate || noMatch).source,
+       (settings.evaluate || noMatch).source
+@@ -1178,19 +1569,18 @@
+     var index = 0;
+     var source = "__p+='";
+     text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
+-      source += text.slice(index, offset)
+-        .replace(escaper, function(match) { return '\\' + escapes[match]; });
++      source += text.slice(index, offset).replace(escapeRegExp, escapeChar);
++      index = offset + match.length;
+ 
+       if (escape) {
+         source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
+-      }
+-      if (interpolate) {
++      } else if (interpolate) {
+         source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
+-      }
+-      if (evaluate) {
++      } else if (evaluate) {
+         source += "';\n" + evaluate + "\n__p+='";
+       }
+-      index = offset + match.length;
++
++      // Adobe VMs need the match returned to produce the correct offset.
+       return match;
+     });
+     source += "';\n";
+@@ -1200,8 +1590,9 @@
+ 
+     source = "var __t,__p='',__j=Array.prototype.join," +
+       "print=function(){__p+=__j.call(arguments,'');};\n" +
+-      source + "return __p;\n";
++      source + 'return __p;\n';
+ 
++    var render;
+     try {
+       render = new Function(settings.variable || 'obj', '_', source);
+     } catch (e) {
+@@ -1209,20 +1600,22 @@
+       throw e;
+     }
+ 
+-    if (data) return render(data, _);
+     var template = function(data) {
+       return render.call(this, data, _);
+     };
+ 
+-    // Provide the compiled function source as a convenience for precompilation.
+-    template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
++    // Provide the compiled source as a convenience for precompilation.
++    var argument = settings.variable || 'obj';
++    template.source = 'function(' + argument + '){\n' + source + '}';
+ 
+     return template;
+   };
+ 
+-  // Add a "chain" function, which will delegate to the wrapper.
++  // Add a "chain" function. Start chaining a wrapped Underscore object.
+   _.chain = function(obj) {
+-    return _(obj).chain();
++    var instance = _(obj);
++    instance._chain = true;
++    return instance;
+   };
+ 
+   // OOP
+@@ -1232,45 +1625,68 @@
+   // underscore functions. Wrapped objects may be chained.
+ 
+   // Helper function to continue chaining intermediate results.
+-  var result = function(obj) {
+-    return this._chain ? _(obj).chain() : obj;
++  var chainResult = function(instance, obj) {
++    return instance._chain ? _(obj).chain() : obj;
++  };
++
++  // Add your own custom functions to the Underscore object.
++  _.mixin = function(obj) {
++    _.each(_.functions(obj), function(name) {
++      var func = _[name] = obj[name];
++      _.prototype[name] = function() {
++        var args = [this._wrapped];
++        push.apply(args, arguments);
++        return chainResult(this, func.apply(_, args));
++      };
++    });
++    return _;
+   };
+ 
+   // Add all of the Underscore functions to the wrapper object.
+   _.mixin(_);
+ 
+   // Add all mutator Array functions to the wrapper.
+-  each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
++  _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
+     var method = ArrayProto[name];
+     _.prototype[name] = function() {
+       var obj = this._wrapped;
+       method.apply(obj, arguments);
+-      if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0];
+-      return result.call(this, obj);
++      if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0];
++      return chainResult(this, obj);
+     };
+   });
+ 
+   // Add all accessor Array functions to the wrapper.
+-  each(['concat', 'join', 'slice'], function(name) {
++  _.each(['concat', 'join', 'slice'], function(name) {
+     var method = ArrayProto[name];
+     _.prototype[name] = function() {
+-      return result.call(this, method.apply(this._wrapped, arguments));
++      return chainResult(this, method.apply(this._wrapped, arguments));
+     };
+   });
+ 
+-  _.extend(_.prototype, {
++  // Extracts the result from a wrapped and chained object.
++  _.prototype.value = function() {
++    return this._wrapped;
++  };
+ 
+-    // Start chaining a wrapped Underscore object.
+-    chain: function() {
+-      this._chain = true;
+-      return this;
+-    },
++  // Provide unwrapping proxy for some methods used in engine operations
++  // such as arithmetic and JSON stringification.
++  _.prototype.valueOf = _.prototype.toJSON = _.prototype.value;
+ 
+-    // Extracts the result from a wrapped and chained object.
+-    value: function() {
+-      return this._wrapped;
+-    }
+-
+-  });
++  _.prototype.toString = function() {
++    return String(this._wrapped);
++  };
+ 
+-}).call(this);
++  // AMD registration happens at the end for compatibility with AMD loaders
++  // that may not enforce next-turn semantics on modules. Even though general
++  // practice for AMD registration is to be anonymous, underscore registers
++  // as a named module because, like jQuery, it is a base library that is
++  // popular enough to be bundled in a third party lib, but not be part of
++  // an AMD load request. Those cases could generate an error when an
++  // anonymous define() is called outside of a loader request.
++  if (typeof define == 'function' && define.amd) {
++    define('underscore', [], function() {
++      return _;
++    });
++  }
++}());
+-- 
+1.8.3.1
+
+
+From 915613962e54f3449dc15433f30c48534d561b06 Mon Sep 17 00:00:00 2001
+From: Alexander Scheel <ascheel@redhat.com>
+Date: Mon, 16 Mar 2020 12:01:39 -0400
+Subject: [PATCH 12/17] Update to patternfly v3.59.3
+
+Signed-off-by: Alexander Scheel <ascheel@redhat.com>
+(cherry picked from commit 57b93b64dd3f026169644cd9745148ea377afe84)
+---
+ base/server/share/webapps/pki/js/patternfly.js | 1867 ++++++++++++++++++++++--
+ 1 file changed, 1760 insertions(+), 107 deletions(-)
+
+diff --git a/base/server/share/webapps/pki/js/patternfly.js b/base/server/share/webapps/pki/js/patternfly.js
+index 5002cb8..4cd59c9 100644
+--- a/base/server/share/webapps/pki/js/patternfly.js
++++ b/base/server/share/webapps/pki/js/patternfly.js
+@@ -1,46 +1,692 @@
+-// PatternFly Namespace
+-var PatternFly = PatternFly || {};
+-
+-$(document).ready(function() {
+-  // Call sidebar() on ready if .sidebar-pf exists and .datatable does not exist
+-  if ($('.sidebar-pf').length > 0 && $('.datatable').length == 0) {
+-  //if ($('.sidebar-pf').length > 0) {
+-    sidebar();
+-  }
+-});
++(function (window) {
++  'use strict';
++
++  var patternfly = {
++    version: "3.59.2"
++  };
++
++  // definition of breakpoint sizes for tablet and desktop modes
++  patternfly.pfBreakpoints = {
++    'tablet': 768,
++    'desktop': 1200
++  };
++
++  window.patternfly = patternfly;
++
++})(typeof window !== 'undefined' ? window : global);
++
++(function (window) {
++  'use strict';
++
++  // Ensure we are assigning these to the patternfly property of the window argument, and not the implicit global patternfly
++  var patternfly = window.patternfly;
++
++  // Util: PatternFly Palette colors
++  patternfly.pfPaletteColors = {
++    black:         '#030303',
++    black100:      '#fafafa',
++    black200:      '#ededed',
++    black300:      '#d1d1d1',
++    black400:      '#bbbbbb',
++    black500:      '#8b8d8f',
++    black600:      '#72767b',
++    black700:      '#4d5258',
++    black800:      '#393f44',
++    black900:      '#292e34',
++    blue:          '#0088ce',
++    blue100:       '#bee1f4',
++    blue200:       '#7dc3e8',
++    blue300:       '#39a5dc',
++    blue400:       '#0088ce',
++    blue500:       '#00659c',
++    blue600:       '#004368',
++    blue700:       '#002235',
++    gold:          '#f0ab00',
++    gold100:       '#fbeabc',
++    gold200:       '#f9d67a',
++    gold300:       '#f5c12e',
++    gold400:       '#f0ab00',
++    gold500:       '#b58100',
++    gold600:       '#795600',
++    gold700:       '#3d2c00',
++    orange:        '#ec7a08',
++    orange100:     '#fbdebf',
++    orange200:     '#f7bd7f',
++    orange300:     '#f39d3c',
++    orange400:     '#ec7a08',
++    orange500:     '#b35c00',
++    orange600:     '#773d00',
++    orange700:     '#3b1f00',
++    lightBlue:     '#00b9e4',
++    lightBlue100:  '#beedf9',
++    lightBlue200:  '#7cdbf3',
++    lightBlue300:  '#35caed',
++    lightBlue400:  '#00b9e4',
++    lightBlue500:  '#008bad',
++    lightBlue600:  '#005c73',
++    lightBlue700:  '#002d39',
++    green:         '#3f9c35',
++    green100:      '#cfe7cd',
++    green200:      '#9ecf99',
++    green300:      '#6ec664',
++    green400:      '#3f9c35',
++    green500:      '#2d7623',
++    green600:      '#1e4f18',
++    green700:      '#0f280d',
++    lightGreen:    '#92d400',
++    lightGreen100: '#e4f5bc',
++    lightGreen200: '#c8eb79',
++    lightGreen300: '#ace12e',
++    lightGreen400: '#92d400',
++    lightGreen500: '#6ca100',
++    lightGreen600: '#486b00',
++    lightGreen700: '#253600',
++    cyan:          '#007a87',
++    cyan100:       '#bedee1',
++    cyan200:       '#7dbdc3',
++    cyan300:       '#3a9ca6',
++    cyan400:       '#007a87',
++    cyan500:       '#005c66',
++    cyan600:       '#003d44',
++    cyan700:       '#001f22',
++    purple:        '#703fec',
++    purple100:     '#c7bfff',
++    purple200:     '#a18fff',
++    purple300:     '#8461f7',
++    purple400:     '#703fec',
++    purple500:     '#582fc0',
++    purple600:     '#40199a',
++    purple700:     '#1f0066',
++    red:           '#cc0000',
++    red100:        '#cc0000',
++    red200:        '#a30000',
++    red300:        '#8b0000',
++    red400:        '#470000',
++    red500:        '#2c0000'
++  };
++})(typeof window !== 'undefined' ? window : global);
++
++
++(function (window) {
++  'use strict';
++
++  // Ensure we are assigning these to the patternfly property of the window argument, and not the implicit global patternfly
++  var patternfly = window.patternfly;
++
++  // Util: PatternFly C3 Chart Defaults
++  patternfly.pfSetDonutChartTitle = function (selector, primary, secondary) {
++    var donutChartRightTitle = window.d3.select(selector).select('text.c3-chart-arcs-title');
++    donutChartRightTitle.text("");
++    donutChartRightTitle.insert('tspan').text(primary).classed('donut-title-big-pf', true).attr('y', 0).attr('x', 0);
++    donutChartRightTitle.insert('tspan').text(secondary).classed('donut-title-small-pf', true).attr('y', 20).attr('x', 0);
++  };
++
++  patternfly.pfDonutTooltipContents = function (d, defaultTitleFormat, defaultValueFormat, color) {
++    return '<table class="c3-tooltip">' +
++      '  <tr>' +
++      '    <td><span style="background-color:' + color(d[0].id) + '"></span>' + '<strong>' + d[0].value + '</strong> ' + d[0].name + '</td>' +
++      '    <td>' + (Math.round(d[0].ratio * 1000) / 10) + '%</td>' +
++      '  </tr>' +
++      '</table>';
++  };
++
++  patternfly.pfGetUtilizationDonutTooltipContentsFn = function (units) {
++    return function (d) {
++      return '<span class="donut-tooltip-pf" style="white-space: nowrap;">' +
++        (Math.round(d[0].ratio * 1000) / 10) + '%' + ' ' + units + ' ' + d[0].name +
++        '</span>';
++    };
++  };
++
++  patternfly.pfGetBarChartTooltipContentsFn = function (categories) {
++    return function (d) {
++      var name = categories ? categories[d[0].index] : d[0].index;
++      return '<table class="c3-tooltip">' +
++        '  <tr>' +
++        '    <td><strong>' + name + ':</td>' +
++        '    <td>' + d[0].value + '</td>' +
++        '  </tr>' +
++        '</table>';
++    };
++  };
++
++  patternfly.pfSingleLineChartTooltipContentsFn = function (categories) {
++    return function (d) {
++      var name = categories ? categories[d[0].index] : d[0].index;
++      return '<table class="c3-tooltip">' +
++        '  <tr>' +
++        '    <td><strong>' + name + ':</td>' +
++        '    <td>' + d[0].value + '</td>' +
++        '  </tr>' +
++        '</table>';
++    };
++  };
++
++  patternfly.pfPieTooltipContents = function (d, defaultTitleFormat, defaultValueFormat, color) {
++    return patternfly.pfDonutTooltipContents(d, defaultTitleFormat, defaultValueFormat, color);
++  };
++
++  patternfly.c3ChartDefaults = function () {
++    var
++      getDefaultColors = function () {
++        return {
++          pattern: [
++            patternfly.pfPaletteColors.blue,
++            patternfly.pfPaletteColors.blue300,
++            patternfly.pfPaletteColors.green,
++            patternfly.pfPaletteColors.orange,
++            patternfly.pfPaletteColors.red
++          ]
++        };
++      },
++      getDefaultBarGrid = function () {
++        return {
++          y: {
++            show: true
++          }
++        };
++      },
++      getDefaultBarTooltip = function (categories) {
++        return {
++          contents: patternfly.pfGetBarChartTooltipContentsFn(categories)
++        };
++      },
++      getDefaultBarLegend = function () {
++        return {
++          show: false
++        };
++      },
++      getDefaultBarConfig = function (categories) {
++        return {
++          color: this.getDefaultColors(),
++          grid: this.getDefaultBarGrid(),
++          tooltip: this.getDefaultBarTooltip(categories),
++          legend: this.getDefaultBarLegend()
++        };
++      },
++      getDefaultGroupedBarGrid = function () {
++        return {
++          y: {
++            show: true
++          }
++        };
++      },
++      getDefaultGroupedBarLegend = function () {
++        return {
++          show: true,
++          position: 'bottom'
++        };
++      },
++      getDefaultGroupedBarConfig = function () {
++        return {
++          color: this.getDefaultColors(),
++          grid: this.getDefaultGroupedBarGrid(),
++          legend: this.getDefaultGroupedBarLegend()
++        };
++      },
++      getDefaultStackedBarGrid = function () {
++        return {
++          y: {
++            show: true
++          }
++        };
++      },
++      getDefaultStackedBarLegend = function () {
++        return {
++          show: true,
++          position: 'bottom'
++        };
++      },
++      getDefaultStackedBarConfig = function () {
++        return {
++          color: this.getDefaultColors(),
++          grid: this.getDefaultStackedBarGrid(),
++          legend: this.getDefaultStackedBarLegend()
++        };
++      },
++      getDefaultDonut = function (title) {
++        return {
++          title: title,
++          label: {
++            show: false
++          },
++          width: 11
++        };
++      },
++      getDefaultDonutSize = function () {
++        return {
++          height: 171 // produces a diameter of 150 and a centered chart when there is no legend
++          // Don't set a width here, the default is to center horizontally in the parent container
++        };
++      },
++      getDefaultDonutColors = function () {
++        return {
++          pattern: [
++            patternfly.pfPaletteColors.blue,
++            patternfly.pfPaletteColors.black300
++          ]
++        };
++      },
++      getDefaultRelationshipDonutColors = function () {
++        return {
++          pattern: [
++            patternfly.pfPaletteColors.blue,
++            patternfly.pfPaletteColors.red100,
++            patternfly.pfPaletteColors.orange400,
++            patternfly.pfPaletteColors.green400,
++            patternfly.pfPaletteColors.cyan500,
++            patternfly.pfPaletteColors.gold200,
++          ]
++        };
++      },
++      getDefaultDonutTooltip = function () {
++        return {
++          show: false
++        };
++      },
++      getDefaultDonutLegend = function () {
++        return {
++          show: false
++        };
++      },
++      getDefaultDonutConfig = function (title) {
++        return {
++          donut: this.getDefaultDonut(title),
++          size: this.getDefaultDonutSize(),
++          legend: this.getDefaultDonutLegend(),
++          color: this.getDefaultDonutColors(),
++          tooltip: this.getDefaultDonutTooltip()
++        };
++      },
++      getDefaultRelationshipDonutConfig = function (title) {
++        return {
++          donut: this.getDefaultDonut(title),
++          size: this.getDefaultDonutSize(),
++          legend: this.getDefaultDonutLegend(),
++          color: this.getDefaultRelationshipDonutColors(),
++          tooltip: this.getDefaultDonutTooltip()
++        };
++      },
++      getDefaultPie = function () {
++        return {
++          expand: true,
++          label: {
++            show: false
++          }
++        };
++      },
++      getDefaultPieSize = function () {
++        return {
++          height: 171 // produces a diameter of 150 and a centered chart when there is no legend
++          // Don't set a width here, default is to center horizontally in the parent container
++        };
++      },
++      getDefaultPieColors = function () {
++        return {
++          pattern: [
++            patternfly.pfPaletteColors.blue,
++            patternfly.pfPaletteColors.black300
++          ]
++        };
++      },
++      getDefaultPieTooltip = function () {
++        return {
++          contents: patternfly.pfPieTooltipContents
++        };
++      },
++      getDefaultPieLegend = function () {
++        return {
++          show: false
++        };
++      },
++      getDefaultPieConfig = function () {
++        return {
++          pie: this.getDefaultPie(),
++          size: this.getDefaultPieSize(),
++          legend: this.getDefaultPieLegend(),
++          color: this.getDefaultPieColors(),
++          tooltip: this.getDefaultPieTooltip()
++        };
++      },
++      getDefaultSparklineArea = function () {
++        return {
++          zerobased: true
++        };
++      },
++      getDefaultSparklineSize = function () {
++        return {
++          height: 60
++        };
++      },
++      getDefaultSparklineAxis = function () {
++        return {
++          x: {
++            show: false
++          },
++          y: {
++            show: false
++          }
++        };
++      },
++      getDefaultSparklineLegend = function () {
++        return {
++          show: false
++        };
++      },
++      getDefaultSparklinePoint = function () {
++        return {
++          r: 1,
++          focus: {
++            expand: {
++              r: 4
++            }
++          }
++        };
++      },
++      getDefaultSparklineTooltip = function () {
++        return {
++          // because a sparkline should only contain a single data column,
++          // the tooltip will only work for a single data column
++          contents: function (d) {
++            return '<span class="c3-tooltip-sparkline">' + d[0].value + ' ' + d[0].name + '</span>';
++          }
++        };
++      },
++      getDefaultSparklineConfig = function () {
++        return {
++          area: getDefaultSparklineArea(),
++          size: getDefaultSparklineSize(),
++          axis: getDefaultSparklineAxis(),
++          color: getDefaultColors(),
++          legend: getDefaultSparklineLegend(),
++          point: getDefaultSparklinePoint(),
++          tooltip: getDefaultSparklineTooltip()
++        };
++      },
++      getDefaultLineAxis = function () {
++        return {
++          x: {
++            show: true
++          },
++          y: {
++            show: true
++          }
++        };
++      },
++      getDefaultLineGrid = function () {
++        return {
++          x: {
++            show: false
++          },
++          y: {
++            show: true
++          }
++        };
++      },
++      getDefaultLineLegend = function () {
++        return {
++          show: true
++        };
++      },
++      getDefaultLinePoint = function () {
++        return {
++          r: 3,
++          focus: {
++            expand: {
++              r: 5
++            }
++          }
++        };
++      },
++      getDefaultLineConfig = function () {
++        return {
++          axis: getDefaultLineAxis(),
++          grid: getDefaultLineGrid(),
++          color: getDefaultColors(),
++          legend: getDefaultLineLegend(),
++          point: getDefaultLinePoint()
++        };
++      },
++      getDefaultSingleLineTooltip = function () {
++        return {
++          contents: patternfly.pfGetBarChartTooltipContentsFn()
++        };
++      },
++      getDefaultSingleLineLegend = function () {
++        return {
++          show: false
++        };
++      },
++      getDefaultSingleLineConfig = function () {
++        return {
++          axis: getDefaultLineAxis(),
++          grid: getDefaultLineGrid(),
++          color: getDefaultColors(),
++          legend: getDefaultSingleLineLegend(),
++          point: getDefaultLinePoint(),
++          tooltip: getDefaultSingleLineTooltip()
++        };
++      },
++      getDefaultAreaAxis = function () {
++        return getDefaultLineAxis();
++      },
++      getDefaultAreaGrid = function () {
++        return getDefaultLineGrid();
++      },
++      getDefaultAreaLegend = function () {
++        return getDefaultLineLegend();
++      },
++      getDefaultAreaPoint = function () {
++        return getDefaultLinePoint();
++      },
++      getDefaultAreaConfig = function () {
++        return {
++          axis: getDefaultAreaAxis(),
++          grid: getDefaultAreaGrid(),
++          color: getDefaultColors(),
++          legend: getDefaultAreaLegend(),
++          point: getDefaultAreaPoint()
++        };
++      },
++      getDefaultSingleAreaTooltip = function () {
++        return {
++          contents: patternfly.pfGetBarChartTooltipContentsFn()
++        };
++      },
++      getDefaultSingleAreaLegend = function () {
++        return getDefaultSingleLineLegend();
++      },
++      getDefaultSingleAreaConfig = function () {
++        return {
++          axis: getDefaultAreaAxis(),
++          grid: getDefaultAreaGrid(),
++          color: getDefaultColors(),
++          legend: getDefaultSingleAreaLegend(),
++          point: getDefaultAreaPoint(),
++          tooltip: getDefaultSingleAreaTooltip()
++        };
++      };
++    return {
++      getDefaultColors: getDefaultColors,
++      getDefaultBarGrid: getDefaultBarGrid,
++      getDefaultBarTooltip: getDefaultBarTooltip,
++      getDefaultBarLegend: getDefaultBarLegend,
++      getDefaultBarConfig: getDefaultBarConfig,
++      getDefaultGroupedBarGrid: getDefaultGroupedBarGrid,
++      getDefaultGroupedBarLegend: getDefaultGroupedBarLegend,
++      getDefaultGroupedBarConfig: getDefaultGroupedBarConfig,
++      getDefaultStackedBarGrid: getDefaultStackedBarGrid,
++      getDefaultStackedBarLegend: getDefaultStackedBarLegend,
++      getDefaultStackedBarConfig: getDefaultStackedBarConfig,
++      getDefaultDonut: getDefaultDonut,
++      getDefaultDonutSize: getDefaultDonutSize,
++      getDefaultDonutColors: getDefaultDonutColors,
++      getDefaultDonutTooltip: getDefaultDonutTooltip,
++      getDefaultDonutLegend: getDefaultDonutLegend,
++      getDefaultDonutConfig: getDefaultDonutConfig,
++      getDefaultRelationshipDonutConfig: getDefaultRelationshipDonutConfig,
++      getDefaultPie: getDefaultPie,
++      getDefaultPieSize: getDefaultPieSize,
++      getDefaultPieColors: getDefaultPieColors,
++      getDefaultRelationshipDonutColors: getDefaultRelationshipDonutColors,
++      getDefaultPieTooltip: getDefaultPieTooltip,
++      getDefaultPieLegend: getDefaultPieLegend,
++      getDefaultPieConfig: getDefaultPieConfig,
++      getDefaultSparklineArea: getDefaultSparklineArea,
++      getDefaultSparklineSize: getDefaultSparklineSize,
++      getDefaultSparklineAxis: getDefaultSparklineAxis,
++      getDefaultSparklineLegend: getDefaultSparklineLegend,
++      getDefaultSparklinePoint: getDefaultSparklinePoint,
++      getDefaultSparklineTooltip: getDefaultSparklineTooltip,
++      getDefaultSparklineConfig: getDefaultSparklineConfig,
++      getDefaultLineAxis: getDefaultLineAxis,
++      getDefaultLineGrid: getDefaultLineGrid,
++      getDefaultLineLegend: getDefaultLineLegend,
++      getDefaultLinePoint: getDefaultLinePoint,
++      getDefaultLineConfig: getDefaultLineConfig,
++      getDefaultSingleLineTooltip: getDefaultSingleLineTooltip,
++      getDefaultSingleLineConfig: getDefaultSingleLineConfig,
++      getDefaultAreaAxis: getDefaultAreaAxis,
++      getDefaultAreaGrid: getDefaultAreaGrid,
++      getDefaultAreaLegend: getDefaultAreaLegend,
++      getDefaultAreaPoint: getDefaultAreaPoint,
++      getDefaultAreaConfig: getDefaultAreaConfig,
++      getDefaultSingleAreaTooltip: getDefaultSingleAreaTooltip,
++      getDefaultSingleAreaConfig: getDefaultSingleAreaConfig
++    };
++  };
++})(typeof window !== 'undefined' ? window : global);
+ 
+-$(window).resize(function() {
+-  // Call sidebar() on resize if .sidebar-pf exists
+-  if ($('.sidebar-pf').length > 0) {
+-    sidebar();
++// Util: definition of breakpoint sizes for tablet and desktop modes
++(function ($) {
++  'use strict';
++  if (patternfly !== undefined) {
++    $.pfBreakpoints = patternfly.pfBreakpoints;
+   }
+-});
++}(jQuery));
++// PatternFly pf-list
++(function ($) {
++  'use strict';
++
++  $.fn.pfList = function () {
++    function init (list) {
++      // Ensure the state of the expansion elements is consistent
++      list.find('[data-list=expansion], .list-pf-item, .list-pf-expansion').each(function (index, element) {
++        var $expansion = $(element),
++          $collapse = $expansion.find('.collapse').first(),
++          expanded = $collapse.hasClass('in');
++        updateChevron($expansion, expanded);
++        if ($expansion.hasClass('list-pf-item')) {
++          updateActive($expansion, expanded);
++        }
++      });
++      list.find('.list-pf-container').each(function (index, element) {
++        var $element = $(element);
++        // The toggle element is the element with the data-list=toggle attribute
++        // or the entire .list-pf-container as a fallback
++        var $toggles = $element.find('[data-list=toggle]');
++        $toggles.length || ($toggles = $element);
++        $toggles.on('keydown', function (event) {
++          if (event.keyCode === 13 || event.keyCode === 32) {
++            toggleCollapse(this);
++            event.stopPropagation();
++            event.preventDefault();
++          }
++        });
++        $toggles.on('click', function (event) {
++          toggleCollapse(this);
++          event.stopPropagation();
++          event.preventDefault();
++        });
++      });
++    }
++
++    function toggleCollapse (toggle) {
++      var $toggle, $expansion, $collapse, expanded, $listItem;
++      $toggle = $(toggle);
++      // Find the parent expansion of the toggle
++      $expansion = $toggle.parentsUntil('.list-pf', '[data-list=expansion]').first();
++      $expansion.length || ($expansion = $toggle.closest('.list-pf-item, .list-pf-expansion'));
++
++      // toggle the "in" class of its  first .collapse child
++      $collapse = $expansion.find('.collapse').first();
++      $collapse.toggleClass('in');
++
++      // update the state of the expansion element
++      updateChevron($expansion, $collapse.hasClass('in'));
++      $listItem = $expansion.closest('.list-pf-item');
++      updateActive($listItem, $listItem.find('.collapse').first().hasClass('in'));
++    }
+ 
+-// PatternFly Sidebar
++    function updateActive ($listItem, expanded) {
++      // Find the closest .list-pf-item of the expansion, and set its "active" class
++      if (expanded) {
++        $listItem.addClass('active');
++      } else {
++        $listItem.removeClass('active');
++      }
++    }
++
++    function updateChevron ($expansion, expanded) {
++      var $chevron = $expansion.find('.list-pf-chevron .fa').first();
++      if (expanded) {
++        $chevron.removeClass('fa-angle-right');
++        $chevron.addClass('fa-angle-down');
++      } else {
++        $chevron.addClass('fa-angle-right');
++        $chevron.removeClass('fa-angle-down');
++      }
++    }
++
++    init(this);
++
++    return this;
++  };
++}(jQuery));
++
++// Util: PatternFly Sidebar
+ // Set height of sidebar-pf to height of document minus height of navbar-pf if not mobile
+-function sidebar() {
+-  var documentHeight = 0;
+-  var navbarpfHeight = 0;
+-  var colHeight = 0;
+-  if ( $('.navbar-pf .navbar-toggle').is(':hidden') ) {
+-    documentHeight = $(document).height();
+-    navbarpfHeight = $('.navbar-pf').outerHeight();
+-    colHeight = documentHeight - navbarpfHeight;
+-  }
+-  $('.sidebar-pf').parent('.row').children('[class*="col-"]').css({ "min-height":colHeight});
+-}
++(function ($) {
++  'use strict';
++  $.fn.sidebar = function () {
++    var documentHeight = 0,
++      navbarpfHeight = 0,
++      colHeight = 0;
++
++    if ($('.navbar-pf .navbar-toggle').is(':hidden')) {
++      documentHeight = $(document).height();
++      navbarpfHeight = $('.navbar-pf').outerHeight();
++      colHeight = documentHeight - navbarpfHeight;
++    }
++    $('.sidebar-pf').parent('.row').children('[class*="col-"]').css({"min-height" : colHeight});
++  };
++
++  $(document).ready(function () {
++    // Call sidebar() on ready if .sidebar-pf exists and .datatable does not exist
++    if ($('.sidebar-pf').length > 0 && $('.datatable').length === 0) {
++      $.fn.sidebar();
++    }
++  });
++
++  $(window).on('resize', function () {
++    // Call sidebar() on resize if .sidebar-pf exists
++    if ($('.sidebar-pf').length > 0) {
++      $.fn.sidebar();
++    }
++  });
++}(jQuery));
+ 
+ // Util: PatternFly Popovers
+ // Add data-close="true" to insert close X icon
+-(function($) {
+-  PatternFly.popovers = function( selector ) {
+-    var allpopovers = $(selector);
++(function ($) {
++  'use strict';
+ 
++  $.fn.popovers = function () {
+     // Initialize
+-    allpopovers.popover();
++    this.popover();
+ 
+     // Add close icons
+-    allpopovers.filter('[data-close=true]').each(function(index, element) {
++    this.filter('[data-close=true]').each(function (index, element) {
+       var $this = $(element),
+         title = $this.attr('data-original-title') + '<button type="button" class="close" aria-hidden="true"><span class="pficon pficon-close"></span></button>';
+ 
+@@ -48,30 +694,32 @@ function sidebar() {
+     });
+ 
+     // Bind Close Icon to Toggle Display
+-    allpopovers.on('click', function(e) {
+-      var $this = $(this);
++    this.on('click', function (e) {
++      var $this = $(this),
+         $title = $this.next('.popover').find('.popover-title');
+ 
+       // Only if data-close is true add class "x" to title for right padding
+       $title.find('.close').parent('.popover-title').addClass('closable');
+ 
+       // Bind x icon to close popover
+-      $title.find('.close').on('click', function() {
+-        $this.popover('toggle');
++      $title.find('.close').on('click', function () {
++        $this.popover('hide');
+       });
+ 
+       // Prevent href="#" page scroll to top
+       e.preventDefault();
+     });
+-  };
+-})(jQuery);
+ 
++    return this;
++  };
++}(jQuery));
+ 
+ // Util: DataTables Settings
+-(function($) {
++(function ($) {
++  'use strict';
+   if ($.fn.dataTableExt) {
+     /* Set the defaults for DataTables initialisation */
+-    $.extend( true, $.fn.dataTable.defaults, {
++    $.extend(true, $.fn.dataTable.defaults, {
+       "bDestroy": true,
+       "bAutoWidth": false,
+       "iDisplayLength": 20,
+@@ -86,111 +734,115 @@ function sidebar() {
+         "sZeroRecords":
+           "<p>Suggestions</p>" +
+           "<ul>" +
+-            "<li>Check the syntax of the search term.</li>" +
++            "<li>Check the javascript regular expression syntax of the search term.</li>" +
+             "<li>Check that the correct menu option is chosen (token ID vs. user ID).</li>" +
+-            "<li>Use wildcards (* to match zero or more characters or ? to match a single character).</li>" +
++            "<li>Use wildcards (* to match 0 or more characters, + to match 1 or more characters, ? to match 0 or 1 character).</li>" +
+             "<li>Clear the search field, then click Search to return to the 20 most recent records.</li>" +
+           "</ul>",
+         "sSearch": ""
+       },
+-      "sPaginationType": "bootstrap_input"
++      "sPaginationType": "bootstrap_input",
++      "oSearch": {
++        "sSearch": "",
++        "bRegex": true,
++        "bSmart": false
++      }
+     });
+ 
+     /* Default class modification */
+-    $.extend( $.fn.dataTableExt.oStdClasses, {
++    $.extend($.fn.dataTableExt.oStdClasses, {
+       "sWrapper": "dataTables_wrapper"
+     });
+ 
+     /* API method to get paging information */
+-    $.fn.dataTableExt.oApi.fnPagingInfo = function ( oSettings ) {
++    $.fn.dataTableExt.oApi.fnPagingInfo = function (oSettings) {
+       return {
+         "iStart":         oSettings._iDisplayStart,
+         "iEnd":           oSettings.fnDisplayEnd(),
+         "iLength":        oSettings._iDisplayLength,
+         "iTotal":         oSettings.fnRecordsTotal(),
+         "iFilteredTotal": oSettings.fnRecordsDisplay(),
+-        "iPage":          oSettings._iDisplayLength === -1 ?
+-          0 : Math.ceil( oSettings._iDisplayStart / oSettings._iDisplayLength ),
+-        "iTotalPages":    oSettings._iDisplayLength === -1 ?
+-          0 : Math.ceil( oSettings.fnRecordsDisplay() / oSettings._iDisplayLength )
++        "iPage":          oSettings._iDisplayLength === -1 ? 0 : Math.ceil(oSettings._iDisplayStart / oSettings._iDisplayLength),
++        "iTotalPages":    oSettings._iDisplayLength === -1 ? 0 : Math.ceil(oSettings.fnRecordsDisplay() / oSettings._iDisplayLength)
+       };
+     };
+ 
+     /* Combination of Bootstrap + Input Text style pagination control */
+-    $.extend( $.fn.dataTableExt.oPagination, {
++    $.extend($.fn.dataTableExt.oPagination, {
+       "bootstrap_input": {
+-        "fnInit": function( oSettings, nPaging, fnDraw ) {
+-          var oLang = oSettings.oLanguage.oPaginate;
+-          var fnClickHandler = function ( e ) {
+-            e.preventDefault();
+-            if ( oSettings.oApi._fnPageChange(oSettings, e.data.action) ) {
+-              fnDraw( oSettings );
+-            }
+-          };
++        "fnInit": function (oSettings, nPaging, fnDraw) {
++          var fnClickHandler = function (e) {
++              e.preventDefault();
++              if (oSettings.oApi._fnPageChange(oSettings, e.data.action)) {
++                fnDraw(oSettings);
++              }
++            },
++            els,
++            nInput;
+ 
+           $(nPaging).append(
+-            '<ul class="pagination">'+
+-              '<li class="first disabled"><span class="i fa fa-angle-double-left"></span></li>' +
+-              '<li class="prev disabled"><span class="i fa fa-angle-left"></span></li>' +
+-            '</ul>' +
+-            '<div class="pagination-input">' +
+-              '<input type="text" class="paginate_input">' +
+-              '<span class="paginate_of">of <b>3</b></span>' +
+-            '</div>' +
+-            '<ul class="pagination">'+
+-              '<li class="next disabled"><span class="i fa fa-angle-right"></span></li>' +
+-              '<li class="last disabled"><span class="i fa fa-angle-double-right"></span></li>' +
+-            '</ul>'
++            '<ul class="pagination">' +
++              '  <li class="first disabled"><span class="i fa fa-angle-double-left"></span></li>' +
++              '  <li class="prev disabled"><span class="i fa fa-angle-left"></span></li>' +
++              '</ul>' +
++              '<div class="pagination-input">' +
++              '  <input type="text" class="paginate_input">' +
++              '  <span class="paginate_of">of <b>3</b></span>' +
++              '</div>' +
++              '<ul class="pagination">' +
++              '  <li class="next disabled"><span class="i fa fa-angle-right"></span></li>' +
++              '  <li class="last disabled"><span class="i fa fa-angle-double-right"></span></li>' +
++              '</ul>'
+           );
+ 
+-          var els = $('li', nPaging);
+-          $(els[0]).bind( 'click.DT', { action: "first" }, fnClickHandler );
+-          $(els[1]).bind( 'click.DT', { action: "previous" }, fnClickHandler );
+-          $(els[2]).bind( 'click.DT', { action: "next" }, fnClickHandler );
+-          $(els[3]).bind( 'click.DT', { action: "last" }, fnClickHandler );
++          els = $('li', nPaging);
++          $(els[0]).bind('click.DT', { action: "first" }, fnClickHandler);
++          $(els[1]).bind('click.DT', { action: "previous" }, fnClickHandler);
++          $(els[2]).bind('click.DT', { action: "next" }, fnClickHandler);
++          $(els[3]).bind('click.DT', { action: "last" }, fnClickHandler);
+ 
+-          var nInput = $('input', nPaging);
+-          $(nInput).keyup( function (e) {
+-              if ( e.which == 38 || e.which == 39 ) {
+-                this.value++;
+-              }
+-              else if ( (e.which == 37 || e.which == 40) && this.value > 1 ) {
+-                this.value--;
+-              }
++          nInput = $('input', nPaging);
++          $(nInput).keyup(function (e) {
++            var iNewStart;
++            if (e.which === 38 || e.which === 39) {
++              this.value += 1;
++            } else if ((e.which === 37 || e.which === 40) && this.value > 1) {
++              this.value -= 1;
++            }
+ 
+-              if ( this.value == "" || this.value.match(/[^0-9]/) ) {
+-                /* Nothing entered or non-numeric character */
+-                return;
+-              }
++            if (this.value === "" || !this.value.match(/[0-9]/)) {
++              /* Nothing entered or non-numeric character */
++              return;
++            }
+ 
+-              var iNewStart = oSettings._iDisplayLength * (this.value - 1);
+-              if ( iNewStart > oSettings.fnRecordsDisplay() ) {
+-                /* Display overrun */
+-                oSettings._iDisplayStart = (Math.ceil((oSettings.fnRecordsDisplay()-1) /
+-                  oSettings._iDisplayLength)-1) * oSettings._iDisplayLength;
+-                fnDraw( oSettings );
+-                return;
+-              }
++            iNewStart = oSettings._iDisplayLength * (this.value - 1);
++            if (iNewStart >= oSettings.fnRecordsDisplay()) {
++              /* Display overrun */
++              oSettings._iDisplayStart = (Math.ceil((oSettings.fnRecordsDisplay() - 1) /
++                oSettings._iDisplayLength) - 1) * oSettings._iDisplayLength;
++              fnDraw(oSettings);
++              return;
++            }
+ 
+-              oSettings._iDisplayStart = iNewStart;
+-              fnDraw( oSettings );
++            oSettings._iDisplayStart = iNewStart;
++            fnDraw(oSettings);
+           });
+         },
+ 
+-        "fnUpdate": function ( oSettings, fnDraw ) {
++        "fnUpdate": function (oSettings, fnDraw) {
+           var oPaging = oSettings.oInstance.fnPagingInfo(),
+             an = oSettings.aanFeatures.p,
+-            i,
+-            ien,
++            ien = an.length,
+             iPages = Math.ceil((oSettings.fnRecordsDisplay()) / oSettings._iDisplayLength),
+-            iCurrentPage = Math.ceil(oSettings._iDisplayStart / oSettings._iDisplayLength) + 1;
++            iCurrentPage = Math.ceil(oSettings._iDisplayStart / oSettings._iDisplayLength) + 1,
++            i;
+ 
+-          for ( i=0, ien=an.length ; i<ien ; i++ ) {
+-            $('.paginate_input').val(iCurrentPage);
+-            $('.paginate_of b').html(iPages);
++          for (i = 0; i < ien; i += 1) {
++            $('.paginate_input', an[i]).val(iCurrentPage)
++              .siblings('.paginate_of').find('b').html(iPages);
+ 
+             // Add / remove disabled classes from the static elements
+-            if ( oPaging.iPage === 0 ) {
++            if (oPaging.iPage === 0) {
+               $('li.first', an[i]).addClass('disabled');
+               $('li.prev', an[i]).addClass('disabled');
+             } else {
+@@ -198,7 +850,7 @@ function sidebar() {
+               $('li.prev', an[i]).removeClass('disabled');
+             }
+ 
+-            if ( oPaging.iPage === oPaging.iTotalPages-1 || oPaging.iTotalPages === 0 ) {
++            if (oPaging.iPage === oPaging.iTotalPages - 1 || oPaging.iTotalPages === 0) {
+               $('li.next', an[i]).addClass('disabled');
+               $('li.last', an[i]).addClass('disabled');
+             } else {
+@@ -210,4 +862,1005 @@ function sidebar() {
+       }
+     });
+   }
+-})(jQuery);
++}(jQuery));
++
++// Util: PatternFly Collapsible Left Hand Navigation
++// Must have navbar-toggle in navbar-pf-alt for expand/collapse
++(function ($) {
++
++  'use strict';
++
++  $.fn.navigation = function () {
++
++    var navElement = $('.layout-pf-alt-fixed .nav-pf-vertical-alt'),
++      bodyContentElement = $('.container-pf-alt-nav-pf-vertical-alt'),
++      toggleNavBarButton = $('.navbar-toggle'),
++      explicitCollapse = false,
++      checkNavState = function () {
++        var width = $(window).width();
++
++        //Always remove the hidden & peek class
++        navElement.removeClass('hidden show-mobile-nav collapsed');
++
++        //Set the body class back to the default
++        bodyContentElement.removeClass('collapsed-nav hidden-nav');
++
++        // Check to see if the nav needs to collapse
++        if (width < $.pfBreakpoints.desktop || explicitCollapse) {
++          navElement.addClass('collapsed');
++          bodyContentElement.addClass('collapsed-nav');
++        }
++
++        // Check to see if we need to move down to the mobile state
++        if (width < $.pfBreakpoints.tablet) {
++          //Set the nav to being hidden
++          navElement.addClass('hidden');
++
++          //Make sure this is expanded
++          navElement.removeClass('collapsed');
++
++          //Set the body class to the correct state
++          bodyContentElement.removeClass('collapsed-nav');
++          bodyContentElement.addClass('hidden-nav');
++        }
++      },
++      collapseMenu = function () {
++        //Make sure this is expanded
++        navElement.addClass('collapsed');
++        //Set the body class to the correct state
++        bodyContentElement.addClass('collapsed-nav');
++
++        explicitCollapse = true;
++      },
++      enableTransitions = function () {
++        // enable transitions only when toggleNavBarButton is clicked or window is resized
++        $('html').addClass('transitions');
++      },
++      expandMenu = function () {
++        //Make sure this is expanded
++        navElement.removeClass('collapsed');
++        //Set the body class to the correct state
++        bodyContentElement.removeClass('collapsed-nav');
++
++        explicitCollapse = false;
++      },
++      bindMenuBehavior = function () {
++        toggleNavBarButton.on('click', function (e) {
++          var inMobileState = bodyContentElement.hasClass('hidden-nav');
++          enableTransitions();
++
++          if (inMobileState && navElement.hasClass('show-mobile-nav')) {
++            //In mobile state just need to hide the nav
++            navElement.removeClass('show-mobile-nav');
++          } else if (inMobileState) {
++            navElement.addClass('show-mobile-nav');
++          } else if (navElement.hasClass('collapsed')) {
++            expandMenu();
++          } else {
++            collapseMenu();
++          }
++        });
++      },
++      setTooltips = function () {
++        $('.nav-pf-vertical-alt [data-toggle="tooltip"]').tooltip({'container': 'body', 'delay': { 'show': '500', 'hide': '200' }});
++
++        $(".nav-pf-vertical-alt").on("show.bs.tooltip", function (e) {
++          return $(this).hasClass("collapsed");
++        });
++
++      },
++      init = function () {
++        //Set correct state on load
++        checkNavState();
++
++        // Bind Top level hamburger menu with menu behavior;
++        bindMenuBehavior();
++
++        //Set tooltips
++        setTooltips();
++      };
++
++    //Listen for the window resize event and collapse/hide as needed
++    $(window).on('resize', function () {
++      checkNavState();
++      enableTransitions();
++    });
++
++    init();
++
++  };
++
++  $(document).ready(function () {
++    if ($('.nav-pf-vertical-alt').length > 0) {
++      $.fn.navigation();
++    }
++  });
++
++}(jQuery));
++
++// Count and Display Remaining Characters
++(function ($) {
++
++  'use strict';
++
++  $.fn.countRemainingChars = function (options) {
++
++    var settings = $.extend({
++        // These are the defaults.
++        charsMaxLimit: 100,
++        charsWarnRemaining: 5,
++        blockInputAtMaxLimit: false
++      }, options),
++      $taFld = this,
++      $countFld = $('#'  + settings.countFld).text(settings.charsMaxLimit),
++      charsRemainingFn = function (charsLength) {
++        var charsRemaining = settings.charsMaxLimit - charsLength;
++        $countFld.text(charsRemaining);
++        $countFld.toggleClass('chars-warn-remaining-pf', charsRemaining <= settings.charsWarnRemaining);
++        if (charsRemaining < 0) {
++          $taFld.trigger("overCharsMaxLimitEvent", $taFld.attr('id'));
++        } else {
++          $taFld.trigger("underCharsMaxLimitEvent", $taFld.attr('id'));
++        }
++      };
++
++    this.on('paste', function (event) {
++      setTimeout(function () {
++        var charsLength = $taFld.val().length, maxTxt;
++
++        if (settings.blockInputAtMaxLimit && charsLength > settings.charsMaxLimit) {
++          maxTxt = $taFld.val();
++          maxTxt = maxTxt.substring(0, settings.charsMaxLimit);
++          $taFld.val(maxTxt);
++          charsLength = $taFld.val().length;
++        }
++
++        charsRemainingFn(charsLength);
++      }, 100);
++    });
++
++    this.keyup(function (event) {
++      charsRemainingFn($taFld.val().length);
++    });
++
++    this.keydown(function (event) {
++      var charsLength = $taFld.val().length;
++
++      if (settings.blockInputAtMaxLimit && charsLength >= settings.charsMaxLimit) {
++        // Except backspace
++        if (event.keyCode !== 8) {
++          event.preventDefault();
++        }
++      }
++    });
++
++    return this;
++  };
++}(jQuery));
++
++// Util: PatternFly Palette colors
++(function ($) {
++  'use strict';
++
++  if (patternfly !== undefined) {
++    $.pfPaletteColors = patternfly.pfPaletteColors;
++  }
++}(jQuery));
++
++// Util: PatternFly C3 Chart Defaults
++(function ($) {
++  'use strict';
++  if (patternfly !== undefined) {
++    $.fn.pfSetDonutChartTitle = patternfly.pfSetDonutChartTitle;
++    $.fn.pfDonutTooltipContents = patternfly.pfDonutTooltipContents;
++    $.fn.pfGetUtilizationDonutTooltipContentsFn = patternfly.pfGetUtilizationDonutTooltipContentsFn;
++    $.fn.pfGetBarChartTooltipContentsFn = patternfly.pfGetBarChartTooltipContentsFn;
++    $.fn.pfSingleLineChartTooltipContentsFn = patternfly.pfSingleLineChartTooltipContentsFn;
++    $.fn.pfPieTooltipContents = patternfly.pfPieTooltipContents;
++    $.fn.c3ChartDefaults = patternfly.c3ChartDefaults;
++  }
++}(jQuery));
++
++// Util: PatternFly Collapse with fixed heights
++// Update the max-height of collapse elements based on the parent container's height.
++(function ($) {
++  'use strict';
++
++  $.fn.initCollapseHeights = function (scrollSelector) {
++    var parentElement = this, setCollapseHeights, targetScrollSelector = scrollSelector;
++
++    setCollapseHeights = function () {
++      var height, openPanel, contentHeight, bodyHeight, overflowY = 'hidden';
++
++      height = parentElement.height();
++
++      // Close any open panel
++      openPanel = parentElement.find('.collapse.in');
++      if (openPanel && openPanel.length > 0) {
++        openPanel.removeClass('in');
++      }
++
++      // Determine the necessary height for the closed content
++      contentHeight = 0;
++      parentElement.children().each($.proxy(function (i, element) {
++        var $element = $(element);
++        contentHeight += $element.outerHeight(true);
++      }, parentElement)).end();
++
++      // Determine the height remaining for opened collapse panels
++      bodyHeight = height - contentHeight;
++
++      // Make sure we have enough height to be able to scroll the contents if necessary
++      if (bodyHeight < 25) {
++        bodyHeight = 25;
++
++        // Allow the parent to scroll so the child elements are accessible
++        overflowY = 'auto';
++      }
++
++      // Reopen the initially opened panel
++      if (openPanel && openPanel.length > 0) {
++        openPanel.addClass("in");
++      }
++
++      setTimeout(function () {
++        // Set the max-height for the collapse panels
++        parentElement.find('[data-toggle="collapse"]').each($.proxy(function (i, element) {
++          var $element, selector, $target, scrollElement, innerHeight = 0;
++          $element = $(element);
++
++          // Determine the selector to find the target
++          selector = $element.attr('data-target');
++          if (!selector) {
++            selector = $element.attr('href');
++          }
++
++          // Determine the scroll element (either the target or the child of the target based on the given selector)
++          $target = $(selector);
++          scrollElement = $target;
++          if (targetScrollSelector) {
++            scrollElement = $target.find(targetScrollSelector);
++            if (scrollElement.length === 1) {
++              innerHeight = 0;
++              $target.children().each($.proxy(function (j, sibling) {
++                var $sibling = $(sibling);
++                if (sibling !== scrollElement[0]) {
++                  innerHeight += $sibling.outerHeight(true);
++                }
++              }, $target)).end();
++              bodyHeight -= innerHeight;
++            } else {
++              scrollElement = $target;
++            }
++          }
++          // Set the max-height and vertical scroll of the scroll element
++          scrollElement.css({'max-height': (bodyHeight - innerHeight) + 'px', 'overflow-y': 'auto'});
++        }, parentElement)).end();
++
++        parentElement.css({'overflow-y': overflowY});
++      }, 100);
++    };
++
++    setCollapseHeights();
++
++    // Update on window resizing
++    $(window).on('resize', setCollapseHeights);
++
++  };
++
++  $.fn.initFixedAccordion = function () {
++    var fixedAccordion = this, initOpen;
++
++    fixedAccordion.on('show.bs.collapse','.collapse', function (event) {
++      $(event.target.parentNode).addClass('panel-open');
++    });
++
++    fixedAccordion.on('hide.bs.collapse','.collapse', function (event) {
++      $(event.target.parentNode).removeClass('panel-open');
++    });
++
++    fixedAccordion.find('.panel').each(function (index, item) {
++      $(item).removeClass('panel-open');
++    });
++
++    initOpen = $(fixedAccordion.find('.collapse.in'))[0];
++    if (initOpen) {
++      $(initOpen.parentNode).addClass('panel-open');
++    }
++  };
++
++}(jQuery));
++
++// Util: PatternFly TreeGrid Tables
++(function ($) {
++  'use strict';
++
++  function getParent (rows, node) {
++    var parent = node.attr('data-parent');
++
++    if (typeof parent === "string") {
++      if (isNaN(parent)) {
++        parent = $(parent);
++        if (parent.length > 1) {
++          parent = rows.closest(parent);
++        }
++      } else {
++        parent = $(rows[parseInt(parent, 10)]);
++      }
++      return parent;
++    }
++    return undefined;
++  }
++
++  function renderItem (item, parent) {
++    if (parent) {
++      parent.find('.treegrid-node > span.expand-icon')
++        .toggleClass('fa-angle-right', parent.hasClass('collapsed'))
++        .toggleClass('fa-angle-down', !parent.hasClass('collapsed'));
++      item.toggleClass('hidden', parent.hasClass('collapsed'));
++      if (parent.hasClass('collapsed')) {
++        item.addClass('collapsed');
++      }
++    }
++  }
++
++  function reStripe (tree) {
++    tree.find('tbody > tr').removeClass('odd');
++    tree.find('tbody > tr:not(.hidden):odd').addClass('odd');
++  }
++
++  $.fn.treegrid = function (options) {
++    var i, rows, _this;
++    rows = this.find('tbody > tr');
++    _this = this;
++    $.each(rows, function () {
++      var node, parent;
++      node = $(this);
++      parent = getParent(rows, node);
++      // Append expand icon dummies
++      node.children('.treegrid-node').prepend('<span class="icon expand-icon fa"/>');
++
++      // Set up an event listener for the node
++      node.children('.treegrid-node').on('click', function (e) {
++        var icon = node.find('span.expand-icon');
++
++        if (options && typeof options.callback === 'function') {
++          options.callback(e);
++        }
++
++        if (icon.hasClass('fa-angle-right')) {
++          node.removeClass('collapsed');
++        }
++        if (icon.hasClass('fa-angle-down')) {
++          node.addClass('collapsed');
++        }
++        $.each(rows.slice(rows.index(node) + 1), function () {
++          renderItem($(this), getParent(rows, $(this)));
++        });
++        reStripe(_this);
++      });
++
++      if (parent) {
++        // Calculate indentation depth
++        i = parent.find('.treegrid-node > span.indent').length + 1;
++        for (; i > 0; i -= 1) {
++          node.children('.treegrid-node').prepend('<span class="indent"/>');
++        }
++        // Render expand/collapse icons
++        renderItem(node, parent);
++      }
++    });
++    reStripe(_this);
++  };
++}(jQuery));
++
++// Util: PatternFly Vertical Navigation
++// Must have navbar-toggle in navbar-pf-vertical for expand/collapse
++(function ($) {
++  'use strict';
++
++  $.fn.setupVerticalNavigation = function (handleItemSelections, ignoreDrawer, userOptions) {
++
++    var options = $.extend({
++        hoverDelay: 500,
++        hideDelay: 700,
++        rememberOpenState: true,
++        storage: 'localStorage',
++      }, userOptions || {}),
++
++      navElement = $('.nav-pf-vertical'),
++      bodyContentElement = $('.container-pf-nav-pf-vertical'),
++      toggleNavBarButton = $('.navbar-toggle'),
++      handleResize = true,
++      explicitCollapse = false,
++      subDesktop = false,
++      storageLocation = options.storage === 'sessionStorage' ? 'sessionStorage' : 'localStorage',
++
++      inMobileState = function () {
++        return bodyContentElement.hasClass('hidden-nav');
++      },
++
++      forceResize = function (delay) {
++        setTimeout(function () {
++          $(window).trigger('resize');
++        }, delay);
++      },
++
++      showSecondaryMenu = function () {
++        if (inMobileState() || !subDesktop) {
++          navElement.addClass('secondary-visible-pf');
++          bodyContentElement.addClass('secondary-visible-pf');
++        }
++
++        // Dispatch a resize event when showing the secondary menu in non-subdesktop state to
++        // allow content to adjust to the secondary menu sizing
++        if (!subDesktop) {
++          forceResize(100);
++        }
++      },
++
++      hideSecondaryMenu = function () {
++        navElement.removeClass('secondary-visible-pf');
++        bodyContentElement.removeClass('secondary-visible-pf');
++
++        if (navElement.find('.secondary-nav-item-pf.is-hover').length <= 1) {
++          navElement.removeClass('hover-secondary-nav-pf');
++        }
++
++        navElement.find('.mobile-nav-item-pf').each(function (index, item) {
++          $(item).removeClass('mobile-nav-item-pf');
++        });
++
++        navElement.find('.is-hover').each(function (index, item) {
++          $(item).removeClass('is-hover');
++        });
++      },
++
++      hideTertiaryMenu = function () {
++        navElement.removeClass('tertiary-visible-pf');
++        bodyContentElement.removeClass('tertiary-visible-pf');
++
++        if (navElement.find('.tertiary-nav-item-pf.is-hover').length <= 1) {
++          navElement.removeClass('hover-tertiary-nav-pf');
++        }
++
++        navElement.find('.mobile-nav-item-pf').each(function (index, item) {
++          $(item).removeClass('mobile-nav-item-pf');
++        });
++
++        navElement.find('.is-hover').each(function (index, item) {
++          $(item).removeClass('is-hover');
++        });
++      },
++
++      setActiveItem = function (item) {
++        // remove all .active
++        $('.nav-pf-vertical .list-group-item.active').removeClass('active');
++
++        // add .active to item and its parents
++        item.addClass('active').parents('.list-group-item').addClass('active');
++      },
++
++      updateSecondaryMenuDisplayAfterSelection = function () {
++        if (inMobileState()) {
++          navElement.removeClass('show-mobile-nav');
++          hideSecondaryMenu();
++          navElement.find('.mobile-nav-item-pf').each(function (index, item) {
++            $(item).removeClass('mobile-nav-item-pf');
++          });
++        } else {
++          showSecondaryMenu();
++        }
++      },
++
++      updateSecondaryCollapsedState = function (setCollapsed, collapsedItem) {
++        if (setCollapsed) {
++          collapsedItem.addClass('collapsed');
++          navElement.addClass('collapsed-secondary-nav-pf');
++          bodyContentElement.addClass('collapsed-secondary-nav-pf');
++        } else {
++          if (collapsedItem) {
++            collapsedItem.removeClass('collapsed');
++          } else {
++            // Remove any collapsed secondary menus
++            navElement.find('[data-toggle="collapse-secondary-nav"]').each(function (index, element) {
++              var $e = $(element);
++              $e.removeClass('collapsed');
++            });
++          }
++          navElement.removeClass('collapsed-secondary-nav-pf');
++          bodyContentElement.removeClass('collapsed-secondary-nav-pf');
++        }
++      },
++
++      updateTertiaryCollapsedState = function (setCollapsed, collapsedItem) {
++        if (setCollapsed) {
++          collapsedItem.addClass('collapsed');
++          navElement.addClass('collapsed-tertiary-nav-pf');
++          bodyContentElement.addClass('collapsed-tertiary-nav-pf');
++          updateSecondaryCollapsedState(false);
++        } else {
++          if (collapsedItem) {
++            collapsedItem.removeClass('collapsed');
++          } else {
++            // Remove any collapsed tertiary menus
++            navElement.find('[data-toggle="collapse-tertiary-nav"]').each(function (index, element) {
++              var $e = $(element);
++              $e.removeClass('collapsed');
++            });
++          }
++          navElement.removeClass('collapsed-tertiary-nav-pf');
++          bodyContentElement.removeClass('collapsed-tertiary-nav-pf');
++        }
++      },
++
++      updateMobileMenu = function (selected, secondaryItem) {
++        $(document).find('.list-group-item.mobile-nav-item-pf').each(function (index, item) {
++          $(item).removeClass('mobile-nav-item-pf');
++        });
++        $(document).find('.list-group-item.mobile-secondary-item-pf').each(function (index, item) {
++          $(item).removeClass('mobile-secondary-item-pf');
++        });
++        if (selected) {
++          selected.addClass('mobile-nav-item-pf');
++          if (secondaryItem) {
++            secondaryItem.addClass('mobile-secondary-item-pf');
++            navElement.removeClass('show-mobile-secondary');
++            navElement.addClass('show-mobile-tertiary');
++          } else {
++            navElement.addClass('show-mobile-secondary');
++            navElement.removeClass('show-mobile-tertiary');
++          }
++        } else {
++          navElement.removeClass('show-mobile-secondary');
++          navElement.removeClass('show-mobile-tertiary');
++        }
++      },
++
++      enterMobileState = function () {
++        if (!navElement.hasClass('hidden')) {
++          //Set the nav to being hidden
++          navElement.addClass('hidden');
++          navElement.removeClass('collapsed');
++
++          //Set the body class to the correct state
++          bodyContentElement.removeClass('collapsed-nav');
++          bodyContentElement.addClass('hidden-nav');
++
++          // Reset the collapsed states
++          updateSecondaryCollapsedState(false);
++          updateTertiaryCollapsedState(false);
++
++          explicitCollapse = false;
++        }
++      },
++
++      exitMobileState = function () {
++        // Always remove the hidden & peek class
++        navElement.removeClass('hidden show-mobile-nav');
++
++        // Set the body class back to the default
++        bodyContentElement.removeClass('hidden-nav');
++      },
++
++      checkNavState = function () {
++        var width = $(window).width(), makeSecondaryVisible;
++        if (!handleResize) {
++          return;
++        }
++        // Check to see if we need to enter/exit the mobile state
++        if (width < $.pfBreakpoints.tablet && !explicitCollapse) {
++          enterMobileState();
++        } else if (navElement.hasClass('hidden')) {
++          exitMobileState();
++        }
++
++        // Check to see if we need to enter/exit the sub desktop state
++        if (width < $.pfBreakpoints.desktop) {
++          if (!subDesktop) {
++            // Collapse the navigation bars when entering sub desktop mode
++            navElement.addClass('collapsed');
++            bodyContentElement.addClass('collapsed-nav');
++          }
++          if (width >= $.pfBreakpoints.tablet) {
++            hideSecondaryMenu();
++          }
++          subDesktop = true;
++        } else {
++          makeSecondaryVisible = subDesktop && (navElement.find('.secondary-nav-item-pf.active').length > 0);
++          subDesktop = false;
++          if (makeSecondaryVisible) {
++
++            showSecondaryMenu();
++          }
++        }
++
++        if (explicitCollapse) {
++          navElement.addClass('collapsed');
++          bodyContentElement.addClass('collapsed-nav');
++        } else {
++          navElement.removeClass('collapsed');
++          bodyContentElement.removeClass('collapsed-nav');
++        }
++      },
++
++      collapseMenu = function () {
++        //Make sure this is expanded
++        navElement.addClass('collapsed');
++        //Set the body class to the correct state
++        bodyContentElement.addClass('collapsed-nav');
++
++        if (subDesktop) {
++          hideSecondaryMenu();
++        }
++
++        explicitCollapse = true;
++      },
++
++      enableTransitions = function () {
++        // enable transitions only when toggleNavBarButton is clicked or window is resized
++        $('html').addClass('transitions');
++      },
++
++      expandMenu = function () {
++        //Make sure this is expanded
++        navElement.removeClass('collapsed');
++        //Set the body class to the correct state
++        bodyContentElement.removeClass('collapsed-nav');
++
++        explicitCollapse = false;
++
++        // Dispatch a resize event when showing the expanding then menu to
++        // allow content to adjust to the menu sizing
++        if (!subDesktop) {
++          forceResize(100);
++        }
++      },
++
++      bindMenuBehavior = function () {
++        toggleNavBarButton.on('click', function (e) {
++          var $drawer;
++
++          enableTransitions();
++
++          if (inMobileState()) {
++            // Toggle the mobile nav
++            if (navElement.hasClass('show-mobile-nav')) {
++              navElement.removeClass('show-mobile-nav');
++            } else {
++              // Always start at the primary menu
++              updateMobileMenu();
++              navElement.addClass('show-mobile-nav');
++
++              // If the notification drawer is shown, hide it
++              if (!ignoreDrawer) {
++                $drawer = $('.drawer-pf');
++                if ($drawer.length) {
++                  $('.drawer-pf-trigger').removeClass('open');
++                  $drawer.addClass('hide');
++                }
++              }
++            }
++          } else if (navElement.hasClass('collapsed')) {
++            if (options.rememberOpenState) {
++              window[storageLocation].setItem('patternfly-navigation-primary', 'expanded');
++            }
++            expandMenu();
++          } else {
++            if (options.rememberOpenState) {
++              window[storageLocation].setItem('patternfly-navigation-primary', 'collapsed');
++            }
++            collapseMenu();
++          }
++        });
++      },
++
++      forceHideSecondaryMenu = function () {
++        navElement.addClass('force-hide-secondary-nav-pf');
++        setTimeout(function () {
++          navElement.removeClass('force-hide-secondary-nav-pf');
++        }, 500);
++      },
++
++      bindMenuItemsBehavior = function (handleSelection) {
++        $(document).find('.nav-pf-vertical .list-group-item').each(function (index, item) {
++          var onClickFn,
++            $item = $(item),
++            $nav = $item.closest('[class*="nav-pf-"]');
++
++          if ($nav.hasClass('nav-pf-vertical')) {
++            // Set main nav active item on click or show secondary nav if it has a secondary nav bar and we are in the mobile state
++            onClickFn = function (event) {
++              var $this = $(this), $secondaryItem, $tertiaryItem, $activeItem;
++
++              if (!$this.hasClass('secondary-nav-item-pf')) {
++                hideSecondaryMenu();
++                if (inMobileState()) {
++                  updateMobileMenu();
++                  navElement.removeClass('show-mobile-nav');
++                }
++                if (handleSelection) {
++                  setActiveItem($this);
++                  // Don't process the click on the item
++                  event.stopImmediatePropagation();
++                }
++              } else if (inMobileState()) {
++                updateMobileMenu($this);
++              } else if (handleSelection) {
++                $activeItem = $secondaryItem = $item.find('.nav-pf-secondary-nav > .list-group > .list-group-item').eq(0);
++
++                if ($secondaryItem.hasClass('tertiary-nav-item-pf')) {
++                  $activeItem = $secondaryItem.find('.nav-pf-tertiary-nav > .list-group > .list-group-item').eq(0);
++                }
++
++                setActiveItem($activeItem);
++                event.stopImmediatePropagation();
++              }
++            };
++
++          } else if ($nav.hasClass('nav-pf-secondary-nav')) {
++            // Set secondary nav active item on click or show tertiary nav if it has a tertiary nav bar and we are in the mobile state
++            onClickFn = function (event) {
++              var $this = $(this), $tertiaryItem, $primaryItem;
++              if (!$this.hasClass('tertiary-nav-item-pf')) {
++                if (inMobileState()) {
++                  updateMobileMenu();
++                  navElement.removeClass('show-mobile-nav');
++                }
++                updateSecondaryMenuDisplayAfterSelection();
++                if (handleSelection) {
++                  setActiveItem($item);
++                  hideSecondaryMenu();
++                  event.stopImmediatePropagation();
++                }
++              } else if (inMobileState()) {
++                $primaryItem = $item.parents('.list-group-item');
++                updateMobileMenu($this, $primaryItem);
++                event.stopImmediatePropagation();
++              } else if (handleSelection) {
++                $tertiaryItem = $item.find('.nav-pf-tertiary-nav > .list-group > .list-group-item').eq(0);
++                setActiveItem($tertiaryItem);
++                event.stopImmediatePropagation();
++              }
++            };
++
++          } else if ($nav.hasClass('nav-pf-tertiary-nav')) {
++            // Set tertiary nav active item on click
++            onClickFn = function (event) {
++              if (inMobileState()) {
++                updateMobileMenu();
++                navElement.removeClass('show-mobile-nav');
++              }
++              updateSecondaryMenuDisplayAfterSelection();
++              if (handleSelection) {
++                setActiveItem($item);
++                hideTertiaryMenu();
++                hideSecondaryMenu();
++                event.stopImmediatePropagation();
++              }
++            };
++          }
++
++          // register event handler
++          $item.on('click.pf.secondarynav.data-api', onClickFn);
++        });
++
++        $(document).find('.secondary-nav-item-pf').each(function (index, secondaryItem) {
++          var $secondaryItem = $(secondaryItem);
++
++          // Collapse the secondary nav bar when the toggle is clicked
++          $secondaryItem.on('click.pf.secondarynav.data-api', '[data-toggle="collapse-secondary-nav"]', function (e) {
++            var $this = $(this);
++            if (inMobileState()) {
++              updateMobileMenu();
++              e.stopImmediatePropagation();
++            } else {
++              if ($this.hasClass('collapsed')) {
++                if (options.rememberOpenState) {
++                  window[storageLocation].setItem('patternfly-navigation-secondary', 'expanded');
++                  window[storageLocation].setItem('patternfly-navigation-tertiary', 'expanded');
++                }
++                updateSecondaryCollapsedState(false, $this);
++                forceHideSecondaryMenu();
++              } else {
++                if (options.rememberOpenState) {
++                  window[storageLocation].setItem('patternfly-navigation-secondary', 'collapsed');
++                }
++                updateSecondaryCollapsedState(true, $this);
++              }
++            }
++            navElement.removeClass('hover-secondary-nav-pf');
++            if (handleSelection) {
++              // Don't process the click on the parent item
++              e.stopImmediatePropagation();
++            }
++          });
++
++          $secondaryItem.find('.tertiary-nav-item-pf').each(function (index, primaryItem) {
++            var $primaryItem = $(primaryItem);
++            // Collapse the tertiary nav bar when the toggle is clicked
++            $primaryItem.on('click.pf.tertiarynav.data-api', '[data-toggle="collapse-tertiary-nav"]', function (e) {
++              var $this = $(this);
++              if (inMobileState()) {
++                updateMobileMenu($secondaryItem);
++                e.stopImmediatePropagation();
++              } else {
++                if ($this.hasClass('collapsed')) {
++                  if (options.rememberOpenState) {
++                    window[storageLocation].setItem('patternfly-navigation-secondary', 'expanded');
++                    window[storageLocation].setItem('patternfly-navigation-tertiary', 'expanded');
++                  }
++                  updateTertiaryCollapsedState(false, $this);
++                  forceHideSecondaryMenu();
++                } else {
++                  if (options.rememberOpenState) {
++                    window[storageLocation].setItem('patternfly-navigation-tertiary', 'collapsed');
++                  }
++                  updateTertiaryCollapsedState(true, $this);
++                }
++              }
++              navElement.removeClass('hover-secondary-nav-pf');
++              navElement.removeClass('hover-tertiary-nav-pf');
++              if (handleSelection) {
++                // Don't process the click on the parent item
++                e.stopImmediatePropagation();
++              }
++            });
++          });
++        });
++
++        // Show secondary nav bar on hover of secondary nav items
++        $(document).on('mouseenter.pf.tertiarynav.data-api', '.secondary-nav-item-pf', function (e) {
++          var $this = $(this);
++          if (!inMobileState()) {
++            if ($this[0].navUnHoverTimeout !== undefined) {
++              clearTimeout($this[0].navUnHoverTimeout);
++              $this[0].navUnHoverTimeout = undefined;
++            } else if ($this[0].navHoverTimeout === undefined) {
++              $this[0].navHoverTimeout = setTimeout(function () {
++                navElement.addClass('hover-secondary-nav-pf');
++                $this.addClass('is-hover');
++                $this[0].navHoverTimeout = undefined;
++              }, options.hoverDelay);
++            }
++          }
++        });
++
++        $(document).on('mouseleave.pf.tertiarynav.data-api', '.secondary-nav-item-pf', function (e) {
++          var $this = $(this);
++          if ($this[0].navHoverTimeout !== undefined) {
++            clearTimeout($this[0].navHoverTimeout);
++            $this[0].navHoverTimeout = undefined;
++          } else if ($this[0].navUnHoverTimeout === undefined &&
++              navElement.find('.secondary-nav-item-pf.is-hover').length > 0) {
++            $this[0].navUnHoverTimeout = setTimeout(function () {
++              if (navElement.find('.secondary-nav-item-pf.is-hover').length <= 1) {
++                navElement.removeClass('hover-secondary-nav-pf');
++              }
++              $this.removeClass('is-hover');
++              $this[0].navUnHoverTimeout = undefined;
++            }, options.hideDelay);
++          }
++        });
++
++        // Show tertiary nav bar on hover of secondary nav items
++        $(document).on('mouseover.pf.tertiarynav.data-api', '.tertiary-nav-item-pf', function (e) {
++          var $this = $(this);
++          if (!inMobileState()) {
++            if ($this[0].navUnHoverTimeout !== undefined) {
++              clearTimeout($this[0].navUnHoverTimeout);
++              $this[0].navUnHoverTimeout = undefined;
++            } else if ($this[0].navHoverTimeout === undefined) {
++              $this[0].navHoverTimeout = setTimeout(function () {
++                navElement.addClass('hover-tertiary-nav-pf');
++                $this.addClass('is-hover');
++                $this[0].navHoverTimeout = undefined;
++              }, options.hoverDelay);
++            }
++          }
++        });
++        $(document).on('mouseout.pf.tertiarynav.data-api', '.tertiary-nav-item-pf', function (e) {
++          var $this = $(this);
++          if ($this[0].navHoverTimeout !== undefined) {
++            clearTimeout($this[0].navHoverTimeout);
++            $this[0].navHoverTimeout = undefined;
++          } else if ($this[0].navUnHoverTimeout === undefined) {
++            $this[0].navUnHoverTimeout = setTimeout(function () {
++              if (navElement.find('.tertiary-nav-item-pf.is-hover').length <= 1) {
++                navElement.removeClass('hover-tertiary-nav-pf');
++              }
++              $this.removeClass('is-hover');
++              $this[0].navUnHoverTimeout = undefined;
++            }, options.hideDelay);
++          }
++        });
++      },
++
++      loadFromLocalStorage = function () {
++        if (inMobileState()) {
++          return;
++        }
++
++        if (window[storageLocation].getItem('patternfly-navigation-primary') === 'collapsed') {
++          collapseMenu();
++        }
++
++        if ($('.nav-pf-vertical.nav-pf-vertical-collapsible-menus').length > 0) {
++          if (window[storageLocation].getItem('patternfly-navigation-secondary') === 'collapsed') {
++            updateSecondaryCollapsedState(true, $('.secondary-nav-item-pf.active [data-toggle=collapse-secondary-nav]'));
++          }
++
++          if (window[storageLocation].getItem('patternfly-navigation-tertiary') === 'collapsed') {
++            updateTertiaryCollapsedState(true, $('.tertiary-nav-item-pf.active [data-toggle=collapse-tertiary-nav]'));
++          }
++        }
++      },
++
++      setTooltips = function () {
++        var tooltipOptions = {
++          container: 'body',
++          placement: 'bottom',
++          delay: { 'show': '500', 'hide': '200' },
++          template: '<div class="nav-pf-vertical-tooltip tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
++        };
++        $('.nav-pf-vertical [data-toggle="tooltip"]').tooltip(tooltipOptions);
++
++        $('.nav-pf-vertical').on("show.bs.tooltip", function (e) {
++          return $(this).hasClass("collapsed");
++        });
++      },
++
++      init = function (handleItemSelections) {
++        // Hide the nav menus during initialization
++        navElement.addClass('hide-nav-pf');
++        bodyContentElement.addClass('hide-nav-pf');
++
++        //Set correct state on load
++        checkNavState();
++
++        // Bind Top level hamburger menu with menu behavior;
++        bindMenuBehavior();
++
++        // Bind menu items
++        bindMenuItemsBehavior(handleItemSelections);
++
++        //Set tooltips
++        setTooltips();
++
++        if (options.rememberOpenState) {
++          loadFromLocalStorage();
++        }
++
++        // Show the nav menus
++        navElement.removeClass('hide-nav-pf');
++        bodyContentElement.removeClass('hide-nav-pf');
++        forceResize(250);
++      },
++
++      self = {
++        hideMenu: function () {
++          handleResize = false;
++          enterMobileState();
++        },
++        showMenu: function () {
++          handleResize = true;
++          exitMobileState();
++        },
++        isVisible: function () {
++          return handleResize;
++        }
++      };
++
++    if (!$.fn.setupVerticalNavigation.self) {
++      $.fn.setupVerticalNavigation.self = self;
++      //Listen for the window resize event and collapse/hide as needed
++      $(window).on('resize', function () {
++        checkNavState();
++        enableTransitions();
++      });
++
++      init(handleItemSelections);
++    }
++    return $.fn.setupVerticalNavigation.self;
++  };
++}(jQuery));
+-- 
+1.8.3.1
+
+From 2c6f5ad870ab04aef4fcb22eaf56b9ef16456b7f Mon Sep 17 00:00:00 2001
+From: Alexander Scheel <ascheel@redhat.com>
+Date: Thu, 7 May 2020 16:37:41 -0400
+Subject: [PATCH 13/17] Update to jQuery v3.5.1
+
+Signed-off-by: Alexander Scheel <ascheel@redhat.com>
+(cherry picked from commit 4cb88fca4c7e7d0bea86bc2c667db1f2c8e4fb06)
+---
+ base/server/share/webapps/pki/js/jquery.js | 1238 +++++++++++++++++-----------
+ 1 file changed, 756 insertions(+), 482 deletions(-)
+
+diff --git a/base/server/share/webapps/pki/js/jquery.js b/base/server/share/webapps/pki/js/jquery.js
+index 773ad95..5093733 100644
+--- a/base/server/share/webapps/pki/js/jquery.js
++++ b/base/server/share/webapps/pki/js/jquery.js
+@@ -1,5 +1,5 @@
+ /*!
+- * jQuery JavaScript Library v3.4.1
++ * jQuery JavaScript Library v3.5.1
+  * https://jquery.com/
+  *
+  * Includes Sizzle.js
+@@ -9,7 +9,7 @@
+  * Released under the MIT license
+  * https://jquery.org/license
+  *
+- * Date: 2019-05-01T21:04Z
++ * Date: 2020-05-04T22:49Z
+  */
+ ( function( global, factory ) {
+ 
+@@ -47,13 +47,16 @@
+ 
+ var arr = [];
+ 
+-var document = window.document;
+-
+ var getProto = Object.getPrototypeOf;
+ 
+ var slice = arr.slice;
+ 
+-var concat = arr.concat;
++var flat = arr.flat ? function( array ) {
++	return arr.flat.call( array );
++} : function( array ) {
++	return arr.concat.apply( [], array );
++};
++
+ 
+ var push = arr.push;
+ 
+@@ -86,6 +89,8 @@ var isWindow = function isWindow( obj ) {
+ 	};
+ 
+ 
++var document = window.document;
++
+ 
+ 
+ 	var preservedScriptAttributes = {
+@@ -142,7 +147,7 @@ function toType( obj ) {
+ 
+ 
+ var
+-	version = "3.4.1",
++	version = "3.5.1",
+ 
+ 	// Define a local copy of jQuery
+ 	jQuery = function( selector, context ) {
+@@ -150,11 +155,7 @@ var
+ 		// The jQuery object is actually just the init constructor 'enhanced'
+ 		// Need init if jQuery is called (just allow error to be thrown if not included)
+ 		return new jQuery.fn.init( selector, context );
+-	},
+-
+-	// Support: Android <=4.0 only
+-	// Make sure we trim BOM and NBSP
+-	rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;
++	};
+ 
+ jQuery.fn = jQuery.prototype = {
+ 
+@@ -220,6 +221,18 @@ jQuery.fn = jQuery.prototype = {
+ 		return this.eq( -1 );
+ 	},
+ 
++	even: function() {
++		return this.pushStack( jQuery.grep( this, function( _elem, i ) {
++			return ( i + 1 ) % 2;
++		} ) );
++	},
++
++	odd: function() {
++		return this.pushStack( jQuery.grep( this, function( _elem, i ) {
++			return i % 2;
++		} ) );
++	},
++
+ 	eq: function( i ) {
+ 		var len = this.length,
+ 			j = +i + ( i < 0 ? len : 0 );
+@@ -353,9 +366,10 @@ jQuery.extend( {
+ 		return true;
+ 	},
+ 
+-	// Evaluates a script in a global context
+-	globalEval: function( code, options ) {
+-		DOMEval( code, { nonce: options && options.nonce } );
++	// Evaluates a script in a provided context; falls back to the global one
++	// if not specified.
++	globalEval: function( code, options, doc ) {
++		DOMEval( code, { nonce: options && options.nonce }, doc );
+ 	},
+ 
+ 	each: function( obj, callback ) {
+@@ -379,13 +393,6 @@ jQuery.extend( {
+ 		return obj;
+ 	},
+ 
+-	// Support: Android <=4.0 only
+-	trim: function( text ) {
+-		return text == null ?
+-			"" :
+-			( text + "" ).replace( rtrim, "" );
+-	},
+-
+ 	// results is for internal usage only
+ 	makeArray: function( arr, results ) {
+ 		var ret = results || [];
+@@ -472,7 +479,7 @@ jQuery.extend( {
+ 		}
+ 
+ 		// Flatten any nested arrays
+-		return concat.apply( [], ret );
++		return flat( ret );
+ 	},
+ 
+ 	// A global GUID counter for objects
+@@ -489,7 +496,7 @@ if ( typeof Symbol === "function" ) {
+ 
+ // Populate the class2type map
+ jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ),
+-function( i, name ) {
++function( _i, name ) {
+ 	class2type[ "[object " + name + "]" ] = name.toLowerCase();
+ } );
+ 
+@@ -511,17 +518,16 @@ function isArrayLike( obj ) {
+ }
+ var Sizzle =
+ /*!
+- * Sizzle CSS Selector Engine v2.3.4
++ * Sizzle CSS Selector Engine v2.3.5
+  * https://sizzlejs.com/
+  *
+  * Copyright JS Foundation and other contributors
+  * Released under the MIT license
+  * https://js.foundation/
+  *
+- * Date: 2019-04-08
++ * Date: 2020-03-14
+  */
+-(function( window ) {
+-
++( function( window ) {
+ var i,
+ 	support,
+ 	Expr,
+@@ -561,59 +567,70 @@ var i,
+ 	},
+ 
+ 	// Instance methods
+-	hasOwn = ({}).hasOwnProperty,
++	hasOwn = ( {} ).hasOwnProperty,
+ 	arr = [],
+ 	pop = arr.pop,
+-	push_native = arr.push,
++	pushNative = arr.push,
+ 	push = arr.push,
+ 	slice = arr.slice,
++
+ 	// Use a stripped-down indexOf as it's faster than native
+ 	// https://jsperf.com/thor-indexof-vs-for/5
+ 	indexOf = function( list, elem ) {
+ 		var i = 0,
+ 			len = list.length;
+ 		for ( ; i < len; i++ ) {
+-			if ( list[i] === elem ) {
++			if ( list[ i ] === elem ) {
+ 				return i;
+ 			}
+ 		}
+ 		return -1;
+ 	},
+ 
+-	booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
++	booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|" +
++		"ismap|loop|multiple|open|readonly|required|scoped",
+ 
+ 	// Regular expressions
+ 
+ 	// http://www.w3.org/TR/css3-selectors/#whitespace
+ 	whitespace = "[\\x20\\t\\r\\n\\f]",
+ 
+-	// http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
+-	identifier = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+",
++	// https://www.w3.org/TR/css-syntax-3/#ident-token-diagram
++	identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace +
++		"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",
+ 
+ 	// Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors
+ 	attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace +
++
+ 		// Operator (capture 2)
+ 		"*([*^$|!~]?=)" + whitespace +
+-		// "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
+-		"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace +
+-		"*\\]",
++
++		// "Attribute values must be CSS identifiers [capture 5]
++		// or strings [capture 3 or capture 4]"
++		"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" +
++		whitespace + "*\\]",
+ 
+ 	pseudos = ":(" + identifier + ")(?:\\((" +
++
+ 		// To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
+ 		// 1. quoted (capture 3; capture 4 or capture 5)
+ 		"('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
++
+ 		// 2. simple (capture 6)
+ 		"((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
++
+ 		// 3. anything else (capture 2)
+ 		".*" +
+ 		")\\)|)",
+ 
+ 	// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
+ 	rwhitespace = new RegExp( whitespace + "+", "g" ),
+-	rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
++	rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" +
++		whitespace + "+$", "g" ),
+ 
+ 	rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
+-	rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ),
++	rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace +
++		"*" ),
+ 	rdescend = new RegExp( whitespace + "|>" ),
+ 
+ 	rpseudo = new RegExp( pseudos ),
+@@ -625,14 +642,16 @@ var i,
+ 		"TAG": new RegExp( "^(" + identifier + "|[*])" ),
+ 		"ATTR": new RegExp( "^" + attributes ),
+ 		"PSEUDO": new RegExp( "^" + pseudos ),
+-		"CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
+-			"*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
+-			"*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
++		"CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" +
++			whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" +
++			whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
+ 		"bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
++
+ 		// For use in libraries implementing .is()
+ 		// We use this for POS matching in `select`
+-		"needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
+-			whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
++		"needsContext": new RegExp( "^" + whitespace +
++			"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace +
++			"*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
+ 	},
+ 
+ 	rhtml = /HTML$/i,
+@@ -648,18 +667,21 @@ var i,
+ 
+ 	// CSS escapes
+ 	// http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
+-	runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
+-	funescape = function( _, escaped, escapedWhitespace ) {
+-		var high = "0x" + escaped - 0x10000;
+-		// NaN means non-codepoint
+-		// Support: Firefox<24
+-		// Workaround erroneous numeric interpretation of +"0x"
+-		return high !== high || escapedWhitespace ?
+-			escaped :
++	runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace + "?|\\\\([^\\r\\n\\f])", "g" ),
++	funescape = function( escape, nonHex ) {
++		var high = "0x" + escape.slice( 1 ) - 0x10000;
++
++		return nonHex ?
++
++			// Strip the backslash prefix from a non-hex escape sequence
++			nonHex :
++
++			// Replace a hexadecimal escape sequence with the encoded Unicode code point
++			// Support: IE <=11+
++			// For values outside the Basic Multilingual Plane (BMP), manually construct a
++			// surrogate pair
+ 			high < 0 ?
+-				// BMP codepoint
+ 				String.fromCharCode( high + 0x10000 ) :
+-				// Supplemental Plane codepoint (surrogate pair)
+ 				String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
+ 	},
+ 
+@@ -675,7 +697,8 @@ var i,
+ 			}
+ 
+ 			// Control characters and (dependent upon position) numbers get escaped as code points
+-			return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " ";
++			return ch.slice( 0, -1 ) + "\\" +
++				ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " ";
+ 		}
+ 
+ 		// Other potentially-special ASCII characters get backslash-escaped
+@@ -700,18 +723,20 @@ var i,
+ // Optimize for push.apply( _, NodeList )
+ try {
+ 	push.apply(
+-		(arr = slice.call( preferredDoc.childNodes )),
++		( arr = slice.call( preferredDoc.childNodes ) ),
+ 		preferredDoc.childNodes
+ 	);
++
+ 	// Support: Android<4.0
+ 	// Detect silently failing push.apply
++	// eslint-disable-next-line no-unused-expressions
+ 	arr[ preferredDoc.childNodes.length ].nodeType;
+ } catch ( e ) {
+ 	push = { apply: arr.length ?
+ 
+ 		// Leverage slice if possible
+ 		function( target, els ) {
+-			push_native.apply( target, slice.call(els) );
++			pushNative.apply( target, slice.call( els ) );
+ 		} :
+ 
+ 		// Support: IE<9
+@@ -719,8 +744,9 @@ try {
+ 		function( target, els ) {
+ 			var j = target.length,
+ 				i = 0;
++
+ 			// Can't trust NodeList.length
+-			while ( (target[j++] = els[i++]) ) {}
++			while ( ( target[ j++ ] = els[ i++ ] ) ) {}
+ 			target.length = j - 1;
+ 		}
+ 	};
+@@ -744,24 +770,21 @@ function Sizzle( selector, context, results, seed ) {
+ 
+ 	// Try to shortcut find operations (as opposed to filters) in HTML documents
+ 	if ( !seed ) {
+-
+-		if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
+-			setDocument( context );
+-		}
++		setDocument( context );
+ 		context = context || document;
+ 
+ 		if ( documentIsHTML ) {
+ 
+ 			// If the selector is sufficiently simple, try using a "get*By*" DOM method
+ 			// (excepting DocumentFragment context, where the methods don't exist)
+-			if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) {
++			if ( nodeType !== 11 && ( match = rquickExpr.exec( selector ) ) ) {
+ 
+ 				// ID selector
+-				if ( (m = match[1]) ) {
++				if ( ( m = match[ 1 ] ) ) {
+ 
+ 					// Document context
+ 					if ( nodeType === 9 ) {
+-						if ( (elem = context.getElementById( m )) ) {
++						if ( ( elem = context.getElementById( m ) ) ) {
+ 
+ 							// Support: IE, Opera, Webkit
+ 							// TODO: identify versions
+@@ -780,7 +803,7 @@ function Sizzle( selector, context, results, seed ) {
+ 						// Support: IE, Opera, Webkit
+ 						// TODO: identify versions
+ 						// getElementById can match elements by name instead of ID
+-						if ( newContext && (elem = newContext.getElementById( m )) &&
++						if ( newContext && ( elem = newContext.getElementById( m ) ) &&
+ 							contains( context, elem ) &&
+ 							elem.id === m ) {
+ 
+@@ -790,12 +813,12 @@ function Sizzle( selector, context, results, seed ) {
+ 					}
+ 
+ 				// Type selector
+-				} else if ( match[2] ) {
++				} else if ( match[ 2 ] ) {
+ 					push.apply( results, context.getElementsByTagName( selector ) );
+ 					return results;
+ 
+ 				// Class selector
+-				} else if ( (m = match[3]) && support.getElementsByClassName &&
++				} else if ( ( m = match[ 3 ] ) && support.getElementsByClassName &&
+ 					context.getElementsByClassName ) {
+ 
+ 					push.apply( results, context.getElementsByClassName( m ) );
+@@ -806,11 +829,11 @@ function Sizzle( selector, context, results, seed ) {
+ 			// Take advantage of querySelectorAll
+ 			if ( support.qsa &&
+ 				!nonnativeSelectorCache[ selector + " " ] &&
+-				(!rbuggyQSA || !rbuggyQSA.test( selector )) &&
++				( !rbuggyQSA || !rbuggyQSA.test( selector ) ) &&
+ 
+ 				// Support: IE 8 only
+ 				// Exclude object elements
+-				(nodeType !== 1 || context.nodeName.toLowerCase() !== "object") ) {
++				( nodeType !== 1 || context.nodeName.toLowerCase() !== "object" ) ) {
+ 
+ 				newSelector = selector;
+ 				newContext = context;
+@@ -819,27 +842,36 @@ function Sizzle( selector, context, results, seed ) {
+ 				// descendant combinators, which is not what we want.
+ 				// In such cases, we work around the behavior by prefixing every selector in the
+ 				// list with an ID selector referencing the scope context.
++				// The technique has to be used as well when a leading combinator is used
++				// as such selectors are not recognized by querySelectorAll.
+ 				// Thanks to Andrew Dupont for this technique.
+-				if ( nodeType === 1 && rdescend.test( selector ) ) {
++				if ( nodeType === 1 &&
++					( rdescend.test( selector ) || rcombinators.test( selector ) ) ) {
+ 
+-					// Capture the context ID, setting it first if necessary
+-					if ( (nid = context.getAttribute( "id" )) ) {
+-						nid = nid.replace( rcssescape, fcssescape );
+-					} else {
+-						context.setAttribute( "id", (nid = expando) );
++					// Expand context for sibling selectors
++					newContext = rsibling.test( selector ) && testContext( context.parentNode ) ||
++						context;
++
++					// We can use :scope instead of the ID hack if the browser
++					// supports it & if we're not changing the context.
++					if ( newContext !== context || !support.scope ) {
++
++						// Capture the context ID, setting it first if necessary
++						if ( ( nid = context.getAttribute( "id" ) ) ) {
++							nid = nid.replace( rcssescape, fcssescape );
++						} else {
++							context.setAttribute( "id", ( nid = expando ) );
++						}
+ 					}
+ 
+ 					// Prefix every selector in the list
+ 					groups = tokenize( selector );
+ 					i = groups.length;
+ 					while ( i-- ) {
+-						groups[i] = "#" + nid + " " + toSelector( groups[i] );
++						groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " +
++							toSelector( groups[ i ] );
+ 					}
+ 					newSelector = groups.join( "," );
+-
+-					// Expand context for sibling selectors
+-					newContext = rsibling.test( selector ) && testContext( context.parentNode ) ||
+-						context;
+ 				}
+ 
+ 				try {
+@@ -872,12 +904,14 @@ function createCache() {
+ 	var keys = [];
+ 
+ 	function cache( key, value ) {
++
+ 		// Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
+ 		if ( keys.push( key + " " ) > Expr.cacheLength ) {
++
+ 			// Only keep the most recent entries
+ 			delete cache[ keys.shift() ];
+ 		}
+-		return (cache[ key + " " ] = value);
++		return ( cache[ key + " " ] = value );
+ 	}
+ 	return cache;
+ }
+@@ -896,17 +930,19 @@ function markFunction( fn ) {
+  * @param {Function} fn Passed the created element and returns a boolean result
+  */
+ function assert( fn ) {
+-	var el = document.createElement("fieldset");
++	var el = document.createElement( "fieldset" );
+ 
+ 	try {
+ 		return !!fn( el );
+-	} catch (e) {
++	} catch ( e ) {
+ 		return false;
+ 	} finally {
++
+ 		// Remove from its parent by default
+ 		if ( el.parentNode ) {
+ 			el.parentNode.removeChild( el );
+ 		}
++
+ 		// release memory in IE
+ 		el = null;
+ 	}
+@@ -918,11 +954,11 @@ function assert( fn ) {
+  * @param {Function} handler The method that will be applied
+  */
+ function addHandle( attrs, handler ) {
+-	var arr = attrs.split("|"),
++	var arr = attrs.split( "|" ),
+ 		i = arr.length;
+ 
+ 	while ( i-- ) {
+-		Expr.attrHandle[ arr[i] ] = handler;
++		Expr.attrHandle[ arr[ i ] ] = handler;
+ 	}
+ }
+ 
+@@ -944,7 +980,7 @@ function siblingCheck( a, b ) {
+ 
+ 	// Check if b follows a
+ 	if ( cur ) {
+-		while ( (cur = cur.nextSibling) ) {
++		while ( ( cur = cur.nextSibling ) ) {
+ 			if ( cur === b ) {
+ 				return -1;
+ 			}
+@@ -972,7 +1008,7 @@ function createInputPseudo( type ) {
+ function createButtonPseudo( type ) {
+ 	return function( elem ) {
+ 		var name = elem.nodeName.toLowerCase();
+-		return (name === "input" || name === "button") && elem.type === type;
++		return ( name === "input" || name === "button" ) && elem.type === type;
+ 	};
+ }
+ 
+@@ -1015,7 +1051,7 @@ function createDisabledPseudo( disabled ) {
+ 					// Where there is no isDisabled, check manually
+ 					/* jshint -W018 */
+ 					elem.isDisabled !== !disabled &&
+-						inDisabledFieldset( elem ) === disabled;
++					inDisabledFieldset( elem ) === disabled;
+ 			}
+ 
+ 			return elem.disabled === disabled;
+@@ -1037,21 +1073,21 @@ function createDisabledPseudo( disabled ) {
+  * @param {Function} fn
+  */
+ function createPositionalPseudo( fn ) {
+-	return markFunction(function( argument ) {
++	return markFunction( function( argument ) {
+ 		argument = +argument;
+-		return markFunction(function( seed, matches ) {
++		return markFunction( function( seed, matches ) {
+ 			var j,
+ 				matchIndexes = fn( [], seed.length, argument ),
+ 				i = matchIndexes.length;
+ 
+ 			// Match elements found at the specified indexes
+ 			while ( i-- ) {
+-				if ( seed[ (j = matchIndexes[i]) ] ) {
+-					seed[j] = !(matches[j] = seed[j]);
++				if ( seed[ ( j = matchIndexes[ i ] ) ] ) {
++					seed[ j ] = !( matches[ j ] = seed[ j ] );
+ 				}
+ 			}
+-		});
+-	});
++		} );
++	} );
+ }
+ 
+ /**
+@@ -1073,7 +1109,7 @@ support = Sizzle.support = {};
+  */
+ isXML = Sizzle.isXML = function( elem ) {
+ 	var namespace = elem.namespaceURI,
+-		docElem = (elem.ownerDocument || elem).documentElement;
++		docElem = ( elem.ownerDocument || elem ).documentElement;
+ 
+ 	// Support: IE <=8
+ 	// Assume HTML when documentElement doesn't yet exist, such as inside loading iframes
+@@ -1091,7 +1127,11 @@ setDocument = Sizzle.setDocument = function( node ) {
+ 		doc = node ? node.ownerDocument || node : preferredDoc;
+ 
+ 	// Return early if doc is invalid or already selected
+-	if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
++	// Support: IE 11+, Edge 17 - 18+
++	// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
++	// two documents; shallow comparisons work.
++	// eslint-disable-next-line eqeqeq
++	if ( doc == document || doc.nodeType !== 9 || !doc.documentElement ) {
+ 		return document;
+ 	}
+ 
+@@ -1100,10 +1140,14 @@ setDocument = Sizzle.setDocument = function( node ) {
+ 	docElem = document.documentElement;
+ 	documentIsHTML = !isXML( document );
+ 
+-	// Support: IE 9-11, Edge
++	// Support: IE 9 - 11+, Edge 12 - 18+
+ 	// Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936)
+-	if ( preferredDoc !== document &&
+-		(subWindow = document.defaultView) && subWindow.top !== subWindow ) {
++	// Support: IE 11+, Edge 17 - 18+
++	// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
++	// two documents; shallow comparisons work.
++	// eslint-disable-next-line eqeqeq
++	if ( preferredDoc != document &&
++		( subWindow = document.defaultView ) && subWindow.top !== subWindow ) {
+ 
+ 		// Support: IE 11, Edge
+ 		if ( subWindow.addEventListener ) {
+@@ -1115,25 +1159,36 @@ setDocument = Sizzle.setDocument = function( node ) {
+ 		}
+ 	}
+ 
++	// Support: IE 8 - 11+, Edge 12 - 18+, Chrome <=16 - 25 only, Firefox <=3.6 - 31 only,
++	// Safari 4 - 5 only, Opera <=11.6 - 12.x only
++	// IE/Edge & older browsers don't support the :scope pseudo-class.
++	// Support: Safari 6.0 only
++	// Safari 6.0 supports :scope but it's an alias of :root there.
++	support.scope = assert( function( el ) {
++		docElem.appendChild( el ).appendChild( document.createElement( "div" ) );
++		return typeof el.querySelectorAll !== "undefined" &&
++			!el.querySelectorAll( ":scope fieldset div" ).length;
++	} );
++
+ 	/* Attributes
+ 	---------------------------------------------------------------------- */
+ 
+ 	// Support: IE<8
+ 	// Verify that getAttribute really returns attributes and not properties
+ 	// (excepting IE8 booleans)
+-	support.attributes = assert(function( el ) {
++	support.attributes = assert( function( el ) {
+ 		el.className = "i";
+-		return !el.getAttribute("className");
+-	});
++		return !el.getAttribute( "className" );
++	} );
+ 
+ 	/* getElement(s)By*
+ 	---------------------------------------------------------------------- */
+ 
+ 	// Check if getElementsByTagName("*") returns only elements
+-	support.getElementsByTagName = assert(function( el ) {
+-		el.appendChild( document.createComment("") );
+-		return !el.getElementsByTagName("*").length;
+-	});
++	support.getElementsByTagName = assert( function( el ) {
++		el.appendChild( document.createComment( "" ) );
++		return !el.getElementsByTagName( "*" ).length;
++	} );
+ 
+ 	// Support: IE<9
+ 	support.getElementsByClassName = rnative.test( document.getElementsByClassName );
+@@ -1142,38 +1197,38 @@ setDocument = Sizzle.setDocument = function( node ) {
+ 	// Check if getElementById returns elements by name
+ 	// The broken getElementById methods don't pick up programmatically-set names,
+ 	// so use a roundabout getElementsByName test
+-	support.getById = assert(function( el ) {
++	support.getById = assert( function( el ) {
+ 		docElem.appendChild( el ).id = expando;
+ 		return !document.getElementsByName || !document.getElementsByName( expando ).length;
+-	});
++	} );
+ 
+ 	// ID filter and find
+ 	if ( support.getById ) {
+-		Expr.filter["ID"] = function( id ) {
++		Expr.filter[ "ID" ] = function( id ) {
+ 			var attrId = id.replace( runescape, funescape );
+ 			return function( elem ) {
+-				return elem.getAttribute("id") === attrId;
++				return elem.getAttribute( "id" ) === attrId;
+ 			};
+ 		};
+-		Expr.find["ID"] = function( id, context ) {
++		Expr.find[ "ID" ] = function( id, context ) {
+ 			if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
+ 				var elem = context.getElementById( id );
+ 				return elem ? [ elem ] : [];
+ 			}
+ 		};
+ 	} else {
+-		Expr.filter["ID"] =  function( id ) {
++		Expr.filter[ "ID" ] =  function( id ) {
+ 			var attrId = id.replace( runescape, funescape );
+ 			return function( elem ) {
+ 				var node = typeof elem.getAttributeNode !== "undefined" &&
+-					elem.getAttributeNode("id");
++					elem.getAttributeNode( "id" );
+ 				return node && node.value === attrId;
+ 			};
+ 		};
+ 
+ 		// Support: IE 6 - 7 only
+ 		// getElementById is not reliable as a find shortcut
+-		Expr.find["ID"] = function( id, context ) {
++		Expr.find[ "ID" ] = function( id, context ) {
+ 			if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
+ 				var node, i, elems,
+ 					elem = context.getElementById( id );
+@@ -1181,7 +1236,7 @@ setDocument = Sizzle.setDocument = function( node ) {
+ 				if ( elem ) {
+ 
+ 					// Verify the id attribute
+-					node = elem.getAttributeNode("id");
++					node = elem.getAttributeNode( "id" );
+ 					if ( node && node.value === id ) {
+ 						return [ elem ];
+ 					}
+@@ -1189,8 +1244,8 @@ setDocument = Sizzle.setDocument = function( node ) {
+ 					// Fall back on getElementsByName
+ 					elems = context.getElementsByName( id );
+ 					i = 0;
+-					while ( (elem = elems[i++]) ) {
+-						node = elem.getAttributeNode("id");
++					while ( ( elem = elems[ i++ ] ) ) {
++						node = elem.getAttributeNode( "id" );
+ 						if ( node && node.value === id ) {
+ 							return [ elem ];
+ 						}
+@@ -1203,7 +1258,7 @@ setDocument = Sizzle.setDocument = function( node ) {
+ 	}
+ 
+ 	// Tag
+-	Expr.find["TAG"] = support.getElementsByTagName ?
++	Expr.find[ "TAG" ] = support.getElementsByTagName ?
+ 		function( tag, context ) {
+ 			if ( typeof context.getElementsByTagName !== "undefined" ) {
+ 				return context.getElementsByTagName( tag );
+@@ -1218,12 +1273,13 @@ setDocument = Sizzle.setDocument = function( node ) {
+ 			var elem,
+ 				tmp = [],
+ 				i = 0,
++
+ 				// By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too
+ 				results = context.getElementsByTagName( tag );
+ 
+ 			// Filter out possible comments
+ 			if ( tag === "*" ) {
+-				while ( (elem = results[i++]) ) {
++				while ( ( elem = results[ i++ ] ) ) {
+ 					if ( elem.nodeType === 1 ) {
+ 						tmp.push( elem );
+ 					}
+@@ -1235,7 +1291,7 @@ setDocument = Sizzle.setDocument = function( node ) {
+ 		};
+ 
+ 	// Class
+-	Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
++	Expr.find[ "CLASS" ] = support.getElementsByClassName && function( className, context ) {
+ 		if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) {
+ 			return context.getElementsByClassName( className );
+ 		}
+@@ -1256,10 +1312,14 @@ setDocument = Sizzle.setDocument = function( node ) {
+ 	// See https://bugs.jquery.com/ticket/13378
+ 	rbuggyQSA = [];
+ 
+-	if ( (support.qsa = rnative.test( document.querySelectorAll )) ) {
++	if ( ( support.qsa = rnative.test( document.querySelectorAll ) ) ) {
++
+ 		// Build QSA regex
+ 		// Regex strategy adopted from Diego Perini
+-		assert(function( el ) {
++		assert( function( el ) {
++
++			var input;
++
+ 			// Select is set to empty string on purpose
+ 			// This is to test IE's treatment of not explicitly
+ 			// setting a boolean content attribute,
+@@ -1273,78 +1333,98 @@ setDocument = Sizzle.setDocument = function( node ) {
+ 			// Nothing should be selected when empty strings follow ^= or $= or *=
+ 			// The test attribute must be unknown in Opera but "safe" for WinRT
+ 			// https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section
+-			if ( el.querySelectorAll("[msallowcapture^='']").length ) {
++			if ( el.querySelectorAll( "[msallowcapture^='']" ).length ) {
+ 				rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
+ 			}
+ 
+ 			// Support: IE8
+ 			// Boolean attributes and "value" are not treated correctly
+-			if ( !el.querySelectorAll("[selected]").length ) {
++			if ( !el.querySelectorAll( "[selected]" ).length ) {
+ 				rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
+ 			}
+ 
+ 			// Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+
+ 			if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) {
+-				rbuggyQSA.push("~=");
++				rbuggyQSA.push( "~=" );
++			}
++
++			// Support: IE 11+, Edge 15 - 18+
++			// IE 11/Edge don't find elements on a `[name='']` query in some cases.
++			// Adding a temporary attribute to the document before the selection works
++			// around the issue.
++			// Interestingly, IE 10 & older don't seem to have the issue.
++			input = document.createElement( "input" );
++			input.setAttribute( "name", "" );
++			el.appendChild( input );
++			if ( !el.querySelectorAll( "[name='']" ).length ) {
++				rbuggyQSA.push( "\\[" + whitespace + "*name" + whitespace + "*=" +
++					whitespace + "*(?:''|\"\")" );
+ 			}
+ 
+ 			// Webkit/Opera - :checked should return selected option elements
+ 			// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
+ 			// IE8 throws error here and will not see later tests
+-			if ( !el.querySelectorAll(":checked").length ) {
+-				rbuggyQSA.push(":checked");
++			if ( !el.querySelectorAll( ":checked" ).length ) {
++				rbuggyQSA.push( ":checked" );
+ 			}
+ 
+ 			// Support: Safari 8+, iOS 8+
+ 			// https://bugs.webkit.org/show_bug.cgi?id=136851
+ 			// In-page `selector#id sibling-combinator selector` fails
+ 			if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) {
+-				rbuggyQSA.push(".#.+[+~]");
++				rbuggyQSA.push( ".#.+[+~]" );
+ 			}
+-		});
+ 
+-		assert(function( el ) {
++			// Support: Firefox <=3.6 - 5 only
++			// Old Firefox doesn't throw on a badly-escaped identifier.
++			el.querySelectorAll( "\\\f" );
++			rbuggyQSA.push( "[\\r\\n\\f]" );
++		} );
++
++		assert( function( el ) {
+ 			el.innerHTML = "<a href='' disabled='disabled'></a>" +
+ 				"<select disabled='disabled'><option/></select>";
+ 
+ 			// Support: Windows 8 Native Apps
+ 			// The type and name attributes are restricted during .innerHTML assignment
+-			var input = document.createElement("input");
++			var input = document.createElement( "input" );
+ 			input.setAttribute( "type", "hidden" );
+ 			el.appendChild( input ).setAttribute( "name", "D" );
+ 
+ 			// Support: IE8
+ 			// Enforce case-sensitivity of name attribute
+-			if ( el.querySelectorAll("[name=d]").length ) {
++			if ( el.querySelectorAll( "[name=d]" ).length ) {
+ 				rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" );
+ 			}
+ 
+ 			// FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
+ 			// IE8 throws error here and will not see later tests
+-			if ( el.querySelectorAll(":enabled").length !== 2 ) {
++			if ( el.querySelectorAll( ":enabled" ).length !== 2 ) {
+ 				rbuggyQSA.push( ":enabled", ":disabled" );
+ 			}
+ 
+ 			// Support: IE9-11+
+ 			// IE's :disabled selector does not pick up the children of disabled fieldsets
+ 			docElem.appendChild( el ).disabled = true;
+-			if ( el.querySelectorAll(":disabled").length !== 2 ) {
++			if ( el.querySelectorAll( ":disabled" ).length !== 2 ) {
+ 				rbuggyQSA.push( ":enabled", ":disabled" );
+ 			}
+ 
++			// Support: Opera 10 - 11 only
+ 			// Opera 10-11 does not throw on post-comma invalid pseudos
+-			el.querySelectorAll("*,:x");
+-			rbuggyQSA.push(",.*:");
+-		});
++			el.querySelectorAll( "*,:x" );
++			rbuggyQSA.push( ",.*:" );
++		} );
+ 	}
+ 
+-	if ( (support.matchesSelector = rnative.test( (matches = docElem.matches ||
++	if ( ( support.matchesSelector = rnative.test( ( matches = docElem.matches ||
+ 		docElem.webkitMatchesSelector ||
+ 		docElem.mozMatchesSelector ||
+ 		docElem.oMatchesSelector ||
+-		docElem.msMatchesSelector) )) ) {
++		docElem.msMatchesSelector ) ) ) ) {
++
++		assert( function( el ) {
+ 
+-		assert(function( el ) {
+ 			// Check to see if it's possible to do matchesSelector
+ 			// on a disconnected node (IE 9)
+ 			support.disconnectedMatch = matches.call( el, "*" );
+@@ -1353,11 +1433,11 @@ setDocument = Sizzle.setDocument = function( node ) {
+ 			// Gecko does not error, returns false instead
+ 			matches.call( el, "[s!='']:x" );
+ 			rbuggyMatches.push( "!=", pseudos );
+-		});
++		} );
+ 	}
+ 
+-	rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
+-	rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );
++	rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( "|" ) );
++	rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join( "|" ) );
+ 
+ 	/* Contains
+ 	---------------------------------------------------------------------- */
+@@ -1374,11 +1454,11 @@ setDocument = Sizzle.setDocument = function( node ) {
+ 				adown.contains ?
+ 					adown.contains( bup ) :
+ 					a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
+-			));
++			) );
+ 		} :
+ 		function( a, b ) {
+ 			if ( b ) {
+-				while ( (b = b.parentNode) ) {
++				while ( ( b = b.parentNode ) ) {
+ 					if ( b === a ) {
+ 						return true;
+ 					}
+@@ -1407,7 +1487,11 @@ setDocument = Sizzle.setDocument = function( node ) {
+ 		}
+ 
+ 		// Calculate position if both inputs belong to the same document
+-		compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ?
++		// Support: IE 11+, Edge 17 - 18+
++		// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
++		// two documents; shallow comparisons work.
++		// eslint-disable-next-line eqeqeq
++		compare = ( a.ownerDocument || a ) == ( b.ownerDocument || b ) ?
+ 			a.compareDocumentPosition( b ) :
+ 
+ 			// Otherwise we know they are disconnected
+@@ -1415,13 +1499,24 @@ setDocument = Sizzle.setDocument = function( node ) {
+ 
+ 		// Disconnected nodes
+ 		if ( compare & 1 ||
+-			(!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {
++			( !support.sortDetached && b.compareDocumentPosition( a ) === compare ) ) {
+ 
+ 			// Choose the first element that is related to our preferred document
+-			if ( a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) {
++			// Support: IE 11+, Edge 17 - 18+
++			// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
++			// two documents; shallow comparisons work.
++			// eslint-disable-next-line eqeqeq
++			if ( a == document || a.ownerDocument == preferredDoc &&
++				contains( preferredDoc, a ) ) {
+ 				return -1;
+ 			}
+-			if ( b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) {
++
++			// Support: IE 11+, Edge 17 - 18+
++			// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
++			// two documents; shallow comparisons work.
++			// eslint-disable-next-line eqeqeq
++			if ( b == document || b.ownerDocument == preferredDoc &&
++				contains( preferredDoc, b ) ) {
+ 				return 1;
+ 			}
+ 
+@@ -1434,6 +1529,7 @@ setDocument = Sizzle.setDocument = function( node ) {
+ 		return compare & 4 ? -1 : 1;
+ 	} :
+ 	function( a, b ) {
++
+ 		// Exit early if the nodes are identical
+ 		if ( a === b ) {
+ 			hasDuplicate = true;
+@@ -1449,8 +1545,14 @@ setDocument = Sizzle.setDocument = function( node ) {
+ 
+ 		// Parentless nodes are either documents or disconnected
+ 		if ( !aup || !bup ) {
+-			return a === document ? -1 :
+-				b === document ? 1 :
++
++			// Support: IE 11+, Edge 17 - 18+
++			// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
++			// two documents; shallow comparisons work.
++			/* eslint-disable eqeqeq */
++			return a == document ? -1 :
++				b == document ? 1 :
++				/* eslint-enable eqeqeq */
+ 				aup ? -1 :
+ 				bup ? 1 :
+ 				sortInput ?
+@@ -1464,26 +1566,32 @@ setDocument = Sizzle.setDocument = function( node ) {
+ 
+ 		// Otherwise we need full lists of their ancestors for comparison
+ 		cur = a;
+-		while ( (cur = cur.parentNode) ) {
++		while ( ( cur = cur.parentNode ) ) {
+ 			ap.unshift( cur );
+ 		}
+ 		cur = b;
+-		while ( (cur = cur.parentNode) ) {
++		while ( ( cur = cur.parentNode ) ) {
+ 			bp.unshift( cur );
+ 		}
+ 
+ 		// Walk down the tree looking for a discrepancy
+-		while ( ap[i] === bp[i] ) {
++		while ( ap[ i ] === bp[ i ] ) {
+ 			i++;
+ 		}
+ 
+ 		return i ?
++
+ 			// Do a sibling check if the nodes have a common ancestor
+-			siblingCheck( ap[i], bp[i] ) :
++			siblingCheck( ap[ i ], bp[ i ] ) :
+ 
+ 			// Otherwise nodes in our document sort first
+-			ap[i] === preferredDoc ? -1 :
+-			bp[i] === preferredDoc ? 1 :
++			// Support: IE 11+, Edge 17 - 18+
++			// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
++			// two documents; shallow comparisons work.
++			/* eslint-disable eqeqeq */
++			ap[ i ] == preferredDoc ? -1 :
++			bp[ i ] == preferredDoc ? 1 :
++			/* eslint-enable eqeqeq */
+ 			0;
+ 	};
+ 
+@@ -1495,10 +1603,7 @@ Sizzle.matches = function( expr, elements ) {
+ };
+ 
+ Sizzle.matchesSelector = function( elem, expr ) {
+-	// Set document vars if needed
+-	if ( ( elem.ownerDocument || elem ) !== document ) {
+-		setDocument( elem );
+-	}
++	setDocument( elem );
+ 
+ 	if ( support.matchesSelector && documentIsHTML &&
+ 		!nonnativeSelectorCache[ expr + " " ] &&
+@@ -1510,12 +1615,13 @@ Sizzle.matchesSelector = function( elem, expr ) {
+ 
+ 			// IE 9's matchesSelector returns false on disconnected nodes
+ 			if ( ret || support.disconnectedMatch ||
+-					// As well, disconnected nodes are said to be in a document
+-					// fragment in IE 9
+-					elem.document && elem.document.nodeType !== 11 ) {
++
++				// As well, disconnected nodes are said to be in a document
++				// fragment in IE 9
++				elem.document && elem.document.nodeType !== 11 ) {
+ 				return ret;
+ 			}
+-		} catch (e) {
++		} catch ( e ) {
+ 			nonnativeSelectorCache( expr, true );
+ 		}
+ 	}
+@@ -1524,20 +1630,31 @@ Sizzle.matchesSelector = function( elem, expr ) {
+ };
+ 
+ Sizzle.contains = function( context, elem ) {
++
+ 	// Set document vars if needed
+-	if ( ( context.ownerDocument || context ) !== document ) {
++	// Support: IE 11+, Edge 17 - 18+
++	// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
++	// two documents; shallow comparisons work.
++	// eslint-disable-next-line eqeqeq
++	if ( ( context.ownerDocument || context ) != document ) {
+ 		setDocument( context );
+ 	}
+ 	return contains( context, elem );
+ };
+ 
+ Sizzle.attr = function( elem, name ) {
++
+ 	// Set document vars if needed
+-	if ( ( elem.ownerDocument || elem ) !== document ) {
++	// Support: IE 11+, Edge 17 - 18+
++	// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
++	// two documents; shallow comparisons work.
++	// eslint-disable-next-line eqeqeq
++	if ( ( elem.ownerDocument || elem ) != document ) {
+ 		setDocument( elem );
+ 	}
+ 
+ 	var fn = Expr.attrHandle[ name.toLowerCase() ],
++
+ 		// Don't get fooled by Object.prototype properties (jQuery #13807)
+ 		val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
+ 			fn( elem, name, !documentIsHTML ) :
+@@ -1547,13 +1664,13 @@ Sizzle.attr = function( elem, name ) {
+ 		val :
+ 		support.attributes || !documentIsHTML ?
+ 			elem.getAttribute( name ) :
+-			(val = elem.getAttributeNode(name)) && val.specified ?
++			( val = elem.getAttributeNode( name ) ) && val.specified ?
+ 				val.value :
+ 				null;
+ };
+ 
+ Sizzle.escape = function( sel ) {
+-	return (sel + "").replace( rcssescape, fcssescape );
++	return ( sel + "" ).replace( rcssescape, fcssescape );
+ };
+ 
+ Sizzle.error = function( msg ) {
+@@ -1576,7 +1693,7 @@ Sizzle.uniqueSort = function( results ) {
+ 	results.sort( sortOrder );
+ 
+ 	if ( hasDuplicate ) {
+-		while ( (elem = results[i++]) ) {
++		while ( ( elem = results[ i++ ] ) ) {
+ 			if ( elem === results[ i ] ) {
+ 				j = duplicates.push( i );
+ 			}
+@@ -1604,17 +1721,21 @@ getText = Sizzle.getText = function( elem ) {
+ 		nodeType = elem.nodeType;
+ 
+ 	if ( !nodeType ) {
++
+ 		// If no nodeType, this is expected to be an array
+-		while ( (node = elem[i++]) ) {
++		while ( ( node = elem[ i++ ] ) ) {
++
+ 			// Do not traverse comment nodes
+ 			ret += getText( node );
+ 		}
+ 	} else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
++
+ 		// Use textContent for elements
+ 		// innerText usage removed for consistency of new lines (jQuery #11153)
+ 		if ( typeof elem.textContent === "string" ) {
+ 			return elem.textContent;
+ 		} else {
++
+ 			// Traverse its children
+ 			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
+ 				ret += getText( elem );
+@@ -1623,6 +1744,7 @@ getText = Sizzle.getText = function( elem ) {
+ 	} else if ( nodeType === 3 || nodeType === 4 ) {
+ 		return elem.nodeValue;
+ 	}
++
+ 	// Do not include comment or processing instruction nodes
+ 
+ 	return ret;
+@@ -1650,19 +1772,21 @@ Expr = Sizzle.selectors = {
+ 
+ 	preFilter: {
+ 		"ATTR": function( match ) {
+-			match[1] = match[1].replace( runescape, funescape );
++			match[ 1 ] = match[ 1 ].replace( runescape, funescape );
+ 
+ 			// Move the given value to match[3] whether quoted or unquoted
+-			match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape );
++			match[ 3 ] = ( match[ 3 ] || match[ 4 ] ||
++				match[ 5 ] || "" ).replace( runescape, funescape );
+ 
+-			if ( match[2] === "~=" ) {
+-				match[3] = " " + match[3] + " ";
++			if ( match[ 2 ] === "~=" ) {
++				match[ 3 ] = " " + match[ 3 ] + " ";
+ 			}
+ 
+ 			return match.slice( 0, 4 );
+ 		},
+ 
+ 		"CHILD": function( match ) {
++
+ 			/* matches from matchExpr["CHILD"]
+ 				1 type (only|nth|...)
+ 				2 what (child|of-type)
+@@ -1673,22 +1797,25 @@ Expr = Sizzle.selectors = {
+ 				7 sign of y-component
+ 				8 y of y-component
+ 			*/
+-			match[1] = match[1].toLowerCase();
++			match[ 1 ] = match[ 1 ].toLowerCase();
++
++			if ( match[ 1 ].slice( 0, 3 ) === "nth" ) {
+ 
+-			if ( match[1].slice( 0, 3 ) === "nth" ) {
+ 				// nth-* requires argument
+-				if ( !match[3] ) {
+-					Sizzle.error( match[0] );
++				if ( !match[ 3 ] ) {
++					Sizzle.error( match[ 0 ] );
+ 				}
+ 
+ 				// numeric x and y parameters for Expr.filter.CHILD
+ 				// remember that false/true cast respectively to 0/1
+-				match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
+-				match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
++				match[ 4 ] = +( match[ 4 ] ?
++					match[ 5 ] + ( match[ 6 ] || 1 ) :
++					2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" ) );
++				match[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === "odd" );
+ 
+-			// other types prohibit arguments
+-			} else if ( match[3] ) {
+-				Sizzle.error( match[0] );
++				// other types prohibit arguments
++			} else if ( match[ 3 ] ) {
++				Sizzle.error( match[ 0 ] );
+ 			}
+ 
+ 			return match;
+@@ -1696,26 +1823,28 @@ Expr = Sizzle.selectors = {
+ 
+ 		"PSEUDO": function( match ) {
+ 			var excess,
+-				unquoted = !match[6] && match[2];
++				unquoted = !match[ 6 ] && match[ 2 ];
+ 
+-			if ( matchExpr["CHILD"].test( match[0] ) ) {
++			if ( matchExpr[ "CHILD" ].test( match[ 0 ] ) ) {
+ 				return null;
+ 			}
+ 
+ 			// Accept quoted arguments as-is
+-			if ( match[3] ) {
+-				match[2] = match[4] || match[5] || "";
++			if ( match[ 3 ] ) {
++				match[ 2 ] = match[ 4 ] || match[ 5 ] || "";
+ 
+ 			// Strip excess characters from unquoted arguments
+ 			} else if ( unquoted && rpseudo.test( unquoted ) &&
++
+ 				// Get excess from tokenize (recursively)
+-				(excess = tokenize( unquoted, true )) &&
++				( excess = tokenize( unquoted, true ) ) &&
++
+ 				// advance to the next closing parenthesis
+-				(excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
++				( excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length ) ) {
+ 
+ 				// excess is a negative index
+-				match[0] = match[0].slice( 0, excess );
+-				match[2] = unquoted.slice( 0, excess );
++				match[ 0 ] = match[ 0 ].slice( 0, excess );
++				match[ 2 ] = unquoted.slice( 0, excess );
+ 			}
+ 
+ 			// Return only captures needed by the pseudo filter method (type and argument)
+@@ -1728,7 +1857,9 @@ Expr = Sizzle.selectors = {
+ 		"TAG": function( nodeNameSelector ) {
+ 			var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
+ 			return nodeNameSelector === "*" ?
+-				function() { return true; } :
++				function() {
++					return true;
++				} :
+ 				function( elem ) {
+ 					return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
+ 				};
+@@ -1738,10 +1869,16 @@ Expr = Sizzle.selectors = {
+ 			var pattern = classCache[ className + " " ];
+ 
+ 			return pattern ||
+-				(pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
+-				classCache( className, function( elem ) {
+-					return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" );
+-				});
++				( pattern = new RegExp( "(^|" + whitespace +
++					")" + className + "(" + whitespace + "|$)" ) ) && classCache(
++						className, function( elem ) {
++							return pattern.test(
++								typeof elem.className === "string" && elem.className ||
++								typeof elem.getAttribute !== "undefined" &&
++									elem.getAttribute( "class" ) ||
++								""
++							);
++				} );
+ 		},
+ 
+ 		"ATTR": function( name, operator, check ) {
+@@ -1757,6 +1894,8 @@ Expr = Sizzle.selectors = {
+ 
+ 				result += "";
+ 
++				/* eslint-disable max-len */
++
+ 				return operator === "=" ? result === check :
+ 					operator === "!=" ? result !== check :
+ 					operator === "^=" ? check && result.indexOf( check ) === 0 :
+@@ -1765,10 +1904,12 @@ Expr = Sizzle.selectors = {
+ 					operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 :
+ 					operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
+ 					false;
++				/* eslint-enable max-len */
++
+ 			};
+ 		},
+ 
+-		"CHILD": function( type, what, argument, first, last ) {
++		"CHILD": function( type, what, _argument, first, last ) {
+ 			var simple = type.slice( 0, 3 ) !== "nth",
+ 				forward = type.slice( -4 ) !== "last",
+ 				ofType = what === "of-type";
+@@ -1780,7 +1921,7 @@ Expr = Sizzle.selectors = {
+ 					return !!elem.parentNode;
+ 				} :
+ 
+-				function( elem, context, xml ) {
++				function( elem, _context, xml ) {
+ 					var cache, uniqueCache, outerCache, node, nodeIndex, start,
+ 						dir = simple !== forward ? "nextSibling" : "previousSibling",
+ 						parent = elem.parentNode,
+@@ -1794,7 +1935,7 @@ Expr = Sizzle.selectors = {
+ 						if ( simple ) {
+ 							while ( dir ) {
+ 								node = elem;
+-								while ( (node = node[ dir ]) ) {
++								while ( ( node = node[ dir ] ) ) {
+ 									if ( ofType ?
+ 										node.nodeName.toLowerCase() === name :
+ 										node.nodeType === 1 ) {
+@@ -1802,6 +1943,7 @@ Expr = Sizzle.selectors = {
+ 										return false;
+ 									}
+ 								}
++
+ 								// Reverse direction for :only-* (if we haven't yet done so)
+ 								start = dir = type === "only" && !start && "nextSibling";
+ 							}
+@@ -1817,22 +1959,22 @@ Expr = Sizzle.selectors = {
+ 
+ 							// ...in a gzip-friendly way
+ 							node = parent;
+-							outerCache = node[ expando ] || (node[ expando ] = {});
++							outerCache = node[ expando ] || ( node[ expando ] = {} );
+ 
+ 							// Support: IE <9 only
+ 							// Defend against cloned attroperties (jQuery gh-1709)
+ 							uniqueCache = outerCache[ node.uniqueID ] ||
+-								(outerCache[ node.uniqueID ] = {});
++								( outerCache[ node.uniqueID ] = {} );
+ 
+ 							cache = uniqueCache[ type ] || [];
+ 							nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
+ 							diff = nodeIndex && cache[ 2 ];
+ 							node = nodeIndex && parent.childNodes[ nodeIndex ];
+ 
+-							while ( (node = ++nodeIndex && node && node[ dir ] ||
++							while ( ( node = ++nodeIndex && node && node[ dir ] ||
+ 
+ 								// Fallback to seeking `elem` from the start
+-								(diff = nodeIndex = 0) || start.pop()) ) {
++								( diff = nodeIndex = 0 ) || start.pop() ) ) {
+ 
+ 								// When found, cache indexes on `parent` and break
+ 								if ( node.nodeType === 1 && ++diff && node === elem ) {
+@@ -1842,16 +1984,18 @@ Expr = Sizzle.selectors = {
+ 							}
+ 
+ 						} else {
++
+ 							// Use previously-cached element index if available
+ 							if ( useCache ) {
++
+ 								// ...in a gzip-friendly way
+ 								node = elem;
+-								outerCache = node[ expando ] || (node[ expando ] = {});
++								outerCache = node[ expando ] || ( node[ expando ] = {} );
+ 
+ 								// Support: IE <9 only
+ 								// Defend against cloned attroperties (jQuery gh-1709)
+ 								uniqueCache = outerCache[ node.uniqueID ] ||
+-									(outerCache[ node.uniqueID ] = {});
++									( outerCache[ node.uniqueID ] = {} );
+ 
+ 								cache = uniqueCache[ type ] || [];
+ 								nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
+@@ -1861,9 +2005,10 @@ Expr = Sizzle.selectors = {
+ 							// xml :nth-child(...)
+ 							// or :nth-last-child(...) or :nth(-last)?-of-type(...)
+ 							if ( diff === false ) {
++
+ 								// Use the same loop as above to seek `elem` from the start
+-								while ( (node = ++nodeIndex && node && node[ dir ] ||
+-									(diff = nodeIndex = 0) || start.pop()) ) {
++								while ( ( node = ++nodeIndex && node && node[ dir ] ||
++									( diff = nodeIndex = 0 ) || start.pop() ) ) {
+ 
+ 									if ( ( ofType ?
+ 										node.nodeName.toLowerCase() === name :
+@@ -1872,12 +2017,13 @@ Expr = Sizzle.selectors = {
+ 
+ 										// Cache the index of each encountered element
+ 										if ( useCache ) {
+-											outerCache = node[ expando ] || (node[ expando ] = {});
++											outerCache = node[ expando ] ||
++												( node[ expando ] = {} );
+ 
+ 											// Support: IE <9 only
+ 											// Defend against cloned attroperties (jQuery gh-1709)
+ 											uniqueCache = outerCache[ node.uniqueID ] ||
+-												(outerCache[ node.uniqueID ] = {});
++												( outerCache[ node.uniqueID ] = {} );
+ 
+ 											uniqueCache[ type ] = [ dirruns, diff ];
+ 										}
+@@ -1898,6 +2044,7 @@ Expr = Sizzle.selectors = {
+ 		},
+ 
+ 		"PSEUDO": function( pseudo, argument ) {
++
+ 			// pseudo-class names are case-insensitive
+ 			// http://www.w3.org/TR/selectors/#pseudo-classes
+ 			// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
+@@ -1917,15 +2064,15 @@ Expr = Sizzle.selectors = {
+ 			if ( fn.length > 1 ) {
+ 				args = [ pseudo, pseudo, "", argument ];
+ 				return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
+-					markFunction(function( seed, matches ) {
++					markFunction( function( seed, matches ) {
+ 						var idx,
+ 							matched = fn( seed, argument ),
+ 							i = matched.length;
+ 						while ( i-- ) {
+-							idx = indexOf( seed, matched[i] );
+-							seed[ idx ] = !( matches[ idx ] = matched[i] );
++							idx = indexOf( seed, matched[ i ] );
++							seed[ idx ] = !( matches[ idx ] = matched[ i ] );
+ 						}
+-					}) :
++					} ) :
+ 					function( elem ) {
+ 						return fn( elem, 0, args );
+ 					};
+@@ -1936,8 +2083,10 @@ Expr = Sizzle.selectors = {
+ 	},
+ 
+ 	pseudos: {
++
+ 		// Potentially complex pseudos
+-		"not": markFunction(function( selector ) {
++		"not": markFunction( function( selector ) {
++
+ 			// Trim the selector passed to compile
+ 			// to avoid treating leading and trailing
+ 			// spaces as combinators
+@@ -1946,39 +2095,40 @@ Expr = Sizzle.selectors = {
+ 				matcher = compile( selector.replace( rtrim, "$1" ) );
+ 
+ 			return matcher[ expando ] ?
+-				markFunction(function( seed, matches, context, xml ) {
++				markFunction( function( seed, matches, _context, xml ) {
+ 					var elem,
+ 						unmatched = matcher( seed, null, xml, [] ),
+ 						i = seed.length;
+ 
+ 					// Match elements unmatched by `matcher`
+ 					while ( i-- ) {
+-						if ( (elem = unmatched[i]) ) {
+-							seed[i] = !(matches[i] = elem);
++						if ( ( elem = unmatched[ i ] ) ) {
++							seed[ i ] = !( matches[ i ] = elem );
+ 						}
+ 					}
+-				}) :
+-				function( elem, context, xml ) {
+-					input[0] = elem;
++				} ) :
++				function( elem, _context, xml ) {
++					input[ 0 ] = elem;
+ 					matcher( input, null, xml, results );
++
+ 					// Don't keep the element (issue #299)
+-					input[0] = null;
++					input[ 0 ] = null;
+ 					return !results.pop();
+ 				};
+-		}),
++		} ),
+ 
+-		"has": markFunction(function( selector ) {
++		"has": markFunction( function( selector ) {
+ 			return function( elem ) {
+ 				return Sizzle( selector, elem ).length > 0;
+ 			};
+-		}),
++		} ),
+ 
+-		"contains": markFunction(function( text ) {
++		"contains": markFunction( function( text ) {
+ 			text = text.replace( runescape, funescape );
+ 			return function( elem ) {
+ 				return ( elem.textContent || getText( elem ) ).indexOf( text ) > -1;
+ 			};
+-		}),
++		} ),
+ 
+ 		// "Whether an element is represented by a :lang() selector
+ 		// is based solely on the element's language value
+@@ -1988,25 +2138,26 @@ Expr = Sizzle.selectors = {
+ 		// The identifier C does not have to be a valid language name."
+ 		// http://www.w3.org/TR/selectors/#lang-pseudo
+ 		"lang": markFunction( function( lang ) {
++
+ 			// lang value must be a valid identifier
+-			if ( !ridentifier.test(lang || "") ) {
++			if ( !ridentifier.test( lang || "" ) ) {
+ 				Sizzle.error( "unsupported lang: " + lang );
+ 			}
+ 			lang = lang.replace( runescape, funescape ).toLowerCase();
+ 			return function( elem ) {
+ 				var elemLang;
+ 				do {
+-					if ( (elemLang = documentIsHTML ?
++					if ( ( elemLang = documentIsHTML ?
+ 						elem.lang :
+-						elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {
++						elem.getAttribute( "xml:lang" ) || elem.getAttribute( "lang" ) ) ) {
+ 
+ 						elemLang = elemLang.toLowerCase();
+ 						return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
+ 					}
+-				} while ( (elem = elem.parentNode) && elem.nodeType === 1 );
++				} while ( ( elem = elem.parentNode ) && elem.nodeType === 1 );
+ 				return false;
+ 			};
+-		}),
++		} ),
+ 
+ 		// Miscellaneous
+ 		"target": function( elem ) {
+@@ -2019,7 +2170,9 @@ Expr = Sizzle.selectors = {
+ 		},
+ 
+ 		"focus": function( elem ) {
+-			return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
++			return elem === document.activeElement &&
++				( !document.hasFocus || document.hasFocus() ) &&
++				!!( elem.type || elem.href || ~elem.tabIndex );
+ 		},
+ 
+ 		// Boolean properties
+@@ -2027,16 +2180,20 @@ Expr = Sizzle.selectors = {
+ 		"disabled": createDisabledPseudo( true ),
+ 
+ 		"checked": function( elem ) {
++
+ 			// In CSS3, :checked should return both checked and selected elements
+ 			// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
+ 			var nodeName = elem.nodeName.toLowerCase();
+-			return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
++			return ( nodeName === "input" && !!elem.checked ) ||
++				( nodeName === "option" && !!elem.selected );
+ 		},
+ 
+ 		"selected": function( elem ) {
++
+ 			// Accessing this property makes selected-by-default
+ 			// options in Safari work properly
+ 			if ( elem.parentNode ) {
++				// eslint-disable-next-line no-unused-expressions
+ 				elem.parentNode.selectedIndex;
+ 			}
+ 
+@@ -2045,6 +2202,7 @@ Expr = Sizzle.selectors = {
+ 
+ 		// Contents
+ 		"empty": function( elem ) {
++
+ 			// http://www.w3.org/TR/selectors/#empty-pseudo
+ 			// :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
+ 			//   but not by others (comment: 8; processing instruction: 7; etc.)
+@@ -2058,7 +2216,7 @@ Expr = Sizzle.selectors = {
+ 		},
+ 
+ 		"parent": function( elem ) {
+-			return !Expr.pseudos["empty"]( elem );
++			return !Expr.pseudos[ "empty" ]( elem );
+ 		},
+ 
+ 		// Element/input types
+@@ -2082,39 +2240,40 @@ Expr = Sizzle.selectors = {
+ 
+ 				// Support: IE<8
+ 				// New HTML5 attribute values (e.g., "search") appear with elem.type === "text"
+-				( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" );
++				( ( attr = elem.getAttribute( "type" ) ) == null ||
++					attr.toLowerCase() === "text" );
+ 		},
+ 
+ 		// Position-in-collection
+-		"first": createPositionalPseudo(function() {
++		"first": createPositionalPseudo( function() {
+ 			return [ 0 ];
+-		}),
++		} ),
+ 
+-		"last": createPositionalPseudo(function( matchIndexes, length ) {
++		"last": createPositionalPseudo( function( _matchIndexes, length ) {
+ 			return [ length - 1 ];
+-		}),
++		} ),
+ 
+-		"eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
++		"eq": createPositionalPseudo( function( _matchIndexes, length, argument ) {
+ 			return [ argument < 0 ? argument + length : argument ];
+-		}),
++		} ),
+ 
+-		"even": createPositionalPseudo(function( matchIndexes, length ) {
++		"even": createPositionalPseudo( function( matchIndexes, length ) {
+ 			var i = 0;
+ 			for ( ; i < length; i += 2 ) {
+ 				matchIndexes.push( i );
+ 			}
+ 			return matchIndexes;
+-		}),
++		} ),
+ 
+-		"odd": createPositionalPseudo(function( matchIndexes, length ) {
++		"odd": createPositionalPseudo( function( matchIndexes, length ) {
+ 			var i = 1;
+ 			for ( ; i < length; i += 2 ) {
+ 				matchIndexes.push( i );
+ 			}
+ 			return matchIndexes;
+-		}),
++		} ),
+ 
+-		"lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
++		"lt": createPositionalPseudo( function( matchIndexes, length, argument ) {
+ 			var i = argument < 0 ?
+ 				argument + length :
+ 				argument > length ?
+@@ -2124,19 +2283,19 @@ Expr = Sizzle.selectors = {
+ 				matchIndexes.push( i );
+ 			}
+ 			return matchIndexes;
+-		}),
++		} ),
+ 
+-		"gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
++		"gt": createPositionalPseudo( function( matchIndexes, length, argument ) {
+ 			var i = argument < 0 ? argument + length : argument;
+ 			for ( ; ++i < length; ) {
+ 				matchIndexes.push( i );
+ 			}
+ 			return matchIndexes;
+-		})
++		} )
+ 	}
+ };
+ 
+-Expr.pseudos["nth"] = Expr.pseudos["eq"];
++Expr.pseudos[ "nth" ] = Expr.pseudos[ "eq" ];
+ 
+ // Add button/input type pseudos
+ for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
+@@ -2167,37 +2326,39 @@ tokenize = Sizzle.tokenize = function( selector, parseOnly ) {
+ 	while ( soFar ) {
+ 
+ 		// Comma and first run
+-		if ( !matched || (match = rcomma.exec( soFar )) ) {
++		if ( !matched || ( match = rcomma.exec( soFar ) ) ) {
+ 			if ( match ) {
++
+ 				// Don't consume trailing commas as valid
+-				soFar = soFar.slice( match[0].length ) || soFar;
++				soFar = soFar.slice( match[ 0 ].length ) || soFar;
+ 			}
+-			groups.push( (tokens = []) );
++			groups.push( ( tokens = [] ) );
+ 		}
+ 
+ 		matched = false;
+ 
+ 		// Combinators
+-		if ( (match = rcombinators.exec( soFar )) ) {
++		if ( ( match = rcombinators.exec( soFar ) ) ) {
+ 			matched = match.shift();
+-			tokens.push({
++			tokens.push( {
+ 				value: matched,
++
+ 				// Cast descendant combinators to space
+-				type: match[0].replace( rtrim, " " )
+-			});
++				type: match[ 0 ].replace( rtrim, " " )
++			} );
+ 			soFar = soFar.slice( matched.length );
+ 		}
+ 
+ 		// Filters
+ 		for ( type in Expr.filter ) {
+-			if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
+-				(match = preFilters[ type ]( match ))) ) {
++			if ( ( match = matchExpr[ type ].exec( soFar ) ) && ( !preFilters[ type ] ||
++				( match = preFilters[ type ]( match ) ) ) ) {
+ 				matched = match.shift();
+-				tokens.push({
++				tokens.push( {
+ 					value: matched,
+ 					type: type,
+ 					matches: match
+-				});
++				} );
+ 				soFar = soFar.slice( matched.length );
+ 			}
+ 		}
+@@ -2214,6 +2375,7 @@ tokenize = Sizzle.tokenize = function( selector, parseOnly ) {
+ 		soFar.length :
+ 		soFar ?
+ 			Sizzle.error( selector ) :
++
+ 			// Cache the tokens
+ 			tokenCache( selector, groups ).slice( 0 );
+ };
+@@ -2223,7 +2385,7 @@ function toSelector( tokens ) {
+ 		len = tokens.length,
+ 		selector = "";
+ 	for ( ; i < len; i++ ) {
+-		selector += tokens[i].value;
++		selector += tokens[ i ].value;
+ 	}
+ 	return selector;
+ }
+@@ -2236,9 +2398,10 @@ function addCombinator( matcher, combinator, base ) {
+ 		doneName = done++;
+ 
+ 	return combinator.first ?
++
+ 		// Check against closest ancestor/preceding element
+ 		function( elem, context, xml ) {
+-			while ( (elem = elem[ dir ]) ) {
++			while ( ( elem = elem[ dir ] ) ) {
+ 				if ( elem.nodeType === 1 || checkNonElements ) {
+ 					return matcher( elem, context, xml );
+ 				}
+@@ -2253,7 +2416,7 @@ function addCombinator( matcher, combinator, base ) {
+ 
+ 			// We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching
+ 			if ( xml ) {
+-				while ( (elem = elem[ dir ]) ) {
++				while ( ( elem = elem[ dir ] ) ) {
+ 					if ( elem.nodeType === 1 || checkNonElements ) {
+ 						if ( matcher( elem, context, xml ) ) {
+ 							return true;
+@@ -2261,27 +2424,29 @@ function addCombinator( matcher, combinator, base ) {
+ 					}
+ 				}
+ 			} else {
+-				while ( (elem = elem[ dir ]) ) {
++				while ( ( elem = elem[ dir ] ) ) {
+ 					if ( elem.nodeType === 1 || checkNonElements ) {
+-						outerCache = elem[ expando ] || (elem[ expando ] = {});
++						outerCache = elem[ expando ] || ( elem[ expando ] = {} );
+ 
+ 						// Support: IE <9 only
+ 						// Defend against cloned attroperties (jQuery gh-1709)
+-						uniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {});
++						uniqueCache = outerCache[ elem.uniqueID ] ||
++							( outerCache[ elem.uniqueID ] = {} );
+ 
+ 						if ( skip && skip === elem.nodeName.toLowerCase() ) {
+ 							elem = elem[ dir ] || elem;
+-						} else if ( (oldCache = uniqueCache[ key ]) &&
++						} else if ( ( oldCache = uniqueCache[ key ] ) &&
+ 							oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {
+ 
+ 							// Assign to newCache so results back-propagate to previous elements
+-							return (newCache[ 2 ] = oldCache[ 2 ]);
++							return ( newCache[ 2 ] = oldCache[ 2 ] );
+ 						} else {
++
+ 							// Reuse newcache so results back-propagate to previous elements
+ 							uniqueCache[ key ] = newCache;
+ 
+ 							// A match means we're done; a fail means we have to keep checking
+-							if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) {
++							if ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) {
+ 								return true;
+ 							}
+ 						}
+@@ -2297,20 +2462,20 @@ function elementMatcher( matchers ) {
+ 		function( elem, context, xml ) {
+ 			var i = matchers.length;
+ 			while ( i-- ) {
+-				if ( !matchers[i]( elem, context, xml ) ) {
++				if ( !matchers[ i ]( elem, context, xml ) ) {
+ 					return false;
+ 				}
+ 			}
+ 			return true;
+ 		} :
+-		matchers[0];
++		matchers[ 0 ];
+ }
+ 
+ function multipleContexts( selector, contexts, results ) {
+ 	var i = 0,
+ 		len = contexts.length;
+ 	for ( ; i < len; i++ ) {
+-		Sizzle( selector, contexts[i], results );
++		Sizzle( selector, contexts[ i ], results );
+ 	}
+ 	return results;
+ }
+@@ -2323,7 +2488,7 @@ function condense( unmatched, map, filter, context, xml ) {
+ 		mapped = map != null;
+ 
+ 	for ( ; i < len; i++ ) {
+-		if ( (elem = unmatched[i]) ) {
++		if ( ( elem = unmatched[ i ] ) ) {
+ 			if ( !filter || filter( elem, context, xml ) ) {
+ 				newUnmatched.push( elem );
+ 				if ( mapped ) {
+@@ -2343,14 +2508,18 @@ function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postS
+ 	if ( postFinder && !postFinder[ expando ] ) {
+ 		postFinder = setMatcher( postFinder, postSelector );
+ 	}
+-	return markFunction(function( seed, results, context, xml ) {
++	return markFunction( function( seed, results, context, xml ) {
+ 		var temp, i, elem,
+ 			preMap = [],
+ 			postMap = [],
+ 			preexisting = results.length,
+ 
+ 			// Get initial elements from seed or context
+-			elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
++			elems = seed || multipleContexts(
++				selector || "*",
++				context.nodeType ? [ context ] : context,
++				[]
++			),
+ 
+ 			// Prefilter to get matcher input, preserving a map for seed-results synchronization
+ 			matcherIn = preFilter && ( seed || !selector ) ?
+@@ -2358,6 +2527,7 @@ function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postS
+ 				elems,
+ 
+ 			matcherOut = matcher ?
++
+ 				// If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
+ 				postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
+ 
+@@ -2381,8 +2551,8 @@ function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postS
+ 			// Un-match failing elements by moving them back to matcherIn
+ 			i = temp.length;
+ 			while ( i-- ) {
+-				if ( (elem = temp[i]) ) {
+-					matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
++				if ( ( elem = temp[ i ] ) ) {
++					matcherOut[ postMap[ i ] ] = !( matcherIn[ postMap[ i ] ] = elem );
+ 				}
+ 			}
+ 		}
+@@ -2390,25 +2560,27 @@ function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postS
+ 		if ( seed ) {
+ 			if ( postFinder || preFilter ) {
+ 				if ( postFinder ) {
++
+ 					// Get the final matcherOut by condensing this intermediate into postFinder contexts
+ 					temp = [];
+ 					i = matcherOut.length;
+ 					while ( i-- ) {
+-						if ( (elem = matcherOut[i]) ) {
++						if ( ( elem = matcherOut[ i ] ) ) {
++
+ 							// Restore matcherIn since elem is not yet a final match
+-							temp.push( (matcherIn[i] = elem) );
++							temp.push( ( matcherIn[ i ] = elem ) );
+ 						}
+ 					}
+-					postFinder( null, (matcherOut = []), temp, xml );
++					postFinder( null, ( matcherOut = [] ), temp, xml );
+ 				}
+ 
+ 				// Move matched elements from seed to results to keep them synchronized
+ 				i = matcherOut.length;
+ 				while ( i-- ) {
+-					if ( (elem = matcherOut[i]) &&
+-						(temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) {
++					if ( ( elem = matcherOut[ i ] ) &&
++						( temp = postFinder ? indexOf( seed, elem ) : preMap[ i ] ) > -1 ) {
+ 
+-						seed[temp] = !(results[temp] = elem);
++						seed[ temp ] = !( results[ temp ] = elem );
+ 					}
+ 				}
+ 			}
+@@ -2426,14 +2598,14 @@ function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postS
+ 				push.apply( results, matcherOut );
+ 			}
+ 		}
+-	});
++	} );
+ }
+ 
+ function matcherFromTokens( tokens ) {
+ 	var checkContext, matcher, j,
+ 		len = tokens.length,
+-		leadingRelative = Expr.relative[ tokens[0].type ],
+-		implicitRelative = leadingRelative || Expr.relative[" "],
++		leadingRelative = Expr.relative[ tokens[ 0 ].type ],
++		implicitRelative = leadingRelative || Expr.relative[ " " ],
+ 		i = leadingRelative ? 1 : 0,
+ 
+ 		// The foundational matcher ensures that elements are reachable from top-level context(s)
+@@ -2445,38 +2617,43 @@ function matcherFromTokens( tokens ) {
+ 		}, implicitRelative, true ),
+ 		matchers = [ function( elem, context, xml ) {
+ 			var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
+-				(checkContext = context).nodeType ?
++				( checkContext = context ).nodeType ?
+ 					matchContext( elem, context, xml ) :
+ 					matchAnyContext( elem, context, xml ) );
++
+ 			// Avoid hanging onto element (issue #299)
+ 			checkContext = null;
+ 			return ret;
+ 		} ];
+ 
+ 	for ( ; i < len; i++ ) {
+-		if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
+-			matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
++		if ( ( matcher = Expr.relative[ tokens[ i ].type ] ) ) {
++			matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ];
+ 		} else {
+-			matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
++			matcher = Expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches );
+ 
+ 			// Return special upon seeing a positional matcher
+ 			if ( matcher[ expando ] ) {
++
+ 				// Find the next relative operator (if any) for proper handling
+ 				j = ++i;
+ 				for ( ; j < len; j++ ) {
+-					if ( Expr.relative[ tokens[j].type ] ) {
++					if ( Expr.relative[ tokens[ j ].type ] ) {
+ 						break;
+ 					}
+ 				}
+ 				return setMatcher(
+ 					i > 1 && elementMatcher( matchers ),
+ 					i > 1 && toSelector(
+-						// If the preceding token was a descendant combinator, insert an implicit any-element `*`
+-						tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" })
++
++					// If the preceding token was a descendant combinator, insert an implicit any-element `*`
++					tokens
++						.slice( 0, i - 1 )
++						.concat( { value: tokens[ i - 2 ].type === " " ? "*" : "" } )
+ 					).replace( rtrim, "$1" ),
+ 					matcher,
+ 					i < j && matcherFromTokens( tokens.slice( i, j ) ),
+-					j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
++					j < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ),
+ 					j < len && toSelector( tokens )
+ 				);
+ 			}
+@@ -2497,28 +2674,40 @@ function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
+ 				unmatched = seed && [],
+ 				setMatched = [],
+ 				contextBackup = outermostContext,
++
+ 				// We must always have either seed elements or outermost context
+-				elems = seed || byElement && Expr.find["TAG"]( "*", outermost ),
++				elems = seed || byElement && Expr.find[ "TAG" ]( "*", outermost ),
++
+ 				// Use integer dirruns iff this is the outermost matcher
+-				dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1),
++				dirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 ),
+ 				len = elems.length;
+ 
+ 			if ( outermost ) {
+-				outermostContext = context === document || context || outermost;
++
++				// Support: IE 11+, Edge 17 - 18+
++				// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
++				// two documents; shallow comparisons work.
++				// eslint-disable-next-line eqeqeq
++				outermostContext = context == document || context || outermost;
+ 			}
+ 
+ 			// Add elements passing elementMatchers directly to results
+ 			// Support: IE<9, Safari
+ 			// Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id
+-			for ( ; i !== len && (elem = elems[i]) != null; i++ ) {
++			for ( ; i !== len && ( elem = elems[ i ] ) != null; i++ ) {
+ 				if ( byElement && elem ) {
+ 					j = 0;
+-					if ( !context && elem.ownerDocument !== document ) {
++
++					// Support: IE 11+, Edge 17 - 18+
++					// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
++					// two documents; shallow comparisons work.
++					// eslint-disable-next-line eqeqeq
++					if ( !context && elem.ownerDocument != document ) {
+ 						setDocument( elem );
+ 						xml = !documentIsHTML;
+ 					}
+-					while ( (matcher = elementMatchers[j++]) ) {
+-						if ( matcher( elem, context || document, xml) ) {
++					while ( ( matcher = elementMatchers[ j++ ] ) ) {
++						if ( matcher( elem, context || document, xml ) ) {
+ 							results.push( elem );
+ 							break;
+ 						}
+@@ -2530,8 +2719,9 @@ function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
+ 
+ 				// Track unmatched elements for set filters
+ 				if ( bySet ) {
++
+ 					// They will have gone through all possible matchers
+-					if ( (elem = !matcher && elem) ) {
++					if ( ( elem = !matcher && elem ) ) {
+ 						matchedCount--;
+ 					}
+ 
+@@ -2555,16 +2745,17 @@ function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
+ 			// numerically zero.
+ 			if ( bySet && i !== matchedCount ) {
+ 				j = 0;
+-				while ( (matcher = setMatchers[j++]) ) {
++				while ( ( matcher = setMatchers[ j++ ] ) ) {
+ 					matcher( unmatched, setMatched, context, xml );
+ 				}
+ 
+ 				if ( seed ) {
++
+ 					// Reintegrate element matches to eliminate the need for sorting
+ 					if ( matchedCount > 0 ) {
+ 						while ( i-- ) {
+-							if ( !(unmatched[i] || setMatched[i]) ) {
+-								setMatched[i] = pop.call( results );
++							if ( !( unmatched[ i ] || setMatched[ i ] ) ) {
++								setMatched[ i ] = pop.call( results );
+ 							}
+ 						}
+ 					}
+@@ -2605,13 +2796,14 @@ compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {
+ 		cached = compilerCache[ selector + " " ];
+ 
+ 	if ( !cached ) {
++
+ 		// Generate a function of recursive functions that can be used to check each element
+ 		if ( !match ) {
+ 			match = tokenize( selector );
+ 		}
+ 		i = match.length;
+ 		while ( i-- ) {
+-			cached = matcherFromTokens( match[i] );
++			cached = matcherFromTokens( match[ i ] );
+ 			if ( cached[ expando ] ) {
+ 				setMatchers.push( cached );
+ 			} else {
+@@ -2620,7 +2812,10 @@ compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {
+ 		}
+ 
+ 		// Cache the compiled function
+-		cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
++		cached = compilerCache(
++			selector,
++			matcherFromGroupMatchers( elementMatchers, setMatchers )
++		);
+ 
+ 		// Save selector and tokenization
+ 		cached.selector = selector;
+@@ -2640,7 +2835,7 @@ compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {
+ select = Sizzle.select = function( selector, context, results, seed ) {
+ 	var i, tokens, token, type, find,
+ 		compiled = typeof selector === "function" && selector,
+-		match = !seed && tokenize( (selector = compiled.selector || selector) );
++		match = !seed && tokenize( ( selector = compiled.selector || selector ) );
+ 
+ 	results = results || [];
+ 
+@@ -2649,11 +2844,12 @@ select = Sizzle.select = function( selector, context, results, seed ) {
+ 	if ( match.length === 1 ) {
+ 
+ 		// Reduce context if the leading compound selector is an ID
+-		tokens = match[0] = match[0].slice( 0 );
+-		if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
+-				context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[1].type ] ) {
++		tokens = match[ 0 ] = match[ 0 ].slice( 0 );
++		if ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === "ID" &&
++			context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) {
+ 
+-			context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
++			context = ( Expr.find[ "ID" ]( token.matches[ 0 ]
++				.replace( runescape, funescape ), context ) || [] )[ 0 ];
+ 			if ( !context ) {
+ 				return results;
+ 
+@@ -2666,20 +2862,22 @@ select = Sizzle.select = function( selector, context, results, seed ) {
+ 		}
+ 
+ 		// Fetch a seed set for right-to-left matching
+-		i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
++		i = matchExpr[ "needsContext" ].test( selector ) ? 0 : tokens.length;
+ 		while ( i-- ) {
+-			token = tokens[i];
++			token = tokens[ i ];
+ 
+ 			// Abort if we hit a combinator
+-			if ( Expr.relative[ (type = token.type) ] ) {
++			if ( Expr.relative[ ( type = token.type ) ] ) {
+ 				break;
+ 			}
+-			if ( (find = Expr.find[ type ]) ) {
++			if ( ( find = Expr.find[ type ] ) ) {
++
+ 				// Search, expanding context for leading sibling combinators
+-				if ( (seed = find(
+-					token.matches[0].replace( runescape, funescape ),
+-					rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context
+-				)) ) {
++				if ( ( seed = find(
++					token.matches[ 0 ].replace( runescape, funescape ),
++					rsibling.test( tokens[ 0 ].type ) && testContext( context.parentNode ) ||
++						context
++				) ) ) {
+ 
+ 					// If seed is empty or no tokens remain, we can return early
+ 					tokens.splice( i, 1 );
+@@ -2710,7 +2908,7 @@ select = Sizzle.select = function( selector, context, results, seed ) {
+ // One-time assignments
+ 
+ // Sort stability
+-support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;
++support.sortStable = expando.split( "" ).sort( sortOrder ).join( "" ) === expando;
+ 
+ // Support: Chrome 14-35+
+ // Always assume duplicates if they aren't passed to the comparison function
+@@ -2721,58 +2919,59 @@ setDocument();
+ 
+ // Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
+ // Detached nodes confoundingly follow *each other*
+-support.sortDetached = assert(function( el ) {
++support.sortDetached = assert( function( el ) {
++
+ 	// Should return 1, but returns 4 (following)
+-	return el.compareDocumentPosition( document.createElement("fieldset") ) & 1;
+-});
++	return el.compareDocumentPosition( document.createElement( "fieldset" ) ) & 1;
++} );
+ 
+ // Support: IE<8
+ // Prevent attribute/property "interpolation"
+ // https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
+-if ( !assert(function( el ) {
++if ( !assert( function( el ) {
+ 	el.innerHTML = "<a href='#'></a>";
+-	return el.firstChild.getAttribute("href") === "#" ;
+-}) ) {
++	return el.firstChild.getAttribute( "href" ) === "#";
++} ) ) {
+ 	addHandle( "type|href|height|width", function( elem, name, isXML ) {
+ 		if ( !isXML ) {
+ 			return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
+ 		}
+-	});
++	} );
+ }
+ 
+ // Support: IE<9
+ // Use defaultValue in place of getAttribute("value")
+-if ( !support.attributes || !assert(function( el ) {
++if ( !support.attributes || !assert( function( el ) {
+ 	el.innerHTML = "<input/>";
+ 	el.firstChild.setAttribute( "value", "" );
+ 	return el.firstChild.getAttribute( "value" ) === "";
+-}) ) {
+-	addHandle( "value", function( elem, name, isXML ) {
++} ) ) {
++	addHandle( "value", function( elem, _name, isXML ) {
+ 		if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
+ 			return elem.defaultValue;
+ 		}
+-	});
++	} );
+ }
+ 
+ // Support: IE<9
+ // Use getAttributeNode to fetch booleans when getAttribute lies
+-if ( !assert(function( el ) {
+-	return el.getAttribute("disabled") == null;
+-}) ) {
++if ( !assert( function( el ) {
++	return el.getAttribute( "disabled" ) == null;
++} ) ) {
+ 	addHandle( booleans, function( elem, name, isXML ) {
+ 		var val;
+ 		if ( !isXML ) {
+ 			return elem[ name ] === true ? name.toLowerCase() :
+-					(val = elem.getAttributeNode( name )) && val.specified ?
++				( val = elem.getAttributeNode( name ) ) && val.specified ?
+ 					val.value :
+-				null;
++					null;
+ 		}
+-	});
++	} );
+ }
+ 
+ return Sizzle;
+ 
+-})( window );
++} )( window );
+ 
+ 
+ 
+@@ -3141,7 +3340,7 @@ jQuery.each( {
+ 	parents: function( elem ) {
+ 		return dir( elem, "parentNode" );
+ 	},
+-	parentsUntil: function( elem, i, until ) {
++	parentsUntil: function( elem, _i, until ) {
+ 		return dir( elem, "parentNode", until );
+ 	},
+ 	next: function( elem ) {
+@@ -3156,10 +3355,10 @@ jQuery.each( {
+ 	prevAll: function( elem ) {
+ 		return dir( elem, "previousSibling" );
+ 	},
+-	nextUntil: function( elem, i, until ) {
++	nextUntil: function( elem, _i, until ) {
+ 		return dir( elem, "nextSibling", until );
+ 	},
+-	prevUntil: function( elem, i, until ) {
++	prevUntil: function( elem, _i, until ) {
+ 		return dir( elem, "previousSibling", until );
+ 	},
+ 	siblings: function( elem ) {
+@@ -3169,7 +3368,13 @@ jQuery.each( {
+ 		return siblings( elem.firstChild );
+ 	},
+ 	contents: function( elem ) {
+-		if ( typeof elem.contentDocument !== "undefined" ) {
++		if ( elem.contentDocument != null &&
++
++			// Support: IE 11+
++			// <object> elements with no `data` attribute has an object
++			// `contentDocument` with a `null` prototype.
++			getProto( elem.contentDocument ) ) {
++
+ 			return elem.contentDocument;
+ 		}
+ 
+@@ -3512,7 +3717,7 @@ jQuery.extend( {
+ 					var fns = arguments;
+ 
+ 					return jQuery.Deferred( function( newDefer ) {
+-						jQuery.each( tuples, function( i, tuple ) {
++						jQuery.each( tuples, function( _i, tuple ) {
+ 
+ 							// Map tuples (progress, done, fail) to arguments (done, fail, progress)
+ 							var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ];
+@@ -3965,7 +4170,7 @@ var access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
+ 			// ...except when executing function values
+ 			} else {
+ 				bulk = fn;
+-				fn = function( elem, key, value ) {
++				fn = function( elem, _key, value ) {
+ 					return bulk.call( jQuery( elem ), value );
+ 				};
+ 			}
+@@ -4000,7 +4205,7 @@ var rmsPrefix = /^-ms-/,
+ 	rdashAlpha = /-([a-z])/g;
+ 
+ // Used by camelCase as callback to replace()
+-function fcamelCase( all, letter ) {
++function fcamelCase( _all, letter ) {
+ 	return letter.toUpperCase();
+ }
+ 
+@@ -4528,27 +4733,6 @@ var isHiddenWithinTree = function( elem, el ) {
+ 			jQuery.css( elem, "display" ) === "none";
+ 	};
+ 
+-var swap = function( elem, options, callback, args ) {
+-	var ret, name,
+-		old = {};
+-
+-	// Remember the old values, and insert the new ones
+-	for ( name in options ) {
+-		old[ name ] = elem.style[ name ];
+-		elem.style[ name ] = options[ name ];
+-	}
+-
+-	ret = callback.apply( elem, args || [] );
+-
+-	// Revert the old values
+-	for ( name in options ) {
+-		elem.style[ name ] = old[ name ];
+-	}
+-
+-	return ret;
+-};
+-
+-
+ 
+ 
+ function adjustCSS( elem, prop, valueParts, tween ) {
+@@ -4719,11 +4903,40 @@ var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i );
+ 
+ 
+ 
+-// We have to close these tags to support XHTML (#13200)
+-var wrapMap = {
++( function() {
++	var fragment = document.createDocumentFragment(),
++		div = fragment.appendChild( document.createElement( "div" ) ),
++		input = document.createElement( "input" );
++
++	// Support: Android 4.0 - 4.3 only
++	// Check state lost if the name is set (#11217)
++	// Support: Windows Web Apps (WWA)
++	// `name` and `type` must use .setAttribute for WWA (#14901)
++	input.setAttribute( "type", "radio" );
++	input.setAttribute( "checked", "checked" );
++	input.setAttribute( "name", "t" );
++
++	div.appendChild( input );
++
++	// Support: Android <=4.1 only
++	// Older WebKit doesn't clone checked state correctly in fragments
++	support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;
++
++	// Support: IE <=11 only
++	// Make sure textarea (and checkbox) defaultValue is properly cloned
++	div.innerHTML = "<textarea>x</textarea>";
++	support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;
+ 
+ 	// Support: IE <=9 only
+-	option: [ 1, "<select multiple='multiple'>", "</select>" ],
++	// IE <=9 replaces <option> tags with their contents when inserted outside of
++	// the select element.
++	div.innerHTML = "<option></option>";
++	support.option = !!div.lastChild;
++} )();
++
++
++// We have to close these tags to support XHTML (#13200)
++var wrapMap = {
+ 
+ 	// XHTML parsers do not magically insert elements in the
+ 	// same way that tag soup parsers do. So we cannot shorten
+@@ -4736,12 +4949,14 @@ var wrapMap = {
+ 	_default: [ 0, "", "" ]
+ };
+ 
+-// Support: IE <=9 only
+-wrapMap.optgroup = wrapMap.option;
+-
+ wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
+ wrapMap.th = wrapMap.td;
+ 
++// Support: IE <=9 only
++if ( !support.option ) {
++	wrapMap.optgroup = wrapMap.option = [ 1, "<select multiple='multiple'>", "</select>" ];
++}
++
+ 
+ function getAll( context, tag ) {
+ 
+@@ -4874,32 +5089,6 @@ function buildFragment( elems, context, scripts, selection, ignored ) {
+ }
+ 
+ 
+-( function() {
+-	var fragment = document.createDocumentFragment(),
+-		div = fragment.appendChild( document.createElement( "div" ) ),
+-		input = document.createElement( "input" );
+-
+-	// Support: Android 4.0 - 4.3 only
+-	// Check state lost if the name is set (#11217)
+-	// Support: Windows Web Apps (WWA)
+-	// `name` and `type` must use .setAttribute for WWA (#14901)
+-	input.setAttribute( "type", "radio" );
+-	input.setAttribute( "checked", "checked" );
+-	input.setAttribute( "name", "t" );
+-
+-	div.appendChild( input );
+-
+-	// Support: Android <=4.1 only
+-	// Older WebKit doesn't clone checked state correctly in fragments
+-	support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;
+-
+-	// Support: IE <=11 only
+-	// Make sure textarea (and checkbox) defaultValue is properly cloned
+-	div.innerHTML = "<textarea>x</textarea>";
+-	support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;
+-} )();
+-
+-
+ var
+ 	rkeyEvent = /^key/,
+ 	rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/,
+@@ -5008,8 +5197,8 @@ jQuery.event = {
+ 			special, handlers, type, namespaces, origType,
+ 			elemData = dataPriv.get( elem );
+ 
+-		// Don't attach events to noData or text/comment nodes (but allow plain objects)
+-		if ( !elemData ) {
++		// Only attach events to objects that accept data
++		if ( !acceptData( elem ) ) {
+ 			return;
+ 		}
+ 
+@@ -5033,7 +5222,7 @@ jQuery.event = {
+ 
+ 		// Init the element's event structure and main handler, if this is the first
+ 		if ( !( events = elemData.events ) ) {
+-			events = elemData.events = {};
++			events = elemData.events = Object.create( null );
+ 		}
+ 		if ( !( eventHandle = elemData.handle ) ) {
+ 			eventHandle = elemData.handle = function( e ) {
+@@ -5191,12 +5380,15 @@ jQuery.event = {
+ 
+ 	dispatch: function( nativeEvent ) {
+ 
+-		// Make a writable jQuery.Event from the native event object
+-		var event = jQuery.event.fix( nativeEvent );
+-
+ 		var i, j, ret, matched, handleObj, handlerQueue,
+ 			args = new Array( arguments.length ),
+-			handlers = ( dataPriv.get( this, "events" ) || {} )[ event.type ] || [],
++
++			// Make a writable jQuery.Event from the native event object
++			event = jQuery.event.fix( nativeEvent ),
++
++			handlers = (
++					dataPriv.get( this, "events" ) || Object.create( null )
++				)[ event.type ] || [],
+ 			special = jQuery.event.special[ event.type ] || {};
+ 
+ 		// Use the fix-ed jQuery.Event rather than the (read-only) native event
+@@ -5771,13 +5963,6 @@ jQuery.fn.extend( {
+ 
+ var
+ 
+-	/* eslint-disable max-len */
+-
+-	// See https://github.com/eslint/eslint/issues/3229
+-	rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi,
+-
+-	/* eslint-enable */
+-
+ 	// Support: IE <=10 - 11, Edge 12 - 13 only
+ 	// In IE/Edge using regex groups here causes severe slowdowns.
+ 	// See https://connect.microsoft.com/IE/feedback/details/1736512/
+@@ -5814,7 +5999,7 @@ function restoreScript( elem ) {
+ }
+ 
+ function cloneCopyEvent( src, dest ) {
+-	var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events;
++	var i, l, type, pdataOld, udataOld, udataCur, events;
+ 
+ 	if ( dest.nodeType !== 1 ) {
+ 		return;
+@@ -5822,13 +6007,11 @@ function cloneCopyEvent( src, dest ) {
+ 
+ 	// 1. Copy private data: events, handlers, etc.
+ 	if ( dataPriv.hasData( src ) ) {
+-		pdataOld = dataPriv.access( src );
+-		pdataCur = dataPriv.set( dest, pdataOld );
++		pdataOld = dataPriv.get( src );
+ 		events = pdataOld.events;
+ 
+ 		if ( events ) {
+-			delete pdataCur.handle;
+-			pdataCur.events = {};
++			dataPriv.remove( dest, "handle events" );
+ 
+ 			for ( type in events ) {
+ 				for ( i = 0, l = events[ type ].length; i < l; i++ ) {
+@@ -5864,7 +6047,7 @@ function fixInput( src, dest ) {
+ function domManip( collection, args, callback, ignored ) {
+ 
+ 	// Flatten any nested arrays
+-	args = concat.apply( [], args );
++	args = flat( args );
+ 
+ 	var fragment, first, scripts, hasScripts, node, doc,
+ 		i = 0,
+@@ -5939,7 +6122,7 @@ function domManip( collection, args, callback, ignored ) {
+ 							if ( jQuery._evalUrl && !node.noModule ) {
+ 								jQuery._evalUrl( node.src, {
+ 									nonce: node.nonce || node.getAttribute( "nonce" )
+-								} );
++								}, doc );
+ 							}
+ 						} else {
+ 							DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc );
+@@ -5976,7 +6159,7 @@ function remove( elem, selector, keepData ) {
+ 
+ jQuery.extend( {
+ 	htmlPrefilter: function( html ) {
+-		return html.replace( rxhtmlTag, "<$1></$2>" );
++		return html;
+ 	},
+ 
+ 	clone: function( elem, dataAndEvents, deepDataAndEvents ) {
+@@ -6238,6 +6421,27 @@ var getStyles = function( elem ) {
+ 		return view.getComputedStyle( elem );
+ 	};
+ 
++var swap = function( elem, options, callback ) {
++	var ret, name,
++		old = {};
++
++	// Remember the old values, and insert the new ones
++	for ( name in options ) {
++		old[ name ] = elem.style[ name ];
++		elem.style[ name ] = options[ name ];
++	}
++
++	ret = callback.call( elem );
++
++	// Revert the old values
++	for ( name in options ) {
++		elem.style[ name ] = old[ name ];
++	}
++
++	return ret;
++};
++
++
+ var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" );
+ 
+ 
+@@ -6295,7 +6499,7 @@ var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" );
+ 	}
+ 
+ 	var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal,
+-		reliableMarginLeftVal,
++		reliableTrDimensionsVal, reliableMarginLeftVal,
+ 		container = document.createElement( "div" ),
+ 		div = document.createElement( "div" );
+ 
+@@ -6330,6 +6534,35 @@ var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" );
+ 		scrollboxSize: function() {
+ 			computeStyleTests();
+ 			return scrollboxSizeVal;
++		},
++
++		// Support: IE 9 - 11+, Edge 15 - 18+
++		// IE/Edge misreport `getComputedStyle` of table rows with width/height
++		// set in CSS while `offset*` properties report correct values.
++		// Behavior in IE 9 is more subtle than in newer versions & it passes
++		// some versions of this test; make sure not to make it pass there!
++		reliableTrDimensions: function() {
++			var table, tr, trChild, trStyle;
++			if ( reliableTrDimensionsVal == null ) {
++				table = document.createElement( "table" );
++				tr = document.createElement( "tr" );
++				trChild = document.createElement( "div" );
++
++				table.style.cssText = "position:absolute;left:-11111px";
++				tr.style.height = "1px";
++				trChild.style.height = "9px";
++
++				documentElement
++					.appendChild( table )
++					.appendChild( tr )
++					.appendChild( trChild );
++
++				trStyle = window.getComputedStyle( tr );
++				reliableTrDimensionsVal = parseInt( trStyle.height ) > 3;
++
++				documentElement.removeChild( table );
++			}
++			return reliableTrDimensionsVal;
+ 		}
+ 	} );
+ } )();
+@@ -6454,7 +6687,7 @@ var
+ 		fontWeight: "400"
+ 	};
+ 
+-function setPositiveNumber( elem, value, subtract ) {
++function setPositiveNumber( _elem, value, subtract ) {
+ 
+ 	// Any relative (+/-) values have already been
+ 	// normalized at this point
+@@ -6559,17 +6792,26 @@ function getWidthOrHeight( elem, dimension, extra ) {
+ 	}
+ 
+ 
+-	// Fall back to offsetWidth/offsetHeight when value is "auto"
+-	// This happens for inline elements with no explicit setting (gh-3571)
+-	// Support: Android <=4.1 - 4.3 only
+-	// Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602)
+-	// Support: IE 9-11 only
+-	// Also use offsetWidth/offsetHeight for when box sizing is unreliable
+-	// We use getClientRects() to check for hidden/disconnected.
+-	// In those cases, the computed value can be trusted to be border-box
++	// Support: IE 9 - 11 only
++	// Use offsetWidth/offsetHeight for when box sizing is unreliable.
++	// In those cases, the computed value can be trusted to be border-box.
+ 	if ( ( !support.boxSizingReliable() && isBorderBox ||
++
++		// Support: IE 10 - 11+, Edge 15 - 18+
++		// IE/Edge misreport `getComputedStyle` of table rows with width/height
++		// set in CSS while `offset*` properties report correct values.
++		// Interestingly, in some cases IE 9 doesn't suffer from this issue.
++		!support.reliableTrDimensions() && nodeName( elem, "tr" ) ||
++
++		// Fall back to offsetWidth/offsetHeight when value is "auto"
++		// This happens for inline elements with no explicit setting (gh-3571)
+ 		val === "auto" ||
++
++		// Support: Android <=4.1 - 4.3 only
++		// Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602)
+ 		!parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) &&
++
++		// Make sure the element is visible & connected
+ 		elem.getClientRects().length ) {
+ 
+ 		isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box";
+@@ -6764,7 +7006,7 @@ jQuery.extend( {
+ 	}
+ } );
+ 
+-jQuery.each( [ "height", "width" ], function( i, dimension ) {
++jQuery.each( [ "height", "width" ], function( _i, dimension ) {
+ 	jQuery.cssHooks[ dimension ] = {
+ 		get: function( elem, computed, extra ) {
+ 			if ( computed ) {
+@@ -7537,7 +7779,7 @@ jQuery.fn.extend( {
+ 			clearQueue = type;
+ 			type = undefined;
+ 		}
+-		if ( clearQueue && type !== false ) {
++		if ( clearQueue ) {
+ 			this.queue( type || "fx", [] );
+ 		}
+ 
+@@ -7620,7 +7862,7 @@ jQuery.fn.extend( {
+ 	}
+ } );
+ 
+-jQuery.each( [ "toggle", "show", "hide" ], function( i, name ) {
++jQuery.each( [ "toggle", "show", "hide" ], function( _i, name ) {
+ 	var cssFn = jQuery.fn[ name ];
+ 	jQuery.fn[ name ] = function( speed, easing, callback ) {
+ 		return speed == null || typeof speed === "boolean" ?
+@@ -7841,7 +8083,7 @@ boolHook = {
+ 	}
+ };
+ 
+-jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) {
++jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( _i, name ) {
+ 	var getter = attrHandle[ name ] || jQuery.find.attr;
+ 
+ 	attrHandle[ name ] = function( elem, name, isXML ) {
+@@ -8465,7 +8707,9 @@ jQuery.extend( jQuery.event, {
+ 				special.bindType || type;
+ 
+ 			// jQuery handler
+-			handle = ( dataPriv.get( cur, "events" ) || {} )[ event.type ] &&
++			handle = (
++					dataPriv.get( cur, "events" ) || Object.create( null )
++				)[ event.type ] &&
+ 				dataPriv.get( cur, "handle" );
+ 			if ( handle ) {
+ 				handle.apply( cur, data );
+@@ -8576,7 +8820,10 @@ if ( !support.focusin ) {
+ 
+ 		jQuery.event.special[ fix ] = {
+ 			setup: function() {
+-				var doc = this.ownerDocument || this,
++
++				// Handle: regular nodes (via `this.ownerDocument`), window
++				// (via `this.document`) & document (via `this`).
++				var doc = this.ownerDocument || this.document || this,
+ 					attaches = dataPriv.access( doc, fix );
+ 
+ 				if ( !attaches ) {
+@@ -8585,7 +8832,7 @@ if ( !support.focusin ) {
+ 				dataPriv.access( doc, fix, ( attaches || 0 ) + 1 );
+ 			},
+ 			teardown: function() {
+-				var doc = this.ownerDocument || this,
++				var doc = this.ownerDocument || this.document || this,
+ 					attaches = dataPriv.access( doc, fix ) - 1;
+ 
+ 				if ( !attaches ) {
+@@ -8601,7 +8848,7 @@ if ( !support.focusin ) {
+ }
+ var location = window.location;
+ 
+-var nonce = Date.now();
++var nonce = { guid: Date.now() };
+ 
+ var rquery = ( /\?/ );
+ 
+@@ -8733,7 +8980,7 @@ jQuery.fn.extend( {
+ 				rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
+ 				( this.checked || !rcheckableType.test( type ) );
+ 		} )
+-		.map( function( i, elem ) {
++		.map( function( _i, elem ) {
+ 			var val = jQuery( this ).val();
+ 
+ 			if ( val == null ) {
+@@ -9346,7 +9593,8 @@ jQuery.extend( {
+ 			// Add or update anti-cache param if needed
+ 			if ( s.cache === false ) {
+ 				cacheURL = cacheURL.replace( rantiCache, "$1" );
+-				uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce++ ) + uncached;
++				uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce.guid++ ) +
++					uncached;
+ 			}
+ 
+ 			// Put hash and anti-cache on the URL that will be requested (gh-1732)
+@@ -9479,6 +9727,11 @@ jQuery.extend( {
+ 				response = ajaxHandleResponses( s, jqXHR, responses );
+ 			}
+ 
++			// Use a noop converter for missing script
++			if ( !isSuccess && jQuery.inArray( "script", s.dataTypes ) > -1 ) {
++				s.converters[ "text script" ] = function() {};
++			}
++
+ 			// Convert no matter what (that way responseXXX fields are always set)
+ 			response = ajaxConvert( s, response, jqXHR, isSuccess );
+ 
+@@ -9569,7 +9822,7 @@ jQuery.extend( {
+ 	}
+ } );
+ 
+-jQuery.each( [ "get", "post" ], function( i, method ) {
++jQuery.each( [ "get", "post" ], function( _i, method ) {
+ 	jQuery[ method ] = function( url, data, callback, type ) {
+ 
+ 		// Shift arguments if data argument was omitted
+@@ -9590,8 +9843,17 @@ jQuery.each( [ "get", "post" ], function( i, method ) {
+ 	};
+ } );
+ 
++jQuery.ajaxPrefilter( function( s ) {
++	var i;
++	for ( i in s.headers ) {
++		if ( i.toLowerCase() === "content-type" ) {
++			s.contentType = s.headers[ i ] || "";
++		}
++	}
++} );
++
+ 
+-jQuery._evalUrl = function( url, options ) {
++jQuery._evalUrl = function( url, options, doc ) {
+ 	return jQuery.ajax( {
+ 		url: url,
+ 
+@@ -9609,7 +9871,7 @@ jQuery._evalUrl = function( url, options ) {
+ 			"text script": function() {}
+ 		},
+ 		dataFilter: function( response ) {
+-			jQuery.globalEval( response, options );
++			jQuery.globalEval( response, options, doc );
+ 		}
+ 	} );
+ };
+@@ -9931,7 +10193,7 @@ var oldCallbacks = [],
+ jQuery.ajaxSetup( {
+ 	jsonp: "callback",
+ 	jsonpCallback: function() {
+-		var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) );
++		var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce.guid++ ) );
+ 		this[ callback ] = true;
+ 		return callback;
+ 	}
+@@ -10148,23 +10410,6 @@ jQuery.fn.load = function( url, params, callback ) {
+ 
+ 
+ 
+-// Attach a bunch of functions for handling common AJAX events
+-jQuery.each( [
+-	"ajaxStart",
+-	"ajaxStop",
+-	"ajaxComplete",
+-	"ajaxError",
+-	"ajaxSuccess",
+-	"ajaxSend"
+-], function( i, type ) {
+-	jQuery.fn[ type ] = function( fn ) {
+-		return this.on( type, fn );
+-	};
+-} );
+-
+-
+-
+-
+ jQuery.expr.pseudos.animated = function( elem ) {
+ 	return jQuery.grep( jQuery.timers, function( fn ) {
+ 		return elem === fn.elem;
+@@ -10221,6 +10466,12 @@ jQuery.offset = {
+ 			options.using.call( elem, props );
+ 
+ 		} else {
++			if ( typeof props.top === "number" ) {
++				props.top += "px";
++			}
++			if ( typeof props.left === "number" ) {
++				props.left += "px";
++			}
+ 			curElem.css( props );
+ 		}
+ 	}
+@@ -10371,7 +10622,7 @@ jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function(
+ // Blink bug: https://bugs.chromium.org/p/chromium/issues/detail?id=589347
+ // getComputedStyle returns percent when specified for top/left/bottom/right;
+ // rather than make the css module depend on the offset module, just check for it here
+-jQuery.each( [ "top", "left" ], function( i, prop ) {
++jQuery.each( [ "top", "left" ], function( _i, prop ) {
+ 	jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition,
+ 		function( elem, computed ) {
+ 			if ( computed ) {
+@@ -10434,25 +10685,19 @@ jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
+ } );
+ 
+ 
+-jQuery.each( ( "blur focus focusin focusout resize scroll click dblclick " +
+-	"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
+-	"change select submit keydown keypress keyup contextmenu" ).split( " " ),
+-	function( i, name ) {
+-
+-	// Handle event binding
+-	jQuery.fn[ name ] = function( data, fn ) {
+-		return arguments.length > 0 ?
+-			this.on( name, null, data, fn ) :
+-			this.trigger( name );
++jQuery.each( [
++	"ajaxStart",
++	"ajaxStop",
++	"ajaxComplete",
++	"ajaxError",
++	"ajaxSuccess",
++	"ajaxSend"
++], function( _i, type ) {
++	jQuery.fn[ type ] = function( fn ) {
++		return this.on( type, fn );
+ 	};
+ } );
+ 
+-jQuery.fn.extend( {
+-	hover: function( fnOver, fnOut ) {
+-		return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
+-	}
+-} );
+-
+ 
+ 
+ 
+@@ -10474,9 +10719,33 @@ jQuery.fn.extend( {
+ 		return arguments.length === 1 ?
+ 			this.off( selector, "**" ) :
+ 			this.off( types, selector || "**", fn );
++	},
++
++	hover: function( fnOver, fnOut ) {
++		return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
+ 	}
+ } );
+ 
++jQuery.each( ( "blur focus focusin focusout resize scroll click dblclick " +
++	"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
++	"change select submit keydown keypress keyup contextmenu" ).split( " " ),
++	function( _i, name ) {
++
++		// Handle event binding
++		jQuery.fn[ name ] = function( data, fn ) {
++			return arguments.length > 0 ?
++				this.on( name, null, data, fn ) :
++				this.trigger( name );
++		};
++	} );
++
++
++
++
++// Support: Android <=4.0 only
++// Make sure we trim BOM and NBSP
++var rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;
++
+ // Bind a function to a context, optionally partially applying any
+ // arguments.
+ // jQuery.proxy is deprecated to promote standards (specifically Function#bind)
+@@ -10539,6 +10808,11 @@ jQuery.isNumeric = function( obj ) {
+ 		!isNaN( obj - parseFloat( obj ) );
+ };
+ 
++jQuery.trim = function( text ) {
++	return text == null ?
++		"" :
++		( text + "" ).replace( rtrim, "" );
++};
+ 
+ 
+ 
+@@ -10587,7 +10861,7 @@ jQuery.noConflict = function( deep ) {
+ // Expose jQuery and $ identifiers, even in AMD
+ // (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
+ // and CommonJS for browser emulators (#13566)
+-if ( !noGlobal ) {
++if ( typeof noGlobal === "undefined" ) {
+ 	window.jQuery = window.$ = jQuery;
+ }
+ 
+-- 
+1.8.3.1
+
+
+From bd5da67acf1dfaa6641d45abc97910d7fabf1ed4 Mon Sep 17 00:00:00 2001
+From: Alexander Scheel <ascheel@redhat.com>
+Date: Mon, 16 Mar 2020 12:03:07 -0400
+Subject: [PATCH 14/17] Upgrade to bootstrap v3.4.1
+
+Signed-off-by: Alexander Scheel <ascheel@redhat.com>
+(cherry picked from commit a12e68ba4e479d5af1b4ba32080890337ccbf8ff)
+---
+ base/server/share/webapps/pki/js/bootstrap.js | 1858 ++++++++++++++++---------
+ 1 file changed, 1216 insertions(+), 642 deletions(-)
+
+diff --git a/base/server/share/webapps/pki/js/bootstrap.js b/base/server/share/webapps/pki/js/bootstrap.js
+index 850e6e5..170bd60 100644
+--- a/base/server/share/webapps/pki/js/bootstrap.js
++++ b/base/server/share/webapps/pki/js/bootstrap.js
+@@ -1,44 +1,44 @@
+ /*!
+- * Bootstrap v3.0.3 (http://getbootstrap.com)
+- * Copyright 2013 Twitter, Inc.
+- * Licensed under http://www.apache.org/licenses/LICENSE-2.0
++ * Bootstrap v3.4.1 (https://getbootstrap.com/)
++ * Copyright 2011-2019 Twitter, Inc.
++ * Licensed under the MIT license
+  */
+ 
+-if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery") }
++if (typeof jQuery === 'undefined') {
++  throw new Error('Bootstrap\'s JavaScript requires jQuery')
++}
++
+++function ($) {
++  'use strict';
++  var version = $.fn.jquery.split(' ')[0].split('.')
++  if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1) || (version[0] > 3)) {
++    throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4')
++  }
++}(jQuery);
+ 
+ /* ========================================================================
+- * Bootstrap: transition.js v3.0.3
+- * http://getbootstrap.com/javascript/#transitions
++ * Bootstrap: transition.js v3.4.1
++ * https://getbootstrap.com/docs/3.4/javascript/#transitions
+  * ========================================================================
+- * Copyright 2013 Twitter, Inc.
+- *
+- * Licensed under the Apache License, Version 2.0 (the "License");
+- * you may not use this file except in compliance with the License.
+- * You may obtain a copy of the License at
+- *
+- * http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
++ * Copyright 2011-2019 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+  * ======================================================================== */
+ 
+ 
+-+function ($) { "use strict";
+++function ($) {
++  'use strict';
+ 
+-  // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
++  // CSS TRANSITION SUPPORT (Shoutout: https://modernizr.com/)
+   // ============================================================
+ 
+   function transitionEnd() {
+     var el = document.createElement('bootstrap')
+ 
+     var transEndEventNames = {
+-      'WebkitTransition' : 'webkitTransitionEnd'
+-    , 'MozTransition'    : 'transitionend'
+-    , 'OTransition'      : 'oTransitionEnd otransitionend'
+-    , 'transition'       : 'transitionend'
++      WebkitTransition : 'webkitTransitionEnd',
++      MozTransition    : 'transitionend',
++      OTransition      : 'oTransitionEnd otransitionend',
++      transition       : 'transitionend'
+     }
+ 
+     for (var name in transEndEventNames) {
+@@ -46,12 +46,15 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+         return { end: transEndEventNames[name] }
+       }
+     }
++
++    return false // explicit for ie8 (  ._.)
+   }
+ 
+-  // http://blog.alexmaccaw.com/css-transitions
++  // https://blog.alexmaccaw.com/css-transitions
+   $.fn.emulateTransitionEnd = function (duration) {
+-    var called = false, $el = this
+-    $(this).one($.support.transition.end, function () { called = true })
++    var called = false
++    var $el = this
++    $(this).one('bsTransitionEnd', function () { called = true })
+     var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
+     setTimeout(callback, duration)
+     return this
+@@ -59,31 +62,31 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+ 
+   $(function () {
+     $.support.transition = transitionEnd()
++
++    if (!$.support.transition) return
++
++    $.event.special.bsTransitionEnd = {
++      bindType: $.support.transition.end,
++      delegateType: $.support.transition.end,
++      handle: function (e) {
++        if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments)
++      }
++    }
+   })
+ 
+ }(jQuery);
+ 
+ /* ========================================================================
+- * Bootstrap: alert.js v3.0.3
+- * http://getbootstrap.com/javascript/#alerts
++ * Bootstrap: alert.js v3.4.1
++ * https://getbootstrap.com/docs/3.4/javascript/#alerts
+  * ========================================================================
+- * Copyright 2013 Twitter, Inc.
+- *
+- * Licensed under the Apache License, Version 2.0 (the "License");
+- * you may not use this file except in compliance with the License.
+- * You may obtain a copy of the License at
+- *
+- * http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
++ * Copyright 2011-2019 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+  * ======================================================================== */
+ 
+ 
+-+function ($) { "use strict";
+++function ($) {
++  'use strict';
+ 
+   // ALERT CLASS DEFINITION
+   // ======================
+@@ -93,6 +96,10 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+     $(el).on('click', dismiss, this.close)
+   }
+ 
++  Alert.VERSION = '3.4.1'
++
++  Alert.TRANSITION_DURATION = 150
++
+   Alert.prototype.close = function (e) {
+     var $this    = $(this)
+     var selector = $this.attr('data-target')
+@@ -102,12 +109,13 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+       selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
+     }
+ 
+-    var $parent = $(selector)
++    selector    = selector === '#' ? [] : selector
++    var $parent = $(document).find(selector)
+ 
+     if (e) e.preventDefault()
+ 
+     if (!$parent.length) {
+-      $parent = $this.hasClass('alert') ? $this : $this.parent()
++      $parent = $this.closest('.alert')
+     }
+ 
+     $parent.trigger(e = $.Event('close.bs.alert'))
+@@ -117,13 +125,14 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+     $parent.removeClass('in')
+ 
+     function removeElement() {
+-      $parent.trigger('closed.bs.alert').remove()
++      // detach from parent, fire event then clean up data
++      $parent.detach().trigger('closed.bs.alert').remove()
+     }
+ 
+     $.support.transition && $parent.hasClass('fade') ?
+       $parent
+-        .one($.support.transition.end, removeElement)
+-        .emulateTransitionEnd(150) :
++        .one('bsTransitionEnd', removeElement)
++        .emulateTransitionEnd(Alert.TRANSITION_DURATION) :
+       removeElement()
+   }
+ 
+@@ -131,9 +140,7 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+   // ALERT PLUGIN DEFINITION
+   // =======================
+ 
+-  var old = $.fn.alert
+-
+-  $.fn.alert = function (option) {
++  function Plugin(option) {
+     return this.each(function () {
+       var $this = $(this)
+       var data  = $this.data('bs.alert')
+@@ -143,6 +150,9 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+     })
+   }
+ 
++  var old = $.fn.alert
++
++  $.fn.alert             = Plugin
+   $.fn.alert.Constructor = Alert
+ 
+ 
+@@ -163,35 +173,28 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+ }(jQuery);
+ 
+ /* ========================================================================
+- * Bootstrap: button.js v3.0.3
+- * http://getbootstrap.com/javascript/#buttons
++ * Bootstrap: button.js v3.4.1
++ * https://getbootstrap.com/docs/3.4/javascript/#buttons
+  * ========================================================================
+- * Copyright 2013 Twitter, Inc.
+- *
+- * Licensed under the Apache License, Version 2.0 (the "License");
+- * you may not use this file except in compliance with the License.
+- * You may obtain a copy of the License at
+- *
+- * http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
++ * Copyright 2011-2019 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+  * ======================================================================== */
+ 
+ 
+-+function ($) { "use strict";
+++function ($) {
++  'use strict';
+ 
+   // BUTTON PUBLIC CLASS DEFINITION
+   // ==============================
+ 
+   var Button = function (element, options) {
+-    this.$element = $(element)
+-    this.options  = $.extend({}, Button.DEFAULTS, options)
++    this.$element  = $(element)
++    this.options   = $.extend({}, Button.DEFAULTS, options)
++    this.isLoading = false
+   }
+ 
++  Button.VERSION  = '3.4.1'
++
+   Button.DEFAULTS = {
+     loadingText: 'loading...'
+   }
+@@ -202,46 +205,51 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+     var val  = $el.is('input') ? 'val' : 'html'
+     var data = $el.data()
+ 
+-    state = state + 'Text'
++    state += 'Text'
+ 
+-    if (!data.resetText) $el.data('resetText', $el[val]())
+-
+-    $el[val](data[state] || this.options[state])
++    if (data.resetText == null) $el.data('resetText', $el[val]())
+ 
+     // push to event loop to allow forms to submit
+-    setTimeout(function () {
+-      state == 'loadingText' ?
+-        $el.addClass(d).attr(d, d) :
+-        $el.removeClass(d).removeAttr(d);
+-    }, 0)
++    setTimeout($.proxy(function () {
++      $el[val](data[state] == null ? this.options[state] : data[state])
++
++      if (state == 'loadingText') {
++        this.isLoading = true
++        $el.addClass(d).attr(d, d).prop(d, true)
++      } else if (this.isLoading) {
++        this.isLoading = false
++        $el.removeClass(d).removeAttr(d).prop(d, false)
++      }
++    }, this), 0)
+   }
+ 
+   Button.prototype.toggle = function () {
+-    var $parent = this.$element.closest('[data-toggle="buttons"]')
+     var changed = true
++    var $parent = this.$element.closest('[data-toggle="buttons"]')
+ 
+     if ($parent.length) {
+       var $input = this.$element.find('input')
+-      if ($input.prop('type') === 'radio') {
+-        // see if clicking on current one
+-        if ($input.prop('checked') && this.$element.hasClass('active'))
+-          changed = false
+-        else
+-          $parent.find('.active').removeClass('active')
++      if ($input.prop('type') == 'radio') {
++        if ($input.prop('checked')) changed = false
++        $parent.find('.active').removeClass('active')
++        this.$element.addClass('active')
++      } else if ($input.prop('type') == 'checkbox') {
++        if (($input.prop('checked')) !== this.$element.hasClass('active')) changed = false
++        this.$element.toggleClass('active')
+       }
+-      if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change')
++      $input.prop('checked', this.$element.hasClass('active'))
++      if (changed) $input.trigger('change')
++    } else {
++      this.$element.attr('aria-pressed', !this.$element.hasClass('active'))
++      this.$element.toggleClass('active')
+     }
+-
+-    if (changed) this.$element.toggleClass('active')
+   }
+ 
+ 
+   // BUTTON PLUGIN DEFINITION
+   // ========================
+ 
+-  var old = $.fn.button
+-
+-  $.fn.button = function (option) {
++  function Plugin(option) {
+     return this.each(function () {
+       var $this   = $(this)
+       var data    = $this.data('bs.button')
+@@ -254,6 +262,9 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+     })
+   }
+ 
++  var old = $.fn.button
++
++  $.fn.button             = Plugin
+   $.fn.button.Constructor = Button
+ 
+ 
+@@ -269,36 +280,35 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+   // BUTTON DATA-API
+   // ===============
+ 
+-  $(document).on('click.bs.button.data-api', '[data-toggle^=button]', function (e) {
+-    var $btn = $(e.target)
+-    if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
+-    $btn.button('toggle')
+-    e.preventDefault()
+-  })
++  $(document)
++    .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) {
++      var $btn = $(e.target).closest('.btn')
++      Plugin.call($btn, 'toggle')
++      if (!($(e.target).is('input[type="radio"], input[type="checkbox"]'))) {
++        // Prevent double click on radios, and the double selections (so cancellation) on checkboxes
++        e.preventDefault()
++        // The target component still receive the focus
++        if ($btn.is('input,button')) $btn.trigger('focus')
++        else $btn.find('input:visible,button:visible').first().trigger('focus')
++      }
++    })
++    .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) {
++      $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type))
++    })
+ 
+ }(jQuery);
+ 
+ /* ========================================================================
+- * Bootstrap: carousel.js v3.0.3
+- * http://getbootstrap.com/javascript/#carousel
++ * Bootstrap: carousel.js v3.4.1
++ * https://getbootstrap.com/docs/3.4/javascript/#carousel
+  * ========================================================================
+- * Copyright 2013 Twitter, Inc.
+- *
+- * Licensed under the Apache License, Version 2.0 (the "License");
+- * you may not use this file except in compliance with the License.
+- * You may obtain a copy of the License at
+- *
+- * http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
++ * Copyright 2011-2019 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+  * ======================================================================== */
+ 
+ 
+-+function ($) { "use strict";
+++function ($) {
++  'use strict';
+ 
+   // CAROUSEL CLASS DEFINITION
+   // =========================
+@@ -307,24 +317,42 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+     this.$element    = $(element)
+     this.$indicators = this.$element.find('.carousel-indicators')
+     this.options     = options
+-    this.paused      =
+-    this.sliding     =
+-    this.interval    =
+-    this.$active     =
++    this.paused      = null
++    this.sliding     = null
++    this.interval    = null
++    this.$active     = null
+     this.$items      = null
+ 
+-    this.options.pause == 'hover' && this.$element
+-      .on('mouseenter', $.proxy(this.pause, this))
+-      .on('mouseleave', $.proxy(this.cycle, this))
++    this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this))
++
++    this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element
++      .on('mouseenter.bs.carousel', $.proxy(this.pause, this))
++      .on('mouseleave.bs.carousel', $.proxy(this.cycle, this))
+   }
+ 
++  Carousel.VERSION  = '3.4.1'
++
++  Carousel.TRANSITION_DURATION = 600
++
+   Carousel.DEFAULTS = {
+-    interval: 5000
+-  , pause: 'hover'
+-  , wrap: true
++    interval: 5000,
++    pause: 'hover',
++    wrap: true,
++    keyboard: true
++  }
++
++  Carousel.prototype.keydown = function (e) {
++    if (/input|textarea/i.test(e.target.tagName)) return
++    switch (e.which) {
++      case 37: this.prev(); break
++      case 39: this.next(); break
++      default: return
++    }
++
++    e.preventDefault()
+   }
+ 
+-  Carousel.prototype.cycle =  function (e) {
++  Carousel.prototype.cycle = function (e) {
+     e || (this.paused = false)
+ 
+     this.interval && clearInterval(this.interval)
+@@ -336,29 +364,37 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+     return this
+   }
+ 
+-  Carousel.prototype.getActiveIndex = function () {
+-    this.$active = this.$element.find('.item.active')
+-    this.$items  = this.$active.parent().children()
++  Carousel.prototype.getItemIndex = function (item) {
++    this.$items = item.parent().children('.item')
++    return this.$items.index(item || this.$active)
++  }
+ 
+-    return this.$items.index(this.$active)
++  Carousel.prototype.getItemForDirection = function (direction, active) {
++    var activeIndex = this.getItemIndex(active)
++    var willWrap = (direction == 'prev' && activeIndex === 0)
++                || (direction == 'next' && activeIndex == (this.$items.length - 1))
++    if (willWrap && !this.options.wrap) return active
++    var delta = direction == 'prev' ? -1 : 1
++    var itemIndex = (activeIndex + delta) % this.$items.length
++    return this.$items.eq(itemIndex)
+   }
+ 
+   Carousel.prototype.to = function (pos) {
+     var that        = this
+-    var activeIndex = this.getActiveIndex()
++    var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active'))
+ 
+     if (pos > (this.$items.length - 1) || pos < 0) return
+ 
+-    if (this.sliding)       return this.$element.one('slid.bs.carousel', function () { that.to(pos) })
++    if (this.sliding)       return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid"
+     if (activeIndex == pos) return this.pause().cycle()
+ 
+-    return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos]))
++    return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos))
+   }
+ 
+   Carousel.prototype.pause = function (e) {
+     e || (this.paused = true)
+ 
+-    if (this.$element.find('.next, .prev').length && $.support.transition.end) {
++    if (this.$element.find('.next, .prev').length && $.support.transition) {
+       this.$element.trigger($.support.transition.end)
+       this.cycle(true)
+     }
+@@ -380,55 +416,54 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+ 
+   Carousel.prototype.slide = function (type, next) {
+     var $active   = this.$element.find('.item.active')
+-    var $next     = next || $active[type]()
++    var $next     = next || this.getItemForDirection(type, $active)
+     var isCycling = this.interval
+     var direction = type == 'next' ? 'left' : 'right'
+-    var fallback  = type == 'next' ? 'first' : 'last'
+     var that      = this
+ 
+-    if (!$next.length) {
+-      if (!this.options.wrap) return
+-      $next = this.$element.find('.item')[fallback]()
+-    }
++    if ($next.hasClass('active')) return (this.sliding = false)
++
++    var relatedTarget = $next[0]
++    var slideEvent = $.Event('slide.bs.carousel', {
++      relatedTarget: relatedTarget,
++      direction: direction
++    })
++    this.$element.trigger(slideEvent)
++    if (slideEvent.isDefaultPrevented()) return
+ 
+     this.sliding = true
+ 
+     isCycling && this.pause()
+ 
+-    var e = $.Event('slide.bs.carousel', { relatedTarget: $next[0], direction: direction })
+-
+-    if ($next.hasClass('active')) return
+-
+     if (this.$indicators.length) {
+       this.$indicators.find('.active').removeClass('active')
+-      this.$element.one('slid.bs.carousel', function () {
+-        var $nextIndicator = $(that.$indicators.children()[that.getActiveIndex()])
+-        $nextIndicator && $nextIndicator.addClass('active')
+-      })
++      var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)])
++      $nextIndicator && $nextIndicator.addClass('active')
+     }
+ 
++    var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid"
+     if ($.support.transition && this.$element.hasClass('slide')) {
+-      this.$element.trigger(e)
+-      if (e.isDefaultPrevented()) return
+       $next.addClass(type)
+-      $next[0].offsetWidth // force reflow
++      if (typeof $next === 'object' && $next.length) {
++        $next[0].offsetWidth // force reflow
++      }
+       $active.addClass(direction)
+       $next.addClass(direction)
+       $active
+-        .one($.support.transition.end, function () {
++        .one('bsTransitionEnd', function () {
+           $next.removeClass([type, direction].join(' ')).addClass('active')
+           $active.removeClass(['active', direction].join(' '))
+           that.sliding = false
+-          setTimeout(function () { that.$element.trigger('slid.bs.carousel') }, 0)
++          setTimeout(function () {
++            that.$element.trigger(slidEvent)
++          }, 0)
+         })
+-        .emulateTransitionEnd(600)
++        .emulateTransitionEnd(Carousel.TRANSITION_DURATION)
+     } else {
+-      this.$element.trigger(e)
+-      if (e.isDefaultPrevented()) return
+       $active.removeClass('active')
+       $next.addClass('active')
+       this.sliding = false
+-      this.$element.trigger('slid.bs.carousel')
++      this.$element.trigger(slidEvent)
+     }
+ 
+     isCycling && this.cycle()
+@@ -440,9 +475,7 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+   // CAROUSEL PLUGIN DEFINITION
+   // ==========================
+ 
+-  var old = $.fn.carousel
+-
+-  $.fn.carousel = function (option) {
++  function Plugin(option) {
+     return this.each(function () {
+       var $this   = $(this)
+       var data    = $this.data('bs.carousel')
+@@ -456,6 +489,9 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+     })
+   }
+ 
++  var old = $.fn.carousel
++
++  $.fn.carousel             = Plugin
+   $.fn.carousel.Constructor = Carousel
+ 
+ 
+@@ -471,52 +507,56 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+   // CAROUSEL DATA-API
+   // =================
+ 
+-  $(document).on('click.bs.carousel.data-api', '[data-slide], [data-slide-to]', function (e) {
+-    var $this   = $(this), href
+-    var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
++  var clickHandler = function (e) {
++    var $this   = $(this)
++    var href    = $this.attr('href')
++    if (href) {
++      href = href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
++    }
++
++    var target  = $this.attr('data-target') || href
++    var $target = $(document).find(target)
++
++    if (!$target.hasClass('carousel')) return
++
+     var options = $.extend({}, $target.data(), $this.data())
+     var slideIndex = $this.attr('data-slide-to')
+     if (slideIndex) options.interval = false
+ 
+-    $target.carousel(options)
++    Plugin.call($target, options)
+ 
+-    if (slideIndex = $this.attr('data-slide-to')) {
++    if (slideIndex) {
+       $target.data('bs.carousel').to(slideIndex)
+     }
+ 
+     e.preventDefault()
+-  })
++  }
++
++  $(document)
++    .on('click.bs.carousel.data-api', '[data-slide]', clickHandler)
++    .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler)
+ 
+   $(window).on('load', function () {
+     $('[data-ride="carousel"]').each(function () {
+       var $carousel = $(this)
+-      $carousel.carousel($carousel.data())
++      Plugin.call($carousel, $carousel.data())
+     })
+   })
+ 
+ }(jQuery);
+ 
+ /* ========================================================================
+- * Bootstrap: collapse.js v3.0.3
+- * http://getbootstrap.com/javascript/#collapse
++ * Bootstrap: collapse.js v3.4.1
++ * https://getbootstrap.com/docs/3.4/javascript/#collapse
+  * ========================================================================
+- * Copyright 2013 Twitter, Inc.
+- *
+- * Licensed under the Apache License, Version 2.0 (the "License");
+- * you may not use this file except in compliance with the License.
+- * You may obtain a copy of the License at
+- *
+- * http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
++ * Copyright 2011-2019 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+  * ======================================================================== */
+ 
++/* jshint latedef: false */
+ 
+-+function ($) { "use strict";
+++function ($) {
++  'use strict';
+ 
+   // COLLAPSE PUBLIC CLASS DEFINITION
+   // ================================
+@@ -524,12 +564,23 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+   var Collapse = function (element, options) {
+     this.$element      = $(element)
+     this.options       = $.extend({}, Collapse.DEFAULTS, options)
++    this.$trigger      = $('[data-toggle="collapse"][href="#' + element.id + '"],' +
++                           '[data-toggle="collapse"][data-target="#' + element.id + '"]')
+     this.transitioning = null
+ 
+-    if (this.options.parent) this.$parent = $(this.options.parent)
++    if (this.options.parent) {
++      this.$parent = this.getParent()
++    } else {
++      this.addAriaAndCollapsedClass(this.$element, this.$trigger)
++    }
++
+     if (this.options.toggle) this.toggle()
+   }
+ 
++  Collapse.VERSION  = '3.4.1'
++
++  Collapse.TRANSITION_DURATION = 350
++
+   Collapse.DEFAULTS = {
+     toggle: true
+   }
+@@ -542,35 +593,43 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+   Collapse.prototype.show = function () {
+     if (this.transitioning || this.$element.hasClass('in')) return
+ 
++    var activesData
++    var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing')
++
++    if (actives && actives.length) {
++      activesData = actives.data('bs.collapse')
++      if (activesData && activesData.transitioning) return
++    }
++
+     var startEvent = $.Event('show.bs.collapse')
+     this.$element.trigger(startEvent)
+     if (startEvent.isDefaultPrevented()) return
+ 
+-    var actives = this.$parent && this.$parent.find('> .panel > .in')
+-
+     if (actives && actives.length) {
+-      var hasData = actives.data('bs.collapse')
+-      if (hasData && hasData.transitioning) return
+-      actives.collapse('hide')
+-      hasData || actives.data('bs.collapse', null)
++      Plugin.call(actives, 'hide')
++      activesData || actives.data('bs.collapse', null)
+     }
+ 
+     var dimension = this.dimension()
+ 
+     this.$element
+       .removeClass('collapse')
+-      .addClass('collapsing')
+-      [dimension](0)
++      .addClass('collapsing')[dimension](0)
++      .attr('aria-expanded', true)
++
++    this.$trigger
++      .removeClass('collapsed')
++      .attr('aria-expanded', true)
+ 
+     this.transitioning = 1
+ 
+     var complete = function () {
+       this.$element
+         .removeClass('collapsing')
+-        .addClass('in')
+-        [dimension]('auto')
++        .addClass('collapse in')[dimension]('')
+       this.transitioning = 0
+-      this.$element.trigger('shown.bs.collapse')
++      this.$element
++        .trigger('shown.bs.collapse')
+     }
+ 
+     if (!$.support.transition) return complete.call(this)
+@@ -578,9 +637,8 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+     var scrollSize = $.camelCase(['scroll', dimension].join('-'))
+ 
+     this.$element
+-      .one($.support.transition.end, $.proxy(complete, this))
+-      .emulateTransitionEnd(350)
+-      [dimension](this.$element[0][scrollSize])
++      .one('bsTransitionEnd', $.proxy(complete, this))
++      .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize])
+   }
+ 
+   Collapse.prototype.hide = function () {
+@@ -592,54 +650,85 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+ 
+     var dimension = this.dimension()
+ 
+-    this.$element
+-      [dimension](this.$element[dimension]())
+-      [0].offsetHeight
++    this.$element[dimension](this.$element[dimension]())[0].offsetHeight
+ 
+     this.$element
+       .addClass('collapsing')
+-      .removeClass('collapse')
+-      .removeClass('in')
++      .removeClass('collapse in')
++      .attr('aria-expanded', false)
++
++    this.$trigger
++      .addClass('collapsed')
++      .attr('aria-expanded', false)
+ 
+     this.transitioning = 1
+ 
+     var complete = function () {
+       this.transitioning = 0
+       this.$element
+-        .trigger('hidden.bs.collapse')
+         .removeClass('collapsing')
+         .addClass('collapse')
++        .trigger('hidden.bs.collapse')
+     }
+ 
+     if (!$.support.transition) return complete.call(this)
+ 
+     this.$element
+       [dimension](0)
+-      .one($.support.transition.end, $.proxy(complete, this))
+-      .emulateTransitionEnd(350)
++      .one('bsTransitionEnd', $.proxy(complete, this))
++      .emulateTransitionEnd(Collapse.TRANSITION_DURATION)
+   }
+ 
+   Collapse.prototype.toggle = function () {
+     this[this.$element.hasClass('in') ? 'hide' : 'show']()
+   }
+ 
++  Collapse.prototype.getParent = function () {
++    return $(document).find(this.options.parent)
++      .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]')
++      .each($.proxy(function (i, element) {
++        var $element = $(element)
++        this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element)
++      }, this))
++      .end()
++  }
++
++  Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) {
++    var isOpen = $element.hasClass('in')
++
++    $element.attr('aria-expanded', isOpen)
++    $trigger
++      .toggleClass('collapsed', !isOpen)
++      .attr('aria-expanded', isOpen)
++  }
++
++  function getTargetFromTrigger($trigger) {
++    var href
++    var target = $trigger.attr('data-target')
++      || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
++
++    return $(document).find(target)
++  }
++
+ 
+   // COLLAPSE PLUGIN DEFINITION
+   // ==========================
+ 
+-  var old = $.fn.collapse
+-
+-  $.fn.collapse = function (option) {
++  function Plugin(option) {
+     return this.each(function () {
+       var $this   = $(this)
+       var data    = $this.data('bs.collapse')
+       var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)
+ 
++      if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false
+       if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
+       if (typeof option == 'string') data[option]()
+     })
+   }
+ 
++  var old = $.fn.collapse
++
++  $.fn.collapse             = Plugin
+   $.fn.collapse.Constructor = Collapse
+ 
+ 
+@@ -655,58 +744,77 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+   // COLLAPSE DATA-API
+   // =================
+ 
+-  $(document).on('click.bs.collapse.data-api', '[data-toggle=collapse]', function (e) {
+-    var $this   = $(this), href
+-    var target  = $this.attr('data-target')
+-        || e.preventDefault()
+-        || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
+-    var $target = $(target)
++  $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) {
++    var $this   = $(this)
++
++    if (!$this.attr('data-target')) e.preventDefault()
++
++    var $target = getTargetFromTrigger($this)
+     var data    = $target.data('bs.collapse')
+     var option  = data ? 'toggle' : $this.data()
+-    var parent  = $this.attr('data-parent')
+-    var $parent = parent && $(parent)
+-
+-    if (!data || !data.transitioning) {
+-      if ($parent) $parent.find('[data-toggle=collapse][data-parent="' + parent + '"]').not($this).addClass('collapsed')
+-      $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed')
+-    }
+ 
+-    $target.collapse(option)
++    Plugin.call($target, option)
+   })
+ 
+ }(jQuery);
+ 
+ /* ========================================================================
+- * Bootstrap: dropdown.js v3.0.3
+- * http://getbootstrap.com/javascript/#dropdowns
++ * Bootstrap: dropdown.js v3.4.1
++ * https://getbootstrap.com/docs/3.4/javascript/#dropdowns
+  * ========================================================================
+- * Copyright 2013 Twitter, Inc.
+- *
+- * Licensed under the Apache License, Version 2.0 (the "License");
+- * you may not use this file except in compliance with the License.
+- * You may obtain a copy of the License at
+- *
+- * http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
++ * Copyright 2011-2019 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+  * ======================================================================== */
+ 
+ 
+-+function ($) { "use strict";
+++function ($) {
++  'use strict';
+ 
+   // DROPDOWN CLASS DEFINITION
+   // =========================
+ 
+   var backdrop = '.dropdown-backdrop'
+-  var toggle   = '[data-toggle=dropdown]'
++  var toggle   = '[data-toggle="dropdown"]'
+   var Dropdown = function (element) {
+     $(element).on('click.bs.dropdown', this.toggle)
+   }
+ 
++  Dropdown.VERSION = '3.4.1'
++
++  function getParent($this) {
++    var selector = $this.attr('data-target')
++
++    if (!selector) {
++      selector = $this.attr('href')
++      selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
++    }
++
++    var $parent = selector !== '#' ? $(document).find(selector) : null
++
++    return $parent && $parent.length ? $parent : $this.parent()
++  }
++
++  function clearMenus(e) {
++    if (e && e.which === 3) return
++    $(backdrop).remove()
++    $(toggle).each(function () {
++      var $this         = $(this)
++      var $parent       = getParent($this)
++      var relatedTarget = { relatedTarget: this }
++
++      if (!$parent.hasClass('open')) return
++
++      if (e && e.type == 'click' && /input|textarea/i.test(e.target.tagName) && $.contains($parent[0], e.target)) return
++
++      $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
++
++      if (e.isDefaultPrevented()) return
++
++      $this.attr('aria-expanded', 'false')
++      $parent.removeClass('open').trigger($.Event('hidden.bs.dropdown', relatedTarget))
++    })
++  }
++
+   Dropdown.prototype.toggle = function (e) {
+     var $this = $(this)
+ 
+@@ -720,25 +828,31 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+     if (!isActive) {
+       if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
+         // if mobile we use a backdrop because click events don't delegate
+-        $('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus)
++        $(document.createElement('div'))
++          .addClass('dropdown-backdrop')
++          .insertAfter($(this))
++          .on('click', clearMenus)
+       }
+ 
+-      $parent.trigger(e = $.Event('show.bs.dropdown'))
++      var relatedTarget = { relatedTarget: this }
++      $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget))
+ 
+       if (e.isDefaultPrevented()) return
+ 
++      $this
++        .trigger('focus')
++        .attr('aria-expanded', 'true')
++
+       $parent
+         .toggleClass('open')
+-        .trigger('shown.bs.dropdown')
+-
+-      $this.focus()
++        .trigger($.Event('shown.bs.dropdown', relatedTarget))
+     }
+ 
+     return false
+   }
+ 
+   Dropdown.prototype.keydown = function (e) {
+-    if (!/(38|40|27)/.test(e.keyCode)) return
++    if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return
+ 
+     var $this = $(this)
+ 
+@@ -750,55 +864,30 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+     var $parent  = getParent($this)
+     var isActive = $parent.hasClass('open')
+ 
+-    if (!isActive || (isActive && e.keyCode == 27)) {
+-      if (e.which == 27) $parent.find(toggle).focus()
+-      return $this.click()
++    if (!isActive && e.which != 27 || isActive && e.which == 27) {
++      if (e.which == 27) $parent.find(toggle).trigger('focus')
++      return $this.trigger('click')
+     }
+ 
+-    var $items = $('[role=menu] li:not(.divider):visible a', $parent)
++    var desc = ' li:not(.disabled):visible a'
++    var $items = $parent.find('.dropdown-menu' + desc)
+ 
+     if (!$items.length) return
+ 
+-    var index = $items.index($items.filter(':focus'))
++    var index = $items.index(e.target)
+ 
+-    if (e.keyCode == 38 && index > 0)                 index--                        // up
+-    if (e.keyCode == 40 && index < $items.length - 1) index++                        // down
+-    if (!~index)                                      index=0
++    if (e.which == 38 && index > 0)                 index--         // up
++    if (e.which == 40 && index < $items.length - 1) index++         // down
++    if (!~index)                                    index = 0
+ 
+-    $items.eq(index).focus()
+-  }
+-
+-  function clearMenus() {
+-    $(backdrop).remove()
+-    $(toggle).each(function (e) {
+-      var $parent = getParent($(this))
+-      if (!$parent.hasClass('open')) return
+-      $parent.trigger(e = $.Event('hide.bs.dropdown'))
+-      if (e.isDefaultPrevented()) return
+-      $parent.removeClass('open').trigger('hidden.bs.dropdown')
+-    })
+-  }
+-
+-  function getParent($this) {
+-    var selector = $this.attr('data-target')
+-
+-    if (!selector) {
+-      selector = $this.attr('href')
+-      selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
+-    }
+-
+-    var $parent = selector && $(selector)
+-
+-    return $parent && $parent.length ? $parent : $this.parent()
++    $items.eq(index).trigger('focus')
+   }
+ 
+ 
+   // DROPDOWN PLUGIN DEFINITION
+   // ==========================
+ 
+-  var old = $.fn.dropdown
+-
+-  $.fn.dropdown = function (option) {
++  function Plugin(option) {
+     return this.each(function () {
+       var $this = $(this)
+       var data  = $this.data('bs.dropdown')
+@@ -808,6 +897,9 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+     })
+   }
+ 
++  var old = $.fn.dropdown
++
++  $.fn.dropdown             = Plugin
+   $.fn.dropdown.Constructor = Dropdown
+ 
+ 
+@@ -826,58 +918,66 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+   $(document)
+     .on('click.bs.dropdown.data-api', clearMenus)
+     .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
+-    .on('click.bs.dropdown.data-api'  , toggle, Dropdown.prototype.toggle)
+-    .on('keydown.bs.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown)
++    .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle)
++    .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown)
++    .on('keydown.bs.dropdown.data-api', '.dropdown-menu', Dropdown.prototype.keydown)
+ 
+ }(jQuery);
+ 
+ /* ========================================================================
+- * Bootstrap: modal.js v3.0.3
+- * http://getbootstrap.com/javascript/#modals
++ * Bootstrap: modal.js v3.4.1
++ * https://getbootstrap.com/docs/3.4/javascript/#modals
+  * ========================================================================
+- * Copyright 2013 Twitter, Inc.
+- *
+- * Licensed under the Apache License, Version 2.0 (the "License");
+- * you may not use this file except in compliance with the License.
+- * You may obtain a copy of the License at
+- *
+- * http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
++ * Copyright 2011-2019 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+  * ======================================================================== */
+ 
+ 
+-+function ($) { "use strict";
+++function ($) {
++  'use strict';
+ 
+   // MODAL CLASS DEFINITION
+   // ======================
+ 
+   var Modal = function (element, options) {
+-    this.options   = options
+-    this.$element  = $(element)
+-    this.$backdrop =
+-    this.isShown   = null
++    this.options = options
++    this.$body = $(document.body)
++    this.$element = $(element)
++    this.$dialog = this.$element.find('.modal-dialog')
++    this.$backdrop = null
++    this.isShown = null
++    this.originalBodyPad = null
++    this.scrollbarWidth = 0
++    this.ignoreBackdropClick = false
++    this.fixedContent = '.navbar-fixed-top, .navbar-fixed-bottom'
+ 
+-    if (this.options.remote) this.$element.load(this.options.remote)
++    if (this.options.remote) {
++      this.$element
++        .find('.modal-content')
++        .load(this.options.remote, $.proxy(function () {
++          this.$element.trigger('loaded.bs.modal')
++        }, this))
++    }
+   }
+ 
++  Modal.VERSION = '3.4.1'
++
++  Modal.TRANSITION_DURATION = 300
++  Modal.BACKDROP_TRANSITION_DURATION = 150
++
+   Modal.DEFAULTS = {
+-      backdrop: true
+-    , keyboard: true
+-    , show: true
++    backdrop: true,
++    keyboard: true,
++    show: true
+   }
+ 
+   Modal.prototype.toggle = function (_relatedTarget) {
+-    return this[!this.isShown ? 'show' : 'hide'](_relatedTarget)
++    return this.isShown ? this.hide() : this.show(_relatedTarget)
+   }
+ 
+   Modal.prototype.show = function (_relatedTarget) {
+     var that = this
+-    var e    = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
++    var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
+ 
+     this.$element.trigger(e)
+ 
+@@ -885,38 +985,51 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+ 
+     this.isShown = true
+ 
++    this.checkScrollbar()
++    this.setScrollbar()
++    this.$body.addClass('modal-open')
++
+     this.escape()
++    this.resize()
+ 
+-    this.$element.on('click.dismiss.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
++    this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
++
++    this.$dialog.on('mousedown.dismiss.bs.modal', function () {
++      that.$element.one('mouseup.dismiss.bs.modal', function (e) {
++        if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true
++      })
++    })
+ 
+     this.backdrop(function () {
+       var transition = $.support.transition && that.$element.hasClass('fade')
+ 
+       if (!that.$element.parent().length) {
+-        that.$element.appendTo(document.body) // don't move modals dom position
++        that.$element.appendTo(that.$body) // don't move modals dom position
+       }
+ 
+-      that.$element.show()
++      that.$element
++        .show()
++        .scrollTop(0)
++
++      that.adjustDialog()
+ 
+       if (transition) {
+         that.$element[0].offsetWidth // force reflow
+       }
+ 
+-      that.$element
+-        .addClass('in')
+-        .attr('aria-hidden', false)
++      that.$element.addClass('in')
+ 
+       that.enforceFocus()
+ 
+       var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
+ 
+       transition ?
+-        that.$element.find('.modal-dialog') // wait for modal to slide in
+-          .one($.support.transition.end, function () {
+-            that.$element.focus().trigger(e)
++        that.$dialog // wait for modal to slide in
++          .one('bsTransitionEnd', function () {
++            that.$element.trigger('focus').trigger(e)
+           })
+-          .emulateTransitionEnd(300) :
+-        that.$element.focus().trigger(e)
++          .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
++        that.$element.trigger('focus').trigger(e)
+     })
+   }
+ 
+@@ -932,18 +1045,21 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+     this.isShown = false
+ 
+     this.escape()
++    this.resize()
+ 
+     $(document).off('focusin.bs.modal')
+ 
+     this.$element
+       .removeClass('in')
+-      .attr('aria-hidden', true)
+-      .off('click.dismiss.modal')
++      .off('click.dismiss.bs.modal')
++      .off('mouseup.dismiss.bs.modal')
++
++    this.$dialog.off('mousedown.dismiss.bs.modal')
+ 
+     $.support.transition && this.$element.hasClass('fade') ?
+       this.$element
+-        .one($.support.transition.end, $.proxy(this.hideModal, this))
+-        .emulateTransitionEnd(300) :
++        .one('bsTransitionEnd', $.proxy(this.hideModal, this))
++        .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
+       this.hideModal()
+   }
+ 
+@@ -951,19 +1067,29 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+     $(document)
+       .off('focusin.bs.modal') // guard against infinite focus loop
+       .on('focusin.bs.modal', $.proxy(function (e) {
+-        if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {
+-          this.$element.focus()
++        if (document !== e.target &&
++          this.$element[0] !== e.target &&
++          !this.$element.has(e.target).length) {
++          this.$element.trigger('focus')
+         }
+       }, this))
+   }
+ 
+   Modal.prototype.escape = function () {
+     if (this.isShown && this.options.keyboard) {
+-      this.$element.on('keyup.dismiss.bs.modal', $.proxy(function (e) {
++      this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) {
+         e.which == 27 && this.hide()
+       }, this))
+     } else if (!this.isShown) {
+-      this.$element.off('keyup.dismiss.bs.modal')
++      this.$element.off('keydown.dismiss.bs.modal')
++    }
++  }
++
++  Modal.prototype.resize = function () {
++    if (this.isShown) {
++      $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this))
++    } else {
++      $(window).off('resize.bs.modal')
+     }
+   }
+ 
+@@ -971,7 +1097,9 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+     var that = this
+     this.$element.hide()
+     this.backdrop(function () {
+-      that.removeBackdrop()
++      that.$body.removeClass('modal-open')
++      that.resetAdjustments()
++      that.resetScrollbar()
+       that.$element.trigger('hidden.bs.modal')
+     })
+   }
+@@ -982,20 +1110,25 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+   }
+ 
+   Modal.prototype.backdrop = function (callback) {
+-    var that    = this
++    var that = this
+     var animate = this.$element.hasClass('fade') ? 'fade' : ''
+ 
+     if (this.isShown && this.options.backdrop) {
+       var doAnimate = $.support.transition && animate
+ 
+-      this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
+-        .appendTo(document.body)
++      this.$backdrop = $(document.createElement('div'))
++        .addClass('modal-backdrop ' + animate)
++        .appendTo(this.$body)
+ 
+-      this.$element.on('click.dismiss.modal', $.proxy(function (e) {
++      this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) {
++        if (this.ignoreBackdropClick) {
++          this.ignoreBackdropClick = false
++          return
++        }
+         if (e.target !== e.currentTarget) return
+         this.options.backdrop == 'static'
+-          ? this.$element[0].focus.call(this.$element[0])
+-          : this.hide.call(this)
++          ? this.$element[0].focus()
++          : this.hide()
+       }, this))
+ 
+       if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
+@@ -1006,34 +1139,102 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+ 
+       doAnimate ?
+         this.$backdrop
+-          .one($.support.transition.end, callback)
+-          .emulateTransitionEnd(150) :
++          .one('bsTransitionEnd', callback)
++          .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
+         callback()
+ 
+     } else if (!this.isShown && this.$backdrop) {
+       this.$backdrop.removeClass('in')
+ 
+-      $.support.transition && this.$element.hasClass('fade')?
++      var callbackRemove = function () {
++        that.removeBackdrop()
++        callback && callback()
++      }
++      $.support.transition && this.$element.hasClass('fade') ?
+         this.$backdrop
+-          .one($.support.transition.end, callback)
+-          .emulateTransitionEnd(150) :
+-        callback()
++          .one('bsTransitionEnd', callbackRemove)
++          .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
++        callbackRemove()
+ 
+     } else if (callback) {
+       callback()
+     }
+   }
+ 
++  // these following methods are used to handle overflowing modals
++
++  Modal.prototype.handleUpdate = function () {
++    this.adjustDialog()
++  }
++
++  Modal.prototype.adjustDialog = function () {
++    var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight
++
++    this.$element.css({
++      paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '',
++      paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : ''
++    })
++  }
++
++  Modal.prototype.resetAdjustments = function () {
++    this.$element.css({
++      paddingLeft: '',
++      paddingRight: ''
++    })
++  }
++
++  Modal.prototype.checkScrollbar = function () {
++    var fullWindowWidth = window.innerWidth
++    if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8
++      var documentElementRect = document.documentElement.getBoundingClientRect()
++      fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left)
++    }
++    this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth
++    this.scrollbarWidth = this.measureScrollbar()
++  }
++
++  Modal.prototype.setScrollbar = function () {
++    var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
++    this.originalBodyPad = document.body.style.paddingRight || ''
++    var scrollbarWidth = this.scrollbarWidth
++    if (this.bodyIsOverflowing) {
++      this.$body.css('padding-right', bodyPad + scrollbarWidth)
++      $(this.fixedContent).each(function (index, element) {
++        var actualPadding = element.style.paddingRight
++        var calculatedPadding = $(element).css('padding-right')
++        $(element)
++          .data('padding-right', actualPadding)
++          .css('padding-right', parseFloat(calculatedPadding) + scrollbarWidth + 'px')
++      })
++    }
++  }
++
++  Modal.prototype.resetScrollbar = function () {
++    this.$body.css('padding-right', this.originalBodyPad)
++    $(this.fixedContent).each(function (index, element) {
++      var padding = $(element).data('padding-right')
++      $(element).removeData('padding-right')
++      element.style.paddingRight = padding ? padding : ''
++    })
++  }
++
++  Modal.prototype.measureScrollbar = function () { // thx walsh
++    var scrollDiv = document.createElement('div')
++    scrollDiv.className = 'modal-scrollbar-measure'
++    this.$body.append(scrollDiv)
++    var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth
++    this.$body[0].removeChild(scrollDiv)
++    return scrollbarWidth
++  }
++
+ 
+   // MODAL PLUGIN DEFINITION
+   // =======================
+ 
+-  var old = $.fn.modal
+-
+-  $.fn.modal = function (option, _relatedTarget) {
++  function Plugin(option, _relatedTarget) {
+     return this.each(function () {
+-      var $this   = $(this)
+-      var data    = $this.data('bs.modal')
++      var $this = $(this)
++      var data = $this.data('bs.modal')
+       var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
+ 
+       if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
+@@ -1042,6 +1243,9 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+     })
+   }
+ 
++  var old = $.fn.modal
++
++  $.fn.modal = Plugin
+   $.fn.modal.Constructor = Modal
+ 
+ 
+@@ -1058,80 +1262,219 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+   // ==============
+ 
+   $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
+-    var $this   = $(this)
+-    var href    = $this.attr('href')
+-    var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) //strip for ie7
+-    var option  = $target.data('modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
++    var $this = $(this)
++    var href = $this.attr('href')
++    var target = $this.attr('data-target') ||
++      (href && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7
+ 
+-    e.preventDefault()
++    var $target = $(document).find(target)
++    var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
++
++    if ($this.is('a')) e.preventDefault()
+ 
+-    $target
+-      .modal(option, this)
+-      .one('hide', function () {
+-        $this.is(':visible') && $this.focus()
++    $target.one('show.bs.modal', function (showEvent) {
++      if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown
++      $target.one('hidden.bs.modal', function () {
++        $this.is(':visible') && $this.trigger('focus')
+       })
++    })
++    Plugin.call($target, option, this)
+   })
+ 
+-  $(document)
+-    .on('show.bs.modal',  '.modal', function () { $(document.body).addClass('modal-open') })
+-    .on('hidden.bs.modal', '.modal', function () { $(document.body).removeClass('modal-open') })
+-
+ }(jQuery);
+ 
+ /* ========================================================================
+- * Bootstrap: tooltip.js v3.0.3
+- * http://getbootstrap.com/javascript/#tooltip
++ * Bootstrap: tooltip.js v3.4.1
++ * https://getbootstrap.com/docs/3.4/javascript/#tooltip
+  * Inspired by the original jQuery.tipsy by Jason Frame
+  * ========================================================================
+- * Copyright 2013 Twitter, Inc.
+- *
+- * Licensed under the Apache License, Version 2.0 (the "License");
+- * you may not use this file except in compliance with the License.
+- * You may obtain a copy of the License at
+- *
+- * http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
++ * Copyright 2011-2019 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+  * ======================================================================== */
+ 
+++function ($) {
++  'use strict';
++
++  var DISALLOWED_ATTRIBUTES = ['sanitize', 'whiteList', 'sanitizeFn']
++
++  var uriAttrs = [
++    'background',
++    'cite',
++    'href',
++    'itemtype',
++    'longdesc',
++    'poster',
++    'src',
++    'xlink:href'
++  ]
++
++  var ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i
++
++  var DefaultWhitelist = {
++    // Global attributes allowed on any supplied element below.
++    '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],
++    a: ['target', 'href', 'title', 'rel'],
++    area: [],
++    b: [],
++    br: [],
++    col: [],
++    code: [],
++    div: [],
++    em: [],
++    hr: [],
++    h1: [],
++    h2: [],
++    h3: [],
++    h4: [],
++    h5: [],
++    h6: [],
++    i: [],
++    img: ['src', 'alt', 'title', 'width', 'height'],
++    li: [],
++    ol: [],
++    p: [],
++    pre: [],
++    s: [],
++    small: [],
++    span: [],
++    sub: [],
++    sup: [],
++    strong: [],
++    u: [],
++    ul: []
++  }
++
++  /**
++   * A pattern that recognizes a commonly useful subset of URLs that are safe.
++   *
++   * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts
++   */
++  var SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file):|[^&:/?#]*(?:[/?#]|$))/gi
++
++  /**
++   * A pattern that matches safe data URLs. Only matches image, video and audio types.
++   *
++   * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts
++   */
++  var DATA_URL_PATTERN = /^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[a-z0-9+/]+=*$/i
++
++  function allowedAttribute(attr, allowedAttributeList) {
++    var attrName = attr.nodeName.toLowerCase()
++
++    if ($.inArray(attrName, allowedAttributeList) !== -1) {
++      if ($.inArray(attrName, uriAttrs) !== -1) {
++        return Boolean(attr.nodeValue.match(SAFE_URL_PATTERN) || attr.nodeValue.match(DATA_URL_PATTERN))
++      }
++
++      return true
++    }
++
++    var regExp = $(allowedAttributeList).filter(function (index, value) {
++      return value instanceof RegExp
++    })
++
++    // Check if a regular expression validates the attribute.
++    for (var i = 0, l = regExp.length; i < l; i++) {
++      if (attrName.match(regExp[i])) {
++        return true
++      }
++    }
++
++    return false
++  }
+ 
+-+function ($) { "use strict";
++  function sanitizeHtml(unsafeHtml, whiteList, sanitizeFn) {
++    if (unsafeHtml.length === 0) {
++      return unsafeHtml
++    }
++
++    if (sanitizeFn && typeof sanitizeFn === 'function') {
++      return sanitizeFn(unsafeHtml)
++    }
++
++    // IE 8 and below don't support createHTMLDocument
++    if (!document.implementation || !document.implementation.createHTMLDocument) {
++      return unsafeHtml
++    }
++
++    var createdDocument = document.implementation.createHTMLDocument('sanitization')
++    createdDocument.body.innerHTML = unsafeHtml
++
++    var whitelistKeys = $.map(whiteList, function (el, i) { return i })
++    var elements = $(createdDocument.body).find('*')
++
++    for (var i = 0, len = elements.length; i < len; i++) {
++      var el = elements[i]
++      var elName = el.nodeName.toLowerCase()
++
++      if ($.inArray(elName, whitelistKeys) === -1) {
++        el.parentNode.removeChild(el)
++
++        continue
++      }
++
++      var attributeList = $.map(el.attributes, function (el) { return el })
++      var whitelistedAttributes = [].concat(whiteList['*'] || [], whiteList[elName] || [])
++
++      for (var j = 0, len2 = attributeList.length; j < len2; j++) {
++        if (!allowedAttribute(attributeList[j], whitelistedAttributes)) {
++          el.removeAttribute(attributeList[j].nodeName)
++        }
++      }
++    }
++
++    return createdDocument.body.innerHTML
++  }
+ 
+   // TOOLTIP PUBLIC CLASS DEFINITION
+   // ===============================
+ 
+   var Tooltip = function (element, options) {
+-    this.type       =
+-    this.options    =
+-    this.enabled    =
+-    this.timeout    =
+-    this.hoverState =
++    this.type       = null
++    this.options    = null
++    this.enabled    = null
++    this.timeout    = null
++    this.hoverState = null
+     this.$element   = null
++    this.inState    = null
+ 
+     this.init('tooltip', element, options)
+   }
+ 
++  Tooltip.VERSION  = '3.4.1'
++
++  Tooltip.TRANSITION_DURATION = 150
++
+   Tooltip.DEFAULTS = {
+-    animation: true
+-  , placement: 'top'
+-  , selector: false
+-  , template: '<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
+-  , trigger: 'hover focus'
+-  , title: ''
+-  , delay: 0
+-  , html: false
+-  , container: false
++    animation: true,
++    placement: 'top',
++    selector: false,
++    template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
++    trigger: 'hover focus',
++    title: '',
++    delay: 0,
++    html: false,
++    container: false,
++    viewport: {
++      selector: 'body',
++      padding: 0
++    },
++    sanitize : true,
++    sanitizeFn : null,
++    whiteList : DefaultWhitelist
+   }
+ 
+   Tooltip.prototype.init = function (type, element, options) {
+-    this.enabled  = true
+-    this.type     = type
+-    this.$element = $(element)
+-    this.options  = this.getOptions(options)
++    this.enabled   = true
++    this.type      = type
++    this.$element  = $(element)
++    this.options   = this.getOptions(options)
++    this.$viewport = this.options.viewport && $(document).find($.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : (this.options.viewport.selector || this.options.viewport))
++    this.inState   = { click: false, hover: false, focus: false }
++
++    if (this.$element[0] instanceof document.constructor && !this.options.selector) {
++      throw new Error('`selector` option must be specified when initializing ' + this.type + ' on the window.document object!')
++    }
+ 
+     var triggers = this.options.trigger.split(' ')
+ 
+@@ -1141,8 +1484,8 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+       if (trigger == 'click') {
+         this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
+       } else if (trigger != 'manual') {
+-        var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focus'
+-        var eventOut = trigger == 'hover' ? 'mouseleave' : 'blur'
++        var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
++        var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
+ 
+         this.$element.on(eventIn  + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
+         this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
+@@ -1159,15 +1502,27 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+   }
+ 
+   Tooltip.prototype.getOptions = function (options) {
+-    options = $.extend({}, this.getDefaults(), this.$element.data(), options)
++    var dataAttributes = this.$element.data()
++
++    for (var dataAttr in dataAttributes) {
++      if (dataAttributes.hasOwnProperty(dataAttr) && $.inArray(dataAttr, DISALLOWED_ATTRIBUTES) !== -1) {
++        delete dataAttributes[dataAttr]
++      }
++    }
++
++    options = $.extend({}, this.getDefaults(), dataAttributes, options)
+ 
+     if (options.delay && typeof options.delay == 'number') {
+       options.delay = {
+-        show: options.delay
+-      , hide: options.delay
++        show: options.delay,
++        hide: options.delay
+       }
+     }
+ 
++    if (options.sanitize) {
++      options.template = sanitizeHtml(options.template, options.whiteList, options.sanitizeFn)
++    }
++
+     return options
+   }
+ 
+@@ -1184,7 +1539,21 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+ 
+   Tooltip.prototype.enter = function (obj) {
+     var self = obj instanceof this.constructor ?
+-      obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
++      obj : $(obj.currentTarget).data('bs.' + this.type)
++
++    if (!self) {
++      self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
++      $(obj.currentTarget).data('bs.' + this.type, self)
++    }
++
++    if (obj instanceof $.Event) {
++      self.inState[obj.type == 'focusin' ? 'focus' : 'hover'] = true
++    }
++
++    if (self.tip().hasClass('in') || self.hoverState == 'in') {
++      self.hoverState = 'in'
++      return
++    }
+ 
+     clearTimeout(self.timeout)
+ 
+@@ -1197,9 +1566,28 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+     }, self.options.delay.show)
+   }
+ 
++  Tooltip.prototype.isInStateTrue = function () {
++    for (var key in this.inState) {
++      if (this.inState[key]) return true
++    }
++
++    return false
++  }
++
+   Tooltip.prototype.leave = function (obj) {
+     var self = obj instanceof this.constructor ?
+-      obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
++      obj : $(obj.currentTarget).data('bs.' + this.type)
++
++    if (!self) {
++      self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
++      $(obj.currentTarget).data('bs.' + this.type, self)
++    }
++
++    if (obj instanceof $.Event) {
++      self.inState[obj.type == 'focusout' ? 'focus' : 'hover'] = false
++    }
++
++    if (self.isInStateTrue()) return
+ 
+     clearTimeout(self.timeout)
+ 
+@@ -1213,16 +1601,22 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+   }
+ 
+   Tooltip.prototype.show = function () {
+-    var e = $.Event('show.bs.'+ this.type)
++    var e = $.Event('show.bs.' + this.type)
+ 
+     if (this.hasContent() && this.enabled) {
+       this.$element.trigger(e)
+ 
+-      if (e.isDefaultPrevented()) return
++      var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0])
++      if (e.isDefaultPrevented() || !inDom) return
++      var that = this
+ 
+       var $tip = this.tip()
+ 
++      var tipId = this.getUID(this.type)
++
+       this.setContent()
++      $tip.attr('id', tipId)
++      this.$element.attr('aria-describedby', tipId)
+ 
+       if (this.options.animation) $tip.addClass('fade')
+ 
+@@ -1238,26 +1632,23 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+         .detach()
+         .css({ top: 0, left: 0, display: 'block' })
+         .addClass(placement)
++        .data('bs.' + this.type, this)
+ 
+-      this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
++      this.options.container ? $tip.appendTo($(document).find(this.options.container)) : $tip.insertAfter(this.$element)
++      this.$element.trigger('inserted.bs.' + this.type)
+ 
+       var pos          = this.getPosition()
+       var actualWidth  = $tip[0].offsetWidth
+       var actualHeight = $tip[0].offsetHeight
+ 
+       if (autoPlace) {
+-        var $parent = this.$element.parent()
+-
+         var orgPlacement = placement
+-        var docScroll    = document.documentElement.scrollTop || document.body.scrollTop
+-        var parentWidth  = this.options.container == 'body' ? window.innerWidth  : $parent.outerWidth()
+-        var parentHeight = this.options.container == 'body' ? window.innerHeight : $parent.outerHeight()
+-        var parentLeft   = this.options.container == 'body' ? 0 : $parent.offset().left
+-
+-        placement = placement == 'bottom' && pos.top   + pos.height  + actualHeight - docScroll > parentHeight  ? 'top'    :
+-                    placement == 'top'    && pos.top   - docScroll   - actualHeight < 0                         ? 'bottom' :
+-                    placement == 'right'  && pos.right + actualWidth > parentWidth                              ? 'left'   :
+-                    placement == 'left'   && pos.left  - actualWidth < parentLeft                               ? 'right'  :
++        var viewportDim = this.getPosition(this.$viewport)
++
++        placement = placement == 'bottom' && pos.bottom + actualHeight > viewportDim.bottom ? 'top'    :
++                    placement == 'top'    && pos.top    - actualHeight < viewportDim.top    ? 'bottom' :
++                    placement == 'right'  && pos.right  + actualWidth  > viewportDim.width  ? 'left'   :
++                    placement == 'left'   && pos.left   - actualWidth  < viewportDim.left   ? 'right'  :
+                     placement
+ 
+         $tip
+@@ -1268,12 +1659,24 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+       var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
+ 
+       this.applyPlacement(calculatedOffset, placement)
+-      this.$element.trigger('shown.bs.' + this.type)
++
++      var complete = function () {
++        var prevHoverState = that.hoverState
++        that.$element.trigger('shown.bs.' + that.type)
++        that.hoverState = null
++
++        if (prevHoverState == 'out') that.leave(that)
++      }
++
++      $.support.transition && this.$tip.hasClass('fade') ?
++        $tip
++          .one('bsTransitionEnd', complete)
++          .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
++        complete()
+     }
+   }
+ 
+-  Tooltip.prototype.applyPlacement = function(offset, placement) {
+-    var replace
++  Tooltip.prototype.applyPlacement = function (offset, placement) {
+     var $tip   = this.tip()
+     var width  = $tip[0].offsetWidth
+     var height = $tip[0].offsetHeight
+@@ -1286,62 +1689,79 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+     if (isNaN(marginTop))  marginTop  = 0
+     if (isNaN(marginLeft)) marginLeft = 0
+ 
+-    offset.top  = offset.top  + marginTop
+-    offset.left = offset.left + marginLeft
++    offset.top  += marginTop
++    offset.left += marginLeft
++
++    // $.fn.offset doesn't round pixel values
++    // so we use setOffset directly with our own function B-0
++    $.offset.setOffset($tip[0], $.extend({
++      using: function (props) {
++        $tip.css({
++          top: Math.round(props.top),
++          left: Math.round(props.left)
++        })
++      }
++    }, offset), 0)
+ 
+-    $tip
+-      .offset(offset)
+-      .addClass('in')
++    $tip.addClass('in')
+ 
+     // check to see if placing tip in new offset caused the tip to resize itself
+     var actualWidth  = $tip[0].offsetWidth
+     var actualHeight = $tip[0].offsetHeight
+ 
+     if (placement == 'top' && actualHeight != height) {
+-      replace = true
+       offset.top = offset.top + height - actualHeight
+     }
+ 
+-    if (/bottom|top/.test(placement)) {
+-      var delta = 0
+-
+-      if (offset.left < 0) {
+-        delta       = offset.left * -2
+-        offset.left = 0
+-
+-        $tip.offset(offset)
++    var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)
+ 
+-        actualWidth  = $tip[0].offsetWidth
+-        actualHeight = $tip[0].offsetHeight
+-      }
++    if (delta.left) offset.left += delta.left
++    else offset.top += delta.top
+ 
+-      this.replaceArrow(delta - width + actualWidth, actualWidth, 'left')
+-    } else {
+-      this.replaceArrow(actualHeight - height, actualHeight, 'top')
+-    }
++    var isVertical          = /top|bottom/.test(placement)
++    var arrowDelta          = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
++    var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight'
+ 
+-    if (replace) $tip.offset(offset)
++    $tip.offset(offset)
++    this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical)
+   }
+ 
+-  Tooltip.prototype.replaceArrow = function(delta, dimension, position) {
+-    this.arrow().css(position, delta ? (50 * (1 - delta / dimension) + "%") : '')
++  Tooltip.prototype.replaceArrow = function (delta, dimension, isVertical) {
++    this.arrow()
++      .css(isVertical ? 'left' : 'top', 50 * (1 - delta / dimension) + '%')
++      .css(isVertical ? 'top' : 'left', '')
+   }
+ 
+   Tooltip.prototype.setContent = function () {
+     var $tip  = this.tip()
+     var title = this.getTitle()
+ 
+-    $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
++    if (this.options.html) {
++      if (this.options.sanitize) {
++        title = sanitizeHtml(title, this.options.whiteList, this.options.sanitizeFn)
++      }
++
++      $tip.find('.tooltip-inner').html(title)
++    } else {
++      $tip.find('.tooltip-inner').text(title)
++    }
++
+     $tip.removeClass('fade in top bottom left right')
+   }
+ 
+-  Tooltip.prototype.hide = function () {
++  Tooltip.prototype.hide = function (callback) {
+     var that = this
+-    var $tip = this.tip()
++    var $tip = $(this.$tip)
+     var e    = $.Event('hide.bs.' + this.type)
+ 
+     function complete() {
+       if (that.hoverState != 'in') $tip.detach()
++      if (that.$element) { // TODO: Check whether guarding this code with this `if` is really necessary.
++        that.$element
++          .removeAttr('aria-describedby')
++          .trigger('hidden.bs.' + that.type)
++      }
++      callback && callback()
+     }
+ 
+     this.$element.trigger(e)
+@@ -1350,20 +1770,20 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+ 
+     $tip.removeClass('in')
+ 
+-    $.support.transition && this.$tip.hasClass('fade') ?
++    $.support.transition && $tip.hasClass('fade') ?
+       $tip
+-        .one($.support.transition.end, complete)
+-        .emulateTransitionEnd(150) :
++        .one('bsTransitionEnd', complete)
++        .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
+       complete()
+ 
+-    this.$element.trigger('hidden.bs.' + this.type)
++    this.hoverState = null
+ 
+     return this
+   }
+ 
+   Tooltip.prototype.fixTitle = function () {
+     var $e = this.$element
+-    if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
++    if ($e.attr('title') || typeof $e.attr('data-original-title') != 'string') {
+       $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
+     }
+   }
+@@ -1372,19 +1792,61 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+     return this.getTitle()
+   }
+ 
+-  Tooltip.prototype.getPosition = function () {
+-    var el = this.$element[0]
+-    return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : {
+-      width: el.offsetWidth
+-    , height: el.offsetHeight
+-    }, this.$element.offset())
++  Tooltip.prototype.getPosition = function ($element) {
++    $element   = $element || this.$element
++
++    var el     = $element[0]
++    var isBody = el.tagName == 'BODY'
++
++    var elRect    = el.getBoundingClientRect()
++    if (elRect.width == null) {
++      // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093
++      elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top })
++    }
++    var isSvg = window.SVGElement && el instanceof window.SVGElement
++    // Avoid using $.offset() on SVGs since it gives incorrect results in jQuery 3.
++    // See https://github.com/twbs/bootstrap/issues/20280
++    var elOffset  = isBody ? { top: 0, left: 0 } : (isSvg ? null : $element.offset())
++    var scroll    = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() }
++    var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null
++
++    return $.extend({}, elRect, scroll, outerDims, elOffset)
+   }
+ 
+   Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
+-    return placement == 'bottom' ? { top: pos.top + pos.height,   left: pos.left + pos.width / 2 - actualWidth / 2  } :
+-           placement == 'top'    ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2  } :
++    return placement == 'bottom' ? { top: pos.top + pos.height,   left: pos.left + pos.width / 2 - actualWidth / 2 } :
++           placement == 'top'    ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } :
+            placement == 'left'   ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
+-        /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width   }
++        /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width }
++
++  }
++
++  Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) {
++    var delta = { top: 0, left: 0 }
++    if (!this.$viewport) return delta
++
++    var viewportPadding = this.options.viewport && this.options.viewport.padding || 0
++    var viewportDimensions = this.getPosition(this.$viewport)
++
++    if (/right|left/.test(placement)) {
++      var topEdgeOffset    = pos.top - viewportPadding - viewportDimensions.scroll
++      var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight
++      if (topEdgeOffset < viewportDimensions.top) { // top overflow
++        delta.top = viewportDimensions.top - topEdgeOffset
++      } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
++        delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset
++      }
++    } else {
++      var leftEdgeOffset  = pos.left - viewportPadding
++      var rightEdgeOffset = pos.left + viewportPadding + actualWidth
++      if (leftEdgeOffset < viewportDimensions.left) { // left overflow
++        delta.left = viewportDimensions.left - leftEdgeOffset
++      } else if (rightEdgeOffset > viewportDimensions.right) { // right overflow
++        delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
++      }
++    }
++
++    return delta
+   }
+ 
+   Tooltip.prototype.getTitle = function () {
+@@ -1398,20 +1860,24 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+     return title
+   }
+ 
+-  Tooltip.prototype.tip = function () {
+-    return this.$tip = this.$tip || $(this.options.template)
++  Tooltip.prototype.getUID = function (prefix) {
++    do prefix += ~~(Math.random() * 1000000)
++    while (document.getElementById(prefix))
++    return prefix
+   }
+ 
+-  Tooltip.prototype.arrow = function () {
+-    return this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')
++  Tooltip.prototype.tip = function () {
++    if (!this.$tip) {
++      this.$tip = $(this.options.template)
++      if (this.$tip.length != 1) {
++        throw new Error(this.type + ' `template` option must consist of exactly 1 top-level element!')
++      }
++    }
++    return this.$tip
+   }
+ 
+-  Tooltip.prototype.validate = function () {
+-    if (!this.$element[0].parentNode) {
+-      this.hide()
+-      this.$element = null
+-      this.options  = null
+-    }
++  Tooltip.prototype.arrow = function () {
++    return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow'))
+   }
+ 
+   Tooltip.prototype.enable = function () {
+@@ -1427,31 +1893,61 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+   }
+ 
+   Tooltip.prototype.toggle = function (e) {
+-    var self = e ? $(e.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type) : this
+-    self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
++    var self = this
++    if (e) {
++      self = $(e.currentTarget).data('bs.' + this.type)
++      if (!self) {
++        self = new this.constructor(e.currentTarget, this.getDelegateOptions())
++        $(e.currentTarget).data('bs.' + this.type, self)
++      }
++    }
++
++    if (e) {
++      self.inState.click = !self.inState.click
++      if (self.isInStateTrue()) self.enter(self)
++      else self.leave(self)
++    } else {
++      self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
++    }
+   }
+ 
+   Tooltip.prototype.destroy = function () {
+-    this.hide().$element.off('.' + this.type).removeData('bs.' + this.type)
++    var that = this
++    clearTimeout(this.timeout)
++    this.hide(function () {
++      that.$element.off('.' + that.type).removeData('bs.' + that.type)
++      if (that.$tip) {
++        that.$tip.detach()
++      }
++      that.$tip = null
++      that.$arrow = null
++      that.$viewport = null
++      that.$element = null
++    })
+   }
+ 
++  Tooltip.prototype.sanitizeHtml = function (unsafeHtml) {
++    return sanitizeHtml(unsafeHtml, this.options.whiteList, this.options.sanitizeFn)
++  }
+ 
+   // TOOLTIP PLUGIN DEFINITION
+   // =========================
+ 
+-  var old = $.fn.tooltip
+-
+-  $.fn.tooltip = function (option) {
++  function Plugin(option) {
+     return this.each(function () {
+       var $this   = $(this)
+       var data    = $this.data('bs.tooltip')
+       var options = typeof option == 'object' && option
+ 
++      if (!data && /destroy|hide/.test(option)) return
+       if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
+       if (typeof option == 'string') data[option]()
+     })
+   }
+ 
++  var old = $.fn.tooltip
++
++  $.fn.tooltip             = Plugin
+   $.fn.tooltip.Constructor = Tooltip
+ 
+ 
+@@ -1466,26 +1962,16 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+ }(jQuery);
+ 
+ /* ========================================================================
+- * Bootstrap: popover.js v3.0.3
+- * http://getbootstrap.com/javascript/#popovers
++ * Bootstrap: popover.js v3.4.1
++ * https://getbootstrap.com/docs/3.4/javascript/#popovers
+  * ========================================================================
+- * Copyright 2013 Twitter, Inc.
+- *
+- * Licensed under the Apache License, Version 2.0 (the "License");
+- * you may not use this file except in compliance with the License.
+- * You may obtain a copy of the License at
+- *
+- * http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
++ * Copyright 2011-2019 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+  * ======================================================================== */
+ 
+ 
+-+function ($) { "use strict";
+++function ($) {
++  'use strict';
+ 
+   // POPOVER PUBLIC CLASS DEFINITION
+   // ===============================
+@@ -1496,11 +1982,13 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+ 
+   if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
+ 
+-  Popover.DEFAULTS = $.extend({} , $.fn.tooltip.Constructor.DEFAULTS, {
+-    placement: 'right'
+-  , trigger: 'click'
+-  , content: ''
+-  , template: '<div class="popover"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
++  Popover.VERSION  = '3.4.1'
++
++  Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, {
++    placement: 'right',
++    trigger: 'click',
++    content: '',
++    template: '<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
+   })
+ 
+ 
+@@ -1520,8 +2008,25 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+     var title   = this.getTitle()
+     var content = this.getContent()
+ 
+-    $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
+-    $tip.find('.popover-content')[this.options.html ? 'html' : 'text'](content)
++    if (this.options.html) {
++      var typeContent = typeof content
++
++      if (this.options.sanitize) {
++        title = this.sanitizeHtml(title)
++
++        if (typeContent === 'string') {
++          content = this.sanitizeHtml(content)
++        }
++      }
++
++      $tip.find('.popover-title').html(title)
++      $tip.find('.popover-content').children().detach().end()[
++        typeContent === 'string' ? 'html' : 'append'
++      ](content)
++    } else {
++      $tip.find('.popover-title').text(title)
++      $tip.find('.popover-content').children().detach().end().text(content)
++    }
+ 
+     $tip.removeClass('fade top bottom left right in')
+ 
+@@ -1540,36 +2045,33 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+ 
+     return $e.attr('data-content')
+       || (typeof o.content == 'function' ?
+-            o.content.call($e[0]) :
+-            o.content)
++        o.content.call($e[0]) :
++        o.content)
+   }
+ 
+   Popover.prototype.arrow = function () {
+-    return this.$arrow = this.$arrow || this.tip().find('.arrow')
+-  }
+-
+-  Popover.prototype.tip = function () {
+-    if (!this.$tip) this.$tip = $(this.options.template)
+-    return this.$tip
++    return (this.$arrow = this.$arrow || this.tip().find('.arrow'))
+   }
+ 
+ 
+   // POPOVER PLUGIN DEFINITION
+   // =========================
+ 
+-  var old = $.fn.popover
+-
+-  $.fn.popover = function (option) {
++  function Plugin(option) {
+     return this.each(function () {
+       var $this   = $(this)
+       var data    = $this.data('bs.popover')
+       var options = typeof option == 'object' && option
+ 
++      if (!data && /destroy|hide/.test(option)) return
+       if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
+       if (typeof option == 'string') data[option]()
+     })
+   }
+ 
++  var old = $.fn.popover
++
++  $.fn.popover             = Plugin
+   $.fn.popover.Constructor = Popover
+ 
+ 
+@@ -1584,115 +2086,122 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+ }(jQuery);
+ 
+ /* ========================================================================
+- * Bootstrap: scrollspy.js v3.0.3
+- * http://getbootstrap.com/javascript/#scrollspy
++ * Bootstrap: scrollspy.js v3.4.1
++ * https://getbootstrap.com/docs/3.4/javascript/#scrollspy
+  * ========================================================================
+- * Copyright 2013 Twitter, Inc.
+- *
+- * Licensed under the Apache License, Version 2.0 (the "License");
+- * you may not use this file except in compliance with the License.
+- * You may obtain a copy of the License at
+- *
+- * http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
++ * Copyright 2011-2019 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+  * ======================================================================== */
+ 
+ 
+-+function ($) { "use strict";
+++function ($) {
++  'use strict';
+ 
+   // SCROLLSPY CLASS DEFINITION
+   // ==========================
+ 
+   function ScrollSpy(element, options) {
+-    var href
+-    var process  = $.proxy(this.process, this)
+-
+-    this.$element       = $(element).is('body') ? $(window) : $(element)
+-    this.$body          = $('body')
+-    this.$scrollElement = this.$element.on('scroll.bs.scroll-spy.data-api', process)
++    this.$body          = $(document.body)
++    this.$scrollElement = $(element).is(document.body) ? $(window) : $(element)
+     this.options        = $.extend({}, ScrollSpy.DEFAULTS, options)
+-    this.selector       = (this.options.target
+-      || ((href = $(element).attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
+-      || '') + ' .nav li > a'
+-    this.offsets        = $([])
+-    this.targets        = $([])
++    this.selector       = (this.options.target || '') + ' .nav li > a'
++    this.offsets        = []
++    this.targets        = []
+     this.activeTarget   = null
++    this.scrollHeight   = 0
+ 
++    this.$scrollElement.on('scroll.bs.scrollspy', $.proxy(this.process, this))
+     this.refresh()
+     this.process()
+   }
+ 
++  ScrollSpy.VERSION  = '3.4.1'
++
+   ScrollSpy.DEFAULTS = {
+     offset: 10
+   }
+ 
++  ScrollSpy.prototype.getScrollHeight = function () {
++    return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight)
++  }
++
+   ScrollSpy.prototype.refresh = function () {
+-    var offsetMethod = this.$element[0] == window ? 'offset' : 'position'
++    var that          = this
++    var offsetMethod  = 'offset'
++    var offsetBase    = 0
++
++    this.offsets      = []
++    this.targets      = []
++    this.scrollHeight = this.getScrollHeight()
+ 
+-    this.offsets = $([])
+-    this.targets = $([])
++    if (!$.isWindow(this.$scrollElement[0])) {
++      offsetMethod = 'position'
++      offsetBase   = this.$scrollElement.scrollTop()
++    }
+ 
+-    var self     = this
+-    var $targets = this.$body
++    this.$body
+       .find(this.selector)
+       .map(function () {
+         var $el   = $(this)
+         var href  = $el.data('target') || $el.attr('href')
+-        var $href = /^#\w/.test(href) && $(href)
++        var $href = /^#./.test(href) && $(href)
+ 
+         return ($href
+           && $href.length
+-          && [[ $href[offsetMethod]().top + (!$.isWindow(self.$scrollElement.get(0)) && self.$scrollElement.scrollTop()), href ]]) || null
++          && $href.is(':visible')
++          && [[$href[offsetMethod]().top + offsetBase, href]]) || null
+       })
+       .sort(function (a, b) { return a[0] - b[0] })
+       .each(function () {
+-        self.offsets.push(this[0])
+-        self.targets.push(this[1])
++        that.offsets.push(this[0])
++        that.targets.push(this[1])
+       })
+   }
+ 
+   ScrollSpy.prototype.process = function () {
+     var scrollTop    = this.$scrollElement.scrollTop() + this.options.offset
+-    var scrollHeight = this.$scrollElement[0].scrollHeight || this.$body[0].scrollHeight
+-    var maxScroll    = scrollHeight - this.$scrollElement.height()
++    var scrollHeight = this.getScrollHeight()
++    var maxScroll    = this.options.offset + scrollHeight - this.$scrollElement.height()
+     var offsets      = this.offsets
+     var targets      = this.targets
+     var activeTarget = this.activeTarget
+     var i
+ 
++    if (this.scrollHeight != scrollHeight) {
++      this.refresh()
++    }
++
+     if (scrollTop >= maxScroll) {
+-      return activeTarget != (i = targets.last()[0]) && this.activate(i)
++      return activeTarget != (i = targets[targets.length - 1]) && this.activate(i)
++    }
++
++    if (activeTarget && scrollTop < offsets[0]) {
++      this.activeTarget = null
++      return this.clear()
+     }
+ 
+     for (i = offsets.length; i--;) {
+       activeTarget != targets[i]
+         && scrollTop >= offsets[i]
+-        && (!offsets[i + 1] || scrollTop <= offsets[i + 1])
+-        && this.activate( targets[i] )
++        && (offsets[i + 1] === undefined || scrollTop < offsets[i + 1])
++        && this.activate(targets[i])
+     }
+   }
+ 
+   ScrollSpy.prototype.activate = function (target) {
+     this.activeTarget = target
+ 
+-    $(this.selector)
+-      .parents('.active')
+-      .removeClass('active')
++    this.clear()
+ 
+-    var selector = this.selector
+-      + '[data-target="' + target + '"],'
+-      + this.selector + '[href="' + target + '"]'
++    var selector = this.selector +
++      '[data-target="' + target + '"],' +
++      this.selector + '[href="' + target + '"]'
+ 
+     var active = $(selector)
+       .parents('li')
+       .addClass('active')
+ 
+-    if (active.parent('.dropdown-menu').length)  {
++    if (active.parent('.dropdown-menu').length) {
+       active = active
+         .closest('li.dropdown')
+         .addClass('active')
+@@ -1701,13 +2210,17 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+     active.trigger('activate.bs.scrollspy')
+   }
+ 
++  ScrollSpy.prototype.clear = function () {
++    $(this.selector)
++      .parentsUntil(this.options.target, '.active')
++      .removeClass('active')
++  }
++
+ 
+   // SCROLLSPY PLUGIN DEFINITION
+   // ===========================
+ 
+-  var old = $.fn.scrollspy
+-
+-  $.fn.scrollspy = function (option) {
++  function Plugin(option) {
+     return this.each(function () {
+       var $this   = $(this)
+       var data    = $this.data('bs.scrollspy')
+@@ -1718,6 +2231,9 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+     })
+   }
+ 
++  var old = $.fn.scrollspy
++
++  $.fn.scrollspy             = Plugin
+   $.fn.scrollspy.Constructor = ScrollSpy
+ 
+ 
+@@ -1733,44 +2249,40 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+   // SCROLLSPY DATA-API
+   // ==================
+ 
+-  $(window).on('load', function () {
++  $(window).on('load.bs.scrollspy.data-api', function () {
+     $('[data-spy="scroll"]').each(function () {
+       var $spy = $(this)
+-      $spy.scrollspy($spy.data())
++      Plugin.call($spy, $spy.data())
+     })
+   })
+ 
+ }(jQuery);
+ 
+ /* ========================================================================
+- * Bootstrap: tab.js v3.0.3
+- * http://getbootstrap.com/javascript/#tabs
++ * Bootstrap: tab.js v3.4.1
++ * https://getbootstrap.com/docs/3.4/javascript/#tabs
+  * ========================================================================
+- * Copyright 2013 Twitter, Inc.
+- *
+- * Licensed under the Apache License, Version 2.0 (the "License");
+- * you may not use this file except in compliance with the License.
+- * You may obtain a copy of the License at
+- *
+- * http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
++ * Copyright 2011-2019 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+  * ======================================================================== */
+ 
+ 
+-+function ($) { "use strict";
+++function ($) {
++  'use strict';
+ 
+   // TAB CLASS DEFINITION
+   // ====================
+ 
+   var Tab = function (element) {
++    // jscs:disable requireDollarBeforejQueryAssignment
+     this.element = $(element)
++    // jscs:enable requireDollarBeforejQueryAssignment
+   }
+ 
++  Tab.VERSION = '3.4.1'
++
++  Tab.TRANSITION_DURATION = 150
++
+   Tab.prototype.show = function () {
+     var $this    = this.element
+     var $ul      = $this.closest('ul:not(.dropdown-menu)')
+@@ -1778,27 +2290,35 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+ 
+     if (!selector) {
+       selector = $this.attr('href')
+-      selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
++      selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
+     }
+ 
+     if ($this.parent('li').hasClass('active')) return
+ 
+-    var previous = $ul.find('.active:last a')[0]
+-    var e        = $.Event('show.bs.tab', {
+-      relatedTarget: previous
++    var $previous = $ul.find('.active:last a')
++    var hideEvent = $.Event('hide.bs.tab', {
++      relatedTarget: $this[0]
++    })
++    var showEvent = $.Event('show.bs.tab', {
++      relatedTarget: $previous[0]
+     })
+ 
+-    $this.trigger(e)
++    $previous.trigger(hideEvent)
++    $this.trigger(showEvent)
+ 
+-    if (e.isDefaultPrevented()) return
++    if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return
+ 
+-    var $target = $(selector)
++    var $target = $(document).find(selector)
+ 
+-    this.activate($this.parent('li'), $ul)
++    this.activate($this.closest('li'), $ul)
+     this.activate($target, $target.parent(), function () {
++      $previous.trigger({
++        type: 'hidden.bs.tab',
++        relatedTarget: $this[0]
++      })
+       $this.trigger({
+-        type: 'shown.bs.tab'
+-      , relatedTarget: previous
++        type: 'shown.bs.tab',
++        relatedTarget: $previous[0]
+       })
+     })
+   }
+@@ -1807,15 +2327,21 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+     var $active    = container.find('> .active')
+     var transition = callback
+       && $.support.transition
+-      && $active.hasClass('fade')
++      && ($active.length && $active.hasClass('fade') || !!container.find('> .fade').length)
+ 
+     function next() {
+       $active
+         .removeClass('active')
+         .find('> .dropdown-menu > .active')
+         .removeClass('active')
++        .end()
++        .find('[data-toggle="tab"]')
++        .attr('aria-expanded', false)
+ 
+-      element.addClass('active')
++      element
++        .addClass('active')
++        .find('[data-toggle="tab"]')
++        .attr('aria-expanded', true)
+ 
+       if (transition) {
+         element[0].offsetWidth // reflow for transition
+@@ -1824,17 +2350,22 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+         element.removeClass('fade')
+       }
+ 
+-      if (element.parent('.dropdown-menu')) {
+-        element.closest('li.dropdown').addClass('active')
++      if (element.parent('.dropdown-menu').length) {
++        element
++          .closest('li.dropdown')
++          .addClass('active')
++          .end()
++          .find('[data-toggle="tab"]')
++          .attr('aria-expanded', true)
+       }
+ 
+       callback && callback()
+     }
+ 
+-    transition ?
++    $active.length && transition ?
+       $active
+-        .one($.support.transition.end, next)
+-        .emulateTransitionEnd(150) :
++        .one('bsTransitionEnd', next)
++        .emulateTransitionEnd(Tab.TRANSITION_DURATION) :
+       next()
+ 
+     $active.removeClass('in')
+@@ -1844,9 +2375,7 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+   // TAB PLUGIN DEFINITION
+   // =====================
+ 
+-  var old = $.fn.tab
+-
+-  $.fn.tab = function ( option ) {
++  function Plugin(option) {
+     return this.each(function () {
+       var $this = $(this)
+       var data  = $this.data('bs.tab')
+@@ -1856,6 +2385,9 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+     })
+   }
+ 
++  var old = $.fn.tab
++
++  $.fn.tab             = Plugin
+   $.fn.tab.Constructor = Tab
+ 
+ 
+@@ -1871,55 +2403,86 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+   // TAB DATA-API
+   // ============
+ 
+-  $(document).on('click.bs.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
++  var clickHandler = function (e) {
+     e.preventDefault()
+-    $(this).tab('show')
+-  })
++    Plugin.call($(this), 'show')
++  }
++
++  $(document)
++    .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler)
++    .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler)
+ 
+ }(jQuery);
+ 
+ /* ========================================================================
+- * Bootstrap: affix.js v3.0.3
+- * http://getbootstrap.com/javascript/#affix
++ * Bootstrap: affix.js v3.4.1
++ * https://getbootstrap.com/docs/3.4/javascript/#affix
+  * ========================================================================
+- * Copyright 2013 Twitter, Inc.
+- *
+- * Licensed under the Apache License, Version 2.0 (the "License");
+- * you may not use this file except in compliance with the License.
+- * You may obtain a copy of the License at
+- *
+- * http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
++ * Copyright 2011-2019 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+  * ======================================================================== */
+ 
+ 
+-+function ($) { "use strict";
+++function ($) {
++  'use strict';
+ 
+   // AFFIX CLASS DEFINITION
+   // ======================
+ 
+   var Affix = function (element, options) {
+     this.options = $.extend({}, Affix.DEFAULTS, options)
+-    this.$window = $(window)
++
++    var target = this.options.target === Affix.DEFAULTS.target ? $(this.options.target) : $(document).find(this.options.target)
++
++    this.$target = target
+       .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
+       .on('click.bs.affix.data-api',  $.proxy(this.checkPositionWithEventLoop, this))
+ 
+-    this.$element = $(element)
+-    this.affixed  =
+-    this.unpin    = null
++    this.$element     = $(element)
++    this.affixed      = null
++    this.unpin        = null
++    this.pinnedOffset = null
+ 
+     this.checkPosition()
+   }
+ 
+-  Affix.RESET = 'affix affix-top affix-bottom'
++  Affix.VERSION  = '3.4.1'
++
++  Affix.RESET    = 'affix affix-top affix-bottom'
+ 
+   Affix.DEFAULTS = {
+-    offset: 0
++    offset: 0,
++    target: window
++  }
++
++  Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) {
++    var scrollTop    = this.$target.scrollTop()
++    var position     = this.$element.offset()
++    var targetHeight = this.$target.height()
++
++    if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false
++
++    if (this.affixed == 'bottom') {
++      if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom'
++      return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom'
++    }
++
++    var initializing   = this.affixed == null
++    var colliderTop    = initializing ? scrollTop : position.top
++    var colliderHeight = initializing ? targetHeight : height
++
++    if (offsetTop != null && scrollTop <= offsetTop) return 'top'
++    if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom'
++
++    return false
++  }
++
++  Affix.prototype.getPinnedOffset = function () {
++    if (this.pinnedOffset) return this.pinnedOffset
++    this.$element.removeClass(Affix.RESET).addClass('affix')
++    var scrollTop = this.$target.scrollTop()
++    var position  = this.$element.offset()
++    return (this.pinnedOffset = position.top - scrollTop)
+   }
+ 
+   Affix.prototype.checkPositionWithEventLoop = function () {
+@@ -1929,31 +2492,41 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+   Affix.prototype.checkPosition = function () {
+     if (!this.$element.is(':visible')) return
+ 
+-    var scrollHeight = $(document).height()
+-    var scrollTop    = this.$window.scrollTop()
+-    var position     = this.$element.offset()
++    var height       = this.$element.height()
+     var offset       = this.options.offset
+     var offsetTop    = offset.top
+     var offsetBottom = offset.bottom
++    var scrollHeight = Math.max($(document).height(), $(document.body).height())
+ 
+     if (typeof offset != 'object')         offsetBottom = offsetTop = offset
+-    if (typeof offsetTop == 'function')    offsetTop    = offset.top()
+-    if (typeof offsetBottom == 'function') offsetBottom = offset.bottom()
++    if (typeof offsetTop == 'function')    offsetTop    = offset.top(this.$element)
++    if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element)
+ 
+-    var affix = this.unpin   != null && (scrollTop + this.unpin <= position.top) ? false :
+-                offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ? 'bottom' :
+-                offsetTop    != null && (scrollTop <= offsetTop) ? 'top' : false
++    var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom)
+ 
+-    if (this.affixed === affix) return
+-    if (this.unpin) this.$element.css('top', '')
++    if (this.affixed != affix) {
++      if (this.unpin != null) this.$element.css('top', '')
+ 
+-    this.affixed = affix
+-    this.unpin   = affix == 'bottom' ? position.top - scrollTop : null
++      var affixType = 'affix' + (affix ? '-' + affix : '')
++      var e         = $.Event(affixType + '.bs.affix')
+ 
+-    this.$element.removeClass(Affix.RESET).addClass('affix' + (affix ? '-' + affix : ''))
++      this.$element.trigger(e)
++
++      if (e.isDefaultPrevented()) return
++
++      this.affixed = affix
++      this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null
++
++      this.$element
++        .removeClass(Affix.RESET)
++        .addClass(affixType)
++        .trigger(affixType.replace('affix', 'affixed') + '.bs.affix')
++    }
+ 
+     if (affix == 'bottom') {
+-      this.$element.offset({ top: document.body.offsetHeight - offsetBottom - this.$element.height() })
++      this.$element.offset({
++        top: scrollHeight - height - offsetBottom
++      })
+     }
+   }
+ 
+@@ -1961,9 +2534,7 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+   // AFFIX PLUGIN DEFINITION
+   // =======================
+ 
+-  var old = $.fn.affix
+-
+-  $.fn.affix = function (option) {
++  function Plugin(option) {
+     return this.each(function () {
+       var $this   = $(this)
+       var data    = $this.data('bs.affix')
+@@ -1974,6 +2545,9 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+     })
+   }
+ 
++  var old = $.fn.affix
++
++  $.fn.affix             = Plugin
+   $.fn.affix.Constructor = Affix
+ 
+ 
+@@ -1996,10 +2570,10 @@ if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery"
+ 
+       data.offset = data.offset || {}
+ 
+-      if (data.offsetBottom) data.offset.bottom = data.offsetBottom
+-      if (data.offsetTop)    data.offset.top    = data.offsetTop
++      if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom
++      if (data.offsetTop    != null) data.offset.top    = data.offsetTop
+ 
+-      $spy.affix(data)
++      Plugin.call($spy, data)
+     })
+   })
+ 
+-- 
+1.8.3.1
+
+From 883b6675a23993f5f22702df17eb1b6f46a07ebd Mon Sep 17 00:00:00 2001
+From: Alexander Scheel <ascheel@redhat.com>
+Date: Tue, 30 Jun 2020 17:30:33 -0400
+Subject: [PATCH 15/17] Link in new Bootstrap CSS file
+
+Signed-off-by: Alexander Scheel <ascheel@redhat.com>
+(cherry picked from commit 1dda032e9527a98e0684e65260bb941dcaa18b48)
+---
+ base/server/share/webapps/pki/index.jsp  | 1 +
+ base/tps/shared/webapps/tps/index.jsp    | 1 +
+ base/tps/shared/webapps/tps/ui/index.jsp | 1 +
+ 3 files changed, 3 insertions(+)
+
+diff --git a/base/server/share/webapps/pki/index.jsp b/base/server/share/webapps/pki/index.jsp
+index 8eb5854..9360e1d 100644
+--- a/base/server/share/webapps/pki/index.jsp
++++ b/base/server/share/webapps/pki/index.jsp
+@@ -18,6 +18,7 @@
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+ <html>
+ <head>
++<link href="/pki/css/bootstrap.css" rel="stylesheet" media="screen, print">
+ <link href="/pki/css/patternfly.css" rel="stylesheet" media="screen, print">
+ <script type="text/javascript" language="JavaScript" src="/pki/js/jquery.js"></script>
+ <script type="text/javascript" language="JavaScript" src="/pki/js/jquery.i18n.properties.js"></script>
+diff --git a/base/tps/shared/webapps/tps/index.jsp b/base/tps/shared/webapps/tps/index.jsp
+index 3c9abf8..2046820 100644
+--- a/base/tps/shared/webapps/tps/index.jsp
++++ b/base/tps/shared/webapps/tps/index.jsp
+@@ -19,6 +19,7 @@
+ <head>
+     <title>Token Processing System</title>
+     <meta name="viewport" content="width=device-width, initial-scale=1.0">
++    <link href="/pki/css/bootstrap.css" rel="stylesheet" media="screen, print">
+     <link href="/pki/css/patternfly.css" rel="stylesheet" media="screen, print">
+     <script src="/pki/js/jquery.js"></script>
+     <script src="/pki/js/jquery.i18n.properties.js"></script>
+diff --git a/base/tps/shared/webapps/tps/ui/index.jsp b/base/tps/shared/webapps/tps/ui/index.jsp
+index 165a028..a729aa2 100644
+--- a/base/tps/shared/webapps/tps/ui/index.jsp
++++ b/base/tps/shared/webapps/tps/ui/index.jsp
+@@ -19,6 +19,7 @@
+ <head>
+     <title>Token Processing System</title>
+     <meta name="viewport" content="width=device-width, initial-scale=1.0">
++    <link href="/pki/css/bootstrap.css" rel="stylesheet" media="screen, print">
+     <link href="/pki/css/patternfly.css" rel="stylesheet" media="screen, print">
+     <link href="/pki/css/pki-ui.css" rel="stylesheet" type="text/css">
+     <script src="/pki/js/jquery.js"></script>
+-- 
+1.8.3.1
+
+From 49b0900f605b019bf9e63e51a4ad890181c88a9b Mon Sep 17 00:00:00 2001
+From: Matthew Harmsen <mharmsen@redhat.com>
+Date: Thu, 11 Feb 2021 16:47:17 -0700
+Subject: [PATCH 16/17] Fix renewal profile approval process
+
+Due to a recent change in PKI CLI, the CLI now passes along user
+authentication with submissions to the renewal endpoint. Unlike the EE
+pages, the REST API has passed along this authentication for a while.
+Due to a bug in the RenewalProcessor, requests with credentials against
+profiles with no authentication method and no ACLs result in the
+certificiate automatically being approved. This occurs because, when
+an earlier commit (cb9eb967) modified
+the code to allow Light-Weight SubCAs to issue certificates, validation
+wasn't done on the passed principal, to see if it was a trusted agent.
+Because profiles requring Agent approval have an empty ACL list (as, no
+user should be able to submit a certificate request and have it
+automatically signed without agent approval), authorize allows any user
+to approve this request and thus accepts the AuthToken.
+
+Critical analysis: the RenewalProcessor code interprets (authToken
+!= null) as evidence that the authenticated user is /authorized/ to
+immediately issue the certificate.  This mismatch of concerns (authn
+vs authz) resulted in a misunderstanding of system behaviour.  The
+"latent" AuthToken (from the HTTP request) was assigned to authToken
+without realising that authorization needed to be performed.
+
+We fix this by splitting the logic on whether the profile defines an
+authenticator.  If so, we (re)authenticate and authorize the user
+according to the profile configuration.
+
+If the profile does not define an authenticator but there is a
+principal in the HTTP request, if (and only if) the user has
+permission to approve certificate requests *and* the requested
+renewal profile is caManualRenewal (which is hardcoded to be used
+for LWCA renewal), then we issue the certificate immediately.  This
+special case ensures that LWCA renewal keeps working.
+
+Otherwise, if there is no principal in the HTTP request or the
+principal does not have permission to approve certificate requests,
+we leave the authToken unset.  The resulting renewal request will be
+created with status PENDING, i.e. enqueued for agent review.
+
+Signed-off-by: Fraser Tweedale <ftweedal@redhat.com>
+Signed-off-by: Alexander Scheel <ascheel@redhat.com>
+---
+ .../src/com/netscape/ca/CertificateAuthority.java  | 10 +++
+ .../cms/servlet/cert/RenewalProcessor.java         | 75 ++++++++++++++++++++--
+ 2 files changed, 79 insertions(+), 6 deletions(-)
+
+diff --git a/base/ca/src/com/netscape/ca/CertificateAuthority.java b/base/ca/src/com/netscape/ca/CertificateAuthority.java
+index f414628..bd99344 100644
+--- a/base/ca/src/com/netscape/ca/CertificateAuthority.java
++++ b/base/ca/src/com/netscape/ca/CertificateAuthority.java
+@@ -2927,6 +2927,16 @@ public class CertificateAuthority
+ 
+         IProfileSubsystem ps = (IProfileSubsystem)
+             CMS.getSubsystem(IProfileSubsystem.ID);
++        /* NOTE: hard-coding the profile to use for Lightweight CA renewal
++         * might be OK, but caManualRenewal was not the right one to use.
++         * As a consequence, we have an undesirable special case in
++         * RenewalProcessor.processRenewal().
++         *
++         * We should introduce a new profile specifically for LWCA renewal,
++         * with an authenticator and ACLs to match the authz requirements
++         * for the renewAuthority REST resource itself.  Then we can use
++         * it here, and remove the workaround from RenewalProcessor.
++         */
+         IProfile profile = ps.getProfile("caManualRenewal");
+         CertEnrollmentRequest req = CertEnrollmentRequestFactory.create(
+             new ArgBlock(), profile, httpReq.getLocale());
+diff --git a/base/server/cms/src/com/netscape/cms/servlet/cert/RenewalProcessor.java b/base/server/cms/src/com/netscape/cms/servlet/cert/RenewalProcessor.java
+index 39c893e..cf76542 100644
+--- a/base/server/cms/src/com/netscape/cms/servlet/cert/RenewalProcessor.java
++++ b/base/server/cms/src/com/netscape/cms/servlet/cert/RenewalProcessor.java
+@@ -33,6 +33,7 @@ import org.apache.commons.lang.StringUtils;
+ 
+ import com.netscape.certsrv.apps.CMS;
+ import com.netscape.certsrv.authentication.IAuthToken;
++import com.netscape.certsrv.authorization.AuthzToken;
+ import com.netscape.certsrv.base.BadRequestDataException;
+ import com.netscape.certsrv.base.BadRequestException;
+ import com.netscape.certsrv.base.EBaseException;
+@@ -266,15 +267,77 @@ public class RenewalProcessor extends CertProcessor {
+ 
+             // before creating the request, authenticate the request
+             IAuthToken authToken = null;
+-            Principal principal = request.getUserPrincipal();
+-            if (principal instanceof PKIPrincipal)
+-                authToken = ((PKIPrincipal) principal).getAuthToken();
+-            if (authToken == null)
+-                authToken = authenticate(request, origReq, authenticator, context, true, credentials);
++            if (authenticator != null) {
++                /* The profile specifies an authenticator.  Use it to
++                 * authenticate the user.  Ignore the "latent" session
++                 * principal (if any).
++                 */
++                authToken = authenticate(
++                    request,
++                    origReq,
++                    authenticator,
++                    context,
++                    true /* isRenewal */,
++                    credentials);
++            } else {
++                /* When authenticator is null, we expect manual agent
++                 * review (leave authToken as null).
++                 *
++                 * But as a special case to ensure Lightweight CA (LWCA)
++                 * renewal works, if there is a latent user in the HTTP
++                 * request, we use that user (i.e. set authToken to the
++                 * principal's IAuthToken) if and only if:
++                 *
++                 * - The renewal profile is caManualRenewal (LWCA renewal
++                 *   is hardcoded to use this profile); AND
++                 *
++                 * - The latent user is authorized to "execute"
++                 *   certificate requests (i.e. agent approval)
++                 *
++                 * See also CertificateAuthority.renewAuthority().
++                 */
++
++                Principal principal = request.getUserPrincipal();
++                if (
++                    renewProfileId.equals("caManualRenewal")
++                    && principal instanceof PKIPrincipal
++                ) {
++                    IAuthToken latentToken = ((PKIPrincipal) principal).getAuthToken();
++                    AuthzToken authzToken = authorize(
++                        "DirAclAuthz", latentToken, "certServer.ca.certrequests", "execute");
++                    if (authzToken != null) {
++                        // Success (no exception); user is authorized to approve
++                        // cert requests.  Set the authToken.
++                        //
++                        // NOTE: This authz does not replace or subsume the
++                        // profile-specific authz check below.
++                        authToken = latentToken;
++                    } else {
++                        // leave authToken as null to enqueue a pending request.
++                    }
++                } else {
++                    // not caManualRenewal or no latent principal;
++                    // leave authToken as null to enqueue a pending request.
++                }
++            }
+ 
+-            // authentication success, now authorize
++            /* Authorize the request.
++             *
++             * If authToken != null, it will be checked against ACLs specified
++             * in the profile (if any).  If ACLs are defined and authToken does
++             * not match, throws an authorization exception.
++             *
++             * If authToken == null, no check is performed (even if the profile
++             * defines ACLs).  This is fine, because null authToken will cause
++             * the request status to be 'pending' [agent approval].
++             */
+             authorize(profileId, renewProfile, authToken);
+ 
++            /* At this point, the request will be created.  If authToken
++             * is non-null, then the certificate will be issued
++             * immediately.  Otherwise the request will be pending. */
++
++
+             ///////////////////////////////////////////////
+             // create and populate requests
+             ///////////////////////////////////////////////
+-- 
+1.8.3.1
+
+
+From 7732ccb75f277338e728b963d06e92ca7c37414b Mon Sep 17 00:00:00 2001
+From: Matthew Harmsen <mharmsen@redhat.com>
+Date: Thu, 11 Feb 2021 16:48:18 -0700
+Subject: [PATCH 17/17] Resolve XSS in ca queryCert pagination
+
+Several values in ListCerts were reflected back to the caller, making a
+reflected XSS attack possible. These values were sanitized and the
+front-end template fixed to prevent this type of attack in general.
+
+Resolves: CVE-2020-25715
+Signed-off-by: Alexander Scheel <ascheel@redhat.com>
+---
+ base/ca/shared/webapps/ca/ee/ca/queryCert.template | 96 +++++++++++++---------
+ .../com/netscape/cms/servlet/cert/ListCerts.java   | 14 ++--
+ .../netscape/cms/servlet/common/CMSTemplate.java   |  4 +
+ 3 files changed, 68 insertions(+), 46 deletions(-)
+
+diff --git a/base/ca/shared/webapps/ca/ee/ca/queryCert.template b/base/ca/shared/webapps/ca/ee/ca/queryCert.template
+index dcf183d..878e3be 100644
+--- a/base/ca/shared/webapps/ca/ee/ca/queryCert.template
++++ b/base/ca/shared/webapps/ca/ee/ca/queryCert.template
+@@ -390,6 +390,27 @@ function renderHidden(name,value)
+ 	return 	"<INPUT TYPE='hidden' NAME='"+ name +"' VALUE=''>\n";
+ }
+ 
++function renderHiddenElement(name, value)
++{
++    var result = document.createElement("input");
++    result.type = 'hidden';
++    result.name = name;
++    result.value = value ? value : '';
++    return result;
++}
++
++function renderNextButtonElement(name, label, disabled)
++{
++    var result = document.createElement("button");
++    result.name = name;
++    result.value = label;
++    result.innerText = label;
++    result.width = 72;
++    result.disabled = disabled ? true : false;
++    result.onClick = () => doNext(result);
++    return result;
++}
++
+ function doNext(element)
+ {
+ 	var form = element.form;
+@@ -428,48 +449,45 @@ function doNext(element)
+ 
+ function displayNextForm()
+ {
+-	document.write(
+-//"<div align=center> \n"+
+-"<FORM NAME ='nextForm' METHOD=POST ACTION=''>\n"+
+-renderHidden("op"));
++    var nextForm = document.createElement("form");
++    nextForm.name = 'nextForm';
++    nextForm.method = 'POST';
++    nextForm.action = '';
+ 
+-if (result.header.revokeAll != null) {
+-	document.write(renderHidden("revokeAll"));
+-}
++    nextForm.appendChild(renderHiddenElement("op"));
+ 
+-if (result.header.queryFilterHash != null) {
+-    document.write(renderHidden("queryFilterHash"));
+-}
++    if (result.header.revokeAll != null) {
++        nextForm.appendChild(renderHiddenElement("revokeAll"));
++    }
++
++    if (result.header.queryFilterHash != null) {
++        nextForm.appendChild(renderHiddenElement("queryFilterHash"));
++    }
++
++    var disabledDown = ((result.header.querySentinelDown == null) ||
++                        (result.fixed.maxCount+1 >= result.header.currentRecordCount));
++    var disabledUp = (result.header.querySentinelUp != null && result.header.querySentinelUp <= 1);
++
++    nextForm.appendChild(renderNextButtonElement("begin", "|<<"));
++    nextForm.appendChild(renderNextButtonElement("up", "<", disabledUp));
++
++    for (let element of ["totalRecordCount", "queryCertFilter", "skipRevoked", "skipNonValid", "querySentinelDown", "querySentinelUp", "serialTo", "direction"]) {
++        nextForm.appendChild(renderHiddenElement(element, result.header[element]));
++    }
++
++    var maxCount = document.createElement('input');
++    maxCount.style = 'padding-left: 2px;';
++    maxCount.type = 'text';
++    maxCount.size = '16';
++    maxCount.name = 'maxCount';
++    maxCount.value = result.header.maxCount;
++    nextForm.appendChild(maxCount);
++
++    nextForm.appendChild(renderNextButtonElement("down", ">", disabledDown));
++    nextForm.appendChild(renderNextButtonElement("end", ">>|"));
+ 
+-var disabledDown = ((result.header.querySentinelDown == null) ||
+-                    (result.fixed.maxCount+1 >= result.header.currentRecordCount)) ? "disabled='true'" : "";
+-var disabledUp = (result.header.querySentinelUp != null && result.header.querySentinelUp <= 1) ? "disabled='true'" : "";
+-
+-document.write(
+-"<button NAME=begin onClick='doNext(this)' VALUE='|<<' width='72'>|&lt;&lt;</button>\n"+
+-"<button "+disabledUp+" NAME=up onClick='doNext(this)' VALUE='<' width='72'>&lt;</button>\n"+
+-"<INPUT TYPE=hidden NAME=totalRecordCount VALUE='"+
+-result.header.totalRecordCount+ "'>\n"+
+-"<INPUT TYPE=hidden NAME=queryCertFilter VALUE='"+
+-result.header.queryCertFilter+ "'>\n"+
+-"<INPUT TYPE=hidden NAME=skipRevoked VALUE='"+
+-(result.header.skipRevoked ? result.header.skipRevoked : "") + "'>\n"+
+-"<INPUT TYPE=hidden NAME=skipNonValid VALUE='"+
+-(result.header.skipNonValid ? result.header.skipNonValid : "") + "'>\n"+
+-"<INPUT TYPE=hidden NAME=querySentinelDown VALUE='"+
+-result.header.querySentinelDown+ "'>\n"+
+-"<INPUT TYPE=hidden NAME=querySentinelUp VALUE='"+
+-result.header.querySentinelUp+ "'>\n"+
+-"<INPUT TYPE=hidden NAME=serialTo VALUE='"+
+-result.header.serialTo+ "'>\n"+
+-"<INPUT TYPE=hidden NAME=direction VALUE='"+
+-result.header.direction+ "'>\n"+
+-"<INPUT style='padding-left: 2px;' TYPE=text SIZE=16 NAME=maxCount VALUE='"+
+-result.header.maxCount+ "'>\n"+
+-
+-"<button "+disabledDown+" NAME=down onClick='doNext(this)' VALUE='>' width='72'>&gt;</button>\n"+
+-"<button NAME=end onClick='doNext(this)' VALUE='>>|' width='72'>&gt;&gt;|</button>\n"+
+-"</FORM>\n");
++    document.body.appendChild(nextForm);
++    return nextForm;
+ }
+ 
+ function doRevokeAll(form)
+diff --git a/base/server/cms/src/com/netscape/cms/servlet/cert/ListCerts.java b/base/server/cms/src/com/netscape/cms/servlet/cert/ListCerts.java
+index 01c4b6a..31fe662 100644
+--- a/base/server/cms/src/com/netscape/cms/servlet/cert/ListCerts.java
++++ b/base/server/cms/src/com/netscape/cms/servlet/cert/ListCerts.java
+@@ -534,9 +534,9 @@ public class ListCerts extends CMSServlet {
+             CMS.debug("ListCerts: no next record");
+         }
+ 
+-        header.addStringValue("op", req.getParameter("op"));
++        header.addStringValue("op", CMSTemplate.escapeJavaScriptString(req.getParameter("op")));
+         if (revokeAll != null)
+-            header.addStringValue("revokeAll", revokeAll);
++            header.addStringValue("revokeAll", CMSTemplate.escapeJavaScriptString(revokeAll));
+ 
+         if (mAuthName != null)
+             header.addStringValue("issuerName", mAuthName.toString());
+@@ -548,10 +548,10 @@ public class ListCerts extends CMSServlet {
+         header.addStringValue("queryCertFilter", filter);
+ 
+         String skipRevoked = req.getParameter("skipRevoked");
+-        header.addStringValue("skipRevoked", skipRevoked == null ? "" : skipRevoked);
++        header.addStringValue("skipRevoked", skipRevoked == null ? "" : (skipRevoked.equals("on") ? "on" : "off"));
+ 
+         String skipNonValid = req.getParameter("skipNonValid");
+-        header.addStringValue("skipNonValid", skipNonValid == null ? "" : skipNonValid);
++        header.addStringValue("skipNonValid", skipNonValid == null ? "" : (skipNonValid.equals("on") ? "on" : "off"));
+ 
+         header.addStringValue("templateName", "queryCert");
+         header.addStringValue("queryFilter", filter);
+@@ -646,7 +646,7 @@ public class ListCerts extends CMSServlet {
+         if (cert.getSubjectDN().toString().equals("")) {
+             rarg.addStringValue("subject", " ");
+         } else
+-            rarg.addStringValue("subject", cert.getSubjectDN().toString());
++            rarg.addStringValue("subject", CMSTemplate.escapeJavaScriptString(cert.getSubjectDN().toString()));
+ 
+         rarg.addStringValue("type", "X.509");
+ 
+@@ -680,11 +680,11 @@ public class ListCerts extends CMSServlet {
+ 
+         if (issuedBy == null)
+             issuedBy = "";
+-        rarg.addStringValue("issuedBy", issuedBy); // cert.getIssuerDN().toString()
++        rarg.addStringValue("issuedBy", CMSTemplate.escapeJavaScriptString(issuedBy)); // cert.getIssuerDN().toString()
+         rarg.addLongValue("issuedOn", rec.getCreateTime().getTime() / 1000);
+ 
+         rarg.addStringValue("revokedBy",
+-                ((rec.getRevokedBy() == null) ? "" : rec.getRevokedBy()));
++                ((rec.getRevokedBy() == null) ? "" : CMSTemplate.escapeJavaScriptString(rec.getRevokedBy())));
+         if (rec.getRevokedOn() == null) {
+             rarg.addStringValue("revokedOn", null);
+         } else {
+diff --git a/base/server/cms/src/com/netscape/cms/servlet/common/CMSTemplate.java b/base/server/cms/src/com/netscape/cms/servlet/common/CMSTemplate.java
+index 8588dfc..87aff98 100644
+--- a/base/server/cms/src/com/netscape/cms/servlet/common/CMSTemplate.java
++++ b/base/server/cms/src/com/netscape/cms/servlet/common/CMSTemplate.java
+@@ -358,6 +358,10 @@ public class CMSTemplate extends CMSFile {
+      * outputting in JavaScript.
+      */
+     public static String escapeJavaScriptStringHTML(String v) {
++        if (v == null) {
++            return null;
++        }
++
+         return StringEscapeUtils.escapeJavaScript(StringEscapeUtils.escapeHtml(v));
+     }
+ 
+-- 
+1.8.3.1
+
diff --git a/SPECS/pki-core.spec b/SPECS/pki-core.spec
index 67e317a..afc93e3 100644
--- a/SPECS/pki-core.spec
+++ b/SPECS/pki-core.spec
@@ -65,13 +65,13 @@
 Name:             pki-core
 %if 0%{?rhel}
 Version:                10.5.18
-%define redhat_release  7
+%define redhat_release  12
 %define redhat_stage    0
 #%define default_release %{redhat_release}.%{redhat_stage}
 %define default_release %{redhat_release}
 %else
 Version:                10.5.18
-%define fedora_release  7
+%define fedora_release  12
 %define fedora_stage    0
 #%define default_release %{fedora_release}.%{fedora_stage}
 %define default_release %{fedora_release}
@@ -212,6 +212,11 @@ Patch2:  pki-core-rhel-7-9-rhcs-9-7-post-beta.patch
 Patch3:  pki-core-Fix-RSA-PSS-for-SHA512.patch
 Patch4:  pki-core-rhel-7-9-rhcs-9-7-post-beta-2.patch
 Patch5:  pki-core-Fix-CMCResponse-tool.patch
+Patch6:  pki-core-rhel-7-9-rhcs-9-7-bu-2.patch
+Patch7:  pki-core-Fix-auditProfileUpgrade.patch
+Patch8:  pki-core-Fix-AddProfileCaAuditSigningCert.patch
+Patch9:  pki-core-rhel-7-9-rhcs-9-7-bu-4.patch
+Patch10: pki-core-Change-var-TPS-to-tps.patch
 
 # Obtain version phase number (e. g. - used by "alpha", "beta", etc.)
 #
@@ -565,6 +570,13 @@ Obsoletes:        redhat-pki-console-theme < 10.3.0
 Obsoletes:        redhat-pki-server-theme < 10.3.0
 %endif
 
+Provides:         bundled(js-backbone) = 1.4.0
+Provides:         bundled(js-bootstrap) = 3.4.1
+Provides:         bundled(js-jquery) = 3.5.1
+Provides:         bundled(js-jquery-i18n-properties) = 1.2.7
+Provides:         bundled(js-patternfly) = 3.59.2
+Provides:         bundled(js-underscore) = 1.9.2
+
 %description -n   pki-server
 The PKI Server Framework is required by the following four PKI subsystems:
 
@@ -811,6 +823,11 @@ This package is a part of the PKI Core used by the Certificate System.
 %patch3 -p1
 %patch4 -p1
 %patch5 -p1
+%patch6 -p1
+%patch7 -p1
+%patch8 -p1
+%patch9 -p1
+%patch10 -p1
 
 %clean
 %{__rm} -rf %{buildroot}
@@ -1348,6 +1365,117 @@ fi
 %endif # %{with server}
 
 %changelog
+* Wed Feb 24 2021 Dogtag Team <pki-devel@redhat.com> 10.5.18-12
+- Change variable 'TPS' to 'tps'
+- ##########################################################################
+- # RHEL 7.9:
+- ##########################################################################
+- Bugzilla Bug 1883639 - Add KRA Transport and Storage Certificates
+  profiles, audit for IPA (edewata)
+- ##########################################################################
+- # Backported CVEs (ascheel):
+- ##########################################################################
+- Bugzilla Bug 1724697 - CVE-2019-10180 pki-core: unsanitized token
+  parameters in TPS resulting in stored XSS [certificate_system_9-default]
+  (edewata, ascheel)
+- Bugzilla Bug 1725128 - CVE-2019-10178 pki-core: stored Cross-site
+  scripting (XSS) in the pki-tps web Activity tab
+  [certificate_system_9-default] (edewata, ascheel)
+- Bugzilla Bug 1791100 - CVE-2020-1696 pki-core: Stored XSS in TPS profile
+  creation [certificate_system_9-default] (edewata, ascheel)
+- Bugzilla Bug 1724688 - CVE-2019-10146 pki-core: Reflected Cross-Site
+  Scripting in 'path length' constraint field in CA's Agent page
+  [rhel-7.9.z] (dmoluguw, ascheel)
+- Bugzilla Bug 1789843 - CVE-2019-10221 pki-core: reflected cross site
+  scripting in getcookies?url= endpoint in CA [rhel-7.9.z]
+  (dmoluguw, ascheel)
+- Bugzilla Bug 1724713 - CVE-2019-10179 pki-core: pki-core/pki-kra:
+  Reflected XSS in recoveryID search field at KRA's DRM agent page in
+  authorize recovery tab [rhel-7.9.z] (ascheel)
+- Bugzilla Bug 1798011 - CVE-2020-1721 pki-core: KRA vulnerable to
+  reflected XSS via the getPk12 page [rhel-7.9.z] (ascheel,jmagne)
+- ##########################################################################
+- Update to jquery v3.4.1 (ascheel)
+- Update to jquery-i18n-properties v1.2.7 (ascheel)
+- Update to backbone v1.4.0 (ascheel)
+- Upgrade to underscore v1.9.2 (ascheel)
+- Update to patternfly v3.59.3 (ascheel)
+- Update to jQuery v3.5.1 (ascheel)
+- Upgrade to bootstrap v3.4.1 (ascheel)
+- Link in new Bootstrap CSS file (ascheel)
+- ##########################################################################
+- # RHCS 9.7:
+- ##########################################################################
+- # Bugzilla Bug #1733588 - Rebase redhat-pki, redhat-pki-theme, pki-core, and
+  # pki-console to 10.5.18 in RHCS 9.7
+
+* Thu Feb 11 2021 Dogtag Team <pki-devel@redhat.com> 10.5.18-11
+- ##########################################################################
+- # RHEL 7.9:
+- ##########################################################################
+- Bugzilla Bug 1883639 - Add KRA Transport and Storage Certificates
+  profiles, audit for IPA (edewata)
+- ##########################################################################
+- # Backported CVEs (ascheel):
+- ##########################################################################
+- Bugzilla Bug 1724697 - CVE-2019-10180 pki-core: unsanitized token
+  parameters in TPS resulting in stored XSS [certificate_system_9-default]
+  (edewata, ascheel)
+- Bugzilla Bug 1725128 - CVE-2019-10178 pki-core: stored Cross-site
+  scripting (XSS) in the pki-tps web Activity tab
+  [certificate_system_9-default] (edewata, ascheel)
+- Bugzilla Bug 1791100 - CVE-2020-1696 pki-core: Stored XSS in TPS profile
+  creation [certificate_system_9-default] (edewata, ascheel)
+- Bugzilla Bug 1724688 - CVE-2019-10146 pki-core: Reflected Cross-Site
+  Scripting in 'path length' constraint field in CA's Agent page
+  [rhel-7.9.z] (dmoluguw, ascheel)
+- Bugzilla Bug 1789843 - CVE-2019-10221 pki-core: reflected cross site
+  scripting in getcookies?url= endpoint in CA [rhel-7.9.z]
+  (dmoluguw, ascheel)
+- Bugzilla Bug 1724713 - CVE-2019-10179 pki-core: pki-core/pki-kra:
+  Reflected XSS in recoveryID search field at KRA's DRM agent page in
+  authorize recovery tab [rhel-7.9.z] (ascheel)
+- Bugzilla Bug 1798011 - CVE-2020-1721 pki-core: KRA vulnerable to
+  reflected XSS via the getPk12 page [rhel-7.9.z] (ascheel,jmagne)
+- ##########################################################################
+- Update to jquery v3.4.1 (ascheel)
+- Update to jquery-i18n-properties v1.2.7 (ascheel)
+- Update to backbone v1.4.0 (ascheel)
+- Upgrade to underscore v1.9.2 (ascheel)
+- Update to patternfly v3.59.3 (ascheel)
+- Update to jQuery v3.5.1 (ascheel)
+- Upgrade to bootstrap v3.4.1 (ascheel)
+- Link in new Bootstrap CSS file (ascheel)
+- ##########################################################################
+- # RHCS 9.7:
+- ##########################################################################
+- # Bugzilla Bug #1733588 - Rebase redhat-pki, redhat-pki-theme, pki-core, and
+  # pki-console to 10.5.18 in RHCS 9.7
+
+* Fri Dec  4 2020 Dogtag Team <pki-devel@redhat.com> 10.5.18-10
+- Bugzilla Bug #1883639 - additional fix to upgrade script (edewata)
+
+* Thu Dec  3 2020 Dogtag Team <pki-devel@redhat.com> 10.5.18-9
+- Bugzilla Bug #1883639 - additional support on upgrade for audit
+  cert profile and auditProfileUpgrade + auditProfileUpgrade part 2 (cfu)
+
+* Tue Nov 17 2020 Dogtag Team <pki-devel@redhat.com> 10.5.18-8
+- ##########################################################################
+- # RHEL 7.9:
+- ##########################################################################
+- Bugzilla Bug #1883639 - add profile caAuditSigningCert (cfu)
+- ##########################################################################
+- # RHCS 9.7:
+- ##########################################################################
+- # Bugzilla Bug #1710978 - TPS - Add logging to tdbAddCertificatesForCUID if
+  # adding or searching for cert record fails (jmagne)
+- # Bugzilla Bug #1858860 - TPS - Update Error Codes returned to client
+  # (CIW/ESC) to Match CS8. (jmagne)
+- # Bugzilla Bug #1858861 - TPS - Server side key generation is not working
+  # for Identity only tokens Missing some commits (cfu)
+- # Bugzilla Bug #1858867 - TPS does not check token cuid on the user
+  # externalReg record during PIN reset (cfu)
+
 * Wed May 27 2020 Dogtag Team <pki-devel@redhat.com> 10.5.18-7
 - Patch for CMCResponse tool
 - Bugzilla Bug #1710109 - add RSA PSS support - fix CMCResponse tool (jmagne)