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 <script> + * 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: { +- '&': '&', +- '<': '<', +- '>': '>', +- '"': '"', +- "'": ''' +- } ++ // 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 = { ++ '&': '&', ++ '<': '<', ++ '>': '>', ++ '"': '"', ++ "'": ''', ++ '`': '`' + }; ++ 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'>|<<</button>\n"+ +-"<button "+disabledUp+" NAME=up onClick='doNext(this)' VALUE='<' width='72'><</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'>></button>\n"+ +-"<button NAME=end onClick='doNext(this)' VALUE='>>|' width='72'>>>|</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)