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 +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 +(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" +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 +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 +-# +-# 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 ++# ++# 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 +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 +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 +(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 cnIssuerPairsRemaining = new ArrayList(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 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 +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 +(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 +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 +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 ++# ++# 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 +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 +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 +(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" +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 +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 +(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.()"); + } +@@ -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 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 properties = database.getRecord(profileData.getID()).getProperties(); +- Map 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 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 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 +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 +Signed-off-by: Alexander Scheel +(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(''); + } else if (recordSet[i].defListSet[j].defSyntax == 'string_list') { + document.writeln(''); + } else if (recordSet[i].defListSet[j].defSyntax == 'integer') { +- document.writeln(''); ++ document.writeln(''); + } else if (recordSet[i].defListSet[j].defSyntax == 'image_url') { + document.writeln(''); + document.writeln(''); +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 +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 +Signed-off-by: Alexander Scheel +(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 +Date: Fri, 19 Jun 2020 12:28:38 -0400 +Subject: [PATCH 05/17] Replace CMSTemplate custom sanitization with lang2 + +Signed-off-by: Alexander Scheel +(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)' || +- in[i+1] == 'x' || in[i+1] == ';' || +- in[i+1] == '\"' || in[i+1] == '\'' || in[i+1] == '\\')) { +- if (in[i+1] == 'x' && ((i+3)') { +- 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 +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 +(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 +Date: Tue, 9 Jun 2020 15:06:21 -0700 +Subject: [PATCH 07/17] Address CVE-2020-1721. (#434) + +Co-authored-by: Jack Magne +(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.

"); +- document.write("

");
+         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("
"); + if (result.header.errorDescription != null) { + document.write('

Additional Information:

'); +- document.write('

'); +- document.write(result.header.errorDescription); +- document.write('
'); ++ 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("

"); + 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.

"); +- document.write("

");
+         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("
"); + if (result.header.errorDescription != null) { + document.write('

Additional Information:

'); +- document.write('

'); +- document.write(result.header.errorDescription); +- document.write('
'); ++ 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("

"); + 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.

"); +- document.write("

");
+         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("
"); + if (result.header.errorDescription != null) { + document.write('

Additional Information:

'); +- document.write('

'); +- document.write(result.header.errorDescription); +- document.write('
'); ++ 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("

"); + 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.

"); +- document.write("

");
+         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("
"); + if (result.header.errorDescription != null) { + document.write('

Additional Information:

'); +- document.write('

'); +- document.write(result.header.errorDescription); +- document.write('
'); ++ 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("

"); + document.write("Please consult your local administrator for " + +-- +1.8.3.1 + + +From a4e95bb22a79c48157f7d3902515411cdbf6e302 Mon Sep 17 00:00:00 2001 +From: Alexander Scheel +Date: Mon, 16 Mar 2020 11:44:53 -0400 +Subject: [PATCH 08/17] Update to jquery v3.4.1 + +Signed-off-by: Alexander Scheel +(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 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 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 = "
"; +- +- // 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 = ""; ++ // https://bugs.jquery.com/ticket/12359 ++ docElem.appendChild( el ).innerHTML = "" + ++ ""; ++ ++ // 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 = "" + ++ ""; + +- // 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: ) 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 = ""; +- return div.firstChild.getAttribute("href") === "#" ; ++// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx ++if ( !assert(function( el ) { ++ el.innerHTML = ""; ++ 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 = ""; +- div.firstChild.setAttribute( "value", "" ); +- return div.firstChild.getAttribute( "value" ) === ""; ++if ( !support.attributes || !assert(function( el ) { ++ el.innerHTML = ""; ++ 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 = "
a"; + +- // 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>"; ++ // 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 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 = "
t
"; +- 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.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 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, "" ], + +-// 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 or other required elements. ++ thead: [ 1, "", "
" ], ++ col: [ 2, "", "
" ], ++ tr: [ 2, "", "
" ], ++ td: [ 3, "", "
" ], + +- 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 = ""; ++ 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 = /\s*$/g, +- +- // We have to close these tags to support XHTML (#13200) +- wrapMap = { +- option: [ 1, "" ], +- legend: [ 1, "
", "
" ], +- area: [ 1, "", "" ], +- param: [ 1, "", "" ], +- thead: [ 1, "", "
" ], +- tr: [ 2, "", "
" ], +- col: [ 2, "", "
" ], +- td: [ 3, "", "
" ], +- +- // 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
", "
" ] +- }, +- 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>" ); ++ // 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 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>" ) + 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 from table fragments +- if ( !jQuery.support.tbody ) { ++ this.isDefaultPrevented = returnTrue; + +- // String was a , *may* have spurious +- elem = tag === "table" && !rtbody.test( elem ) ? +- tmp.firstChild : ++ if ( e && !this.isSimulated ) { ++ e.preventDefault(); ++ } ++ }, ++ stopPropagation: function() { ++ var e = this.originalEvent; + +- // String was a bare or +- wrap[1] === "
" && !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 = /\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>" ); ++ }, + +- 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("