389-ds-base (1.4.4.8-1) unstable; urgency=medium
authorTimo Aaltonen <tjaalton@debian.org>
Thu, 12 Nov 2020 13:57:11 +0000 (13:57 +0000)
committerTimo Aaltonen <tjaalton@debian.org>
Thu, 12 Nov 2020 13:57:11 +0000 (13:57 +0000)
  * New upstream release.
  * fix-systemctl-path.diff, drop-old-man.diff: Dropped, obsolete.
  * fix-prlog-include.diff: Fix build by dropping nspr4/ prefix.
  * install, rules: Clean up perl cruft that got removed upstream.
  * install: Add openldap_to_ds.
  * watch: Follow 1.4.4.x.

[dgit import unpatched 389-ds-base 1.4.4.8-1]

42 files changed:
1  2 
debian/389-ds-base-dev.install
debian/389-ds-base-libs.install
debian/389-ds-base.default
debian/389-ds-base.dirs
debian/389-ds-base.install
debian/389-ds-base.links
debian/389-ds-base.lintian-overrides
debian/389-ds-base.postinst
debian/389-ds-base.postrm
debian/389-ds-base.prerm
debian/README.Debian
debian/changelog
debian/cockpit-389-ds.install
debian/control
debian/copyright
debian/gitlab-ci.yml
debian/missing-sources/bootpopup.js
debian/missing-sources/bootstrap.js
debian/missing-sources/c3.js
debian/missing-sources/d3.js
debian/missing-sources/jquery-1.12.4.js
debian/missing-sources/jquery-3.3.1.js
debian/missing-sources/jquery-ui.js
debian/missing-sources/jquery.dataTables.js
debian/missing-sources/jquery.dataTables.select.js
debian/missing-sources/jquery.dropdown.js
debian/missing-sources/jquery.js
debian/missing-sources/jquery.timepicker.js
debian/missing-sources/jstree.js
debian/missing-sources/moment.js
debian/missing-sources/patternfly.js
debian/patches/CVE-2017-15135.patch
debian/patches/fix-prlog-include.diff
debian/patches/fix-saslpath.diff
debian/patches/series
debian/python3-lib389.install
debian/rules
debian/source/format
debian/source/lintian-overrides
debian/tests/control
debian/tests/setup
debian/watch

index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2a61a5f4e5154960a891ffea8441a93686afa4a1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++usr/include/dirsrv/*
++usr/include/svrcore.h
++usr/lib/*/dirsrv/libldaputil.so
++usr/lib/*/dirsrv/libns-dshttpd.so
++usr/lib/*/dirsrv/librewriters.so
++usr/lib/*/dirsrv/libsds.so
++usr/lib/*/dirsrv/libslapd.so
++usr/lib/*/libsvrcore.so
++usr/lib/*/pkgconfig/*
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7b9c4dd33f54ec0d6fc86723ce5253257daaf5cc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++usr/lib/*/dirsrv/libldaputil.so.*
++usr/lib/*/dirsrv/libns-dshttpd-*.so
++usr/lib/*/dirsrv/librewriters.so.*
++usr/lib/*/dirsrv/libsds.so.*
++usr/lib/*/dirsrv/libslapd.so.*
++usr/lib/*/libsvrcore.so.*
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..14beb77cc8e3af8c867c50cab5d0963354861745
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++# Defaults for dirsrv
++#
++# This is a POSIX shell fragment
++
++# Enable bindnow hardening
++LD_BIND_NOW=1
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f12d71e63d37fdc0f170704048b6a4c2ecad04c8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2 @@@
++var/log/dirsrv
++var/lib/dirsrv
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..58178de3a114e08493e63cde26048c45258f5b26
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++etc/dirsrv/config/
++etc/dirsrv/schema/*.ldif
++etc/systemd/
++lib/systemd/system/dirsrv-snmp.service
++lib/systemd/system/dirsrv.target
++lib/systemd/system/dirsrv@.service
++lib/systemd/system/dirsrv@.service.d/custom.conf
++usr/bin/dbscan
++usr/bin/ds-logpipe
++usr/bin/ds-replcheck
++usr/bin/ldclt
++usr/bin/logconv
++usr/bin/pwdhash
++usr/lib/*/dirsrv/plugins/*.so
++usr/lib/*/dirsrv/python/
++usr/libexec/dirsrv/dscontainer
++usr/libexec/ds_systemd_ask_password_acl
++usr/lib/sysctl.d/70-dirsrv.conf
++usr/sbin/ldap-agent
++usr/sbin/ns-slapd
++usr/sbin/openldap_to_ds
++usr/share/dirsrv/data
++usr/share/dirsrv/inf
++usr/share/dirsrv/mibs
++usr/share/dirsrv/schema
++usr/share/gdb/auto-load/usr/sbin/ns-slapd-gdb.py
++usr/share/man/man1/dbscan.1
++usr/share/man/man1/ds-logpipe.1
++usr/share/man/man1/ds-replcheck.1
++usr/share/man/man1/ldap-agent.1
++usr/share/man/man1/ldclt.1
++usr/share/man/man1/logconv.1
++usr/share/man/man1/pwdhash.1
++usr/share/man/man5/*.5
++usr/share/man/man8/ns-slapd.8
++usr/share/man/man8/openldap_to_ds.8
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2f83bc6dbbafb51c3038eb11866bad9237f3c9e0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++/dev/null     lib/systemd/system/dirsrv.service
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c4d2be5c1745634125a4dc380d2f3c8e02efd759
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2 @@@
++# these are bogus warnings, no libs shipped in a public libdir
++unused-shlib-entry-in-control-file
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..413fb606e8b7470ee50df8c87cdeec3f6d93ec73
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,35 @@@
++#!/bin/sh
++set -e
++
++. /usr/share/debconf/confmodule
++
++CONFIG_DIR=/etc/dirsrv
++OUT=/dev/null
++INSTANCES=`ls -d /etc/dirsrv/slapd-* 2>/dev/null | grep -v removed | sed 's/.*slapd-//'`
++
++if [ "$1" = configure ]; then
++    # lets give them a user/group in all cases.
++    if ! getent passwd dirsrv > $OUT; then
++      adduser --quiet --system --home /var/lib/dirsrv \
++          --disabled-password --group \
++          --gecos "389 Directory Server user" \
++          --no-create-home \
++          dirsrv > $OUT
++    fi
++
++    chown -R dirsrv:dirsrv /etc/dirsrv/ /var/log/dirsrv/ /var/lib/dirsrv/ > $OUT || true
++    chmod 750 /etc/dirsrv/ /var/log/dirsrv/ /var/lib/dirsrv/ > $OUT || true
++fi
++
++invoke_failure() {
++    # invoke-rc.d failed, likely because no instance has been configured yet
++    # but exit with an error if an instance is configured and the invoke failed
++    if [ -z $INSTANCES ]; then
++        echo "... because no instance has been configured yet."
++    else
++      exit 1
++    fi
++}
++
++
++#DEBHELPER#
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0a70e0edbd2e759300b9b041c5e5cd9c1058d53e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16 @@@
++#!/bin/sh
++set -e
++
++. /usr/share/debconf/confmodule
++
++if [ "$1" = "purge" ]; then
++    if getent group dirsrv > /dev/null; then
++        deluser --system dirsrv || true
++    fi
++    rm -f /etc/systemd/system/dirsrv.target.wants/dirsrv@*.service
++    rm -rf /etc/dirsrv
++    rm -rf /var/lib/dirsrv
++    rm -rf /var/log/dirsrv
++fi
++
++#DEBHELPER#
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bfa9c610bb91a5c1dbd06e7ee7554ba27f09a8d9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,14 @@@
++#!/bin/sh -e
++set -e
++
++#DEBHELPER#
++
++if [ "$1" = "purge" ]; then
++    # remove all installed instances
++    for FILE in `ls -d /etc/dirsrv/slapd-* 2>/dev/null | sed -n '/\.removed$/!$'`
++    do
++        if [ -d "$FILE" ] ; then
++            dsctl $FILE remove --do-it
++        fi
++    done
++fi
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..eba838e25040546fd5226a59fd5cc4df3bb0596a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++To complete the 389 Directory Server installation just run /usr/sbin/setup-ds.
++
++If you experience problems accessing the Directory Server, check with
++"netstat -tapen |grep 389" and verify that the server is not listening only
++to ipv6 (check for ^tcp6). In such case you will need to tweak the cn=config
++DIT with something like the following:
++
++dn: cn=config
++changetype: modify
++add: nsslapd-listenhost
++nsslapd-listenhost: <youripv4>
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bdb883a4a053c60bd8ec13a15614d29af77d2476
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,956 @@@
++389-ds-base (1.4.4.8-1) unstable; urgency=medium
++
++  * New upstream release.
++  * fix-systemctl-path.diff, drop-old-man.diff: Dropped, obsolete.
++  * fix-prlog-include.diff: Fix build by dropping nspr4/ prefix.
++  * install, rules: Clean up perl cruft that got removed upstream.
++  * install: Add openldap_to_ds.
++  * watch: Follow 1.4.4.x.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Thu, 12 Nov 2020 15:57:11 +0200
++
++389-ds-base (1.4.4.4-1) unstable; urgency=medium
++
++  * New upstream release.
++  * watch: Update upstream git repo url.
++  * control: Add python3-dateutil to build-depends.
++  * copyright: Drop duplicate globbing patterns.
++  * lintian: Drop obsolete overrides.
++  * postinst: Drop obsolete rule to upgrade the instances.
++  * prerm: Use dsctl instead of remove-ds.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Tue, 22 Sep 2020 09:23:30 +0300
++
++389-ds-base (1.4.4.3-1) unstable; urgency=medium
++
++  * New upstream release.
++  * fix-db-home-dir.diff: Dropped, upstream.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Tue, 02 Jun 2020 11:33:44 +0300
++
++389-ds-base (1.4.3.6-2) unstable; urgency=medium
++
++  * fix-db-home-dir.diff: Set db_home_dir same as db_dir to fix an issue
++    starting a newly created instance.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Tue, 21 Apr 2020 20:19:06 +0300
++
++389-ds-base (1.4.3.6-1) unstable; urgency=medium
++
++  * New upstream release.
++  * install: Updated.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Mon, 20 Apr 2020 15:01:35 +0300
++
++389-ds-base (1.4.3.4-1) unstable; urgency=medium
++
++  * New upstream release.
++  * Add debian/gitlab-ci.yml.
++    - allow blhc to fail
++  * control: Bump policy to 4.5.0.
++  * control: Use https url for upstream.
++  * control: Use canonical URL in Vcs-Browser.
++  * copyright: Use spaces rather than tabs to start continuation lines.
++  * Add lintian-overrides for the source, cockpit index.js has long lines.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Wed, 18 Mar 2020 08:47:32 +0200
++
++389-ds-base (1.4.3.2-1) unstable; urgency=medium
++
++  * New upstream release.
++  * prerm: Fix slapd install path. (Closes: #945583)
++  * install: Updated.
++  * control: Use debhelper-compat.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Wed, 12 Feb 2020 19:39:22 +0200
++
++389-ds-base (1.4.2.4-1) unstable; urgency=medium
++
++  * New upstream release.
++    - CVE-2019-14824 deref plugin displays restricted attributes
++      (Closes: #944150)
++  * fix-obsolete-target.diff: Dropped, obsolete
++    drop-old-man.diff: Refreshed
++  * control: Add python3-packaging to build-depends and python3-lib389 depends.
++  * dev,libs.install: Nunc-stans got dropped.
++  * source/local-options: Add some files to diff-ignore.
++  * rules: Refresh list of files to purge.
++  * rules: Update dh_auto_clean override.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Wed, 27 Nov 2019 00:00:59 +0200
++
++389-ds-base (1.4.1.6-4) unstable; urgency=medium
++
++  * tests: Redirect stderr to stdout.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Tue, 17 Sep 2019 01:37:39 +0300
++
++389-ds-base (1.4.1.6-3) unstable; urgency=medium
++
++  * control: Add openssl to python3-lib389 depends.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Fri, 13 Sep 2019 07:32:27 +0300
++
++389-ds-base (1.4.1.6-2) unstable; urgency=medium
++
++  * Restore perl build partly, setup-ds is still needed for upgrades
++    until Ubuntu 20.04 is released (for versions << 1.4.0.9).
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Thu, 12 Sep 2019 14:50:36 +0300
++
++389-ds-base (1.4.1.6-1) unstable; urgency=medium
++
++  * New upstream release.
++  * control: Drop direct depends on python from 389-ds-base. (Closes:
++    #936102)
++  * Drop -legacy-tools and other obsolete scripts.
++  * use-bash-instead-of-sh.diff, rename-online-scripts.diff, perl-use-
++    move-instead-of-rename.diff: Dropped, obsolete.
++  * rules: Fix dsconf/dscreate/dsctl/dsidm manpage section.
++  * tests/setup: Migrate to dscreate.
++  * control: Add libnss3-tools to python3-lib389 depends. (Closes: #920025)
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Wed, 11 Sep 2019 17:01:03 +0300
++
++389-ds-base (1.4.1.5-1) unstable; urgency=medium
++
++  * New upstream release.
++  * watch: Use https.
++  * control: Bump policy to 4.4.0.
++  * Bump debhelper to 12.
++  * patches: fix-dsctl-remove.diff, fix-nss-path.diff, icu_pkg-config.patch
++    removed, upstream. Others refreshed.
++  * rules: Pass --enable-perl, we still need the perl tools.
++  * *.install: Updated.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Wed, 10 Jul 2019 10:05:31 +0300
++
++389-ds-base (1.4.0.22-1) unstable; urgency=medium
++
++  * New upstream bugfix release.
++  * control: Drop 389-ds-base from -legacy-tools Depends. (Closes:
++    #924265)
++  * fix-dsctl-remove.diff: Don't hardcode sysconfig. (Closes: #925221)
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Sat, 06 Apr 2019 00:32:06 +0300
++
++389-ds-base (1.4.0.21-1) unstable; urgency=medium
++
++  * New upstream release.
++  * Run offline upgrade only when upgrading from versions below 1.4.0.9,
++    ns-slapd itself handles upgrades in newer versions.
++  * rules: Actually install the minified javascript files. (Closes:
++    #913820)
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Tue, 12 Feb 2019 16:28:15 +0200
++
++389-ds-base (1.4.0.20-3) unstable; urgency=medium
++
++  * control: 389-ds-base should depend on the legacy tools for now.
++    (Closes: #919420)
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Wed, 16 Jan 2019 11:30:51 +0200
++
++389-ds-base (1.4.0.20-2) unstable; urgency=medium
++
++  * Upload to unstable.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Mon, 14 Jan 2019 20:03:58 +0200
++
++389-ds-base (1.4.0.20-1) experimental; urgency=medium
++
++  * New upstream release. (Closes: #913821)
++  * fix-nss-path.diff: Fix includes.
++  * Build ds* manpages, add missing build-depends.
++  * Move deprecated tools in a new subpackage.
++  * control: Add python3-lib389 to 389-ds-base depends.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Sun, 13 Jan 2019 21:13:22 +0200
++
++389-ds-base (1.4.0.19-3) unstable; urgency=medium
++
++  [ Jelmer Vernooij ]
++  * Use secure copyright file specification URI.
++  * Trim trailing whitespace.
++  * Use secure URI in Vcs control header.
++
++  [ Hugh McMaster ]
++  * control: Mark 389-ds-base-libs{,-dev} M-A: same, cockpit-389-ds M-A:
++    foreign and arch:all. (Closes: #916118)
++  * Use pkg-config to detect icu. (Closes: #916115)
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Wed, 02 Jan 2019 12:43:23 +0200
++
++389-ds-base (1.4.0.19-2) unstable; urgency=medium
++
++  * rules: Add -latomic to LDFLAGS on archs failing to build. (Closes:
++    #910982)
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Thu, 06 Dec 2018 01:06:37 +0200
++
++389-ds-base (1.4.0.19-1) unstable; urgency=medium
++
++  * New upstream release.
++  * control: Make C/R backports-compatible. (Closes: #910796)
++  * use-packaged-js.diff: Dropped, packaged versions don't work.
++    (Closes: #913820)
++  * Follow upstream, and drop python3-dirsrvtests.
++  * cockpit-389-ds.install: Updated.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Mon, 03 Dec 2018 15:56:40 +0200
++
++389-ds-base (1.4.0.18-1) unstable; urgency=medium
++
++  * New upstream release.
++    - CVE-2018-14624 (Closes: #907778)
++    - CVE-2018-14638 (Closes: #908859)
++  * control: Build on any arch again.
++  * perl-use-move-instead-of-rename.diff: Use copy instead of move,
++    except when restoring files in case of an error.
++  * Move the new utils (dsconf, dscreate, dsctl, dsidm) to python3-
++    lib389.
++  * control: Add python3-argcomplete to python3-lib389 depends. (Closes:
++    #910761)
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Thu, 11 Oct 2018 00:56:02 +0300
++
++389-ds-base (1.4.0.16-1) unstable; urgency=medium
++
++  * New upstream release.
++  * control: 389-ds-base-dev provides libsvrcore-dev. (Closes: #907140)
++  * perl-use-move-instead-of-rename.diff: Fix upgrade on systems where
++    /var is on a separate partition: (Closes: #905184)
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Thu, 27 Sep 2018 22:39:34 +0300
++
++389-ds-base (1.4.0.15-2) unstable; urgency=medium
++
++  * control: Build cockpit-389-ds only on 64bit and i386.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Thu, 23 Aug 2018 08:54:06 +0300
++
++389-ds-base (1.4.0.15-1) unstable; urgency=medium
++
++  * New upstream release
++    - CVE-2018-10935 (Closes: #906985)
++  * control: Add libcrack2-dev to build-depends.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Thu, 23 Aug 2018 00:46:45 +0300
++
++389-ds-base (1.4.0.13-1) experimental; urgency=medium
++
++  * New upstream release.
++    - CVE-2018-10850 (Closes: #903501)
++  * control: Update maintainer address.
++  * control: Upstream dropped support for non-64bit architectures, so
++    build only on supported 64bit archs (amd64, arm64, mips64el,
++    ppc64el, s390x).
++  * control: svrcore got merged here, drop it from build-depends.
++  * ftbs_lsoftotkn3.diff: Dropped, obsolete.
++  * control: Add rsync to build-depends.
++  * libs, dev, control: Add libsvrcore files, replace old package.
++  * base: Add new scripts, add python3-selinux, -semanage, -sepolicy to
++    depends.
++  * Add a package for cockpit-389-ds.
++  * rules: Clean up cruft left after build.
++  * control: Drop dh_systemd from build-depends, bump debhelper to 11.
++  * Add varions libjs packages to cockpit-389-ds Depends, add the rest
++    to d/missing-sources.
++  * copyright: Updated. (Closes: #904760)
++  * control: Modify 389-ds to depend on cockpit-389-ds and drop the old
++    GUI packages which are deprecated upstream.
++  * dont-build-new-manpages.diff: Debian doesn't have argparse-manpage,
++    so in order to not FTBFS don't build new manpages.
++  * base.install: Add man5/*.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Tue, 31 Jul 2018 23:46:17 +0300
++
++389-ds-base (1.3.8.2-1) unstable; urgency=medium
++
++  * New upstream release.
++  * fix-saslpath.diff: Updated to support ppc64el and s390x. (LP:
++    #1764744)
++  * CVE-2017-15135.patch: Refreshed
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Fri, 01 Jun 2018 11:21:19 +0300
++
++389-ds-base (1.3.7.10-1) unstable; urgency=medium
++
++  * New upstream release.
++    - fix CVE-2018-1054 (Closes: #892124)
++  * control: Update maintainer address, freeipa-team handles this from
++    now on. Drop kklimonda from uploaders.
++  * control: Update VCS urls.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Tue, 13 Mar 2018 11:32:29 +0200
++
++389-ds-base (1.3.7.9-1) unstable; urgency=medium
++
++  * New upstream release.
++    - CVE-2017-15134 (Closes: #888452)
++  * patches: Fix CVE-2017-15135. (Closes: #888451)
++  * tests: Add some debug output.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Mon, 05 Feb 2018 16:25:09 +0200
++
++389-ds-base (1.3.7.8-4) unstable; urgency=medium
++
++  * tests: Drop python3-lib389 from depends, it's not used currently
++    anyway.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Thu, 21 Dec 2017 15:42:04 +0200
++
++389-ds-base (1.3.7.8-3) unstable; urgency=medium
++
++  * tests/control: Depend on python3-lib389.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Wed, 20 Dec 2017 23:54:43 +0200
++
++389-ds-base (1.3.7.8-2) unstable; urgency=medium
++
++  * Fix autopkgtest to be robust in the face of changed iproute2 output.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Wed, 20 Dec 2017 15:57:26 +0200
++
++389-ds-base (1.3.7.8-1) unstable; urgency=medium
++
++  * New upstream release.
++  * Package python3-lib389 and python3-dirsrvtests.
++  * control: Add python3 depends to 389-ds-base, since it ships a few
++    python scripts.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Tue, 12 Dec 2017 17:32:27 +0200
++
++389-ds-base (1.3.7.5-1) unstable; urgency=medium
++
++  * New upstream release.
++  * patches: ftbfs-fix.diff, reproducible-build.diff dropped (upstream)
++    others refreshed.
++  * *.install: Updated.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Wed, 04 Oct 2017 10:33:45 +0300
++
++389-ds-base (1.3.6.7-5) unstable; urgency=medium
++
++  * Move all libs from base to -libs, add B/R. (Closes: #874764)
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Thu, 21 Sep 2017 16:44:13 +0300
++
++389-ds-base (1.3.6.7-4) unstable; urgency=medium
++
++  * control, install: Fix library/dev-link installs, add Breaks/Replaces
++    to fit, and drop obsolete B/R.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Wed, 30 Aug 2017 00:19:41 +0300
++
++389-ds-base (1.3.6.7-3) unstable; urgency=medium
++
++  * ftbfs-fix.diff: Fix build. (Closes: #873120)
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Mon, 28 Aug 2017 15:09:02 +0300
++
++389-ds-base (1.3.6.7-2) unstable; urgency=medium
++
++  * control: Bump policy to 4.1.0, no changes.
++  * rules: Override dh_missing.
++  * control: Add libltdl-dev to build-depends. (Closes: #872979)
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Thu, 24 Aug 2017 12:15:03 +0300
++
++389-ds-base (1.3.6.7-1) unstable; urgency=medium
++
++  * New upstream release
++    - fix CVE-2017-7551 (Closes: #870752)
++  * fix-tests.diff: Dropped, fixed upstream.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Tue, 22 Aug 2017 16:30:11 +0300
++
++389-ds-base (1.3.6.5-1) experimental; urgency=medium
++
++  * New upstream release.
++    - fix-bsd.patch, support-kfreebsd.patch, fix-48986-cve-2017-2591.diff:
++      Dropped, upstream.
++  * *.install: Updated.
++  * control: Add doxygen, libcmocka-dev, libevent-dev to build-deps.
++  * rules: Enable cmocka tests.
++  * fix-tests.diff: Fix building the tests.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Wed, 10 May 2017 09:38:30 +0300
++
++389-ds-base (1.3.5.17-2) unstable; urgency=medium
++
++  * fix-upstream-49245.diff: Pull commits from upstream 1.3.5.x, which
++    remove rest of the asm code. (Closes: #862194)
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Wed, 10 May 2017 09:25:03 +0300
++
++389-ds-base (1.3.5.17-1) unstable; urgency=medium
++
++  * New upstream bugfix release.
++    - CVE-2017-2668 (Closes: #860125)
++  * watch: Updated.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Tue, 09 May 2017 11:06:14 +0300
++
++389-ds-base (1.3.5.15-2) unstable; urgency=medium
++
++  * fix-48986-cve-2017-2591.diff: Fix upstream ticket 48986,
++    CVE-2017-2591. (Closes: #851769)
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Fri, 27 Jan 2017 00:01:53 +0200
++
++389-ds-base (1.3.5.15-1) unstable; urgency=medium
++
++  * New upstream release.
++    - CVE-2016-5405 (Closes: #842121)
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Wed, 16 Nov 2016 11:01:00 +0200
++
++389-ds-base (1.3.5.14-1) unstable; urgency=medium
++
++  * New upstream release.
++  * postrm: Remove /etc/dirsrv, /var/lib/dirsrv and /var/log/dirsrv on
++    purge.
++  * control: Bump build-dep on libsvrcore-dev to ensure it has support
++    for systemd password agent.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Fri, 28 Oct 2016 01:42:27 +0300
++
++389-ds-base (1.3.5.13-1) unstable; urgency=medium
++
++  * New upstream release.
++  * control: Bump policy to 3.9.8, no changes.
++  * patches/default_user: Dropped, upstream.
++  * support-non-nss-libldap.diff: Dropped, upstream.
++  * fix-obsolete-target.diff: Updated.
++  * patches: Refreshed.
++  * control: Add libsystemd-dev to build-deps.
++  * control: Add acl to -base depends.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Wed, 12 Oct 2016 11:11:20 +0300
++
++389-ds-base (1.3.4.14-2) unstable; urgency=medium
++
++  * tests: Add simple autopkgtests.
++  * postinst: Start instances after offline update.
++  * control, rules: Drop -dbg packages.
++  * control: Drop conflicts on slapd. (Closes: #822532)
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Mon, 03 Oct 2016 17:53:26 +0300
++
++389-ds-base (1.3.4.14-1) unstable; urgency=medium
++
++  * New upstream release.
++  * support-non-nss-libldap.diff: Refreshed.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Mon, 29 Aug 2016 10:17:41 +0300
++
++389-ds-base (1.3.4.9-1) unstable; urgency=medium
++
++  * New upstream release.
++  * support-non-nss-libldap.diff: Support libldap built against gnutls.
++    (LP: #1564179)
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Mon, 18 Apr 2016 18:08:14 +0300
++
++389-ds-base (1.3.4.8-4) unstable; urgency=medium
++
++  * use-perl-move.diff: Dropped, 'rename' is more reliable.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Wed, 30 Mar 2016 08:38:24 +0300
++
++389-ds-base (1.3.4.8-3) unstable; urgency=medium
++
++  * use-perl-move.diff: Fix 60upgradeschemafiles.pl to use File::Copy.
++    (Closes: #818578)
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Fri, 18 Mar 2016 11:15:23 +0200
++
++389-ds-base (1.3.4.8-2) unstable; urgency=medium
++
++  * postinst: Silence ls and adduser.
++  * Drop the init file, we depend on systemd anyway.
++  * rules: Don't enable dirsrv-snmp.service by default.
++  * postrm: Clean up /var/lib/dirsrv/scripts-* on purge.
++  * user-perl-move.diff: Use move instead of rename during upgrade.
++    (Closes: #775550)
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Thu, 17 Mar 2016 08:13:38 +0200
++
++389-ds-base (1.3.4.8-1) unstable; urgency=medium
++
++  * New upstream release.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Mon, 22 Feb 2016 07:58:40 +0200
++
++389-ds-base (1.3.4.5-2) unstable; urgency=medium
++
++  * fix-systemctl-path.diff: Use correct path to /bin/systemctl.
++    (Closes: #779653)
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Wed, 09 Dec 2015 08:31:20 +0200
++
++389-ds-base (1.3.4.5-1) unstable; urgency=medium
++
++  * New upstream release.
++  * patches: Refreshed.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Wed, 09 Dec 2015 08:14:56 +0200
++
++389-ds-base (1.3.3.13-1) unstable; urgency=medium
++
++  * New upstream release.
++  * control: Add systemd to 389-ds-base Depends. (Closes: #794301)
++  * postrm: Clean target.wants in postrm.
++  * reproducible-build.diff: Make builds reproducible. Thanks, Chris
++    Lamb! (Closes: #799010)
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Tue, 20 Oct 2015 14:25:05 +0300
++
++389-ds-base (1.3.3.12-1) unstable; urgency=medium
++
++  * New upstream release
++    - fix CVE-2015-3230 (Closes: #789202)
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Wed, 24 Jun 2015 11:47:50 +0300
++
++389-ds-base (1.3.3.10-1) unstable; urgency=medium
++
++  * New upstream release
++    - fix CVE-2015-1854 (Closes: #783923)
++  * postinst: Stop actual instances instead of 'dirsrv' on upgrade, and
++    use service(8) instead of invoke-rc.d.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Thu, 07 May 2015 07:58:35 +0300
++
++389-ds-base (1.3.3.9-1) experimental; urgency=medium
++
++  * New upstream bugfix release.
++    - Drop cve-2014-8*.diff, upstream.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Thu, 02 Apr 2015 14:47:20 +0300
++
++389-ds-base (1.3.3.5-4) unstable; urgency=medium
++
++  * Security fixes (Closes: #779909)
++    - cve-2014-8105.diff: Fix for CVE-2014-8105
++    - cve-2014-8112.diff: Fix for CVE-2014-8112
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Mon, 09 Mar 2015 10:53:03 +0200
++
++389-ds-base (1.3.3.5-3) unstable; urgency=medium
++
++  * use-bash-instead-of-sh.diff: Drop admin_scripts.diff and patch the
++    scripts to use bash instead of trying to fix bashisms. (Closes:
++    #772195)
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Fri, 16 Jan 2015 15:40:23 +0200
++
++389-ds-base (1.3.3.5-2) unstable; urgency=medium
++
++  * fix-saslpath.diff: Fix SASL library path.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Sat, 25 Oct 2014 01:48:34 +0300
++
++389-ds-base (1.3.3.5-1) unstable; urgency=medium
++
++  * New upstream bugfix release.
++  * control: Bump policy, no changes.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Mon, 20 Oct 2014 09:57:14 +0300
++
++389-ds-base (1.3.3.3-1) unstable; urgency=medium
++
++  * New upstream release.
++  * Dropped upstreamed patches, refresh others.
++  * control, rules, 389-ds-base.install: Add support for systemd.
++  * fix-obsolete-target.diff: Drop syslog.target from the service files.
++  * 389-ds-base.links: Mask the initscript so that it's not used with systemd.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Mon, 06 Oct 2014 17:13:01 +0300
++
++389-ds-base (1.3.2.23-2) unstable; urgency=medium
++
++  * Team upload.
++  * Add fix-bsd.patch and support-kfreebsd.patch to fix the build failure
++    on kFreeBSD.
++
++ -- Benjamin Drung <benjamin.drung@profitbricks.com>  Wed, 03 Sep 2014 15:32:22 +0200
++
++389-ds-base (1.3.2.23-1) unstable; urgency=medium
++
++  * New bugfix release.
++  * watch: Update the url.
++  * control: Update Vcs-Browser url to use cgit.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Mon, 01 Sep 2014 13:32:59 +0300
++
++389-ds-base (1.3.2.21-1) unstable; urgency=medium
++
++  * New upstream release.
++    - CVE-2014-3562 (Closes: #757437)
++
++ -- Timo Aaltonen <tjaalton@ubuntu.com>  Fri, 08 Aug 2014 10:48:55 +0300
++
++389-ds-base (1.3.2.19-1) unstable; urgency=medium
++
++  * New upstream release.
++  * admin_scripts.diff: Updated to fix more bashisms.
++  * watch: Update the url.
++  * Install failedbinds.py and logregex.py scripts.
++  * init: Use status from init-functions.
++  * control: Update my email.
++
++ -- Timo Aaltonen <tjaalton@debian.org>  Tue, 08 Jul 2014 15:50:11 +0300
++
++389-ds-base (1.3.2.9-1.1) unstable; urgency=medium
++
++  * Non-maintainer upload.
++  * Apply fix for CVE-2014-0132, see like named patch (Closes: 741600)
++  * Fix m4-macro for libsrvcore and add missing B-D on libpci-dev
++    (Closes: #745821)
++
++ -- Tobias Frost <tobi@coldtobi.de>  Fri, 25 Apr 2014 15:11:16 +0200
++
++389-ds-base (1.3.2.9-1) unstable; urgency=low
++
++  * New upstream release.
++    - fixes CVE-2013-0336 (Closes: #704077)
++    - fixes CVE-2013-1897 (Closes: #704421)
++    - fixes CVE-2013-2219 (Closes: #718325)
++    - fixes CVE-2013-4283 (Closes: #721222)
++    - fixes CVE-2013-4485 (Closes: #730115)
++  * Drop fix-CVE-2013-0312.diff, upstream.
++  * rules: Add new scripts to rename.
++  * fix-sasl-path.diff: Use a triplet path to find libsasl2. (LP:
++    #1088822)
++  * admin_scripts.diff: Add patch from upstream #47511 to fix bashisms.
++  * control: Add ldap-utils to -base depends.
++  * rules, rename-online-scripts.diff: Some scripts with .pl suffix are
++    meant for an online server, so instead of overwriting the offline
++    scripts use -online suffix.
++  * rules: Enable parallel build, but limit the jobs to 1 for
++    dh_auto_install.
++  * control: Bump policy to 3.9.5, no changes.
++  * rules: Add get-orig-source target.
++  * lintian-overrides: Drop obsolete entries, add comments for the rest.
++
++ -- Timo Aaltonen <tjaalton@ubuntu.com>  Mon, 03 Feb 2014 11:08:50 +0200
++
++389-ds-base (1.3.0.3-1) unstable; urgency=low
++
++  * New upstream release.
++  * control: Bump the policy to 3.9.4, no changes.
++  * fix-CVE-2013-0312.diff: Patch to fix handling LDAPv3 control data.
++
++ -- Timo Aaltonen <tjaalton@ubuntu.com>  Mon, 11 Mar 2013 14:23:20 +0200
++
++389-ds-base (1.2.11.17-1) UNRELEASED; urgency=low
++
++  * New upstream release.
++  * watch: Add a comment about the upstream git tree.
++  * fix-cve-2012-4450.diff: Remove, upstream.
++
++ -- Timo Aaltonen <tjaalton@ubuntu.com>  Sat, 01 Dec 2012 14:22:13 +0200
++
++389-ds-base (1.2.11.15-1) unstable; urgency=low
++
++  * New upstream release.
++  * Add fix-cve-2012-4450.diff. (Closes: #688942)
++  * dirsrv.init: Fix stop() to remove the pidfile only when the process
++    is finished. (Closes: #689389)
++  * copyright: Update the source url.
++  * control: Drop quilt from build-depends, since using 3.0 (quilt)
++  * lintian-overrides: Add an override for hardening-no-fortify-
++    functions, since it's a false positive in this case.
++  * control: Drop dpkg-dev from build-depends, no need to specify it
++    directly.
++  * copyright: Add myself as a copyright holder for debian/*.
++  * 389-ds-base.prerm: Add 'set -e'.
++  * rules: drop DEB_HOST_MULTIARCH, dh9 handles it.
++
++ -- Timo Aaltonen <tjaalton@ubuntu.com>  Wed, 03 Oct 2012 19:33:52 +0300
++
++389-ds-base (1.2.11.7-5) unstable; urgency=low
++
++  * control: Drop debconf-utils and po-debconf from build-depends.
++  * control: Add libnetaddr-ip-perl and libsocket-getaddrinfo-perl to
++    389-ds-base Depends for ipv6 support. (Closes: #682847)
++
++ -- Timo Aaltonen <tjaalton@ubuntu.com>  Mon, 30 Jul 2012 13:12:23 +0200
++
++389-ds-base (1.2.11.7-4) unstable; urgency=low
++
++  * debian/po: Remove, leftover from the template purge. (Closes: #681543)
++
++ -- Timo Aaltonen <tjaalton@ubuntu.com>  Thu, 19 Jul 2012 23:12:01 +0300
++
++389-ds-base (1.2.11.7-3) unstable; urgency=low
++
++  * 389-ds-base.config: Removed, the debconf template is no more.
++    (Closes: #680351)
++  * control: Remove duplicate 'the' from the 389-ds description.
++
++ -- Timo Aaltonen <tjaalton@ubuntu.com>  Wed, 11 Jul 2012 11:59:36 +0300
++
++389-ds-base (1.2.11.7-2) unstable; urgency=low
++
++  * control: Stop hardcoding libs to binary depends. (Closes: #679790)
++  * control: Add libnspr4-dev and libldap2-dev to 389-ds-base-dev
++    Depends. (Closes: #679742)
++  * l10n review (Closes: #679870) :
++    - Drop the debconf template, and rewrap README.Debian.
++    - control: Update the descriptions
++
++ -- Timo Aaltonen <tjaalton@ubuntu.com>  Tue, 03 Jul 2012 17:58:20 +0300
++
++389-ds-base (1.2.11.7-1) unstable; urgency=low
++
++  [ Timo Aaltonen ]
++  * New upstream release.
++  * watch: Fix the url.
++  * patches/remove_license_prompt: Dropped, included upstream.
++  * patches/default_user: Refreshed.
++  * control: Change the VCS header to point to the git repository.
++  * control: Rename last remnants of Fedora to 389.
++  * changelog, control: Be consistent with the naming; renamed the source
++    to just '389-ds-base', which matches upstream tarball naming.
++  * control: Wrap Depends.
++  * compat, control: Bump compat to 9, and debhelper build-dep to (>= 9).
++  * rules: Switch to dh.
++  * Move dirsrv.lintian to dirsrv.lintian-overrides, adjust dirsrv.install.
++  * *.dirs: Clean up.
++  * control: Build-depend on dh-autoreconf, drop duplicate bdeps.
++  * Fold dirsrv-tools into the main package.
++  * Build against libldap2-dev (>= 2.4.28).
++  * Rename binary package to 389-ds-base.
++  * -dev.install: Install the pkgconfig file.
++  * rules: Enable PIE hardening.
++  * Add a default file, currently sets LD_BIND_NOW=1.
++  * control: 'dbgen' uses old perl libs, add libperl4-corelibs-perl
++    dependency to 389-ds-base.
++  * rules: Add --fail-missing for dh_install, remove files not needed
++    and make sure to install the rest.
++  * rules, control: Fix the installation name of ds-logpipe.py, add
++    python dependency to 389-ds-base..
++  * libns-dshttpd is internal to the server, ship it in 389-ds-base.
++  * Rename libdirsrv{-dev,0} -> 389-ds-base-{dev,libs}, includes only
++    libslapd and headers for external plugin development.
++  * control: Breaks/Replaces old libdirsrv-dev/libdirsrv0/dirsrv.
++  * Drop hyphen_used_as_minus, applied upstream.
++  * copyright: Use DEP5 format.
++  * Cherry-pick upstream commit ee320163c6 to get rid of unnecessary
++    and non-free MIB's from the tree, and build a dfsg compliant tarball.
++  * lintian-overrides: Update, create one for -libs.
++  * Fix the initscript to create the lockdir, and refactor code into separate
++    functions.
++  * Drop obsolete entries from copyright, and make it lintian clean.
++  * debian/po: Refer to the correct file after rename.
++  * control: Bump Standards-Version to 3.9.3, no changes.
++  * postinst: Drop unused 'lastversion'.
++  * patches: Add DEP3 compliant headers.
++  * rules, postinst: Add an error handler function for dh_installinit, so
++    that clean installs don't fail due to missing configuration.
++  * postinst: Run the update tool.
++  * dirsrv.init:
++    - Make the start and stop functions much simpler and LSB compliant
++    - Fix starting multiple instances
++    - Use '-b' for start-stop-daemon, since ns-slapd doesn't detach properly
++  * control: Add 389-ds metapackage.
++  * control: Change libdb4.8-dev build-depends to libdb-dev, since this version
++    supports db5.x.
++  * 389-ds-base.prerm: Add prerm script for removing installed instances on
++    purge.
++
++  [ Krzysztof Klimonda ]
++  * dirsrv.init:
++    - return 0 code if there are no instances configured and tweak message
++      so it doesn't indicate a failure.
++
++ -- Krzysztof Klimonda <kklimonda@syntaxhighlighted.com>  Tue, 27 Mar 2012 14:26:16 +0200
++
++389-directory-server (1.2.6.1-5) unstable; urgency=low
++
++  * Removed db_stop from dirsrv.postinst
++  * Fix short description in libdirsrv0-dbg
++
++ -- Michele Baldessari <michele@acksyn.org>  Wed, 20 Oct 2010 20:24:20 +0200
++
++389-directory-server (1.2.6.1-4) unstable; urgency=low
++
++  * Make libicu dep dependent on dpkg-vendor
++
++ -- Michele Baldessari <michele@acksyn.org>  Mon, 18 Oct 2010 21:21:52 +0200
++
++389-directory-server (1.2.6.1-3) unstable; urgency=low
++
++  * Remove dirsrv user and group in postrm
++  * Clean up postrm and postinst
++
++ -- Michele Baldessari <michele@acksyn.org>  Sun, 17 Oct 2010 21:54:08 +0200
++
++389-directory-server (1.2.6.1-2) unstable; urgency=low
++
++  * Fix QUILT_STAMPFN
++
++ -- Michele Baldessari <michele@acksyn.org>  Sun, 17 Oct 2010 15:03:34 +0200
++
++389-directory-server (1.2.6.1-1) unstable; urgency=low
++
++  * New upstream
++
++ -- Michele Baldessari <michele@acksyn.org>  Sat, 16 Oct 2010 23:08:09 +0200
++
++389-directory-server (1.2.6-2) unstable; urgency=low
++
++  * Update my email address
++
++ -- Michele Baldessari <michele@acksyn.org>  Sat, 16 Oct 2010 22:34:19 +0200
++
++389-directory-server (1.2.6-1) unstable; urgency=low
++
++  * New upstream
++  * s/Fedora/389/g to clean up the branding
++  * Remove automatic configuration (breaks too often with every update)
++  * Remove dirsrv.config translation, no questions are asked anymore
++  * Fix old changelog versions with proper ~ on rc versions
++  * Update policy to 3.9.1
++  * Improve README.Debian
++  * Depend on libicu44
++  * Remove /var/run/dirsrv from the postinst scripts (managed by init script)
++
++ -- Michele Baldessari <michele@pupazzo.org>  Sat, 04 Sep 2010 11:58:21 +0200
++
++389-directory-server (1.2.6~rc7-1) unstable; urgency=low
++
++  * New upstream
++
++ -- Michele Baldessari <michele@pupazzo.org>  Fri, 03 Sep 2010 20:06:08 +0200
++
++389-directory-server (1.2.6~a3-1) unstable; urgency=low
++
++  * New upstream
++  * Rename man page remove-ds.pl in remove-ds
++  * Removed Debian.source
++
++ -- Michele Baldessari <michele@pupazzo.org>  Sun, 23 May 2010 22:12:13 +0200
++
++389-directory-server (1.2.6~a2-1) unstable; urgency=low
++
++  * New upstream
++  * Removed speling_fixes patch, applied upstream
++
++ -- Michele Baldessari <michele@pupazzo.org>  Sun, 23 May 2010 13:36:25 +0200
++
++389-directory-server (1.2.5-1) unstable; urgency=low
++
++  * New upstream
++  * Add libpcre3-dev Build-dep
++  * ldap-agent moved ti /usr/sbin
++  * Fix spelling errors in code and manpages
++  * Fix some lintian warnings
++  * Bump policy to 3.8.3
++  * Ignore lintian warning pkg-has-shlibs-control-file-but-no-actual-shared-libs
++    as the shlibs file is for dirsrv plugins
++  * Upgraded deps to libicu42 and libdb4.8
++  * Do create /var/lib/dirsrv as dirsrv user's home
++  * Added libsasl2-modules-gssapi-mit as a dependency for dirsrv (needed by
++    mandatory LDAP SASL mechs)
++  * Install all files of etc/dirsrv/config
++  * Add some missing start scripts in usr/sbin
++  * Fixed a bug in the dirsrv.init script
++  * Switch to dpkg-source 3.0 (quilt) format
++  * Bump policy to 3.8.4
++
++ -- Michele Baldessari <michele@pupazzo.org>  Sun, 23 May 2010 12:31:24 +0200
++
++389-directory-server (1.2.1-0) unstable; urgency=low
++
++  * Rename of source package (note, since this is still staging work no
++    replace or upgrade is in place)
++  * Update watch file
++  * New Upstream
++
++ -- Michele Baldessari <michele@pupazzo.org>  Fri, 12 Jun 2009 22:08:42 +0200
++
++fedora-directory-server (1.2.0-1) unstable; urgency=low
++
++  * New upstream release
++  * Add missing libkrb5-dev dependency
++  * Fix section of -dbg packages
++  * Fix all "dpatch-missing-description" lintian warnings
++
++ -- Michele Baldessari <michele@pupazzo.org>  Wed, 22 Apr 2009 23:36:22 +0200
++
++fedora-directory-server (1.1.3-1) unstable; urgency=low
++
++  * New upstream
++  * Added watch file
++  * Make setup-ds use dirsrv:dirsrv user/group as defaults
++  * Added VCS-* fields
++  * --enable-autobind
++  * Add ldap/servers/plugins/replication/winsync-plugin.h to libdirsrv-dev
++
++ -- Michele Baldessari <michele@pupazzo.org>  Mon, 24 Nov 2008 22:42:26 +0100
++
++fedora-directory-server (1.1.2-2) unstable; urgency=low
++
++  * Fixed build+configure twice issue
++  * Added Conflicts: slapd (thanks Alessandro)
++
++ -- Michele Baldessari <michele@pupazzo.org>  Tue, 23 Sep 2008 21:12:44 +0200
++
++fedora-directory-server (1.1.2-1) unstable; urgency=low
++
++  * New upstream
++  * Removed /usr/sbin PATH from postinst script
++
++ -- Michele Baldessari <michele@pupazzo.org>  Sat, 20 Sep 2008 20:10:52 +0000
++
++fedora-directory-server (1.1.1-0) unstable; urgency=low
++
++  * New upstream
++  * Don't apply patch for 439829, fixed upstream
++  * Bump to policy 3.8.0
++  * Added README.source
++
++ -- Michele Baldessari <michele@pupazzo.org>  Fri, 22 Aug 2008 00:09:40 +0200
++
++fedora-directory-server (1.1.0-4) unstable; urgency=low
++
++  * dirsrv should depend on libmozilla-ldap-perl (thanks Mathias Kaufmann
++    <steiger@mmforces.de>)
++
++ -- Michele Baldessari <michele@pupazzo.org>  Sun, 20 Jul 2008 18:41:58 +0200
++
++fedora-directory-server (1.1.0-3) unstable; urgency=low
++
++  * Fix up some descriptions
++
++ -- Michele Baldessari <michele@pupazzo.org>  Sun, 25 May 2008 21:36:32 +0200
++
++fedora-directory-server (1.1.0-2) unstable; urgency=low
++
++  * Silenced init warning messages when chowning pid directory
++
++ -- Michele Baldessari <michele@pupazzo.org>  Wed, 21 May 2008 23:08:32 +0200
++
++fedora-directory-server (1.1.0-1) unstable; urgency=low
++
++  * Removed template lintian warning
++  * Cleaned up manpages
++
++ -- Michele Baldessari <michele@pupazzo.org>  Sun, 18 May 2008 13:39:58 +0200
++
++fedora-directory-server (1.1.0-0) unstable; urgency=low
++
++  * Initial release (Closes: #497098).
++  * Fixed postinst after renaming setup-ds.pl to setup-ds
++  * Applied patch from https://bugzilla.redhat.com/show_bug.cgi?id=439829 to
++    fix segfault against late NSS versions
++  * Switched to parseable copyright format
++  * Source package is lintian clean now
++  * Added initial manpage patch
++  * Switched to dh_install
++
++ -- Michele Baldessari <michele@pupazzo.org>  Thu, 27 Mar 2008 23:56:17 +0200
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d3f77dc8ea1552ea44e8cba0c20e15009e7148df
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2 @@@
++usr/share/cockpit/389-console/
++usr/share/metainfo/389-console/org.port389.cockpit_console.metainfo.xml
diff --cc debian/control
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7c7f980f48b4a14222ce6aad8b603d777bb350fc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,181 @@@
++Source: 389-ds-base
++Section: net
++Priority: optional
++Maintainer: Debian FreeIPA Team <pkg-freeipa-devel@alioth-lists.debian.net>
++Uploaders:
++ Timo Aaltonen <tjaalton@debian.org>,
++Build-Depends:
++ libcmocka-dev,
++ debhelper-compat (= 12),
++ dh-python,
++ doxygen,
++ libbz2-dev,
++ libcrack2-dev,
++ libdb-dev,
++ libevent-dev,
++ libicu-dev,
++ libkrb5-dev,
++ libldap2-dev (>= 2.4.28),
++ libltdl-dev,
++ libnspr4-dev,
++ libnss3-dev,
++ libpam0g-dev,
++ libpci-dev,
++ libpcre3-dev,
++ libperl-dev,
++ libsasl2-dev,
++ libsnmp-dev,
++ libssl-dev,
++ libsystemd-dev,
++ pkg-config,
++ python3-all-dev,
++ python3-argcomplete,
++ python3-argparse-manpage,
++ python3-dateutil,
++ python3-ldap,
++ python3-packaging,
++ python3-selinux,
++ python3-sepolicy,
++ python3-setuptools,
++ python3-six,
++ rsync,
++ zlib1g-dev,
++Standards-Version: 4.5.0
++Vcs-Git: https://salsa.debian.org/freeipa-team/389-ds-base.git
++Vcs-Browser: https://salsa.debian.org/freeipa-team/389-ds-base
++Homepage: https://directory.fedoraproject.org
++
++Package: 389-ds
++Architecture: all
++Depends:
++ 389-ds-base,
++ cockpit-389-ds,
++ ${misc:Depends},
++Description: 389 Directory Server suite - metapackage
++ Based on the Lightweight Directory Access Protocol (LDAP), the 389
++ Directory Server is designed to manage large directories of users and
++ resources robustly and scalably.
++ .
++ This is a metapackage depending on the LDAPv3 server and a Cockpit UI plugin
++ for administration.
++
++Package: 389-ds-base-libs
++Section: libs
++Architecture: any
++Multi-Arch: same
++Pre-Depends: ${misc:Pre-Depends}
++Depends: ${misc:Depends}, ${shlibs:Depends}
++Breaks: 389-ds-base (<< 1.3.6.7-5),
++ 389-ds-base-dev (<< 1.3.6.7-4),
++ libsvrcore0,
++Replaces: 389-ds-base (<< 1.3.6.7-5),
++ 389-ds-base-dev (<< 1.3.6.7-4),
++ libsvrcore0,
++Description: 389 Directory Server suite - libraries
++ Based on the Lightweight Directory Access Protocol (LDAP), the 389
++ Directory Server is designed to manage large directories of users and
++ resources robustly and scalably.
++ .
++ This package contains core libraries for the 389 Directory Server.
++
++Package: 389-ds-base-dev
++Section: libdevel
++Architecture: any
++Multi-Arch: same
++Depends:
++ 389-ds-base-libs (= ${binary:Version}),
++ libldap2-dev,
++ libnspr4-dev,
++ ${misc:Depends},
++ ${shlibs:Depends},
++Breaks: 389-ds-base (<< 1.3.6.7-4),
++ libsvrcore-dev,
++Replaces: 389-ds-base (<< 1.3.6.7-4),
++ libsvrcore-dev,
++Provides:
++ libsvrcore-dev,
++Description: 389 Directory Server suite - development files
++ Based on the Lightweight Directory Access Protocol (LDAP), the 389
++ Directory Server is designed to manage large directories of users and
++ resources robustly and scalably.
++ .
++ This package contains development headers for the core libraries
++ of the 389 Directory Server, useful for developing plugins without
++ having to install the server itself.
++
++Package: 389-ds-base
++Architecture: any
++Pre-Depends: debconf (>= 0.5) | debconf-2.0
++Depends:
++ 389-ds-base-libs (= ${binary:Version}),
++ adduser,
++ acl,
++ ldap-utils,
++ libmozilla-ldap-perl,
++ libnetaddr-ip-perl,
++ libsocket-getaddrinfo-perl,
++ libsasl2-modules-gssapi-mit,
++ perl,
++ python3-lib389,
++ python3-selinux,
++ python3-semanage,
++ python3-sepolicy,
++ systemd,
++ ${misc:Depends},
++ ${shlibs:Depends},
++ ${python3:Depends},
++Replaces: 389-ds-base-legacy-tools
++Description: 389 Directory Server suite - server
++ Based on the Lightweight Directory Access Protocol (LDAP), the 389
++ Directory Server is designed to manage large directories of users and
++ resources robustly and scalably.
++ .
++ Its key features include:
++  * four-way multi-master replication;
++  * great scalability;
++  * extensive documentation;
++  * Active Directory user and group synchronization;
++  * secure authentication and transport;
++  * support for LDAPv3;
++  * graphical management console;
++  * on-line, zero downtime update of schema, configuration, and
++    in-tree Access Control Information.
++
++Package: python3-lib389
++Architecture: all
++Depends: ${misc:Depends}, ${python3:Depends},
++ libnss3-tools,
++ openssl,
++ python3-argcomplete,
++ python3-dateutil,
++ python3-ldap,
++ python3-packaging,
++ python3-pyasn1,
++ python3-pyasn1-modules,
++ python3-pytest,
++ python3-six,
++Conflicts: python-lib389 (<< 1.3.7.8),
++ 389-ds-base (<< 1.4.0.18-1~),
++Replaces: python-lib389 (<< 1.3.7.8),
++ 389-ds-base (<< 1.4.0.18-1~),
++Description: Python3 module for accessing and configuring the 389 Directory Server
++ This Python3 module contains tools and libraries for accessing, testing,
++ and configuring the 389 Directory Server.
++
++Package: cockpit-389-ds
++Architecture: all
++Multi-Arch: foreign
++Depends: ${misc:Depends},
++ cockpit,
++ libjs-bootstrap,
++ libjs-c3,
++ libjs-d3,
++ libjs-jquery-datatables,
++ libjs-jquery-datatables-extensions,
++ libjs-jquery-jstree,
++ libjs-moment,
++ python3,
++ python3-lib389,
++Description: Cockpit user interface for 389 Directory Server
++ This package includes a Cockpit UI plugin for configuring and administering
++ the 389 Directory Server.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2ad9ec432c9ab4045b622aa037a5a744e9a21da8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,652 @@@
++Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
++Upstream-name: 389-ds-base
++Source: http://directory.fedoraproject.org/wiki/Source
++
++Files: *
++Copyright: 2001 Sun Microsystems, Inc.
++           2005 Red Hat, Inc.
++License: GPL-3+ and Other
++
++Files: ldap/libraries/libavl/*.[ch] ldap/servers/slapd/abandon.c
++ ldap/servers/slapd/add.c ldap/servers/slapd/bind.c
++ ldap/servers/slapd/bulk_import.c ldap/servers/slapd/compare.c
++ ldap/servers/slapd/delete.c ldap/servers/slapd/detach.c
++ ldap/servers/slapd/globals.c ldap/servers/slapd/modify.c
++ ldap/servers/slapd/modrdn.c ldap/servers/slapd/monitor.c
++ ldap/servers/slapd/search.c ldap/servers/slapd/unbind.c
++Copyright: 1993 Regents of the University of Michigan
++           2001 Sun Microsystems, Inc.
++           2005 Red Hat, Inc.
++License: GPL-3+ and Other
++
++Files: ldap/servers/slapd/tools/ldaptool.h
++Copyright: 1998 Netscape Communication Corporation
++License: GPL-2+ or LGPL-2.1 or MPL-1.1
++
++Files: ldap/servers/slapd/tools/ldaptool-sasl.c
++ ldap/servers/slapd/tools/ldaptool-sasl.h
++Copyright: 2005 Sun Microsystems, Inc.
++License: GPL-2+ or LGPL-2.1 or MPL-1.1
++
++Files: m4/*
++Copyright: 2006-2017 Red Hat, Inc.
++           2016 William Brown <william at blackhats dot net dot au>
++License: GPL-3+
++
++Files: src/svrcore/*
++Copyright: 2016 Red Hat, Inc.
++License: MPL-2.0
++
++Files: src/cockpit/389-console/static/bootpopup.min.js
++       debian/missing-sources/bootpopup.js
++Copyright: 2016 rigon<ricardompgoncalves@gmail.com>
++License: GPL-3+
++
++Files: src/cockpit/389-console/static/bootstrap.min.js
++       debian/missing-sources/bootstrap.js
++Copyright: 2011-2016 Twitter, Inc.
++License: MIT
++
++Files: src/cockpit/389-console/static/c3.min.js
++       debian/missing-sources/c3.js
++Copyright: 2013 Masayuki Tanaka
++License: MIT
++
++Files: src/cockpit/389-console/static/d3.min.js
++       debian/missing-sources/d3.js
++Copyright: 2018 Mike Bostock
++License: BSD-3-clause
++
++Files: src/cockpit/389-console/static/jquery-3.3.1.min.js
++       src/cockpit/389-console/static/jquery.min.js
++       src/cockpit/389-console/static/jquery-ui.min.js
++       src/cockpit/389-console/static/moment.min.js
++       debian/missing-sources/jquery-1.12.4.js
++       debian/missing-sources/jquery-3.3.1.js
++       debian/missing-sources/jquery-ui.js
++       debian/missing-sources/moment.js
++Copyright: 2015-2018 JS Foundation and other contributors
++License: MIT
++
++Files: src/cockpit/389-console/static/jquery.dataTables*.min.js
++       debian/missing-sources/jquery.dataTables*.js
++Copyright: 2008-2017 SpryMedia Ltd
++License: MIT
++
++Files: src/cockpit/389-console/static/jquery.dropdown.min.js
++       debian/missing-sources/jquery.dropdown.js
++Copyright: 2018 A Beautiful Site, LLC
++License: MIT
++
++Files: src/cockpit/389-console/static/jquery.timepicker.min.js
++       debian/missing-sources/jquery.timepicker.js
++Copyright: 2015 Jon Thornton
++License: MIT
++
++Files: src/cockpit/389-console/static/jstree.min.js
++       debian/missing-sources/jstree.js
++Copyright: 2014 Ivan Bozhanov
++License: MIT
++
++Files: src/cockpit/389-console/static/patternfly.min.js
++       debian/missing-sources/patternfly.js
++Copyright: 2013 Red Hat, Inc.
++License: Apache-2.0
++
++Files: debian/*
++Copyright: 2008 Michele Baldessari <michele@acksyn.org>
++           2012 Timo Aaltonen <tjaalton@ubuntu.com>
++License: GPL-2+ or LGPL-2.1 or MPL-1.1
++
++License: Apache-2.0
++ On Debian machines the full text of the Apache License 2.0 can be found in the
++ file /usr/share/common-licenses/Apache-2.0.
++
++License: Other
++ In addition, as a special exception, Red Hat, Inc. gives You the additional
++ right to link the code of this Program with code not covered under the GNU
++ General Public License ("Non-GPL Code") and to distribute linked combinations
++ including the two, subject to the limitations in this paragraph. Non-GPL Code
++ permitted under this exception must only link to the code of this Program
++ through those well defined interfaces identified in the file named EXCEPTION
++ found in the source code files (the "Approved Interfaces"). The files of
++ Non-GPL Code may instantiate templates or use macros or inline functions from
++ the Approved Interfaces without causing the resulting work to be covered by
++ the GNU General Public License. Only Red Hat, Inc. may make changes or
++ additions to the list of Approved Interfaces. You must obey the GNU General
++ Public License in all respects for all of the Program code and other code used
++ in conjunction with the Program except the Non-GPL Code covered by this
++ exception. If you modify this file, you may extend this exception to your
++ version of the file, but you are not obligated to do so. If you do not wish to
++ provide this exception without modification, you must delete this exception
++ statement from your version and license this file solely under the GPL without
++ exception.
++
++License: BSD-3-clause
++ Redistribution and use in source and binary forms, with or without
++ modification, are permitted provided that the following conditions are met:
++ .
++   * Redistributions of source code must retain the above copyright notice, this
++     list of conditions and the following disclaimer.
++   * Redistributions in binary form must reproduce the above copyright notice,
++     this list of conditions and the following disclaimer in the documentation
++     and/or other materials provided with the distribution.
++   * Neither the name of the Dojo Foundation nor the names of its contributors
++     may be used to endorse or promote products derived from this software
++     without specific prior written permission.
++ .
++ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
++ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
++ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
++ DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
++ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
++ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
++ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
++ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++
++License: GPL-2 or GPL-2+
++ On Debian machines the full text of the GNU General Public License
++ can be found in the file /usr/share/common-licenses/GPL-2.
++
++License: GPL-3+
++ On Debian machines the full text of the GNU General Public License v3
++ can be found in the file /usr/share/common-licenses/GPL-3.
++
++License: LGPL-2.1
++ On Debian machines the full text of the GNU General Public License
++ can be found in the file /usr/share/common-licenses/LGPL-2.1.
++
++License: MIT
++ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 
++ associated documentation files (the "Software"), to deal in the Software without restriction, including 
++ without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
++ copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the 
++ following conditions:
++ .
++ The above copyright notice and this permission notice shall be included in all copies or substantial 
++ portions of the Software.
++ .
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 
++ LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO 
++ EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 
++ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR 
++ THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++
++
++License: MPL-1.1
++                          MOZILLA PUBLIC LICENSE
++                                Version 1.1
++ .
++                              ---------------
++ .
++ 1. Definitions.
++ .
++     1.0.1. "Commercial Use" means distribution or otherwise making the
++     Covered Code available to a third party.
++ .
++     1.1. "Contributor" means each entity that creates or contributes to
++     the creation of Modifications.
++ .
++     1.2. "Contributor Version" means the combination of the Original
++     Code, prior Modifications used by a Contributor, and the Modifications
++     made by that particular Contributor.
++ .
++     1.3. "Covered Code" means the Original Code or Modifications or the
++     combination of the Original Code and Modifications, in each case
++     including portions thereof.
++ .
++     1.4. "Electronic Distribution Mechanism" means a mechanism generally
++     accepted in the software development community for the electronic
++     transfer of data.
++ .
++     1.5. "Executable" means Covered Code in any form other than Source
++     Code.
++ .
++     1.6. "Initial Developer" means the individual or entity identified
++     as the Initial Developer in the Source Code notice required by Exhibit
++     A.
++ .
++     1.7. "Larger Work" means a work which combines Covered Code or
++     portions thereof with code not governed by the terms of this License.
++ .
++     1.8. "License" means this document.
++ .
++     1.8.1. "Licensable" means having the right to grant, to the maximum
++     extent possible, whether at the time of the initial grant or
++     subsequently acquired, any and all of the rights conveyed herein.
++ .
++     1.9. "Modifications" means any addition to or deletion from the
++     substance or structure of either the Original Code or any previous
++     Modifications. When Covered Code is released as a series of files, a
++     Modification is:
++          A. Any addition to or deletion from the contents of a file
++          containing Original Code or previous Modifications.
++ .
++          B. Any new file that contains any part of the Original Code or
++          previous Modifications.
++ .
++     1.10. "Original Code" means Source Code of computer software code
++     which is described in the Source Code notice required by Exhibit A as
++     Original Code, and which, at the time of its release under this
++     License is not already Covered Code governed by this License.
++ .
++     1.10.1. "Patent Claims" means any patent claim(s), now owned or
++     hereafter acquired, including without limitation,  method, process,
++     and apparatus claims, in any patent Licensable by grantor.
++ .
++     1.11. "Source Code" means the preferred form of the Covered Code for
++     making modifications to it, including all modules it contains, plus
++     any associated interface definition files, scripts used to control
++     compilation and installation of an Executable, or source code
++     differential comparisons against either the Original Code or another
++     well known, available Covered Code of the Contributor's choice. The
++     Source Code can be in a compressed or archival form, provided the
++     appropriate decompression or de-archiving software is widely available
++     for no charge.
++ .
++     1.12. "You" (or "Your")  means an individual or a legal entity
++     exercising rights under, and complying with all of the terms of, this
++     License or a future version of this License issued under Section 6.1.
++     For legal entities, "You" includes any entity which controls, is
++     controlled by, or is under common control with You. For purposes of
++     this definition, "control" means (a) the power, direct or indirect,
++     to cause the direction or management of such entity, whether by
++     contract or otherwise, or (b) ownership of more than fifty percent
++     (50%) of the outstanding shares or beneficial ownership of such
++     entity.
++ .
++ 2. Source Code License.
++ .
++     2.1. The Initial Developer Grant.
++     The Initial Developer hereby grants You a world-wide, royalty-free,
++     non-exclusive license, subject to third party intellectual property
++     claims:
++          (a)  under intellectual property rights (other than patent or
++          trademark) Licensable by Initial Developer to use, reproduce,
++          modify, display, perform, sublicense and distribute the Original
++          Code (or portions thereof) with or without Modifications, and/or
++          as part of a Larger Work; and
++ .
++          (b) under Patents Claims infringed by the making, using or
++          selling of Original Code, to make, have made, use, practice,
++          sell, and offer for sale, and/or otherwise dispose of the
++          Original Code (or portions thereof).
++ .
++          (c) the licenses granted in this Section 2.1(a) and (b) are
++          effective on the date Initial Developer first distributes
++          Original Code under the terms of this License.
++ .
++          (d) Notwithstanding Section 2.1(b) above, no patent license is
++          granted: 1) for code that You delete from the Original Code; 2)
++          separate from the Original Code;  or 3) for infringements caused
++          by: i) the modification of the Original Code or ii) the
++          combination of the Original Code with other software or devices.
++ .
++     2.2. Contributor Grant.
++     Subject to third party intellectual property claims, each Contributor
++     hereby grants You a world-wide, royalty-free, non-exclusive license
++ .
++          (a)  under intellectual property rights (other than patent or
++          trademark) Licensable by Contributor, to use, reproduce, modify,
++          display, perform, sublicense and distribute the Modifications
++          created by such Contributor (or portions thereof) either on an
++          unmodified basis, with other Modifications, as Covered Code
++          and/or as part of a Larger Work; and
++ .
++          (b) under Patent Claims infringed by the making, using, or
++          selling of  Modifications made by that Contributor either alone
++          and/or in combination with its Contributor Version (or portions
++          of such combination), to make, use, sell, offer for sale, have
++          made, and/or otherwise dispose of: 1) Modifications made by that
++          Contributor (or portions thereof); and 2) the combination of
++          Modifications made by that Contributor with its Contributor
++          Version (or portions of such combination).
++ .
++          (c) the licenses granted in Sections 2.2(a) and 2.2(b) are
++          effective on the date Contributor first makes Commercial Use of
++          the Covered Code.
++ .
++          (d)    Notwithstanding Section 2.2(b) above, no patent license is
++          granted: 1) for any code that Contributor has deleted from the
++          Contributor Version; 2)  separate from the Contributor Version;
++          3)  for infringements caused by: i) third party modifications of
++          Contributor Version or ii)  the combination of Modifications made
++          by that Contributor with other software  (except as part of the
++          Contributor Version) or other devices; or 4) under Patent Claims
++          infringed by Covered Code in the absence of Modifications made by
++          that Contributor.
++ .
++ 3. Distribution Obligations.
++ .
++     3.1. Application of License.
++     The Modifications which You create or to which You contribute are
++     governed by the terms of this License, including without limitation
++     Section 2.2. The Source Code version of Covered Code may be
++     distributed only under the terms of this License or a future version
++     of this License released under Section 6.1, and You must include a
++     copy of this License with every copy of the Source Code You
++     distribute. You may not offer or impose any terms on any Source Code
++     version that alters or restricts the applicable version of this
++     License or the recipients' rights hereunder. However, You may include
++     an additional document offering the additional rights described in
++     Section 3.5.
++ .
++     3.2. Availability of Source Code.
++     Any Modification which You create or to which You contribute must be
++     made available in Source Code form under the terms of this License
++     either on the same media as an Executable version or via an accepted
++     Electronic Distribution Mechanism to anyone to whom you made an
++     Executable version available; and if made available via Electronic
++     Distribution Mechanism, must remain available for at least twelve (12)
++     months after the date it initially became available, or at least six
++     (6) months after a subsequent version of that particular Modification
++     has been made available to such recipients. You are responsible for
++     ensuring that the Source Code version remains available even if the
++     Electronic Distribution Mechanism is maintained by a third party.
++ .
++     3.3. Description of Modifications.
++     You must cause all Covered Code to which You contribute to contain a
++     file documenting the changes You made to create that Covered Code and
++     the date of any change. You must include a prominent statement that
++     the Modification is derived, directly or indirectly, from Original
++     Code provided by the Initial Developer and including the name of the
++     Initial Developer in (a) the Source Code, and (b) in any notice in an
++     Executable version or related documentation in which You describe the
++     origin or ownership of the Covered Code.
++ .
++     3.4. Intellectual Property Matters
++          (a) Third Party Claims.
++          If Contributor has knowledge that a license under a third party's
++          intellectual property rights is required to exercise the rights
++          granted by such Contributor under Sections 2.1 or 2.2,
++          Contributor must include a text file with the Source Code
++          distribution titled "LEGAL" which describes the claim and the
++          party making the claim in sufficient detail that a recipient will
++          know whom to contact. If Contributor obtains such knowledge after
++          the Modification is made available as described in Section 3.2,
++          Contributor shall promptly modify the LEGAL file in all copies
++          Contributor makes available thereafter and shall take other steps
++          (such as notifying appropriate mailing lists or newsgroups)
++          reasonably calculated to inform those who received the Covered
++          Code that new knowledge has been obtained.
++ .
++          (b) Contributor APIs.
++          If Contributor's Modifications include an application programming
++          interface and Contributor has knowledge of patent licenses which
++          are reasonably necessary to implement that API, Contributor must
++          also include this information in the LEGAL file.
++ .
++               (c)    Representations.
++          Contributor represents that, except as disclosed pursuant to
++          Section 3.4(a) above, Contributor believes that Contributor's
++          Modifications are Contributor's original creation(s) and/or
++          Contributor has sufficient rights to grant the rights conveyed by
++          this License.
++ .
++     3.5. Required Notices.
++     You must duplicate the notice in Exhibit A in each file of the Source
++     Code.  If it is not possible to put such notice in a particular Source
++     Code file due to its structure, then You must include such notice in a
++     location (such as a relevant directory) where a user would be likely
++     to look for such a notice.  If You created one or more Modification(s)
++     You may add your name as a Contributor to the notice described in
++     Exhibit A.  You must also duplicate this License in any documentation
++     for the Source Code where You describe recipients' rights or ownership
++     rights relating to Covered Code.  You may choose to offer, and to
++     charge a fee for, warranty, support, indemnity or liability
++     obligations to one or more recipients of Covered Code. However, You
++     may do so only on Your own behalf, and not on behalf of the Initial
++     Developer or any Contributor. You must make it absolutely clear than
++     any such warranty, support, indemnity or liability obligation is
++     offered by You alone, and You hereby agree to indemnify the Initial
++     Developer and every Contributor for any liability incurred by the
++     Initial Developer or such Contributor as a result of warranty,
++     support, indemnity or liability terms You offer.
++ .
++     3.6. Distribution of Executable Versions.
++     You may distribute Covered Code in Executable form only if the
++     requirements of Section 3.1-3.5 have been met for that Covered Code,
++     and if You include a notice stating that the Source Code version of
++     the Covered Code is available under the terms of this License,
++     including a description of how and where You have fulfilled the
++     obligations of Section 3.2. The notice must be conspicuously included
++     in any notice in an Executable version, related documentation or
++     collateral in which You describe recipients' rights relating to the
++     Covered Code. You may distribute the Executable version of Covered
++     Code or ownership rights under a license of Your choice, which may
++     contain terms different from this License, provided that You are in
++     compliance with the terms of this License and that the license for the
++     Executable version does not attempt to limit or alter the recipient's
++     rights in the Source Code version from the rights set forth in this
++     License. If You distribute the Executable version under a different
++     license You must make it absolutely clear that any terms which differ
++     from this License are offered by You alone, not by the Initial
++     Developer or any Contributor. You hereby agree to indemnify the
++     Initial Developer and every Contributor for any liability incurred by
++     the Initial Developer or such Contributor as a result of any such
++     terms You offer.
++ .
++     3.7. Larger Works.
++     You may create a Larger Work by combining Covered Code with other code
++     not governed by the terms of this License and distribute the Larger
++     Work as a single product. In such a case, You must make sure the
++     requirements of this License are fulfilled for the Covered Code.
++ .
++ 4. Inability to Comply Due to Statute or Regulation.
++ .
++     If it is impossible for You to comply with any of the terms of this
++     License with respect to some or all of the Covered Code due to
++     statute, judicial order, or regulation then You must: (a) comply with
++     the terms of this License to the maximum extent possible; and (b)
++     describe the limitations and the code they affect. Such description
++     must be included in the LEGAL file described in Section 3.4 and must
++     be included with all distributions of the Source Code. Except to the
++     extent prohibited by statute or regulation, such description must be
++     sufficiently detailed for a recipient of ordinary skill to be able to
++     understand it.
++ .
++ 5. Application of this License.
++ .
++     This License applies to code to which the Initial Developer has
++     attached the notice in Exhibit A and to related Covered Code.
++ .
++ 6. Versions of the License.
++ .
++     6.1. New Versions.
++     Netscape Communications Corporation ("Netscape") may publish revised
++     and/or new versions of the License from time to time. Each version
++     will be given a distinguishing version number.
++ .
++     6.2. Effect of New Versions.
++     Once Covered Code has been published under a particular version of the
++     License, You may always continue to use it under the terms of that
++     version. You may also choose to use such Covered Code under the terms
++     of any subsequent version of the License published by Netscape. No one
++     other than Netscape has the right to modify the terms applicable to
++     Covered Code created under this License.
++ .
++     6.3. Derivative Works.
++     If You create or use a modified version of this License (which you may
++     only do in order to apply it to code which is not already Covered Code
++     governed by this License), You must (a) rename Your license so that
++     the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape",
++     "MPL", "NPL" or any confusingly similar phrase do not appear in your
++     license (except to note that your license differs from this License)
++     and (b) otherwise make it clear that Your version of the license
++     contains terms which differ from the Mozilla Public License and
++     Netscape Public License. (Filling in the name of the Initial
++     Developer, Original Code or Contributor in the notice described in
++     Exhibit A shall not of themselves be deemed to be modifications of
++     this License.)
++ .
++ 7. DISCLAIMER OF WARRANTY.
++ .
++     COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
++     WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
++     WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF
++     DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING.
++     THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE
++     IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT,
++     YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE
++     COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER
++     OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF
++     ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
++ .
++ 8. TERMINATION.
++ .
++     8.1.  This License and the rights granted hereunder will terminate
++     automatically if You fail to comply with terms herein and fail to cure
++     such breach within 30 days of becoming aware of the breach. All
++     sublicenses to the Covered Code which are properly granted shall
++     survive any termination of this License. Provisions which, by their
++     nature, must remain in effect beyond the termination of this License
++     shall survive.
++ .
++     8.2.  If You initiate litigation by asserting a patent infringement
++     claim (excluding declatory judgment actions) against Initial Developer
++     or a Contributor (the Initial Developer or Contributor against whom
++     You file such action is referred to as "Participant")  alleging that:
++ .
++     (a)  such Participant's Contributor Version directly or indirectly
++     infringes any patent, then any and all rights granted by such
++     Participant to You under Sections 2.1 and/or 2.2 of this License
++     shall, upon 60 days notice from Participant terminate prospectively,
++     unless if within 60 days after receipt of notice You either: (i)
++     agree in writing to pay Participant a mutually agreeable reasonable
++     royalty for Your past and future use of Modifications made by such
++     Participant, or (ii) withdraw Your litigation claim with respect to
++     the Contributor Version against such Participant.  If within 60 days
++     of notice, a reasonable royalty and payment arrangement are not
++     mutually agreed upon in writing by the parties or the litigation claim
++     is not withdrawn, the rights granted by Participant to You under
++     Sections 2.1 and/or 2.2 automatically terminate at the expiration of
++     the 60 day notice period specified above.
++ .
++     (b)  any software, hardware, or device, other than such Participant's
++     Contributor Version, directly or indirectly infringes any patent, then
++     any rights granted to You by such Participant under Sections 2.1(b)
++     and 2.2(b) are revoked effective as of the date You first made, used,
++     sold, distributed, or had made, Modifications made by that
++     Participant.
++ .
++     8.3.  If You assert a patent infringement claim against Participant
++     alleging that such Participant's Contributor Version directly or
++     indirectly infringes any patent where such claim is resolved (such as
++     by license or settlement) prior to the initiation of patent
++     infringement litigation, then the reasonable value of the licenses
++     granted by such Participant under Sections 2.1 or 2.2 shall be taken
++     into account in determining the amount or value of any payment or
++     license.
++ .
++     8.4.  In the event of termination under Sections 8.1 or 8.2 above,
++     all end user license agreements (excluding distributors and resellers)
++     which have been validly granted by You or any distributor hereunder
++     prior to termination shall survive termination.
++ .
++ 9. LIMITATION OF LIABILITY.
++ .
++     UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
++     (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL
++     DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE,
++     OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR
++     ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY
++     CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL,
++     WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
++     COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
++     INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
++     LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY
++     RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW
++     PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE
++     EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO
++     THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
++ .
++ 10. U.S. GOVERNMENT END USERS.
++ .
++     The Covered Code is a "commercial item," as that term is defined in
++     48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer
++     software" and "commercial computer software documentation," as such
++     terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48
++     C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995),
++     all U.S. Government End Users acquire Covered Code with only those
++     rights set forth herein.
++ .
++ 11. MISCELLANEOUS.
++ .
++     This License represents the complete agreement concerning subject
++     matter hereof. If any provision of this License is held to be
++     unenforceable, such provision shall be reformed only to the extent
++     necessary to make it enforceable. This License shall be governed by
++     California law provisions (except to the extent applicable law, if
++     any, provides otherwise), excluding its conflict-of-law provisions.
++     With respect to disputes in which at least one party is a citizen of,
++     or an entity chartered or registered to do business in the United
++     States of America, any litigation relating to this License shall be
++     subject to the jurisdiction of the Federal Courts of the Northern
++     District of California, with venue lying in Santa Clara County,
++     California, with the losing party responsible for costs, including
++     without limitation, court costs and reasonable attorneys' fees and
++     expenses. The application of the United Nations Convention on
++     Contracts for the International Sale of Goods is expressly excluded.
++     Any law or regulation which provides that the language of a contract
++     shall be construed against the drafter shall not apply to this
++     License.
++ .
++ 12. RESPONSIBILITY FOR CLAIMS.
++ .
++     As between Initial Developer and the Contributors, each party is
++     responsible for claims and damages arising, directly or indirectly,
++     out of its utilization of rights under this License and You agree to
++     work with Initial Developer and Contributors to distribute such
++     responsibility on an equitable basis. Nothing herein is intended or
++     shall be deemed to constitute any admission of liability.
++ .
++ 13. MULTIPLE-LICENSED CODE.
++ .
++     Initial Developer may designate portions of the Covered Code as
++     "Multiple-Licensed".  "Multiple-Licensed" means that the Initial
++     Developer permits you to utilize portions of the Covered Code under
++     Your choice of the NPL or the alternative licenses, if any, specified
++     by the Initial Developer in the file described in Exhibit A.
++ .
++ EXHIBIT A -Mozilla Public License.
++ .
++     ``The contents of this file are subject to the Mozilla Public License
++     Version 1.1 (the "License"); you may not use this file except in
++     compliance with the License. You may obtain a copy of the License at
++     http://www.mozilla.org/MPL/
++ .
++     Software distributed under the License is distributed on an "AS IS"
++     basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
++     License for the specific language governing rights and limitations
++     under the License.
++ .
++     The Original Code is ______________________________________.
++ .
++     The Initial Developer of the Original Code is ________________________.
++     Portions created by ______________________ are Copyright (C) ______
++     _______________________. All Rights Reserved.
++ .
++     Contributor(s): ______________________________________.
++ .
++     Alternatively, the contents of this file may be used under the terms
++     of the _____ license (the  "[___] License"), in which case the
++     provisions of [______] License are applicable instead of those
++     above.  If you wish to allow use of your version of this file only
++     under the terms of the [____] License and not to allow others to use
++     your version of this file under the MPL, indicate your decision by
++     deleting  the provisions above and replace  them with the notice and
++     other provisions required by the [___] License.  If you do not delete
++     the provisions above, a recipient may use your version of this file
++     under either the MPL or the [___] License."
++ .
++     [NOTE: The text of this Exhibit A may differ slightly from the text of
++     the notices in the Source Code files of the Original Code. You should
++     use the text of this Exhibit A rather than the text found in the
++     Original Code Source Code for Your Modifications.]
++
++License: MPL-2.0
++ On Debian machines the full text of the Mozilla Public License version 2.0
++ can be found in the file /usr/share/common-licenses/MPL-2.0.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4545f3e1e45433f7034dab77abac13ff9b390a34
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,6 @@@
++include:
++  - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml
++  - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml
++
++blhc:
++  allow_failure: true
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..404337ab8f10230427fcfb71c60a199b731b0437
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,429 @@@
++/**************************************************************************
++ * Popup dialog boxes for Bootstrap - http://www.bootpopup.tk
++ * Copyright (C) 2016  rigon<ricardompgoncalves@gmail.com>
++ *
++ * This program is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, either version 3 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
++ *************************************************************************/
++
++ 
++/* // List of input types
++case "button": case "checkbox": case "color": case "date": case "datetime-local":
++case "email": case "file": case "hidden": case "image": case "month": case "number":
++case "password": case "radio": case "range": case "reset": case "search":
++case "submit": case "tel": case "text": case "time": case "url": case "week": */
++var INPUT_SHORTCUT_TYPES = [ "button", "text", "submit", "color", "url", "password",
++      "hidden", "file", "number", "email", "reset", "date", "checkbox", "select", "radio" ];
++
++
++function bootpopup(options) {
++      // Create a new instance if this is not
++      if(!(this instanceof bootpopup))
++              return new bootpopup(options);
++
++      var self = this;
++      // Create a global random ID for the form
++      this.formid = "bootpopup-form" + String(Math.random()).substr(2);
++
++      this.options = {
++              title: document.title,
++              showclose: true,
++              size: "normal",
++              size_labels: "col-sm-4",
++              size_inputs: "col-sm-8",
++              content: [],
++              onsubmit: "close",
++              buttons: ["close"],
++
++              before: function() {},
++              dismiss: function() {},
++              close: function() {},
++              ok: function() {},
++              cancel: function() {},
++              yes: function() {},
++              no: function() {},
++              complete: function() {},
++              submit: function(e) {
++                      self.callback(self.options.onsubmit, e);
++                      return false;   // Cancel form submision
++              }
++      };
++
++      this.addOptions = function(options) {
++              var buttons = [];
++              for(var key in options) {
++                      if(key in this.options)
++                              this.options[key] = options[key];
++                      // If an event for a button is given, show the respective button
++                      if(["close", "ok", "cancel", "yes", "no"].indexOf(key) >= 0)
++                              buttons.push(key);
++              }
++              // Copy news buttons to this.options.buttons
++              if(buttons.length > 0) {
++                      // Clear default buttons if new are not given
++                      if(!("buttons" in options)) this.options.buttons = [];
++                      buttons.forEach(function(item) {
++                              if(self.options.buttons.indexOf(item) < 0)
++                                      self.options.buttons.push(item);
++                      });
++              }
++              // Determine what is the best action if none is given
++              if(typeof options.onsubmit !== "string") {
++                      if(this.options.buttons.indexOf("close") > 0) this.options.onsubmit = "close";
++                      else if(this.options.buttons.indexOf("ok") > 0) this.options.onsubmit = "ok";
++                      else if(this.options.buttons.indexOf("yes") > 0) this.options.onsubmit = "yes";
++              }
++
++              return this.options;
++      };
++
++      this.setOptions = function(options) {
++              this.options = options;
++              return this.options;
++      };
++
++      this.create = function() {
++              // Option for modal dialog size
++              var classModalDialog = "modal-dialog";
++              if(this.options.size == "large") classModalDialog += " modal-lg";
++              if(this.options.size == "small") classModalDialog += " modal-sm";
++
++              // Create HTML elements for modal dialog
++              this.modal = $('<div class="modal fade" tabindex="-1" role="dialog" aria-labelledby="bootpopup-title"></div>');
++              this.dialog = $('<div></div>', { class: classModalDialog, role: "document" });
++              this.content = $('<div class="modal-content"></div>');
++              this.dialog.append(this.content);
++              this.modal.append(this.dialog);
++
++              // Header
++              this.header = $('<div class="modal-header"></div>');
++              if(this.options.showclose)      // Close button
++                      this.header.append('<button type="button" class="bootpopup-button close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>');
++              this.header.append('<h4 class="modal-title" id="bootpopup-title">' + this.options.title + '</h4>');
++              this.content.append(this.header);
++
++              // Body
++              this.body = $('<div class="modal-body"></div>');
++              this.form = $("<form></form>", { id: this.formid, class: "form-horizontal", submit: function(e) { return self.options.submit(e); } });
++              this.body.append(this.form);
++              this.content.append(this.body);
++
++              // Iterate over entries
++              for(var i in this.options.content) {
++                      var entry = this.options.content[i];
++                      switch(typeof entry) {
++                      case "string":          // HTML string
++                              this.form.append(entry);
++                              break;
++                      case "object":
++                              for(var type in entry) {
++                                      var attrs = entry[type];
++
++                                      // Convert functions to string to be used as callback
++                                      for(var attribute in attrs)
++                                              if(typeof attrs[attribute] === "function")
++                                                      attrs[attribute] = "(" + attrs[attribute] + ")(this)";
++
++                                      // Check if type is a shortcut for input
++                                      if(INPUT_SHORTCUT_TYPES.indexOf(type) >= 0) {
++                                              attrs.type = type;  // Add attribute for type
++                                              type = "input";     // Continue to input
++                                      }
++
++                                      if(type == "input") {
++                                              // To avoid adding "form-control" class
++                                              if(attrs.type === "checkbox" && typeof attrs.class === "undefined")
++                                                      attrs.class = "";
++
++                                              // Create a random id for the input if none provided
++                                              attrs.id = (typeof attrs.id === "undefined" ? "bootpopup-input" + String(Math.random()).substr(2) : attrs.id);
++                                              attrs.class = (typeof attrs.class === "undefined" ? "form-control" : attrs.class);
++                                              attrs.type = (typeof attrs.type === "undefined" ? "text" : attrs.type);
++
++
++                                              // Create input
++                                              var input;
++                                              switch(attrs.type) {
++                                                      case "checkbox":
++                                                              // Special case for checkbox
++                                                              input = $('<div class="checkbox"></div>')
++                                                                      .append($('<label></label>')
++                                                                              .append($("<input />", attrs))
++                                                                              .append(attrs.label));
++                                                              
++                                                              // Clear label to not be added as header
++                                                              attrs.label = "";
++                                                              break;
++                                                      case "select":
++                                                              // Special case for select
++                                                              input = $("<select></select>", attrs);
++                                                              for(var option in attrs.options)
++                                                                      input.append($("<option></option>", { value: option })
++                                                                              .append(attrs.options[option]));
++                                                              
++                                                              break;
++                                                      case "radio":
++                                                              // Special case for radios
++                                                              input = [];
++                                                              for(var option in attrs.options)
++                                                                      input.push($('<div class="radio"></div>', attrs)
++                                                                              .append($('<label></label>')
++                                                                                      .append($("<input />", { type: "radio", name: attrs.name, value: option }))
++                                                                                      .append(attrs.options[option])));
++                                                              break;
++                                                      default:
++                                                              input = $("<input />", attrs);
++                                              }
++
++                                              // Form Group
++                                              var formGroup = $('<div class="form-group"></div>').appendTo(this.form);
++                                              // Label
++                                              $("<label></label>", { for: attrs.id, class: "control-label " + this.options.size_labels, text: attrs.label }).appendTo(formGroup);
++
++                                              // Input and div to control width
++                                              var divInput = $('<div></div>', { class: this.options.size_inputs });
++                                              divInput.append(input);
++                                              formGroup.append(divInput);
++                                      }
++                                      else    // Anything else besides input
++                                              this.form.append($("<" + type + "></" + type + ">", attrs));    // Add directly
++                              }
++                              break;
++                      default:
++                              throw "Invalid entry type";
++                      }
++              }
++
++              // Footer
++              this.footer = $('<div class="modal-footer"></div>');
++              this.content.append(this.footer);
++
++              for(var key in this.options.buttons) {
++                      var item = this.options.buttons[key];
++                      var btnClass = "";
++                      var btnText = "";
++
++                      switch(item) {
++                      case "close": btnClass = "btn-primary"; btnText = "Close"; break;
++                      case "ok": btnClass = "btn-primary"; btnText = "OK"; break;
++                      case "cancel": btnClass = "btn-default"; btnText = "Cancel"; break;
++                      case "yes": btnClass = "btn-primary"; btnText = "Yes"; break;
++                      case "no": btnClass = "btn-default"; btnText = "No"; break;
++                      }
++
++                      var button = $("<button></button>", {
++                              type: "button",
++                              text: btnText,
++                              class: "btn " + btnClass,
++                              "data-dismiss": "modal",
++                              "data-callback": item,
++                              "data-form": this.formid,
++
++                              click: function(event) {
++                                      var name = $(event.target).data("callback");
++                                      self.callback(name, event);
++                              }
++                      });
++                      this.footer.append(button);
++
++                      // Reference for buttons
++                      switch(item) {
++                      case "close": this.btnClose = button; break;
++                      case "ok": this.btnOk = button; break;
++                      case "cancel": this.btnCancel = button; break;
++                      case "yes": this.btnYes = button; break;
++                      case "no": this.btnNo = button; break;
++                      }
++              }
++
++              // Setup events for dismiss and complete
++              this.modal.on('hide.bs.modal', this.options.dismiss);
++              this.modal.on('hidden.bs.modal', function(e) {
++                      self.options.complete(e);
++                      self.modal.remove();    // Delete window after complete
++              });
++
++              // Don't close on backdrop click
++              if(this.options.showclose === false)
++                      this.modal.attr('data-backdrop', 'static');
++
++              // Add window to body
++              $(document.body).append(this.modal);
++      };
++
++      this.show = function() {
++              // Call before event
++              this.options.before(this);
++
++              // Fire the modal window
++              this.modal.modal();
++      };
++
++      this.data = function() {
++              var keyval = {};
++              var array = this.form.serializeArray();
++              for(var i in array) {
++                      var name = array[i].name, val = array[i].value;
++
++                      if(typeof keyval[name] === "undefined")
++                              keyval[name] = val;
++                      else {
++                              if(!Array.isArray(keyval[name]))
++                                      keyval[name] = [keyval[name]];
++                              keyval[name].push(val);
++                      }
++
++              }
++              return keyval;
++      };
++
++      this.callback = function(name, event) {
++              var func = this.options[name];          // Get function to call
++              if(typeof func !== "function") return;
++
++              // Perform callback
++              var array = this.form.serializeArray();
++              var ret = func(this.data(), array, event);
++
++              // Hide window
++              this.modal.modal("hide");
++              return ret;
++      };
++
++      this.dismiss = function() { this.callback("dismiss"); };
++      this.submit = function() { this.callback("submit"); };
++      this.close = function() { this.callback("close"); };
++      this.ok = function() { this.callback("ok"); };
++      this.cancel = function() { this.callback("cancel"); };
++      this.yes = function() { this.callback("yes"); };
++      this.no = function() { this.callback("no"); };
++
++      this.addOptions(options);
++      this.create();
++      this.show();
++}
++
++
++bootpopup.alert = function(message, title, callback) {
++      if(typeof title === "function")
++              callback = title;
++
++      if(typeof title !== "string")
++              title = document.title;
++      if(typeof callback !== "function")
++              callback = function() {};
++
++      return bootpopup({
++              title: title,
++              content: [{ p: { text: message } }],
++              dismiss: function() { callback(); }
++      });
++};
++
++bootpopup.confirm = function(message, title, callback) {
++      if(typeof title === "function")
++              callback = title;
++
++      if(typeof title !== "string")
++              title = document.title;
++      if(typeof callback !== "function")
++              callback = function() {};
++
++      var answer = false;
++      return bootpopup({
++              title: title,
++              showclose: false,
++              content: [{ p: { text: message } }],
++              buttons: ["no", "yes"],
++              yes: function() { answer = true; },
++              dismiss: function() { callback(answer); }
++      });
++};
++
++bootpopup.prompt = function(label, type, message, title, callback) {
++      // Callback can be in any position, except label
++      var callback_function = function() {};
++      if(typeof type === "function")
++              callback_function = type;
++      if(typeof message === "function")
++              callback_function = message;
++      if(typeof title === "function")
++              callback_function = title;
++      if(typeof callback === "function")
++              callback_function = callback;
++
++      // If a list of values is provided, then the parameters are shifted
++      // because type should be ignored
++      if(typeof label === "object") {
++              title = message;
++              message = type;
++              type = null;
++      }
++
++      // Sanitize message and title
++      if(typeof message !== "string")
++              message = "Please, provide values for:";
++      if(typeof title !== "string")
++              title = document.title;
++
++      // Add message to the window
++      var content = [{ p: { text: message } }];
++
++      // If label is a list of values to be asked to input
++      if(typeof label === "object") {
++              label.forEach(function(entry) {
++                      var obj = null;
++
++                      // HTML
++                      if (typeof entry === "string")
++                              obj = entry;
++                      
++                      // Input
++                      if ( entry !== null && typeof entry === "object" && typeof entry.label === "string") {
++                              if(typeof entry.name !== "string")  // Name in lower case and dashes instead spaces
++                                      entry.name = entry.label.toLowerCase().replace(/\s+/g, "-");
++                              if(typeof entry.type !== "string")
++                                      entry.type = "text";
++                              
++                              obj = { input: entry };
++                      }
++
++                      if(obj !== null)
++                              content.push(obj);
++              });
++      }
++      else {
++              if(typeof type !== "string") type = "text";
++              content.push({ input: { type: type, name: "value", label: label } });
++              var callback_tmp = callback_function;   // Overload callback function to return "data.value"
++              callback_function = function(data) { callback_tmp(data.value); };
++      }
++
++      return bootpopup({
++              title: title,
++              content: content,
++              buttons: ["cancel", "ok"],
++              ok: function(data) {
++                      callback_function(data);
++              }
++      });
++};
++
++/**
++ * AMD support: require.js
++ */
++if(typeof define === "function") {
++      define(["jquery", "bootstrap"], function() {
++              return bootpopup;
++      });
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8a2e99a535d47e5798b167d1074ae2c77cab21e7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2377 @@@
++/*!
++ * Bootstrap v3.3.7 (http://getbootstrap.com)
++ * Copyright 2011-2016 Twitter, Inc.
++ * Licensed under the MIT license
++ */
++
++if (typeof jQuery === 'undefined') {
++  throw new Error('Bootstrap\'s JavaScript requires jQuery')
++}
++
+++function ($) {
++  'use strict';
++  var version = $.fn.jquery.split(' ')[0].split('.')
++  if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1) || (version[0] > 3)) {
++    throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4')
++  }
++}(jQuery);
++
++/* ========================================================================
++ * Bootstrap: transition.js v3.3.7
++ * http://getbootstrap.com/javascript/#transitions
++ * ========================================================================
++ * Copyright 2011-2016 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
++ * ======================================================================== */
++
++
+++function ($) {
++  'use strict';
++
++  // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
++  // ============================================================
++
++  function transitionEnd() {
++    var el = document.createElement('bootstrap')
++
++    var transEndEventNames = {
++      WebkitTransition : 'webkitTransitionEnd',
++      MozTransition    : 'transitionend',
++      OTransition      : 'oTransitionEnd otransitionend',
++      transition       : 'transitionend'
++    }
++
++    for (var name in transEndEventNames) {
++      if (el.style[name] !== undefined) {
++        return { end: transEndEventNames[name] }
++      }
++    }
++
++    return false // explicit for ie8 (  ._.)
++  }
++
++  // http://blog.alexmaccaw.com/css-transitions
++  $.fn.emulateTransitionEnd = function (duration) {
++    var called = false
++    var $el = this
++    $(this).one('bsTransitionEnd', function () { called = true })
++    var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
++    setTimeout(callback, duration)
++    return this
++  }
++
++  $(function () {
++    $.support.transition = transitionEnd()
++
++    if (!$.support.transition) return
++
++    $.event.special.bsTransitionEnd = {
++      bindType: $.support.transition.end,
++      delegateType: $.support.transition.end,
++      handle: function (e) {
++        if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments)
++      }
++    }
++  })
++
++}(jQuery);
++
++/* ========================================================================
++ * Bootstrap: alert.js v3.3.7
++ * http://getbootstrap.com/javascript/#alerts
++ * ========================================================================
++ * Copyright 2011-2016 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
++ * ======================================================================== */
++
++
+++function ($) {
++  'use strict';
++
++  // ALERT CLASS DEFINITION
++  // ======================
++
++  var dismiss = '[data-dismiss="alert"]'
++  var Alert   = function (el) {
++    $(el).on('click', dismiss, this.close)
++  }
++
++  Alert.VERSION = '3.3.7'
++
++  Alert.TRANSITION_DURATION = 150
++
++  Alert.prototype.close = function (e) {
++    var $this    = $(this)
++    var selector = $this.attr('data-target')
++
++    if (!selector) {
++      selector = $this.attr('href')
++      selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
++    }
++
++    var $parent = $(selector === '#' ? [] : selector)
++
++    if (e) e.preventDefault()
++
++    if (!$parent.length) {
++      $parent = $this.closest('.alert')
++    }
++
++    $parent.trigger(e = $.Event('close.bs.alert'))
++
++    if (e.isDefaultPrevented()) return
++
++    $parent.removeClass('in')
++
++    function removeElement() {
++      // detach from parent, fire event then clean up data
++      $parent.detach().trigger('closed.bs.alert').remove()
++    }
++
++    $.support.transition && $parent.hasClass('fade') ?
++      $parent
++        .one('bsTransitionEnd', removeElement)
++        .emulateTransitionEnd(Alert.TRANSITION_DURATION) :
++      removeElement()
++  }
++
++
++  // ALERT PLUGIN DEFINITION
++  // =======================
++
++  function Plugin(option) {
++    return this.each(function () {
++      var $this = $(this)
++      var data  = $this.data('bs.alert')
++
++      if (!data) $this.data('bs.alert', (data = new Alert(this)))
++      if (typeof option == 'string') data[option].call($this)
++    })
++  }
++
++  var old = $.fn.alert
++
++  $.fn.alert             = Plugin
++  $.fn.alert.Constructor = Alert
++
++
++  // ALERT NO CONFLICT
++  // =================
++
++  $.fn.alert.noConflict = function () {
++    $.fn.alert = old
++    return this
++  }
++
++
++  // ALERT DATA-API
++  // ==============
++
++  $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close)
++
++}(jQuery);
++
++/* ========================================================================
++ * Bootstrap: button.js v3.3.7
++ * http://getbootstrap.com/javascript/#buttons
++ * ========================================================================
++ * Copyright 2011-2016 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
++ * ======================================================================== */
++
++
+++function ($) {
++  'use strict';
++
++  // BUTTON PUBLIC CLASS DEFINITION
++  // ==============================
++
++  var Button = function (element, options) {
++    this.$element  = $(element)
++    this.options   = $.extend({}, Button.DEFAULTS, options)
++    this.isLoading = false
++  }
++
++  Button.VERSION  = '3.3.7'
++
++  Button.DEFAULTS = {
++    loadingText: 'loading...'
++  }
++
++  Button.prototype.setState = function (state) {
++    var d    = 'disabled'
++    var $el  = this.$element
++    var val  = $el.is('input') ? 'val' : 'html'
++    var data = $el.data()
++
++    state += 'Text'
++
++    if (data.resetText == null) $el.data('resetText', $el[val]())
++
++    // push to event loop to allow forms to submit
++    setTimeout($.proxy(function () {
++      $el[val](data[state] == null ? this.options[state] : data[state])
++
++      if (state == 'loadingText') {
++        this.isLoading = true
++        $el.addClass(d).attr(d, d).prop(d, true)
++      } else if (this.isLoading) {
++        this.isLoading = false
++        $el.removeClass(d).removeAttr(d).prop(d, false)
++      }
++    }, this), 0)
++  }
++
++  Button.prototype.toggle = function () {
++    var changed = true
++    var $parent = this.$element.closest('[data-toggle="buttons"]')
++
++    if ($parent.length) {
++      var $input = this.$element.find('input')
++      if ($input.prop('type') == 'radio') {
++        if ($input.prop('checked')) changed = false
++        $parent.find('.active').removeClass('active')
++        this.$element.addClass('active')
++      } else if ($input.prop('type') == 'checkbox') {
++        if (($input.prop('checked')) !== this.$element.hasClass('active')) changed = false
++        this.$element.toggleClass('active')
++      }
++      $input.prop('checked', this.$element.hasClass('active'))
++      if (changed) $input.trigger('change')
++    } else {
++      this.$element.attr('aria-pressed', !this.$element.hasClass('active'))
++      this.$element.toggleClass('active')
++    }
++  }
++
++
++  // BUTTON PLUGIN DEFINITION
++  // ========================
++
++  function Plugin(option) {
++    return this.each(function () {
++      var $this   = $(this)
++      var data    = $this.data('bs.button')
++      var options = typeof option == 'object' && option
++
++      if (!data) $this.data('bs.button', (data = new Button(this, options)))
++
++      if (option == 'toggle') data.toggle()
++      else if (option) data.setState(option)
++    })
++  }
++
++  var old = $.fn.button
++
++  $.fn.button             = Plugin
++  $.fn.button.Constructor = Button
++
++
++  // BUTTON NO CONFLICT
++  // ==================
++
++  $.fn.button.noConflict = function () {
++    $.fn.button = old
++    return this
++  }
++
++
++  // BUTTON DATA-API
++  // ===============
++
++  $(document)
++    .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) {
++      var $btn = $(e.target).closest('.btn')
++      Plugin.call($btn, 'toggle')
++      if (!($(e.target).is('input[type="radio"], input[type="checkbox"]'))) {
++        // Prevent double click on radios, and the double selections (so cancellation) on checkboxes
++        e.preventDefault()
++        // The target component still receive the focus
++        if ($btn.is('input,button')) $btn.trigger('focus')
++        else $btn.find('input:visible,button:visible').first().trigger('focus')
++      }
++    })
++    .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) {
++      $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type))
++    })
++
++}(jQuery);
++
++/* ========================================================================
++ * Bootstrap: carousel.js v3.3.7
++ * http://getbootstrap.com/javascript/#carousel
++ * ========================================================================
++ * Copyright 2011-2016 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
++ * ======================================================================== */
++
++
+++function ($) {
++  'use strict';
++
++  // CAROUSEL CLASS DEFINITION
++  // =========================
++
++  var Carousel = function (element, options) {
++    this.$element    = $(element)
++    this.$indicators = this.$element.find('.carousel-indicators')
++    this.options     = options
++    this.paused      = null
++    this.sliding     = null
++    this.interval    = null
++    this.$active     = null
++    this.$items      = null
++
++    this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this))
++
++    this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element
++      .on('mouseenter.bs.carousel', $.proxy(this.pause, this))
++      .on('mouseleave.bs.carousel', $.proxy(this.cycle, this))
++  }
++
++  Carousel.VERSION  = '3.3.7'
++
++  Carousel.TRANSITION_DURATION = 600
++
++  Carousel.DEFAULTS = {
++    interval: 5000,
++    pause: 'hover',
++    wrap: true,
++    keyboard: true
++  }
++
++  Carousel.prototype.keydown = function (e) {
++    if (/input|textarea/i.test(e.target.tagName)) return
++    switch (e.which) {
++      case 37: this.prev(); break
++      case 39: this.next(); break
++      default: return
++    }
++
++    e.preventDefault()
++  }
++
++  Carousel.prototype.cycle = function (e) {
++    e || (this.paused = false)
++
++    this.interval && clearInterval(this.interval)
++
++    this.options.interval
++      && !this.paused
++      && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
++
++    return this
++  }
++
++  Carousel.prototype.getItemIndex = function (item) {
++    this.$items = item.parent().children('.item')
++    return this.$items.index(item || this.$active)
++  }
++
++  Carousel.prototype.getItemForDirection = function (direction, active) {
++    var activeIndex = this.getItemIndex(active)
++    var willWrap = (direction == 'prev' && activeIndex === 0)
++                || (direction == 'next' && activeIndex == (this.$items.length - 1))
++    if (willWrap && !this.options.wrap) return active
++    var delta = direction == 'prev' ? -1 : 1
++    var itemIndex = (activeIndex + delta) % this.$items.length
++    return this.$items.eq(itemIndex)
++  }
++
++  Carousel.prototype.to = function (pos) {
++    var that        = this
++    var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active'))
++
++    if (pos > (this.$items.length - 1) || pos < 0) return
++
++    if (this.sliding)       return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid"
++    if (activeIndex == pos) return this.pause().cycle()
++
++    return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos))
++  }
++
++  Carousel.prototype.pause = function (e) {
++    e || (this.paused = true)
++
++    if (this.$element.find('.next, .prev').length && $.support.transition) {
++      this.$element.trigger($.support.transition.end)
++      this.cycle(true)
++    }
++
++    this.interval = clearInterval(this.interval)
++
++    return this
++  }
++
++  Carousel.prototype.next = function () {
++    if (this.sliding) return
++    return this.slide('next')
++  }
++
++  Carousel.prototype.prev = function () {
++    if (this.sliding) return
++    return this.slide('prev')
++  }
++
++  Carousel.prototype.slide = function (type, next) {
++    var $active   = this.$element.find('.item.active')
++    var $next     = next || this.getItemForDirection(type, $active)
++    var isCycling = this.interval
++    var direction = type == 'next' ? 'left' : 'right'
++    var that      = this
++
++    if ($next.hasClass('active')) return (this.sliding = false)
++
++    var relatedTarget = $next[0]
++    var slideEvent = $.Event('slide.bs.carousel', {
++      relatedTarget: relatedTarget,
++      direction: direction
++    })
++    this.$element.trigger(slideEvent)
++    if (slideEvent.isDefaultPrevented()) return
++
++    this.sliding = true
++
++    isCycling && this.pause()
++
++    if (this.$indicators.length) {
++      this.$indicators.find('.active').removeClass('active')
++      var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)])
++      $nextIndicator && $nextIndicator.addClass('active')
++    }
++
++    var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid"
++    if ($.support.transition && this.$element.hasClass('slide')) {
++      $next.addClass(type)
++      $next[0].offsetWidth // force reflow
++      $active.addClass(direction)
++      $next.addClass(direction)
++      $active
++        .one('bsTransitionEnd', function () {
++          $next.removeClass([type, direction].join(' ')).addClass('active')
++          $active.removeClass(['active', direction].join(' '))
++          that.sliding = false
++          setTimeout(function () {
++            that.$element.trigger(slidEvent)
++          }, 0)
++        })
++        .emulateTransitionEnd(Carousel.TRANSITION_DURATION)
++    } else {
++      $active.removeClass('active')
++      $next.addClass('active')
++      this.sliding = false
++      this.$element.trigger(slidEvent)
++    }
++
++    isCycling && this.cycle()
++
++    return this
++  }
++
++
++  // CAROUSEL PLUGIN DEFINITION
++  // ==========================
++
++  function Plugin(option) {
++    return this.each(function () {
++      var $this   = $(this)
++      var data    = $this.data('bs.carousel')
++      var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option)
++      var action  = typeof option == 'string' ? option : options.slide
++
++      if (!data) $this.data('bs.carousel', (data = new Carousel(this, options)))
++      if (typeof option == 'number') data.to(option)
++      else if (action) data[action]()
++      else if (options.interval) data.pause().cycle()
++    })
++  }
++
++  var old = $.fn.carousel
++
++  $.fn.carousel             = Plugin
++  $.fn.carousel.Constructor = Carousel
++
++
++  // CAROUSEL NO CONFLICT
++  // ====================
++
++  $.fn.carousel.noConflict = function () {
++    $.fn.carousel = old
++    return this
++  }
++
++
++  // CAROUSEL DATA-API
++  // =================
++
++  var clickHandler = function (e) {
++    var href
++    var $this   = $(this)
++    var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7
++    if (!$target.hasClass('carousel')) return
++    var options = $.extend({}, $target.data(), $this.data())
++    var slideIndex = $this.attr('data-slide-to')
++    if (slideIndex) options.interval = false
++
++    Plugin.call($target, options)
++
++    if (slideIndex) {
++      $target.data('bs.carousel').to(slideIndex)
++    }
++
++    e.preventDefault()
++  }
++
++  $(document)
++    .on('click.bs.carousel.data-api', '[data-slide]', clickHandler)
++    .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler)
++
++  $(window).on('load', function () {
++    $('[data-ride="carousel"]').each(function () {
++      var $carousel = $(this)
++      Plugin.call($carousel, $carousel.data())
++    })
++  })
++
++}(jQuery);
++
++/* ========================================================================
++ * Bootstrap: collapse.js v3.3.7
++ * http://getbootstrap.com/javascript/#collapse
++ * ========================================================================
++ * Copyright 2011-2016 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
++ * ======================================================================== */
++
++/* jshint latedef: false */
++
+++function ($) {
++  'use strict';
++
++  // COLLAPSE PUBLIC CLASS DEFINITION
++  // ================================
++
++  var Collapse = function (element, options) {
++    this.$element      = $(element)
++    this.options       = $.extend({}, Collapse.DEFAULTS, options)
++    this.$trigger      = $('[data-toggle="collapse"][href="#' + element.id + '"],' +
++                           '[data-toggle="collapse"][data-target="#' + element.id + '"]')
++    this.transitioning = null
++
++    if (this.options.parent) {
++      this.$parent = this.getParent()
++    } else {
++      this.addAriaAndCollapsedClass(this.$element, this.$trigger)
++    }
++
++    if (this.options.toggle) this.toggle()
++  }
++
++  Collapse.VERSION  = '3.3.7'
++
++  Collapse.TRANSITION_DURATION = 350
++
++  Collapse.DEFAULTS = {
++    toggle: true
++  }
++
++  Collapse.prototype.dimension = function () {
++    var hasWidth = this.$element.hasClass('width')
++    return hasWidth ? 'width' : 'height'
++  }
++
++  Collapse.prototype.show = function () {
++    if (this.transitioning || this.$element.hasClass('in')) return
++
++    var activesData
++    var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing')
++
++    if (actives && actives.length) {
++      activesData = actives.data('bs.collapse')
++      if (activesData && activesData.transitioning) return
++    }
++
++    var startEvent = $.Event('show.bs.collapse')
++    this.$element.trigger(startEvent)
++    if (startEvent.isDefaultPrevented()) return
++
++    if (actives && actives.length) {
++      Plugin.call(actives, 'hide')
++      activesData || actives.data('bs.collapse', null)
++    }
++
++    var dimension = this.dimension()
++
++    this.$element
++      .removeClass('collapse')
++      .addClass('collapsing')[dimension](0)
++      .attr('aria-expanded', true)
++
++    this.$trigger
++      .removeClass('collapsed')
++      .attr('aria-expanded', true)
++
++    this.transitioning = 1
++
++    var complete = function () {
++      this.$element
++        .removeClass('collapsing')
++        .addClass('collapse in')[dimension]('')
++      this.transitioning = 0
++      this.$element
++        .trigger('shown.bs.collapse')
++    }
++
++    if (!$.support.transition) return complete.call(this)
++
++    var scrollSize = $.camelCase(['scroll', dimension].join('-'))
++
++    this.$element
++      .one('bsTransitionEnd', $.proxy(complete, this))
++      .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize])
++  }
++
++  Collapse.prototype.hide = function () {
++    if (this.transitioning || !this.$element.hasClass('in')) return
++
++    var startEvent = $.Event('hide.bs.collapse')
++    this.$element.trigger(startEvent)
++    if (startEvent.isDefaultPrevented()) return
++
++    var dimension = this.dimension()
++
++    this.$element[dimension](this.$element[dimension]())[0].offsetHeight
++
++    this.$element
++      .addClass('collapsing')
++      .removeClass('collapse in')
++      .attr('aria-expanded', false)
++
++    this.$trigger
++      .addClass('collapsed')
++      .attr('aria-expanded', false)
++
++    this.transitioning = 1
++
++    var complete = function () {
++      this.transitioning = 0
++      this.$element
++        .removeClass('collapsing')
++        .addClass('collapse')
++        .trigger('hidden.bs.collapse')
++    }
++
++    if (!$.support.transition) return complete.call(this)
++
++    this.$element
++      [dimension](0)
++      .one('bsTransitionEnd', $.proxy(complete, this))
++      .emulateTransitionEnd(Collapse.TRANSITION_DURATION)
++  }
++
++  Collapse.prototype.toggle = function () {
++    this[this.$element.hasClass('in') ? 'hide' : 'show']()
++  }
++
++  Collapse.prototype.getParent = function () {
++    return $(this.options.parent)
++      .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]')
++      .each($.proxy(function (i, element) {
++        var $element = $(element)
++        this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element)
++      }, this))
++      .end()
++  }
++
++  Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) {
++    var isOpen = $element.hasClass('in')
++
++    $element.attr('aria-expanded', isOpen)
++    $trigger
++      .toggleClass('collapsed', !isOpen)
++      .attr('aria-expanded', isOpen)
++  }
++
++  function getTargetFromTrigger($trigger) {
++    var href
++    var target = $trigger.attr('data-target')
++      || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
++
++    return $(target)
++  }
++
++
++  // COLLAPSE PLUGIN DEFINITION
++  // ==========================
++
++  function Plugin(option) {
++    return this.each(function () {
++      var $this   = $(this)
++      var data    = $this.data('bs.collapse')
++      var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)
++
++      if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false
++      if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
++      if (typeof option == 'string') data[option]()
++    })
++  }
++
++  var old = $.fn.collapse
++
++  $.fn.collapse             = Plugin
++  $.fn.collapse.Constructor = Collapse
++
++
++  // COLLAPSE NO CONFLICT
++  // ====================
++
++  $.fn.collapse.noConflict = function () {
++    $.fn.collapse = old
++    return this
++  }
++
++
++  // COLLAPSE DATA-API
++  // =================
++
++  $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) {
++    var $this   = $(this)
++
++    if (!$this.attr('data-target')) e.preventDefault()
++
++    var $target = getTargetFromTrigger($this)
++    var data    = $target.data('bs.collapse')
++    var option  = data ? 'toggle' : $this.data()
++
++    Plugin.call($target, option)
++  })
++
++}(jQuery);
++
++/* ========================================================================
++ * Bootstrap: dropdown.js v3.3.7
++ * http://getbootstrap.com/javascript/#dropdowns
++ * ========================================================================
++ * Copyright 2011-2016 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
++ * ======================================================================== */
++
++
+++function ($) {
++  'use strict';
++
++  // DROPDOWN CLASS DEFINITION
++  // =========================
++
++  var backdrop = '.dropdown-backdrop'
++  var toggle   = '[data-toggle="dropdown"]'
++  var Dropdown = function (element) {
++    $(element).on('click.bs.dropdown', this.toggle)
++  }
++
++  Dropdown.VERSION = '3.3.7'
++
++  function getParent($this) {
++    var selector = $this.attr('data-target')
++
++    if (!selector) {
++      selector = $this.attr('href')
++      selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
++    }
++
++    var $parent = selector && $(selector)
++
++    return $parent && $parent.length ? $parent : $this.parent()
++  }
++
++  function clearMenus(e) {
++    if (e && e.which === 3) return
++    $(backdrop).remove()
++    $(toggle).each(function () {
++      var $this         = $(this)
++      var $parent       = getParent($this)
++      var relatedTarget = { relatedTarget: this }
++
++      if (!$parent.hasClass('open')) return
++
++      if (e && e.type == 'click' && /input|textarea/i.test(e.target.tagName) && $.contains($parent[0], e.target)) return
++
++      $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
++
++      if (e.isDefaultPrevented()) return
++
++      $this.attr('aria-expanded', 'false')
++      $parent.removeClass('open').trigger($.Event('hidden.bs.dropdown', relatedTarget))
++    })
++  }
++
++  Dropdown.prototype.toggle = function (e) {
++    var $this = $(this)
++
++    if ($this.is('.disabled, :disabled')) return
++
++    var $parent  = getParent($this)
++    var isActive = $parent.hasClass('open')
++
++    clearMenus()
++
++    if (!isActive) {
++      if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
++        // if mobile we use a backdrop because click events don't delegate
++        $(document.createElement('div'))
++          .addClass('dropdown-backdrop')
++          .insertAfter($(this))
++          .on('click', clearMenus)
++      }
++
++      var relatedTarget = { relatedTarget: this }
++      $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget))
++
++      if (e.isDefaultPrevented()) return
++
++      $this
++        .trigger('focus')
++        .attr('aria-expanded', 'true')
++
++      $parent
++        .toggleClass('open')
++        .trigger($.Event('shown.bs.dropdown', relatedTarget))
++    }
++
++    return false
++  }
++
++  Dropdown.prototype.keydown = function (e) {
++    if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return
++
++    var $this = $(this)
++
++    e.preventDefault()
++    e.stopPropagation()
++
++    if ($this.is('.disabled, :disabled')) return
++
++    var $parent  = getParent($this)
++    var isActive = $parent.hasClass('open')
++
++    if (!isActive && e.which != 27 || isActive && e.which == 27) {
++      if (e.which == 27) $parent.find(toggle).trigger('focus')
++      return $this.trigger('click')
++    }
++
++    var desc = ' li:not(.disabled):visible a'
++    var $items = $parent.find('.dropdown-menu' + desc)
++
++    if (!$items.length) return
++
++    var index = $items.index(e.target)
++
++    if (e.which == 38 && index > 0)                 index--         // up
++    if (e.which == 40 && index < $items.length - 1) index++         // down
++    if (!~index)                                    index = 0
++
++    $items.eq(index).trigger('focus')
++  }
++
++
++  // DROPDOWN PLUGIN DEFINITION
++  // ==========================
++
++  function Plugin(option) {
++    return this.each(function () {
++      var $this = $(this)
++      var data  = $this.data('bs.dropdown')
++
++      if (!data) $this.data('bs.dropdown', (data = new Dropdown(this)))
++      if (typeof option == 'string') data[option].call($this)
++    })
++  }
++
++  var old = $.fn.dropdown
++
++  $.fn.dropdown             = Plugin
++  $.fn.dropdown.Constructor = Dropdown
++
++
++  // DROPDOWN NO CONFLICT
++  // ====================
++
++  $.fn.dropdown.noConflict = function () {
++    $.fn.dropdown = old
++    return this
++  }
++
++
++  // APPLY TO STANDARD DROPDOWN ELEMENTS
++  // ===================================
++
++  $(document)
++    .on('click.bs.dropdown.data-api', clearMenus)
++    .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
++    .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle)
++    .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown)
++    .on('keydown.bs.dropdown.data-api', '.dropdown-menu', Dropdown.prototype.keydown)
++
++}(jQuery);
++
++/* ========================================================================
++ * Bootstrap: modal.js v3.3.7
++ * http://getbootstrap.com/javascript/#modals
++ * ========================================================================
++ * Copyright 2011-2016 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
++ * ======================================================================== */
++
++
+++function ($) {
++  'use strict';
++
++  // MODAL CLASS DEFINITION
++  // ======================
++
++  var Modal = function (element, options) {
++    this.options             = options
++    this.$body               = $(document.body)
++    this.$element            = $(element)
++    this.$dialog             = this.$element.find('.modal-dialog')
++    this.$backdrop           = null
++    this.isShown             = null
++    this.originalBodyPad     = null
++    this.scrollbarWidth      = 0
++    this.ignoreBackdropClick = false
++
++    if (this.options.remote) {
++      this.$element
++        .find('.modal-content')
++        .load(this.options.remote, $.proxy(function () {
++          this.$element.trigger('loaded.bs.modal')
++        }, this))
++    }
++  }
++
++  Modal.VERSION  = '3.3.7'
++
++  Modal.TRANSITION_DURATION = 300
++  Modal.BACKDROP_TRANSITION_DURATION = 150
++
++  Modal.DEFAULTS = {
++    backdrop: true,
++    keyboard: true,
++    show: true
++  }
++
++  Modal.prototype.toggle = function (_relatedTarget) {
++    return this.isShown ? this.hide() : this.show(_relatedTarget)
++  }
++
++  Modal.prototype.show = function (_relatedTarget) {
++    var that = this
++    var e    = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
++
++    this.$element.trigger(e)
++
++    if (this.isShown || e.isDefaultPrevented()) return
++
++    this.isShown = true
++
++    this.checkScrollbar()
++    this.setScrollbar()
++    this.$body.addClass('modal-open')
++
++    this.escape()
++    this.resize()
++
++    this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
++
++    this.$dialog.on('mousedown.dismiss.bs.modal', function () {
++      that.$element.one('mouseup.dismiss.bs.modal', function (e) {
++        if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true
++      })
++    })
++
++    this.backdrop(function () {
++      var transition = $.support.transition && that.$element.hasClass('fade')
++
++      if (!that.$element.parent().length) {
++        that.$element.appendTo(that.$body) // don't move modals dom position
++      }
++
++      that.$element
++        .show()
++        .scrollTop(0)
++
++      that.adjustDialog()
++
++      if (transition) {
++        that.$element[0].offsetWidth // force reflow
++      }
++
++      that.$element.addClass('in')
++
++      that.enforceFocus()
++
++      var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
++
++      transition ?
++        that.$dialog // wait for modal to slide in
++          .one('bsTransitionEnd', function () {
++            that.$element.trigger('focus').trigger(e)
++          })
++          .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
++        that.$element.trigger('focus').trigger(e)
++    })
++  }
++
++  Modal.prototype.hide = function (e) {
++    if (e) e.preventDefault()
++
++    e = $.Event('hide.bs.modal')
++
++    this.$element.trigger(e)
++
++    if (!this.isShown || e.isDefaultPrevented()) return
++
++    this.isShown = false
++
++    this.escape()
++    this.resize()
++
++    $(document).off('focusin.bs.modal')
++
++    this.$element
++      .removeClass('in')
++      .off('click.dismiss.bs.modal')
++      .off('mouseup.dismiss.bs.modal')
++
++    this.$dialog.off('mousedown.dismiss.bs.modal')
++
++    $.support.transition && this.$element.hasClass('fade') ?
++      this.$element
++        .one('bsTransitionEnd', $.proxy(this.hideModal, this))
++        .emulateTransitionEnd(Modal.TRANSITION_DURATION) :
++      this.hideModal()
++  }
++
++  Modal.prototype.enforceFocus = function () {
++    $(document)
++      .off('focusin.bs.modal') // guard against infinite focus loop
++      .on('focusin.bs.modal', $.proxy(function (e) {
++        if (document !== e.target &&
++            this.$element[0] !== e.target &&
++            !this.$element.has(e.target).length) {
++          this.$element.trigger('focus')
++        }
++      }, this))
++  }
++
++  Modal.prototype.escape = function () {
++    if (this.isShown && this.options.keyboard) {
++      this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) {
++        e.which == 27 && this.hide()
++      }, this))
++    } else if (!this.isShown) {
++      this.$element.off('keydown.dismiss.bs.modal')
++    }
++  }
++
++  Modal.prototype.resize = function () {
++    if (this.isShown) {
++      $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this))
++    } else {
++      $(window).off('resize.bs.modal')
++    }
++  }
++
++  Modal.prototype.hideModal = function () {
++    var that = this
++    this.$element.hide()
++    this.backdrop(function () {
++      that.$body.removeClass('modal-open')
++      that.resetAdjustments()
++      that.resetScrollbar()
++      that.$element.trigger('hidden.bs.modal')
++    })
++  }
++
++  Modal.prototype.removeBackdrop = function () {
++    this.$backdrop && this.$backdrop.remove()
++    this.$backdrop = null
++  }
++
++  Modal.prototype.backdrop = function (callback) {
++    var that = this
++    var animate = this.$element.hasClass('fade') ? 'fade' : ''
++
++    if (this.isShown && this.options.backdrop) {
++      var doAnimate = $.support.transition && animate
++
++      this.$backdrop = $(document.createElement('div'))
++        .addClass('modal-backdrop ' + animate)
++        .appendTo(this.$body)
++
++      this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) {
++        if (this.ignoreBackdropClick) {
++          this.ignoreBackdropClick = false
++          return
++        }
++        if (e.target !== e.currentTarget) return
++        this.options.backdrop == 'static'
++          ? this.$element[0].focus()
++          : this.hide()
++      }, this))
++
++      if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
++
++      this.$backdrop.addClass('in')
++
++      if (!callback) return
++
++      doAnimate ?
++        this.$backdrop
++          .one('bsTransitionEnd', callback)
++          .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
++        callback()
++
++    } else if (!this.isShown && this.$backdrop) {
++      this.$backdrop.removeClass('in')
++
++      var callbackRemove = function () {
++        that.removeBackdrop()
++        callback && callback()
++      }
++      $.support.transition && this.$element.hasClass('fade') ?
++        this.$backdrop
++          .one('bsTransitionEnd', callbackRemove)
++          .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
++        callbackRemove()
++
++    } else if (callback) {
++      callback()
++    }
++  }
++
++  // these following methods are used to handle overflowing modals
++
++  Modal.prototype.handleUpdate = function () {
++    this.adjustDialog()
++  }
++
++  Modal.prototype.adjustDialog = function () {
++    var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight
++
++    this.$element.css({
++      paddingLeft:  !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '',
++      paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : ''
++    })
++  }
++
++  Modal.prototype.resetAdjustments = function () {
++    this.$element.css({
++      paddingLeft: '',
++      paddingRight: ''
++    })
++  }
++
++  Modal.prototype.checkScrollbar = function () {
++    var fullWindowWidth = window.innerWidth
++    if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8
++      var documentElementRect = document.documentElement.getBoundingClientRect()
++      fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left)
++    }
++    this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth
++    this.scrollbarWidth = this.measureScrollbar()
++  }
++
++  Modal.prototype.setScrollbar = function () {
++    var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10)
++    this.originalBodyPad = document.body.style.paddingRight || ''
++    if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth)
++  }
++
++  Modal.prototype.resetScrollbar = function () {
++    this.$body.css('padding-right', this.originalBodyPad)
++  }
++
++  Modal.prototype.measureScrollbar = function () { // thx walsh
++    var scrollDiv = document.createElement('div')
++    scrollDiv.className = 'modal-scrollbar-measure'
++    this.$body.append(scrollDiv)
++    var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth
++    this.$body[0].removeChild(scrollDiv)
++    return scrollbarWidth
++  }
++
++
++  // MODAL PLUGIN DEFINITION
++  // =======================
++
++  function Plugin(option, _relatedTarget) {
++    return this.each(function () {
++      var $this   = $(this)
++      var data    = $this.data('bs.modal')
++      var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
++
++      if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
++      if (typeof option == 'string') data[option](_relatedTarget)
++      else if (options.show) data.show(_relatedTarget)
++    })
++  }
++
++  var old = $.fn.modal
++
++  $.fn.modal             = Plugin
++  $.fn.modal.Constructor = Modal
++
++
++  // MODAL NO CONFLICT
++  // =================
++
++  $.fn.modal.noConflict = function () {
++    $.fn.modal = old
++    return this
++  }
++
++
++  // MODAL DATA-API
++  // ==============
++
++  $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
++    var $this   = $(this)
++    var href    = $this.attr('href')
++    var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7
++    var option  = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
++
++    if ($this.is('a')) e.preventDefault()
++
++    $target.one('show.bs.modal', function (showEvent) {
++      if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown
++      $target.one('hidden.bs.modal', function () {
++        $this.is(':visible') && $this.trigger('focus')
++      })
++    })
++    Plugin.call($target, option, this)
++  })
++
++}(jQuery);
++
++/* ========================================================================
++ * Bootstrap: tooltip.js v3.3.7
++ * http://getbootstrap.com/javascript/#tooltip
++ * Inspired by the original jQuery.tipsy by Jason Frame
++ * ========================================================================
++ * Copyright 2011-2016 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
++ * ======================================================================== */
++
++
+++function ($) {
++  'use strict';
++
++  // TOOLTIP PUBLIC CLASS DEFINITION
++  // ===============================
++
++  var Tooltip = function (element, options) {
++    this.type       = null
++    this.options    = null
++    this.enabled    = null
++    this.timeout    = null
++    this.hoverState = null
++    this.$element   = null
++    this.inState    = null
++
++    this.init('tooltip', element, options)
++  }
++
++  Tooltip.VERSION  = '3.3.7'
++
++  Tooltip.TRANSITION_DURATION = 150
++
++  Tooltip.DEFAULTS = {
++    animation: true,
++    placement: 'top',
++    selector: false,
++    template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
++    trigger: 'hover focus',
++    title: '',
++    delay: 0,
++    html: false,
++    container: false,
++    viewport: {
++      selector: 'body',
++      padding: 0
++    }
++  }
++
++  Tooltip.prototype.init = function (type, element, options) {
++    this.enabled   = true
++    this.type      = type
++    this.$element  = $(element)
++    this.options   = this.getOptions(options)
++    this.$viewport = this.options.viewport && $($.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : (this.options.viewport.selector || this.options.viewport))
++    this.inState   = { click: false, hover: false, focus: false }
++
++    if (this.$element[0] instanceof document.constructor && !this.options.selector) {
++      throw new Error('`selector` option must be specified when initializing ' + this.type + ' on the window.document object!')
++    }
++
++    var triggers = this.options.trigger.split(' ')
++
++    for (var i = triggers.length; i--;) {
++      var trigger = triggers[i]
++
++      if (trigger == 'click') {
++        this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
++      } else if (trigger != 'manual') {
++        var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
++        var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
++
++        this.$element.on(eventIn  + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
++        this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
++      }
++    }
++
++    this.options.selector ?
++      (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
++      this.fixTitle()
++  }
++
++  Tooltip.prototype.getDefaults = function () {
++    return Tooltip.DEFAULTS
++  }
++
++  Tooltip.prototype.getOptions = function (options) {
++    options = $.extend({}, this.getDefaults(), this.$element.data(), options)
++
++    if (options.delay && typeof options.delay == 'number') {
++      options.delay = {
++        show: options.delay,
++        hide: options.delay
++      }
++    }
++
++    return options
++  }
++
++  Tooltip.prototype.getDelegateOptions = function () {
++    var options  = {}
++    var defaults = this.getDefaults()
++
++    this._options && $.each(this._options, function (key, value) {
++      if (defaults[key] != value) options[key] = value
++    })
++
++    return options
++  }
++
++  Tooltip.prototype.enter = function (obj) {
++    var self = obj instanceof this.constructor ?
++      obj : $(obj.currentTarget).data('bs.' + this.type)
++
++    if (!self) {
++      self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
++      $(obj.currentTarget).data('bs.' + this.type, self)
++    }
++
++    if (obj instanceof $.Event) {
++      self.inState[obj.type == 'focusin' ? 'focus' : 'hover'] = true
++    }
++
++    if (self.tip().hasClass('in') || self.hoverState == 'in') {
++      self.hoverState = 'in'
++      return
++    }
++
++    clearTimeout(self.timeout)
++
++    self.hoverState = 'in'
++
++    if (!self.options.delay || !self.options.delay.show) return self.show()
++
++    self.timeout = setTimeout(function () {
++      if (self.hoverState == 'in') self.show()
++    }, self.options.delay.show)
++  }
++
++  Tooltip.prototype.isInStateTrue = function () {
++    for (var key in this.inState) {
++      if (this.inState[key]) return true
++    }
++
++    return false
++  }
++
++  Tooltip.prototype.leave = function (obj) {
++    var self = obj instanceof this.constructor ?
++      obj : $(obj.currentTarget).data('bs.' + this.type)
++
++    if (!self) {
++      self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
++      $(obj.currentTarget).data('bs.' + this.type, self)
++    }
++
++    if (obj instanceof $.Event) {
++      self.inState[obj.type == 'focusout' ? 'focus' : 'hover'] = false
++    }
++
++    if (self.isInStateTrue()) return
++
++    clearTimeout(self.timeout)
++
++    self.hoverState = 'out'
++
++    if (!self.options.delay || !self.options.delay.hide) return self.hide()
++
++    self.timeout = setTimeout(function () {
++      if (self.hoverState == 'out') self.hide()
++    }, self.options.delay.hide)
++  }
++
++  Tooltip.prototype.show = function () {
++    var e = $.Event('show.bs.' + this.type)
++
++    if (this.hasContent() && this.enabled) {
++      this.$element.trigger(e)
++
++      var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0])
++      if (e.isDefaultPrevented() || !inDom) return
++      var that = this
++
++      var $tip = this.tip()
++
++      var tipId = this.getUID(this.type)
++
++      this.setContent()
++      $tip.attr('id', tipId)
++      this.$element.attr('aria-describedby', tipId)
++
++      if (this.options.animation) $tip.addClass('fade')
++
++      var placement = typeof this.options.placement == 'function' ?
++        this.options.placement.call(this, $tip[0], this.$element[0]) :
++        this.options.placement
++
++      var autoToken = /\s?auto?\s?/i
++      var autoPlace = autoToken.test(placement)
++      if (autoPlace) placement = placement.replace(autoToken, '') || 'top'
++
++      $tip
++        .detach()
++        .css({ top: 0, left: 0, display: 'block' })
++        .addClass(placement)
++        .data('bs.' + this.type, this)
++
++      this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
++      this.$element.trigger('inserted.bs.' + this.type)
++
++      var pos          = this.getPosition()
++      var actualWidth  = $tip[0].offsetWidth
++      var actualHeight = $tip[0].offsetHeight
++
++      if (autoPlace) {
++        var orgPlacement = placement
++        var viewportDim = this.getPosition(this.$viewport)
++
++        placement = placement == 'bottom' && pos.bottom + actualHeight > viewportDim.bottom ? 'top'    :
++                    placement == 'top'    && pos.top    - actualHeight < viewportDim.top    ? 'bottom' :
++                    placement == 'right'  && pos.right  + actualWidth  > viewportDim.width  ? 'left'   :
++                    placement == 'left'   && pos.left   - actualWidth  < viewportDim.left   ? 'right'  :
++                    placement
++
++        $tip
++          .removeClass(orgPlacement)
++          .addClass(placement)
++      }
++
++      var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
++
++      this.applyPlacement(calculatedOffset, placement)
++
++      var complete = function () {
++        var prevHoverState = that.hoverState
++        that.$element.trigger('shown.bs.' + that.type)
++        that.hoverState = null
++
++        if (prevHoverState == 'out') that.leave(that)
++      }
++
++      $.support.transition && this.$tip.hasClass('fade') ?
++        $tip
++          .one('bsTransitionEnd', complete)
++          .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
++        complete()
++    }
++  }
++
++  Tooltip.prototype.applyPlacement = function (offset, placement) {
++    var $tip   = this.tip()
++    var width  = $tip[0].offsetWidth
++    var height = $tip[0].offsetHeight
++
++    // manually read margins because getBoundingClientRect includes difference
++    var marginTop = parseInt($tip.css('margin-top'), 10)
++    var marginLeft = parseInt($tip.css('margin-left'), 10)
++
++    // we must check for NaN for ie 8/9
++    if (isNaN(marginTop))  marginTop  = 0
++    if (isNaN(marginLeft)) marginLeft = 0
++
++    offset.top  += marginTop
++    offset.left += marginLeft
++
++    // $.fn.offset doesn't round pixel values
++    // so we use setOffset directly with our own function B-0
++    $.offset.setOffset($tip[0], $.extend({
++      using: function (props) {
++        $tip.css({
++          top: Math.round(props.top),
++          left: Math.round(props.left)
++        })
++      }
++    }, offset), 0)
++
++    $tip.addClass('in')
++
++    // check to see if placing tip in new offset caused the tip to resize itself
++    var actualWidth  = $tip[0].offsetWidth
++    var actualHeight = $tip[0].offsetHeight
++
++    if (placement == 'top' && actualHeight != height) {
++      offset.top = offset.top + height - actualHeight
++    }
++
++    var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)
++
++    if (delta.left) offset.left += delta.left
++    else offset.top += delta.top
++
++    var isVertical          = /top|bottom/.test(placement)
++    var arrowDelta          = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
++    var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight'
++
++    $tip.offset(offset)
++    this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical)
++  }
++
++  Tooltip.prototype.replaceArrow = function (delta, dimension, isVertical) {
++    this.arrow()
++      .css(isVertical ? 'left' : 'top', 50 * (1 - delta / dimension) + '%')
++      .css(isVertical ? 'top' : 'left', '')
++  }
++
++  Tooltip.prototype.setContent = function () {
++    var $tip  = this.tip()
++    var title = this.getTitle()
++
++    $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
++    $tip.removeClass('fade in top bottom left right')
++  }
++
++  Tooltip.prototype.hide = function (callback) {
++    var that = this
++    var $tip = $(this.$tip)
++    var e    = $.Event('hide.bs.' + this.type)
++
++    function complete() {
++      if (that.hoverState != 'in') $tip.detach()
++      if (that.$element) { // TODO: Check whether guarding this code with this `if` is really necessary.
++        that.$element
++          .removeAttr('aria-describedby')
++          .trigger('hidden.bs.' + that.type)
++      }
++      callback && callback()
++    }
++
++    this.$element.trigger(e)
++
++    if (e.isDefaultPrevented()) return
++
++    $tip.removeClass('in')
++
++    $.support.transition && $tip.hasClass('fade') ?
++      $tip
++        .one('bsTransitionEnd', complete)
++        .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) :
++      complete()
++
++    this.hoverState = null
++
++    return this
++  }
++
++  Tooltip.prototype.fixTitle = function () {
++    var $e = this.$element
++    if ($e.attr('title') || typeof $e.attr('data-original-title') != 'string') {
++      $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
++    }
++  }
++
++  Tooltip.prototype.hasContent = function () {
++    return this.getTitle()
++  }
++
++  Tooltip.prototype.getPosition = function ($element) {
++    $element   = $element || this.$element
++
++    var el     = $element[0]
++    var isBody = el.tagName == 'BODY'
++
++    var elRect    = el.getBoundingClientRect()
++    if (elRect.width == null) {
++      // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093
++      elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top })
++    }
++    var isSvg = window.SVGElement && el instanceof window.SVGElement
++    // Avoid using $.offset() on SVGs since it gives incorrect results in jQuery 3.
++    // See https://github.com/twbs/bootstrap/issues/20280
++    var elOffset  = isBody ? { top: 0, left: 0 } : (isSvg ? null : $element.offset())
++    var scroll    = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() }
++    var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null
++
++    return $.extend({}, elRect, scroll, outerDims, elOffset)
++  }
++
++  Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
++    return placement == 'bottom' ? { top: pos.top + pos.height,   left: pos.left + pos.width / 2 - actualWidth / 2 } :
++           placement == 'top'    ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } :
++           placement == 'left'   ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
++        /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width }
++
++  }
++
++  Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) {
++    var delta = { top: 0, left: 0 }
++    if (!this.$viewport) return delta
++
++    var viewportPadding = this.options.viewport && this.options.viewport.padding || 0
++    var viewportDimensions = this.getPosition(this.$viewport)
++
++    if (/right|left/.test(placement)) {
++      var topEdgeOffset    = pos.top - viewportPadding - viewportDimensions.scroll
++      var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight
++      if (topEdgeOffset < viewportDimensions.top) { // top overflow
++        delta.top = viewportDimensions.top - topEdgeOffset
++      } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
++        delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset
++      }
++    } else {
++      var leftEdgeOffset  = pos.left - viewportPadding
++      var rightEdgeOffset = pos.left + viewportPadding + actualWidth
++      if (leftEdgeOffset < viewportDimensions.left) { // left overflow
++        delta.left = viewportDimensions.left - leftEdgeOffset
++      } else if (rightEdgeOffset > viewportDimensions.right) { // right overflow
++        delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
++      }
++    }
++
++    return delta
++  }
++
++  Tooltip.prototype.getTitle = function () {
++    var title
++    var $e = this.$element
++    var o  = this.options
++
++    title = $e.attr('data-original-title')
++      || (typeof o.title == 'function' ? o.title.call($e[0]) :  o.title)
++
++    return title
++  }
++
++  Tooltip.prototype.getUID = function (prefix) {
++    do prefix += ~~(Math.random() * 1000000)
++    while (document.getElementById(prefix))
++    return prefix
++  }
++
++  Tooltip.prototype.tip = function () {
++    if (!this.$tip) {
++      this.$tip = $(this.options.template)
++      if (this.$tip.length != 1) {
++        throw new Error(this.type + ' `template` option must consist of exactly 1 top-level element!')
++      }
++    }
++    return this.$tip
++  }
++
++  Tooltip.prototype.arrow = function () {
++    return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow'))
++  }
++
++  Tooltip.prototype.enable = function () {
++    this.enabled = true
++  }
++
++  Tooltip.prototype.disable = function () {
++    this.enabled = false
++  }
++
++  Tooltip.prototype.toggleEnabled = function () {
++    this.enabled = !this.enabled
++  }
++
++  Tooltip.prototype.toggle = function (e) {
++    var self = this
++    if (e) {
++      self = $(e.currentTarget).data('bs.' + this.type)
++      if (!self) {
++        self = new this.constructor(e.currentTarget, this.getDelegateOptions())
++        $(e.currentTarget).data('bs.' + this.type, self)
++      }
++    }
++
++    if (e) {
++      self.inState.click = !self.inState.click
++      if (self.isInStateTrue()) self.enter(self)
++      else self.leave(self)
++    } else {
++      self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
++    }
++  }
++
++  Tooltip.prototype.destroy = function () {
++    var that = this
++    clearTimeout(this.timeout)
++    this.hide(function () {
++      that.$element.off('.' + that.type).removeData('bs.' + that.type)
++      if (that.$tip) {
++        that.$tip.detach()
++      }
++      that.$tip = null
++      that.$arrow = null
++      that.$viewport = null
++      that.$element = null
++    })
++  }
++
++
++  // TOOLTIP PLUGIN DEFINITION
++  // =========================
++
++  function Plugin(option) {
++    return this.each(function () {
++      var $this   = $(this)
++      var data    = $this.data('bs.tooltip')
++      var options = typeof option == 'object' && option
++
++      if (!data && /destroy|hide/.test(option)) return
++      if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
++      if (typeof option == 'string') data[option]()
++    })
++  }
++
++  var old = $.fn.tooltip
++
++  $.fn.tooltip             = Plugin
++  $.fn.tooltip.Constructor = Tooltip
++
++
++  // TOOLTIP NO CONFLICT
++  // ===================
++
++  $.fn.tooltip.noConflict = function () {
++    $.fn.tooltip = old
++    return this
++  }
++
++}(jQuery);
++
++/* ========================================================================
++ * Bootstrap: popover.js v3.3.7
++ * http://getbootstrap.com/javascript/#popovers
++ * ========================================================================
++ * Copyright 2011-2016 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
++ * ======================================================================== */
++
++
+++function ($) {
++  'use strict';
++
++  // POPOVER PUBLIC CLASS DEFINITION
++  // ===============================
++
++  var Popover = function (element, options) {
++    this.init('popover', element, options)
++  }
++
++  if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
++
++  Popover.VERSION  = '3.3.7'
++
++  Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, {
++    placement: 'right',
++    trigger: 'click',
++    content: '',
++    template: '<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
++  })
++
++
++  // NOTE: POPOVER EXTENDS tooltip.js
++  // ================================
++
++  Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype)
++
++  Popover.prototype.constructor = Popover
++
++  Popover.prototype.getDefaults = function () {
++    return Popover.DEFAULTS
++  }
++
++  Popover.prototype.setContent = function () {
++    var $tip    = this.tip()
++    var title   = this.getTitle()
++    var content = this.getContent()
++
++    $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
++    $tip.find('.popover-content').children().detach().end()[ // we use append for html objects to maintain js events
++      this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text'
++    ](content)
++
++    $tip.removeClass('fade top bottom left right in')
++
++    // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do
++    // this manually by checking the contents.
++    if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide()
++  }
++
++  Popover.prototype.hasContent = function () {
++    return this.getTitle() || this.getContent()
++  }
++
++  Popover.prototype.getContent = function () {
++    var $e = this.$element
++    var o  = this.options
++
++    return $e.attr('data-content')
++      || (typeof o.content == 'function' ?
++            o.content.call($e[0]) :
++            o.content)
++  }
++
++  Popover.prototype.arrow = function () {
++    return (this.$arrow = this.$arrow || this.tip().find('.arrow'))
++  }
++
++
++  // POPOVER PLUGIN DEFINITION
++  // =========================
++
++  function Plugin(option) {
++    return this.each(function () {
++      var $this   = $(this)
++      var data    = $this.data('bs.popover')
++      var options = typeof option == 'object' && option
++
++      if (!data && /destroy|hide/.test(option)) return
++      if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
++      if (typeof option == 'string') data[option]()
++    })
++  }
++
++  var old = $.fn.popover
++
++  $.fn.popover             = Plugin
++  $.fn.popover.Constructor = Popover
++
++
++  // POPOVER NO CONFLICT
++  // ===================
++
++  $.fn.popover.noConflict = function () {
++    $.fn.popover = old
++    return this
++  }
++
++}(jQuery);
++
++/* ========================================================================
++ * Bootstrap: scrollspy.js v3.3.7
++ * http://getbootstrap.com/javascript/#scrollspy
++ * ========================================================================
++ * Copyright 2011-2016 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
++ * ======================================================================== */
++
++
+++function ($) {
++  'use strict';
++
++  // SCROLLSPY CLASS DEFINITION
++  // ==========================
++
++  function ScrollSpy(element, options) {
++    this.$body          = $(document.body)
++    this.$scrollElement = $(element).is(document.body) ? $(window) : $(element)
++    this.options        = $.extend({}, ScrollSpy.DEFAULTS, options)
++    this.selector       = (this.options.target || '') + ' .nav li > a'
++    this.offsets        = []
++    this.targets        = []
++    this.activeTarget   = null
++    this.scrollHeight   = 0
++
++    this.$scrollElement.on('scroll.bs.scrollspy', $.proxy(this.process, this))
++    this.refresh()
++    this.process()
++  }
++
++  ScrollSpy.VERSION  = '3.3.7'
++
++  ScrollSpy.DEFAULTS = {
++    offset: 10
++  }
++
++  ScrollSpy.prototype.getScrollHeight = function () {
++    return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight)
++  }
++
++  ScrollSpy.prototype.refresh = function () {
++    var that          = this
++    var offsetMethod  = 'offset'
++    var offsetBase    = 0
++
++    this.offsets      = []
++    this.targets      = []
++    this.scrollHeight = this.getScrollHeight()
++
++    if (!$.isWindow(this.$scrollElement[0])) {
++      offsetMethod = 'position'
++      offsetBase   = this.$scrollElement.scrollTop()
++    }
++
++    this.$body
++      .find(this.selector)
++      .map(function () {
++        var $el   = $(this)
++        var href  = $el.data('target') || $el.attr('href')
++        var $href = /^#./.test(href) && $(href)
++
++        return ($href
++          && $href.length
++          && $href.is(':visible')
++          && [[$href[offsetMethod]().top + offsetBase, href]]) || null
++      })
++      .sort(function (a, b) { return a[0] - b[0] })
++      .each(function () {
++        that.offsets.push(this[0])
++        that.targets.push(this[1])
++      })
++  }
++
++  ScrollSpy.prototype.process = function () {
++    var scrollTop    = this.$scrollElement.scrollTop() + this.options.offset
++    var scrollHeight = this.getScrollHeight()
++    var maxScroll    = this.options.offset + scrollHeight - this.$scrollElement.height()
++    var offsets      = this.offsets
++    var targets      = this.targets
++    var activeTarget = this.activeTarget
++    var i
++
++    if (this.scrollHeight != scrollHeight) {
++      this.refresh()
++    }
++
++    if (scrollTop >= maxScroll) {
++      return activeTarget != (i = targets[targets.length - 1]) && this.activate(i)
++    }
++
++    if (activeTarget && scrollTop < offsets[0]) {
++      this.activeTarget = null
++      return this.clear()
++    }
++
++    for (i = offsets.length; i--;) {
++      activeTarget != targets[i]
++        && scrollTop >= offsets[i]
++        && (offsets[i + 1] === undefined || scrollTop < offsets[i + 1])
++        && this.activate(targets[i])
++    }
++  }
++
++  ScrollSpy.prototype.activate = function (target) {
++    this.activeTarget = target
++
++    this.clear()
++
++    var selector = this.selector +
++      '[data-target="' + target + '"],' +
++      this.selector + '[href="' + target + '"]'
++
++    var active = $(selector)
++      .parents('li')
++      .addClass('active')
++
++    if (active.parent('.dropdown-menu').length) {
++      active = active
++        .closest('li.dropdown')
++        .addClass('active')
++    }
++
++    active.trigger('activate.bs.scrollspy')
++  }
++
++  ScrollSpy.prototype.clear = function () {
++    $(this.selector)
++      .parentsUntil(this.options.target, '.active')
++      .removeClass('active')
++  }
++
++
++  // SCROLLSPY PLUGIN DEFINITION
++  // ===========================
++
++  function Plugin(option) {
++    return this.each(function () {
++      var $this   = $(this)
++      var data    = $this.data('bs.scrollspy')
++      var options = typeof option == 'object' && option
++
++      if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options)))
++      if (typeof option == 'string') data[option]()
++    })
++  }
++
++  var old = $.fn.scrollspy
++
++  $.fn.scrollspy             = Plugin
++  $.fn.scrollspy.Constructor = ScrollSpy
++
++
++  // SCROLLSPY NO CONFLICT
++  // =====================
++
++  $.fn.scrollspy.noConflict = function () {
++    $.fn.scrollspy = old
++    return this
++  }
++
++
++  // SCROLLSPY DATA-API
++  // ==================
++
++  $(window).on('load.bs.scrollspy.data-api', function () {
++    $('[data-spy="scroll"]').each(function () {
++      var $spy = $(this)
++      Plugin.call($spy, $spy.data())
++    })
++  })
++
++}(jQuery);
++
++/* ========================================================================
++ * Bootstrap: tab.js v3.3.7
++ * http://getbootstrap.com/javascript/#tabs
++ * ========================================================================
++ * Copyright 2011-2016 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
++ * ======================================================================== */
++
++
+++function ($) {
++  'use strict';
++
++  // TAB CLASS DEFINITION
++  // ====================
++
++  var Tab = function (element) {
++    // jscs:disable requireDollarBeforejQueryAssignment
++    this.element = $(element)
++    // jscs:enable requireDollarBeforejQueryAssignment
++  }
++
++  Tab.VERSION = '3.3.7'
++
++  Tab.TRANSITION_DURATION = 150
++
++  Tab.prototype.show = function () {
++    var $this    = this.element
++    var $ul      = $this.closest('ul:not(.dropdown-menu)')
++    var selector = $this.data('target')
++
++    if (!selector) {
++      selector = $this.attr('href')
++      selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
++    }
++
++    if ($this.parent('li').hasClass('active')) return
++
++    var $previous = $ul.find('.active:last a')
++    var hideEvent = $.Event('hide.bs.tab', {
++      relatedTarget: $this[0]
++    })
++    var showEvent = $.Event('show.bs.tab', {
++      relatedTarget: $previous[0]
++    })
++
++    $previous.trigger(hideEvent)
++    $this.trigger(showEvent)
++
++    if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return
++
++    var $target = $(selector)
++
++    this.activate($this.closest('li'), $ul)
++    this.activate($target, $target.parent(), function () {
++      $previous.trigger({
++        type: 'hidden.bs.tab',
++        relatedTarget: $this[0]
++      })
++      $this.trigger({
++        type: 'shown.bs.tab',
++        relatedTarget: $previous[0]
++      })
++    })
++  }
++
++  Tab.prototype.activate = function (element, container, callback) {
++    var $active    = container.find('> .active')
++    var transition = callback
++      && $.support.transition
++      && ($active.length && $active.hasClass('fade') || !!container.find('> .fade').length)
++
++    function next() {
++      $active
++        .removeClass('active')
++        .find('> .dropdown-menu > .active')
++          .removeClass('active')
++        .end()
++        .find('[data-toggle="tab"]')
++          .attr('aria-expanded', false)
++
++      element
++        .addClass('active')
++        .find('[data-toggle="tab"]')
++          .attr('aria-expanded', true)
++
++      if (transition) {
++        element[0].offsetWidth // reflow for transition
++        element.addClass('in')
++      } else {
++        element.removeClass('fade')
++      }
++
++      if (element.parent('.dropdown-menu').length) {
++        element
++          .closest('li.dropdown')
++            .addClass('active')
++          .end()
++          .find('[data-toggle="tab"]')
++            .attr('aria-expanded', true)
++      }
++
++      callback && callback()
++    }
++
++    $active.length && transition ?
++      $active
++        .one('bsTransitionEnd', next)
++        .emulateTransitionEnd(Tab.TRANSITION_DURATION) :
++      next()
++
++    $active.removeClass('in')
++  }
++
++
++  // TAB PLUGIN DEFINITION
++  // =====================
++
++  function Plugin(option) {
++    return this.each(function () {
++      var $this = $(this)
++      var data  = $this.data('bs.tab')
++
++      if (!data) $this.data('bs.tab', (data = new Tab(this)))
++      if (typeof option == 'string') data[option]()
++    })
++  }
++
++  var old = $.fn.tab
++
++  $.fn.tab             = Plugin
++  $.fn.tab.Constructor = Tab
++
++
++  // TAB NO CONFLICT
++  // ===============
++
++  $.fn.tab.noConflict = function () {
++    $.fn.tab = old
++    return this
++  }
++
++
++  // TAB DATA-API
++  // ============
++
++  var clickHandler = function (e) {
++    e.preventDefault()
++    Plugin.call($(this), 'show')
++  }
++
++  $(document)
++    .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler)
++    .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler)
++
++}(jQuery);
++
++/* ========================================================================
++ * Bootstrap: affix.js v3.3.7
++ * http://getbootstrap.com/javascript/#affix
++ * ========================================================================
++ * Copyright 2011-2016 Twitter, Inc.
++ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
++ * ======================================================================== */
++
++
+++function ($) {
++  'use strict';
++
++  // AFFIX CLASS DEFINITION
++  // ======================
++
++  var Affix = function (element, options) {
++    this.options = $.extend({}, Affix.DEFAULTS, options)
++
++    this.$target = $(this.options.target)
++      .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
++      .on('click.bs.affix.data-api',  $.proxy(this.checkPositionWithEventLoop, this))
++
++    this.$element     = $(element)
++    this.affixed      = null
++    this.unpin        = null
++    this.pinnedOffset = null
++
++    this.checkPosition()
++  }
++
++  Affix.VERSION  = '3.3.7'
++
++  Affix.RESET    = 'affix affix-top affix-bottom'
++
++  Affix.DEFAULTS = {
++    offset: 0,
++    target: window
++  }
++
++  Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) {
++    var scrollTop    = this.$target.scrollTop()
++    var position     = this.$element.offset()
++    var targetHeight = this.$target.height()
++
++    if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false
++
++    if (this.affixed == 'bottom') {
++      if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom'
++      return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom'
++    }
++
++    var initializing   = this.affixed == null
++    var colliderTop    = initializing ? scrollTop : position.top
++    var colliderHeight = initializing ? targetHeight : height
++
++    if (offsetTop != null && scrollTop <= offsetTop) return 'top'
++    if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom'
++
++    return false
++  }
++
++  Affix.prototype.getPinnedOffset = function () {
++    if (this.pinnedOffset) return this.pinnedOffset
++    this.$element.removeClass(Affix.RESET).addClass('affix')
++    var scrollTop = this.$target.scrollTop()
++    var position  = this.$element.offset()
++    return (this.pinnedOffset = position.top - scrollTop)
++  }
++
++  Affix.prototype.checkPositionWithEventLoop = function () {
++    setTimeout($.proxy(this.checkPosition, this), 1)
++  }
++
++  Affix.prototype.checkPosition = function () {
++    if (!this.$element.is(':visible')) return
++
++    var height       = this.$element.height()
++    var offset       = this.options.offset
++    var offsetTop    = offset.top
++    var offsetBottom = offset.bottom
++    var scrollHeight = Math.max($(document).height(), $(document.body).height())
++
++    if (typeof offset != 'object')         offsetBottom = offsetTop = offset
++    if (typeof offsetTop == 'function')    offsetTop    = offset.top(this.$element)
++    if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element)
++
++    var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom)
++
++    if (this.affixed != affix) {
++      if (this.unpin != null) this.$element.css('top', '')
++
++      var affixType = 'affix' + (affix ? '-' + affix : '')
++      var e         = $.Event(affixType + '.bs.affix')
++
++      this.$element.trigger(e)
++
++      if (e.isDefaultPrevented()) return
++
++      this.affixed = affix
++      this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null
++
++      this.$element
++        .removeClass(Affix.RESET)
++        .addClass(affixType)
++        .trigger(affixType.replace('affix', 'affixed') + '.bs.affix')
++    }
++
++    if (affix == 'bottom') {
++      this.$element.offset({
++        top: scrollHeight - height - offsetBottom
++      })
++    }
++  }
++
++
++  // AFFIX PLUGIN DEFINITION
++  // =======================
++
++  function Plugin(option) {
++    return this.each(function () {
++      var $this   = $(this)
++      var data    = $this.data('bs.affix')
++      var options = typeof option == 'object' && option
++
++      if (!data) $this.data('bs.affix', (data = new Affix(this, options)))
++      if (typeof option == 'string') data[option]()
++    })
++  }
++
++  var old = $.fn.affix
++
++  $.fn.affix             = Plugin
++  $.fn.affix.Constructor = Affix
++
++
++  // AFFIX NO CONFLICT
++  // =================
++
++  $.fn.affix.noConflict = function () {
++    $.fn.affix = old
++    return this
++  }
++
++
++  // AFFIX DATA-API
++  // ==============
++
++  $(window).on('load', function () {
++    $('[data-spy="affix"]').each(function () {
++      var $spy = $(this)
++      var data = $spy.data()
++
++      data.offset = data.offset || {}
++
++      if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom
++      if (data.offsetTop    != null) data.offset.top    = data.offsetTop
++
++      Plugin.call($spy, data)
++    })
++  })
++
++}(jQuery);
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dcdf93b9373ed6459cc5e5a4a2afc05d3ca7dd0b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9519 @@@
++/* @license C3.js v0.6.6 | (c) C3 Team and other contributors | http://c3js.org/ */
++(function (global, factory) {
++    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
++    typeof define === 'function' && define.amd ? define(factory) :
++    (global.c3 = factory());
++}(this, (function () { 'use strict';
++
++    function ChartInternal(api) {
++        var $$ = this;
++        $$.d3 = window.d3 ? window.d3 : typeof require !== 'undefined' ? require("d3") : undefined;
++        $$.api = api;
++        $$.config = $$.getDefaultConfig();
++        $$.data = {};
++        $$.cache = {};
++        $$.axes = {};
++    }
++
++    function Chart(config) {
++        var $$ = this.internal = new ChartInternal(this);
++        $$.loadConfig(config);
++
++        $$.beforeInit(config);
++        $$.init();
++        $$.afterInit(config);
++
++        // bind "this" to nested API
++        (function bindThis(fn, target, argThis) {
++            Object.keys(fn).forEach(function (key) {
++                target[key] = fn[key].bind(argThis);
++                if (Object.keys(fn[key]).length > 0) {
++                    bindThis(fn[key], target[key], argThis);
++                }
++            });
++        })(Chart.prototype, this, this);
++    }
++
++    function AxisInternal(component, params) {
++        var internal = this;
++        internal.component = component;
++        internal.params = params || {};
++
++        internal.d3 = component.d3;
++        internal.scale = internal.d3.scaleLinear();
++        internal.range;
++        internal.orient = "bottom";
++        internal.innerTickSize = 6;
++        internal.outerTickSize = this.params.withOuterTick ? 6 : 0;
++        internal.tickPadding = 3;
++        internal.tickValues = null;
++        internal.tickFormat;
++        internal.tickArguments;
++
++        internal.tickOffset = 0;
++        internal.tickCulling = true;
++        internal.tickCentered;
++        internal.tickTextCharSize;
++        internal.tickTextRotate = internal.params.tickTextRotate;
++        internal.tickLength;
++
++        internal.axis = internal.generateAxis();
++    }
++
++    AxisInternal.prototype.axisX = function (selection, x, tickOffset) {
++        selection.attr("transform", function (d) {
++            return "translate(" + Math.ceil(x(d) + tickOffset) + ", 0)";
++        });
++    };
++    AxisInternal.prototype.axisY = function (selection, y) {
++        selection.attr("transform", function (d) {
++            return "translate(0," + Math.ceil(y(d)) + ")";
++        });
++    };
++    AxisInternal.prototype.scaleExtent = function (domain) {
++        var start = domain[0],
++            stop = domain[domain.length - 1];
++        return start < stop ? [start, stop] : [stop, start];
++    };
++    AxisInternal.prototype.generateTicks = function (scale) {
++        var internal = this;
++        var i,
++            domain,
++            ticks = [];
++        if (scale.ticks) {
++            return scale.ticks.apply(scale, internal.tickArguments);
++        }
++        domain = scale.domain();
++        for (i = Math.ceil(domain[0]); i < domain[1]; i++) {
++            ticks.push(i);
++        }
++        if (ticks.length > 0 && ticks[0] > 0) {
++            ticks.unshift(ticks[0] - (ticks[1] - ticks[0]));
++        }
++        return ticks;
++    };
++    AxisInternal.prototype.copyScale = function () {
++        var internal = this;
++        var newScale = internal.scale.copy(),
++            domain;
++        if (internal.params.isCategory) {
++            domain = internal.scale.domain();
++            newScale.domain([domain[0], domain[1] - 1]);
++        }
++        return newScale;
++    };
++    AxisInternal.prototype.textFormatted = function (v) {
++        var internal = this,
++            formatted = internal.tickFormat ? internal.tickFormat(v) : v;
++        return typeof formatted !== 'undefined' ? formatted : '';
++    };
++    AxisInternal.prototype.updateRange = function () {
++        var internal = this;
++        internal.range = internal.scale.rangeExtent ? internal.scale.rangeExtent() : internal.scaleExtent(internal.scale.range());
++        return internal.range;
++    };
++    AxisInternal.prototype.updateTickTextCharSize = function (tick) {
++        var internal = this;
++        if (internal.tickTextCharSize) {
++            return internal.tickTextCharSize;
++        }
++        var size = {
++            h: 11.5,
++            w: 5.5
++        };
++        tick.select('text').text(function (d) {
++            return internal.textFormatted(d);
++        }).each(function (d) {
++            var box = this.getBoundingClientRect(),
++                text = internal.textFormatted(d),
++                h = box.height,
++                w = text ? box.width / text.length : undefined;
++            if (h && w) {
++                size.h = h;
++                size.w = w;
++            }
++        }).text('');
++        internal.tickTextCharSize = size;
++        return size;
++    };
++    AxisInternal.prototype.isVertical = function () {
++        return this.orient === 'left' || this.orient === 'right';
++    };
++    AxisInternal.prototype.tspanData = function (d, i, scale) {
++        var internal = this;
++        var splitted = internal.params.tickMultiline ? internal.splitTickText(d, scale) : [].concat(internal.textFormatted(d));
++
++        if (internal.params.tickMultiline && internal.params.tickMultilineMax > 0) {
++            splitted = internal.ellipsify(splitted, internal.params.tickMultilineMax);
++        }
++
++        return splitted.map(function (s) {
++            return { index: i, splitted: s, length: splitted.length };
++        });
++    };
++    AxisInternal.prototype.splitTickText = function (d, scale) {
++        var internal = this,
++            tickText = internal.textFormatted(d),
++            maxWidth = internal.params.tickWidth,
++            subtext,
++            spaceIndex,
++            textWidth,
++            splitted = [];
++
++        if (Object.prototype.toString.call(tickText) === "[object Array]") {
++            return tickText;
++        }
++
++        if (!maxWidth || maxWidth <= 0) {
++            maxWidth = internal.isVertical() ? 95 : internal.params.isCategory ? Math.ceil(scale(1) - scale(0)) - 12 : 110;
++        }
++
++        function split(splitted, text) {
++            spaceIndex = undefined;
++            for (var i = 1; i < text.length; i++) {
++                if (text.charAt(i) === ' ') {
++                    spaceIndex = i;
++                }
++                subtext = text.substr(0, i + 1);
++                textWidth = internal.tickTextCharSize.w * subtext.length;
++                // if text width gets over tick width, split by space index or crrent index
++                if (maxWidth < textWidth) {
++                    return split(splitted.concat(text.substr(0, spaceIndex ? spaceIndex : i)), text.slice(spaceIndex ? spaceIndex + 1 : i));
++                }
++            }
++            return splitted.concat(text);
++        }
++
++        return split(splitted, tickText + "");
++    };
++    AxisInternal.prototype.ellipsify = function (splitted, max) {
++        if (splitted.length <= max) {
++            return splitted;
++        }
++
++        var ellipsified = splitted.slice(0, max);
++        var remaining = 3;
++        for (var i = max - 1; i >= 0; i--) {
++            var available = ellipsified[i].length;
++
++            ellipsified[i] = ellipsified[i].substr(0, available - remaining).padEnd(available, '.');
++
++            remaining -= available;
++
++            if (remaining <= 0) {
++                break;
++            }
++        }
++
++        return ellipsified;
++    };
++    AxisInternal.prototype.updateTickLength = function () {
++        var internal = this;
++        internal.tickLength = Math.max(internal.innerTickSize, 0) + internal.tickPadding;
++    };
++    AxisInternal.prototype.lineY2 = function (d) {
++        var internal = this,
++            tickPosition = internal.scale(d) + (internal.tickCentered ? 0 : internal.tickOffset);
++        return internal.range[0] < tickPosition && tickPosition < internal.range[1] ? internal.innerTickSize : 0;
++    };
++    AxisInternal.prototype.textY = function () {
++        var internal = this,
++            rotate = internal.tickTextRotate;
++        return rotate ? 11.5 - 2.5 * (rotate / 15) * (rotate > 0 ? 1 : -1) : internal.tickLength;
++    };
++    AxisInternal.prototype.textTransform = function () {
++        var internal = this,
++            rotate = internal.tickTextRotate;
++        return rotate ? "rotate(" + rotate + ")" : "";
++    };
++    AxisInternal.prototype.textTextAnchor = function () {
++        var internal = this,
++            rotate = internal.tickTextRotate;
++        return rotate ? rotate > 0 ? "start" : "end" : "middle";
++    };
++    AxisInternal.prototype.tspanDx = function () {
++        var internal = this,
++            rotate = internal.tickTextRotate;
++        return rotate ? 8 * Math.sin(Math.PI * (rotate / 180)) : 0;
++    };
++    AxisInternal.prototype.tspanDy = function (d, i) {
++        var internal = this,
++            dy = internal.tickTextCharSize.h;
++        if (i === 0) {
++            if (internal.isVertical()) {
++                dy = -((d.length - 1) * (internal.tickTextCharSize.h / 2) - 3);
++            } else {
++                dy = ".71em";
++            }
++        }
++        return dy;
++    };
++
++    AxisInternal.prototype.generateAxis = function () {
++        var internal = this,
++            d3 = internal.d3,
++            params = internal.params;
++        function axis(g, transition) {
++            var self;
++            g.each(function () {
++                var g = axis.g = d3.select(this);
++
++                var scale0 = this.__chart__ || internal.scale,
++                    scale1 = this.__chart__ = internal.copyScale();
++
++                var ticksValues = internal.tickValues ? internal.tickValues : internal.generateTicks(scale1),
++                    ticks = g.selectAll(".tick").data(ticksValues, scale1),
++                    tickEnter = ticks.enter().insert("g", ".domain").attr("class", "tick").style("opacity", 1e-6),
++
++                // MEMO: No exit transition. The reason is this transition affects max tick width calculation because old tick will be included in the ticks.
++                tickExit = ticks.exit().remove(),
++                    tickUpdate = ticks.merge(tickEnter),
++                    tickTransform,
++                    tickX,
++                    tickY;
++
++                if (params.isCategory) {
++                    internal.tickOffset = Math.ceil((scale1(1) - scale1(0)) / 2);
++                    tickX = internal.tickCentered ? 0 : internal.tickOffset;
++                    tickY = internal.tickCentered ? internal.tickOffset : 0;
++                } else {
++                    internal.tickOffset = tickX = 0;
++                }
++
++                internal.updateRange();
++                internal.updateTickLength();
++                internal.updateTickTextCharSize(g.select('.tick'));
++
++                var lineUpdate = tickUpdate.select("line").merge(tickEnter.append("line")),
++                    textUpdate = tickUpdate.select("text").merge(tickEnter.append("text"));
++
++                var tspans = tickUpdate.selectAll('text').selectAll('tspan').data(function (d, i) {
++                    return internal.tspanData(d, i, scale1);
++                }),
++                    tspanEnter = tspans.enter().append('tspan'),
++                    tspanUpdate = tspanEnter.merge(tspans).text(function (d) {
++                    return d.splitted;
++                });
++                tspans.exit().remove();
++
++                var path = g.selectAll(".domain").data([0]),
++                    pathUpdate = path.enter().append("path").merge(path).attr("class", "domain");
++
++                // TODO: each attr should be one function and change its behavior by internal.orient, probably
++                switch (internal.orient) {
++                    case "bottom":
++                        {
++                            tickTransform = internal.axisX;
++                            lineUpdate.attr("x1", tickX).attr("x2", tickX).attr("y2", function (d, i) {
++                                return internal.lineY2(d, i);
++                            });
++                            textUpdate.attr("x", 0).attr("y", function (d, i) {
++                                return internal.textY(d, i);
++                            }).attr("transform", function (d, i) {
++                                return internal.textTransform(d, i);
++                            }).style("text-anchor", function (d, i) {
++                                return internal.textTextAnchor(d, i);
++                            });
++                            tspanUpdate.attr('x', 0).attr("dy", function (d, i) {
++                                return internal.tspanDy(d, i);
++                            }).attr('dx', function (d, i) {
++                                return internal.tspanDx(d, i);
++                            });
++                            pathUpdate.attr("d", "M" + internal.range[0] + "," + internal.outerTickSize + "V0H" + internal.range[1] + "V" + internal.outerTickSize);
++                            break;
++                        }
++                    case "top":
++                        {
++                            // TODO: rotated tick text
++                            tickTransform = internal.axisX;
++                            lineUpdate.attr("x1", tickX).attr("x2", tickX).attr("y2", function (d, i) {
++                                return -1 * internal.lineY2(d, i);
++                            });
++                            textUpdate.attr("x", 0).attr("y", function (d, i) {
++                                return -1 * internal.textY(d, i) - (params.isCategory ? 2 : internal.tickLength - 2);
++                            }).attr("transform", function (d, i) {
++                                return internal.textTransform(d, i);
++                            }).style("text-anchor", function (d, i) {
++                                return internal.textTextAnchor(d, i);
++                            });
++                            tspanUpdate.attr('x', 0).attr("dy", function (d, i) {
++                                return internal.tspanDy(d, i);
++                            }).attr('dx', function (d, i) {
++                                return internal.tspanDx(d, i);
++                            });
++                            pathUpdate.attr("d", "M" + internal.range[0] + "," + -internal.outerTickSize + "V0H" + internal.range[1] + "V" + -internal.outerTickSize);
++                            break;
++                        }
++                    case "left":
++                        {
++                            tickTransform = internal.axisY;
++                            lineUpdate.attr("x2", -internal.innerTickSize).attr("y1", tickY).attr("y2", tickY);
++                            textUpdate.attr("x", -internal.tickLength).attr("y", internal.tickOffset).style("text-anchor", "end");
++                            tspanUpdate.attr('x', -internal.tickLength).attr("dy", function (d, i) {
++                                return internal.tspanDy(d, i);
++                            });
++                            pathUpdate.attr("d", "M" + -internal.outerTickSize + "," + internal.range[0] + "H0V" + internal.range[1] + "H" + -internal.outerTickSize);
++                            break;
++                        }
++                    case "right":
++                        {
++                            tickTransform = internal.axisY;
++                            lineUpdate.attr("x2", internal.innerTickSize).attr("y1", tickY).attr("y2", tickY);
++                            textUpdate.attr("x", internal.tickLength).attr("y", internal.tickOffset).style("text-anchor", "start");
++                            tspanUpdate.attr('x', internal.tickLength).attr("dy", function (d, i) {
++                                return internal.tspanDy(d, i);
++                            });
++                            pathUpdate.attr("d", "M" + internal.outerTickSize + "," + internal.range[0] + "H0V" + internal.range[1] + "H" + internal.outerTickSize);
++                            break;
++                        }
++                }
++                if (scale1.rangeBand) {
++                    var x = scale1,
++                        dx = x.rangeBand() / 2;
++                    scale0 = scale1 = function scale1(d) {
++                        return x(d) + dx;
++                    };
++                } else if (scale0.rangeBand) {
++                    scale0 = scale1;
++                } else {
++                    tickExit.call(tickTransform, scale1, internal.tickOffset);
++                }
++                tickEnter.call(tickTransform, scale0, internal.tickOffset);
++                self = (transition ? tickUpdate.transition(transition) : tickUpdate).style('opacity', 1).call(tickTransform, scale1, internal.tickOffset);
++            });
++            return self;
++        }
++        axis.scale = function (x) {
++            if (!arguments.length) {
++                return internal.scale;
++            }
++            internal.scale = x;
++            return axis;
++        };
++        axis.orient = function (x) {
++            if (!arguments.length) {
++                return internal.orient;
++            }
++            internal.orient = x in { top: 1, right: 1, bottom: 1, left: 1 } ? x + "" : "bottom";
++            return axis;
++        };
++        axis.tickFormat = function (format) {
++            if (!arguments.length) {
++                return internal.tickFormat;
++            }
++            internal.tickFormat = format;
++            return axis;
++        };
++        axis.tickCentered = function (isCentered) {
++            if (!arguments.length) {
++                return internal.tickCentered;
++            }
++            internal.tickCentered = isCentered;
++            return axis;
++        };
++        axis.tickOffset = function () {
++            return internal.tickOffset;
++        };
++        axis.tickInterval = function () {
++            var interval, length;
++            if (params.isCategory) {
++                interval = internal.tickOffset * 2;
++            } else {
++                length = axis.g.select('path.domain').node().getTotalLength() - internal.outerTickSize * 2;
++                interval = length / axis.g.selectAll('line').size();
++            }
++            return interval === Infinity ? 0 : interval;
++        };
++        axis.ticks = function () {
++            if (!arguments.length) {
++                return internal.tickArguments;
++            }
++            internal.tickArguments = arguments;
++            return axis;
++        };
++        axis.tickCulling = function (culling) {
++            if (!arguments.length) {
++                return internal.tickCulling;
++            }
++            internal.tickCulling = culling;
++            return axis;
++        };
++        axis.tickValues = function (x) {
++            if (typeof x === 'function') {
++                internal.tickValues = function () {
++                    return x(internal.scale.domain());
++                };
++            } else {
++                if (!arguments.length) {
++                    return internal.tickValues;
++                }
++                internal.tickValues = x;
++            }
++            return axis;
++        };
++        return axis;
++    };
++
++    var CLASS = {
++        target: 'c3-target',
++        chart: 'c3-chart',
++        chartLine: 'c3-chart-line',
++        chartLines: 'c3-chart-lines',
++        chartBar: 'c3-chart-bar',
++        chartBars: 'c3-chart-bars',
++        chartText: 'c3-chart-text',
++        chartTexts: 'c3-chart-texts',
++        chartArc: 'c3-chart-arc',
++        chartArcs: 'c3-chart-arcs',
++        chartArcsTitle: 'c3-chart-arcs-title',
++        chartArcsBackground: 'c3-chart-arcs-background',
++        chartArcsGaugeUnit: 'c3-chart-arcs-gauge-unit',
++        chartArcsGaugeMax: 'c3-chart-arcs-gauge-max',
++        chartArcsGaugeMin: 'c3-chart-arcs-gauge-min',
++        selectedCircle: 'c3-selected-circle',
++        selectedCircles: 'c3-selected-circles',
++        eventRect: 'c3-event-rect',
++        eventRects: 'c3-event-rects',
++        eventRectsSingle: 'c3-event-rects-single',
++        eventRectsMultiple: 'c3-event-rects-multiple',
++        zoomRect: 'c3-zoom-rect',
++        brush: 'c3-brush',
++        focused: 'c3-focused',
++        defocused: 'c3-defocused',
++        region: 'c3-region',
++        regions: 'c3-regions',
++        title: 'c3-title',
++        tooltipContainer: 'c3-tooltip-container',
++        tooltip: 'c3-tooltip',
++        tooltipName: 'c3-tooltip-name',
++        shape: 'c3-shape',
++        shapes: 'c3-shapes',
++        line: 'c3-line',
++        lines: 'c3-lines',
++        bar: 'c3-bar',
++        bars: 'c3-bars',
++        circle: 'c3-circle',
++        circles: 'c3-circles',
++        arc: 'c3-arc',
++        arcLabelLine: 'c3-arc-label-line',
++        arcs: 'c3-arcs',
++        area: 'c3-area',
++        areas: 'c3-areas',
++        empty: 'c3-empty',
++        text: 'c3-text',
++        texts: 'c3-texts',
++        gaugeValue: 'c3-gauge-value',
++        grid: 'c3-grid',
++        gridLines: 'c3-grid-lines',
++        xgrid: 'c3-xgrid',
++        xgrids: 'c3-xgrids',
++        xgridLine: 'c3-xgrid-line',
++        xgridLines: 'c3-xgrid-lines',
++        xgridFocus: 'c3-xgrid-focus',
++        ygrid: 'c3-ygrid',
++        ygrids: 'c3-ygrids',
++        ygridLine: 'c3-ygrid-line',
++        ygridLines: 'c3-ygrid-lines',
++        axis: 'c3-axis',
++        axisX: 'c3-axis-x',
++        axisXLabel: 'c3-axis-x-label',
++        axisY: 'c3-axis-y',
++        axisYLabel: 'c3-axis-y-label',
++        axisY2: 'c3-axis-y2',
++        axisY2Label: 'c3-axis-y2-label',
++        legendBackground: 'c3-legend-background',
++        legendItem: 'c3-legend-item',
++        legendItemEvent: 'c3-legend-item-event',
++        legendItemTile: 'c3-legend-item-tile',
++        legendItemHidden: 'c3-legend-item-hidden',
++        legendItemFocused: 'c3-legend-item-focused',
++        dragarea: 'c3-dragarea',
++        EXPANDED: '_expanded_',
++        SELECTED: '_selected_',
++        INCLUDED: '_included_'
++    };
++
++    var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
++      return typeof obj;
++    } : function (obj) {
++      return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
++    };
++
++    var classCallCheck = function (instance, Constructor) {
++      if (!(instance instanceof Constructor)) {
++        throw new TypeError("Cannot call a class as a function");
++      }
++    };
++
++    var defineProperty = function (obj, key, value) {
++      if (key in obj) {
++        Object.defineProperty(obj, key, {
++          value: value,
++          enumerable: true,
++          configurable: true,
++          writable: true
++        });
++      } else {
++        obj[key] = value;
++      }
++
++      return obj;
++    };
++
++    var asHalfPixel = function asHalfPixel(n) {
++        return Math.ceil(n) + 0.5;
++    };
++    var ceil10 = function ceil10(v) {
++        return Math.ceil(v / 10) * 10;
++    };
++    var diffDomain = function diffDomain(d) {
++        return d[1] - d[0];
++    };
++    var getOption = function getOption(options, key, defaultValue) {
++        return isDefined(options[key]) ? options[key] : defaultValue;
++    };
++    var getPathBox = function getPathBox(path) {
++        var box = path.getBoundingClientRect(),
++            items = [path.pathSegList.getItem(0), path.pathSegList.getItem(1)],
++            minX = items[0].x,
++            minY = Math.min(items[0].y, items[1].y);
++        return { x: minX, y: minY, width: box.width, height: box.height };
++    };
++    var hasValue = function hasValue(dict, value) {
++        var found = false;
++        Object.keys(dict).forEach(function (key) {
++            if (dict[key] === value) {
++                found = true;
++            }
++        });
++        return found;
++    };
++    var isArray = function isArray(o) {
++        return Array.isArray(o);
++    };
++    var isDefined = function isDefined(v) {
++        return typeof v !== 'undefined';
++    };
++    var isEmpty = function isEmpty(o) {
++        return typeof o === 'undefined' || o === null || isString(o) && o.length === 0 || (typeof o === 'undefined' ? 'undefined' : _typeof(o)) === 'object' && Object.keys(o).length === 0;
++    };
++    var isFunction = function isFunction(o) {
++        return typeof o === 'function';
++    };
++    var isString = function isString(o) {
++        return typeof o === 'string';
++    };
++    var isUndefined = function isUndefined(v) {
++        return typeof v === 'undefined';
++    };
++    var isValue = function isValue(v) {
++        return v || v === 0;
++    };
++    var notEmpty = function notEmpty(o) {
++        return !isEmpty(o);
++    };
++    var sanitise = function sanitise(str) {
++        return typeof str === 'string' ? str.replace(/</g, '&lt;').replace(/>/g, '&gt;') : str;
++    };
++
++    var Axis = function Axis(owner) {
++        classCallCheck(this, Axis);
++
++        this.owner = owner;
++        this.d3 = owner.d3;
++        this.internal = AxisInternal;
++    };
++
++    Axis.prototype.init = function init() {
++        var $$ = this.owner,
++            config = $$.config,
++            main = $$.main;
++        $$.axes.x = main.append("g").attr("class", CLASS.axis + ' ' + CLASS.axisX).attr("clip-path", config.axis_x_inner ? "" : $$.clipPathForXAxis).attr("transform", $$.getTranslate('x')).style("visibility", config.axis_x_show ? 'visible' : 'hidden');
++        $$.axes.x.append("text").attr("class", CLASS.axisXLabel).attr("transform", config.axis_rotated ? "rotate(-90)" : "").style("text-anchor", this.textAnchorForXAxisLabel.bind(this));
++        $$.axes.y = main.append("g").attr("class", CLASS.axis + ' ' + CLASS.axisY).attr("clip-path", config.axis_y_inner ? "" : $$.clipPathForYAxis).attr("transform", $$.getTranslate('y')).style("visibility", config.axis_y_show ? 'visible' : 'hidden');
++        $$.axes.y.append("text").attr("class", CLASS.axisYLabel).attr("transform", config.axis_rotated ? "" : "rotate(-90)").style("text-anchor", this.textAnchorForYAxisLabel.bind(this));
++
++        $$.axes.y2 = main.append("g").attr("class", CLASS.axis + ' ' + CLASS.axisY2)
++        // clip-path?
++        .attr("transform", $$.getTranslate('y2')).style("visibility", config.axis_y2_show ? 'visible' : 'hidden');
++        $$.axes.y2.append("text").attr("class", CLASS.axisY2Label).attr("transform", config.axis_rotated ? "" : "rotate(-90)").style("text-anchor", this.textAnchorForY2AxisLabel.bind(this));
++    };
++    Axis.prototype.getXAxis = function getXAxis(scale, orient, tickFormat, tickValues, withOuterTick, withoutTransition, withoutRotateTickText) {
++        var $$ = this.owner,
++            config = $$.config,
++            axisParams = {
++            isCategory: $$.isCategorized(),
++            withOuterTick: withOuterTick,
++            tickMultiline: config.axis_x_tick_multiline,
++            tickMultilineMax: config.axis_x_tick_multiline ? Number(config.axis_x_tick_multilineMax) : 0,
++            tickWidth: config.axis_x_tick_width,
++            tickTextRotate: withoutRotateTickText ? 0 : config.axis_x_tick_rotate,
++            withoutTransition: withoutTransition
++        },
++            axis = new this.internal(this, axisParams).axis.scale(scale).orient(orient);
++
++        if ($$.isTimeSeries() && tickValues && typeof tickValues !== "function") {
++            tickValues = tickValues.map(function (v) {
++                return $$.parseDate(v);
++            });
++        }
++
++        // Set tick
++        axis.tickFormat(tickFormat).tickValues(tickValues);
++        if ($$.isCategorized()) {
++            axis.tickCentered(config.axis_x_tick_centered);
++            if (isEmpty(config.axis_x_tick_culling)) {
++                config.axis_x_tick_culling = false;
++            }
++        }
++
++        return axis;
++    };
++    Axis.prototype.updateXAxisTickValues = function updateXAxisTickValues(targets, axis) {
++        var $$ = this.owner,
++            config = $$.config,
++            tickValues;
++        if (config.axis_x_tick_fit || config.axis_x_tick_count) {
++            tickValues = this.generateTickValues($$.mapTargetsToUniqueXs(targets), config.axis_x_tick_count, $$.isTimeSeries());
++        }
++        if (axis) {
++            axis.tickValues(tickValues);
++        } else {
++            $$.xAxis.tickValues(tickValues);
++            $$.subXAxis.tickValues(tickValues);
++        }
++        return tickValues;
++    };
++    Axis.prototype.getYAxis = function getYAxis(scale, orient, tickFormat, tickValues, withOuterTick, withoutTransition, withoutRotateTickText) {
++        var $$ = this.owner,
++            config = $$.config,
++            axisParams = {
++            withOuterTick: withOuterTick,
++            withoutTransition: withoutTransition,
++            tickTextRotate: withoutRotateTickText ? 0 : config.axis_y_tick_rotate
++        },
++            axis = new this.internal(this, axisParams).axis.scale(scale).orient(orient).tickFormat(tickFormat);
++        if ($$.isTimeSeriesY()) {
++            axis.ticks(config.axis_y_tick_time_type, config.axis_y_tick_time_interval);
++        } else {
++            axis.tickValues(tickValues);
++        }
++        return axis;
++    };
++    Axis.prototype.getId = function getId(id) {
++        var config = this.owner.config;
++        return id in config.data_axes ? config.data_axes[id] : 'y';
++    };
++    Axis.prototype.getXAxisTickFormat = function getXAxisTickFormat() {
++        // #2251 previously set any negative values to a whole number,
++        // however both should be truncated according to the users format specification
++        var $$ = this.owner,
++            config = $$.config;
++        var format = $$.isTimeSeries() ? $$.defaultAxisTimeFormat : $$.isCategorized() ? $$.categoryName : function (v) {
++            return v;
++        };
++
++        if (config.axis_x_tick_format) {
++            if (isFunction(config.axis_x_tick_format)) {
++                format = config.axis_x_tick_format;
++            } else if ($$.isTimeSeries()) {
++                format = function format(date) {
++                    return date ? $$.axisTimeFormat(config.axis_x_tick_format)(date) : "";
++                };
++            }
++        }
++        return isFunction(format) ? function (v) {
++            return format.call($$, v);
++        } : format;
++    };
++    Axis.prototype.getTickValues = function getTickValues(tickValues, axis) {
++        return tickValues ? tickValues : axis ? axis.tickValues() : undefined;
++    };
++    Axis.prototype.getXAxisTickValues = function getXAxisTickValues() {
++        return this.getTickValues(this.owner.config.axis_x_tick_values, this.owner.xAxis);
++    };
++    Axis.prototype.getYAxisTickValues = function getYAxisTickValues() {
++        return this.getTickValues(this.owner.config.axis_y_tick_values, this.owner.yAxis);
++    };
++    Axis.prototype.getY2AxisTickValues = function getY2AxisTickValues() {
++        return this.getTickValues(this.owner.config.axis_y2_tick_values, this.owner.y2Axis);
++    };
++    Axis.prototype.getLabelOptionByAxisId = function getLabelOptionByAxisId(axisId) {
++        var $$ = this.owner,
++            config = $$.config,
++            option;
++        if (axisId === 'y') {
++            option = config.axis_y_label;
++        } else if (axisId === 'y2') {
++            option = config.axis_y2_label;
++        } else if (axisId === 'x') {
++            option = config.axis_x_label;
++        }
++        return option;
++    };
++    Axis.prototype.getLabelText = function getLabelText(axisId) {
++        var option = this.getLabelOptionByAxisId(axisId);
++        return isString(option) ? option : option ? option.text : null;
++    };
++    Axis.prototype.setLabelText = function setLabelText(axisId, text) {
++        var $$ = this.owner,
++            config = $$.config,
++            option = this.getLabelOptionByAxisId(axisId);
++        if (isString(option)) {
++            if (axisId === 'y') {
++                config.axis_y_label = text;
++            } else if (axisId === 'y2') {
++                config.axis_y2_label = text;
++            } else if (axisId === 'x') {
++                config.axis_x_label = text;
++            }
++        } else if (option) {
++            option.text = text;
++        }
++    };
++    Axis.prototype.getLabelPosition = function getLabelPosition(axisId, defaultPosition) {
++        var option = this.getLabelOptionByAxisId(axisId),
++            position = option && (typeof option === 'undefined' ? 'undefined' : _typeof(option)) === 'object' && option.position ? option.position : defaultPosition;
++        return {
++            isInner: position.indexOf('inner') >= 0,
++            isOuter: position.indexOf('outer') >= 0,
++            isLeft: position.indexOf('left') >= 0,
++            isCenter: position.indexOf('center') >= 0,
++            isRight: position.indexOf('right') >= 0,
++            isTop: position.indexOf('top') >= 0,
++            isMiddle: position.indexOf('middle') >= 0,
++            isBottom: position.indexOf('bottom') >= 0
++        };
++    };
++    Axis.prototype.getXAxisLabelPosition = function getXAxisLabelPosition() {
++        return this.getLabelPosition('x', this.owner.config.axis_rotated ? 'inner-top' : 'inner-right');
++    };
++    Axis.prototype.getYAxisLabelPosition = function getYAxisLabelPosition() {
++        return this.getLabelPosition('y', this.owner.config.axis_rotated ? 'inner-right' : 'inner-top');
++    };
++    Axis.prototype.getY2AxisLabelPosition = function getY2AxisLabelPosition() {
++        return this.getLabelPosition('y2', this.owner.config.axis_rotated ? 'inner-right' : 'inner-top');
++    };
++    Axis.prototype.getLabelPositionById = function getLabelPositionById(id) {
++        return id === 'y2' ? this.getY2AxisLabelPosition() : id === 'y' ? this.getYAxisLabelPosition() : this.getXAxisLabelPosition();
++    };
++    Axis.prototype.textForXAxisLabel = function textForXAxisLabel() {
++        return this.getLabelText('x');
++    };
++    Axis.prototype.textForYAxisLabel = function textForYAxisLabel() {
++        return this.getLabelText('y');
++    };
++    Axis.prototype.textForY2AxisLabel = function textForY2AxisLabel() {
++        return this.getLabelText('y2');
++    };
++    Axis.prototype.xForAxisLabel = function xForAxisLabel(forHorizontal, position) {
++        var $$ = this.owner;
++        if (forHorizontal) {
++            return position.isLeft ? 0 : position.isCenter ? $$.width / 2 : $$.width;
++        } else {
++            return position.isBottom ? -$$.height : position.isMiddle ? -$$.height / 2 : 0;
++        }
++    };
++    Axis.prototype.dxForAxisLabel = function dxForAxisLabel(forHorizontal, position) {
++        if (forHorizontal) {
++            return position.isLeft ? "0.5em" : position.isRight ? "-0.5em" : "0";
++        } else {
++            return position.isTop ? "-0.5em" : position.isBottom ? "0.5em" : "0";
++        }
++    };
++    Axis.prototype.textAnchorForAxisLabel = function textAnchorForAxisLabel(forHorizontal, position) {
++        if (forHorizontal) {
++            return position.isLeft ? 'start' : position.isCenter ? 'middle' : 'end';
++        } else {
++            return position.isBottom ? 'start' : position.isMiddle ? 'middle' : 'end';
++        }
++    };
++    Axis.prototype.xForXAxisLabel = function xForXAxisLabel() {
++        return this.xForAxisLabel(!this.owner.config.axis_rotated, this.getXAxisLabelPosition());
++    };
++    Axis.prototype.xForYAxisLabel = function xForYAxisLabel() {
++        return this.xForAxisLabel(this.owner.config.axis_rotated, this.getYAxisLabelPosition());
++    };
++    Axis.prototype.xForY2AxisLabel = function xForY2AxisLabel() {
++        return this.xForAxisLabel(this.owner.config.axis_rotated, this.getY2AxisLabelPosition());
++    };
++    Axis.prototype.dxForXAxisLabel = function dxForXAxisLabel() {
++        return this.dxForAxisLabel(!this.owner.config.axis_rotated, this.getXAxisLabelPosition());
++    };
++    Axis.prototype.dxForYAxisLabel = function dxForYAxisLabel() {
++        return this.dxForAxisLabel(this.owner.config.axis_rotated, this.getYAxisLabelPosition());
++    };
++    Axis.prototype.dxForY2AxisLabel = function dxForY2AxisLabel() {
++        return this.dxForAxisLabel(this.owner.config.axis_rotated, this.getY2AxisLabelPosition());
++    };
++    Axis.prototype.dyForXAxisLabel = function dyForXAxisLabel() {
++        var $$ = this.owner,
++            config = $$.config,
++            position = this.getXAxisLabelPosition();
++        if (config.axis_rotated) {
++            return position.isInner ? "1.2em" : -25 - ($$.config.axis_x_inner ? 0 : this.getMaxTickWidth('x'));
++        } else {
++            return position.isInner ? "-0.5em" : config.axis_x_height ? config.axis_x_height - 10 : "3em";
++        }
++    };
++    Axis.prototype.dyForYAxisLabel = function dyForYAxisLabel() {
++        var $$ = this.owner,
++            position = this.getYAxisLabelPosition();
++        if ($$.config.axis_rotated) {
++            return position.isInner ? "-0.5em" : "3em";
++        } else {
++            return position.isInner ? "1.2em" : -10 - ($$.config.axis_y_inner ? 0 : this.getMaxTickWidth('y') + 10);
++        }
++    };
++    Axis.prototype.dyForY2AxisLabel = function dyForY2AxisLabel() {
++        var $$ = this.owner,
++            position = this.getY2AxisLabelPosition();
++        if ($$.config.axis_rotated) {
++            return position.isInner ? "1.2em" : "-2.2em";
++        } else {
++            return position.isInner ? "-0.5em" : 15 + ($$.config.axis_y2_inner ? 0 : this.getMaxTickWidth('y2') + 15);
++        }
++    };
++    Axis.prototype.textAnchorForXAxisLabel = function textAnchorForXAxisLabel() {
++        var $$ = this.owner;
++        return this.textAnchorForAxisLabel(!$$.config.axis_rotated, this.getXAxisLabelPosition());
++    };
++    Axis.prototype.textAnchorForYAxisLabel = function textAnchorForYAxisLabel() {
++        var $$ = this.owner;
++        return this.textAnchorForAxisLabel($$.config.axis_rotated, this.getYAxisLabelPosition());
++    };
++    Axis.prototype.textAnchorForY2AxisLabel = function textAnchorForY2AxisLabel() {
++        var $$ = this.owner;
++        return this.textAnchorForAxisLabel($$.config.axis_rotated, this.getY2AxisLabelPosition());
++    };
++    Axis.prototype.getMaxTickWidth = function getMaxTickWidth(id, withoutRecompute) {
++        var $$ = this.owner,
++            config = $$.config,
++            maxWidth = 0,
++            targetsToShow,
++            scale,
++            axis,
++            dummy,
++            svg;
++        if (withoutRecompute && $$.currentMaxTickWidths[id]) {
++            return $$.currentMaxTickWidths[id];
++        }
++        if ($$.svg) {
++            targetsToShow = $$.filterTargetsToShow($$.data.targets);
++            if (id === 'y') {
++                scale = $$.y.copy().domain($$.getYDomain(targetsToShow, 'y'));
++                axis = this.getYAxis(scale, $$.yOrient, config.axis_y_tick_format, $$.yAxisTickValues, false, true, true);
++            } else if (id === 'y2') {
++                scale = $$.y2.copy().domain($$.getYDomain(targetsToShow, 'y2'));
++                axis = this.getYAxis(scale, $$.y2Orient, config.axis_y2_tick_format, $$.y2AxisTickValues, false, true, true);
++            } else {
++                scale = $$.x.copy().domain($$.getXDomain(targetsToShow));
++                axis = this.getXAxis(scale, $$.xOrient, $$.xAxisTickFormat, $$.xAxisTickValues, false, true, true);
++                this.updateXAxisTickValues(targetsToShow, axis);
++            }
++            dummy = $$.d3.select('body').append('div').classed('c3', true);
++            svg = dummy.append("svg").style('visibility', 'hidden').style('position', 'fixed').style('top', 0).style('left', 0), svg.append('g').call(axis).each(function () {
++                $$.d3.select(this).selectAll('text').each(function () {
++                    var box = this.getBoundingClientRect();
++                    if (maxWidth < box.width) {
++                        maxWidth = box.width;
++                    }
++                });
++                dummy.remove();
++            });
++        }
++        $$.currentMaxTickWidths[id] = maxWidth <= 0 ? $$.currentMaxTickWidths[id] : maxWidth;
++        return $$.currentMaxTickWidths[id];
++    };
++
++    Axis.prototype.updateLabels = function updateLabels(withTransition) {
++        var $$ = this.owner;
++        var axisXLabel = $$.main.select('.' + CLASS.axisX + ' .' + CLASS.axisXLabel),
++            axisYLabel = $$.main.select('.' + CLASS.axisY + ' .' + CLASS.axisYLabel),
++            axisY2Label = $$.main.select('.' + CLASS.axisY2 + ' .' + CLASS.axisY2Label);
++        (withTransition ? axisXLabel.transition() : axisXLabel).attr("x", this.xForXAxisLabel.bind(this)).attr("dx", this.dxForXAxisLabel.bind(this)).attr("dy", this.dyForXAxisLabel.bind(this)).text(this.textForXAxisLabel.bind(this));
++        (withTransition ? axisYLabel.transition() : axisYLabel).attr("x", this.xForYAxisLabel.bind(this)).attr("dx", this.dxForYAxisLabel.bind(this)).attr("dy", this.dyForYAxisLabel.bind(this)).text(this.textForYAxisLabel.bind(this));
++        (withTransition ? axisY2Label.transition() : axisY2Label).attr("x", this.xForY2AxisLabel.bind(this)).attr("dx", this.dxForY2AxisLabel.bind(this)).attr("dy", this.dyForY2AxisLabel.bind(this)).text(this.textForY2AxisLabel.bind(this));
++    };
++    Axis.prototype.getPadding = function getPadding(padding, key, defaultValue, domainLength) {
++        var p = typeof padding === 'number' ? padding : padding[key];
++        if (!isValue(p)) {
++            return defaultValue;
++        }
++        if (padding.unit === 'ratio') {
++            return padding[key] * domainLength;
++        }
++        // assume padding is pixels if unit is not specified
++        return this.convertPixelsToAxisPadding(p, domainLength);
++    };
++    Axis.prototype.convertPixelsToAxisPadding = function convertPixelsToAxisPadding(pixels, domainLength) {
++        var $$ = this.owner,
++            length = $$.config.axis_rotated ? $$.width : $$.height;
++        return domainLength * (pixels / length);
++    };
++    Axis.prototype.generateTickValues = function generateTickValues(values, tickCount, forTimeSeries) {
++        var tickValues = values,
++            targetCount,
++            start,
++            end,
++            count,
++            interval,
++            i,
++            tickValue;
++        if (tickCount) {
++            targetCount = isFunction(tickCount) ? tickCount() : tickCount;
++            // compute ticks according to tickCount
++            if (targetCount === 1) {
++                tickValues = [values[0]];
++            } else if (targetCount === 2) {
++                tickValues = [values[0], values[values.length - 1]];
++            } else if (targetCount > 2) {
++                count = targetCount - 2;
++                start = values[0];
++                end = values[values.length - 1];
++                interval = (end - start) / (count + 1);
++                // re-construct unique values
++                tickValues = [start];
++                for (i = 0; i < count; i++) {
++                    tickValue = +start + interval * (i + 1);
++                    tickValues.push(forTimeSeries ? new Date(tickValue) : tickValue);
++                }
++                tickValues.push(end);
++            }
++        }
++        if (!forTimeSeries) {
++            tickValues = tickValues.sort(function (a, b) {
++                return a - b;
++            });
++        }
++        return tickValues;
++    };
++    Axis.prototype.generateTransitions = function generateTransitions(duration) {
++        var $$ = this.owner,
++            axes = $$.axes;
++        return {
++            axisX: duration ? axes.x.transition().duration(duration) : axes.x,
++            axisY: duration ? axes.y.transition().duration(duration) : axes.y,
++            axisY2: duration ? axes.y2.transition().duration(duration) : axes.y2,
++            axisSubX: duration ? axes.subx.transition().duration(duration) : axes.subx
++        };
++    };
++    Axis.prototype.redraw = function redraw(duration, isHidden) {
++        var $$ = this.owner,
++            transition = duration ? $$.d3.transition().duration(duration) : null;
++        $$.axes.x.style("opacity", isHidden ? 0 : 1).call($$.xAxis, transition);
++        $$.axes.y.style("opacity", isHidden ? 0 : 1).call($$.yAxis, transition);
++        $$.axes.y2.style("opacity", isHidden ? 0 : 1).call($$.y2Axis, transition);
++        $$.axes.subx.style("opacity", isHidden ? 0 : 1).call($$.subXAxis, transition);
++    };
++
++    var c3 = {
++        version: "0.6.6",
++        chart: {
++            fn: Chart.prototype,
++            internal: {
++                fn: ChartInternal.prototype,
++                axis: {
++                    fn: Axis.prototype,
++                    internal: {
++                        fn: AxisInternal.prototype
++                    }
++                }
++            }
++        },
++        generate: function generate(config) {
++            return new Chart(config);
++        }
++    };
++
++    ChartInternal.prototype.beforeInit = function () {
++        // can do something
++    };
++    ChartInternal.prototype.afterInit = function () {
++        // can do something
++    };
++    ChartInternal.prototype.init = function () {
++        var $$ = this,
++            config = $$.config;
++
++        $$.initParams();
++
++        if (config.data_url) {
++            $$.convertUrlToData(config.data_url, config.data_mimeType, config.data_headers, config.data_keys, $$.initWithData);
++        } else if (config.data_json) {
++            $$.initWithData($$.convertJsonToData(config.data_json, config.data_keys));
++        } else if (config.data_rows) {
++            $$.initWithData($$.convertRowsToData(config.data_rows));
++        } else if (config.data_columns) {
++            $$.initWithData($$.convertColumnsToData(config.data_columns));
++        } else {
++            throw Error('url or json or rows or columns is required.');
++        }
++    };
++
++    ChartInternal.prototype.initParams = function () {
++        var $$ = this,
++            d3 = $$.d3,
++            config = $$.config;
++
++        // MEMO: clipId needs to be unique because it conflicts when multiple charts exist
++        $$.clipId = "c3-" + +new Date() + '-clip';
++        $$.clipIdForXAxis = $$.clipId + '-xaxis';
++        $$.clipIdForYAxis = $$.clipId + '-yaxis';
++        $$.clipIdForGrid = $$.clipId + '-grid';
++        $$.clipIdForSubchart = $$.clipId + '-subchart';
++        $$.clipPath = $$.getClipPath($$.clipId);
++        $$.clipPathForXAxis = $$.getClipPath($$.clipIdForXAxis);
++        $$.clipPathForYAxis = $$.getClipPath($$.clipIdForYAxis);
++        $$.clipPathForGrid = $$.getClipPath($$.clipIdForGrid);
++        $$.clipPathForSubchart = $$.getClipPath($$.clipIdForSubchart);
++
++        $$.dragStart = null;
++        $$.dragging = false;
++        $$.flowing = false;
++        $$.cancelClick = false;
++        $$.mouseover = false;
++        $$.transiting = false;
++
++        $$.color = $$.generateColor();
++        $$.levelColor = $$.generateLevelColor();
++
++        $$.dataTimeParse = (config.data_xLocaltime ? d3.timeParse : d3.utcParse)($$.config.data_xFormat);
++        $$.axisTimeFormat = config.axis_x_localtime ? d3.timeFormat : d3.utcFormat;
++        $$.defaultAxisTimeFormat = function (date) {
++            if (date.getMilliseconds()) {
++                return d3.timeFormat(".%L")(date);
++            }
++            if (date.getSeconds()) {
++                return d3.timeFormat(":%S")(date);
++            }
++            if (date.getMinutes()) {
++                return d3.timeFormat("%I:%M")(date);
++            }
++            if (date.getHours()) {
++                return d3.timeFormat("%I %p")(date);
++            }
++            if (date.getDay() && date.getDate() !== 1) {
++                return d3.timeFormat("%-m/%-d")(date);
++            }
++            if (date.getDate() !== 1) {
++                return d3.timeFormat("%-m/%-d")(date);
++            }
++            if (date.getMonth()) {
++                return d3.timeFormat("%-m/%-d")(date);
++            }
++            return d3.timeFormat("%Y/%-m/%-d")(date);
++        };
++        $$.hiddenTargetIds = [];
++        $$.hiddenLegendIds = [];
++        $$.focusedTargetIds = [];
++        $$.defocusedTargetIds = [];
++
++        $$.xOrient = config.axis_rotated ? config.axis_x_inner ? "right" : "left" : config.axis_x_inner ? "top" : "bottom";
++        $$.yOrient = config.axis_rotated ? config.axis_y_inner ? "top" : "bottom" : config.axis_y_inner ? "right" : "left";
++        $$.y2Orient = config.axis_rotated ? config.axis_y2_inner ? "bottom" : "top" : config.axis_y2_inner ? "left" : "right";
++        $$.subXOrient = config.axis_rotated ? "left" : "bottom";
++
++        $$.isLegendRight = config.legend_position === 'right';
++        $$.isLegendInset = config.legend_position === 'inset';
++        $$.isLegendTop = config.legend_inset_anchor === 'top-left' || config.legend_inset_anchor === 'top-right';
++        $$.isLegendLeft = config.legend_inset_anchor === 'top-left' || config.legend_inset_anchor === 'bottom-left';
++        $$.legendStep = 0;
++        $$.legendItemWidth = 0;
++        $$.legendItemHeight = 0;
++
++        $$.currentMaxTickWidths = {
++            x: 0,
++            y: 0,
++            y2: 0
++        };
++
++        $$.rotated_padding_left = 30;
++        $$.rotated_padding_right = config.axis_rotated && !config.axis_x_show ? 0 : 30;
++        $$.rotated_padding_top = 5;
++
++        $$.withoutFadeIn = {};
++
++        $$.intervalForObserveInserted = undefined;
++
++        $$.axes.subx = d3.selectAll([]); // needs when excluding subchart.js
++    };
++
++    ChartInternal.prototype.initChartElements = function () {
++        if (this.initBar) {
++            this.initBar();
++        }
++        if (this.initLine) {
++            this.initLine();
++        }
++        if (this.initArc) {
++            this.initArc();
++        }
++        if (this.initGauge) {
++            this.initGauge();
++        }
++        if (this.initText) {
++            this.initText();
++        }
++    };
++
++    ChartInternal.prototype.initWithData = function (data) {
++        var $$ = this,
++            d3 = $$.d3,
++            config = $$.config;
++        var defs,
++            main,
++            binding = true;
++
++        $$.axis = new Axis($$);
++
++        if (!config.bindto) {
++            $$.selectChart = d3.selectAll([]);
++        } else if (typeof config.bindto.node === 'function') {
++            $$.selectChart = config.bindto;
++        } else {
++            $$.selectChart = d3.select(config.bindto);
++        }
++        if ($$.selectChart.empty()) {
++            $$.selectChart = d3.select(document.createElement('div')).style('opacity', 0);
++            $$.observeInserted($$.selectChart);
++            binding = false;
++        }
++        $$.selectChart.html("").classed("c3", true);
++
++        // Init data as targets
++        $$.data.xs = {};
++        $$.data.targets = $$.convertDataToTargets(data);
++
++        if (config.data_filter) {
++            $$.data.targets = $$.data.targets.filter(config.data_filter);
++        }
++
++        // Set targets to hide if needed
++        if (config.data_hide) {
++            $$.addHiddenTargetIds(config.data_hide === true ? $$.mapToIds($$.data.targets) : config.data_hide);
++        }
++        if (config.legend_hide) {
++            $$.addHiddenLegendIds(config.legend_hide === true ? $$.mapToIds($$.data.targets) : config.legend_hide);
++        }
++
++        // Init sizes and scales
++        $$.updateSizes();
++        $$.updateScales();
++
++        // Set domains for each scale
++        $$.x.domain(d3.extent($$.getXDomain($$.data.targets)));
++        $$.y.domain($$.getYDomain($$.data.targets, 'y'));
++        $$.y2.domain($$.getYDomain($$.data.targets, 'y2'));
++        $$.subX.domain($$.x.domain());
++        $$.subY.domain($$.y.domain());
++        $$.subY2.domain($$.y2.domain());
++
++        // Save original x domain for zoom update
++        $$.orgXDomain = $$.x.domain();
++
++        /*-- Basic Elements --*/
++
++        // Define svgs
++        $$.svg = $$.selectChart.append("svg").style("overflow", "hidden").on('mouseenter', function () {
++            return config.onmouseover.call($$);
++        }).on('mouseleave', function () {
++            return config.onmouseout.call($$);
++        });
++
++        if ($$.config.svg_classname) {
++            $$.svg.attr('class', $$.config.svg_classname);
++        }
++
++        // Define defs
++        defs = $$.svg.append("defs");
++        $$.clipChart = $$.appendClip(defs, $$.clipId);
++        $$.clipXAxis = $$.appendClip(defs, $$.clipIdForXAxis);
++        $$.clipYAxis = $$.appendClip(defs, $$.clipIdForYAxis);
++        $$.clipGrid = $$.appendClip(defs, $$.clipIdForGrid);
++        $$.clipSubchart = $$.appendClip(defs, $$.clipIdForSubchart);
++        $$.updateSvgSize();
++
++        // Define regions
++        main = $$.main = $$.svg.append("g").attr("transform", $$.getTranslate('main'));
++
++        if ($$.initPie) {
++            $$.initPie();
++        }
++        if ($$.initSubchart) {
++            $$.initSubchart();
++        }
++        if ($$.initTooltip) {
++            $$.initTooltip();
++        }
++        if ($$.initLegend) {
++            $$.initLegend();
++        }
++        if ($$.initTitle) {
++            $$.initTitle();
++        }
++        if ($$.initZoom) {
++            $$.initZoom();
++        }
++
++        // Update selection based on size and scale
++        // TODO: currently this must be called after initLegend because of update of sizes, but it should be done in initSubchart.
++        if ($$.initSubchartBrush) {
++            $$.initSubchartBrush();
++        }
++
++        /*-- Main Region --*/
++
++        // text when empty
++        main.append("text").attr("class", CLASS.text + ' ' + CLASS.empty).attr("text-anchor", "middle") // horizontal centering of text at x position in all browsers.
++        .attr("dominant-baseline", "middle"); // vertical centering of text at y position in all browsers, except IE.
++
++        // Regions
++        $$.initRegion();
++
++        // Grids
++        $$.initGrid();
++
++        // Define g for chart area
++        main.append('g').attr("clip-path", $$.clipPath).attr('class', CLASS.chart);
++
++        // Grid lines
++        if (config.grid_lines_front) {
++            $$.initGridLines();
++        }
++
++        // Cover whole with rects for events
++        $$.initEventRect();
++
++        // Define g for chart
++        $$.initChartElements();
++
++        // Add Axis
++        $$.axis.init();
++
++        // Set targets
++        $$.updateTargets($$.data.targets);
++
++        // Set default extent if defined
++        if (config.axis_x_selection) {
++            $$.brush.selectionAsValue($$.getDefaultSelection());
++        }
++
++        // Draw with targets
++        if (binding) {
++            $$.updateDimension();
++            $$.config.oninit.call($$);
++            $$.redraw({
++                withTransition: false,
++                withTransform: true,
++                withUpdateXDomain: true,
++                withUpdateOrgXDomain: true,
++                withTransitionForAxis: false
++            });
++        }
++
++        // Bind resize event
++        $$.bindResize();
++
++        // export element of the chart
++        $$.api.element = $$.selectChart.node();
++    };
++
++    ChartInternal.prototype.smoothLines = function (el, type) {
++        var $$ = this;
++        if (type === 'grid') {
++            el.each(function () {
++                var g = $$.d3.select(this),
++                    x1 = g.attr('x1'),
++                    x2 = g.attr('x2'),
++                    y1 = g.attr('y1'),
++                    y2 = g.attr('y2');
++                g.attr({
++                    'x1': Math.ceil(x1),
++                    'x2': Math.ceil(x2),
++                    'y1': Math.ceil(y1),
++                    'y2': Math.ceil(y2)
++                });
++            });
++        }
++    };
++
++    ChartInternal.prototype.updateSizes = function () {
++        var $$ = this,
++            config = $$.config;
++        var legendHeight = $$.legend ? $$.getLegendHeight() : 0,
++            legendWidth = $$.legend ? $$.getLegendWidth() : 0,
++            legendHeightForBottom = $$.isLegendRight || $$.isLegendInset ? 0 : legendHeight,
++            hasArc = $$.hasArcType(),
++            xAxisHeight = config.axis_rotated || hasArc ? 0 : $$.getHorizontalAxisHeight('x'),
++            subchartHeight = config.subchart_show && !hasArc ? config.subchart_size_height + xAxisHeight : 0;
++
++        $$.currentWidth = $$.getCurrentWidth();
++        $$.currentHeight = $$.getCurrentHeight();
++
++        // for main
++        $$.margin = config.axis_rotated ? {
++            top: $$.getHorizontalAxisHeight('y2') + $$.getCurrentPaddingTop(),
++            right: hasArc ? 0 : $$.getCurrentPaddingRight(),
++            bottom: $$.getHorizontalAxisHeight('y') + legendHeightForBottom + $$.getCurrentPaddingBottom(),
++            left: subchartHeight + (hasArc ? 0 : $$.getCurrentPaddingLeft())
++        } : {
++            top: 4 + $$.getCurrentPaddingTop(), // for top tick text
++            right: hasArc ? 0 : $$.getCurrentPaddingRight(),
++            bottom: xAxisHeight + subchartHeight + legendHeightForBottom + $$.getCurrentPaddingBottom(),
++            left: hasArc ? 0 : $$.getCurrentPaddingLeft()
++        };
++
++        // for subchart
++        $$.margin2 = config.axis_rotated ? {
++            top: $$.margin.top,
++            right: NaN,
++            bottom: 20 + legendHeightForBottom,
++            left: $$.rotated_padding_left
++        } : {
++            top: $$.currentHeight - subchartHeight - legendHeightForBottom,
++            right: NaN,
++            bottom: xAxisHeight + legendHeightForBottom,
++            left: $$.margin.left
++        };
++
++        // for legend
++        $$.margin3 = {
++            top: 0,
++            right: NaN,
++            bottom: 0,
++            left: 0
++        };
++        if ($$.updateSizeForLegend) {
++            $$.updateSizeForLegend(legendHeight, legendWidth);
++        }
++
++        $$.width = $$.currentWidth - $$.margin.left - $$.margin.right;
++        $$.height = $$.currentHeight - $$.margin.top - $$.margin.bottom;
++        if ($$.width < 0) {
++            $$.width = 0;
++        }
++        if ($$.height < 0) {
++            $$.height = 0;
++        }
++
++        $$.width2 = config.axis_rotated ? $$.margin.left - $$.rotated_padding_left - $$.rotated_padding_right : $$.width;
++        $$.height2 = config.axis_rotated ? $$.height : $$.currentHeight - $$.margin2.top - $$.margin2.bottom;
++        if ($$.width2 < 0) {
++            $$.width2 = 0;
++        }
++        if ($$.height2 < 0) {
++            $$.height2 = 0;
++        }
++
++        // for arc
++        $$.arcWidth = $$.width - ($$.isLegendRight ? legendWidth + 10 : 0);
++        $$.arcHeight = $$.height - ($$.isLegendRight ? 0 : 10);
++        if ($$.hasType('gauge') && !config.gauge_fullCircle) {
++            $$.arcHeight += $$.height - $$.getGaugeLabelHeight();
++        }
++        if ($$.updateRadius) {
++            $$.updateRadius();
++        }
++
++        if ($$.isLegendRight && hasArc) {
++            $$.margin3.left = $$.arcWidth / 2 + $$.radiusExpanded * 1.1;
++        }
++    };
++
++    ChartInternal.prototype.updateTargets = function (targets) {
++        var $$ = this;
++
++        /*-- Main --*/
++
++        //-- Text --//
++        $$.updateTargetsForText(targets);
++
++        //-- Bar --//
++        $$.updateTargetsForBar(targets);
++
++        //-- Line --//
++        $$.updateTargetsForLine(targets);
++
++        //-- Arc --//
++        if ($$.hasArcType() && $$.updateTargetsForArc) {
++            $$.updateTargetsForArc(targets);
++        }
++
++        /*-- Sub --*/
++
++        if ($$.updateTargetsForSubchart) {
++            $$.updateTargetsForSubchart(targets);
++        }
++
++        // Fade-in each chart
++        $$.showTargets();
++    };
++    ChartInternal.prototype.showTargets = function () {
++        var $$ = this;
++        $$.svg.selectAll('.' + CLASS.target).filter(function (d) {
++            return $$.isTargetToShow(d.id);
++        }).transition().duration($$.config.transition_duration).style("opacity", 1);
++    };
++
++    ChartInternal.prototype.redraw = function (options, transitions) {
++        var $$ = this,
++            main = $$.main,
++            d3 = $$.d3,
++            config = $$.config;
++        var areaIndices = $$.getShapeIndices($$.isAreaType),
++            barIndices = $$.getShapeIndices($$.isBarType),
++            lineIndices = $$.getShapeIndices($$.isLineType);
++        var withY, withSubchart, withTransition, withTransitionForExit, withTransitionForAxis, withTransform, withUpdateXDomain, withUpdateOrgXDomain, withTrimXDomain, withLegend, withEventRect, withDimension, withUpdateXAxis;
++        var hideAxis = $$.hasArcType();
++        var drawArea, drawBar, drawLine, xForText, yForText;
++        var duration, durationForExit, durationForAxis;
++        var transitionsToWait, waitForDraw, flow, transition;
++        var targetsToShow = $$.filterTargetsToShow($$.data.targets),
++            tickValues,
++            i,
++            intervalForCulling,
++            xDomainForZoom;
++        var xv = $$.xv.bind($$),
++            cx,
++            cy;
++
++        options = options || {};
++        withY = getOption(options, "withY", true);
++        withSubchart = getOption(options, "withSubchart", true);
++        withTransition = getOption(options, "withTransition", true);
++        withTransform = getOption(options, "withTransform", false);
++        withUpdateXDomain = getOption(options, "withUpdateXDomain", false);
++        withUpdateOrgXDomain = getOption(options, "withUpdateOrgXDomain", false);
++        withTrimXDomain = getOption(options, "withTrimXDomain", true);
++        withUpdateXAxis = getOption(options, "withUpdateXAxis", withUpdateXDomain);
++        withLegend = getOption(options, "withLegend", false);
++        withEventRect = getOption(options, "withEventRect", true);
++        withDimension = getOption(options, "withDimension", true);
++        withTransitionForExit = getOption(options, "withTransitionForExit", withTransition);
++        withTransitionForAxis = getOption(options, "withTransitionForAxis", withTransition);
++
++        duration = withTransition ? config.transition_duration : 0;
++        durationForExit = withTransitionForExit ? duration : 0;
++        durationForAxis = withTransitionForAxis ? duration : 0;
++
++        transitions = transitions || $$.axis.generateTransitions(durationForAxis);
++
++        // update legend and transform each g
++        if (withLegend && config.legend_show) {
++            $$.updateLegend($$.mapToIds($$.data.targets), options, transitions);
++        } else if (withDimension) {
++            // need to update dimension (e.g. axis.y.tick.values) because y tick values should change
++            // no need to update axis in it because they will be updated in redraw()
++            $$.updateDimension(true);
++        }
++
++        // MEMO: needed for grids calculation
++        if ($$.isCategorized() && targetsToShow.length === 0) {
++            $$.x.domain([0, $$.axes.x.selectAll('.tick').size()]);
++        }
++
++        if (targetsToShow.length) {
++            $$.updateXDomain(targetsToShow, withUpdateXDomain, withUpdateOrgXDomain, withTrimXDomain);
++            if (!config.axis_x_tick_values) {
++                tickValues = $$.axis.updateXAxisTickValues(targetsToShow);
++            }
++        } else {
++            $$.xAxis.tickValues([]);
++            $$.subXAxis.tickValues([]);
++        }
++
++        if (config.zoom_rescale && !options.flow) {
++            xDomainForZoom = $$.x.orgDomain();
++        }
++
++        $$.y.domain($$.getYDomain(targetsToShow, 'y', xDomainForZoom));
++        $$.y2.domain($$.getYDomain(targetsToShow, 'y2', xDomainForZoom));
++
++        if (!config.axis_y_tick_values && config.axis_y_tick_count) {
++            $$.yAxis.tickValues($$.axis.generateTickValues($$.y.domain(), config.axis_y_tick_count));
++        }
++        if (!config.axis_y2_tick_values && config.axis_y2_tick_count) {
++            $$.y2Axis.tickValues($$.axis.generateTickValues($$.y2.domain(), config.axis_y2_tick_count));
++        }
++
++        // axes
++        $$.axis.redraw(durationForAxis, hideAxis);
++
++        // Update axis label
++        $$.axis.updateLabels(withTransition);
++
++        // show/hide if manual culling needed
++        if ((withUpdateXDomain || withUpdateXAxis) && targetsToShow.length) {
++            if (config.axis_x_tick_culling && tickValues) {
++                for (i = 1; i < tickValues.length; i++) {
++                    if (tickValues.length / i < config.axis_x_tick_culling_max) {
++                        intervalForCulling = i;
++                        break;
++                    }
++                }
++                $$.svg.selectAll('.' + CLASS.axisX + ' .tick text').each(function (e) {
++                    var index = tickValues.indexOf(e);
++                    if (index >= 0) {
++                        d3.select(this).style('display', index % intervalForCulling ? 'none' : 'block');
++                    }
++                });
++            } else {
++                $$.svg.selectAll('.' + CLASS.axisX + ' .tick text').style('display', 'block');
++            }
++        }
++
++        // setup drawer - MEMO: these must be called after axis updated
++        drawArea = $$.generateDrawArea ? $$.generateDrawArea(areaIndices, false) : undefined;
++        drawBar = $$.generateDrawBar ? $$.generateDrawBar(barIndices) : undefined;
++        drawLine = $$.generateDrawLine ? $$.generateDrawLine(lineIndices, false) : undefined;
++        xForText = $$.generateXYForText(areaIndices, barIndices, lineIndices, true);
++        yForText = $$.generateXYForText(areaIndices, barIndices, lineIndices, false);
++
++        // update circleY based on updated parameters
++        $$.updateCircleY();
++        // generate circle x/y functions depending on updated params
++        cx = ($$.config.axis_rotated ? $$.circleY : $$.circleX).bind($$);
++        cy = ($$.config.axis_rotated ? $$.circleX : $$.circleY).bind($$);
++
++        // Update sub domain
++        if (withY) {
++            $$.subY.domain($$.getYDomain(targetsToShow, 'y'));
++            $$.subY2.domain($$.getYDomain(targetsToShow, 'y2'));
++        }
++
++        // xgrid focus
++        $$.updateXgridFocus();
++
++        // Data empty label positioning and text.
++        main.select("text." + CLASS.text + '.' + CLASS.empty).attr("x", $$.width / 2).attr("y", $$.height / 2).text(config.data_empty_label_text).transition().style('opacity', targetsToShow.length ? 0 : 1);
++
++        // event rect
++        if (withEventRect) {
++            $$.redrawEventRect();
++        }
++
++        // grid
++        $$.updateGrid(duration);
++
++        // rect for regions
++        $$.updateRegion(duration);
++
++        // bars
++        $$.updateBar(durationForExit);
++
++        // lines, areas and cricles
++        $$.updateLine(durationForExit);
++        $$.updateArea(durationForExit);
++        $$.updateCircle(cx, cy);
++
++        // text
++        if ($$.hasDataLabel()) {
++            $$.updateText(xForText, yForText, durationForExit);
++        }
++
++        // title
++        if ($$.redrawTitle) {
++            $$.redrawTitle();
++        }
++
++        // arc
++        if ($$.redrawArc) {
++            $$.redrawArc(duration, durationForExit, withTransform);
++        }
++
++        // subchart
++        if ($$.redrawSubchart) {
++            $$.redrawSubchart(withSubchart, transitions, duration, durationForExit, areaIndices, barIndices, lineIndices);
++        }
++
++        // circles for select
++        main.selectAll('.' + CLASS.selectedCircles).filter($$.isBarType.bind($$)).selectAll('circle').remove();
++
++        if (options.flow) {
++            flow = $$.generateFlow({
++                targets: targetsToShow,
++                flow: options.flow,
++                duration: options.flow.duration,
++                drawBar: drawBar,
++                drawLine: drawLine,
++                drawArea: drawArea,
++                cx: cx,
++                cy: cy,
++                xv: xv,
++                xForText: xForText,
++                yForText: yForText
++            });
++        }
++
++        if ($$.isTabVisible()) {
++            // Only use transition if tab visible. See #938.
++            if (duration) {
++                // transition should be derived from one transition
++                transition = d3.transition().duration(duration);
++                transitionsToWait = [];
++                [$$.redrawBar(drawBar, true, transition), $$.redrawLine(drawLine, true, transition), $$.redrawArea(drawArea, true, transition), $$.redrawCircle(cx, cy, true, transition), $$.redrawText(xForText, yForText, options.flow, true, transition), $$.redrawRegion(true, transition), $$.redrawGrid(true, transition)].forEach(function (transitions) {
++                    transitions.forEach(function (transition) {
++                        transitionsToWait.push(transition);
++                    });
++                });
++                // Wait for end of transitions to call flow and onrendered callback
++                waitForDraw = $$.generateWait();
++                transitionsToWait.forEach(function (t) {
++                    waitForDraw.add(t);
++                });
++                waitForDraw(function () {
++                    if (flow) {
++                        flow();
++                    }
++                    if (config.onrendered) {
++                        config.onrendered.call($$);
++                    }
++                });
++            } else {
++                $$.redrawBar(drawBar);
++                $$.redrawLine(drawLine);
++                $$.redrawArea(drawArea);
++                $$.redrawCircle(cx, cy);
++                $$.redrawText(xForText, yForText, options.flow);
++                $$.redrawRegion();
++                $$.redrawGrid();
++                if (flow) {
++                    flow();
++                }
++                if (config.onrendered) {
++                    config.onrendered.call($$);
++                }
++            }
++        }
++
++        // update fadein condition
++        $$.mapToIds($$.data.targets).forEach(function (id) {
++            $$.withoutFadeIn[id] = true;
++        });
++    };
++
++    ChartInternal.prototype.updateAndRedraw = function (options) {
++        var $$ = this,
++            config = $$.config,
++            transitions;
++        options = options || {};
++        // same with redraw
++        options.withTransition = getOption(options, "withTransition", true);
++        options.withTransform = getOption(options, "withTransform", false);
++        options.withLegend = getOption(options, "withLegend", false);
++        // NOT same with redraw
++        options.withUpdateXDomain = getOption(options, "withUpdateXDomain", true);
++        options.withUpdateOrgXDomain = getOption(options, "withUpdateOrgXDomain", true);
++        options.withTransitionForExit = false;
++        options.withTransitionForTransform = getOption(options, "withTransitionForTransform", options.withTransition);
++        // MEMO: this needs to be called before updateLegend and it means this ALWAYS needs to be called)
++        $$.updateSizes();
++        // MEMO: called in updateLegend in redraw if withLegend
++        if (!(options.withLegend && config.legend_show)) {
++            transitions = $$.axis.generateTransitions(options.withTransitionForAxis ? config.transition_duration : 0);
++            // Update scales
++            $$.updateScales();
++            $$.updateSvgSize();
++            // Update g positions
++            $$.transformAll(options.withTransitionForTransform, transitions);
++        }
++        // Draw with new sizes & scales
++        $$.redraw(options, transitions);
++    };
++    ChartInternal.prototype.redrawWithoutRescale = function () {
++        this.redraw({
++            withY: false,
++            withSubchart: false,
++            withEventRect: false,
++            withTransitionForAxis: false
++        });
++    };
++
++    ChartInternal.prototype.isTimeSeries = function () {
++        return this.config.axis_x_type === 'timeseries';
++    };
++    ChartInternal.prototype.isCategorized = function () {
++        return this.config.axis_x_type.indexOf('categor') >= 0;
++    };
++    ChartInternal.prototype.isCustomX = function () {
++        var $$ = this,
++            config = $$.config;
++        return !$$.isTimeSeries() && (config.data_x || notEmpty(config.data_xs));
++    };
++
++    ChartInternal.prototype.isTimeSeriesY = function () {
++        return this.config.axis_y_type === 'timeseries';
++    };
++
++    ChartInternal.prototype.getTranslate = function (target) {
++        var $$ = this,
++            config = $$.config,
++            x,
++            y;
++        if (target === 'main') {
++            x = asHalfPixel($$.margin.left);
++            y = asHalfPixel($$.margin.top);
++        } else if (target === 'context') {
++            x = asHalfPixel($$.margin2.left);
++            y = asHalfPixel($$.margin2.top);
++        } else if (target === 'legend') {
++            x = $$.margin3.left;
++            y = $$.margin3.top;
++        } else if (target === 'x') {
++            x = 0;
++            y = config.axis_rotated ? 0 : $$.height;
++        } else if (target === 'y') {
++            x = 0;
++            y = config.axis_rotated ? $$.height : 0;
++        } else if (target === 'y2') {
++            x = config.axis_rotated ? 0 : $$.width;
++            y = config.axis_rotated ? 1 : 0;
++        } else if (target === 'subx') {
++            x = 0;
++            y = config.axis_rotated ? 0 : $$.height2;
++        } else if (target === 'arc') {
++            x = $$.arcWidth / 2;
++            y = $$.arcHeight / 2 - ($$.hasType('gauge') ? 6 : 0); // to prevent wrong display of min and max label
++        }
++        return "translate(" + x + "," + y + ")";
++    };
++    ChartInternal.prototype.initialOpacity = function (d) {
++        return d.value !== null && this.withoutFadeIn[d.id] ? 1 : 0;
++    };
++    ChartInternal.prototype.initialOpacityForCircle = function (d) {
++        return d.value !== null && this.withoutFadeIn[d.id] ? this.opacityForCircle(d) : 0;
++    };
++    ChartInternal.prototype.opacityForCircle = function (d) {
++        var isPointShouldBeShown = isFunction(this.config.point_show) ? this.config.point_show(d) : this.config.point_show;
++        var opacity = isPointShouldBeShown ? 1 : 0;
++        return isValue(d.value) ? this.isScatterType(d) ? 0.5 : opacity : 0;
++    };
++    ChartInternal.prototype.opacityForText = function () {
++        return this.hasDataLabel() ? 1 : 0;
++    };
++    ChartInternal.prototype.xx = function (d) {
++        return d ? this.x(d.x) : null;
++    };
++    ChartInternal.prototype.xv = function (d) {
++        var $$ = this,
++            value = d.value;
++        if ($$.isTimeSeries()) {
++            value = $$.parseDate(d.value);
++        } else if ($$.isCategorized() && typeof d.value === 'string') {
++            value = $$.config.axis_x_categories.indexOf(d.value);
++        }
++        return Math.ceil($$.x(value));
++    };
++    ChartInternal.prototype.yv = function (d) {
++        var $$ = this,
++            yScale = d.axis && d.axis === 'y2' ? $$.y2 : $$.y;
++        return Math.ceil(yScale(d.value));
++    };
++    ChartInternal.prototype.subxx = function (d) {
++        return d ? this.subX(d.x) : null;
++    };
++
++    ChartInternal.prototype.transformMain = function (withTransition, transitions) {
++        var $$ = this,
++            xAxis,
++            yAxis,
++            y2Axis;
++        if (transitions && transitions.axisX) {
++            xAxis = transitions.axisX;
++        } else {
++            xAxis = $$.main.select('.' + CLASS.axisX);
++            if (withTransition) {
++                xAxis = xAxis.transition();
++            }
++        }
++        if (transitions && transitions.axisY) {
++            yAxis = transitions.axisY;
++        } else {
++            yAxis = $$.main.select('.' + CLASS.axisY);
++            if (withTransition) {
++                yAxis = yAxis.transition();
++            }
++        }
++        if (transitions && transitions.axisY2) {
++            y2Axis = transitions.axisY2;
++        } else {
++            y2Axis = $$.main.select('.' + CLASS.axisY2);
++            if (withTransition) {
++                y2Axis = y2Axis.transition();
++            }
++        }
++        (withTransition ? $$.main.transition() : $$.main).attr("transform", $$.getTranslate('main'));
++        xAxis.attr("transform", $$.getTranslate('x'));
++        yAxis.attr("transform", $$.getTranslate('y'));
++        y2Axis.attr("transform", $$.getTranslate('y2'));
++        $$.main.select('.' + CLASS.chartArcs).attr("transform", $$.getTranslate('arc'));
++    };
++    ChartInternal.prototype.transformAll = function (withTransition, transitions) {
++        var $$ = this;
++        $$.transformMain(withTransition, transitions);
++        if ($$.config.subchart_show) {
++            $$.transformContext(withTransition, transitions);
++        }
++        if ($$.legend) {
++            $$.transformLegend(withTransition);
++        }
++    };
++
++    ChartInternal.prototype.updateSvgSize = function () {
++        var $$ = this,
++            brush = $$.svg.select(".c3-brush .overlay");
++        $$.svg.attr('width', $$.currentWidth).attr('height', $$.currentHeight);
++        $$.svg.selectAll(['#' + $$.clipId, '#' + $$.clipIdForGrid]).select('rect').attr('width', $$.width).attr('height', $$.height);
++        $$.svg.select('#' + $$.clipIdForXAxis).select('rect').attr('x', $$.getXAxisClipX.bind($$)).attr('y', $$.getXAxisClipY.bind($$)).attr('width', $$.getXAxisClipWidth.bind($$)).attr('height', $$.getXAxisClipHeight.bind($$));
++        $$.svg.select('#' + $$.clipIdForYAxis).select('rect').attr('x', $$.getYAxisClipX.bind($$)).attr('y', $$.getYAxisClipY.bind($$)).attr('width', $$.getYAxisClipWidth.bind($$)).attr('height', $$.getYAxisClipHeight.bind($$));
++        $$.svg.select('#' + $$.clipIdForSubchart).select('rect').attr('width', $$.width).attr('height', brush.size() ? brush.attr('height') : 0);
++        // MEMO: parent div's height will be bigger than svg when <!DOCTYPE html>
++        $$.selectChart.style('max-height', $$.currentHeight + "px");
++    };
++
++    ChartInternal.prototype.updateDimension = function (withoutAxis) {
++        var $$ = this;
++        if (!withoutAxis) {
++            if ($$.config.axis_rotated) {
++                $$.axes.x.call($$.xAxis);
++                $$.axes.subx.call($$.subXAxis);
++            } else {
++                $$.axes.y.call($$.yAxis);
++                $$.axes.y2.call($$.y2Axis);
++            }
++        }
++        $$.updateSizes();
++        $$.updateScales();
++        $$.updateSvgSize();
++        $$.transformAll(false);
++    };
++
++    ChartInternal.prototype.observeInserted = function (selection) {
++        var $$ = this,
++            observer;
++        if (typeof MutationObserver === 'undefined') {
++            window.console.error("MutationObserver not defined.");
++            return;
++        }
++        observer = new MutationObserver(function (mutations) {
++            mutations.forEach(function (mutation) {
++                if (mutation.type === 'childList' && mutation.previousSibling) {
++                    observer.disconnect();
++                    // need to wait for completion of load because size calculation requires the actual sizes determined after that completion
++                    $$.intervalForObserveInserted = window.setInterval(function () {
++                        // parentNode will NOT be null when completed
++                        if (selection.node().parentNode) {
++                            window.clearInterval($$.intervalForObserveInserted);
++                            $$.updateDimension();
++                            if ($$.brush) {
++                                $$.brush.update();
++                            }
++                            $$.config.oninit.call($$);
++                            $$.redraw({
++                                withTransform: true,
++                                withUpdateXDomain: true,
++                                withUpdateOrgXDomain: true,
++                                withTransition: false,
++                                withTransitionForTransform: false,
++                                withLegend: true
++                            });
++                            selection.transition().style('opacity', 1);
++                        }
++                    }, 10);
++                }
++            });
++        });
++        observer.observe(selection.node(), {
++            attributes: true,
++            childList: true,
++            characterData: true
++        });
++    };
++
++    ChartInternal.prototype.bindResize = function () {
++        var $$ = this,
++            config = $$.config;
++
++        $$.resizeFunction = $$.generateResize(); // need to call .remove
++
++        $$.resizeFunction.add(function () {
++            config.onresize.call($$);
++        });
++        if (config.resize_auto) {
++            $$.resizeFunction.add(function () {
++                if ($$.resizeTimeout !== undefined) {
++                    window.clearTimeout($$.resizeTimeout);
++                }
++                $$.resizeTimeout = window.setTimeout(function () {
++                    delete $$.resizeTimeout;
++                    $$.updateAndRedraw({
++                        withUpdateXDomain: false,
++                        withUpdateOrgXDomain: false,
++                        withTransition: false,
++                        withTransitionForTransform: false,
++                        withLegend: true
++                    });
++                    if ($$.brush) {
++                        $$.brush.update();
++                    }
++                }, 100);
++            });
++        }
++        $$.resizeFunction.add(function () {
++            config.onresized.call($$);
++        });
++
++        $$.resizeIfElementDisplayed = function () {
++            // if element not displayed skip it
++            if ($$.api == null || !$$.api.element.offsetParent) {
++                return;
++            }
++
++            $$.resizeFunction();
++        };
++
++        if (window.attachEvent) {
++            window.attachEvent('onresize', $$.resizeIfElementDisplayed);
++        } else if (window.addEventListener) {
++            window.addEventListener('resize', $$.resizeIfElementDisplayed, false);
++        } else {
++            // fallback to this, if this is a very old browser
++            var wrapper = window.onresize;
++            if (!wrapper) {
++                // create a wrapper that will call all charts
++                wrapper = $$.generateResize();
++            } else if (!wrapper.add || !wrapper.remove) {
++                // there is already a handler registered, make sure we call it too
++                wrapper = $$.generateResize();
++                wrapper.add(window.onresize);
++            }
++            // add this graph to the wrapper, we will be removed if the user calls destroy
++            wrapper.add($$.resizeFunction);
++            window.onresize = function () {
++                // if element not displayed skip it
++                if (!$$.api.element.offsetParent) {
++                    return;
++                }
++
++                wrapper();
++            };
++        }
++    };
++
++    ChartInternal.prototype.generateResize = function () {
++        var resizeFunctions = [];
++
++        function callResizeFunctions() {
++            resizeFunctions.forEach(function (f) {
++                f();
++            });
++        }
++        callResizeFunctions.add = function (f) {
++            resizeFunctions.push(f);
++        };
++        callResizeFunctions.remove = function (f) {
++            for (var i = 0; i < resizeFunctions.length; i++) {
++                if (resizeFunctions[i] === f) {
++                    resizeFunctions.splice(i, 1);
++                    break;
++                }
++            }
++        };
++        return callResizeFunctions;
++    };
++
++    ChartInternal.prototype.endall = function (transition, callback) {
++        var n = 0;
++        transition.each(function () {
++            ++n;
++        }).on("end", function () {
++            if (! --n) {
++                callback.apply(this, arguments);
++            }
++        });
++    };
++    ChartInternal.prototype.generateWait = function () {
++        var transitionsToWait = [],
++            f = function f(callback) {
++            var timer = setInterval(function () {
++                var done = 0;
++                transitionsToWait.forEach(function (t) {
++                    if (t.empty()) {
++                        done += 1;
++                        return;
++                    }
++                    try {
++                        t.transition();
++                    } catch (e) {
++                        done += 1;
++                    }
++                });
++                if (done === transitionsToWait.length) {
++                    clearInterval(timer);
++                    if (callback) {
++                        callback();
++                    }
++                }
++            }, 50);
++        };
++        f.add = function (transition) {
++            transitionsToWait.push(transition);
++        };
++        return f;
++    };
++
++    ChartInternal.prototype.parseDate = function (date) {
++        var $$ = this,
++            parsedDate;
++        if (date instanceof Date) {
++            parsedDate = date;
++        } else if (typeof date === 'string') {
++            parsedDate = $$.dataTimeParse(date);
++        } else if ((typeof date === 'undefined' ? 'undefined' : _typeof(date)) === 'object') {
++            parsedDate = new Date(+date);
++        } else if (typeof date === 'number' && !isNaN(date)) {
++            parsedDate = new Date(+date);
++        }
++        if (!parsedDate || isNaN(+parsedDate)) {
++            window.console.error("Failed to parse x '" + date + "' to Date object");
++        }
++        return parsedDate;
++    };
++
++    ChartInternal.prototype.isTabVisible = function () {
++        var hidden;
++        if (typeof document.hidden !== "undefined") {
++            // Opera 12.10 and Firefox 18 and later support
++            hidden = "hidden";
++        } else if (typeof document.mozHidden !== "undefined") {
++            hidden = "mozHidden";
++        } else if (typeof document.msHidden !== "undefined") {
++            hidden = "msHidden";
++        } else if (typeof document.webkitHidden !== "undefined") {
++            hidden = "webkitHidden";
++        }
++
++        return document[hidden] ? false : true;
++    };
++
++    ChartInternal.prototype.getPathBox = getPathBox;
++    ChartInternal.prototype.CLASS = CLASS;
++
++    /* jshint ignore:start */
++
++    // PhantomJS doesn't have support for Function.prototype.bind, which has caused confusion. Use
++    // this polyfill to avoid the confusion.
++    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Polyfill
++
++    if (!Function.prototype.bind) {
++        Function.prototype.bind = function (oThis) {
++            if (typeof this !== 'function') {
++                // closest thing possible to the ECMAScript 5
++                // internal IsCallable function
++                throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
++            }
++
++            var aArgs = Array.prototype.slice.call(arguments, 1),
++                fToBind = this,
++                fNOP = function fNOP() {},
++                fBound = function fBound() {
++                return fToBind.apply(this instanceof fNOP ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments)));
++            };
++
++            fNOP.prototype = this.prototype;
++            fBound.prototype = new fNOP();
++
++            return fBound;
++        };
++    }
++
++    // SVGPathSeg API polyfill
++    // https://github.com/progers/pathseg
++    //
++    // This is a drop-in replacement for the SVGPathSeg and SVGPathSegList APIs that were removed from
++    // SVG2 (https://lists.w3.org/Archives/Public/www-svg/2015Jun/0044.html), including the latest spec
++    // changes which were implemented in Firefox 43 and Chrome 46.
++
++    (function () {
++
++        if (!("SVGPathSeg" in window)) {
++            // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGPathSeg
++            window.SVGPathSeg = function (type, typeAsLetter, owningPathSegList) {
++                this.pathSegType = type;
++                this.pathSegTypeAsLetter = typeAsLetter;
++                this._owningPathSegList = owningPathSegList;
++            };
++
++            window.SVGPathSeg.prototype.classname = "SVGPathSeg";
++
++            window.SVGPathSeg.PATHSEG_UNKNOWN = 0;
++            window.SVGPathSeg.PATHSEG_CLOSEPATH = 1;
++            window.SVGPathSeg.PATHSEG_MOVETO_ABS = 2;
++            window.SVGPathSeg.PATHSEG_MOVETO_REL = 3;
++            window.SVGPathSeg.PATHSEG_LINETO_ABS = 4;
++            window.SVGPathSeg.PATHSEG_LINETO_REL = 5;
++            window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS = 6;
++            window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL = 7;
++            window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS = 8;
++            window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL = 9;
++            window.SVGPathSeg.PATHSEG_ARC_ABS = 10;
++            window.SVGPathSeg.PATHSEG_ARC_REL = 11;
++            window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS = 12;
++            window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL = 13;
++            window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS = 14;
++            window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL = 15;
++            window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS = 16;
++            window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL = 17;
++            window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS = 18;
++            window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL = 19;
++
++            // Notify owning PathSegList on any changes so they can be synchronized back to the path element.
++            window.SVGPathSeg.prototype._segmentChanged = function () {
++                if (this._owningPathSegList) this._owningPathSegList.segmentChanged(this);
++            };
++
++            window.SVGPathSegClosePath = function (owningPathSegList) {
++                window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CLOSEPATH, "z", owningPathSegList);
++            };
++            window.SVGPathSegClosePath.prototype = Object.create(window.SVGPathSeg.prototype);
++            window.SVGPathSegClosePath.prototype.toString = function () {
++                return "[object SVGPathSegClosePath]";
++            };
++            window.SVGPathSegClosePath.prototype._asPathString = function () {
++                return this.pathSegTypeAsLetter;
++            };
++            window.SVGPathSegClosePath.prototype.clone = function () {
++                return new window.SVGPathSegClosePath(undefined);
++            };
++
++            window.SVGPathSegMovetoAbs = function (owningPathSegList, x, y) {
++                window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_MOVETO_ABS, "M", owningPathSegList);
++                this._x = x;
++                this._y = y;
++            };
++            window.SVGPathSegMovetoAbs.prototype = Object.create(window.SVGPathSeg.prototype);
++            window.SVGPathSegMovetoAbs.prototype.toString = function () {
++                return "[object SVGPathSegMovetoAbs]";
++            };
++            window.SVGPathSegMovetoAbs.prototype._asPathString = function () {
++                return this.pathSegTypeAsLetter + " " + this._x + " " + this._y;
++            };
++            window.SVGPathSegMovetoAbs.prototype.clone = function () {
++                return new window.SVGPathSegMovetoAbs(undefined, this._x, this._y);
++            };
++            Object.defineProperty(window.SVGPathSegMovetoAbs.prototype, "x", {
++                get: function get() {
++                    return this._x;
++                },
++                set: function set(x) {
++                    this._x = x;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegMovetoAbs.prototype, "y", {
++                get: function get() {
++                    return this._y;
++                },
++                set: function set(y) {
++                    this._y = y;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++
++            window.SVGPathSegMovetoRel = function (owningPathSegList, x, y) {
++                window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_MOVETO_REL, "m", owningPathSegList);
++                this._x = x;
++                this._y = y;
++            };
++            window.SVGPathSegMovetoRel.prototype = Object.create(window.SVGPathSeg.prototype);
++            window.SVGPathSegMovetoRel.prototype.toString = function () {
++                return "[object SVGPathSegMovetoRel]";
++            };
++            window.SVGPathSegMovetoRel.prototype._asPathString = function () {
++                return this.pathSegTypeAsLetter + " " + this._x + " " + this._y;
++            };
++            window.SVGPathSegMovetoRel.prototype.clone = function () {
++                return new window.SVGPathSegMovetoRel(undefined, this._x, this._y);
++            };
++            Object.defineProperty(window.SVGPathSegMovetoRel.prototype, "x", {
++                get: function get() {
++                    return this._x;
++                },
++                set: function set(x) {
++                    this._x = x;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegMovetoRel.prototype, "y", {
++                get: function get() {
++                    return this._y;
++                },
++                set: function set(y) {
++                    this._y = y;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++
++            window.SVGPathSegLinetoAbs = function (owningPathSegList, x, y) {
++                window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_ABS, "L", owningPathSegList);
++                this._x = x;
++                this._y = y;
++            };
++            window.SVGPathSegLinetoAbs.prototype = Object.create(window.SVGPathSeg.prototype);
++            window.SVGPathSegLinetoAbs.prototype.toString = function () {
++                return "[object SVGPathSegLinetoAbs]";
++            };
++            window.SVGPathSegLinetoAbs.prototype._asPathString = function () {
++                return this.pathSegTypeAsLetter + " " + this._x + " " + this._y;
++            };
++            window.SVGPathSegLinetoAbs.prototype.clone = function () {
++                return new window.SVGPathSegLinetoAbs(undefined, this._x, this._y);
++            };
++            Object.defineProperty(window.SVGPathSegLinetoAbs.prototype, "x", {
++                get: function get() {
++                    return this._x;
++                },
++                set: function set(x) {
++                    this._x = x;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegLinetoAbs.prototype, "y", {
++                get: function get() {
++                    return this._y;
++                },
++                set: function set(y) {
++                    this._y = y;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++
++            window.SVGPathSegLinetoRel = function (owningPathSegList, x, y) {
++                window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_REL, "l", owningPathSegList);
++                this._x = x;
++                this._y = y;
++            };
++            window.SVGPathSegLinetoRel.prototype = Object.create(window.SVGPathSeg.prototype);
++            window.SVGPathSegLinetoRel.prototype.toString = function () {
++                return "[object SVGPathSegLinetoRel]";
++            };
++            window.SVGPathSegLinetoRel.prototype._asPathString = function () {
++                return this.pathSegTypeAsLetter + " " + this._x + " " + this._y;
++            };
++            window.SVGPathSegLinetoRel.prototype.clone = function () {
++                return new window.SVGPathSegLinetoRel(undefined, this._x, this._y);
++            };
++            Object.defineProperty(window.SVGPathSegLinetoRel.prototype, "x", {
++                get: function get() {
++                    return this._x;
++                },
++                set: function set(x) {
++                    this._x = x;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegLinetoRel.prototype, "y", {
++                get: function get() {
++                    return this._y;
++                },
++                set: function set(y) {
++                    this._y = y;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++
++            window.SVGPathSegCurvetoCubicAbs = function (owningPathSegList, x, y, x1, y1, x2, y2) {
++                window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS, "C", owningPathSegList);
++                this._x = x;
++                this._y = y;
++                this._x1 = x1;
++                this._y1 = y1;
++                this._x2 = x2;
++                this._y2 = y2;
++            };
++            window.SVGPathSegCurvetoCubicAbs.prototype = Object.create(window.SVGPathSeg.prototype);
++            window.SVGPathSegCurvetoCubicAbs.prototype.toString = function () {
++                return "[object SVGPathSegCurvetoCubicAbs]";
++            };
++            window.SVGPathSegCurvetoCubicAbs.prototype._asPathString = function () {
++                return this.pathSegTypeAsLetter + " " + this._x1 + " " + this._y1 + " " + this._x2 + " " + this._y2 + " " + this._x + " " + this._y;
++            };
++            window.SVGPathSegCurvetoCubicAbs.prototype.clone = function () {
++                return new window.SVGPathSegCurvetoCubicAbs(undefined, this._x, this._y, this._x1, this._y1, this._x2, this._y2);
++            };
++            Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "x", {
++                get: function get() {
++                    return this._x;
++                },
++                set: function set(x) {
++                    this._x = x;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "y", {
++                get: function get() {
++                    return this._y;
++                },
++                set: function set(y) {
++                    this._y = y;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "x1", {
++                get: function get() {
++                    return this._x1;
++                },
++                set: function set(x1) {
++                    this._x1 = x1;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "y1", {
++                get: function get() {
++                    return this._y1;
++                },
++                set: function set(y1) {
++                    this._y1 = y1;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "x2", {
++                get: function get() {
++                    return this._x2;
++                },
++                set: function set(x2) {
++                    this._x2 = x2;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, "y2", {
++                get: function get() {
++                    return this._y2;
++                },
++                set: function set(y2) {
++                    this._y2 = y2;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++
++            window.SVGPathSegCurvetoCubicRel = function (owningPathSegList, x, y, x1, y1, x2, y2) {
++                window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL, "c", owningPathSegList);
++                this._x = x;
++                this._y = y;
++                this._x1 = x1;
++                this._y1 = y1;
++                this._x2 = x2;
++                this._y2 = y2;
++            };
++            window.SVGPathSegCurvetoCubicRel.prototype = Object.create(window.SVGPathSeg.prototype);
++            window.SVGPathSegCurvetoCubicRel.prototype.toString = function () {
++                return "[object SVGPathSegCurvetoCubicRel]";
++            };
++            window.SVGPathSegCurvetoCubicRel.prototype._asPathString = function () {
++                return this.pathSegTypeAsLetter + " " + this._x1 + " " + this._y1 + " " + this._x2 + " " + this._y2 + " " + this._x + " " + this._y;
++            };
++            window.SVGPathSegCurvetoCubicRel.prototype.clone = function () {
++                return new window.SVGPathSegCurvetoCubicRel(undefined, this._x, this._y, this._x1, this._y1, this._x2, this._y2);
++            };
++            Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "x", {
++                get: function get() {
++                    return this._x;
++                },
++                set: function set(x) {
++                    this._x = x;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "y", {
++                get: function get() {
++                    return this._y;
++                },
++                set: function set(y) {
++                    this._y = y;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "x1", {
++                get: function get() {
++                    return this._x1;
++                },
++                set: function set(x1) {
++                    this._x1 = x1;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "y1", {
++                get: function get() {
++                    return this._y1;
++                },
++                set: function set(y1) {
++                    this._y1 = y1;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "x2", {
++                get: function get() {
++                    return this._x2;
++                },
++                set: function set(x2) {
++                    this._x2 = x2;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, "y2", {
++                get: function get() {
++                    return this._y2;
++                },
++                set: function set(y2) {
++                    this._y2 = y2;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++
++            window.SVGPathSegCurvetoQuadraticAbs = function (owningPathSegList, x, y, x1, y1) {
++                window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS, "Q", owningPathSegList);
++                this._x = x;
++                this._y = y;
++                this._x1 = x1;
++                this._y1 = y1;
++            };
++            window.SVGPathSegCurvetoQuadraticAbs.prototype = Object.create(window.SVGPathSeg.prototype);
++            window.SVGPathSegCurvetoQuadraticAbs.prototype.toString = function () {
++                return "[object SVGPathSegCurvetoQuadraticAbs]";
++            };
++            window.SVGPathSegCurvetoQuadraticAbs.prototype._asPathString = function () {
++                return this.pathSegTypeAsLetter + " " + this._x1 + " " + this._y1 + " " + this._x + " " + this._y;
++            };
++            window.SVGPathSegCurvetoQuadraticAbs.prototype.clone = function () {
++                return new window.SVGPathSegCurvetoQuadraticAbs(undefined, this._x, this._y, this._x1, this._y1);
++            };
++            Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, "x", {
++                get: function get() {
++                    return this._x;
++                },
++                set: function set(x) {
++                    this._x = x;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, "y", {
++                get: function get() {
++                    return this._y;
++                },
++                set: function set(y) {
++                    this._y = y;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, "x1", {
++                get: function get() {
++                    return this._x1;
++                },
++                set: function set(x1) {
++                    this._x1 = x1;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, "y1", {
++                get: function get() {
++                    return this._y1;
++                },
++                set: function set(y1) {
++                    this._y1 = y1;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++
++            window.SVGPathSegCurvetoQuadraticRel = function (owningPathSegList, x, y, x1, y1) {
++                window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL, "q", owningPathSegList);
++                this._x = x;
++                this._y = y;
++                this._x1 = x1;
++                this._y1 = y1;
++            };
++            window.SVGPathSegCurvetoQuadraticRel.prototype = Object.create(window.SVGPathSeg.prototype);
++            window.SVGPathSegCurvetoQuadraticRel.prototype.toString = function () {
++                return "[object SVGPathSegCurvetoQuadraticRel]";
++            };
++            window.SVGPathSegCurvetoQuadraticRel.prototype._asPathString = function () {
++                return this.pathSegTypeAsLetter + " " + this._x1 + " " + this._y1 + " " + this._x + " " + this._y;
++            };
++            window.SVGPathSegCurvetoQuadraticRel.prototype.clone = function () {
++                return new window.SVGPathSegCurvetoQuadraticRel(undefined, this._x, this._y, this._x1, this._y1);
++            };
++            Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, "x", {
++                get: function get() {
++                    return this._x;
++                },
++                set: function set(x) {
++                    this._x = x;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, "y", {
++                get: function get() {
++                    return this._y;
++                },
++                set: function set(y) {
++                    this._y = y;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, "x1", {
++                get: function get() {
++                    return this._x1;
++                },
++                set: function set(x1) {
++                    this._x1 = x1;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, "y1", {
++                get: function get() {
++                    return this._y1;
++                },
++                set: function set(y1) {
++                    this._y1 = y1;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++
++            window.SVGPathSegArcAbs = function (owningPathSegList, x, y, r1, r2, angle, largeArcFlag, sweepFlag) {
++                window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_ARC_ABS, "A", owningPathSegList);
++                this._x = x;
++                this._y = y;
++                this._r1 = r1;
++                this._r2 = r2;
++                this._angle = angle;
++                this._largeArcFlag = largeArcFlag;
++                this._sweepFlag = sweepFlag;
++            };
++            window.SVGPathSegArcAbs.prototype = Object.create(window.SVGPathSeg.prototype);
++            window.SVGPathSegArcAbs.prototype.toString = function () {
++                return "[object SVGPathSegArcAbs]";
++            };
++            window.SVGPathSegArcAbs.prototype._asPathString = function () {
++                return this.pathSegTypeAsLetter + " " + this._r1 + " " + this._r2 + " " + this._angle + " " + (this._largeArcFlag ? "1" : "0") + " " + (this._sweepFlag ? "1" : "0") + " " + this._x + " " + this._y;
++            };
++            window.SVGPathSegArcAbs.prototype.clone = function () {
++                return new window.SVGPathSegArcAbs(undefined, this._x, this._y, this._r1, this._r2, this._angle, this._largeArcFlag, this._sweepFlag);
++            };
++            Object.defineProperty(window.SVGPathSegArcAbs.prototype, "x", {
++                get: function get() {
++                    return this._x;
++                },
++                set: function set(x) {
++                    this._x = x;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegArcAbs.prototype, "y", {
++                get: function get() {
++                    return this._y;
++                },
++                set: function set(y) {
++                    this._y = y;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegArcAbs.prototype, "r1", {
++                get: function get() {
++                    return this._r1;
++                },
++                set: function set(r1) {
++                    this._r1 = r1;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegArcAbs.prototype, "r2", {
++                get: function get() {
++                    return this._r2;
++                },
++                set: function set(r2) {
++                    this._r2 = r2;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegArcAbs.prototype, "angle", {
++                get: function get() {
++                    return this._angle;
++                },
++                set: function set(angle) {
++                    this._angle = angle;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegArcAbs.prototype, "largeArcFlag", {
++                get: function get() {
++                    return this._largeArcFlag;
++                },
++                set: function set(largeArcFlag) {
++                    this._largeArcFlag = largeArcFlag;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegArcAbs.prototype, "sweepFlag", {
++                get: function get() {
++                    return this._sweepFlag;
++                },
++                set: function set(sweepFlag) {
++                    this._sweepFlag = sweepFlag;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++
++            window.SVGPathSegArcRel = function (owningPathSegList, x, y, r1, r2, angle, largeArcFlag, sweepFlag) {
++                window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_ARC_REL, "a", owningPathSegList);
++                this._x = x;
++                this._y = y;
++                this._r1 = r1;
++                this._r2 = r2;
++                this._angle = angle;
++                this._largeArcFlag = largeArcFlag;
++                this._sweepFlag = sweepFlag;
++            };
++            window.SVGPathSegArcRel.prototype = Object.create(window.SVGPathSeg.prototype);
++            window.SVGPathSegArcRel.prototype.toString = function () {
++                return "[object SVGPathSegArcRel]";
++            };
++            window.SVGPathSegArcRel.prototype._asPathString = function () {
++                return this.pathSegTypeAsLetter + " " + this._r1 + " " + this._r2 + " " + this._angle + " " + (this._largeArcFlag ? "1" : "0") + " " + (this._sweepFlag ? "1" : "0") + " " + this._x + " " + this._y;
++            };
++            window.SVGPathSegArcRel.prototype.clone = function () {
++                return new window.SVGPathSegArcRel(undefined, this._x, this._y, this._r1, this._r2, this._angle, this._largeArcFlag, this._sweepFlag);
++            };
++            Object.defineProperty(window.SVGPathSegArcRel.prototype, "x", {
++                get: function get() {
++                    return this._x;
++                },
++                set: function set(x) {
++                    this._x = x;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegArcRel.prototype, "y", {
++                get: function get() {
++                    return this._y;
++                },
++                set: function set(y) {
++                    this._y = y;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegArcRel.prototype, "r1", {
++                get: function get() {
++                    return this._r1;
++                },
++                set: function set(r1) {
++                    this._r1 = r1;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegArcRel.prototype, "r2", {
++                get: function get() {
++                    return this._r2;
++                },
++                set: function set(r2) {
++                    this._r2 = r2;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegArcRel.prototype, "angle", {
++                get: function get() {
++                    return this._angle;
++                },
++                set: function set(angle) {
++                    this._angle = angle;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegArcRel.prototype, "largeArcFlag", {
++                get: function get() {
++                    return this._largeArcFlag;
++                },
++                set: function set(largeArcFlag) {
++                    this._largeArcFlag = largeArcFlag;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegArcRel.prototype, "sweepFlag", {
++                get: function get() {
++                    return this._sweepFlag;
++                },
++                set: function set(sweepFlag) {
++                    this._sweepFlag = sweepFlag;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++
++            window.SVGPathSegLinetoHorizontalAbs = function (owningPathSegList, x) {
++                window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS, "H", owningPathSegList);
++                this._x = x;
++            };
++            window.SVGPathSegLinetoHorizontalAbs.prototype = Object.create(window.SVGPathSeg.prototype);
++            window.SVGPathSegLinetoHorizontalAbs.prototype.toString = function () {
++                return "[object SVGPathSegLinetoHorizontalAbs]";
++            };
++            window.SVGPathSegLinetoHorizontalAbs.prototype._asPathString = function () {
++                return this.pathSegTypeAsLetter + " " + this._x;
++            };
++            window.SVGPathSegLinetoHorizontalAbs.prototype.clone = function () {
++                return new window.SVGPathSegLinetoHorizontalAbs(undefined, this._x);
++            };
++            Object.defineProperty(window.SVGPathSegLinetoHorizontalAbs.prototype, "x", {
++                get: function get() {
++                    return this._x;
++                },
++                set: function set(x) {
++                    this._x = x;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++
++            window.SVGPathSegLinetoHorizontalRel = function (owningPathSegList, x) {
++                window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL, "h", owningPathSegList);
++                this._x = x;
++            };
++            window.SVGPathSegLinetoHorizontalRel.prototype = Object.create(window.SVGPathSeg.prototype);
++            window.SVGPathSegLinetoHorizontalRel.prototype.toString = function () {
++                return "[object SVGPathSegLinetoHorizontalRel]";
++            };
++            window.SVGPathSegLinetoHorizontalRel.prototype._asPathString = function () {
++                return this.pathSegTypeAsLetter + " " + this._x;
++            };
++            window.SVGPathSegLinetoHorizontalRel.prototype.clone = function () {
++                return new window.SVGPathSegLinetoHorizontalRel(undefined, this._x);
++            };
++            Object.defineProperty(window.SVGPathSegLinetoHorizontalRel.prototype, "x", {
++                get: function get() {
++                    return this._x;
++                },
++                set: function set(x) {
++                    this._x = x;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++
++            window.SVGPathSegLinetoVerticalAbs = function (owningPathSegList, y) {
++                window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS, "V", owningPathSegList);
++                this._y = y;
++            };
++            window.SVGPathSegLinetoVerticalAbs.prototype = Object.create(window.SVGPathSeg.prototype);
++            window.SVGPathSegLinetoVerticalAbs.prototype.toString = function () {
++                return "[object SVGPathSegLinetoVerticalAbs]";
++            };
++            window.SVGPathSegLinetoVerticalAbs.prototype._asPathString = function () {
++                return this.pathSegTypeAsLetter + " " + this._y;
++            };
++            window.SVGPathSegLinetoVerticalAbs.prototype.clone = function () {
++                return new window.SVGPathSegLinetoVerticalAbs(undefined, this._y);
++            };
++            Object.defineProperty(window.SVGPathSegLinetoVerticalAbs.prototype, "y", {
++                get: function get() {
++                    return this._y;
++                },
++                set: function set(y) {
++                    this._y = y;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++
++            window.SVGPathSegLinetoVerticalRel = function (owningPathSegList, y) {
++                window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL, "v", owningPathSegList);
++                this._y = y;
++            };
++            window.SVGPathSegLinetoVerticalRel.prototype = Object.create(window.SVGPathSeg.prototype);
++            window.SVGPathSegLinetoVerticalRel.prototype.toString = function () {
++                return "[object SVGPathSegLinetoVerticalRel]";
++            };
++            window.SVGPathSegLinetoVerticalRel.prototype._asPathString = function () {
++                return this.pathSegTypeAsLetter + " " + this._y;
++            };
++            window.SVGPathSegLinetoVerticalRel.prototype.clone = function () {
++                return new window.SVGPathSegLinetoVerticalRel(undefined, this._y);
++            };
++            Object.defineProperty(window.SVGPathSegLinetoVerticalRel.prototype, "y", {
++                get: function get() {
++                    return this._y;
++                },
++                set: function set(y) {
++                    this._y = y;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++
++            window.SVGPathSegCurvetoCubicSmoothAbs = function (owningPathSegList, x, y, x2, y2) {
++                window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS, "S", owningPathSegList);
++                this._x = x;
++                this._y = y;
++                this._x2 = x2;
++                this._y2 = y2;
++            };
++            window.SVGPathSegCurvetoCubicSmoothAbs.prototype = Object.create(window.SVGPathSeg.prototype);
++            window.SVGPathSegCurvetoCubicSmoothAbs.prototype.toString = function () {
++                return "[object SVGPathSegCurvetoCubicSmoothAbs]";
++            };
++            window.SVGPathSegCurvetoCubicSmoothAbs.prototype._asPathString = function () {
++                return this.pathSegTypeAsLetter + " " + this._x2 + " " + this._y2 + " " + this._x + " " + this._y;
++            };
++            window.SVGPathSegCurvetoCubicSmoothAbs.prototype.clone = function () {
++                return new window.SVGPathSegCurvetoCubicSmoothAbs(undefined, this._x, this._y, this._x2, this._y2);
++            };
++            Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, "x", {
++                get: function get() {
++                    return this._x;
++                },
++                set: function set(x) {
++                    this._x = x;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, "y", {
++                get: function get() {
++                    return this._y;
++                },
++                set: function set(y) {
++                    this._y = y;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, "x2", {
++                get: function get() {
++                    return this._x2;
++                },
++                set: function set(x2) {
++                    this._x2 = x2;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, "y2", {
++                get: function get() {
++                    return this._y2;
++                },
++                set: function set(y2) {
++                    this._y2 = y2;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++
++            window.SVGPathSegCurvetoCubicSmoothRel = function (owningPathSegList, x, y, x2, y2) {
++                window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL, "s", owningPathSegList);
++                this._x = x;
++                this._y = y;
++                this._x2 = x2;
++                this._y2 = y2;
++            };
++            window.SVGPathSegCurvetoCubicSmoothRel.prototype = Object.create(window.SVGPathSeg.prototype);
++            window.SVGPathSegCurvetoCubicSmoothRel.prototype.toString = function () {
++                return "[object SVGPathSegCurvetoCubicSmoothRel]";
++            };
++            window.SVGPathSegCurvetoCubicSmoothRel.prototype._asPathString = function () {
++                return this.pathSegTypeAsLetter + " " + this._x2 + " " + this._y2 + " " + this._x + " " + this._y;
++            };
++            window.SVGPathSegCurvetoCubicSmoothRel.prototype.clone = function () {
++                return new window.SVGPathSegCurvetoCubicSmoothRel(undefined, this._x, this._y, this._x2, this._y2);
++            };
++            Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, "x", {
++                get: function get() {
++                    return this._x;
++                },
++                set: function set(x) {
++                    this._x = x;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, "y", {
++                get: function get() {
++                    return this._y;
++                },
++                set: function set(y) {
++                    this._y = y;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, "x2", {
++                get: function get() {
++                    return this._x2;
++                },
++                set: function set(x2) {
++                    this._x2 = x2;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, "y2", {
++                get: function get() {
++                    return this._y2;
++                },
++                set: function set(y2) {
++                    this._y2 = y2;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++
++            window.SVGPathSegCurvetoQuadraticSmoothAbs = function (owningPathSegList, x, y) {
++                window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS, "T", owningPathSegList);
++                this._x = x;
++                this._y = y;
++            };
++            window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype = Object.create(window.SVGPathSeg.prototype);
++            window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype.toString = function () {
++                return "[object SVGPathSegCurvetoQuadraticSmoothAbs]";
++            };
++            window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype._asPathString = function () {
++                return this.pathSegTypeAsLetter + " " + this._x + " " + this._y;
++            };
++            window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype.clone = function () {
++                return new window.SVGPathSegCurvetoQuadraticSmoothAbs(undefined, this._x, this._y);
++            };
++            Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype, "x", {
++                get: function get() {
++                    return this._x;
++                },
++                set: function set(x) {
++                    this._x = x;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype, "y", {
++                get: function get() {
++                    return this._y;
++                },
++                set: function set(y) {
++                    this._y = y;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++
++            window.SVGPathSegCurvetoQuadraticSmoothRel = function (owningPathSegList, x, y) {
++                window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL, "t", owningPathSegList);
++                this._x = x;
++                this._y = y;
++            };
++            window.SVGPathSegCurvetoQuadraticSmoothRel.prototype = Object.create(window.SVGPathSeg.prototype);
++            window.SVGPathSegCurvetoQuadraticSmoothRel.prototype.toString = function () {
++                return "[object SVGPathSegCurvetoQuadraticSmoothRel]";
++            };
++            window.SVGPathSegCurvetoQuadraticSmoothRel.prototype._asPathString = function () {
++                return this.pathSegTypeAsLetter + " " + this._x + " " + this._y;
++            };
++            window.SVGPathSegCurvetoQuadraticSmoothRel.prototype.clone = function () {
++                return new window.SVGPathSegCurvetoQuadraticSmoothRel(undefined, this._x, this._y);
++            };
++            Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothRel.prototype, "x", {
++                get: function get() {
++                    return this._x;
++                },
++                set: function set(x) {
++                    this._x = x;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothRel.prototype, "y", {
++                get: function get() {
++                    return this._y;
++                },
++                set: function set(y) {
++                    this._y = y;
++                    this._segmentChanged();
++                },
++                enumerable: true
++            });
++
++            // Add createSVGPathSeg* functions to window.SVGPathElement.
++            // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-Interfacewindow.SVGPathElement.
++            window.SVGPathElement.prototype.createSVGPathSegClosePath = function () {
++                return new window.SVGPathSegClosePath(undefined);
++            };
++            window.SVGPathElement.prototype.createSVGPathSegMovetoAbs = function (x, y) {
++                return new window.SVGPathSegMovetoAbs(undefined, x, y);
++            };
++            window.SVGPathElement.prototype.createSVGPathSegMovetoRel = function (x, y) {
++                return new window.SVGPathSegMovetoRel(undefined, x, y);
++            };
++            window.SVGPathElement.prototype.createSVGPathSegLinetoAbs = function (x, y) {
++                return new window.SVGPathSegLinetoAbs(undefined, x, y);
++            };
++            window.SVGPathElement.prototype.createSVGPathSegLinetoRel = function (x, y) {
++                return new window.SVGPathSegLinetoRel(undefined, x, y);
++            };
++            window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicAbs = function (x, y, x1, y1, x2, y2) {
++                return new window.SVGPathSegCurvetoCubicAbs(undefined, x, y, x1, y1, x2, y2);
++            };
++            window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicRel = function (x, y, x1, y1, x2, y2) {
++                return new window.SVGPathSegCurvetoCubicRel(undefined, x, y, x1, y1, x2, y2);
++            };
++            window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticAbs = function (x, y, x1, y1) {
++                return new window.SVGPathSegCurvetoQuadraticAbs(undefined, x, y, x1, y1);
++            };
++            window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticRel = function (x, y, x1, y1) {
++                return new window.SVGPathSegCurvetoQuadraticRel(undefined, x, y, x1, y1);
++            };
++            window.SVGPathElement.prototype.createSVGPathSegArcAbs = function (x, y, r1, r2, angle, largeArcFlag, sweepFlag) {
++                return new window.SVGPathSegArcAbs(undefined, x, y, r1, r2, angle, largeArcFlag, sweepFlag);
++            };
++            window.SVGPathElement.prototype.createSVGPathSegArcRel = function (x, y, r1, r2, angle, largeArcFlag, sweepFlag) {
++                return new window.SVGPathSegArcRel(undefined, x, y, r1, r2, angle, largeArcFlag, sweepFlag);
++            };
++            window.SVGPathElement.prototype.createSVGPathSegLinetoHorizontalAbs = function (x) {
++                return new window.SVGPathSegLinetoHorizontalAbs(undefined, x);
++            };
++            window.SVGPathElement.prototype.createSVGPathSegLinetoHorizontalRel = function (x) {
++                return new window.SVGPathSegLinetoHorizontalRel(undefined, x);
++            };
++            window.SVGPathElement.prototype.createSVGPathSegLinetoVerticalAbs = function (y) {
++                return new window.SVGPathSegLinetoVerticalAbs(undefined, y);
++            };
++            window.SVGPathElement.prototype.createSVGPathSegLinetoVerticalRel = function (y) {
++                return new window.SVGPathSegLinetoVerticalRel(undefined, y);
++            };
++            window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothAbs = function (x, y, x2, y2) {
++                return new window.SVGPathSegCurvetoCubicSmoothAbs(undefined, x, y, x2, y2);
++            };
++            window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothRel = function (x, y, x2, y2) {
++                return new window.SVGPathSegCurvetoCubicSmoothRel(undefined, x, y, x2, y2);
++            };
++            window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothAbs = function (x, y) {
++                return new window.SVGPathSegCurvetoQuadraticSmoothAbs(undefined, x, y);
++            };
++            window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothRel = function (x, y) {
++                return new window.SVGPathSegCurvetoQuadraticSmoothRel(undefined, x, y);
++            };
++
++            if (!("getPathSegAtLength" in window.SVGPathElement.prototype)) {
++                // Add getPathSegAtLength to SVGPathElement.
++                // Spec: https://www.w3.org/TR/SVG11/single-page.html#paths-__svg__SVGPathElement__getPathSegAtLength
++                // This polyfill requires SVGPathElement.getTotalLength to implement the distance-along-a-path algorithm.
++                window.SVGPathElement.prototype.getPathSegAtLength = function (distance) {
++                    if (distance === undefined || !isFinite(distance)) throw "Invalid arguments.";
++
++                    var measurementElement = document.createElementNS("http://www.w3.org/2000/svg", "path");
++                    measurementElement.setAttribute("d", this.getAttribute("d"));
++                    var lastPathSegment = measurementElement.pathSegList.numberOfItems - 1;
++
++                    // If the path is empty, return 0.
++                    if (lastPathSegment <= 0) return 0;
++
++                    do {
++                        measurementElement.pathSegList.removeItem(lastPathSegment);
++                        if (distance > measurementElement.getTotalLength()) break;
++                        lastPathSegment--;
++                    } while (lastPathSegment > 0);
++                    return lastPathSegment;
++                };
++            }
++        }
++
++        if (!("SVGPathSegList" in window)) {
++            // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGPathSegList
++            window.SVGPathSegList = function (pathElement) {
++                this._pathElement = pathElement;
++                this._list = this._parsePath(this._pathElement.getAttribute("d"));
++
++                // Use a MutationObserver to catch changes to the path's "d" attribute.
++                this._mutationObserverConfig = {
++                    "attributes": true,
++                    "attributeFilter": ["d"]
++                };
++                this._pathElementMutationObserver = new MutationObserver(this._updateListFromPathMutations.bind(this));
++                this._pathElementMutationObserver.observe(this._pathElement, this._mutationObserverConfig);
++            };
++
++            window.SVGPathSegList.prototype.classname = "SVGPathSegList";
++
++            Object.defineProperty(window.SVGPathSegList.prototype, "numberOfItems", {
++                get: function get() {
++                    this._checkPathSynchronizedToList();
++                    return this._list.length;
++                },
++                enumerable: true
++            });
++
++            // Add the pathSegList accessors to window.SVGPathElement.
++            // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGAnimatedPathData
++            Object.defineProperty(window.SVGPathElement.prototype, "pathSegList", {
++                get: function get() {
++                    if (!this._pathSegList) this._pathSegList = new window.SVGPathSegList(this);
++                    return this._pathSegList;
++                },
++                enumerable: true
++            });
++            // FIXME: The following are not implemented and simply return window.SVGPathElement.pathSegList.
++            Object.defineProperty(window.SVGPathElement.prototype, "normalizedPathSegList", {
++                get: function get() {
++                    return this.pathSegList;
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathElement.prototype, "animatedPathSegList", {
++                get: function get() {
++                    return this.pathSegList;
++                },
++                enumerable: true
++            });
++            Object.defineProperty(window.SVGPathElement.prototype, "animatedNormalizedPathSegList", {
++                get: function get() {
++                    return this.pathSegList;
++                },
++                enumerable: true
++            });
++
++            // Process any pending mutations to the path element and update the list as needed.
++            // This should be the first call of all public functions and is needed because
++            // MutationObservers are not synchronous so we can have pending asynchronous mutations.
++            window.SVGPathSegList.prototype._checkPathSynchronizedToList = function () {
++                this._updateListFromPathMutations(this._pathElementMutationObserver.takeRecords());
++            };
++
++            window.SVGPathSegList.prototype._updateListFromPathMutations = function (mutationRecords) {
++                if (!this._pathElement) return;
++                var hasPathMutations = false;
++                mutationRecords.forEach(function (record) {
++                    if (record.attributeName == "d") hasPathMutations = true;
++                });
++                if (hasPathMutations) this._list = this._parsePath(this._pathElement.getAttribute("d"));
++            };
++
++            // Serialize the list and update the path's 'd' attribute.
++            window.SVGPathSegList.prototype._writeListToPath = function () {
++                this._pathElementMutationObserver.disconnect();
++                this._pathElement.setAttribute("d", window.SVGPathSegList._pathSegArrayAsString(this._list));
++                this._pathElementMutationObserver.observe(this._pathElement, this._mutationObserverConfig);
++            };
++
++            // When a path segment changes the list needs to be synchronized back to the path element.
++            window.SVGPathSegList.prototype.segmentChanged = function (pathSeg) {
++                this._writeListToPath();
++            };
++
++            window.SVGPathSegList.prototype.clear = function () {
++                this._checkPathSynchronizedToList();
++
++                this._list.forEach(function (pathSeg) {
++                    pathSeg._owningPathSegList = null;
++                });
++                this._list = [];
++                this._writeListToPath();
++            };
++
++            window.SVGPathSegList.prototype.initialize = function (newItem) {
++                this._checkPathSynchronizedToList();
++
++                this._list = [newItem];
++                newItem._owningPathSegList = this;
++                this._writeListToPath();
++                return newItem;
++            };
++
++            window.SVGPathSegList.prototype._checkValidIndex = function (index) {
++                if (isNaN(index) || index < 0 || index >= this.numberOfItems) throw "INDEX_SIZE_ERR";
++            };
++
++            window.SVGPathSegList.prototype.getItem = function (index) {
++                this._checkPathSynchronizedToList();
++
++                this._checkValidIndex(index);
++                return this._list[index];
++            };
++
++            window.SVGPathSegList.prototype.insertItemBefore = function (newItem, index) {
++                this._checkPathSynchronizedToList();
++
++                // Spec: If the index is greater than or equal to numberOfItems, then the new item is appended to the end of the list.
++                if (index > this.numberOfItems) index = this.numberOfItems;
++                if (newItem._owningPathSegList) {
++                    // SVG2 spec says to make a copy.
++                    newItem = newItem.clone();
++                }
++                this._list.splice(index, 0, newItem);
++                newItem._owningPathSegList = this;
++                this._writeListToPath();
++                return newItem;
++            };
++
++            window.SVGPathSegList.prototype.replaceItem = function (newItem, index) {
++                this._checkPathSynchronizedToList();
++
++                if (newItem._owningPathSegList) {
++                    // SVG2 spec says to make a copy.
++                    newItem = newItem.clone();
++                }
++                this._checkValidIndex(index);
++                this._list[index] = newItem;
++                newItem._owningPathSegList = this;
++                this._writeListToPath();
++                return newItem;
++            };
++
++            window.SVGPathSegList.prototype.removeItem = function (index) {
++                this._checkPathSynchronizedToList();
++
++                this._checkValidIndex(index);
++                var item = this._list[index];
++                this._list.splice(index, 1);
++                this._writeListToPath();
++                return item;
++            };
++
++            window.SVGPathSegList.prototype.appendItem = function (newItem) {
++                this._checkPathSynchronizedToList();
++
++                if (newItem._owningPathSegList) {
++                    // SVG2 spec says to make a copy.
++                    newItem = newItem.clone();
++                }
++                this._list.push(newItem);
++                newItem._owningPathSegList = this;
++                // TODO: Optimize this to just append to the existing attribute.
++                this._writeListToPath();
++                return newItem;
++            };
++
++            window.SVGPathSegList._pathSegArrayAsString = function (pathSegArray) {
++                var string = "";
++                var first = true;
++                pathSegArray.forEach(function (pathSeg) {
++                    if (first) {
++                        first = false;
++                        string += pathSeg._asPathString();
++                    } else {
++                        string += " " + pathSeg._asPathString();
++                    }
++                });
++                return string;
++            };
++
++            // This closely follows SVGPathParser::parsePath from Source/core/svg/SVGPathParser.cpp.
++            window.SVGPathSegList.prototype._parsePath = function (string) {
++                if (!string || string.length == 0) return [];
++
++                var owningPathSegList = this;
++
++                var Builder = function Builder() {
++                    this.pathSegList = [];
++                };
++
++                Builder.prototype.appendSegment = function (pathSeg) {
++                    this.pathSegList.push(pathSeg);
++                };
++
++                var Source = function Source(string) {
++                    this._string = string;
++                    this._currentIndex = 0;
++                    this._endIndex = this._string.length;
++                    this._previousCommand = window.SVGPathSeg.PATHSEG_UNKNOWN;
++
++                    this._skipOptionalSpaces();
++                };
++
++                Source.prototype._isCurrentSpace = function () {
++                    var character = this._string[this._currentIndex];
++                    return character <= " " && (character == " " || character == "\n" || character == "\t" || character == "\r" || character == "\f");
++                };
++
++                Source.prototype._skipOptionalSpaces = function () {
++                    while (this._currentIndex < this._endIndex && this._isCurrentSpace()) {
++                        this._currentIndex++;
++                    }return this._currentIndex < this._endIndex;
++                };
++
++                Source.prototype._skipOptionalSpacesOrDelimiter = function () {
++                    if (this._currentIndex < this._endIndex && !this._isCurrentSpace() && this._string.charAt(this._currentIndex) != ",") return false;
++                    if (this._skipOptionalSpaces()) {
++                        if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == ",") {
++                            this._currentIndex++;
++                            this._skipOptionalSpaces();
++                        }
++                    }
++                    return this._currentIndex < this._endIndex;
++                };
++
++                Source.prototype.hasMoreData = function () {
++                    return this._currentIndex < this._endIndex;
++                };
++
++                Source.prototype.peekSegmentType = function () {
++                    var lookahead = this._string[this._currentIndex];
++                    return this._pathSegTypeFromChar(lookahead);
++                };
++
++                Source.prototype._pathSegTypeFromChar = function (lookahead) {
++                    switch (lookahead) {
++                        case "Z":
++                        case "z":
++                            return window.SVGPathSeg.PATHSEG_CLOSEPATH;
++                        case "M":
++                            return window.SVGPathSeg.PATHSEG_MOVETO_ABS;
++                        case "m":
++                            return window.SVGPathSeg.PATHSEG_MOVETO_REL;
++                        case "L":
++                            return window.SVGPathSeg.PATHSEG_LINETO_ABS;
++                        case "l":
++                            return window.SVGPathSeg.PATHSEG_LINETO_REL;
++                        case "C":
++                            return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS;
++                        case "c":
++                            return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL;
++                        case "Q":
++                            return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS;
++                        case "q":
++                            return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL;
++                        case "A":
++                            return window.SVGPathSeg.PATHSEG_ARC_ABS;
++                        case "a":
++                            return window.SVGPathSeg.PATHSEG_ARC_REL;
++                        case "H":
++                            return window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS;
++                        case "h":
++                            return window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL;
++                        case "V":
++                            return window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS;
++                        case "v":
++                            return window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL;
++                        case "S":
++                            return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS;
++                        case "s":
++                            return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL;
++                        case "T":
++                            return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS;
++                        case "t":
++                            return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL;
++                        default:
++                            return window.SVGPathSeg.PATHSEG_UNKNOWN;
++                    }
++                };
++
++                Source.prototype._nextCommandHelper = function (lookahead, previousCommand) {
++                    // Check for remaining coordinates in the current command.
++                    if ((lookahead == "+" || lookahead == "-" || lookahead == "." || lookahead >= "0" && lookahead <= "9") && previousCommand != window.SVGPathSeg.PATHSEG_CLOSEPATH) {
++                        if (previousCommand == window.SVGPathSeg.PATHSEG_MOVETO_ABS) return window.SVGPathSeg.PATHSEG_LINETO_ABS;
++                        if (previousCommand == window.SVGPathSeg.PATHSEG_MOVETO_REL) return window.SVGPathSeg.PATHSEG_LINETO_REL;
++                        return previousCommand;
++                    }
++                    return window.SVGPathSeg.PATHSEG_UNKNOWN;
++                };
++
++                Source.prototype.initialCommandIsMoveTo = function () {
++                    // If the path is empty it is still valid, so return true.
++                    if (!this.hasMoreData()) return true;
++                    var command = this.peekSegmentType();
++                    // Path must start with moveTo.
++                    return command == window.SVGPathSeg.PATHSEG_MOVETO_ABS || command == window.SVGPathSeg.PATHSEG_MOVETO_REL;
++                };
++
++                // Parse a number from an SVG path. This very closely follows genericParseNumber(...) from Source/core/svg/SVGParserUtilities.cpp.
++                // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-PathDataBNF
++                Source.prototype._parseNumber = function () {
++                    var exponent = 0;
++                    var integer = 0;
++                    var frac = 1;
++                    var decimal = 0;
++                    var sign = 1;
++                    var expsign = 1;
++
++                    var startIndex = this._currentIndex;
++
++                    this._skipOptionalSpaces();
++
++                    // Read the sign.
++                    if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == "+") this._currentIndex++;else if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == "-") {
++                        this._currentIndex++;
++                        sign = -1;
++                    }
++
++                    if (this._currentIndex == this._endIndex || (this._string.charAt(this._currentIndex) < "0" || this._string.charAt(this._currentIndex) > "9") && this._string.charAt(this._currentIndex) != ".")
++                        // The first character of a number must be one of [0-9+-.].
++                        return undefined;
++
++                    // Read the integer part, build right-to-left.
++                    var startIntPartIndex = this._currentIndex;
++                    while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= "0" && this._string.charAt(this._currentIndex) <= "9") {
++                        this._currentIndex++;
++                    } // Advance to first non-digit.
++
++                    if (this._currentIndex != startIntPartIndex) {
++                        var scanIntPartIndex = this._currentIndex - 1;
++                        var multiplier = 1;
++                        while (scanIntPartIndex >= startIntPartIndex) {
++                            integer += multiplier * (this._string.charAt(scanIntPartIndex--) - "0");
++                            multiplier *= 10;
++                        }
++                    }
++
++                    // Read the decimals.
++                    if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == ".") {
++                        this._currentIndex++;
++
++                        // There must be a least one digit following the .
++                        if (this._currentIndex >= this._endIndex || this._string.charAt(this._currentIndex) < "0" || this._string.charAt(this._currentIndex) > "9") return undefined;
++                        while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= "0" && this._string.charAt(this._currentIndex) <= "9") {
++                            frac *= 10;
++                            decimal += (this._string.charAt(this._currentIndex) - "0") / frac;
++                            this._currentIndex += 1;
++                        }
++                    }
++
++                    // Read the exponent part.
++                    if (this._currentIndex != startIndex && this._currentIndex + 1 < this._endIndex && (this._string.charAt(this._currentIndex) == "e" || this._string.charAt(this._currentIndex) == "E") && this._string.charAt(this._currentIndex + 1) != "x" && this._string.charAt(this._currentIndex + 1) != "m") {
++                        this._currentIndex++;
++
++                        // Read the sign of the exponent.
++                        if (this._string.charAt(this._currentIndex) == "+") {
++                            this._currentIndex++;
++                        } else if (this._string.charAt(this._currentIndex) == "-") {
++                            this._currentIndex++;
++                            expsign = -1;
++                        }
++
++                        // There must be an exponent.
++                        if (this._currentIndex >= this._endIndex || this._string.charAt(this._currentIndex) < "0" || this._string.charAt(this._currentIndex) > "9") return undefined;
++
++                        while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= "0" && this._string.charAt(this._currentIndex) <= "9") {
++                            exponent *= 10;
++                            exponent += this._string.charAt(this._currentIndex) - "0";
++                            this._currentIndex++;
++                        }
++                    }
++
++                    var number = integer + decimal;
++                    number *= sign;
++
++                    if (exponent) number *= Math.pow(10, expsign * exponent);
++
++                    if (startIndex == this._currentIndex) return undefined;
++
++                    this._skipOptionalSpacesOrDelimiter();
++
++                    return number;
++                };
++
++                Source.prototype._parseArcFlag = function () {
++                    if (this._currentIndex >= this._endIndex) return undefined;
++                    var flag = false;
++                    var flagChar = this._string.charAt(this._currentIndex++);
++                    if (flagChar == "0") flag = false;else if (flagChar == "1") flag = true;else return undefined;
++
++                    this._skipOptionalSpacesOrDelimiter();
++                    return flag;
++                };
++
++                Source.prototype.parseSegment = function () {
++                    var lookahead = this._string[this._currentIndex];
++                    var command = this._pathSegTypeFromChar(lookahead);
++                    if (command == window.SVGPathSeg.PATHSEG_UNKNOWN) {
++                        // Possibly an implicit command. Not allowed if this is the first command.
++                        if (this._previousCommand == window.SVGPathSeg.PATHSEG_UNKNOWN) return null;
++                        command = this._nextCommandHelper(lookahead, this._previousCommand);
++                        if (command == window.SVGPathSeg.PATHSEG_UNKNOWN) return null;
++                    } else {
++                        this._currentIndex++;
++                    }
++
++                    this._previousCommand = command;
++
++                    switch (command) {
++                        case window.SVGPathSeg.PATHSEG_MOVETO_REL:
++                            return new window.SVGPathSegMovetoRel(owningPathSegList, this._parseNumber(), this._parseNumber());
++                        case window.SVGPathSeg.PATHSEG_MOVETO_ABS:
++                            return new window.SVGPathSegMovetoAbs(owningPathSegList, this._parseNumber(), this._parseNumber());
++                        case window.SVGPathSeg.PATHSEG_LINETO_REL:
++                            return new window.SVGPathSegLinetoRel(owningPathSegList, this._parseNumber(), this._parseNumber());
++                        case window.SVGPathSeg.PATHSEG_LINETO_ABS:
++                            return new window.SVGPathSegLinetoAbs(owningPathSegList, this._parseNumber(), this._parseNumber());
++                        case window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL:
++                            return new window.SVGPathSegLinetoHorizontalRel(owningPathSegList, this._parseNumber());
++                        case window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS:
++                            return new window.SVGPathSegLinetoHorizontalAbs(owningPathSegList, this._parseNumber());
++                        case window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL:
++                            return new window.SVGPathSegLinetoVerticalRel(owningPathSegList, this._parseNumber());
++                        case window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS:
++                            return new window.SVGPathSegLinetoVerticalAbs(owningPathSegList, this._parseNumber());
++                        case window.SVGPathSeg.PATHSEG_CLOSEPATH:
++                            this._skipOptionalSpaces();
++                            return new window.SVGPathSegClosePath(owningPathSegList);
++                        case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL:
++                            var points = {
++                                x1: this._parseNumber(),
++                                y1: this._parseNumber(),
++                                x2: this._parseNumber(),
++                                y2: this._parseNumber(),
++                                x: this._parseNumber(),
++                                y: this._parseNumber()
++                            };
++                            return new window.SVGPathSegCurvetoCubicRel(owningPathSegList, points.x, points.y, points.x1, points.y1, points.x2, points.y2);
++                        case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS:
++                            var points = {
++                                x1: this._parseNumber(),
++                                y1: this._parseNumber(),
++                                x2: this._parseNumber(),
++                                y2: this._parseNumber(),
++                                x: this._parseNumber(),
++                                y: this._parseNumber()
++                            };
++                            return new window.SVGPathSegCurvetoCubicAbs(owningPathSegList, points.x, points.y, points.x1, points.y1, points.x2, points.y2);
++                        case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
++                            var points = {
++                                x2: this._parseNumber(),
++                                y2: this._parseNumber(),
++                                x: this._parseNumber(),
++                                y: this._parseNumber()
++                            };
++                            return new window.SVGPathSegCurvetoCubicSmoothRel(owningPathSegList, points.x, points.y, points.x2, points.y2);
++                        case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
++                            var points = {
++                                x2: this._parseNumber(),
++                                y2: this._parseNumber(),
++                                x: this._parseNumber(),
++                                y: this._parseNumber()
++                            };
++                            return new window.SVGPathSegCurvetoCubicSmoothAbs(owningPathSegList, points.x, points.y, points.x2, points.y2);
++                        case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL:
++                            var points = {
++                                x1: this._parseNumber(),
++                                y1: this._parseNumber(),
++                                x: this._parseNumber(),
++                                y: this._parseNumber()
++                            };
++                            return new window.SVGPathSegCurvetoQuadraticRel(owningPathSegList, points.x, points.y, points.x1, points.y1);
++                        case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS:
++                            var points = {
++                                x1: this._parseNumber(),
++                                y1: this._parseNumber(),
++                                x: this._parseNumber(),
++                                y: this._parseNumber()
++                            };
++                            return new window.SVGPathSegCurvetoQuadraticAbs(owningPathSegList, points.x, points.y, points.x1, points.y1);
++                        case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
++                            return new window.SVGPathSegCurvetoQuadraticSmoothRel(owningPathSegList, this._parseNumber(), this._parseNumber());
++                        case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
++                            return new window.SVGPathSegCurvetoQuadraticSmoothAbs(owningPathSegList, this._parseNumber(), this._parseNumber());
++                        case window.SVGPathSeg.PATHSEG_ARC_REL:
++                            var points = {
++                                x1: this._parseNumber(),
++                                y1: this._parseNumber(),
++                                arcAngle: this._parseNumber(),
++                                arcLarge: this._parseArcFlag(),
++                                arcSweep: this._parseArcFlag(),
++                                x: this._parseNumber(),
++                                y: this._parseNumber()
++                            };
++                            return new window.SVGPathSegArcRel(owningPathSegList, points.x, points.y, points.x1, points.y1, points.arcAngle, points.arcLarge, points.arcSweep);
++                        case window.SVGPathSeg.PATHSEG_ARC_ABS:
++                            var points = {
++                                x1: this._parseNumber(),
++                                y1: this._parseNumber(),
++                                arcAngle: this._parseNumber(),
++                                arcLarge: this._parseArcFlag(),
++                                arcSweep: this._parseArcFlag(),
++                                x: this._parseNumber(),
++                                y: this._parseNumber()
++                            };
++                            return new window.SVGPathSegArcAbs(owningPathSegList, points.x, points.y, points.x1, points.y1, points.arcAngle, points.arcLarge, points.arcSweep);
++                        default:
++                            throw "Unknown path seg type.";
++                    }
++                };
++
++                var builder = new Builder();
++                var source = new Source(string);
++
++                if (!source.initialCommandIsMoveTo()) return [];
++                while (source.hasMoreData()) {
++                    var pathSeg = source.parseSegment();
++                    if (!pathSeg) return [];
++                    builder.appendSegment(pathSeg);
++                }
++
++                return builder.pathSegList;
++            };
++        }
++    })();
++
++    // String.padEnd polyfill for IE11
++    //
++    // https://github.com/uxitten/polyfill/blob/master/string.polyfill.js
++    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd
++    if (!String.prototype.padEnd) {
++        String.prototype.padEnd = function padEnd(targetLength, padString) {
++            targetLength = targetLength >> 0; //floor if number or convert non-number to 0;
++            padString = String(typeof padString !== 'undefined' ? padString : ' ');
++            if (this.length > targetLength) {
++                return String(this);
++            } else {
++                targetLength = targetLength - this.length;
++                if (targetLength > padString.length) {
++                    padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed
++                }
++                return String(this) + padString.slice(0, targetLength);
++            }
++        };
++    }
++
++    /* jshint ignore:end */
++
++    Chart.prototype.axis = function () {};
++    Chart.prototype.axis.labels = function (labels) {
++        var $$ = this.internal;
++        if (arguments.length) {
++            Object.keys(labels).forEach(function (axisId) {
++                $$.axis.setLabelText(axisId, labels[axisId]);
++            });
++            $$.axis.updateLabels();
++        }
++        // TODO: return some values?
++    };
++    Chart.prototype.axis.max = function (max) {
++        var $$ = this.internal,
++            config = $$.config;
++        if (arguments.length) {
++            if ((typeof max === 'undefined' ? 'undefined' : _typeof(max)) === 'object') {
++                if (isValue(max.x)) {
++                    config.axis_x_max = max.x;
++                }
++                if (isValue(max.y)) {
++                    config.axis_y_max = max.y;
++                }
++                if (isValue(max.y2)) {
++                    config.axis_y2_max = max.y2;
++                }
++            } else {
++                config.axis_y_max = config.axis_y2_max = max;
++            }
++            $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true });
++        } else {
++            return {
++                x: config.axis_x_max,
++                y: config.axis_y_max,
++                y2: config.axis_y2_max
++            };
++        }
++    };
++    Chart.prototype.axis.min = function (min) {
++        var $$ = this.internal,
++            config = $$.config;
++        if (arguments.length) {
++            if ((typeof min === 'undefined' ? 'undefined' : _typeof(min)) === 'object') {
++                if (isValue(min.x)) {
++                    config.axis_x_min = min.x;
++                }
++                if (isValue(min.y)) {
++                    config.axis_y_min = min.y;
++                }
++                if (isValue(min.y2)) {
++                    config.axis_y2_min = min.y2;
++                }
++            } else {
++                config.axis_y_min = config.axis_y2_min = min;
++            }
++            $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true });
++        } else {
++            return {
++                x: config.axis_x_min,
++                y: config.axis_y_min,
++                y2: config.axis_y2_min
++            };
++        }
++    };
++    Chart.prototype.axis.range = function (range) {
++        if (arguments.length) {
++            if (isDefined(range.max)) {
++                this.axis.max(range.max);
++            }
++            if (isDefined(range.min)) {
++                this.axis.min(range.min);
++            }
++        } else {
++            return {
++                max: this.axis.max(),
++                min: this.axis.min()
++            };
++        }
++    };
++
++    Chart.prototype.category = function (i, category) {
++        var $$ = this.internal,
++            config = $$.config;
++        if (arguments.length > 1) {
++            config.axis_x_categories[i] = category;
++            $$.redraw();
++        }
++        return config.axis_x_categories[i];
++    };
++    Chart.prototype.categories = function (categories) {
++        var $$ = this.internal,
++            config = $$.config;
++        if (!arguments.length) {
++            return config.axis_x_categories;
++        }
++        config.axis_x_categories = categories;
++        $$.redraw();
++        return config.axis_x_categories;
++    };
++
++    Chart.prototype.resize = function (size) {
++        var $$ = this.internal,
++            config = $$.config;
++        config.size_width = size ? size.width : null;
++        config.size_height = size ? size.height : null;
++        this.flush();
++    };
++
++    Chart.prototype.flush = function () {
++        var $$ = this.internal;
++        $$.updateAndRedraw({ withLegend: true, withTransition: false, withTransitionForTransform: false });
++    };
++
++    Chart.prototype.destroy = function () {
++        var $$ = this.internal;
++
++        window.clearInterval($$.intervalForObserveInserted);
++
++        if ($$.resizeTimeout !== undefined) {
++            window.clearTimeout($$.resizeTimeout);
++        }
++
++        if (window.detachEvent) {
++            window.detachEvent('onresize', $$.resizeIfElementDisplayed);
++        } else if (window.removeEventListener) {
++            window.removeEventListener('resize', $$.resizeIfElementDisplayed);
++        } else {
++            var wrapper = window.onresize;
++            // check if no one else removed our wrapper and remove our resizeFunction from it
++            if (wrapper && wrapper.add && wrapper.remove) {
++                wrapper.remove($$.resizeFunction);
++            }
++        }
++
++        // remove the inner resize functions
++        $$.resizeFunction.remove();
++
++        $$.selectChart.classed('c3', false).html("");
++
++        // MEMO: this is needed because the reference of some elements will not be released, then memory leak will happen.
++        Object.keys($$).forEach(function (key) {
++            $$[key] = null;
++        });
++
++        return null;
++    };
++
++    // TODO: fix
++    Chart.prototype.color = function (id) {
++        var $$ = this.internal;
++        return $$.color(id); // more patterns
++    };
++
++    Chart.prototype.data = function (targetIds) {
++        var targets = this.internal.data.targets;
++        return typeof targetIds === 'undefined' ? targets : targets.filter(function (t) {
++            return [].concat(targetIds).indexOf(t.id) >= 0;
++        });
++    };
++    Chart.prototype.data.shown = function (targetIds) {
++        return this.internal.filterTargetsToShow(this.data(targetIds));
++    };
++    Chart.prototype.data.values = function (targetId) {
++        var targets,
++            values = null;
++        if (targetId) {
++            targets = this.data(targetId);
++            values = targets[0] ? targets[0].values.map(function (d) {
++                return d.value;
++            }) : null;
++        }
++        return values;
++    };
++    Chart.prototype.data.names = function (names) {
++        this.internal.clearLegendItemTextBoxCache();
++        return this.internal.updateDataAttributes('names', names);
++    };
++    Chart.prototype.data.colors = function (colors) {
++        return this.internal.updateDataAttributes('colors', colors);
++    };
++    Chart.prototype.data.axes = function (axes) {
++        return this.internal.updateDataAttributes('axes', axes);
++    };
++
++    Chart.prototype.flow = function (args) {
++        var $$ = this.internal,
++            targets,
++            data,
++            notfoundIds = [],
++            orgDataCount = $$.getMaxDataCount(),
++            dataCount,
++            domain,
++            baseTarget,
++            baseValue,
++            length = 0,
++            tail = 0,
++            diff,
++            to;
++
++        if (args.json) {
++            data = $$.convertJsonToData(args.json, args.keys);
++        } else if (args.rows) {
++            data = $$.convertRowsToData(args.rows);
++        } else if (args.columns) {
++            data = $$.convertColumnsToData(args.columns);
++        } else {
++            return;
++        }
++        targets = $$.convertDataToTargets(data, true);
++
++        // Update/Add data
++        $$.data.targets.forEach(function (t) {
++            var found = false,
++                i,
++                j;
++            for (i = 0; i < targets.length; i++) {
++                if (t.id === targets[i].id) {
++                    found = true;
++
++                    if (t.values[t.values.length - 1]) {
++                        tail = t.values[t.values.length - 1].index + 1;
++                    }
++                    length = targets[i].values.length;
++
++                    for (j = 0; j < length; j++) {
++                        targets[i].values[j].index = tail + j;
++                        if (!$$.isTimeSeries()) {
++                            targets[i].values[j].x = tail + j;
++                        }
++                    }
++                    t.values = t.values.concat(targets[i].values);
++
++                    targets.splice(i, 1);
++                    break;
++                }
++            }
++            if (!found) {
++                notfoundIds.push(t.id);
++            }
++        });
++
++        // Append null for not found targets
++        $$.data.targets.forEach(function (t) {
++            var i, j;
++            for (i = 0; i < notfoundIds.length; i++) {
++                if (t.id === notfoundIds[i]) {
++                    tail = t.values[t.values.length - 1].index + 1;
++                    for (j = 0; j < length; j++) {
++                        t.values.push({
++                            id: t.id,
++                            index: tail + j,
++                            x: $$.isTimeSeries() ? $$.getOtherTargetX(tail + j) : tail + j,
++                            value: null
++                        });
++                    }
++                }
++            }
++        });
++
++        // Generate null values for new target
++        if ($$.data.targets.length) {
++            targets.forEach(function (t) {
++                var i,
++                    missing = [];
++                for (i = $$.data.targets[0].values[0].index; i < tail; i++) {
++                    missing.push({
++                        id: t.id,
++                        index: i,
++                        x: $$.isTimeSeries() ? $$.getOtherTargetX(i) : i,
++                        value: null
++                    });
++                }
++                t.values.forEach(function (v) {
++                    v.index += tail;
++                    if (!$$.isTimeSeries()) {
++                        v.x += tail;
++                    }
++                });
++                t.values = missing.concat(t.values);
++            });
++        }
++        $$.data.targets = $$.data.targets.concat(targets); // add remained
++
++        // check data count because behavior needs to change when it's only one
++        dataCount = $$.getMaxDataCount();
++        baseTarget = $$.data.targets[0];
++        baseValue = baseTarget.values[0];
++
++        // Update length to flow if needed
++        if (isDefined(args.to)) {
++            length = 0;
++            to = $$.isTimeSeries() ? $$.parseDate(args.to) : args.to;
++            baseTarget.values.forEach(function (v) {
++                if (v.x < to) {
++                    length++;
++                }
++            });
++        } else if (isDefined(args.length)) {
++            length = args.length;
++        }
++
++        // If only one data, update the domain to flow from left edge of the chart
++        if (!orgDataCount) {
++            if ($$.isTimeSeries()) {
++                if (baseTarget.values.length > 1) {
++                    diff = baseTarget.values[baseTarget.values.length - 1].x - baseValue.x;
++                } else {
++                    diff = baseValue.x - $$.getXDomain($$.data.targets)[0];
++                }
++            } else {
++                diff = 1;
++            }
++            domain = [baseValue.x - diff, baseValue.x];
++            $$.updateXDomain(null, true, true, false, domain);
++        } else if (orgDataCount === 1) {
++            if ($$.isTimeSeries()) {
++                diff = (baseTarget.values[baseTarget.values.length - 1].x - baseValue.x) / 2;
++                domain = [new Date(+baseValue.x - diff), new Date(+baseValue.x + diff)];
++                $$.updateXDomain(null, true, true, false, domain);
++            }
++        }
++
++        // Set targets
++        $$.updateTargets($$.data.targets);
++
++        // Redraw with new targets
++        $$.redraw({
++            flow: {
++                index: baseValue.index,
++                length: length,
++                duration: isValue(args.duration) ? args.duration : $$.config.transition_duration,
++                done: args.done,
++                orgDataCount: orgDataCount
++            },
++            withLegend: true,
++            withTransition: orgDataCount > 1,
++            withTrimXDomain: false,
++            withUpdateXAxis: true
++        });
++    };
++
++    ChartInternal.prototype.generateFlow = function (args) {
++        var $$ = this,
++            config = $$.config,
++            d3 = $$.d3;
++
++        return function () {
++            var targets = args.targets,
++                flow = args.flow,
++                drawBar = args.drawBar,
++                drawLine = args.drawLine,
++                drawArea = args.drawArea,
++                cx = args.cx,
++                cy = args.cy,
++                xv = args.xv,
++                xForText = args.xForText,
++                yForText = args.yForText,
++                duration = args.duration;
++
++            var translateX,
++                scaleX = 1,
++                transform,
++                flowIndex = flow.index,
++                flowLength = flow.length,
++                flowStart = $$.getValueOnIndex($$.data.targets[0].values, flowIndex),
++                flowEnd = $$.getValueOnIndex($$.data.targets[0].values, flowIndex + flowLength),
++                orgDomain = $$.x.domain(),
++                domain,
++                durationForFlow = flow.duration || duration,
++                done = flow.done || function () {},
++                wait = $$.generateWait();
++
++            var xgrid, xgridLines, mainRegion, mainText, mainBar, mainLine, mainArea, mainCircle;
++
++            // set flag
++            $$.flowing = true;
++
++            // remove head data after rendered
++            $$.data.targets.forEach(function (d) {
++                d.values.splice(0, flowLength);
++            });
++
++            // update x domain to generate axis elements for flow
++            domain = $$.updateXDomain(targets, true, true);
++            // update elements related to x scale
++            if ($$.updateXGrid) {
++                $$.updateXGrid(true);
++            }
++
++            xgrid = $$.xgrid || d3.selectAll([]); // xgrid needs to be obtained after updateXGrid
++            xgridLines = $$.xgridLines || d3.selectAll([]);
++            mainRegion = $$.mainRegion || d3.selectAll([]);
++            mainText = $$.mainText || d3.selectAll([]);
++            mainBar = $$.mainBar || d3.selectAll([]);
++            mainLine = $$.mainLine || d3.selectAll([]);
++            mainArea = $$.mainArea || d3.selectAll([]);
++            mainCircle = $$.mainCircle || d3.selectAll([]);
++
++            // generate transform to flow
++            if (!flow.orgDataCount) {
++                // if empty
++                if ($$.data.targets[0].values.length !== 1) {
++                    translateX = $$.x(orgDomain[0]) - $$.x(domain[0]);
++                } else {
++                    if ($$.isTimeSeries()) {
++                        flowStart = $$.getValueOnIndex($$.data.targets[0].values, 0);
++                        flowEnd = $$.getValueOnIndex($$.data.targets[0].values, $$.data.targets[0].values.length - 1);
++                        translateX = $$.x(flowStart.x) - $$.x(flowEnd.x);
++                    } else {
++                        translateX = diffDomain(domain) / 2;
++                    }
++                }
++            } else if (flow.orgDataCount === 1 || (flowStart && flowStart.x) === (flowEnd && flowEnd.x)) {
++                translateX = $$.x(orgDomain[0]) - $$.x(domain[0]);
++            } else {
++                if ($$.isTimeSeries()) {
++                    translateX = $$.x(orgDomain[0]) - $$.x(domain[0]);
++                } else {
++                    translateX = $$.x(flowStart.x) - $$.x(flowEnd.x);
++                }
++            }
++            scaleX = diffDomain(orgDomain) / diffDomain(domain);
++            transform = 'translate(' + translateX + ',0) scale(' + scaleX + ',1)';
++
++            $$.hideXGridFocus();
++
++            var flowTransition = d3.transition().ease(d3.easeLinear).duration(durationForFlow);
++            wait.add($$.xAxis($$.axes.x, flowTransition));
++            wait.add(mainBar.transition(flowTransition).attr('transform', transform));
++            wait.add(mainLine.transition(flowTransition).attr('transform', transform));
++            wait.add(mainArea.transition(flowTransition).attr('transform', transform));
++            wait.add(mainCircle.transition(flowTransition).attr('transform', transform));
++            wait.add(mainText.transition(flowTransition).attr('transform', transform));
++            wait.add(mainRegion.filter($$.isRegionOnX).transition(flowTransition).attr('transform', transform));
++            wait.add(xgrid.transition(flowTransition).attr('transform', transform));
++            wait.add(xgridLines.transition(flowTransition).attr('transform', transform));
++            wait(function () {
++                var i,
++                    shapes = [],
++                    texts = [];
++
++                // remove flowed elements
++                if (flowLength) {
++                    for (i = 0; i < flowLength; i++) {
++                        shapes.push('.' + CLASS.shape + '-' + (flowIndex + i));
++                        texts.push('.' + CLASS.text + '-' + (flowIndex + i));
++                    }
++                    $$.svg.selectAll('.' + CLASS.shapes).selectAll(shapes).remove();
++                    $$.svg.selectAll('.' + CLASS.texts).selectAll(texts).remove();
++                    $$.svg.select('.' + CLASS.xgrid).remove();
++                }
++
++                // draw again for removing flowed elements and reverting attr
++                xgrid.attr('transform', null).attr('x1', $$.xgridAttr.x1).attr('x2', $$.xgridAttr.x2).attr('y1', $$.xgridAttr.y1).attr('y2', $$.xgridAttr.y2).style("opacity", $$.xgridAttr.opacity);
++                xgridLines.attr('transform', null);
++                xgridLines.select('line').attr("x1", config.axis_rotated ? 0 : xv).attr("x2", config.axis_rotated ? $$.width : xv);
++                xgridLines.select('text').attr("x", config.axis_rotated ? $$.width : 0).attr("y", xv);
++                mainBar.attr('transform', null).attr("d", drawBar);
++                mainLine.attr('transform', null).attr("d", drawLine);
++                mainArea.attr('transform', null).attr("d", drawArea);
++                mainCircle.attr('transform', null).attr("cx", cx).attr("cy", cy);
++                mainText.attr('transform', null).attr('x', xForText).attr('y', yForText).style('fill-opacity', $$.opacityForText.bind($$));
++                mainRegion.attr('transform', null);
++                mainRegion.filter($$.isRegionOnX).attr("x", $$.regionX.bind($$)).attr("width", $$.regionWidth.bind($$));
++
++                // callback for end of flow
++                done();
++
++                $$.flowing = false;
++            });
++        };
++    };
++
++    Chart.prototype.focus = function (targetIds) {
++        var $$ = this.internal,
++            candidates;
++
++        targetIds = $$.mapToTargetIds(targetIds);
++        candidates = $$.svg.selectAll($$.selectorTargets(targetIds.filter($$.isTargetToShow, $$))), this.revert();
++        this.defocus();
++        candidates.classed(CLASS.focused, true).classed(CLASS.defocused, false);
++        if ($$.hasArcType()) {
++            $$.expandArc(targetIds);
++        }
++        $$.toggleFocusLegend(targetIds, true);
++
++        $$.focusedTargetIds = targetIds;
++        $$.defocusedTargetIds = $$.defocusedTargetIds.filter(function (id) {
++            return targetIds.indexOf(id) < 0;
++        });
++    };
++
++    Chart.prototype.defocus = function (targetIds) {
++        var $$ = this.internal,
++            candidates;
++
++        targetIds = $$.mapToTargetIds(targetIds);
++        candidates = $$.svg.selectAll($$.selectorTargets(targetIds.filter($$.isTargetToShow, $$))), candidates.classed(CLASS.focused, false).classed(CLASS.defocused, true);
++        if ($$.hasArcType()) {
++            $$.unexpandArc(targetIds);
++        }
++        $$.toggleFocusLegend(targetIds, false);
++
++        $$.focusedTargetIds = $$.focusedTargetIds.filter(function (id) {
++            return targetIds.indexOf(id) < 0;
++        });
++        $$.defocusedTargetIds = targetIds;
++    };
++
++    Chart.prototype.revert = function (targetIds) {
++        var $$ = this.internal,
++            candidates;
++
++        targetIds = $$.mapToTargetIds(targetIds);
++        candidates = $$.svg.selectAll($$.selectorTargets(targetIds)); // should be for all targets
++
++        candidates.classed(CLASS.focused, false).classed(CLASS.defocused, false);
++        if ($$.hasArcType()) {
++            $$.unexpandArc(targetIds);
++        }
++        if ($$.config.legend_show) {
++            $$.showLegend(targetIds.filter($$.isLegendToShow.bind($$)));
++            $$.legend.selectAll($$.selectorLegends(targetIds)).filter(function () {
++                return $$.d3.select(this).classed(CLASS.legendItemFocused);
++            }).classed(CLASS.legendItemFocused, false);
++        }
++
++        $$.focusedTargetIds = [];
++        $$.defocusedTargetIds = [];
++    };
++
++    Chart.prototype.xgrids = function (grids) {
++        var $$ = this.internal,
++            config = $$.config;
++        if (!grids) {
++            return config.grid_x_lines;
++        }
++        config.grid_x_lines = grids;
++        $$.redrawWithoutRescale();
++        return config.grid_x_lines;
++    };
++    Chart.prototype.xgrids.add = function (grids) {
++        var $$ = this.internal;
++        return this.xgrids($$.config.grid_x_lines.concat(grids ? grids : []));
++    };
++    Chart.prototype.xgrids.remove = function (params) {
++        // TODO: multiple
++        var $$ = this.internal;
++        $$.removeGridLines(params, true);
++    };
++
++    Chart.prototype.ygrids = function (grids) {
++        var $$ = this.internal,
++            config = $$.config;
++        if (!grids) {
++            return config.grid_y_lines;
++        }
++        config.grid_y_lines = grids;
++        $$.redrawWithoutRescale();
++        return config.grid_y_lines;
++    };
++    Chart.prototype.ygrids.add = function (grids) {
++        var $$ = this.internal;
++        return this.ygrids($$.config.grid_y_lines.concat(grids ? grids : []));
++    };
++    Chart.prototype.ygrids.remove = function (params) {
++        // TODO: multiple
++        var $$ = this.internal;
++        $$.removeGridLines(params, false);
++    };
++
++    Chart.prototype.groups = function (groups) {
++        var $$ = this.internal,
++            config = $$.config;
++        if (isUndefined(groups)) {
++            return config.data_groups;
++        }
++        config.data_groups = groups;
++        $$.redraw();
++        return config.data_groups;
++    };
++
++    Chart.prototype.legend = function () {};
++    Chart.prototype.legend.show = function (targetIds) {
++        var $$ = this.internal;
++        $$.showLegend($$.mapToTargetIds(targetIds));
++        $$.updateAndRedraw({ withLegend: true });
++    };
++    Chart.prototype.legend.hide = function (targetIds) {
++        var $$ = this.internal;
++        $$.hideLegend($$.mapToTargetIds(targetIds));
++        $$.updateAndRedraw({ withLegend: false });
++    };
++
++    Chart.prototype.load = function (args) {
++        var $$ = this.internal,
++            config = $$.config;
++        // update xs if specified
++        if (args.xs) {
++            $$.addXs(args.xs);
++        }
++        // update names if exists
++        if ('names' in args) {
++            Chart.prototype.data.names.bind(this)(args.names);
++        }
++        // update classes if exists
++        if ('classes' in args) {
++            Object.keys(args.classes).forEach(function (id) {
++                config.data_classes[id] = args.classes[id];
++            });
++        }
++        // update categories if exists
++        if ('categories' in args && $$.isCategorized()) {
++            config.axis_x_categories = args.categories;
++        }
++        // update axes if exists
++        if ('axes' in args) {
++            Object.keys(args.axes).forEach(function (id) {
++                config.data_axes[id] = args.axes[id];
++            });
++        }
++        // update colors if exists
++        if ('colors' in args) {
++            Object.keys(args.colors).forEach(function (id) {
++                config.data_colors[id] = args.colors[id];
++            });
++        }
++        // use cache if exists
++        if ('cacheIds' in args && $$.hasCaches(args.cacheIds)) {
++            $$.load($$.getCaches(args.cacheIds), args.done);
++            return;
++        }
++        // unload if needed
++        if ('unload' in args) {
++            // TODO: do not unload if target will load (included in url/rows/columns)
++            $$.unload($$.mapToTargetIds(typeof args.unload === 'boolean' && args.unload ? null : args.unload), function () {
++                $$.loadFromArgs(args);
++            });
++        } else {
++            $$.loadFromArgs(args);
++        }
++    };
++
++    Chart.prototype.unload = function (args) {
++        var $$ = this.internal;
++        args = args || {};
++        if (args instanceof Array) {
++            args = { ids: args };
++        } else if (typeof args === 'string') {
++            args = { ids: [args] };
++        }
++        $$.unload($$.mapToTargetIds(args.ids), function () {
++            $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true });
++            if (args.done) {
++                args.done();
++            }
++        });
++    };
++
++    Chart.prototype.regions = function (regions) {
++        var $$ = this.internal,
++            config = $$.config;
++        if (!regions) {
++            return config.regions;
++        }
++        config.regions = regions;
++        $$.redrawWithoutRescale();
++        return config.regions;
++    };
++    Chart.prototype.regions.add = function (regions) {
++        var $$ = this.internal,
++            config = $$.config;
++        if (!regions) {
++            return config.regions;
++        }
++        config.regions = config.regions.concat(regions);
++        $$.redrawWithoutRescale();
++        return config.regions;
++    };
++    Chart.prototype.regions.remove = function (options) {
++        var $$ = this.internal,
++            config = $$.config,
++            duration,
++            classes,
++            regions;
++
++        options = options || {};
++        duration = $$.getOption(options, "duration", config.transition_duration);
++        classes = $$.getOption(options, "classes", [CLASS.region]);
++
++        regions = $$.main.select('.' + CLASS.regions).selectAll(classes.map(function (c) {
++            return '.' + c;
++        }));
++        (duration ? regions.transition().duration(duration) : regions).style('opacity', 0).remove();
++
++        config.regions = config.regions.filter(function (region) {
++            var found = false;
++            if (!region['class']) {
++                return true;
++            }
++            region['class'].split(' ').forEach(function (c) {
++                if (classes.indexOf(c) >= 0) {
++                    found = true;
++                }
++            });
++            return !found;
++        });
++
++        return config.regions;
++    };
++
++    Chart.prototype.selected = function (targetId) {
++        var $$ = this.internal,
++            d3 = $$.d3;
++        return d3.merge($$.main.selectAll('.' + CLASS.shapes + $$.getTargetSelectorSuffix(targetId)).selectAll('.' + CLASS.shape).filter(function () {
++            return d3.select(this).classed(CLASS.SELECTED);
++        }).map(function (d) {
++            return d.map(function (d) {
++                var data = d.__data__;return data.data ? data.data : data;
++            });
++        }));
++    };
++    Chart.prototype.select = function (ids, indices, resetOther) {
++        var $$ = this.internal,
++            d3 = $$.d3,
++            config = $$.config;
++        if (!config.data_selection_enabled) {
++            return;
++        }
++        $$.main.selectAll('.' + CLASS.shapes).selectAll('.' + CLASS.shape).each(function (d, i) {
++            var shape = d3.select(this),
++                id = d.data ? d.data.id : d.id,
++                toggle = $$.getToggle(this, d).bind($$),
++                isTargetId = config.data_selection_grouped || !ids || ids.indexOf(id) >= 0,
++                isTargetIndex = !indices || indices.indexOf(i) >= 0,
++                isSelected = shape.classed(CLASS.SELECTED);
++            // line/area selection not supported yet
++            if (shape.classed(CLASS.line) || shape.classed(CLASS.area)) {
++                return;
++            }
++            if (isTargetId && isTargetIndex) {
++                if (config.data_selection_isselectable(d) && !isSelected) {
++                    toggle(true, shape.classed(CLASS.SELECTED, true), d, i);
++                }
++            } else if (isDefined(resetOther) && resetOther) {
++                if (isSelected) {
++                    toggle(false, shape.classed(CLASS.SELECTED, false), d, i);
++                }
++            }
++        });
++    };
++    Chart.prototype.unselect = function (ids, indices) {
++        var $$ = this.internal,
++            d3 = $$.d3,
++            config = $$.config;
++        if (!config.data_selection_enabled) {
++            return;
++        }
++        $$.main.selectAll('.' + CLASS.shapes).selectAll('.' + CLASS.shape).each(function (d, i) {
++            var shape = d3.select(this),
++                id = d.data ? d.data.id : d.id,
++                toggle = $$.getToggle(this, d).bind($$),
++                isTargetId = config.data_selection_grouped || !ids || ids.indexOf(id) >= 0,
++                isTargetIndex = !indices || indices.indexOf(i) >= 0,
++                isSelected = shape.classed(CLASS.SELECTED);
++            // line/area selection not supported yet
++            if (shape.classed(CLASS.line) || shape.classed(CLASS.area)) {
++                return;
++            }
++            if (isTargetId && isTargetIndex) {
++                if (config.data_selection_isselectable(d)) {
++                    if (isSelected) {
++                        toggle(false, shape.classed(CLASS.SELECTED, false), d, i);
++                    }
++                }
++            }
++        });
++    };
++
++    Chart.prototype.show = function (targetIds, options) {
++        var $$ = this.internal,
++            targets;
++
++        targetIds = $$.mapToTargetIds(targetIds);
++        options = options || {};
++
++        $$.removeHiddenTargetIds(targetIds);
++        targets = $$.svg.selectAll($$.selectorTargets(targetIds));
++
++        targets.transition().style('display', 'initial', 'important').style('opacity', 1, 'important').call($$.endall, function () {
++            targets.style('opacity', null).style('opacity', 1);
++        });
++
++        if (options.withLegend) {
++            $$.showLegend(targetIds);
++        }
++
++        $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true });
++    };
++
++    Chart.prototype.hide = function (targetIds, options) {
++        var $$ = this.internal,
++            targets;
++
++        targetIds = $$.mapToTargetIds(targetIds);
++        options = options || {};
++
++        $$.addHiddenTargetIds(targetIds);
++        targets = $$.svg.selectAll($$.selectorTargets(targetIds));
++
++        targets.transition().style('opacity', 0, 'important').call($$.endall, function () {
++            targets.style('opacity', null).style('opacity', 0);
++            targets.style('display', 'none');
++        });
++
++        if (options.withLegend) {
++            $$.hideLegend(targetIds);
++        }
++
++        $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true });
++    };
++
++    Chart.prototype.toggle = function (targetIds, options) {
++        var that = this,
++            $$ = this.internal;
++        $$.mapToTargetIds(targetIds).forEach(function (targetId) {
++            $$.isTargetToShow(targetId) ? that.hide(targetId, options) : that.show(targetId, options);
++        });
++    };
++
++    Chart.prototype.tooltip = function () {};
++    Chart.prototype.tooltip.show = function (args) {
++        var $$ = this.internal,
++            targets,
++            data,
++            mouse = {};
++
++        // determine mouse position on the chart
++        if (args.mouse) {
++            mouse = args.mouse;
++        } else {
++            // determine focus data
++            if (args.data) {
++                data = args.data;
++            } else if (typeof args.x !== 'undefined') {
++                if (args.id) {
++                    targets = $$.data.targets.filter(function (t) {
++                        return t.id === args.id;
++                    });
++                } else {
++                    targets = $$.data.targets;
++                }
++                data = $$.filterByX(targets, args.x).slice(0, 1)[0];
++            }
++            mouse = data ? $$.getMousePosition(data) : null;
++        }
++
++        // emulate mouse events to show
++        $$.dispatchEvent('mousemove', mouse);
++
++        $$.config.tooltip_onshow.call($$, data);
++    };
++    Chart.prototype.tooltip.hide = function () {
++        // TODO: get target data by checking the state of focus
++        this.internal.dispatchEvent('mouseout', 0);
++
++        this.internal.config.tooltip_onhide.call(this);
++    };
++
++    Chart.prototype.transform = function (type, targetIds) {
++        var $$ = this.internal,
++            options = ['pie', 'donut'].indexOf(type) >= 0 ? { withTransform: true } : null;
++        $$.transformTo(targetIds, type, options);
++    };
++
++    ChartInternal.prototype.transformTo = function (targetIds, type, optionsForRedraw) {
++        var $$ = this,
++            withTransitionForAxis = !$$.hasArcType(),
++            options = optionsForRedraw || { withTransitionForAxis: withTransitionForAxis };
++        options.withTransitionForTransform = false;
++        $$.transiting = false;
++        $$.setTargetType(targetIds, type);
++        $$.updateTargets($$.data.targets); // this is needed when transforming to arc
++        $$.updateAndRedraw(options);
++    };
++
++    Chart.prototype.x = function (x) {
++        var $$ = this.internal;
++        if (arguments.length) {
++            $$.updateTargetX($$.data.targets, x);
++            $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true });
++        }
++        return $$.data.xs;
++    };
++    Chart.prototype.xs = function (xs) {
++        var $$ = this.internal;
++        if (arguments.length) {
++            $$.updateTargetXs($$.data.targets, xs);
++            $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true });
++        }
++        return $$.data.xs;
++    };
++
++    Chart.prototype.zoom = function (domain) {
++        var $$ = this.internal;
++        if (domain) {
++            if ($$.isTimeSeries()) {
++                domain = domain.map(function (x) {
++                    return $$.parseDate(x);
++                });
++            }
++            if ($$.config.subchart_show) {
++                $$.brush.selectionAsValue(domain, true);
++            } else {
++                $$.updateXDomain(null, true, false, false, domain);
++                $$.redraw({ withY: $$.config.zoom_rescale, withSubchart: false });
++            }
++            $$.config.zoom_onzoom.call(this, $$.x.orgDomain());
++            return domain;
++        } else {
++            return $$.x.domain();
++        }
++    };
++    Chart.prototype.zoom.enable = function (enabled) {
++        var $$ = this.internal;
++        $$.config.zoom_enabled = enabled;
++        $$.updateAndRedraw();
++    };
++    Chart.prototype.unzoom = function () {
++        var $$ = this.internal;
++        if ($$.config.subchart_show) {
++            $$.brush.clear();
++        } else {
++            $$.updateXDomain(null, true, false, false, $$.subX.domain());
++            $$.redraw({ withY: $$.config.zoom_rescale, withSubchart: false });
++        }
++    };
++
++    Chart.prototype.zoom.max = function (max) {
++        var $$ = this.internal,
++            config = $$.config,
++            d3 = $$.d3;
++        if (max === 0 || max) {
++            config.zoom_x_max = d3.max([$$.orgXDomain[1], max]);
++        } else {
++            return config.zoom_x_max;
++        }
++    };
++
++    Chart.prototype.zoom.min = function (min) {
++        var $$ = this.internal,
++            config = $$.config,
++            d3 = $$.d3;
++        if (min === 0 || min) {
++            config.zoom_x_min = d3.min([$$.orgXDomain[0], min]);
++        } else {
++            return config.zoom_x_min;
++        }
++    };
++
++    Chart.prototype.zoom.range = function (range) {
++        if (arguments.length) {
++            if (isDefined(range.max)) {
++                this.domain.max(range.max);
++            }
++            if (isDefined(range.min)) {
++                this.domain.min(range.min);
++            }
++        } else {
++            return {
++                max: this.domain.max(),
++                min: this.domain.min()
++            };
++        }
++    };
++
++    ChartInternal.prototype.initPie = function () {
++        var $$ = this,
++            d3 = $$.d3;
++        $$.pie = d3.pie().value(function (d) {
++            return d.values.reduce(function (a, b) {
++                return a + b.value;
++            }, 0);
++        });
++
++        var orderFct = $$.getOrderFunction();
++
++        // we need to reverse the returned order if asc or desc to have the slice in expected order.
++        if (orderFct && ($$.isOrderAsc() || $$.isOrderDesc())) {
++            var defaultSort = orderFct;
++            orderFct = function orderFct(t1, t2) {
++                return defaultSort(t1, t2) * -1;
++            };
++        }
++
++        $$.pie.sort(orderFct || null);
++    };
++
++    ChartInternal.prototype.updateRadius = function () {
++        var $$ = this,
++            config = $$.config,
++            w = config.gauge_width || config.donut_width,
++            gaugeArcWidth = $$.filterTargetsToShow($$.data.targets).length * $$.config.gauge_arcs_minWidth;
++        $$.radiusExpanded = Math.min($$.arcWidth, $$.arcHeight) / 2 * ($$.hasType('gauge') ? 0.85 : 1);
++        $$.radius = $$.radiusExpanded * 0.95;
++        $$.innerRadiusRatio = w ? ($$.radius - w) / $$.radius : 0.6;
++        $$.innerRadius = $$.hasType('donut') || $$.hasType('gauge') ? $$.radius * $$.innerRadiusRatio : 0;
++        $$.gaugeArcWidth = w ? w : gaugeArcWidth <= $$.radius - $$.innerRadius ? $$.radius - $$.innerRadius : gaugeArcWidth <= $$.radius ? gaugeArcWidth : $$.radius;
++    };
++
++    ChartInternal.prototype.updateArc = function () {
++        var $$ = this;
++        $$.svgArc = $$.getSvgArc();
++        $$.svgArcExpanded = $$.getSvgArcExpanded();
++        $$.svgArcExpandedSub = $$.getSvgArcExpanded(0.98);
++    };
++
++    ChartInternal.prototype.updateAngle = function (d) {
++        var $$ = this,
++            config = $$.config,
++            found = false,
++            index = 0,
++            gMin,
++            gMax,
++            gTic,
++            gValue;
++
++        if (!config) {
++            return null;
++        }
++
++        $$.pie($$.filterTargetsToShow($$.data.targets)).forEach(function (t) {
++            if (!found && t.data.id === d.data.id) {
++                found = true;
++                d = t;
++                d.index = index;
++            }
++            index++;
++        });
++        if (isNaN(d.startAngle)) {
++            d.startAngle = 0;
++        }
++        if (isNaN(d.endAngle)) {
++            d.endAngle = d.startAngle;
++        }
++        if ($$.isGaugeType(d.data)) {
++            gMin = config.gauge_min;
++            gMax = config.gauge_max;
++            gTic = Math.PI * (config.gauge_fullCircle ? 2 : 1) / (gMax - gMin);
++            gValue = d.value < gMin ? 0 : d.value < gMax ? d.value - gMin : gMax - gMin;
++            d.startAngle = config.gauge_startingAngle;
++            d.endAngle = d.startAngle + gTic * gValue;
++        }
++        return found ? d : null;
++    };
++
++    ChartInternal.prototype.getSvgArc = function () {
++        var $$ = this,
++            hasGaugeType = $$.hasType('gauge'),
++            singleArcWidth = $$.gaugeArcWidth / $$.filterTargetsToShow($$.data.targets).length,
++            arc = $$.d3.arc().outerRadius(function (d) {
++            return hasGaugeType ? $$.radius - singleArcWidth * d.index : $$.radius;
++        }).innerRadius(function (d) {
++            return hasGaugeType ? $$.radius - singleArcWidth * (d.index + 1) : $$.innerRadius;
++        }),
++            newArc = function newArc(d, withoutUpdate) {
++            var updated;
++            if (withoutUpdate) {
++                return arc(d);
++            } // for interpolate
++            updated = $$.updateAngle(d);
++            return updated ? arc(updated) : "M 0 0";
++        };
++        // TODO: extends all function
++        newArc.centroid = arc.centroid;
++        return newArc;
++    };
++
++    ChartInternal.prototype.getSvgArcExpanded = function (rate) {
++        rate = rate || 1;
++        var $$ = this,
++            hasGaugeType = $$.hasType('gauge'),
++            singleArcWidth = $$.gaugeArcWidth / $$.filterTargetsToShow($$.data.targets).length,
++            expandWidth = Math.min($$.radiusExpanded * rate - $$.radius, singleArcWidth * 0.8 - (1 - rate) * 100),
++            arc = $$.d3.arc().outerRadius(function (d) {
++            return hasGaugeType ? $$.radius - singleArcWidth * d.index + expandWidth : $$.radiusExpanded * rate;
++        }).innerRadius(function (d) {
++            return hasGaugeType ? $$.radius - singleArcWidth * (d.index + 1) : $$.innerRadius;
++        });
++        return function (d) {
++            var updated = $$.updateAngle(d);
++            return updated ? arc(updated) : "M 0 0";
++        };
++    };
++
++    ChartInternal.prototype.getArc = function (d, withoutUpdate, force) {
++        return force || this.isArcType(d.data) ? this.svgArc(d, withoutUpdate) : "M 0 0";
++    };
++
++    ChartInternal.prototype.transformForArcLabel = function (d) {
++        var $$ = this,
++            config = $$.config,
++            updated = $$.updateAngle(d),
++            c,
++            x,
++            y,
++            h,
++            ratio,
++            translate = "",
++            hasGauge = $$.hasType('gauge');
++        if (updated && !hasGauge) {
++            c = this.svgArc.centroid(updated);
++            x = isNaN(c[0]) ? 0 : c[0];
++            y = isNaN(c[1]) ? 0 : c[1];
++            h = Math.sqrt(x * x + y * y);
++            if ($$.hasType('donut') && config.donut_label_ratio) {
++                ratio = isFunction(config.donut_label_ratio) ? config.donut_label_ratio(d, $$.radius, h) : config.donut_label_ratio;
++            } else if ($$.hasType('pie') && config.pie_label_ratio) {
++                ratio = isFunction(config.pie_label_ratio) ? config.pie_label_ratio(d, $$.radius, h) : config.pie_label_ratio;
++            } else {
++                ratio = $$.radius && h ? (36 / $$.radius > 0.375 ? 1.175 - 36 / $$.radius : 0.8) * $$.radius / h : 0;
++            }
++            translate = "translate(" + x * ratio + ',' + y * ratio + ")";
++        } else if (updated && hasGauge && $$.filterTargetsToShow($$.data.targets).length > 1) {
++            var y1 = Math.sin(updated.endAngle - Math.PI / 2);
++            x = Math.cos(updated.endAngle - Math.PI / 2) * ($$.radiusExpanded + 25);
++            y = y1 * ($$.radiusExpanded + 15 - Math.abs(y1 * 10)) + 3;
++            translate = "translate(" + x + ',' + y + ")";
++        }
++        return translate;
++    };
++
++    ChartInternal.prototype.getArcRatio = function (d) {
++        var $$ = this,
++            config = $$.config,
++            whole = Math.PI * ($$.hasType('gauge') && !config.gauge_fullCircle ? 1 : 2);
++        return d ? (d.endAngle - d.startAngle) / whole : null;
++    };
++
++    ChartInternal.prototype.convertToArcData = function (d) {
++        return this.addName({
++            id: d.data.id,
++            value: d.value,
++            ratio: this.getArcRatio(d),
++            index: d.index
++        });
++    };
++
++    ChartInternal.prototype.textForArcLabel = function (d) {
++        var $$ = this,
++            updated,
++            value,
++            ratio,
++            id,
++            format;
++        if (!$$.shouldShowArcLabel()) {
++            return "";
++        }
++        updated = $$.updateAngle(d);
++        value = updated ? updated.value : null;
++        ratio = $$.getArcRatio(updated);
++        id = d.data.id;
++        if (!$$.hasType('gauge') && !$$.meetsArcLabelThreshold(ratio)) {
++            return "";
++        }
++        format = $$.getArcLabelFormat();
++        return format ? format(value, ratio, id) : $$.defaultArcValueFormat(value, ratio);
++    };
++
++    ChartInternal.prototype.textForGaugeMinMax = function (value, isMax) {
++        var $$ = this,
++            format = $$.getGaugeLabelExtents();
++
++        return format ? format(value, isMax) : value;
++    };
++
++    ChartInternal.prototype.expandArc = function (targetIds) {
++        var $$ = this,
++            interval;
++
++        // MEMO: avoid to cancel transition
++        if ($$.transiting) {
++            interval = window.setInterval(function () {
++                if (!$$.transiting) {
++                    window.clearInterval(interval);
++                    if ($$.legend.selectAll('.c3-legend-item-focused').size() > 0) {
++                        $$.expandArc(targetIds);
++                    }
++                }
++            }, 10);
++            return;
++        }
++
++        targetIds = $$.mapToTargetIds(targetIds);
++
++        $$.svg.selectAll($$.selectorTargets(targetIds, '.' + CLASS.chartArc)).each(function (d) {
++            if (!$$.shouldExpand(d.data.id)) {
++                return;
++            }
++            $$.d3.select(this).selectAll('path').transition().duration($$.expandDuration(d.data.id)).attr("d", $$.svgArcExpanded).transition().duration($$.expandDuration(d.data.id) * 2).attr("d", $$.svgArcExpandedSub).each(function (d) {
++                if ($$.isDonutType(d.data)) ;
++            });
++        });
++    };
++
++    ChartInternal.prototype.unexpandArc = function (targetIds) {
++        var $$ = this;
++
++        if ($$.transiting) {
++            return;
++        }
++
++        targetIds = $$.mapToTargetIds(targetIds);
++
++        $$.svg.selectAll($$.selectorTargets(targetIds, '.' + CLASS.chartArc)).selectAll('path').transition().duration(function (d) {
++            return $$.expandDuration(d.data.id);
++        }).attr("d", $$.svgArc);
++        $$.svg.selectAll('.' + CLASS.arc);
++    };
++
++    ChartInternal.prototype.expandDuration = function (id) {
++        var $$ = this,
++            config = $$.config;
++
++        if ($$.isDonutType(id)) {
++            return config.donut_expand_duration;
++        } else if ($$.isGaugeType(id)) {
++            return config.gauge_expand_duration;
++        } else if ($$.isPieType(id)) {
++            return config.pie_expand_duration;
++        } else {
++            return 50;
++        }
++    };
++
++    ChartInternal.prototype.shouldExpand = function (id) {
++        var $$ = this,
++            config = $$.config;
++        return $$.isDonutType(id) && config.donut_expand || $$.isGaugeType(id) && config.gauge_expand || $$.isPieType(id) && config.pie_expand;
++    };
++
++    ChartInternal.prototype.shouldShowArcLabel = function () {
++        var $$ = this,
++            config = $$.config,
++            shouldShow = true;
++        if ($$.hasType('donut')) {
++            shouldShow = config.donut_label_show;
++        } else if ($$.hasType('pie')) {
++            shouldShow = config.pie_label_show;
++        }
++        // when gauge, always true
++        return shouldShow;
++    };
++
++    ChartInternal.prototype.meetsArcLabelThreshold = function (ratio) {
++        var $$ = this,
++            config = $$.config,
++            threshold = $$.hasType('donut') ? config.donut_label_threshold : config.pie_label_threshold;
++        return ratio >= threshold;
++    };
++
++    ChartInternal.prototype.getArcLabelFormat = function () {
++        var $$ = this,
++            config = $$.config,
++            format = config.pie_label_format;
++        if ($$.hasType('gauge')) {
++            format = config.gauge_label_format;
++        } else if ($$.hasType('donut')) {
++            format = config.donut_label_format;
++        }
++        return format;
++    };
++
++    ChartInternal.prototype.getGaugeLabelExtents = function () {
++        var $$ = this,
++            config = $$.config;
++        return config.gauge_label_extents;
++    };
++
++    ChartInternal.prototype.getArcTitle = function () {
++        var $$ = this;
++        return $$.hasType('donut') ? $$.config.donut_title : "";
++    };
++
++    ChartInternal.prototype.updateTargetsForArc = function (targets) {
++        var $$ = this,
++            main = $$.main,
++            mainPies,
++            mainPieEnter,
++            classChartArc = $$.classChartArc.bind($$),
++            classArcs = $$.classArcs.bind($$),
++            classFocus = $$.classFocus.bind($$);
++        mainPies = main.select('.' + CLASS.chartArcs).selectAll('.' + CLASS.chartArc).data($$.pie(targets)).attr("class", function (d) {
++            return classChartArc(d) + classFocus(d.data);
++        });
++        mainPieEnter = mainPies.enter().append("g").attr("class", classChartArc);
++        mainPieEnter.append('g').attr('class', classArcs);
++        mainPieEnter.append("text").attr("dy", $$.hasType('gauge') ? "-.1em" : ".35em").style("opacity", 0).style("text-anchor", "middle").style("pointer-events", "none");
++        // MEMO: can not keep same color..., but not bad to update color in redraw
++        //mainPieUpdate.exit().remove();
++    };
++
++    ChartInternal.prototype.initArc = function () {
++        var $$ = this;
++        $$.arcs = $$.main.select('.' + CLASS.chart).append("g").attr("class", CLASS.chartArcs).attr("transform", $$.getTranslate('arc'));
++        $$.arcs.append('text').attr('class', CLASS.chartArcsTitle).style("text-anchor", "middle").text($$.getArcTitle());
++    };
++
++    ChartInternal.prototype.redrawArc = function (duration, durationForExit, withTransform) {
++        var $$ = this,
++            d3 = $$.d3,
++            config = $$.config,
++            main = $$.main,
++            arcs,
++            mainArc,
++            arcLabelLines,
++            mainArcLabelLine,
++            hasGaugeType = $$.hasType('gauge');
++        arcs = main.selectAll('.' + CLASS.arcs).selectAll('.' + CLASS.arc).data($$.arcData.bind($$));
++        mainArc = arcs.enter().append('path').attr("class", $$.classArc.bind($$)).style("fill", function (d) {
++            return $$.color(d.data);
++        }).style("cursor", function (d) {
++            return config.interaction_enabled && config.data_selection_isselectable(d) ? "pointer" : null;
++        }).each(function (d) {
++            if ($$.isGaugeType(d.data)) {
++                d.startAngle = d.endAngle = config.gauge_startingAngle;
++            }
++            this._current = d;
++        }).merge(arcs);
++        if (hasGaugeType) {
++            arcLabelLines = main.selectAll('.' + CLASS.arcs).selectAll('.' + CLASS.arcLabelLine).data($$.arcData.bind($$));
++            mainArcLabelLine = arcLabelLines.enter().append('rect').attr("class", function (d) {
++                return CLASS.arcLabelLine + ' ' + CLASS.target + ' ' + CLASS.target + '-' + d.data.id;
++            }).merge(arcLabelLines);
++
++            if ($$.filterTargetsToShow($$.data.targets).length === 1) {
++                mainArcLabelLine.style("display", "none");
++            } else {
++                mainArcLabelLine.style("fill", function (d) {
++                    return config.color_pattern.length > 0 ? $$.levelColor(d.data.values[0].value) : $$.color(d.data);
++                }).style("display", config.gauge_labelLine_show ? "" : "none").each(function (d) {
++                    var lineLength = 0,
++                        lineThickness = 2,
++                        x = 0,
++                        y = 0,
++                        transform = "";
++                    if ($$.hiddenTargetIds.indexOf(d.data.id) < 0) {
++                        var updated = $$.updateAngle(d),
++                            innerLineLength = $$.gaugeArcWidth / $$.filterTargetsToShow($$.data.targets).length * (updated.index + 1),
++                            lineAngle = updated.endAngle - Math.PI / 2,
++                            arcInnerRadius = $$.radius - innerLineLength,
++                            linePositioningAngle = lineAngle - (arcInnerRadius === 0 ? 0 : 1 / arcInnerRadius);
++                        lineLength = $$.radiusExpanded - $$.radius + innerLineLength;
++                        x = Math.cos(linePositioningAngle) * arcInnerRadius;
++                        y = Math.sin(linePositioningAngle) * arcInnerRadius;
++                        transform = "rotate(" + lineAngle * 180 / Math.PI + ", " + x + ", " + y + ")";
++                    }
++                    d3.select(this).attr('x', x).attr('y', y).attr('width', lineLength).attr('height', lineThickness).attr('transform', transform).style("stroke-dasharray", "0, " + (lineLength + lineThickness) + ", 0");
++                });
++            }
++        }
++        mainArc.attr("transform", function (d) {
++            return !$$.isGaugeType(d.data) && withTransform ? "scale(0)" : "";
++        }).on('mouseover', config.interaction_enabled ? function (d) {
++            var updated, arcData;
++            if ($$.transiting) {
++                // skip while transiting
++                return;
++            }
++            updated = $$.updateAngle(d);
++            if (updated) {
++                arcData = $$.convertToArcData(updated);
++                // transitions
++                $$.expandArc(updated.data.id);
++                $$.api.focus(updated.data.id);
++                $$.toggleFocusLegend(updated.data.id, true);
++                $$.config.data_onmouseover(arcData, this);
++            }
++        } : null).on('mousemove', config.interaction_enabled ? function (d) {
++            var updated = $$.updateAngle(d),
++                arcData,
++                selectedData;
++            if (updated) {
++                arcData = $$.convertToArcData(updated), selectedData = [arcData];
++                $$.showTooltip(selectedData, this);
++            }
++        } : null).on('mouseout', config.interaction_enabled ? function (d) {
++            var updated, arcData;
++            if ($$.transiting) {
++                // skip while transiting
++                return;
++            }
++            updated = $$.updateAngle(d);
++            if (updated) {
++                arcData = $$.convertToArcData(updated);
++                // transitions
++                $$.unexpandArc(updated.data.id);
++                $$.api.revert();
++                $$.revertLegend();
++                $$.hideTooltip();
++                $$.config.data_onmouseout(arcData, this);
++            }
++        } : null).on('click', config.interaction_enabled ? function (d, i) {
++            var updated = $$.updateAngle(d),
++                arcData;
++            if (updated) {
++                arcData = $$.convertToArcData(updated);
++                if ($$.toggleShape) {
++                    $$.toggleShape(this, arcData, i);
++                }
++                $$.config.data_onclick.call($$.api, arcData, this);
++            }
++        } : null).each(function () {
++            $$.transiting = true;
++        }).transition().duration(duration).attrTween("d", function (d) {
++            var updated = $$.updateAngle(d),
++                interpolate;
++            if (!updated) {
++                return function () {
++                    return "M 0 0";
++                };
++            }
++            //                if (this._current === d) {
++            //                    this._current = {
++            //                        startAngle: Math.PI*2,
++            //                        endAngle: Math.PI*2,
++            //                    };
++            //                }
++            if (isNaN(this._current.startAngle)) {
++                this._current.startAngle = 0;
++            }
++            if (isNaN(this._current.endAngle)) {
++                this._current.endAngle = this._current.startAngle;
++            }
++            interpolate = d3.interpolate(this._current, updated);
++            this._current = interpolate(0);
++            return function (t) {
++                var interpolated = interpolate(t);
++                interpolated.data = d.data; // data.id will be updated by interporator
++                return $$.getArc(interpolated, true);
++            };
++        }).attr("transform", withTransform ? "scale(1)" : "").style("fill", function (d) {
++            return $$.levelColor ? $$.levelColor(d.data.values[0].value) : $$.color(d.data.id);
++        }) // Where gauge reading color would receive customization.
++        .call($$.endall, function () {
++            $$.transiting = false;
++        });
++        arcs.exit().transition().duration(durationForExit).style('opacity', 0).remove();
++        main.selectAll('.' + CLASS.chartArc).select('text').style("opacity", 0).attr('class', function (d) {
++            return $$.isGaugeType(d.data) ? CLASS.gaugeValue : '';
++        }).text($$.textForArcLabel.bind($$)).attr("transform", $$.transformForArcLabel.bind($$)).style('font-size', function (d) {
++            return $$.isGaugeType(d.data) && $$.filterTargetsToShow($$.data.targets).length === 1 ? Math.round($$.radius / 5) + 'px' : '';
++        }).transition().duration(duration).style("opacity", function (d) {
++            return $$.isTargetToShow(d.data.id) && $$.isArcType(d.data) ? 1 : 0;
++        });
++        main.select('.' + CLASS.chartArcsTitle).style("opacity", $$.hasType('donut') || hasGaugeType ? 1 : 0);
++
++        if (hasGaugeType) {
++            var index = 0;
++            var backgroundArc = $$.arcs.select('g.' + CLASS.chartArcsBackground).selectAll('path.' + CLASS.chartArcsBackground).data($$.data.targets);
++
++            backgroundArc.enter().append("path").attr("class", function (d, i) {
++                return CLASS.chartArcsBackground + ' ' + CLASS.chartArcsBackground + '-' + i;
++            }).merge(backgroundArc).attr("d", function (d1) {
++                if ($$.hiddenTargetIds.indexOf(d1.id) >= 0) {
++                    return "M 0 0";
++                }
++
++                var d = {
++                    data: [{ value: config.gauge_max }],
++                    startAngle: config.gauge_startingAngle,
++                    endAngle: -1 * config.gauge_startingAngle * (config.gauge_fullCircle ? Math.PI : 1),
++                    index: index++
++                };
++                return $$.getArc(d, true, true);
++            });
++
++            backgroundArc.exit().remove();
++
++            $$.arcs.select('.' + CLASS.chartArcsGaugeUnit).attr("dy", ".75em").text(config.gauge_label_show ? config.gauge_units : '');
++            $$.arcs.select('.' + CLASS.chartArcsGaugeMin).attr("dx", -1 * ($$.innerRadius + ($$.radius - $$.innerRadius) / (config.gauge_fullCircle ? 1 : 2)) + "px").attr("dy", "1.2em").text(config.gauge_label_show ? $$.textForGaugeMinMax(config.gauge_min, false) : '');
++            $$.arcs.select('.' + CLASS.chartArcsGaugeMax).attr("dx", $$.innerRadius + ($$.radius - $$.innerRadius) / (config.gauge_fullCircle ? 1 : 2) + "px").attr("dy", "1.2em").text(config.gauge_label_show ? $$.textForGaugeMinMax(config.gauge_max, true) : '');
++        }
++    };
++    ChartInternal.prototype.initGauge = function () {
++        var arcs = this.arcs;
++        if (this.hasType('gauge')) {
++            arcs.append('g').attr("class", CLASS.chartArcsBackground);
++            arcs.append("text").attr("class", CLASS.chartArcsGaugeUnit).style("text-anchor", "middle").style("pointer-events", "none");
++            arcs.append("text").attr("class", CLASS.chartArcsGaugeMin).style("text-anchor", "middle").style("pointer-events", "none");
++            arcs.append("text").attr("class", CLASS.chartArcsGaugeMax).style("text-anchor", "middle").style("pointer-events", "none");
++        }
++    };
++    ChartInternal.prototype.getGaugeLabelHeight = function () {
++        return this.config.gauge_label_show ? 20 : 0;
++    };
++
++    ChartInternal.prototype.hasCaches = function (ids) {
++        for (var i = 0; i < ids.length; i++) {
++            if (!(ids[i] in this.cache)) {
++                return false;
++            }
++        }
++        return true;
++    };
++    ChartInternal.prototype.addCache = function (id, target) {
++        this.cache[id] = this.cloneTarget(target);
++    };
++    ChartInternal.prototype.getCaches = function (ids) {
++        var targets = [],
++            i;
++        for (i = 0; i < ids.length; i++) {
++            if (ids[i] in this.cache) {
++                targets.push(this.cloneTarget(this.cache[ids[i]]));
++            }
++        }
++        return targets;
++    };
++
++    ChartInternal.prototype.categoryName = function (i) {
++        var config = this.config;
++        return i < config.axis_x_categories.length ? config.axis_x_categories[i] : i;
++    };
++
++    ChartInternal.prototype.generateTargetClass = function (targetId) {
++        return targetId || targetId === 0 ? ('-' + targetId).replace(/\s/g, '-') : '';
++    };
++    ChartInternal.prototype.generateClass = function (prefix, targetId) {
++        return " " + prefix + " " + prefix + this.generateTargetClass(targetId);
++    };
++    ChartInternal.prototype.classText = function (d) {
++        return this.generateClass(CLASS.text, d.index);
++    };
++    ChartInternal.prototype.classTexts = function (d) {
++        return this.generateClass(CLASS.texts, d.id);
++    };
++    ChartInternal.prototype.classShape = function (d) {
++        return this.generateClass(CLASS.shape, d.index);
++    };
++    ChartInternal.prototype.classShapes = function (d) {
++        return this.generateClass(CLASS.shapes, d.id);
++    };
++    ChartInternal.prototype.classLine = function (d) {
++        return this.classShape(d) + this.generateClass(CLASS.line, d.id);
++    };
++    ChartInternal.prototype.classLines = function (d) {
++        return this.classShapes(d) + this.generateClass(CLASS.lines, d.id);
++    };
++    ChartInternal.prototype.classCircle = function (d) {
++        return this.classShape(d) + this.generateClass(CLASS.circle, d.index);
++    };
++    ChartInternal.prototype.classCircles = function (d) {
++        return this.classShapes(d) + this.generateClass(CLASS.circles, d.id);
++    };
++    ChartInternal.prototype.classBar = function (d) {
++        return this.classShape(d) + this.generateClass(CLASS.bar, d.index);
++    };
++    ChartInternal.prototype.classBars = function (d) {
++        return this.classShapes(d) + this.generateClass(CLASS.bars, d.id);
++    };
++    ChartInternal.prototype.classArc = function (d) {
++        return this.classShape(d.data) + this.generateClass(CLASS.arc, d.data.id);
++    };
++    ChartInternal.prototype.classArcs = function (d) {
++        return this.classShapes(d.data) + this.generateClass(CLASS.arcs, d.data.id);
++    };
++    ChartInternal.prototype.classArea = function (d) {
++        return this.classShape(d) + this.generateClass(CLASS.area, d.id);
++    };
++    ChartInternal.prototype.classAreas = function (d) {
++        return this.classShapes(d) + this.generateClass(CLASS.areas, d.id);
++    };
++    ChartInternal.prototype.classRegion = function (d, i) {
++        return this.generateClass(CLASS.region, i) + ' ' + ('class' in d ? d['class'] : '');
++    };
++    ChartInternal.prototype.classEvent = function (d) {
++        return this.generateClass(CLASS.eventRect, d.index);
++    };
++    ChartInternal.prototype.classTarget = function (id) {
++        var $$ = this;
++        var additionalClassSuffix = $$.config.data_classes[id],
++            additionalClass = '';
++        if (additionalClassSuffix) {
++            additionalClass = ' ' + CLASS.target + '-' + additionalClassSuffix;
++        }
++        return $$.generateClass(CLASS.target, id) + additionalClass;
++    };
++    ChartInternal.prototype.classFocus = function (d) {
++        return this.classFocused(d) + this.classDefocused(d);
++    };
++    ChartInternal.prototype.classFocused = function (d) {
++        return ' ' + (this.focusedTargetIds.indexOf(d.id) >= 0 ? CLASS.focused : '');
++    };
++    ChartInternal.prototype.classDefocused = function (d) {
++        return ' ' + (this.defocusedTargetIds.indexOf(d.id) >= 0 ? CLASS.defocused : '');
++    };
++    ChartInternal.prototype.classChartText = function (d) {
++        return CLASS.chartText + this.classTarget(d.id);
++    };
++    ChartInternal.prototype.classChartLine = function (d) {
++        return CLASS.chartLine + this.classTarget(d.id);
++    };
++    ChartInternal.prototype.classChartBar = function (d) {
++        return CLASS.chartBar + this.classTarget(d.id);
++    };
++    ChartInternal.prototype.classChartArc = function (d) {
++        return CLASS.chartArc + this.classTarget(d.data.id);
++    };
++    ChartInternal.prototype.getTargetSelectorSuffix = function (targetId) {
++        return this.generateTargetClass(targetId).replace(/([?!@#$%^&*()_=+,.<>'":;\[\]\/|~`{}\\])/g, '\\$1');
++    };
++    ChartInternal.prototype.selectorTarget = function (id, prefix) {
++        return (prefix || '') + '.' + CLASS.target + this.getTargetSelectorSuffix(id);
++    };
++    ChartInternal.prototype.selectorTargets = function (ids, prefix) {
++        var $$ = this;
++        ids = ids || [];
++        return ids.length ? ids.map(function (id) {
++            return $$.selectorTarget(id, prefix);
++        }) : null;
++    };
++    ChartInternal.prototype.selectorLegend = function (id) {
++        return '.' + CLASS.legendItem + this.getTargetSelectorSuffix(id);
++    };
++    ChartInternal.prototype.selectorLegends = function (ids) {
++        var $$ = this;
++        return ids && ids.length ? ids.map(function (id) {
++            return $$.selectorLegend(id);
++        }) : null;
++    };
++
++    ChartInternal.prototype.getClipPath = function (id) {
++        var isIE9 = window.navigator.appVersion.toLowerCase().indexOf("msie 9.") >= 0;
++        return "url(" + (isIE9 ? "" : document.URL.split('#')[0]) + "#" + id + ")";
++    };
++    ChartInternal.prototype.appendClip = function (parent, id) {
++        return parent.append("clipPath").attr("id", id).append("rect");
++    };
++    ChartInternal.prototype.getAxisClipX = function (forHorizontal) {
++        // axis line width + padding for left
++        var left = Math.max(30, this.margin.left);
++        return forHorizontal ? -(1 + left) : -(left - 1);
++    };
++    ChartInternal.prototype.getAxisClipY = function (forHorizontal) {
++        return forHorizontal ? -20 : -this.margin.top;
++    };
++    ChartInternal.prototype.getXAxisClipX = function () {
++        var $$ = this;
++        return $$.getAxisClipX(!$$.config.axis_rotated);
++    };
++    ChartInternal.prototype.getXAxisClipY = function () {
++        var $$ = this;
++        return $$.getAxisClipY(!$$.config.axis_rotated);
++    };
++    ChartInternal.prototype.getYAxisClipX = function () {
++        var $$ = this;
++        return $$.config.axis_y_inner ? -1 : $$.getAxisClipX($$.config.axis_rotated);
++    };
++    ChartInternal.prototype.getYAxisClipY = function () {
++        var $$ = this;
++        return $$.getAxisClipY($$.config.axis_rotated);
++    };
++    ChartInternal.prototype.getAxisClipWidth = function (forHorizontal) {
++        var $$ = this,
++            left = Math.max(30, $$.margin.left),
++            right = Math.max(30, $$.margin.right);
++        // width + axis line width + padding for left/right
++        return forHorizontal ? $$.width + 2 + left + right : $$.margin.left + 20;
++    };
++    ChartInternal.prototype.getAxisClipHeight = function (forHorizontal) {
++        // less than 20 is not enough to show the axis label 'outer' without legend
++        return (forHorizontal ? this.margin.bottom : this.margin.top + this.height) + 20;
++    };
++    ChartInternal.prototype.getXAxisClipWidth = function () {
++        var $$ = this;
++        return $$.getAxisClipWidth(!$$.config.axis_rotated);
++    };
++    ChartInternal.prototype.getXAxisClipHeight = function () {
++        var $$ = this;
++        return $$.getAxisClipHeight(!$$.config.axis_rotated);
++    };
++    ChartInternal.prototype.getYAxisClipWidth = function () {
++        var $$ = this;
++        return $$.getAxisClipWidth($$.config.axis_rotated) + ($$.config.axis_y_inner ? 20 : 0);
++    };
++    ChartInternal.prototype.getYAxisClipHeight = function () {
++        var $$ = this;
++        return $$.getAxisClipHeight($$.config.axis_rotated);
++    };
++
++    ChartInternal.prototype.generateColor = function () {
++        var $$ = this,
++            config = $$.config,
++            d3 = $$.d3,
++            colors = config.data_colors,
++            pattern = notEmpty(config.color_pattern) ? config.color_pattern : d3.schemeCategory10,
++            callback = config.data_color,
++            ids = [];
++
++        return function (d) {
++            var id = d.id || d.data && d.data.id || d,
++                color;
++
++            // if callback function is provided
++            if (colors[id] instanceof Function) {
++                color = colors[id](d);
++            }
++            // if specified, choose that color
++            else if (colors[id]) {
++                    color = colors[id];
++                }
++                // if not specified, choose from pattern
++                else {
++                        if (ids.indexOf(id) < 0) {
++                            ids.push(id);
++                        }
++                        color = pattern[ids.indexOf(id) % pattern.length];
++                        colors[id] = color;
++                    }
++            return callback instanceof Function ? callback(color, d) : color;
++        };
++    };
++    ChartInternal.prototype.generateLevelColor = function () {
++        var $$ = this,
++            config = $$.config,
++            colors = config.color_pattern,
++            threshold = config.color_threshold,
++            asValue = threshold.unit === 'value',
++            values = threshold.values && threshold.values.length ? threshold.values : [],
++            max = threshold.max || 100;
++        return notEmpty(config.color_threshold) ? function (value) {
++            var i,
++                v,
++                color = colors[colors.length - 1];
++            for (i = 0; i < values.length; i++) {
++                v = asValue ? value : value * 100 / max;
++                if (v < values[i]) {
++                    color = colors[i];
++                    break;
++                }
++            }
++            return color;
++        } : null;
++    };
++
++    ChartInternal.prototype.getDefaultConfig = function () {
++        var config = {
++            bindto: '#chart',
++            svg_classname: undefined,
++            size_width: undefined,
++            size_height: undefined,
++            padding_left: undefined,
++            padding_right: undefined,
++            padding_top: undefined,
++            padding_bottom: undefined,
++            resize_auto: true,
++            zoom_enabled: false,
++            zoom_initialRange: undefined,
++            zoom_privileged: false,
++            zoom_rescale: false,
++            zoom_onzoom: function zoom_onzoom() {},
++            zoom_onzoomstart: function zoom_onzoomstart() {},
++            zoom_onzoomend: function zoom_onzoomend() {},
++            zoom_x_min: undefined,
++            zoom_x_max: undefined,
++            interaction_brighten: true,
++            interaction_enabled: true,
++            onmouseover: function onmouseover() {},
++            onmouseout: function onmouseout() {},
++            onresize: function onresize() {},
++            onresized: function onresized() {},
++            oninit: function oninit() {},
++            onrendered: function onrendered() {},
++            transition_duration: 350,
++            data_x: undefined,
++            data_xs: {},
++            data_xFormat: '%Y-%m-%d',
++            data_xLocaltime: true,
++            data_xSort: true,
++            data_idConverter: function data_idConverter(id) {
++                return id;
++            },
++            data_names: {},
++            data_classes: {},
++            data_groups: [],
++            data_axes: {},
++            data_type: undefined,
++            data_types: {},
++            data_labels: {},
++            data_order: 'desc',
++            data_regions: {},
++            data_color: undefined,
++            data_colors: {},
++            data_hide: false,
++            data_filter: undefined,
++            data_selection_enabled: false,
++            data_selection_grouped: false,
++            data_selection_isselectable: function data_selection_isselectable() {
++                return true;
++            },
++            data_selection_multiple: true,
++            data_selection_draggable: false,
++            data_onclick: function data_onclick() {},
++            data_onmouseover: function data_onmouseover() {},
++            data_onmouseout: function data_onmouseout() {},
++            data_onselected: function data_onselected() {},
++            data_onunselected: function data_onunselected() {},
++            data_url: undefined,
++            data_headers: undefined,
++            data_json: undefined,
++            data_rows: undefined,
++            data_columns: undefined,
++            data_mimeType: undefined,
++            data_keys: undefined,
++            // configuration for no plot-able data supplied.
++            data_empty_label_text: "",
++            // subchart
++            subchart_show: false,
++            subchart_size_height: 60,
++            subchart_axis_x_show: true,
++            subchart_onbrush: function subchart_onbrush() {},
++            // color
++            color_pattern: [],
++            color_threshold: {},
++            // legend
++            legend_show: true,
++            legend_hide: false,
++            legend_position: 'bottom',
++            legend_inset_anchor: 'top-left',
++            legend_inset_x: 10,
++            legend_inset_y: 0,
++            legend_inset_step: undefined,
++            legend_item_onclick: undefined,
++            legend_item_onmouseover: undefined,
++            legend_item_onmouseout: undefined,
++            legend_equally: false,
++            legend_padding: 0,
++            legend_item_tile_width: 10,
++            legend_item_tile_height: 10,
++            // axis
++            axis_rotated: false,
++            axis_x_show: true,
++            axis_x_type: 'indexed',
++            axis_x_localtime: true,
++            axis_x_categories: [],
++            axis_x_tick_centered: false,
++            axis_x_tick_format: undefined,
++            axis_x_tick_culling: {},
++            axis_x_tick_culling_max: 10,
++            axis_x_tick_count: undefined,
++            axis_x_tick_fit: true,
++            axis_x_tick_values: null,
++            axis_x_tick_rotate: 0,
++            axis_x_tick_outer: true,
++            axis_x_tick_multiline: true,
++            axis_x_tick_multilineMax: 0,
++            axis_x_tick_width: null,
++            axis_x_max: undefined,
++            axis_x_min: undefined,
++            axis_x_padding: {},
++            axis_x_height: undefined,
++            axis_x_selection: undefined,
++            axis_x_label: {},
++            axis_x_inner: undefined,
++            axis_y_show: true,
++            axis_y_type: undefined,
++            axis_y_max: undefined,
++            axis_y_min: undefined,
++            axis_y_inverted: false,
++            axis_y_center: undefined,
++            axis_y_inner: undefined,
++            axis_y_label: {},
++            axis_y_tick_format: undefined,
++            axis_y_tick_outer: true,
++            axis_y_tick_values: null,
++            axis_y_tick_rotate: 0,
++            axis_y_tick_count: undefined,
++            axis_y_tick_time_type: undefined,
++            axis_y_tick_time_interval: undefined,
++            axis_y_padding: {},
++            axis_y_default: undefined,
++            axis_y2_show: false,
++            axis_y2_max: undefined,
++            axis_y2_min: undefined,
++            axis_y2_inverted: false,
++            axis_y2_center: undefined,
++            axis_y2_inner: undefined,
++            axis_y2_label: {},
++            axis_y2_tick_format: undefined,
++            axis_y2_tick_outer: true,
++            axis_y2_tick_values: null,
++            axis_y2_tick_count: undefined,
++            axis_y2_padding: {},
++            axis_y2_default: undefined,
++            // grid
++            grid_x_show: false,
++            grid_x_type: 'tick',
++            grid_x_lines: [],
++            grid_y_show: false,
++            // not used
++            // grid_y_type: 'tick',
++            grid_y_lines: [],
++            grid_y_ticks: 10,
++            grid_focus_show: true,
++            grid_lines_front: true,
++            // point - point of each data
++            point_show: true,
++            point_r: 2.5,
++            point_sensitivity: 10,
++            point_focus_expand_enabled: true,
++            point_focus_expand_r: undefined,
++            point_select_r: undefined,
++            // line
++            line_connectNull: false,
++            line_step_type: 'step',
++            // bar
++            bar_width: undefined,
++            bar_width_ratio: 0.6,
++            bar_width_max: undefined,
++            bar_zerobased: true,
++            bar_space: 0,
++            // area
++            area_zerobased: true,
++            area_above: false,
++            // pie
++            pie_label_show: true,
++            pie_label_format: undefined,
++            pie_label_threshold: 0.05,
++            pie_label_ratio: undefined,
++            pie_expand: {},
++            pie_expand_duration: 50,
++            // gauge
++            gauge_fullCircle: false,
++            gauge_label_show: true,
++            gauge_labelLine_show: true,
++            gauge_label_format: undefined,
++            gauge_min: 0,
++            gauge_max: 100,
++            gauge_startingAngle: -1 * Math.PI / 2,
++            gauge_label_extents: undefined,
++            gauge_units: undefined,
++            gauge_width: undefined,
++            gauge_arcs_minWidth: 5,
++            gauge_expand: {},
++            gauge_expand_duration: 50,
++            // donut
++            donut_label_show: true,
++            donut_label_format: undefined,
++            donut_label_threshold: 0.05,
++            donut_label_ratio: undefined,
++            donut_width: undefined,
++            donut_title: "",
++            donut_expand: {},
++            donut_expand_duration: 50,
++            // spline
++            spline_interpolation_type: 'cardinal',
++            // region - region to change style
++            regions: [],
++            // tooltip - show when mouseover on each data
++            tooltip_show: true,
++            tooltip_grouped: true,
++            tooltip_order: undefined,
++            tooltip_format_title: undefined,
++            tooltip_format_name: undefined,
++            tooltip_format_value: undefined,
++            tooltip_position: undefined,
++            tooltip_contents: function tooltip_contents(d, defaultTitleFormat, defaultValueFormat, color) {
++                return this.getTooltipContent ? this.getTooltipContent(d, defaultTitleFormat, defaultValueFormat, color) : '';
++            },
++            tooltip_init_show: false,
++            tooltip_init_x: 0,
++            tooltip_init_position: { top: '0px', left: '50px' },
++            tooltip_onshow: function tooltip_onshow() {},
++            tooltip_onhide: function tooltip_onhide() {},
++            // title
++            title_text: undefined,
++            title_padding: {
++                top: 0,
++                right: 0,
++                bottom: 0,
++                left: 0
++            },
++            title_position: 'top-center'
++        };
++
++        Object.keys(this.additionalConfig).forEach(function (key) {
++            config[key] = this.additionalConfig[key];
++        }, this);
++
++        return config;
++    };
++    ChartInternal.prototype.additionalConfig = {};
++
++    ChartInternal.prototype.loadConfig = function (config) {
++        var this_config = this.config,
++            target,
++            keys,
++            read;
++        function find() {
++            var key = keys.shift();
++            //        console.log("key =>", key, ", target =>", target);
++            if (key && target && (typeof target === 'undefined' ? 'undefined' : _typeof(target)) === 'object' && key in target) {
++                target = target[key];
++                return find();
++            } else if (!key) {
++                return target;
++            } else {
++                return undefined;
++            }
++        }
++        Object.keys(this_config).forEach(function (key) {
++            target = config;
++            keys = key.split('_');
++            read = find();
++            //        console.log("CONFIG : ", key, read);
++            if (isDefined(read)) {
++                this_config[key] = read;
++            }
++        });
++    };
++
++    ChartInternal.prototype.convertUrlToData = function (url, mimeType, headers, keys, done) {
++        var $$ = this,
++            type = mimeType ? mimeType : 'csv',
++            f,
++            converter;
++
++        if (type === 'json') {
++            f = $$.d3.json;
++            converter = $$.convertJsonToData;
++        } else if (type === 'tsv') {
++            f = $$.d3.tsv;
++            converter = $$.convertXsvToData;
++        } else {
++            f = $$.d3.csv;
++            converter = $$.convertXsvToData;
++        }
++
++        f(url, headers).then(function (data) {
++            done.call($$, converter.call($$, data, keys));
++        }).catch(function (error) {
++            throw error;
++        });
++    };
++    ChartInternal.prototype.convertXsvToData = function (xsv) {
++        var keys = xsv.columns,
++            rows = xsv;
++        if (rows.length === 0) {
++            return { keys: keys, rows: [keys.reduce(function (row, key) {
++                    return Object.assign(row, defineProperty({}, key, null));
++                }, {})] };
++        } else {
++            // [].concat() is to convert result into a plain array otherwise
++            // test is not happy because rows have properties.
++            return { keys: keys, rows: [].concat(xsv) };
++        }
++    };
++    ChartInternal.prototype.convertJsonToData = function (json, keys) {
++        var $$ = this,
++            new_rows = [],
++            targetKeys,
++            data;
++        if (keys) {
++            // when keys specified, json would be an array that includes objects
++            if (keys.x) {
++                targetKeys = keys.value.concat(keys.x);
++                $$.config.data_x = keys.x;
++            } else {
++                targetKeys = keys.value;
++            }
++            new_rows.push(targetKeys);
++            json.forEach(function (o) {
++                var new_row = [];
++                targetKeys.forEach(function (key) {
++                    // convert undefined to null because undefined data will be removed in convertDataToTargets()
++                    var v = $$.findValueInJson(o, key);
++                    if (isUndefined(v)) {
++                        v = null;
++                    }
++                    new_row.push(v);
++                });
++                new_rows.push(new_row);
++            });
++            data = $$.convertRowsToData(new_rows);
++        } else {
++            Object.keys(json).forEach(function (key) {
++                new_rows.push([key].concat(json[key]));
++            });
++            data = $$.convertColumnsToData(new_rows);
++        }
++        return data;
++    };
++    ChartInternal.prototype.findValueInJson = function (object, path) {
++        path = path.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties (replace [] with .)
++        path = path.replace(/^\./, ''); // strip a leading dot
++        var pathArray = path.split('.');
++        for (var i = 0; i < pathArray.length; ++i) {
++            var k = pathArray[i];
++            if (k in object) {
++                object = object[k];
++            } else {
++                return;
++            }
++        }
++        return object;
++    };
++
++    /**
++     * Converts the rows to normalized data.
++     * @param {any[][]} rows The row data
++     * @return {Object}
++     */
++    ChartInternal.prototype.convertRowsToData = function (rows) {
++        var newRows = [];
++        var keys = rows[0];
++
++        for (var i = 1; i < rows.length; i++) {
++            var newRow = {};
++            for (var j = 0; j < rows[i].length; j++) {
++                if (isUndefined(rows[i][j])) {
++                    throw new Error("Source data is missing a component at (" + i + "," + j + ")!");
++                }
++                newRow[keys[j]] = rows[i][j];
++            }
++            newRows.push(newRow);
++        }
++        return { keys: keys, rows: newRows };
++    };
++
++    /**
++     * Converts the columns to normalized data.
++     * @param {any[][]} columns The column data
++     * @return {Object}
++     */
++    ChartInternal.prototype.convertColumnsToData = function (columns) {
++        var newRows = [];
++        var keys = [];
++
++        for (var i = 0; i < columns.length; i++) {
++            var key = columns[i][0];
++            for (var j = 1; j < columns[i].length; j++) {
++                if (isUndefined(newRows[j - 1])) {
++                    newRows[j - 1] = {};
++                }
++                if (isUndefined(columns[i][j])) {
++                    throw new Error("Source data is missing a component at (" + i + "," + j + ")!");
++                }
++                newRows[j - 1][key] = columns[i][j];
++            }
++            keys.push(key);
++        }
++
++        return { keys: keys, rows: newRows };
++    };
++
++    /**
++     * Converts the data format into the target format.
++     * @param {!Object} data
++     * @param {!Array} data.keys Ordered list of target IDs.
++     * @param {!Array} data.rows Rows of data to convert.
++     * @param {boolean} appendXs True to append to $$.data.xs, False to replace.
++     * @return {!Array}
++     */
++    ChartInternal.prototype.convertDataToTargets = function (data, appendXs) {
++        var $$ = this,
++            config = $$.config,
++            targets,
++            ids,
++            xs,
++            keys;
++
++        // handles format where keys are not orderly provided
++        if (isArray(data)) {
++            keys = Object.keys(data[0]);
++        } else {
++            keys = data.keys;
++            data = data.rows;
++        }
++
++        ids = keys.filter($$.isNotX, $$);
++        xs = keys.filter($$.isX, $$);
++
++        // save x for update data by load when custom x and c3.x API
++        ids.forEach(function (id) {
++            var xKey = $$.getXKey(id);
++
++            if ($$.isCustomX() || $$.isTimeSeries()) {
++                // if included in input data
++                if (xs.indexOf(xKey) >= 0) {
++                    $$.data.xs[id] = (appendXs && $$.data.xs[id] ? $$.data.xs[id] : []).concat(data.map(function (d) {
++                        return d[xKey];
++                    }).filter(isValue).map(function (rawX, i) {
++                        return $$.generateTargetX(rawX, id, i);
++                    }));
++                }
++                // if not included in input data, find from preloaded data of other id's x
++                else if (config.data_x) {
++                        $$.data.xs[id] = $$.getOtherTargetXs();
++                    }
++                    // if not included in input data, find from preloaded data
++                    else if (notEmpty(config.data_xs)) {
++                            $$.data.xs[id] = $$.getXValuesOfXKey(xKey, $$.data.targets);
++                        }
++                // MEMO: if no x included, use same x of current will be used
++            } else {
++                $$.data.xs[id] = data.map(function (d, i) {
++                    return i;
++                });
++            }
++        });
++
++        // check x is defined
++        ids.forEach(function (id) {
++            if (!$$.data.xs[id]) {
++                throw new Error('x is not defined for id = "' + id + '".');
++            }
++        });
++
++        // convert to target
++        targets = ids.map(function (id, index) {
++            var convertedId = config.data_idConverter(id);
++            return {
++                id: convertedId,
++                id_org: id,
++                values: data.map(function (d, i) {
++                    var xKey = $$.getXKey(id),
++                        rawX = d[xKey],
++                        value = d[id] !== null && !isNaN(d[id]) ? +d[id] : null,
++                        x;
++                    // use x as categories if custom x and categorized
++                    if ($$.isCustomX() && $$.isCategorized() && !isUndefined(rawX)) {
++                        if (index === 0 && i === 0) {
++                            config.axis_x_categories = [];
++                        }
++                        x = config.axis_x_categories.indexOf(rawX);
++                        if (x === -1) {
++                            x = config.axis_x_categories.length;
++                            config.axis_x_categories.push(rawX);
++                        }
++                    } else {
++                        x = $$.generateTargetX(rawX, id, i);
++                    }
++                    // mark as x = undefined if value is undefined and filter to remove after mapped
++                    if (isUndefined(d[id]) || $$.data.xs[id].length <= i) {
++                        x = undefined;
++                    }
++                    return { x: x, value: value, id: convertedId };
++                }).filter(function (v) {
++                    return isDefined(v.x);
++                })
++            };
++        });
++
++        // finish targets
++        targets.forEach(function (t) {
++            var i;
++            // sort values by its x
++            if (config.data_xSort) {
++                t.values = t.values.sort(function (v1, v2) {
++                    var x1 = v1.x || v1.x === 0 ? v1.x : Infinity,
++                        x2 = v2.x || v2.x === 0 ? v2.x : Infinity;
++                    return x1 - x2;
++                });
++            }
++            // indexing each value
++            i = 0;
++            t.values.forEach(function (v) {
++                v.index = i++;
++            });
++            // this needs to be sorted because its index and value.index is identical
++            $$.data.xs[t.id].sort(function (v1, v2) {
++                return v1 - v2;
++            });
++        });
++
++        // cache information about values
++        $$.hasNegativeValue = $$.hasNegativeValueInTargets(targets);
++        $$.hasPositiveValue = $$.hasPositiveValueInTargets(targets);
++
++        // set target types
++        if (config.data_type) {
++            $$.setTargetType($$.mapToIds(targets).filter(function (id) {
++                return !(id in config.data_types);
++            }), config.data_type);
++        }
++
++        // cache as original id keyed
++        targets.forEach(function (d) {
++            $$.addCache(d.id_org, d);
++        });
++
++        return targets;
++    };
++
++    ChartInternal.prototype.isX = function (key) {
++        var $$ = this,
++            config = $$.config;
++        return config.data_x && key === config.data_x || notEmpty(config.data_xs) && hasValue(config.data_xs, key);
++    };
++    ChartInternal.prototype.isNotX = function (key) {
++        return !this.isX(key);
++    };
++    ChartInternal.prototype.getXKey = function (id) {
++        var $$ = this,
++            config = $$.config;
++        return config.data_x ? config.data_x : notEmpty(config.data_xs) ? config.data_xs[id] : null;
++    };
++    ChartInternal.prototype.getXValuesOfXKey = function (key, targets) {
++        var $$ = this,
++            xValues,
++            ids = targets && notEmpty(targets) ? $$.mapToIds(targets) : [];
++        ids.forEach(function (id) {
++            if ($$.getXKey(id) === key) {
++                xValues = $$.data.xs[id];
++            }
++        });
++        return xValues;
++    };
++    ChartInternal.prototype.getXValue = function (id, i) {
++        var $$ = this;
++        return id in $$.data.xs && $$.data.xs[id] && isValue($$.data.xs[id][i]) ? $$.data.xs[id][i] : i;
++    };
++    ChartInternal.prototype.getOtherTargetXs = function () {
++        var $$ = this,
++            idsForX = Object.keys($$.data.xs);
++        return idsForX.length ? $$.data.xs[idsForX[0]] : null;
++    };
++    ChartInternal.prototype.getOtherTargetX = function (index) {
++        var xs = this.getOtherTargetXs();
++        return xs && index < xs.length ? xs[index] : null;
++    };
++    ChartInternal.prototype.addXs = function (xs) {
++        var $$ = this;
++        Object.keys(xs).forEach(function (id) {
++            $$.config.data_xs[id] = xs[id];
++        });
++    };
++    ChartInternal.prototype.addName = function (data) {
++        var $$ = this,
++            name;
++        if (data) {
++            name = $$.config.data_names[data.id];
++            data.name = name !== undefined ? name : data.id;
++        }
++        return data;
++    };
++    ChartInternal.prototype.getValueOnIndex = function (values, index) {
++        var valueOnIndex = values.filter(function (v) {
++            return v.index === index;
++        });
++        return valueOnIndex.length ? valueOnIndex[0] : null;
++    };
++    ChartInternal.prototype.updateTargetX = function (targets, x) {
++        var $$ = this;
++        targets.forEach(function (t) {
++            t.values.forEach(function (v, i) {
++                v.x = $$.generateTargetX(x[i], t.id, i);
++            });
++            $$.data.xs[t.id] = x;
++        });
++    };
++    ChartInternal.prototype.updateTargetXs = function (targets, xs) {
++        var $$ = this;
++        targets.forEach(function (t) {
++            if (xs[t.id]) {
++                $$.updateTargetX([t], xs[t.id]);
++            }
++        });
++    };
++    ChartInternal.prototype.generateTargetX = function (rawX, id, index) {
++        var $$ = this,
++            x;
++        if ($$.isTimeSeries()) {
++            x = rawX ? $$.parseDate(rawX) : $$.parseDate($$.getXValue(id, index));
++        } else if ($$.isCustomX() && !$$.isCategorized()) {
++            x = isValue(rawX) ? +rawX : $$.getXValue(id, index);
++        } else {
++            x = index;
++        }
++        return x;
++    };
++    ChartInternal.prototype.cloneTarget = function (target) {
++        return {
++            id: target.id,
++            id_org: target.id_org,
++            values: target.values.map(function (d) {
++                return {
++                    x: d.x,
++                    value: d.value,
++                    id: d.id
++                };
++            })
++        };
++    };
++    ChartInternal.prototype.getMaxDataCount = function () {
++        var $$ = this;
++        return $$.d3.max($$.data.targets, function (t) {
++            return t.values.length;
++        });
++    };
++    ChartInternal.prototype.mapToIds = function (targets) {
++        return targets.map(function (d) {
++            return d.id;
++        });
++    };
++    ChartInternal.prototype.mapToTargetIds = function (ids) {
++        var $$ = this;
++        return ids ? [].concat(ids) : $$.mapToIds($$.data.targets);
++    };
++    ChartInternal.prototype.hasTarget = function (targets, id) {
++        var ids = this.mapToIds(targets),
++            i;
++        for (i = 0; i < ids.length; i++) {
++            if (ids[i] === id) {
++                return true;
++            }
++        }
++        return false;
++    };
++    ChartInternal.prototype.isTargetToShow = function (targetId) {
++        return this.hiddenTargetIds.indexOf(targetId) < 0;
++    };
++    ChartInternal.prototype.isLegendToShow = function (targetId) {
++        return this.hiddenLegendIds.indexOf(targetId) < 0;
++    };
++    ChartInternal.prototype.filterTargetsToShow = function (targets) {
++        var $$ = this;
++        return targets.filter(function (t) {
++            return $$.isTargetToShow(t.id);
++        });
++    };
++    ChartInternal.prototype.mapTargetsToUniqueXs = function (targets) {
++        var $$ = this;
++        var xs = $$.d3.set($$.d3.merge(targets.map(function (t) {
++            return t.values.map(function (v) {
++                return +v.x;
++            });
++        }))).values();
++        xs = $$.isTimeSeries() ? xs.map(function (x) {
++            return new Date(+x);
++        }) : xs.map(function (x) {
++            return +x;
++        });
++        return xs.sort(function (a, b) {
++            return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
++        });
++    };
++    ChartInternal.prototype.addHiddenTargetIds = function (targetIds) {
++        targetIds = targetIds instanceof Array ? targetIds : new Array(targetIds);
++        for (var i = 0; i < targetIds.length; i++) {
++            if (this.hiddenTargetIds.indexOf(targetIds[i]) < 0) {
++                this.hiddenTargetIds = this.hiddenTargetIds.concat(targetIds[i]);
++            }
++        }
++    };
++    ChartInternal.prototype.removeHiddenTargetIds = function (targetIds) {
++        this.hiddenTargetIds = this.hiddenTargetIds.filter(function (id) {
++            return targetIds.indexOf(id) < 0;
++        });
++    };
++    ChartInternal.prototype.addHiddenLegendIds = function (targetIds) {
++        targetIds = targetIds instanceof Array ? targetIds : new Array(targetIds);
++        for (var i = 0; i < targetIds.length; i++) {
++            if (this.hiddenLegendIds.indexOf(targetIds[i]) < 0) {
++                this.hiddenLegendIds = this.hiddenLegendIds.concat(targetIds[i]);
++            }
++        }
++    };
++    ChartInternal.prototype.removeHiddenLegendIds = function (targetIds) {
++        this.hiddenLegendIds = this.hiddenLegendIds.filter(function (id) {
++            return targetIds.indexOf(id) < 0;
++        });
++    };
++    ChartInternal.prototype.getValuesAsIdKeyed = function (targets) {
++        var ys = {};
++        targets.forEach(function (t) {
++            ys[t.id] = [];
++            t.values.forEach(function (v) {
++                ys[t.id].push(v.value);
++            });
++        });
++        return ys;
++    };
++    ChartInternal.prototype.checkValueInTargets = function (targets, checker) {
++        var ids = Object.keys(targets),
++            i,
++            j,
++            values;
++        for (i = 0; i < ids.length; i++) {
++            values = targets[ids[i]].values;
++            for (j = 0; j < values.length; j++) {
++                if (checker(values[j].value)) {
++                    return true;
++                }
++            }
++        }
++        return false;
++    };
++    ChartInternal.prototype.hasNegativeValueInTargets = function (targets) {
++        return this.checkValueInTargets(targets, function (v) {
++            return v < 0;
++        });
++    };
++    ChartInternal.prototype.hasPositiveValueInTargets = function (targets) {
++        return this.checkValueInTargets(targets, function (v) {
++            return v > 0;
++        });
++    };
++    ChartInternal.prototype.isOrderDesc = function () {
++        var config = this.config;
++        return typeof config.data_order === 'string' && config.data_order.toLowerCase() === 'desc';
++    };
++    ChartInternal.prototype.isOrderAsc = function () {
++        var config = this.config;
++        return typeof config.data_order === 'string' && config.data_order.toLowerCase() === 'asc';
++    };
++    ChartInternal.prototype.getOrderFunction = function () {
++        var $$ = this,
++            config = $$.config,
++            orderAsc = $$.isOrderAsc(),
++            orderDesc = $$.isOrderDesc();
++        if (orderAsc || orderDesc) {
++            var reducer = function reducer(p, c) {
++                return p + Math.abs(c.value);
++            };
++            return function (t1, t2) {
++                var t1Sum = t1.values.reduce(reducer, 0),
++                    t2Sum = t2.values.reduce(reducer, 0);
++                return orderAsc ? t2Sum - t1Sum : t1Sum - t2Sum;
++            };
++        } else if (isFunction(config.data_order)) {
++            return config.data_order;
++        } else if (isArray(config.data_order)) {
++            var order = config.data_order;
++            return function (t1, t2) {
++                return order.indexOf(t1.id) - order.indexOf(t2.id);
++            };
++        }
++    };
++    ChartInternal.prototype.orderTargets = function (targets) {
++        var fct = this.getOrderFunction();
++        if (fct) {
++            targets.sort(fct);
++        }
++        return targets;
++    };
++    ChartInternal.prototype.filterByX = function (targets, x) {
++        return this.d3.merge(targets.map(function (t) {
++            return t.values;
++        })).filter(function (v) {
++            return v.x - x === 0;
++        });
++    };
++    ChartInternal.prototype.filterRemoveNull = function (data) {
++        return data.filter(function (d) {
++            return isValue(d.value);
++        });
++    };
++    ChartInternal.prototype.filterByXDomain = function (targets, xDomain) {
++        return targets.map(function (t) {
++            return {
++                id: t.id,
++                id_org: t.id_org,
++                values: t.values.filter(function (v) {
++                    return xDomain[0] <= v.x && v.x <= xDomain[1];
++                })
++            };
++        });
++    };
++    ChartInternal.prototype.hasDataLabel = function () {
++        var config = this.config;
++        if (typeof config.data_labels === 'boolean' && config.data_labels) {
++            return true;
++        } else if (_typeof(config.data_labels) === 'object' && notEmpty(config.data_labels)) {
++            return true;
++        }
++        return false;
++    };
++    ChartInternal.prototype.getDataLabelLength = function (min, max, key) {
++        var $$ = this,
++            lengths = [0, 0],
++            paddingCoef = 1.3;
++        $$.selectChart.select('svg').selectAll('.dummy').data([min, max]).enter().append('text').text(function (d) {
++            return $$.dataLabelFormat(d.id)(d);
++        }).each(function (d, i) {
++            lengths[i] = this.getBoundingClientRect()[key] * paddingCoef;
++        }).remove();
++        return lengths;
++    };
++    ChartInternal.prototype.isNoneArc = function (d) {
++        return this.hasTarget(this.data.targets, d.id);
++    }, ChartInternal.prototype.isArc = function (d) {
++        return 'data' in d && this.hasTarget(this.data.targets, d.data.id);
++    };
++    ChartInternal.prototype.findClosestFromTargets = function (targets, pos) {
++        var $$ = this,
++            candidates;
++
++        // map to array of closest points of each target
++        candidates = targets.map(function (target) {
++            return $$.findClosest(target.values, pos);
++        });
++
++        // decide closest point and return
++        return $$.findClosest(candidates, pos);
++    };
++    ChartInternal.prototype.findClosest = function (values, pos) {
++        var $$ = this,
++            minDist = $$.config.point_sensitivity,
++            closest;
++
++        // find mouseovering bar
++        values.filter(function (v) {
++            return v && $$.isBarType(v.id);
++        }).forEach(function (v) {
++            var shape = $$.main.select('.' + CLASS.bars + $$.getTargetSelectorSuffix(v.id) + ' .' + CLASS.bar + '-' + v.index).node();
++            if (!closest && $$.isWithinBar($$.d3.mouse(shape), shape)) {
++                closest = v;
++            }
++        });
++
++        // find closest point from non-bar
++        values.filter(function (v) {
++            return v && !$$.isBarType(v.id);
++        }).forEach(function (v) {
++            var d = $$.dist(v, pos);
++            if (d < minDist) {
++                minDist = d;
++                closest = v;
++            }
++        });
++
++        return closest;
++    };
++    ChartInternal.prototype.dist = function (data, pos) {
++        var $$ = this,
++            config = $$.config,
++            xIndex = config.axis_rotated ? 1 : 0,
++            yIndex = config.axis_rotated ? 0 : 1,
++            y = $$.circleY(data, data.index),
++            x = $$.x(data.x);
++        return Math.sqrt(Math.pow(x - pos[xIndex], 2) + Math.pow(y - pos[yIndex], 2));
++    };
++    ChartInternal.prototype.convertValuesToStep = function (values) {
++        var converted = [].concat(values),
++            i;
++
++        if (!this.isCategorized()) {
++            return values;
++        }
++
++        for (i = values.length + 1; 0 < i; i--) {
++            converted[i] = converted[i - 1];
++        }
++
++        converted[0] = {
++            x: converted[0].x - 1,
++            value: converted[0].value,
++            id: converted[0].id
++        };
++        converted[values.length + 1] = {
++            x: converted[values.length].x + 1,
++            value: converted[values.length].value,
++            id: converted[values.length].id
++        };
++
++        return converted;
++    };
++    ChartInternal.prototype.updateDataAttributes = function (name, attrs) {
++        var $$ = this,
++            config = $$.config,
++            current = config['data_' + name];
++        if (typeof attrs === 'undefined') {
++            return current;
++        }
++        Object.keys(attrs).forEach(function (id) {
++            current[id] = attrs[id];
++        });
++        $$.redraw({
++            withLegend: true
++        });
++        return current;
++    };
++
++    ChartInternal.prototype.load = function (targets, args) {
++        var $$ = this;
++        if (targets) {
++            // filter loading targets if needed
++            if (args.filter) {
++                targets = targets.filter(args.filter);
++            }
++            // set type if args.types || args.type specified
++            if (args.type || args.types) {
++                targets.forEach(function (t) {
++                    var type = args.types && args.types[t.id] ? args.types[t.id] : args.type;
++                    $$.setTargetType(t.id, type);
++                });
++            }
++            // Update/Add data
++            $$.data.targets.forEach(function (d) {
++                for (var i = 0; i < targets.length; i++) {
++                    if (d.id === targets[i].id) {
++                        d.values = targets[i].values;
++                        targets.splice(i, 1);
++                        break;
++                    }
++                }
++            });
++            $$.data.targets = $$.data.targets.concat(targets); // add remained
++        }
++
++        // Set targets
++        $$.updateTargets($$.data.targets);
++
++        // Redraw with new targets
++        $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true });
++
++        if (args.done) {
++            args.done();
++        }
++    };
++    ChartInternal.prototype.loadFromArgs = function (args) {
++        var $$ = this;
++        if (args.data) {
++            $$.load($$.convertDataToTargets(args.data), args);
++        } else if (args.url) {
++            $$.convertUrlToData(args.url, args.mimeType, args.headers, args.keys, function (data) {
++                $$.load($$.convertDataToTargets(data), args);
++            });
++        } else if (args.json) {
++            $$.load($$.convertDataToTargets($$.convertJsonToData(args.json, args.keys)), args);
++        } else if (args.rows) {
++            $$.load($$.convertDataToTargets($$.convertRowsToData(args.rows)), args);
++        } else if (args.columns) {
++            $$.load($$.convertDataToTargets($$.convertColumnsToData(args.columns)), args);
++        } else {
++            $$.load(null, args);
++        }
++    };
++    ChartInternal.prototype.unload = function (targetIds, done) {
++        var $$ = this;
++        if (!done) {
++            done = function done() {};
++        }
++        // filter existing target
++        targetIds = targetIds.filter(function (id) {
++            return $$.hasTarget($$.data.targets, id);
++        });
++        // If no target, call done and return
++        if (!targetIds || targetIds.length === 0) {
++            done();
++            return;
++        }
++        $$.svg.selectAll(targetIds.map(function (id) {
++            return $$.selectorTarget(id);
++        })).transition().style('opacity', 0).remove().call($$.endall, done);
++        targetIds.forEach(function (id) {
++            // Reset fadein for future load
++            $$.withoutFadeIn[id] = false;
++            // Remove target's elements
++            if ($$.legend) {
++                $$.legend.selectAll('.' + CLASS.legendItem + $$.getTargetSelectorSuffix(id)).remove();
++            }
++            // Remove target
++            $$.data.targets = $$.data.targets.filter(function (t) {
++                return t.id !== id;
++            });
++        });
++    };
++
++    ChartInternal.prototype.getYDomainMin = function (targets) {
++        var $$ = this,
++            config = $$.config,
++            ids = $$.mapToIds(targets),
++            ys = $$.getValuesAsIdKeyed(targets),
++            j,
++            k,
++            baseId,
++            idsInGroup,
++            id,
++            hasNegativeValue;
++        if (config.data_groups.length > 0) {
++            hasNegativeValue = $$.hasNegativeValueInTargets(targets);
++            for (j = 0; j < config.data_groups.length; j++) {
++                // Determine baseId
++                idsInGroup = config.data_groups[j].filter(function (id) {
++                    return ids.indexOf(id) >= 0;
++                });
++                if (idsInGroup.length === 0) {
++                    continue;
++                }
++                baseId = idsInGroup[0];
++                // Consider negative values
++                if (hasNegativeValue && ys[baseId]) {
++                    ys[baseId].forEach(function (v, i) {
++                        ys[baseId][i] = v < 0 ? v : 0;
++                    });
++                }
++                // Compute min
++                for (k = 1; k < idsInGroup.length; k++) {
++                    id = idsInGroup[k];
++                    if (!ys[id]) {
++                        continue;
++                    }
++                    ys[id].forEach(function (v, i) {
++                        if ($$.axis.getId(id) === $$.axis.getId(baseId) && ys[baseId] && !(hasNegativeValue && +v > 0)) {
++                            ys[baseId][i] += +v;
++                        }
++                    });
++                }
++            }
++        }
++        return $$.d3.min(Object.keys(ys).map(function (key) {
++            return $$.d3.min(ys[key]);
++        }));
++    };
++    ChartInternal.prototype.getYDomainMax = function (targets) {
++        var $$ = this,
++            config = $$.config,
++            ids = $$.mapToIds(targets),
++            ys = $$.getValuesAsIdKeyed(targets),
++            j,
++            k,
++            baseId,
++            idsInGroup,
++            id,
++            hasPositiveValue;
++        if (config.data_groups.length > 0) {
++            hasPositiveValue = $$.hasPositiveValueInTargets(targets);
++            for (j = 0; j < config.data_groups.length; j++) {
++                // Determine baseId
++                idsInGroup = config.data_groups[j].filter(function (id) {
++                    return ids.indexOf(id) >= 0;
++                });
++                if (idsInGroup.length === 0) {
++                    continue;
++                }
++                baseId = idsInGroup[0];
++                // Consider positive values
++                if (hasPositiveValue && ys[baseId]) {
++                    ys[baseId].forEach(function (v, i) {
++                        ys[baseId][i] = v > 0 ? v : 0;
++                    });
++                }
++                // Compute max
++                for (k = 1; k < idsInGroup.length; k++) {
++                    id = idsInGroup[k];
++                    if (!ys[id]) {
++                        continue;
++                    }
++                    ys[id].forEach(function (v, i) {
++                        if ($$.axis.getId(id) === $$.axis.getId(baseId) && ys[baseId] && !(hasPositiveValue && +v < 0)) {
++                            ys[baseId][i] += +v;
++                        }
++                    });
++                }
++            }
++        }
++        return $$.d3.max(Object.keys(ys).map(function (key) {
++            return $$.d3.max(ys[key]);
++        }));
++    };
++    ChartInternal.prototype.getYDomain = function (targets, axisId, xDomain) {
++        var $$ = this,
++            config = $$.config,
++            targetsByAxisId = targets.filter(function (t) {
++            return $$.axis.getId(t.id) === axisId;
++        }),
++            yTargets = xDomain ? $$.filterByXDomain(targetsByAxisId, xDomain) : targetsByAxisId,
++            yMin = axisId === 'y2' ? config.axis_y2_min : config.axis_y_min,
++            yMax = axisId === 'y2' ? config.axis_y2_max : config.axis_y_max,
++            yDomainMin = $$.getYDomainMin(yTargets),
++            yDomainMax = $$.getYDomainMax(yTargets),
++            domain,
++            domainLength,
++            padding,
++            padding_top,
++            padding_bottom,
++            center = axisId === 'y2' ? config.axis_y2_center : config.axis_y_center,
++            yDomainAbs,
++            lengths,
++            diff,
++            ratio,
++            isAllPositive,
++            isAllNegative,
++            isZeroBased = $$.hasType('bar', yTargets) && config.bar_zerobased || $$.hasType('area', yTargets) && config.area_zerobased,
++            isInverted = axisId === 'y2' ? config.axis_y2_inverted : config.axis_y_inverted,
++            showHorizontalDataLabel = $$.hasDataLabel() && config.axis_rotated,
++            showVerticalDataLabel = $$.hasDataLabel() && !config.axis_rotated;
++
++        // MEMO: avoid inverting domain unexpectedly
++        yDomainMin = isValue(yMin) ? yMin : isValue(yMax) ? yDomainMin < yMax ? yDomainMin : yMax - 10 : yDomainMin;
++        yDomainMax = isValue(yMax) ? yMax : isValue(yMin) ? yMin < yDomainMax ? yDomainMax : yMin + 10 : yDomainMax;
++
++        if (yTargets.length === 0) {
++            // use current domain if target of axisId is none
++            return axisId === 'y2' ? $$.y2.domain() : $$.y.domain();
++        }
++        if (isNaN(yDomainMin)) {
++            // set minimum to zero when not number
++            yDomainMin = 0;
++        }
++        if (isNaN(yDomainMax)) {
++            // set maximum to have same value as yDomainMin
++            yDomainMax = yDomainMin;
++        }
++        if (yDomainMin === yDomainMax) {
++            yDomainMin < 0 ? yDomainMax = 0 : yDomainMin = 0;
++        }
++        isAllPositive = yDomainMin >= 0 && yDomainMax >= 0;
++        isAllNegative = yDomainMin <= 0 && yDomainMax <= 0;
++
++        // Cancel zerobased if axis_*_min / axis_*_max specified
++        if (isValue(yMin) && isAllPositive || isValue(yMax) && isAllNegative) {
++            isZeroBased = false;
++        }
++
++        // Bar/Area chart should be 0-based if all positive|negative
++        if (isZeroBased) {
++            if (isAllPositive) {
++                yDomainMin = 0;
++            }
++            if (isAllNegative) {
++                yDomainMax = 0;
++            }
++        }
++
++        domainLength = Math.abs(yDomainMax - yDomainMin);
++        padding = padding_top = padding_bottom = domainLength * 0.1;
++
++        if (typeof center !== 'undefined') {
++            yDomainAbs = Math.max(Math.abs(yDomainMin), Math.abs(yDomainMax));
++            yDomainMax = center + yDomainAbs;
++            yDomainMin = center - yDomainAbs;
++        }
++        // add padding for data label
++        if (showHorizontalDataLabel) {
++            lengths = $$.getDataLabelLength(yDomainMin, yDomainMax, 'width');
++            diff = diffDomain($$.y.range());
++            ratio = [lengths[0] / diff, lengths[1] / diff];
++            padding_top += domainLength * (ratio[1] / (1 - ratio[0] - ratio[1]));
++            padding_bottom += domainLength * (ratio[0] / (1 - ratio[0] - ratio[1]));
++        } else if (showVerticalDataLabel) {
++            lengths = $$.getDataLabelLength(yDomainMin, yDomainMax, 'height');
++            padding_top += $$.axis.convertPixelsToAxisPadding(lengths[1], domainLength);
++            padding_bottom += $$.axis.convertPixelsToAxisPadding(lengths[0], domainLength);
++        }
++        if (axisId === 'y' && notEmpty(config.axis_y_padding)) {
++            padding_top = $$.axis.getPadding(config.axis_y_padding, 'top', padding_top, domainLength);
++            padding_bottom = $$.axis.getPadding(config.axis_y_padding, 'bottom', padding_bottom, domainLength);
++        }
++        if (axisId === 'y2' && notEmpty(config.axis_y2_padding)) {
++            padding_top = $$.axis.getPadding(config.axis_y2_padding, 'top', padding_top, domainLength);
++            padding_bottom = $$.axis.getPadding(config.axis_y2_padding, 'bottom', padding_bottom, domainLength);
++        }
++        // Bar/Area chart should be 0-based if all positive|negative
++        if (isZeroBased) {
++            if (isAllPositive) {
++                padding_bottom = yDomainMin;
++            }
++            if (isAllNegative) {
++                padding_top = -yDomainMax;
++            }
++        }
++        domain = [yDomainMin - padding_bottom, yDomainMax + padding_top];
++        return isInverted ? domain.reverse() : domain;
++    };
++    ChartInternal.prototype.getXDomainMin = function (targets) {
++        var $$ = this,
++            config = $$.config;
++        return isDefined(config.axis_x_min) ? $$.isTimeSeries() ? this.parseDate(config.axis_x_min) : config.axis_x_min : $$.d3.min(targets, function (t) {
++            return $$.d3.min(t.values, function (v) {
++                return v.x;
++            });
++        });
++    };
++    ChartInternal.prototype.getXDomainMax = function (targets) {
++        var $$ = this,
++            config = $$.config;
++        return isDefined(config.axis_x_max) ? $$.isTimeSeries() ? this.parseDate(config.axis_x_max) : config.axis_x_max : $$.d3.max(targets, function (t) {
++            return $$.d3.max(t.values, function (v) {
++                return v.x;
++            });
++        });
++    };
++    ChartInternal.prototype.getXDomainPadding = function (domain) {
++        var $$ = this,
++            config = $$.config,
++            diff = domain[1] - domain[0],
++            maxDataCount,
++            padding,
++            paddingLeft,
++            paddingRight;
++        if ($$.isCategorized()) {
++            padding = 0;
++        } else if ($$.hasType('bar')) {
++            maxDataCount = $$.getMaxDataCount();
++            padding = maxDataCount > 1 ? diff / (maxDataCount - 1) / 2 : 0.5;
++        } else {
++            padding = diff * 0.01;
++        }
++        if (_typeof(config.axis_x_padding) === 'object' && notEmpty(config.axis_x_padding)) {
++            paddingLeft = isValue(config.axis_x_padding.left) ? config.axis_x_padding.left : padding;
++            paddingRight = isValue(config.axis_x_padding.right) ? config.axis_x_padding.right : padding;
++        } else if (typeof config.axis_x_padding === 'number') {
++            paddingLeft = paddingRight = config.axis_x_padding;
++        } else {
++            paddingLeft = paddingRight = padding;
++        }
++        return { left: paddingLeft, right: paddingRight };
++    };
++    ChartInternal.prototype.getXDomain = function (targets) {
++        var $$ = this,
++            xDomain = [$$.getXDomainMin(targets), $$.getXDomainMax(targets)],
++            firstX = xDomain[0],
++            lastX = xDomain[1],
++            padding = $$.getXDomainPadding(xDomain),
++            min = 0,
++            max = 0;
++        // show center of x domain if min and max are the same
++        if (firstX - lastX === 0 && !$$.isCategorized()) {
++            if ($$.isTimeSeries()) {
++                firstX = new Date(firstX.getTime() * 0.5);
++                lastX = new Date(lastX.getTime() * 1.5);
++            } else {
++                firstX = firstX === 0 ? 1 : firstX * 0.5;
++                lastX = lastX === 0 ? -1 : lastX * 1.5;
++            }
++        }
++        if (firstX || firstX === 0) {
++            min = $$.isTimeSeries() ? new Date(firstX.getTime() - padding.left) : firstX - padding.left;
++        }
++        if (lastX || lastX === 0) {
++            max = $$.isTimeSeries() ? new Date(lastX.getTime() + padding.right) : lastX + padding.right;
++        }
++        return [min, max];
++    };
++    ChartInternal.prototype.updateXDomain = function (targets, withUpdateXDomain, withUpdateOrgXDomain, withTrim, domain) {
++        var $$ = this,
++            config = $$.config;
++
++        if (withUpdateOrgXDomain) {
++            $$.x.domain(domain ? domain : $$.d3.extent($$.getXDomain(targets)));
++            $$.orgXDomain = $$.x.domain();
++            if (config.zoom_enabled) {
++                $$.zoom.update();
++            }
++            $$.subX.domain($$.x.domain());
++            if ($$.brush) {
++                $$.brush.updateScale($$.subX);
++            }
++        }
++        if (withUpdateXDomain) {
++            $$.x.domain(domain ? domain : !$$.brush || $$.brush.empty() ? $$.orgXDomain : $$.brush.selectionAsValue());
++        }
++
++        // Trim domain when too big by zoom mousemove event
++        if (withTrim) {
++            $$.x.domain($$.trimXDomain($$.x.orgDomain()));
++        }
++
++        return $$.x.domain();
++    };
++    ChartInternal.prototype.trimXDomain = function (domain) {
++        var zoomDomain = this.getZoomDomain(),
++            min = zoomDomain[0],
++            max = zoomDomain[1];
++        if (domain[0] <= min) {
++            domain[1] = +domain[1] + (min - domain[0]);
++            domain[0] = min;
++        }
++        if (max <= domain[1]) {
++            domain[0] = +domain[0] - (domain[1] - max);
++            domain[1] = max;
++        }
++        return domain;
++    };
++
++    ChartInternal.prototype.drag = function (mouse) {
++        var $$ = this,
++            config = $$.config,
++            main = $$.main,
++            d3 = $$.d3;
++        var sx, sy, mx, my, minX, maxX, minY, maxY;
++
++        if ($$.hasArcType()) {
++            return;
++        }
++        if (!config.data_selection_enabled) {
++            return;
++        } // do nothing if not selectable
++        if (!config.data_selection_multiple) {
++            return;
++        } // skip when single selection because drag is used for multiple selection
++
++        sx = $$.dragStart[0];
++        sy = $$.dragStart[1];
++        mx = mouse[0];
++        my = mouse[1];
++        minX = Math.min(sx, mx);
++        maxX = Math.max(sx, mx);
++        minY = config.data_selection_grouped ? $$.margin.top : Math.min(sy, my);
++        maxY = config.data_selection_grouped ? $$.height : Math.max(sy, my);
++
++        main.select('.' + CLASS.dragarea).attr('x', minX).attr('y', minY).attr('width', maxX - minX).attr('height', maxY - minY);
++        // TODO: binary search when multiple xs
++        main.selectAll('.' + CLASS.shapes).selectAll('.' + CLASS.shape).filter(function (d) {
++            return config.data_selection_isselectable(d);
++        }).each(function (d, i) {
++            var shape = d3.select(this),
++                isSelected = shape.classed(CLASS.SELECTED),
++                isIncluded = shape.classed(CLASS.INCLUDED),
++                _x,
++                _y,
++                _w,
++                _h,
++                toggle,
++                isWithin = false,
++                box;
++            if (shape.classed(CLASS.circle)) {
++                _x = shape.attr("cx") * 1;
++                _y = shape.attr("cy") * 1;
++                toggle = $$.togglePoint;
++                isWithin = minX < _x && _x < maxX && minY < _y && _y < maxY;
++            } else if (shape.classed(CLASS.bar)) {
++                box = getPathBox(this);
++                _x = box.x;
++                _y = box.y;
++                _w = box.width;
++                _h = box.height;
++                toggle = $$.togglePath;
++                isWithin = !(maxX < _x || _x + _w < minX) && !(maxY < _y || _y + _h < minY);
++            } else {
++                // line/area selection not supported yet
++                return;
++            }
++            if (isWithin ^ isIncluded) {
++                shape.classed(CLASS.INCLUDED, !isIncluded);
++                // TODO: included/unincluded callback here
++                shape.classed(CLASS.SELECTED, !isSelected);
++                toggle.call($$, !isSelected, shape, d, i);
++            }
++        });
++    };
++
++    ChartInternal.prototype.dragstart = function (mouse) {
++        var $$ = this,
++            config = $$.config;
++        if ($$.hasArcType()) {
++            return;
++        }
++        if (!config.data_selection_enabled) {
++            return;
++        } // do nothing if not selectable
++        $$.dragStart = mouse;
++        $$.main.select('.' + CLASS.chart).append('rect').attr('class', CLASS.dragarea).style('opacity', 0.1);
++        $$.dragging = true;
++    };
++
++    ChartInternal.prototype.dragend = function () {
++        var $$ = this,
++            config = $$.config;
++        if ($$.hasArcType()) {
++            return;
++        }
++        if (!config.data_selection_enabled) {
++            return;
++        } // do nothing if not selectable
++        $$.main.select('.' + CLASS.dragarea).transition().duration(100).style('opacity', 0).remove();
++        $$.main.selectAll('.' + CLASS.shape).classed(CLASS.INCLUDED, false);
++        $$.dragging = false;
++    };
++
++    ChartInternal.prototype.getYFormat = function (forArc) {
++        var $$ = this,
++            formatForY = forArc && !$$.hasType('gauge') ? $$.defaultArcValueFormat : $$.yFormat,
++            formatForY2 = forArc && !$$.hasType('gauge') ? $$.defaultArcValueFormat : $$.y2Format;
++        return function (v, ratio, id) {
++            var format = $$.axis.getId(id) === 'y2' ? formatForY2 : formatForY;
++            return format.call($$, v, ratio);
++        };
++    };
++    ChartInternal.prototype.yFormat = function (v) {
++        var $$ = this,
++            config = $$.config,
++            format = config.axis_y_tick_format ? config.axis_y_tick_format : $$.defaultValueFormat;
++        return format(v);
++    };
++    ChartInternal.prototype.y2Format = function (v) {
++        var $$ = this,
++            config = $$.config,
++            format = config.axis_y2_tick_format ? config.axis_y2_tick_format : $$.defaultValueFormat;
++        return format(v);
++    };
++    ChartInternal.prototype.defaultValueFormat = function (v) {
++        return isValue(v) ? +v : "";
++    };
++    ChartInternal.prototype.defaultArcValueFormat = function (v, ratio) {
++        return (ratio * 100).toFixed(1) + '%';
++    };
++    ChartInternal.prototype.dataLabelFormat = function (targetId) {
++        var $$ = this,
++            data_labels = $$.config.data_labels,
++            format,
++            defaultFormat = function defaultFormat(v) {
++            return isValue(v) ? +v : "";
++        };
++        // find format according to axis id
++        if (typeof data_labels.format === 'function') {
++            format = data_labels.format;
++        } else if (_typeof(data_labels.format) === 'object') {
++            if (data_labels.format[targetId]) {
++                format = data_labels.format[targetId] === true ? defaultFormat : data_labels.format[targetId];
++            } else {
++                format = function format() {
++                    return '';
++                };
++            }
++        } else {
++            format = defaultFormat;
++        }
++        return format;
++    };
++
++    ChartInternal.prototype.initGrid = function () {
++        var $$ = this,
++            config = $$.config,
++            d3 = $$.d3;
++        $$.grid = $$.main.append('g').attr("clip-path", $$.clipPathForGrid).attr('class', CLASS.grid);
++        if (config.grid_x_show) {
++            $$.grid.append("g").attr("class", CLASS.xgrids);
++        }
++        if (config.grid_y_show) {
++            $$.grid.append('g').attr('class', CLASS.ygrids);
++        }
++        if (config.grid_focus_show) {
++            $$.grid.append('g').attr("class", CLASS.xgridFocus).append('line').attr('class', CLASS.xgridFocus);
++        }
++        $$.xgrid = d3.selectAll([]);
++        if (!config.grid_lines_front) {
++            $$.initGridLines();
++        }
++    };
++    ChartInternal.prototype.initGridLines = function () {
++        var $$ = this,
++            d3 = $$.d3;
++        $$.gridLines = $$.main.append('g').attr("clip-path", $$.clipPathForGrid).attr('class', CLASS.grid + ' ' + CLASS.gridLines);
++        $$.gridLines.append('g').attr("class", CLASS.xgridLines);
++        $$.gridLines.append('g').attr('class', CLASS.ygridLines);
++        $$.xgridLines = d3.selectAll([]);
++    };
++    ChartInternal.prototype.updateXGrid = function (withoutUpdate) {
++        var $$ = this,
++            config = $$.config,
++            d3 = $$.d3,
++            xgridData = $$.generateGridData(config.grid_x_type, $$.x),
++            tickOffset = $$.isCategorized() ? $$.xAxis.tickOffset() : 0;
++
++        $$.xgridAttr = config.axis_rotated ? {
++            'x1': 0,
++            'x2': $$.width,
++            'y1': function y1(d) {
++                return $$.x(d) - tickOffset;
++            },
++            'y2': function y2(d) {
++                return $$.x(d) - tickOffset;
++            }
++        } : {
++            'x1': function x1(d) {
++                return $$.x(d) + tickOffset;
++            },
++            'x2': function x2(d) {
++                return $$.x(d) + tickOffset;
++            },
++            'y1': 0,
++            'y2': $$.height
++        };
++        $$.xgridAttr.opacity = function () {
++            var pos = +d3.select(this).attr(config.axis_rotated ? 'y1' : 'x1');
++            return pos === (config.axis_rotated ? $$.height : 0) ? 0 : 1;
++        };
++
++        var xgrid = $$.main.select('.' + CLASS.xgrids).selectAll('.' + CLASS.xgrid).data(xgridData);
++        var xgridEnter = xgrid.enter().append('line').attr("class", CLASS.xgrid).attr('x1', $$.xgridAttr.x1).attr('x2', $$.xgridAttr.x2).attr('y1', $$.xgridAttr.y1).attr('y2', $$.xgridAttr.y2).style("opacity", 0);
++        $$.xgrid = xgridEnter.merge(xgrid);
++        if (!withoutUpdate) {
++            $$.xgrid.attr('x1', $$.xgridAttr.x1).attr('x2', $$.xgridAttr.x2).attr('y1', $$.xgridAttr.y1).attr('y2', $$.xgridAttr.y2).style("opacity", $$.xgridAttr.opacity);
++        }
++        xgrid.exit().remove();
++    };
++
++    ChartInternal.prototype.updateYGrid = function () {
++        var $$ = this,
++            config = $$.config,
++            gridValues = $$.yAxis.tickValues() || $$.y.ticks(config.grid_y_ticks);
++        var ygrid = $$.main.select('.' + CLASS.ygrids).selectAll('.' + CLASS.ygrid).data(gridValues);
++        var ygridEnter = ygrid.enter().append('line')
++        // TODO: x1, x2, y1, y2, opacity need to be set here maybe
++        .attr('class', CLASS.ygrid);
++        $$.ygrid = ygridEnter.merge(ygrid);
++        $$.ygrid.attr("x1", config.axis_rotated ? $$.y : 0).attr("x2", config.axis_rotated ? $$.y : $$.width).attr("y1", config.axis_rotated ? 0 : $$.y).attr("y2", config.axis_rotated ? $$.height : $$.y);
++        ygrid.exit().remove();
++        $$.smoothLines($$.ygrid, 'grid');
++    };
++
++    ChartInternal.prototype.gridTextAnchor = function (d) {
++        return d.position ? d.position : "end";
++    };
++    ChartInternal.prototype.gridTextDx = function (d) {
++        return d.position === 'start' ? 4 : d.position === 'middle' ? 0 : -4;
++    };
++    ChartInternal.prototype.xGridTextX = function (d) {
++        return d.position === 'start' ? -this.height : d.position === 'middle' ? -this.height / 2 : 0;
++    };
++    ChartInternal.prototype.yGridTextX = function (d) {
++        return d.position === 'start' ? 0 : d.position === 'middle' ? this.width / 2 : this.width;
++    };
++    ChartInternal.prototype.updateGrid = function (duration) {
++        var $$ = this,
++            main = $$.main,
++            config = $$.config,
++            xgridLine,
++            xgridLineEnter,
++            ygridLine,
++            ygridLineEnter,
++            xv = $$.xv.bind($$),
++            yv = $$.yv.bind($$),
++            xGridTextX = $$.xGridTextX.bind($$),
++            yGridTextX = $$.yGridTextX.bind($$);
++
++        // hide if arc type
++        $$.grid.style('visibility', $$.hasArcType() ? 'hidden' : 'visible');
++
++        main.select('line.' + CLASS.xgridFocus).style("visibility", "hidden");
++        if (config.grid_x_show) {
++            $$.updateXGrid();
++        }
++        xgridLine = main.select('.' + CLASS.xgridLines).selectAll('.' + CLASS.xgridLine).data(config.grid_x_lines);
++        // enter
++        xgridLineEnter = xgridLine.enter().append('g').attr("class", function (d) {
++            return CLASS.xgridLine + (d['class'] ? ' ' + d['class'] : '');
++        });
++        xgridLineEnter.append('line').attr("x1", config.axis_rotated ? 0 : xv).attr("x2", config.axis_rotated ? $$.width : xv).attr("y1", config.axis_rotated ? xv : 0).attr("y2", config.axis_rotated ? xv : $$.height).style("opacity", 0);
++        xgridLineEnter.append('text').attr("text-anchor", $$.gridTextAnchor).attr("transform", config.axis_rotated ? "" : "rotate(-90)").attr("x", config.axis_rotated ? yGridTextX : xGridTextX).attr("y", xv).attr('dx', $$.gridTextDx).attr('dy', -5).style("opacity", 0);
++        // udpate
++        $$.xgridLines = xgridLineEnter.merge(xgridLine);
++        // done in d3.transition() of the end of this function
++        // exit
++        xgridLine.exit().transition().duration(duration).style("opacity", 0).remove();
++
++        // Y-Grid
++        if (config.grid_y_show) {
++            $$.updateYGrid();
++        }
++        ygridLine = main.select('.' + CLASS.ygridLines).selectAll('.' + CLASS.ygridLine).data(config.grid_y_lines);
++        // enter
++        ygridLineEnter = ygridLine.enter().append('g').attr("class", function (d) {
++            return CLASS.ygridLine + (d['class'] ? ' ' + d['class'] : '');
++        });
++        ygridLineEnter.append('line').attr("x1", config.axis_rotated ? yv : 0).attr("x2", config.axis_rotated ? yv : $$.width).attr("y1", config.axis_rotated ? 0 : yv).attr("y2", config.axis_rotated ? $$.height : yv).style("opacity", 0);
++        ygridLineEnter.append('text').attr("text-anchor", $$.gridTextAnchor).attr("transform", config.axis_rotated ? "rotate(-90)" : "").attr("x", config.axis_rotated ? xGridTextX : yGridTextX).attr("y", yv).attr('dx', $$.gridTextDx).attr('dy', -5).style("opacity", 0);
++        // update
++        $$.ygridLines = ygridLineEnter.merge(ygridLine);
++        $$.ygridLines.select('line').transition().duration(duration).attr("x1", config.axis_rotated ? yv : 0).attr("x2", config.axis_rotated ? yv : $$.width).attr("y1", config.axis_rotated ? 0 : yv).attr("y2", config.axis_rotated ? $$.height : yv).style("opacity", 1);
++        $$.ygridLines.select('text').transition().duration(duration).attr("x", config.axis_rotated ? $$.xGridTextX.bind($$) : $$.yGridTextX.bind($$)).attr("y", yv).text(function (d) {
++            return d.text;
++        }).style("opacity", 1);
++        // exit
++        ygridLine.exit().transition().duration(duration).style("opacity", 0).remove();
++    };
++    ChartInternal.prototype.redrawGrid = function (withTransition, transition) {
++        var $$ = this,
++            config = $$.config,
++            xv = $$.xv.bind($$),
++            lines = $$.xgridLines.select('line'),
++            texts = $$.xgridLines.select('text');
++        return [(withTransition ? lines.transition(transition) : lines).attr("x1", config.axis_rotated ? 0 : xv).attr("x2", config.axis_rotated ? $$.width : xv).attr("y1", config.axis_rotated ? xv : 0).attr("y2", config.axis_rotated ? xv : $$.height).style("opacity", 1), (withTransition ? texts.transition(transition) : texts).attr("x", config.axis_rotated ? $$.yGridTextX.bind($$) : $$.xGridTextX.bind($$)).attr("y", xv).text(function (d) {
++            return d.text;
++        }).style("opacity", 1)];
++    };
++    ChartInternal.prototype.showXGridFocus = function (selectedData) {
++        var $$ = this,
++            config = $$.config,
++            dataToShow = selectedData.filter(function (d) {
++            return d && isValue(d.value);
++        }),
++            focusEl = $$.main.selectAll('line.' + CLASS.xgridFocus),
++            xx = $$.xx.bind($$);
++        if (!config.tooltip_show) {
++            return;
++        }
++        // Hide when scatter plot exists
++        if ($$.hasType('scatter') || $$.hasArcType()) {
++            return;
++        }
++        focusEl.style("visibility", "visible").data([dataToShow[0]]).attr(config.axis_rotated ? 'y1' : 'x1', xx).attr(config.axis_rotated ? 'y2' : 'x2', xx);
++        $$.smoothLines(focusEl, 'grid');
++    };
++    ChartInternal.prototype.hideXGridFocus = function () {
++        this.main.select('line.' + CLASS.xgridFocus).style("visibility", "hidden");
++    };
++    ChartInternal.prototype.updateXgridFocus = function () {
++        var $$ = this,
++            config = $$.config;
++        $$.main.select('line.' + CLASS.xgridFocus).attr("x1", config.axis_rotated ? 0 : -10).attr("x2", config.axis_rotated ? $$.width : -10).attr("y1", config.axis_rotated ? -10 : 0).attr("y2", config.axis_rotated ? -10 : $$.height);
++    };
++    ChartInternal.prototype.generateGridData = function (type, scale) {
++        var $$ = this,
++            gridData = [],
++            xDomain,
++            firstYear,
++            lastYear,
++            i,
++            tickNum = $$.main.select("." + CLASS.axisX).selectAll('.tick').size();
++        if (type === 'year') {
++            xDomain = $$.getXDomain();
++            firstYear = xDomain[0].getFullYear();
++            lastYear = xDomain[1].getFullYear();
++            for (i = firstYear; i <= lastYear; i++) {
++                gridData.push(new Date(i + '-01-01 00:00:00'));
++            }
++        } else {
++            gridData = scale.ticks(10);
++            if (gridData.length > tickNum) {
++                // use only int
++                gridData = gridData.filter(function (d) {
++                    return ("" + d).indexOf('.') < 0;
++                });
++            }
++        }
++        return gridData;
++    };
++    ChartInternal.prototype.getGridFilterToRemove = function (params) {
++        return params ? function (line) {
++            var found = false;
++            [].concat(params).forEach(function (param) {
++                if ('value' in param && line.value === param.value || 'class' in param && line['class'] === param['class']) {
++                    found = true;
++                }
++            });
++            return found;
++        } : function () {
++            return true;
++        };
++    };
++    ChartInternal.prototype.removeGridLines = function (params, forX) {
++        var $$ = this,
++            config = $$.config,
++            toRemove = $$.getGridFilterToRemove(params),
++            toShow = function toShow(line) {
++            return !toRemove(line);
++        },
++            classLines = forX ? CLASS.xgridLines : CLASS.ygridLines,
++            classLine = forX ? CLASS.xgridLine : CLASS.ygridLine;
++        $$.main.select('.' + classLines).selectAll('.' + classLine).filter(toRemove).transition().duration(config.transition_duration).style('opacity', 0).remove();
++        if (forX) {
++            config.grid_x_lines = config.grid_x_lines.filter(toShow);
++        } else {
++            config.grid_y_lines = config.grid_y_lines.filter(toShow);
++        }
++    };
++
++    ChartInternal.prototype.initEventRect = function () {
++        var $$ = this,
++            config = $$.config;
++
++        $$.main.select('.' + CLASS.chart).append("g").attr("class", CLASS.eventRects).style('fill-opacity', 0);
++        $$.eventRect = $$.main.select('.' + CLASS.eventRects).append('rect').attr('class', CLASS.eventRect);
++
++        // event rect handle zoom event as well
++        if (config.zoom_enabled && $$.zoom) {
++            $$.eventRect.call($$.zoom).on("dblclick.zoom", null);
++            if (config.zoom_initialRange) {
++                // WORKAROUND: Add transition to apply transform immediately when no subchart
++                $$.eventRect.transition().duration(0).call($$.zoom.transform, $$.zoomTransform(config.zoom_initialRange));
++            }
++        }
++    };
++    ChartInternal.prototype.redrawEventRect = function () {
++        var $$ = this,
++            d3 = $$.d3,
++            config = $$.config,
++            x,
++            y,
++            w,
++            h;
++
++        // TODO: rotated not supported yet
++        x = 0;
++        y = 0;
++        w = $$.width;
++        h = $$.height;
++
++        function mouseout() {
++            $$.svg.select('.' + CLASS.eventRect).style('cursor', null);
++            $$.hideXGridFocus();
++            $$.hideTooltip();
++            $$.unexpandCircles();
++            $$.unexpandBars();
++        }
++
++        // rects for mouseover
++        $$.main.select('.' + CLASS.eventRects).style('cursor', config.zoom_enabled ? config.axis_rotated ? 'ns-resize' : 'ew-resize' : null);
++
++        $$.eventRect.attr('x', x).attr('y', y).attr('width', w).attr('height', h).on('mouseout', config.interaction_enabled ? function () {
++            if (!config) {
++                return;
++            } // chart is destroyed
++            if ($$.hasArcType()) {
++                return;
++            }
++            mouseout();
++        } : null).on('mousemove', config.interaction_enabled ? function () {
++            var targetsToShow, mouse, closest, sameXData, selectedData;
++
++            if ($$.dragging) {
++                return;
++            } // do nothing when dragging
++            if ($$.hasArcType(targetsToShow)) {
++                return;
++            }
++
++            targetsToShow = $$.filterTargetsToShow($$.data.targets);
++            mouse = d3.mouse(this);
++            closest = $$.findClosestFromTargets(targetsToShow, mouse);
++
++            if ($$.mouseover && (!closest || closest.id !== $$.mouseover.id)) {
++                config.data_onmouseout.call($$.api, $$.mouseover);
++                $$.mouseover = undefined;
++            }
++
++            if (!closest) {
++                mouseout();
++                return;
++            }
++
++            if ($$.isScatterType(closest) || !config.tooltip_grouped) {
++                sameXData = [closest];
++            } else {
++                sameXData = $$.filterByX(targetsToShow, closest.x);
++            }
++
++            // show tooltip when cursor is close to some point
++            selectedData = sameXData.map(function (d) {
++                return $$.addName(d);
++            });
++            $$.showTooltip(selectedData, this);
++
++            // expand points
++            if (config.point_focus_expand_enabled) {
++                $$.unexpandCircles();
++                selectedData.forEach(function (d) {
++                    $$.expandCircles(d.index, d.id, false);
++                });
++            }
++            $$.expandBars(closest.index, closest.id, true);
++
++            // Show xgrid focus line
++            $$.showXGridFocus(selectedData);
++
++            // Show cursor as pointer if point is close to mouse position
++            if ($$.isBarType(closest.id) || $$.dist(closest, mouse) < config.point_sensitivity) {
++                $$.svg.select('.' + CLASS.eventRect).style('cursor', 'pointer');
++                if (!$$.mouseover) {
++                    config.data_onmouseover.call($$.api, closest);
++                    $$.mouseover = closest;
++                }
++            }
++        } : null).on('click', config.interaction_enabled ? function () {
++            var targetsToShow, mouse, closest, sameXData;
++            if ($$.hasArcType(targetsToShow)) {
++                return;
++            }
++
++            targetsToShow = $$.filterTargetsToShow($$.data.targets);
++            mouse = d3.mouse(this);
++            closest = $$.findClosestFromTargets(targetsToShow, mouse);
++            if (!closest) {
++                return;
++            }
++            // select if selection enabled
++            if ($$.isBarType(closest.id) || $$.dist(closest, mouse) < config.point_sensitivity) {
++                if ($$.isScatterType(closest) || !config.data_selection_grouped) {
++                    sameXData = [closest];
++                } else {
++                    sameXData = $$.filterByX(targetsToShow, closest.x);
++                }
++                sameXData.forEach(function (d) {
++                    $$.main.selectAll('.' + CLASS.shapes + $$.getTargetSelectorSuffix(d.id)).selectAll('.' + CLASS.shape + '-' + d.index).each(function () {
++                        if (config.data_selection_grouped || $$.isWithinShape(this, d)) {
++                            $$.toggleShape(this, d, d.index);
++                            config.data_onclick.call($$.api, d, this);
++                        }
++                    });
++                });
++            }
++        } : null).call(config.interaction_enabled && config.data_selection_draggable && $$.drag ? d3.drag().on('drag', function () {
++            $$.drag(d3.mouse(this));
++        }).on('start', function () {
++            $$.dragstart(d3.mouse(this));
++        }).on('end', function () {
++            $$.dragend();
++        }) : function () {});
++    };
++    ChartInternal.prototype.getMousePosition = function (data) {
++        var $$ = this;
++        return [$$.x(data.x), $$.getYScale(data.id)(data.value)];
++    };
++    ChartInternal.prototype.dispatchEvent = function (type, mouse) {
++        var $$ = this,
++            selector = '.' + CLASS.eventRect,
++            eventRect = $$.main.select(selector).node(),
++            box = eventRect.getBoundingClientRect(),
++            x = box.left + (mouse ? mouse[0] : 0),
++            y = box.top + (mouse ? mouse[1] : 0),
++            event = document.createEvent("MouseEvents");
++
++        event.initMouseEvent(type, true, true, window, 0, x, y, x, y, false, false, false, false, 0, null);
++        eventRect.dispatchEvent(event);
++    };
++
++    ChartInternal.prototype.initLegend = function () {
++        var $$ = this;
++        $$.legendItemTextBox = {};
++        $$.legendHasRendered = false;
++        $$.legend = $$.svg.append("g").attr("transform", $$.getTranslate('legend'));
++        if (!$$.config.legend_show) {
++            $$.legend.style('visibility', 'hidden');
++            $$.hiddenLegendIds = $$.mapToIds($$.data.targets);
++            return;
++        }
++        // MEMO: call here to update legend box and tranlate for all
++        // MEMO: translate will be upated by this, so transform not needed in updateLegend()
++        $$.updateLegendWithDefaults();
++    };
++    ChartInternal.prototype.updateLegendWithDefaults = function () {
++        var $$ = this;
++        $$.updateLegend($$.mapToIds($$.data.targets), { withTransform: false, withTransitionForTransform: false, withTransition: false });
++    };
++    ChartInternal.prototype.updateSizeForLegend = function (legendHeight, legendWidth) {
++        var $$ = this,
++            config = $$.config,
++            insetLegendPosition = {
++            top: $$.isLegendTop ? $$.getCurrentPaddingTop() + config.legend_inset_y + 5.5 : $$.currentHeight - legendHeight - $$.getCurrentPaddingBottom() - config.legend_inset_y,
++            left: $$.isLegendLeft ? $$.getCurrentPaddingLeft() + config.legend_inset_x + 0.5 : $$.currentWidth - legendWidth - $$.getCurrentPaddingRight() - config.legend_inset_x + 0.5
++        };
++
++        $$.margin3 = {
++            top: $$.isLegendRight ? 0 : $$.isLegendInset ? insetLegendPosition.top : $$.currentHeight - legendHeight,
++            right: NaN,
++            bottom: 0,
++            left: $$.isLegendRight ? $$.currentWidth - legendWidth : $$.isLegendInset ? insetLegendPosition.left : 0
++        };
++    };
++    ChartInternal.prototype.transformLegend = function (withTransition) {
++        var $$ = this;
++        (withTransition ? $$.legend.transition() : $$.legend).attr("transform", $$.getTranslate('legend'));
++    };
++    ChartInternal.prototype.updateLegendStep = function (step) {
++        this.legendStep = step;
++    };
++    ChartInternal.prototype.updateLegendItemWidth = function (w) {
++        this.legendItemWidth = w;
++    };
++    ChartInternal.prototype.updateLegendItemHeight = function (h) {
++        this.legendItemHeight = h;
++    };
++    ChartInternal.prototype.getLegendWidth = function () {
++        var $$ = this;
++        return $$.config.legend_show ? $$.isLegendRight || $$.isLegendInset ? $$.legendItemWidth * ($$.legendStep + 1) : $$.currentWidth : 0;
++    };
++    ChartInternal.prototype.getLegendHeight = function () {
++        var $$ = this,
++            h = 0;
++        if ($$.config.legend_show) {
++            if ($$.isLegendRight) {
++                h = $$.currentHeight;
++            } else {
++                h = Math.max(20, $$.legendItemHeight) * ($$.legendStep + 1);
++            }
++        }
++        return h;
++    };
++    ChartInternal.prototype.opacityForLegend = function (legendItem) {
++        return legendItem.classed(CLASS.legendItemHidden) ? null : 1;
++    };
++    ChartInternal.prototype.opacityForUnfocusedLegend = function (legendItem) {
++        return legendItem.classed(CLASS.legendItemHidden) ? null : 0.3;
++    };
++    ChartInternal.prototype.toggleFocusLegend = function (targetIds, focus) {
++        var $$ = this;
++        targetIds = $$.mapToTargetIds(targetIds);
++        $$.legend.selectAll('.' + CLASS.legendItem).filter(function (id) {
++            return targetIds.indexOf(id) >= 0;
++        }).classed(CLASS.legendItemFocused, focus).transition().duration(100).style('opacity', function () {
++            var opacity = focus ? $$.opacityForLegend : $$.opacityForUnfocusedLegend;
++            return opacity.call($$, $$.d3.select(this));
++        });
++    };
++    ChartInternal.prototype.revertLegend = function () {
++        var $$ = this,
++            d3 = $$.d3;
++        $$.legend.selectAll('.' + CLASS.legendItem).classed(CLASS.legendItemFocused, false).transition().duration(100).style('opacity', function () {
++            return $$.opacityForLegend(d3.select(this));
++        });
++    };
++    ChartInternal.prototype.showLegend = function (targetIds) {
++        var $$ = this,
++            config = $$.config;
++        if (!config.legend_show) {
++            config.legend_show = true;
++            $$.legend.style('visibility', 'visible');
++            if (!$$.legendHasRendered) {
++                $$.updateLegendWithDefaults();
++            }
++        }
++        $$.removeHiddenLegendIds(targetIds);
++        $$.legend.selectAll($$.selectorLegends(targetIds)).style('visibility', 'visible').transition().style('opacity', function () {
++            return $$.opacityForLegend($$.d3.select(this));
++        });
++    };
++    ChartInternal.prototype.hideLegend = function (targetIds) {
++        var $$ = this,
++            config = $$.config;
++        if (config.legend_show && isEmpty(targetIds)) {
++            config.legend_show = false;
++            $$.legend.style('visibility', 'hidden');
++        }
++        $$.addHiddenLegendIds(targetIds);
++        $$.legend.selectAll($$.selectorLegends(targetIds)).style('opacity', 0).style('visibility', 'hidden');
++    };
++    ChartInternal.prototype.clearLegendItemTextBoxCache = function () {
++        this.legendItemTextBox = {};
++    };
++    ChartInternal.prototype.updateLegend = function (targetIds, options, transitions) {
++        var $$ = this,
++            config = $$.config;
++        var xForLegend, xForLegendText, xForLegendRect, yForLegend, yForLegendText, yForLegendRect, x1ForLegendTile, x2ForLegendTile, yForLegendTile;
++        var paddingTop = 4,
++            paddingRight = 10,
++            maxWidth = 0,
++            maxHeight = 0,
++            posMin = 10,
++            tileWidth = config.legend_item_tile_width + 5;
++        var l,
++            totalLength = 0,
++            offsets = {},
++            widths = {},
++            heights = {},
++            margins = [0],
++            steps = {},
++            step = 0;
++        var withTransition, withTransitionForTransform;
++        var texts, rects, tiles, background;
++
++        // Skip elements when their name is set to null
++        targetIds = targetIds.filter(function (id) {
++            return !isDefined(config.data_names[id]) || config.data_names[id] !== null;
++        });
++
++        options = options || {};
++        withTransition = getOption(options, "withTransition", true);
++        withTransitionForTransform = getOption(options, "withTransitionForTransform", true);
++
++        function getTextBox(textElement, id) {
++            if (!$$.legendItemTextBox[id]) {
++                $$.legendItemTextBox[id] = $$.getTextRect(textElement.textContent, CLASS.legendItem, textElement);
++            }
++            return $$.legendItemTextBox[id];
++        }
++
++        function updatePositions(textElement, id, index) {
++            var reset = index === 0,
++                isLast = index === targetIds.length - 1,
++                box = getTextBox(textElement, id),
++                itemWidth = box.width + tileWidth + (isLast && !($$.isLegendRight || $$.isLegendInset) ? 0 : paddingRight) + config.legend_padding,
++                itemHeight = box.height + paddingTop,
++                itemLength = $$.isLegendRight || $$.isLegendInset ? itemHeight : itemWidth,
++                areaLength = $$.isLegendRight || $$.isLegendInset ? $$.getLegendHeight() : $$.getLegendWidth(),
++                margin,
++                maxLength;
++
++            // MEMO: care about condifion of step, totalLength
++            function updateValues(id, withoutStep) {
++                if (!withoutStep) {
++                    margin = (areaLength - totalLength - itemLength) / 2;
++                    if (margin < posMin) {
++                        margin = (areaLength - itemLength) / 2;
++                        totalLength = 0;
++                        step++;
++                    }
++                }
++                steps[id] = step;
++                margins[step] = $$.isLegendInset ? 10 : margin;
++                offsets[id] = totalLength;
++                totalLength += itemLength;
++            }
++
++            if (reset) {
++                totalLength = 0;
++                step = 0;
++                maxWidth = 0;
++                maxHeight = 0;
++            }
++
++            if (config.legend_show && !$$.isLegendToShow(id)) {
++                widths[id] = heights[id] = steps[id] = offsets[id] = 0;
++                return;
++            }
++
++            widths[id] = itemWidth;
++            heights[id] = itemHeight;
++
++            if (!maxWidth || itemWidth >= maxWidth) {
++                maxWidth = itemWidth;
++            }
++            if (!maxHeight || itemHeight >= maxHeight) {
++                maxHeight = itemHeight;
++            }
++            maxLength = $$.isLegendRight || $$.isLegendInset ? maxHeight : maxWidth;
++
++            if (config.legend_equally) {
++                Object.keys(widths).forEach(function (id) {
++                    widths[id] = maxWidth;
++                });
++                Object.keys(heights).forEach(function (id) {
++                    heights[id] = maxHeight;
++                });
++                margin = (areaLength - maxLength * targetIds.length) / 2;
++                if (margin < posMin) {
++                    totalLength = 0;
++                    step = 0;
++                    targetIds.forEach(function (id) {
++                        updateValues(id);
++                    });
++                } else {
++                    updateValues(id, true);
++                }
++            } else {
++                updateValues(id);
++            }
++        }
++
++        if ($$.isLegendInset) {
++            step = config.legend_inset_step ? config.legend_inset_step : targetIds.length;
++            $$.updateLegendStep(step);
++        }
++
++        if ($$.isLegendRight) {
++            xForLegend = function xForLegend(id) {
++                return maxWidth * steps[id];
++            };
++            yForLegend = function yForLegend(id) {
++                return margins[steps[id]] + offsets[id];
++            };
++        } else if ($$.isLegendInset) {
++            xForLegend = function xForLegend(id) {
++                return maxWidth * steps[id] + 10;
++            };
++            yForLegend = function yForLegend(id) {
++                return margins[steps[id]] + offsets[id];
++            };
++        } else {
++            xForLegend = function xForLegend(id) {
++                return margins[steps[id]] + offsets[id];
++            };
++            yForLegend = function yForLegend(id) {
++                return maxHeight * steps[id];
++            };
++        }
++        xForLegendText = function xForLegendText(id, i) {
++            return xForLegend(id, i) + 4 + config.legend_item_tile_width;
++        };
++        yForLegendText = function yForLegendText(id, i) {
++            return yForLegend(id, i) + 9;
++        };
++        xForLegendRect = function xForLegendRect(id, i) {
++            return xForLegend(id, i);
++        };
++        yForLegendRect = function yForLegendRect(id, i) {
++            return yForLegend(id, i) - 5;
++        };
++        x1ForLegendTile = function x1ForLegendTile(id, i) {
++            return xForLegend(id, i) - 2;
++        };
++        x2ForLegendTile = function x2ForLegendTile(id, i) {
++            return xForLegend(id, i) - 2 + config.legend_item_tile_width;
++        };
++        yForLegendTile = function yForLegendTile(id, i) {
++            return yForLegend(id, i) + 4;
++        };
++
++        // Define g for legend area
++        l = $$.legend.selectAll('.' + CLASS.legendItem).data(targetIds).enter().append('g').attr('class', function (id) {
++            return $$.generateClass(CLASS.legendItem, id);
++        }).style('visibility', function (id) {
++            return $$.isLegendToShow(id) ? 'visible' : 'hidden';
++        }).style('cursor', 'pointer').on('click', function (id) {
++            if (config.legend_item_onclick) {
++                config.legend_item_onclick.call($$, id);
++            } else {
++                if ($$.d3.event.altKey) {
++                    $$.api.hide();
++                    $$.api.show(id);
++                } else {
++                    $$.api.toggle(id);
++                    $$.isTargetToShow(id) ? $$.api.focus(id) : $$.api.revert();
++                }
++            }
++        }).on('mouseover', function (id) {
++            if (config.legend_item_onmouseover) {
++                config.legend_item_onmouseover.call($$, id);
++            } else {
++                $$.d3.select(this).classed(CLASS.legendItemFocused, true);
++                if (!$$.transiting && $$.isTargetToShow(id)) {
++                    $$.api.focus(id);
++                }
++            }
++        }).on('mouseout', function (id) {
++            if (config.legend_item_onmouseout) {
++                config.legend_item_onmouseout.call($$, id);
++            } else {
++                $$.d3.select(this).classed(CLASS.legendItemFocused, false);
++                $$.api.revert();
++            }
++        });
++        l.append('text').text(function (id) {
++            return isDefined(config.data_names[id]) ? config.data_names[id] : id;
++        }).each(function (id, i) {
++            updatePositions(this, id, i);
++        }).style("pointer-events", "none").attr('x', $$.isLegendRight || $$.isLegendInset ? xForLegendText : -200).attr('y', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendText);
++        l.append('rect').attr("class", CLASS.legendItemEvent).style('fill-opacity', 0).attr('x', $$.isLegendRight || $$.isLegendInset ? xForLegendRect : -200).attr('y', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendRect);
++        l.append('line').attr('class', CLASS.legendItemTile).style('stroke', $$.color).style("pointer-events", "none").attr('x1', $$.isLegendRight || $$.isLegendInset ? x1ForLegendTile : -200).attr('y1', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendTile).attr('x2', $$.isLegendRight || $$.isLegendInset ? x2ForLegendTile : -200).attr('y2', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendTile).attr('stroke-width', config.legend_item_tile_height);
++
++        // Set background for inset legend
++        background = $$.legend.select('.' + CLASS.legendBackground + ' rect');
++        if ($$.isLegendInset && maxWidth > 0 && background.size() === 0) {
++            background = $$.legend.insert('g', '.' + CLASS.legendItem).attr("class", CLASS.legendBackground).append('rect');
++        }
++
++        texts = $$.legend.selectAll('text').data(targetIds).text(function (id) {
++            return isDefined(config.data_names[id]) ? config.data_names[id] : id;
++        }) // MEMO: needed for update
++        .each(function (id, i) {
++            updatePositions(this, id, i);
++        });
++        (withTransition ? texts.transition() : texts).attr('x', xForLegendText).attr('y', yForLegendText);
++
++        rects = $$.legend.selectAll('rect.' + CLASS.legendItemEvent).data(targetIds);
++        (withTransition ? rects.transition() : rects).attr('width', function (id) {
++            return widths[id];
++        }).attr('height', function (id) {
++            return heights[id];
++        }).attr('x', xForLegendRect).attr('y', yForLegendRect);
++
++        tiles = $$.legend.selectAll('line.' + CLASS.legendItemTile).data(targetIds);
++        (withTransition ? tiles.transition() : tiles).style('stroke', $$.levelColor ? function (id) {
++            return $$.levelColor($$.cache[id].values[0].value);
++        } : $$.color).attr('x1', x1ForLegendTile).attr('y1', yForLegendTile).attr('x2', x2ForLegendTile).attr('y2', yForLegendTile);
++
++        if (background) {
++            (withTransition ? background.transition() : background).attr('height', $$.getLegendHeight() - 12).attr('width', maxWidth * (step + 1) + 10);
++        }
++
++        // toggle legend state
++        $$.legend.selectAll('.' + CLASS.legendItem).classed(CLASS.legendItemHidden, function (id) {
++            return !$$.isTargetToShow(id);
++        });
++
++        // Update all to reflect change of legend
++        $$.updateLegendItemWidth(maxWidth);
++        $$.updateLegendItemHeight(maxHeight);
++        $$.updateLegendStep(step);
++        // Update size and scale
++        $$.updateSizes();
++        $$.updateScales();
++        $$.updateSvgSize();
++        // Update g positions
++        $$.transformAll(withTransitionForTransform, transitions);
++        $$.legendHasRendered = true;
++    };
++
++    ChartInternal.prototype.initRegion = function () {
++        var $$ = this;
++        $$.region = $$.main.append('g').attr("clip-path", $$.clipPath).attr("class", CLASS.regions);
++    };
++    ChartInternal.prototype.updateRegion = function (duration) {
++        var $$ = this,
++            config = $$.config;
++
++        // hide if arc type
++        $$.region.style('visibility', $$.hasArcType() ? 'hidden' : 'visible');
++
++        var mainRegion = $$.main.select('.' + CLASS.regions).selectAll('.' + CLASS.region).data(config.regions);
++        var mainRegionEnter = mainRegion.enter().append('rect').attr("x", $$.regionX.bind($$)).attr("y", $$.regionY.bind($$)).attr("width", $$.regionWidth.bind($$)).attr("height", $$.regionHeight.bind($$)).style("fill-opacity", 0);
++        $$.mainRegion = mainRegionEnter.merge(mainRegion).attr('class', $$.classRegion.bind($$));
++        mainRegion.exit().transition().duration(duration).style("opacity", 0).remove();
++    };
++    ChartInternal.prototype.redrawRegion = function (withTransition, transition) {
++        var $$ = this,
++            regions = $$.mainRegion;
++        return [(withTransition ? regions.transition(transition) : regions).attr("x", $$.regionX.bind($$)).attr("y", $$.regionY.bind($$)).attr("width", $$.regionWidth.bind($$)).attr("height", $$.regionHeight.bind($$)).style("fill-opacity", function (d) {
++            return isValue(d.opacity) ? d.opacity : 0.1;
++        })];
++    };
++    ChartInternal.prototype.regionX = function (d) {
++        var $$ = this,
++            config = $$.config,
++            xPos,
++            yScale = d.axis === 'y' ? $$.y : $$.y2;
++        if (d.axis === 'y' || d.axis === 'y2') {
++            xPos = config.axis_rotated ? 'start' in d ? yScale(d.start) : 0 : 0;
++        } else {
++            xPos = config.axis_rotated ? 0 : 'start' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.start) : d.start) : 0;
++        }
++        return xPos;
++    };
++    ChartInternal.prototype.regionY = function (d) {
++        var $$ = this,
++            config = $$.config,
++            yPos,
++            yScale = d.axis === 'y' ? $$.y : $$.y2;
++        if (d.axis === 'y' || d.axis === 'y2') {
++            yPos = config.axis_rotated ? 0 : 'end' in d ? yScale(d.end) : 0;
++        } else {
++            yPos = config.axis_rotated ? 'start' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.start) : d.start) : 0 : 0;
++        }
++        return yPos;
++    };
++    ChartInternal.prototype.regionWidth = function (d) {
++        var $$ = this,
++            config = $$.config,
++            start = $$.regionX(d),
++            end,
++            yScale = d.axis === 'y' ? $$.y : $$.y2;
++        if (d.axis === 'y' || d.axis === 'y2') {
++            end = config.axis_rotated ? 'end' in d ? yScale(d.end) : $$.width : $$.width;
++        } else {
++            end = config.axis_rotated ? $$.width : 'end' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.end) : d.end) : $$.width;
++        }
++        return end < start ? 0 : end - start;
++    };
++    ChartInternal.prototype.regionHeight = function (d) {
++        var $$ = this,
++            config = $$.config,
++            start = this.regionY(d),
++            end,
++            yScale = d.axis === 'y' ? $$.y : $$.y2;
++        if (d.axis === 'y' || d.axis === 'y2') {
++            end = config.axis_rotated ? $$.height : 'start' in d ? yScale(d.start) : $$.height;
++        } else {
++            end = config.axis_rotated ? 'end' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.end) : d.end) : $$.height : $$.height;
++        }
++        return end < start ? 0 : end - start;
++    };
++    ChartInternal.prototype.isRegionOnX = function (d) {
++        return !d.axis || d.axis === 'x';
++    };
++
++    ChartInternal.prototype.getScale = function (min, max, forTimeseries) {
++        return (forTimeseries ? this.d3.scaleTime() : this.d3.scaleLinear()).range([min, max]);
++    };
++    ChartInternal.prototype.getX = function (min, max, domain, offset) {
++        var $$ = this,
++            scale = $$.getScale(min, max, $$.isTimeSeries()),
++            _scale = domain ? scale.domain(domain) : scale,
++            key;
++        // Define customized scale if categorized axis
++        if ($$.isCategorized()) {
++            offset = offset || function () {
++                return 0;
++            };
++            scale = function scale(d, raw) {
++                var v = _scale(d) + offset(d);
++                return raw ? v : Math.ceil(v);
++            };
++        } else {
++            scale = function scale(d, raw) {
++                var v = _scale(d);
++                return raw ? v : Math.ceil(v);
++            };
++        }
++        // define functions
++        for (key in _scale) {
++            scale[key] = _scale[key];
++        }
++        scale.orgDomain = function () {
++            return _scale.domain();
++        };
++        // define custom domain() for categorized axis
++        if ($$.isCategorized()) {
++            scale.domain = function (domain) {
++                if (!arguments.length) {
++                    domain = this.orgDomain();
++                    return [domain[0], domain[1] + 1];
++                }
++                _scale.domain(domain);
++                return scale;
++            };
++        }
++        return scale;
++    };
++    ChartInternal.prototype.getY = function (min, max, domain) {
++        var scale = this.getScale(min, max, this.isTimeSeriesY());
++        if (domain) {
++            scale.domain(domain);
++        }
++        return scale;
++    };
++    ChartInternal.prototype.getYScale = function (id) {
++        return this.axis.getId(id) === 'y2' ? this.y2 : this.y;
++    };
++    ChartInternal.prototype.getSubYScale = function (id) {
++        return this.axis.getId(id) === 'y2' ? this.subY2 : this.subY;
++    };
++    ChartInternal.prototype.updateScales = function () {
++        var $$ = this,
++            config = $$.config,
++            forInit = !$$.x;
++        // update edges
++        $$.xMin = config.axis_rotated ? 1 : 0;
++        $$.xMax = config.axis_rotated ? $$.height : $$.width;
++        $$.yMin = config.axis_rotated ? 0 : $$.height;
++        $$.yMax = config.axis_rotated ? $$.width : 1;
++        $$.subXMin = $$.xMin;
++        $$.subXMax = $$.xMax;
++        $$.subYMin = config.axis_rotated ? 0 : $$.height2;
++        $$.subYMax = config.axis_rotated ? $$.width2 : 1;
++        // update scales
++        $$.x = $$.getX($$.xMin, $$.xMax, forInit ? undefined : $$.x.orgDomain(), function () {
++            return $$.xAxis.tickOffset();
++        });
++        $$.y = $$.getY($$.yMin, $$.yMax, forInit ? config.axis_y_default : $$.y.domain());
++        $$.y2 = $$.getY($$.yMin, $$.yMax, forInit ? config.axis_y2_default : $$.y2.domain());
++        $$.subX = $$.getX($$.xMin, $$.xMax, $$.orgXDomain, function (d) {
++            return d % 1 ? 0 : $$.subXAxis.tickOffset();
++        });
++        $$.subY = $$.getY($$.subYMin, $$.subYMax, forInit ? config.axis_y_default : $$.subY.domain());
++        $$.subY2 = $$.getY($$.subYMin, $$.subYMax, forInit ? config.axis_y2_default : $$.subY2.domain());
++        // update axes
++        $$.xAxisTickFormat = $$.axis.getXAxisTickFormat();
++        $$.xAxisTickValues = $$.axis.getXAxisTickValues();
++        $$.yAxisTickValues = $$.axis.getYAxisTickValues();
++        $$.y2AxisTickValues = $$.axis.getY2AxisTickValues();
++
++        $$.xAxis = $$.axis.getXAxis($$.x, $$.xOrient, $$.xAxisTickFormat, $$.xAxisTickValues, config.axis_x_tick_outer);
++        $$.subXAxis = $$.axis.getXAxis($$.subX, $$.subXOrient, $$.xAxisTickFormat, $$.xAxisTickValues, config.axis_x_tick_outer);
++        $$.yAxis = $$.axis.getYAxis($$.y, $$.yOrient, config.axis_y_tick_format, $$.yAxisTickValues, config.axis_y_tick_outer);
++        $$.y2Axis = $$.axis.getYAxis($$.y2, $$.y2Orient, config.axis_y2_tick_format, $$.y2AxisTickValues, config.axis_y2_tick_outer);
++
++        // Set initialized scales to brush and zoom
++        if (!forInit) {
++            if ($$.brush) {
++                $$.brush.updateScale($$.subX);
++            }
++        }
++        // update for arc
++        if ($$.updateArc) {
++            $$.updateArc();
++        }
++    };
++
++    ChartInternal.prototype.selectPoint = function (target, d, i) {
++        var $$ = this,
++            config = $$.config,
++            cx = (config.axis_rotated ? $$.circleY : $$.circleX).bind($$),
++            cy = (config.axis_rotated ? $$.circleX : $$.circleY).bind($$),
++            r = $$.pointSelectR.bind($$);
++        config.data_onselected.call($$.api, d, target.node());
++        // add selected-circle on low layer g
++        $$.main.select('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(d.id)).selectAll('.' + CLASS.selectedCircle + '-' + i).data([d]).enter().append('circle').attr("class", function () {
++            return $$.generateClass(CLASS.selectedCircle, i);
++        }).attr("cx", cx).attr("cy", cy).attr("stroke", function () {
++            return $$.color(d);
++        }).attr("r", function (d) {
++            return $$.pointSelectR(d) * 1.4;
++        }).transition().duration(100).attr("r", r);
++    };
++    ChartInternal.prototype.unselectPoint = function (target, d, i) {
++        var $$ = this;
++        $$.config.data_onunselected.call($$.api, d, target.node());
++        // remove selected-circle from low layer g
++        $$.main.select('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(d.id)).selectAll('.' + CLASS.selectedCircle + '-' + i).transition().duration(100).attr('r', 0).remove();
++    };
++    ChartInternal.prototype.togglePoint = function (selected, target, d, i) {
++        selected ? this.selectPoint(target, d, i) : this.unselectPoint(target, d, i);
++    };
++    ChartInternal.prototype.selectPath = function (target, d) {
++        var $$ = this;
++        $$.config.data_onselected.call($$, d, target.node());
++        if ($$.config.interaction_brighten) {
++            target.transition().duration(100).style("fill", function () {
++                return $$.d3.rgb($$.color(d)).brighter(0.75);
++            });
++        }
++    };
++    ChartInternal.prototype.unselectPath = function (target, d) {
++        var $$ = this;
++        $$.config.data_onunselected.call($$, d, target.node());
++        if ($$.config.interaction_brighten) {
++            target.transition().duration(100).style("fill", function () {
++                return $$.color(d);
++            });
++        }
++    };
++    ChartInternal.prototype.togglePath = function (selected, target, d, i) {
++        selected ? this.selectPath(target, d, i) : this.unselectPath(target, d, i);
++    };
++    ChartInternal.prototype.getToggle = function (that, d) {
++        var $$ = this,
++            toggle;
++        if (that.nodeName === 'circle') {
++            if ($$.isStepType(d)) {
++                // circle is hidden in step chart, so treat as within the click area
++                toggle = function toggle() {}; // TODO: how to select step chart?
++            } else {
++                toggle = $$.togglePoint;
++            }
++        } else if (that.nodeName === 'path') {
++            toggle = $$.togglePath;
++        }
++        return toggle;
++    };
++    ChartInternal.prototype.toggleShape = function (that, d, i) {
++        var $$ = this,
++            d3 = $$.d3,
++            config = $$.config,
++            shape = d3.select(that),
++            isSelected = shape.classed(CLASS.SELECTED),
++            toggle = $$.getToggle(that, d).bind($$);
++
++        if (config.data_selection_enabled && config.data_selection_isselectable(d)) {
++            if (!config.data_selection_multiple) {
++                $$.main.selectAll('.' + CLASS.shapes + (config.data_selection_grouped ? $$.getTargetSelectorSuffix(d.id) : "")).selectAll('.' + CLASS.shape).each(function (d, i) {
++                    var shape = d3.select(this);
++                    if (shape.classed(CLASS.SELECTED)) {
++                        toggle(false, shape.classed(CLASS.SELECTED, false), d, i);
++                    }
++                });
++            }
++            shape.classed(CLASS.SELECTED, !isSelected);
++            toggle(!isSelected, shape, d, i);
++        }
++    };
++
++    ChartInternal.prototype.initBar = function () {
++        var $$ = this;
++        $$.main.select('.' + CLASS.chart).append("g").attr("class", CLASS.chartBars);
++    };
++    ChartInternal.prototype.updateTargetsForBar = function (targets) {
++        var $$ = this,
++            config = $$.config,
++            mainBars,
++            mainBarEnter,
++            classChartBar = $$.classChartBar.bind($$),
++            classBars = $$.classBars.bind($$),
++            classFocus = $$.classFocus.bind($$);
++        mainBars = $$.main.select('.' + CLASS.chartBars).selectAll('.' + CLASS.chartBar).data(targets).attr('class', function (d) {
++            return classChartBar(d) + classFocus(d);
++        });
++        mainBarEnter = mainBars.enter().append('g').attr('class', classChartBar).style("pointer-events", "none");
++        // Bars for each data
++        mainBarEnter.append('g').attr("class", classBars).style("cursor", function (d) {
++            return config.data_selection_isselectable(d) ? "pointer" : null;
++        });
++    };
++    ChartInternal.prototype.updateBar = function (durationForExit) {
++        var $$ = this,
++            barData = $$.barData.bind($$),
++            classBar = $$.classBar.bind($$),
++            initialOpacity = $$.initialOpacity.bind($$),
++            color = function color(d) {
++            return $$.color(d.id);
++        };
++        var mainBar = $$.main.selectAll('.' + CLASS.bars).selectAll('.' + CLASS.bar).data(barData);
++        var mainBarEnter = mainBar.enter().append('path').attr("class", classBar).style("stroke", color).style("fill", color);
++        $$.mainBar = mainBarEnter.merge(mainBar).style("opacity", initialOpacity);
++        mainBar.exit().transition().duration(durationForExit).style("opacity", 0);
++    };
++    ChartInternal.prototype.redrawBar = function (drawBar, withTransition, transition) {
++        return [(withTransition ? this.mainBar.transition(transition) : this.mainBar).attr('d', drawBar).style("stroke", this.color).style("fill", this.color).style("opacity", 1)];
++    };
++    ChartInternal.prototype.getBarW = function (axis, barTargetsNum) {
++        var $$ = this,
++            config = $$.config,
++            w = typeof config.bar_width === 'number' ? config.bar_width : barTargetsNum ? axis.tickInterval() * config.bar_width_ratio / barTargetsNum : 0;
++        return config.bar_width_max && w > config.bar_width_max ? config.bar_width_max : w;
++    };
++    ChartInternal.prototype.getBars = function (i, id) {
++        var $$ = this;
++        return (id ? $$.main.selectAll('.' + CLASS.bars + $$.getTargetSelectorSuffix(id)) : $$.main).selectAll('.' + CLASS.bar + (isValue(i) ? '-' + i : ''));
++    };
++    ChartInternal.prototype.expandBars = function (i, id, reset) {
++        var $$ = this;
++        if (reset) {
++            $$.unexpandBars();
++        }
++        $$.getBars(i, id).classed(CLASS.EXPANDED, true);
++    };
++    ChartInternal.prototype.unexpandBars = function (i) {
++        var $$ = this;
++        $$.getBars(i).classed(CLASS.EXPANDED, false);
++    };
++    ChartInternal.prototype.generateDrawBar = function (barIndices, isSub) {
++        var $$ = this,
++            config = $$.config,
++            getPoints = $$.generateGetBarPoints(barIndices, isSub);
++        return function (d, i) {
++            // 4 points that make a bar
++            var points = getPoints(d, i);
++
++            // switch points if axis is rotated, not applicable for sub chart
++            var indexX = config.axis_rotated ? 1 : 0;
++            var indexY = config.axis_rotated ? 0 : 1;
++
++            var path = 'M ' + points[0][indexX] + ',' + points[0][indexY] + ' ' + 'L' + points[1][indexX] + ',' + points[1][indexY] + ' ' + 'L' + points[2][indexX] + ',' + points[2][indexY] + ' ' + 'L' + points[3][indexX] + ',' + points[3][indexY] + ' ' + 'z';
++
++            return path;
++        };
++    };
++    ChartInternal.prototype.generateGetBarPoints = function (barIndices, isSub) {
++        var $$ = this,
++            axis = isSub ? $$.subXAxis : $$.xAxis,
++            barTargetsNum = barIndices.__max__ + 1,
++            barW = $$.getBarW(axis, barTargetsNum),
++            barX = $$.getShapeX(barW, barTargetsNum, barIndices, !!isSub),
++            barY = $$.getShapeY(!!isSub),
++            barOffset = $$.getShapeOffset($$.isBarType, barIndices, !!isSub),
++            barSpaceOffset = barW * ($$.config.bar_space / 2),
++            yScale = isSub ? $$.getSubYScale : $$.getYScale;
++        return function (d, i) {
++            var y0 = yScale.call($$, d.id)(0),
++                offset = barOffset(d, i) || y0,
++                // offset is for stacked bar chart
++            posX = barX(d),
++                posY = barY(d);
++            // fix posY not to overflow opposite quadrant
++            if ($$.config.axis_rotated) {
++                if (0 < d.value && posY < y0 || d.value < 0 && y0 < posY) {
++                    posY = y0;
++                }
++            }
++            // 4 points that make a bar
++            return [[posX + barSpaceOffset, offset], [posX + barSpaceOffset, posY - (y0 - offset)], [posX + barW - barSpaceOffset, posY - (y0 - offset)], [posX + barW - barSpaceOffset, offset]];
++        };
++    };
++    ChartInternal.prototype.isWithinBar = function (mouse, that) {
++        var box = that.getBoundingClientRect(),
++            seg0 = that.pathSegList.getItem(0),
++            seg1 = that.pathSegList.getItem(1),
++            x = Math.min(seg0.x, seg1.x),
++            y = Math.min(seg0.y, seg1.y),
++            w = box.width,
++            h = box.height,
++            offset = 2,
++            sx = x - offset,
++            ex = x + w + offset,
++            sy = y + h + offset,
++            ey = y - offset;
++        return sx < mouse[0] && mouse[0] < ex && ey < mouse[1] && mouse[1] < sy;
++    };
++
++    ChartInternal.prototype.getShapeIndices = function (typeFilter) {
++        var $$ = this,
++            config = $$.config,
++            indices = {},
++            i = 0,
++            j,
++            k;
++        $$.filterTargetsToShow($$.data.targets.filter(typeFilter, $$)).forEach(function (d) {
++            for (j = 0; j < config.data_groups.length; j++) {
++                if (config.data_groups[j].indexOf(d.id) < 0) {
++                    continue;
++                }
++                for (k = 0; k < config.data_groups[j].length; k++) {
++                    if (config.data_groups[j][k] in indices) {
++                        indices[d.id] = indices[config.data_groups[j][k]];
++                        break;
++                    }
++                }
++            }
++            if (isUndefined(indices[d.id])) {
++                indices[d.id] = i++;
++            }
++        });
++        indices.__max__ = i - 1;
++        return indices;
++    };
++    ChartInternal.prototype.getShapeX = function (offset, targetsNum, indices, isSub) {
++        var $$ = this,
++            scale = isSub ? $$.subX : $$.x;
++        return function (d) {
++            var index = d.id in indices ? indices[d.id] : 0;
++            return d.x || d.x === 0 ? scale(d.x) - offset * (targetsNum / 2 - index) : 0;
++        };
++    };
++    ChartInternal.prototype.getShapeY = function (isSub) {
++        var $$ = this;
++        return function (d) {
++            var scale = isSub ? $$.getSubYScale(d.id) : $$.getYScale(d.id);
++            return scale(d.value);
++        };
++    };
++    ChartInternal.prototype.getShapeOffset = function (typeFilter, indices, isSub) {
++        var $$ = this,
++            targets = $$.orderTargets($$.filterTargetsToShow($$.data.targets.filter(typeFilter, $$))),
++            targetIds = targets.map(function (t) {
++            return t.id;
++        });
++        return function (d, i) {
++            var scale = isSub ? $$.getSubYScale(d.id) : $$.getYScale(d.id),
++                y0 = scale(0),
++                offset = y0;
++            targets.forEach(function (t) {
++                var values = $$.isStepType(d) ? $$.convertValuesToStep(t.values) : t.values;
++                if (t.id === d.id || indices[t.id] !== indices[d.id]) {
++                    return;
++                }
++                if (targetIds.indexOf(t.id) < targetIds.indexOf(d.id)) {
++                    // check if the x values line up
++                    if (typeof values[i] === 'undefined' || +values[i].x !== +d.x) {
++                        // "+" for timeseries
++                        // if not, try to find the value that does line up
++                        i = -1;
++                        values.forEach(function (v, j) {
++                            if (v.x === d.x) {
++                                i = j;
++                            }
++                        });
++                    }
++                    if (i in values && values[i].value * d.value >= 0) {
++                        offset += scale(values[i].value) - y0;
++                    }
++                }
++            });
++            return offset;
++        };
++    };
++    ChartInternal.prototype.isWithinShape = function (that, d) {
++        var $$ = this,
++            shape = $$.d3.select(that),
++            isWithin;
++        if (!$$.isTargetToShow(d.id)) {
++            isWithin = false;
++        } else if (that.nodeName === 'circle') {
++            isWithin = $$.isStepType(d) ? $$.isWithinStep(that, $$.getYScale(d.id)(d.value)) : $$.isWithinCircle(that, $$.pointSelectR(d) * 1.5);
++        } else if (that.nodeName === 'path') {
++            isWithin = shape.classed(CLASS.bar) ? $$.isWithinBar($$.d3.mouse(that), that) : true;
++        }
++        return isWithin;
++    };
++
++    ChartInternal.prototype.getInterpolate = function (d) {
++        var $$ = this,
++            d3 = $$.d3,
++            types = {
++            'linear': d3.curveLinear,
++            'linear-closed': d3.curveLinearClosed,
++            'basis': d3.curveBasis,
++            'basis-open': d3.curveBasisOpen,
++            'basis-closed': d3.curveBasisClosed,
++            'bundle': d3.curveBundle,
++            'cardinal': d3.curveCardinal,
++            'cardinal-open': d3.curveCardinalOpen,
++            'cardinal-closed': d3.curveCardinalClosed,
++            'monotone': d3.curveMonotoneX,
++            'step': d3.curveStep,
++            'step-before': d3.curveStepBefore,
++            'step-after': d3.curveStepAfter
++        },
++            type;
++
++        if ($$.isSplineType(d)) {
++            type = types[$$.config.spline_interpolation_type] || types.cardinal;
++        } else if ($$.isStepType(d)) {
++            type = types[$$.config.line_step_type];
++        } else {
++            type = types.linear;
++        }
++        return type;
++    };
++
++    ChartInternal.prototype.initLine = function () {
++        var $$ = this;
++        $$.main.select('.' + CLASS.chart).append("g").attr("class", CLASS.chartLines);
++    };
++    ChartInternal.prototype.updateTargetsForLine = function (targets) {
++        var $$ = this,
++            config = $$.config,
++            mainLines,
++            mainLineEnter,
++            classChartLine = $$.classChartLine.bind($$),
++            classLines = $$.classLines.bind($$),
++            classAreas = $$.classAreas.bind($$),
++            classCircles = $$.classCircles.bind($$),
++            classFocus = $$.classFocus.bind($$);
++        mainLines = $$.main.select('.' + CLASS.chartLines).selectAll('.' + CLASS.chartLine).data(targets).attr('class', function (d) {
++            return classChartLine(d) + classFocus(d);
++        });
++        mainLineEnter = mainLines.enter().append('g').attr('class', classChartLine).style('opacity', 0).style("pointer-events", "none");
++        // Lines for each data
++        mainLineEnter.append('g').attr("class", classLines);
++        // Areas
++        mainLineEnter.append('g').attr('class', classAreas);
++        // Circles for each data point on lines
++        mainLineEnter.append('g').attr("class", function (d) {
++            return $$.generateClass(CLASS.selectedCircles, d.id);
++        });
++        mainLineEnter.append('g').attr("class", classCircles).style("cursor", function (d) {
++            return config.data_selection_isselectable(d) ? "pointer" : null;
++        });
++        // Update date for selected circles
++        targets.forEach(function (t) {
++            $$.main.selectAll('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(t.id)).selectAll('.' + CLASS.selectedCircle).each(function (d) {
++                d.value = t.values[d.index].value;
++            });
++        });
++        // MEMO: can not keep same color...
++        //mainLineUpdate.exit().remove();
++    };
++    ChartInternal.prototype.updateLine = function (durationForExit) {
++        var $$ = this;
++        var mainLine = $$.main.selectAll('.' + CLASS.lines).selectAll('.' + CLASS.line).data($$.lineData.bind($$));
++        var mainLineEnter = mainLine.enter().append('path').attr('class', $$.classLine.bind($$)).style("stroke", $$.color);
++        $$.mainLine = mainLineEnter.merge(mainLine).style("opacity", $$.initialOpacity.bind($$)).style('shape-rendering', function (d) {
++            return $$.isStepType(d) ? 'crispEdges' : '';
++        }).attr('transform', null);
++        mainLine.exit().transition().duration(durationForExit).style('opacity', 0);
++    };
++    ChartInternal.prototype.redrawLine = function (drawLine, withTransition, transition) {
++        return [(withTransition ? this.mainLine.transition(transition) : this.mainLine).attr("d", drawLine).style("stroke", this.color).style("opacity", 1)];
++    };
++    ChartInternal.prototype.generateDrawLine = function (lineIndices, isSub) {
++        var $$ = this,
++            config = $$.config,
++            line = $$.d3.line(),
++            getPoints = $$.generateGetLinePoints(lineIndices, isSub),
++            yScaleGetter = isSub ? $$.getSubYScale : $$.getYScale,
++            xValue = function xValue(d) {
++            return (isSub ? $$.subxx : $$.xx).call($$, d);
++        },
++            yValue = function yValue(d, i) {
++            return config.data_groups.length > 0 ? getPoints(d, i)[0][1] : yScaleGetter.call($$, d.id)(d.value);
++        };
++
++        line = config.axis_rotated ? line.x(yValue).y(xValue) : line.x(xValue).y(yValue);
++        if (!config.line_connectNull) {
++            line = line.defined(function (d) {
++                return d.value != null;
++            });
++        }
++        return function (d) {
++            var values = config.line_connectNull ? $$.filterRemoveNull(d.values) : d.values,
++                x = isSub ? $$.subX : $$.x,
++                y = yScaleGetter.call($$, d.id),
++                x0 = 0,
++                y0 = 0,
++                path;
++            if ($$.isLineType(d)) {
++                if (config.data_regions[d.id]) {
++                    path = $$.lineWithRegions(values, x, y, config.data_regions[d.id]);
++                } else {
++                    if ($$.isStepType(d)) {
++                        values = $$.convertValuesToStep(values);
++                    }
++                    path = line.curve($$.getInterpolate(d))(values);
++                }
++            } else {
++                if (values[0]) {
++                    x0 = x(values[0].x);
++                    y0 = y(values[0].value);
++                }
++                path = config.axis_rotated ? "M " + y0 + " " + x0 : "M " + x0 + " " + y0;
++            }
++            return path ? path : "M 0 0";
++        };
++    };
++    ChartInternal.prototype.generateGetLinePoints = function (lineIndices, isSub) {
++        // partial duplication of generateGetBarPoints
++        var $$ = this,
++            config = $$.config,
++            lineTargetsNum = lineIndices.__max__ + 1,
++            x = $$.getShapeX(0, lineTargetsNum, lineIndices, !!isSub),
++            y = $$.getShapeY(!!isSub),
++            lineOffset = $$.getShapeOffset($$.isLineType, lineIndices, !!isSub),
++            yScale = isSub ? $$.getSubYScale : $$.getYScale;
++        return function (d, i) {
++            var y0 = yScale.call($$, d.id)(0),
++                offset = lineOffset(d, i) || y0,
++                // offset is for stacked area chart
++            posX = x(d),
++                posY = y(d);
++            // fix posY not to overflow opposite quadrant
++            if (config.axis_rotated) {
++                if (0 < d.value && posY < y0 || d.value < 0 && y0 < posY) {
++                    posY = y0;
++                }
++            }
++            // 1 point that marks the line position
++            return [[posX, posY - (y0 - offset)], [posX, posY - (y0 - offset)], // needed for compatibility
++            [posX, posY - (y0 - offset)], // needed for compatibility
++            [posX, posY - (y0 - offset)] // needed for compatibility
++            ];
++        };
++    };
++
++    ChartInternal.prototype.lineWithRegions = function (d, x, y, _regions) {
++        var $$ = this,
++            config = $$.config,
++            prev = -1,
++            i,
++            j,
++            s = "M",
++            sWithRegion,
++            xp,
++            yp,
++            dx,
++            dy,
++            dd,
++            diff,
++            diffx2,
++            xOffset = $$.isCategorized() ? 0.5 : 0,
++            xValue,
++            yValue,
++            regions = [];
++
++        function isWithinRegions(x, regions) {
++            var i;
++            for (i = 0; i < regions.length; i++) {
++                if (regions[i].start < x && x <= regions[i].end) {
++                    return true;
++                }
++            }
++            return false;
++        }
++
++        // Check start/end of regions
++        if (isDefined(_regions)) {
++            for (i = 0; i < _regions.length; i++) {
++                regions[i] = {};
++                if (isUndefined(_regions[i].start)) {
++                    regions[i].start = d[0].x;
++                } else {
++                    regions[i].start = $$.isTimeSeries() ? $$.parseDate(_regions[i].start) : _regions[i].start;
++                }
++                if (isUndefined(_regions[i].end)) {
++                    regions[i].end = d[d.length - 1].x;
++                } else {
++                    regions[i].end = $$.isTimeSeries() ? $$.parseDate(_regions[i].end) : _regions[i].end;
++                }
++            }
++        }
++
++        // Set scales
++        xValue = config.axis_rotated ? function (d) {
++            return y(d.value);
++        } : function (d) {
++            return x(d.x);
++        };
++        yValue = config.axis_rotated ? function (d) {
++            return x(d.x);
++        } : function (d) {
++            return y(d.value);
++        };
++
++        // Define svg generator function for region
++        function generateM(points) {
++            return 'M' + points[0][0] + ' ' + points[0][1] + ' ' + points[1][0] + ' ' + points[1][1];
++        }
++        if ($$.isTimeSeries()) {
++            sWithRegion = function sWithRegion(d0, d1, j, diff) {
++                var x0 = d0.x.getTime(),
++                    x_diff = d1.x - d0.x,
++                    xv0 = new Date(x0 + x_diff * j),
++                    xv1 = new Date(x0 + x_diff * (j + diff)),
++                    points;
++                if (config.axis_rotated) {
++                    points = [[y(yp(j)), x(xv0)], [y(yp(j + diff)), x(xv1)]];
++                } else {
++                    points = [[x(xv0), y(yp(j))], [x(xv1), y(yp(j + diff))]];
++                }
++                return generateM(points);
++            };
++        } else {
++            sWithRegion = function sWithRegion(d0, d1, j, diff) {
++                var points;
++                if (config.axis_rotated) {
++                    points = [[y(yp(j), true), x(xp(j))], [y(yp(j + diff), true), x(xp(j + diff))]];
++                } else {
++                    points = [[x(xp(j), true), y(yp(j))], [x(xp(j + diff), true), y(yp(j + diff))]];
++                }
++                return generateM(points);
++            };
++        }
++
++        // Generate
++        for (i = 0; i < d.length; i++) {
++
++            // Draw as normal
++            if (isUndefined(regions) || !isWithinRegions(d[i].x, regions)) {
++                s += " " + xValue(d[i]) + " " + yValue(d[i]);
++            }
++            // Draw with region // TODO: Fix for horizotal charts
++            else {
++                    xp = $$.getScale(d[i - 1].x + xOffset, d[i].x + xOffset, $$.isTimeSeries());
++                    yp = $$.getScale(d[i - 1].value, d[i].value);
++
++                    dx = x(d[i].x) - x(d[i - 1].x);
++                    dy = y(d[i].value) - y(d[i - 1].value);
++                    dd = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
++                    diff = 2 / dd;
++                    diffx2 = diff * 2;
++
++                    for (j = diff; j <= 1; j += diffx2) {
++                        s += sWithRegion(d[i - 1], d[i], j, diff);
++                    }
++                }
++            prev = d[i].x;
++        }
++
++        return s;
++    };
++
++    ChartInternal.prototype.updateArea = function (durationForExit) {
++        var $$ = this,
++            d3 = $$.d3;
++        var mainArea = $$.main.selectAll('.' + CLASS.areas).selectAll('.' + CLASS.area).data($$.lineData.bind($$));
++        var mainAreaEnter = mainArea.enter().append('path').attr("class", $$.classArea.bind($$)).style("fill", $$.color).style("opacity", function () {
++            $$.orgAreaOpacity = +d3.select(this).style('opacity');return 0;
++        });
++        $$.mainArea = mainAreaEnter.merge(mainArea).style("opacity", $$.orgAreaOpacity);
++        mainArea.exit().transition().duration(durationForExit).style('opacity', 0);
++    };
++    ChartInternal.prototype.redrawArea = function (drawArea, withTransition, transition) {
++        return [(withTransition ? this.mainArea.transition(transition) : this.mainArea).attr("d", drawArea).style("fill", this.color).style("opacity", this.orgAreaOpacity)];
++    };
++    ChartInternal.prototype.generateDrawArea = function (areaIndices, isSub) {
++        var $$ = this,
++            config = $$.config,
++            area = $$.d3.area(),
++            getPoints = $$.generateGetAreaPoints(areaIndices, isSub),
++            yScaleGetter = isSub ? $$.getSubYScale : $$.getYScale,
++            xValue = function xValue(d) {
++            return (isSub ? $$.subxx : $$.xx).call($$, d);
++        },
++            value0 = function value0(d, i) {
++            return config.data_groups.length > 0 ? getPoints(d, i)[0][1] : yScaleGetter.call($$, d.id)($$.getAreaBaseValue(d.id));
++        },
++            value1 = function value1(d, i) {
++            return config.data_groups.length > 0 ? getPoints(d, i)[1][1] : yScaleGetter.call($$, d.id)(d.value);
++        };
++
++        area = config.axis_rotated ? area.x0(value0).x1(value1).y(xValue) : area.x(xValue).y0(config.area_above ? 0 : value0).y1(value1);
++        if (!config.line_connectNull) {
++            area = area.defined(function (d) {
++                return d.value !== null;
++            });
++        }
++
++        return function (d) {
++            var values = config.line_connectNull ? $$.filterRemoveNull(d.values) : d.values,
++                x0 = 0,
++                y0 = 0,
++                path;
++            if ($$.isAreaType(d)) {
++                if ($$.isStepType(d)) {
++                    values = $$.convertValuesToStep(values);
++                }
++                path = area.curve($$.getInterpolate(d))(values);
++            } else {
++                if (values[0]) {
++                    x0 = $$.x(values[0].x);
++                    y0 = $$.getYScale(d.id)(values[0].value);
++                }
++                path = config.axis_rotated ? "M " + y0 + " " + x0 : "M " + x0 + " " + y0;
++            }
++            return path ? path : "M 0 0";
++        };
++    };
++    ChartInternal.prototype.getAreaBaseValue = function () {
++        return 0;
++    };
++    ChartInternal.prototype.generateGetAreaPoints = function (areaIndices, isSub) {
++        // partial duplication of generateGetBarPoints
++        var $$ = this,
++            config = $$.config,
++            areaTargetsNum = areaIndices.__max__ + 1,
++            x = $$.getShapeX(0, areaTargetsNum, areaIndices, !!isSub),
++            y = $$.getShapeY(!!isSub),
++            areaOffset = $$.getShapeOffset($$.isAreaType, areaIndices, !!isSub),
++            yScale = isSub ? $$.getSubYScale : $$.getYScale;
++        return function (d, i) {
++            var y0 = yScale.call($$, d.id)(0),
++                offset = areaOffset(d, i) || y0,
++                // offset is for stacked area chart
++            posX = x(d),
++                posY = y(d);
++            // fix posY not to overflow opposite quadrant
++            if (config.axis_rotated) {
++                if (0 < d.value && posY < y0 || d.value < 0 && y0 < posY) {
++                    posY = y0;
++                }
++            }
++            // 1 point that marks the area position
++            return [[posX, offset], [posX, posY - (y0 - offset)], [posX, posY - (y0 - offset)], // needed for compatibility
++            [posX, offset] // needed for compatibility
++            ];
++        };
++    };
++
++    ChartInternal.prototype.updateCircle = function (cx, cy) {
++        var $$ = this;
++        var mainCircle = $$.main.selectAll('.' + CLASS.circles).selectAll('.' + CLASS.circle).data($$.lineOrScatterData.bind($$));
++        var mainCircleEnter = mainCircle.enter().append("circle").attr("class", $$.classCircle.bind($$)).attr("cx", cx).attr("cy", cy).attr("r", $$.pointR.bind($$)).style("fill", $$.color);
++        $$.mainCircle = mainCircleEnter.merge(mainCircle).style("opacity", $$.initialOpacityForCircle.bind($$));
++        mainCircle.exit().style("opacity", 0);
++    };
++    ChartInternal.prototype.redrawCircle = function (cx, cy, withTransition, transition) {
++        var $$ = this,
++            selectedCircles = $$.main.selectAll('.' + CLASS.selectedCircle);
++        return [(withTransition ? $$.mainCircle.transition(transition) : $$.mainCircle).style('opacity', this.opacityForCircle.bind($$)).style("fill", $$.color).attr("cx", cx).attr("cy", cy), (withTransition ? selectedCircles.transition(transition) : selectedCircles).attr("cx", cx).attr("cy", cy)];
++    };
++    ChartInternal.prototype.circleX = function (d) {
++        return d.x || d.x === 0 ? this.x(d.x) : null;
++    };
++    ChartInternal.prototype.updateCircleY = function () {
++        var $$ = this,
++            lineIndices,
++            getPoints;
++        if ($$.config.data_groups.length > 0) {
++            lineIndices = $$.getShapeIndices($$.isLineType), getPoints = $$.generateGetLinePoints(lineIndices);
++            $$.circleY = function (d, i) {
++                return getPoints(d, i)[0][1];
++            };
++        } else {
++            $$.circleY = function (d) {
++                return $$.getYScale(d.id)(d.value);
++            };
++        }
++    };
++    ChartInternal.prototype.getCircles = function (i, id) {
++        var $$ = this;
++        return (id ? $$.main.selectAll('.' + CLASS.circles + $$.getTargetSelectorSuffix(id)) : $$.main).selectAll('.' + CLASS.circle + (isValue(i) ? '-' + i : ''));
++    };
++    ChartInternal.prototype.expandCircles = function (i, id, reset) {
++        var $$ = this,
++            r = $$.pointExpandedR.bind($$);
++        if (reset) {
++            $$.unexpandCircles();
++        }
++        $$.getCircles(i, id).classed(CLASS.EXPANDED, true).attr('r', r);
++    };
++    ChartInternal.prototype.unexpandCircles = function (i) {
++        var $$ = this,
++            r = $$.pointR.bind($$);
++        $$.getCircles(i).filter(function () {
++            return $$.d3.select(this).classed(CLASS.EXPANDED);
++        }).classed(CLASS.EXPANDED, false).attr('r', r);
++    };
++    ChartInternal.prototype.pointR = function (d) {
++        var $$ = this,
++            config = $$.config;
++        return $$.isStepType(d) ? 0 : isFunction(config.point_r) ? config.point_r(d) : config.point_r;
++    };
++    ChartInternal.prototype.pointExpandedR = function (d) {
++        var $$ = this,
++            config = $$.config;
++        if (config.point_focus_expand_enabled) {
++            return isFunction(config.point_focus_expand_r) ? config.point_focus_expand_r(d) : config.point_focus_expand_r ? config.point_focus_expand_r : $$.pointR(d) * 1.75;
++        } else {
++            return $$.pointR(d);
++        }
++    };
++    ChartInternal.prototype.pointSelectR = function (d) {
++        var $$ = this,
++            config = $$.config;
++        return isFunction(config.point_select_r) ? config.point_select_r(d) : config.point_select_r ? config.point_select_r : $$.pointR(d) * 4;
++    };
++    ChartInternal.prototype.isWithinCircle = function (that, r) {
++        var d3 = this.d3,
++            mouse = d3.mouse(that),
++            d3_this = d3.select(that),
++            cx = +d3_this.attr("cx"),
++            cy = +d3_this.attr("cy");
++        return Math.sqrt(Math.pow(cx - mouse[0], 2) + Math.pow(cy - mouse[1], 2)) < r;
++    };
++    ChartInternal.prototype.isWithinStep = function (that, y) {
++        return Math.abs(y - this.d3.mouse(that)[1]) < 30;
++    };
++
++    ChartInternal.prototype.getCurrentWidth = function () {
++        var $$ = this,
++            config = $$.config;
++        return config.size_width ? config.size_width : $$.getParentWidth();
++    };
++    ChartInternal.prototype.getCurrentHeight = function () {
++        var $$ = this,
++            config = $$.config,
++            h = config.size_height ? config.size_height : $$.getParentHeight();
++        return h > 0 ? h : 320 / ($$.hasType('gauge') && !config.gauge_fullCircle ? 2 : 1);
++    };
++    ChartInternal.prototype.getCurrentPaddingTop = function () {
++        var $$ = this,
++            config = $$.config,
++            padding = isValue(config.padding_top) ? config.padding_top : 0;
++        if ($$.title && $$.title.node()) {
++            padding += $$.getTitlePadding();
++        }
++        return padding;
++    };
++    ChartInternal.prototype.getCurrentPaddingBottom = function () {
++        var config = this.config;
++        return isValue(config.padding_bottom) ? config.padding_bottom : 0;
++    };
++    ChartInternal.prototype.getCurrentPaddingLeft = function (withoutRecompute) {
++        var $$ = this,
++            config = $$.config;
++        if (isValue(config.padding_left)) {
++            return config.padding_left;
++        } else if (config.axis_rotated) {
++            return !config.axis_x_show || config.axis_x_inner ? 1 : Math.max(ceil10($$.getAxisWidthByAxisId('x', withoutRecompute)), 40);
++        } else if (!config.axis_y_show || config.axis_y_inner) {
++            // && !config.axis_rotated
++            return $$.axis.getYAxisLabelPosition().isOuter ? 30 : 1;
++        } else {
++            return ceil10($$.getAxisWidthByAxisId('y', withoutRecompute));
++        }
++    };
++    ChartInternal.prototype.getCurrentPaddingRight = function () {
++        var $$ = this,
++            config = $$.config,
++            defaultPadding = 10,
++            legendWidthOnRight = $$.isLegendRight ? $$.getLegendWidth() + 20 : 0;
++        if (isValue(config.padding_right)) {
++            return config.padding_right + 1; // 1 is needed not to hide tick line
++        } else if (config.axis_rotated) {
++            return defaultPadding + legendWidthOnRight;
++        } else if (!config.axis_y2_show || config.axis_y2_inner) {
++            // && !config.axis_rotated
++            return 2 + legendWidthOnRight + ($$.axis.getY2AxisLabelPosition().isOuter ? 20 : 0);
++        } else {
++            return ceil10($$.getAxisWidthByAxisId('y2')) + legendWidthOnRight;
++        }
++    };
++
++    ChartInternal.prototype.getParentRectValue = function (key) {
++        var parent = this.selectChart.node(),
++            v;
++        while (parent && parent.tagName !== 'BODY') {
++            try {
++                v = parent.getBoundingClientRect()[key];
++            } catch (e) {
++                if (key === 'width') {
++                    // In IE in certain cases getBoundingClientRect
++                    // will cause an "unspecified error"
++                    v = parent.offsetWidth;
++                }
++            }
++            if (v) {
++                break;
++            }
++            parent = parent.parentNode;
++        }
++        return v;
++    };
++    ChartInternal.prototype.getParentWidth = function () {
++        return this.getParentRectValue('width');
++    };
++    ChartInternal.prototype.getParentHeight = function () {
++        var h = this.selectChart.style('height');
++        return h.indexOf('px') > 0 ? +h.replace('px', '') : 0;
++    };
++
++    ChartInternal.prototype.getSvgLeft = function (withoutRecompute) {
++        var $$ = this,
++            config = $$.config,
++            hasLeftAxisRect = config.axis_rotated || !config.axis_rotated && !config.axis_y_inner,
++            leftAxisClass = config.axis_rotated ? CLASS.axisX : CLASS.axisY,
++            leftAxis = $$.main.select('.' + leftAxisClass).node(),
++            svgRect = leftAxis && hasLeftAxisRect ? leftAxis.getBoundingClientRect() : { right: 0 },
++            chartRect = $$.selectChart.node().getBoundingClientRect(),
++            hasArc = $$.hasArcType(),
++            svgLeft = svgRect.right - chartRect.left - (hasArc ? 0 : $$.getCurrentPaddingLeft(withoutRecompute));
++        return svgLeft > 0 ? svgLeft : 0;
++    };
++
++    ChartInternal.prototype.getAxisWidthByAxisId = function (id, withoutRecompute) {
++        var $$ = this,
++            position = $$.axis.getLabelPositionById(id);
++        return $$.axis.getMaxTickWidth(id, withoutRecompute) + (position.isInner ? 20 : 40);
++    };
++    ChartInternal.prototype.getHorizontalAxisHeight = function (axisId) {
++        var $$ = this,
++            config = $$.config,
++            h = 30;
++        if (axisId === 'x' && !config.axis_x_show) {
++            return 8;
++        }
++        if (axisId === 'x' && config.axis_x_height) {
++            return config.axis_x_height;
++        }
++        if (axisId === 'y' && !config.axis_y_show) {
++            return config.legend_show && !$$.isLegendRight && !$$.isLegendInset ? 10 : 1;
++        }
++        if (axisId === 'y2' && !config.axis_y2_show) {
++            return $$.rotated_padding_top;
++        }
++        // Calculate x axis height when tick rotated
++        if (axisId === 'x' && !config.axis_rotated && config.axis_x_tick_rotate) {
++            h = 30 + $$.axis.getMaxTickWidth(axisId) * Math.cos(Math.PI * (90 - Math.abs(config.axis_x_tick_rotate)) / 180);
++        }
++        // Calculate y axis height when tick rotated
++        if (axisId === 'y' && config.axis_rotated && config.axis_y_tick_rotate) {
++            h = 30 + $$.axis.getMaxTickWidth(axisId) * Math.cos(Math.PI * (90 - Math.abs(config.axis_y_tick_rotate)) / 180);
++        }
++        return h + ($$.axis.getLabelPositionById(axisId).isInner ? 0 : 10) + (axisId === 'y2' ? -10 : 0);
++    };
++
++    ChartInternal.prototype.initBrush = function (scale) {
++        var $$ = this,
++            d3 = $$.d3;
++        // TODO: dynamically change brushY/brushX according to axis_rotated.
++        $$.brush = ($$.config.axis_rotated ? d3.brushY() : d3.brushX()).on("brush", function () {
++            var event = d3.event.sourceEvent;
++            if (event && event.type === "zoom") {
++                return;
++            }
++            $$.redrawForBrush();
++        }).on("end", function () {
++            var event = d3.event.sourceEvent;
++            if (event && event.type === "zoom") {
++                return;
++            }
++            if ($$.brush.empty() && event && event.type !== 'end') {
++                $$.brush.clear();
++            }
++        });
++        $$.brush.updateExtent = function () {
++            var range = this.scale.range(),
++                extent;
++            if ($$.config.axis_rotated) {
++                extent = [[0, range[0]], [$$.width2, range[1]]];
++            } else {
++                extent = [[range[0], 0], [range[1], $$.height2]];
++            }
++            this.extent(extent);
++            return this;
++        };
++        $$.brush.updateScale = function (scale) {
++            this.scale = scale;
++            return this;
++        };
++        $$.brush.update = function (scale) {
++            this.updateScale(scale || $$.subX).updateExtent();
++            $$.context.select('.' + CLASS.brush).call(this);
++        };
++        $$.brush.clear = function () {
++            $$.context.select('.' + CLASS.brush).call($$.brush.move, null);
++        };
++        $$.brush.selection = function () {
++            return d3.brushSelection($$.context.select('.' + CLASS.brush).node());
++        };
++        $$.brush.selectionAsValue = function (selectionAsValue, withTransition) {
++            var selection, brush;
++            if (selectionAsValue) {
++                if ($$.context) {
++                    selection = [this.scale(selectionAsValue[0]), this.scale(selectionAsValue[1])];
++                    brush = $$.context.select('.' + CLASS.brush);
++                    if (withTransition) {
++                        brush = brush.transition();
++                    }
++                    $$.brush.move(brush, selection);
++                }
++                return [];
++            }
++            selection = $$.brush.selection() || [0, 0];
++            return [this.scale.invert(selection[0]), this.scale.invert(selection[1])];
++        };
++        $$.brush.empty = function () {
++            var selection = $$.brush.selection();
++            return !selection || selection[0] === selection[1];
++        };
++        return $$.brush.updateScale(scale);
++    };
++    ChartInternal.prototype.initSubchart = function () {
++        var $$ = this,
++            config = $$.config,
++            context = $$.context = $$.svg.append("g").attr("transform", $$.getTranslate('context')),
++            visibility = config.subchart_show ? 'visible' : 'hidden';
++
++        // set style
++        context.style('visibility', visibility);
++
++        // Define g for chart area
++        context.append('g').attr("clip-path", $$.clipPathForSubchart).attr('class', CLASS.chart);
++
++        // Define g for bar chart area
++        context.select('.' + CLASS.chart).append("g").attr("class", CLASS.chartBars);
++
++        // Define g for line chart area
++        context.select('.' + CLASS.chart).append("g").attr("class", CLASS.chartLines);
++
++        // Add extent rect for Brush
++        context.append("g").attr("clip-path", $$.clipPath).attr("class", CLASS.brush);
++
++        // ATTENTION: This must be called AFTER chart added
++        // Add Axis
++        $$.axes.subx = context.append("g").attr("class", CLASS.axisX).attr("transform", $$.getTranslate('subx')).attr("clip-path", config.axis_rotated ? "" : $$.clipPathForXAxis);
++    };
++    ChartInternal.prototype.initSubchartBrush = function () {
++        var $$ = this;
++        // Add extent rect for Brush
++        $$.initBrush($$.subX).updateExtent();
++        $$.context.select('.' + CLASS.brush).call($$.brush);
++    };
++    ChartInternal.prototype.updateTargetsForSubchart = function (targets) {
++        var $$ = this,
++            context = $$.context,
++            config = $$.config,
++            contextLineEnter,
++            contextLine,
++            contextBarEnter,
++            contextBar,
++            classChartBar = $$.classChartBar.bind($$),
++            classBars = $$.classBars.bind($$),
++            classChartLine = $$.classChartLine.bind($$),
++            classLines = $$.classLines.bind($$),
++            classAreas = $$.classAreas.bind($$);
++
++        if (config.subchart_show) {
++            //-- Bar --//
++            contextBar = context.select('.' + CLASS.chartBars).selectAll('.' + CLASS.chartBar).data(targets);
++            contextBarEnter = contextBar.enter().append('g').style('opacity', 0);
++            contextBarEnter.merge(contextBar).attr('class', classChartBar);
++            // Bars for each data
++            contextBarEnter.append('g').attr("class", classBars);
++
++            //-- Line --//
++            contextLine = context.select('.' + CLASS.chartLines).selectAll('.' + CLASS.chartLine).data(targets);
++            contextLineEnter = contextLine.enter().append('g').style('opacity', 0);
++            contextLineEnter.merge(contextLine).attr('class', classChartLine);
++            // Lines for each data
++            contextLineEnter.append("g").attr("class", classLines);
++            // Area
++            contextLineEnter.append("g").attr("class", classAreas);
++
++            //-- Brush --//
++            context.selectAll('.' + CLASS.brush + ' rect').attr(config.axis_rotated ? "width" : "height", config.axis_rotated ? $$.width2 : $$.height2);
++        }
++    };
++    ChartInternal.prototype.updateBarForSubchart = function (durationForExit) {
++        var $$ = this;
++        var contextBar = $$.context.selectAll('.' + CLASS.bars).selectAll('.' + CLASS.bar).data($$.barData.bind($$));
++        var contextBarEnter = contextBar.enter().append('path').attr("class", $$.classBar.bind($$)).style("stroke", 'none').style("fill", $$.color);
++        contextBar.exit().transition().duration(durationForExit).style('opacity', 0).remove();
++        $$.contextBar = contextBarEnter.merge(contextBar).style("opacity", $$.initialOpacity.bind($$));
++    };
++    ChartInternal.prototype.redrawBarForSubchart = function (drawBarOnSub, withTransition, duration) {
++        (withTransition ? this.contextBar.transition(Math.random().toString()).duration(duration) : this.contextBar).attr('d', drawBarOnSub).style('opacity', 1);
++    };
++    ChartInternal.prototype.updateLineForSubchart = function (durationForExit) {
++        var $$ = this;
++        var contextLine = $$.context.selectAll('.' + CLASS.lines).selectAll('.' + CLASS.line).data($$.lineData.bind($$));
++        var contextLineEnter = contextLine.enter().append('path').attr('class', $$.classLine.bind($$)).style('stroke', $$.color);
++        contextLine.exit().transition().duration(durationForExit).style('opacity', 0).remove();
++        $$.contextLine = contextLineEnter.merge(contextLine).style("opacity", $$.initialOpacity.bind($$));
++    };
++    ChartInternal.prototype.redrawLineForSubchart = function (drawLineOnSub, withTransition, duration) {
++        (withTransition ? this.contextLine.transition(Math.random().toString()).duration(duration) : this.contextLine).attr("d", drawLineOnSub).style('opacity', 1);
++    };
++    ChartInternal.prototype.updateAreaForSubchart = function (durationForExit) {
++        var $$ = this,
++            d3 = $$.d3;
++        var contextArea = $$.context.selectAll('.' + CLASS.areas).selectAll('.' + CLASS.area).data($$.lineData.bind($$));
++        var contextAreaEnter = contextArea.enter().append('path').attr("class", $$.classArea.bind($$)).style("fill", $$.color).style("opacity", function () {
++            $$.orgAreaOpacity = +d3.select(this).style('opacity');return 0;
++        });
++        contextArea.exit().transition().duration(durationForExit).style('opacity', 0).remove();
++        $$.contextArea = contextAreaEnter.merge(contextArea).style("opacity", 0);
++    };
++    ChartInternal.prototype.redrawAreaForSubchart = function (drawAreaOnSub, withTransition, duration) {
++        (withTransition ? this.contextArea.transition(Math.random().toString()).duration(duration) : this.contextArea).attr("d", drawAreaOnSub).style("fill", this.color).style("opacity", this.orgAreaOpacity);
++    };
++    ChartInternal.prototype.redrawSubchart = function (withSubchart, transitions, duration, durationForExit, areaIndices, barIndices, lineIndices) {
++        var $$ = this,
++            d3 = $$.d3,
++            config = $$.config,
++            drawAreaOnSub,
++            drawBarOnSub,
++            drawLineOnSub;
++
++        $$.context.style('visibility', config.subchart_show ? 'visible' : 'hidden');
++
++        // subchart
++        if (config.subchart_show) {
++            // reflect main chart to extent on subchart if zoomed
++            if (d3.event && d3.event.type === 'zoom') {
++                $$.brush.selectionAsValue($$.x.orgDomain());
++            }
++            // update subchart elements if needed
++            if (withSubchart) {
++                // extent rect
++                if (!$$.brush.empty()) {
++                    $$.brush.selectionAsValue($$.x.orgDomain());
++                }
++                // setup drawer - MEMO: this must be called after axis updated
++                drawAreaOnSub = $$.generateDrawArea(areaIndices, true);
++                drawBarOnSub = $$.generateDrawBar(barIndices, true);
++                drawLineOnSub = $$.generateDrawLine(lineIndices, true);
++
++                $$.updateBarForSubchart(duration);
++                $$.updateLineForSubchart(duration);
++                $$.updateAreaForSubchart(duration);
++
++                $$.redrawBarForSubchart(drawBarOnSub, duration, duration);
++                $$.redrawLineForSubchart(drawLineOnSub, duration, duration);
++                $$.redrawAreaForSubchart(drawAreaOnSub, duration, duration);
++            }
++        }
++    };
++    ChartInternal.prototype.redrawForBrush = function () {
++        var $$ = this,
++            x = $$.x,
++            d3 = $$.d3,
++            s;
++        $$.redraw({
++            withTransition: false,
++            withY: $$.config.zoom_rescale,
++            withSubchart: false,
++            withUpdateXDomain: true,
++            withEventRect: false,
++            withDimension: false
++        });
++        // update zoom transation binded to event rect
++        s = d3.event.selection || $$.brush.scale.range();
++        $$.main.select('.' + CLASS.eventRect).call($$.zoom.transform, d3.zoomIdentity.scale($$.width / (s[1] - s[0])).translate(-s[0], 0));
++        $$.config.subchart_onbrush.call($$.api, x.orgDomain());
++    };
++    ChartInternal.prototype.transformContext = function (withTransition, transitions) {
++        var $$ = this,
++            subXAxis;
++        if (transitions && transitions.axisSubX) {
++            subXAxis = transitions.axisSubX;
++        } else {
++            subXAxis = $$.context.select('.' + CLASS.axisX);
++            if (withTransition) {
++                subXAxis = subXAxis.transition();
++            }
++        }
++        $$.context.attr("transform", $$.getTranslate('context'));
++        subXAxis.attr("transform", $$.getTranslate('subx'));
++    };
++    ChartInternal.prototype.getDefaultSelection = function () {
++        var $$ = this,
++            config = $$.config,
++            selection = isFunction(config.axis_x_selection) ? config.axis_x_selection($$.getXDomain($$.data.targets)) : config.axis_x_selection;
++        if ($$.isTimeSeries()) {
++            selection = [$$.parseDate(selection[0]), $$.parseDate(selection[1])];
++        }
++        return selection;
++    };
++
++    ChartInternal.prototype.initText = function () {
++        var $$ = this;
++        $$.main.select('.' + CLASS.chart).append("g").attr("class", CLASS.chartTexts);
++        $$.mainText = $$.d3.selectAll([]);
++    };
++    ChartInternal.prototype.updateTargetsForText = function (targets) {
++        var $$ = this,
++            classChartText = $$.classChartText.bind($$),
++            classTexts = $$.classTexts.bind($$),
++            classFocus = $$.classFocus.bind($$);
++        var mainText = $$.main.select('.' + CLASS.chartTexts).selectAll('.' + CLASS.chartText).data(targets);
++        var mainTextEnter = mainText.enter().append('g').attr('class', classChartText).style('opacity', 0).style("pointer-events", "none");
++        mainTextEnter.append('g').attr('class', classTexts);
++        mainTextEnter.merge(mainText).attr('class', function (d) {
++            return classChartText(d) + classFocus(d);
++        });
++    };
++    ChartInternal.prototype.updateText = function (xForText, yForText, durationForExit) {
++        var $$ = this,
++            config = $$.config,
++            barOrLineData = $$.barOrLineData.bind($$),
++            classText = $$.classText.bind($$);
++        var mainText = $$.main.selectAll('.' + CLASS.texts).selectAll('.' + CLASS.text).data(barOrLineData);
++        var mainTextEnter = mainText.enter().append('text').attr("class", classText).attr('text-anchor', function (d) {
++            return config.axis_rotated ? d.value < 0 ? 'end' : 'start' : 'middle';
++        }).style("stroke", 'none').attr('x', xForText).attr('y', yForText).style("fill", function (d) {
++            return $$.color(d);
++        }).style("fill-opacity", 0);
++        $$.mainText = mainTextEnter.merge(mainText).text(function (d, i, j) {
++            return $$.dataLabelFormat(d.id)(d.value, d.id, i, j);
++        });
++        mainText.exit().transition().duration(durationForExit).style('fill-opacity', 0).remove();
++    };
++    ChartInternal.prototype.redrawText = function (xForText, yForText, forFlow, withTransition, transition) {
++        return [(withTransition ? this.mainText.transition(transition) : this.mainText).attr('x', xForText).attr('y', yForText).style("fill", this.color).style("fill-opacity", forFlow ? 0 : this.opacityForText.bind(this))];
++    };
++    ChartInternal.prototype.getTextRect = function (text, cls, element) {
++        var dummy = this.d3.select('body').append('div').classed('c3', true),
++            svg = dummy.append("svg").style('visibility', 'hidden').style('position', 'fixed').style('top', 0).style('left', 0),
++            font = this.d3.select(element).style('font'),
++            rect;
++        svg.selectAll('.dummy').data([text]).enter().append('text').classed(cls ? cls : "", true).style('font', font).text(text).each(function () {
++            rect = this.getBoundingClientRect();
++        });
++        dummy.remove();
++        return rect;
++    };
++    ChartInternal.prototype.generateXYForText = function (areaIndices, barIndices, lineIndices, forX) {
++        var $$ = this,
++            getAreaPoints = $$.generateGetAreaPoints(areaIndices, false),
++            getBarPoints = $$.generateGetBarPoints(barIndices, false),
++            getLinePoints = $$.generateGetLinePoints(lineIndices, false),
++            getter = forX ? $$.getXForText : $$.getYForText;
++        return function (d, i) {
++            var getPoints = $$.isAreaType(d) ? getAreaPoints : $$.isBarType(d) ? getBarPoints : getLinePoints;
++            return getter.call($$, getPoints(d, i), d, this);
++        };
++    };
++    ChartInternal.prototype.getXForText = function (points, d, textElement) {
++        var $$ = this,
++            box = textElement.getBoundingClientRect(),
++            xPos,
++            padding;
++        if ($$.config.axis_rotated) {
++            padding = $$.isBarType(d) ? 4 : 6;
++            xPos = points[2][1] + padding * (d.value < 0 ? -1 : 1);
++        } else {
++            xPos = $$.hasType('bar') ? (points[2][0] + points[0][0]) / 2 : points[0][0];
++        }
++        // show labels regardless of the domain if value is null
++        if (d.value === null) {
++            if (xPos > $$.width) {
++                xPos = $$.width - box.width;
++            } else if (xPos < 0) {
++                xPos = 4;
++            }
++        }
++        return xPos;
++    };
++    ChartInternal.prototype.getYForText = function (points, d, textElement) {
++        var $$ = this,
++            box = textElement.getBoundingClientRect(),
++            yPos;
++        if ($$.config.axis_rotated) {
++            yPos = (points[0][0] + points[2][0] + box.height * 0.6) / 2;
++        } else {
++            yPos = points[2][1];
++            if (d.value < 0 || d.value === 0 && !$$.hasPositiveValue) {
++                yPos += box.height;
++                if ($$.isBarType(d) && $$.isSafari()) {
++                    yPos -= 3;
++                } else if (!$$.isBarType(d) && $$.isChrome()) {
++                    yPos += 3;
++                }
++            } else {
++                yPos += $$.isBarType(d) ? -3 : -6;
++            }
++        }
++        // show labels regardless of the domain if value is null
++        if (d.value === null && !$$.config.axis_rotated) {
++            if (yPos < box.height) {
++                yPos = box.height;
++            } else if (yPos > this.height) {
++                yPos = this.height - 4;
++            }
++        }
++        return yPos;
++    };
++
++    ChartInternal.prototype.initTitle = function () {
++        var $$ = this;
++        $$.title = $$.svg.append("text").text($$.config.title_text).attr("class", $$.CLASS.title);
++    };
++    ChartInternal.prototype.redrawTitle = function () {
++        var $$ = this;
++        $$.title.attr("x", $$.xForTitle.bind($$)).attr("y", $$.yForTitle.bind($$));
++    };
++    ChartInternal.prototype.xForTitle = function () {
++        var $$ = this,
++            config = $$.config,
++            position = config.title_position || 'left',
++            x;
++        if (position.indexOf('right') >= 0) {
++            x = $$.currentWidth - $$.getTextRect($$.title.node().textContent, $$.CLASS.title, $$.title.node()).width - config.title_padding.right;
++        } else if (position.indexOf('center') >= 0) {
++            x = ($$.currentWidth - $$.getTextRect($$.title.node().textContent, $$.CLASS.title, $$.title.node()).width) / 2;
++        } else {
++            // left
++            x = config.title_padding.left;
++        }
++        return x;
++    };
++    ChartInternal.prototype.yForTitle = function () {
++        var $$ = this;
++        return $$.config.title_padding.top + $$.getTextRect($$.title.node().textContent, $$.CLASS.title, $$.title.node()).height;
++    };
++    ChartInternal.prototype.getTitlePadding = function () {
++        var $$ = this;
++        return $$.yForTitle() + $$.config.title_padding.bottom;
++    };
++
++    ChartInternal.prototype.initTooltip = function () {
++        var $$ = this,
++            config = $$.config,
++            i;
++        $$.tooltip = $$.selectChart.style("position", "relative").append("div").attr('class', CLASS.tooltipContainer).style("position", "absolute").style("pointer-events", "none").style("display", "none");
++        // Show tooltip if needed
++        if (config.tooltip_init_show) {
++            if ($$.isTimeSeries() && isString(config.tooltip_init_x)) {
++                config.tooltip_init_x = $$.parseDate(config.tooltip_init_x);
++                for (i = 0; i < $$.data.targets[0].values.length; i++) {
++                    if ($$.data.targets[0].values[i].x - config.tooltip_init_x === 0) {
++                        break;
++                    }
++                }
++                config.tooltip_init_x = i;
++            }
++            $$.tooltip.html(config.tooltip_contents.call($$, $$.data.targets.map(function (d) {
++                return $$.addName(d.values[config.tooltip_init_x]);
++            }), $$.axis.getXAxisTickFormat(), $$.getYFormat($$.hasArcType()), $$.color));
++            $$.tooltip.style("top", config.tooltip_init_position.top).style("left", config.tooltip_init_position.left).style("display", "block");
++        }
++    };
++    ChartInternal.prototype.getTooltipSortFunction = function () {
++        var $$ = this,
++            config = $$.config;
++
++        if (config.data_groups.length === 0 || config.tooltip_order !== undefined) {
++            // if data are not grouped or if an order is specified
++            // for the tooltip values we sort them by their values
++
++            var order = config.tooltip_order;
++            if (order === undefined) {
++                order = config.data_order;
++            }
++
++            var valueOf = function valueOf(obj) {
++                return obj ? obj.value : null;
++            };
++
++            // if data are not grouped, we sort them by their value
++            if (isString(order) && order.toLowerCase() === 'asc') {
++                return function (a, b) {
++                    return valueOf(a) - valueOf(b);
++                };
++            } else if (isString(order) && order.toLowerCase() === 'desc') {
++                return function (a, b) {
++                    return valueOf(b) - valueOf(a);
++                };
++            } else if (isFunction(order)) {
++
++                // if the function is from data_order we need
++                // to wrap the returned function in order to format
++                // the sorted value to the expected format
++
++                var sortFunction = order;
++
++                if (config.tooltip_order === undefined) {
++                    sortFunction = function sortFunction(a, b) {
++                        return order(a ? {
++                            id: a.id,
++                            values: [a]
++                        } : null, b ? {
++                            id: b.id,
++                            values: [b]
++                        } : null);
++                    };
++                }
++
++                return sortFunction;
++            } else if (isArray(order)) {
++                return function (a, b) {
++                    return order.indexOf(a.id) - order.indexOf(b.id);
++                };
++            }
++        } else {
++            // if data are grouped, we follow the order of grouped targets
++            var ids = $$.orderTargets($$.data.targets).map(function (i) {
++                return i.id;
++            });
++
++            // if it was either asc or desc we need to invert the order
++            // returned by orderTargets
++            if ($$.isOrderAsc() || $$.isOrderDesc()) {
++                ids = ids.reverse();
++            }
++
++            return function (a, b) {
++                return ids.indexOf(a.id) - ids.indexOf(b.id);
++            };
++        }
++    };
++    ChartInternal.prototype.getTooltipContent = function (d, defaultTitleFormat, defaultValueFormat, color) {
++        var $$ = this,
++            config = $$.config,
++            titleFormat = config.tooltip_format_title || defaultTitleFormat,
++            nameFormat = config.tooltip_format_name || function (name) {
++            return name;
++        },
++            valueFormat = config.tooltip_format_value || defaultValueFormat,
++            text,
++            i,
++            title,
++            value,
++            name,
++            bgcolor;
++
++        var tooltipSortFunction = this.getTooltipSortFunction();
++        if (tooltipSortFunction) {
++            d.sort(tooltipSortFunction);
++        }
++
++        for (i = 0; i < d.length; i++) {
++            if (!(d[i] && (d[i].value || d[i].value === 0))) {
++                continue;
++            }
++
++            if (!text) {
++                title = sanitise(titleFormat ? titleFormat(d[i].x) : d[i].x);
++                text = "<table class='" + $$.CLASS.tooltip + "'>" + (title || title === 0 ? "<tr><th colspan='2'>" + title + "</th></tr>" : "");
++            }
++
++            value = sanitise(valueFormat(d[i].value, d[i].ratio, d[i].id, d[i].index, d));
++            if (value !== undefined) {
++                // Skip elements when their name is set to null
++                if (d[i].name === null) {
++                    continue;
++                }
++                name = sanitise(nameFormat(d[i].name, d[i].ratio, d[i].id, d[i].index));
++                bgcolor = $$.levelColor ? $$.levelColor(d[i].value) : color(d[i].id);
++
++                text += "<tr class='" + $$.CLASS.tooltipName + "-" + $$.getTargetSelectorSuffix(d[i].id) + "'>";
++                text += "<td class='name'><span style='background-color:" + bgcolor + "'></span>" + name + "</td>";
++                text += "<td class='value'>" + value + "</td>";
++                text += "</tr>";
++            }
++        }
++        return text + "</table>";
++    };
++    ChartInternal.prototype.tooltipPosition = function (dataToShow, tWidth, tHeight, element) {
++        var $$ = this,
++            config = $$.config,
++            d3 = $$.d3;
++        var svgLeft, tooltipLeft, tooltipRight, tooltipTop, chartRight;
++        var forArc = $$.hasArcType(),
++            mouse = d3.mouse(element);
++        // Determin tooltip position
++        if (forArc) {
++            tooltipLeft = ($$.width - ($$.isLegendRight ? $$.getLegendWidth() : 0)) / 2 + mouse[0];
++            tooltipTop = ($$.hasType('gauge') ? $$.height : $$.height / 2) + mouse[1] + 20;
++        } else {
++            svgLeft = $$.getSvgLeft(true);
++            if (config.axis_rotated) {
++                tooltipLeft = svgLeft + mouse[0] + 100;
++                tooltipRight = tooltipLeft + tWidth;
++                chartRight = $$.currentWidth - $$.getCurrentPaddingRight();
++                tooltipTop = $$.x(dataToShow[0].x) + 20;
++            } else {
++                tooltipLeft = svgLeft + $$.getCurrentPaddingLeft(true) + $$.x(dataToShow[0].x) + 20;
++                tooltipRight = tooltipLeft + tWidth;
++                chartRight = svgLeft + $$.currentWidth - $$.getCurrentPaddingRight();
++                tooltipTop = mouse[1] + 15;
++            }
++
++            if (tooltipRight > chartRight) {
++                // 20 is needed for Firefox to keep tooltip width
++                tooltipLeft -= tooltipRight - chartRight + 20;
++            }
++            if (tooltipTop + tHeight > $$.currentHeight) {
++                tooltipTop -= tHeight + 30;
++            }
++        }
++        if (tooltipTop < 0) {
++            tooltipTop = 0;
++        }
++        return {
++            top: tooltipTop,
++            left: tooltipLeft
++        };
++    };
++    ChartInternal.prototype.showTooltip = function (selectedData, element) {
++        var $$ = this,
++            config = $$.config;
++        var tWidth, tHeight, position;
++        var forArc = $$.hasArcType(),
++            dataToShow = selectedData.filter(function (d) {
++            return d && isValue(d.value);
++        }),
++            positionFunction = config.tooltip_position || ChartInternal.prototype.tooltipPosition;
++        if (dataToShow.length === 0 || !config.tooltip_show) {
++            return;
++        }
++        $$.tooltip.html(config.tooltip_contents.call($$, selectedData, $$.axis.getXAxisTickFormat(), $$.getYFormat(forArc), $$.color)).style("display", "block");
++
++        // Get tooltip dimensions
++        tWidth = $$.tooltip.property('offsetWidth');
++        tHeight = $$.tooltip.property('offsetHeight');
++
++        position = positionFunction.call(this, dataToShow, tWidth, tHeight, element);
++        // Set tooltip
++        $$.tooltip.style("top", position.top + "px").style("left", position.left + 'px');
++    };
++    ChartInternal.prototype.hideTooltip = function () {
++        this.tooltip.style("display", "none");
++    };
++
++    ChartInternal.prototype.setTargetType = function (targetIds, type) {
++        var $$ = this,
++            config = $$.config;
++        $$.mapToTargetIds(targetIds).forEach(function (id) {
++            $$.withoutFadeIn[id] = type === config.data_types[id];
++            config.data_types[id] = type;
++        });
++        if (!targetIds) {
++            config.data_type = type;
++        }
++    };
++    ChartInternal.prototype.hasType = function (type, targets) {
++        var $$ = this,
++            types = $$.config.data_types,
++            has = false;
++        targets = targets || $$.data.targets;
++        if (targets && targets.length) {
++            targets.forEach(function (target) {
++                var t = types[target.id];
++                if (t && t.indexOf(type) >= 0 || !t && type === 'line') {
++                    has = true;
++                }
++            });
++        } else if (Object.keys(types).length) {
++            Object.keys(types).forEach(function (id) {
++                if (types[id] === type) {
++                    has = true;
++                }
++            });
++        } else {
++            has = $$.config.data_type === type;
++        }
++        return has;
++    };
++    ChartInternal.prototype.hasArcType = function (targets) {
++        return this.hasType('pie', targets) || this.hasType('donut', targets) || this.hasType('gauge', targets);
++    };
++    ChartInternal.prototype.isLineType = function (d) {
++        var config = this.config,
++            id = isString(d) ? d : d.id;
++        return !config.data_types[id] || ['line', 'spline', 'area', 'area-spline', 'step', 'area-step'].indexOf(config.data_types[id]) >= 0;
++    };
++    ChartInternal.prototype.isStepType = function (d) {
++        var id = isString(d) ? d : d.id;
++        return ['step', 'area-step'].indexOf(this.config.data_types[id]) >= 0;
++    };
++    ChartInternal.prototype.isSplineType = function (d) {
++        var id = isString(d) ? d : d.id;
++        return ['spline', 'area-spline'].indexOf(this.config.data_types[id]) >= 0;
++    };
++    ChartInternal.prototype.isAreaType = function (d) {
++        var id = isString(d) ? d : d.id;
++        return ['area', 'area-spline', 'area-step'].indexOf(this.config.data_types[id]) >= 0;
++    };
++    ChartInternal.prototype.isBarType = function (d) {
++        var id = isString(d) ? d : d.id;
++        return this.config.data_types[id] === 'bar';
++    };
++    ChartInternal.prototype.isScatterType = function (d) {
++        var id = isString(d) ? d : d.id;
++        return this.config.data_types[id] === 'scatter';
++    };
++    ChartInternal.prototype.isPieType = function (d) {
++        var id = isString(d) ? d : d.id;
++        return this.config.data_types[id] === 'pie';
++    };
++    ChartInternal.prototype.isGaugeType = function (d) {
++        var id = isString(d) ? d : d.id;
++        return this.config.data_types[id] === 'gauge';
++    };
++    ChartInternal.prototype.isDonutType = function (d) {
++        var id = isString(d) ? d : d.id;
++        return this.config.data_types[id] === 'donut';
++    };
++    ChartInternal.prototype.isArcType = function (d) {
++        return this.isPieType(d) || this.isDonutType(d) || this.isGaugeType(d);
++    };
++    ChartInternal.prototype.lineData = function (d) {
++        return this.isLineType(d) ? [d] : [];
++    };
++    ChartInternal.prototype.arcData = function (d) {
++        return this.isArcType(d.data) ? [d] : [];
++    };
++    /* not used
++     function scatterData(d) {
++     return isScatterType(d) ? d.values : [];
++     }
++     */
++    ChartInternal.prototype.barData = function (d) {
++        return this.isBarType(d) ? d.values : [];
++    };
++    ChartInternal.prototype.lineOrScatterData = function (d) {
++        return this.isLineType(d) || this.isScatterType(d) ? d.values : [];
++    };
++    ChartInternal.prototype.barOrLineData = function (d) {
++        return this.isBarType(d) || this.isLineType(d) ? d.values : [];
++    };
++
++    ChartInternal.prototype.isSafari = function () {
++        var ua = window.navigator.userAgent;
++        return ua.indexOf('Safari') >= 0 && ua.indexOf('Chrome') < 0;
++    };
++    ChartInternal.prototype.isChrome = function () {
++        var ua = window.navigator.userAgent;
++        return ua.indexOf('Chrome') >= 0;
++    };
++
++    ChartInternal.prototype.initZoom = function () {
++        var $$ = this,
++            d3 = $$.d3,
++            config = $$.config,
++            startEvent;
++
++        $$.zoom = d3.zoom().on("start", function () {
++            var e = d3.event.sourceEvent;
++            if (e && e.type === "brush") {
++                return;
++            }
++            startEvent = e;
++            config.zoom_onzoomstart.call($$.api, e);
++        }).on("zoom", function () {
++            var e = d3.event.sourceEvent;
++            if (e && e.type === "brush") {
++                return;
++            }
++            $$.redrawForZoom.call($$);
++        }).on('end', function () {
++            var e = d3.event.sourceEvent;
++            if (e && e.type === "brush") {
++                return;
++            }
++            // if click, do nothing. otherwise, click interaction will be canceled.
++            if (e && startEvent.clientX === e.clientX && startEvent.clientY === e.clientY) {
++                return;
++            }
++            config.zoom_onzoomend.call($$.api, $$.x.orgDomain());
++        });
++
++        $$.zoom.updateDomain = function () {
++            if (d3.event && d3.event.transform) {
++                $$.x.domain(d3.event.transform.rescaleX($$.subX).domain());
++            }
++            return this;
++        };
++        $$.zoom.updateExtent = function () {
++            this.scaleExtent([1, Infinity]).translateExtent([[0, 0], [$$.width, $$.height]]).extent([[0, 0], [$$.width, $$.height]]);
++            return this;
++        };
++        $$.zoom.update = function () {
++            return this.updateExtent().updateDomain();
++        };
++
++        return $$.zoom.updateExtent();
++    };
++    ChartInternal.prototype.zoomTransform = function (range) {
++        var $$ = this,
++            s = [$$.x(range[0]), $$.x(range[1])];
++        return $$.d3.zoomIdentity.scale($$.width / (s[1] - s[0])).translate(-s[0], 0);
++    };
++
++    ChartInternal.prototype.getZoomDomain = function () {
++        var $$ = this,
++            config = $$.config,
++            d3 = $$.d3,
++            min = d3.min([$$.orgXDomain[0], config.zoom_x_min]),
++            max = d3.max([$$.orgXDomain[1], config.zoom_x_max]);
++        return [min, max];
++    };
++    ChartInternal.prototype.redrawForZoom = function () {
++        var $$ = this,
++            d3 = $$.d3,
++            config = $$.config,
++            zoom = $$.zoom,
++            x = $$.x;
++        if (!config.zoom_enabled) {
++            return;
++        }
++        if ($$.filterTargetsToShow($$.data.targets).length === 0) {
++            return;
++        }
++
++        zoom.update();
++
++        if ($$.isCategorized() && x.orgDomain()[0] === $$.orgXDomain[0]) {
++            x.domain([$$.orgXDomain[0] - 1e-10, x.orgDomain()[1]]);
++        }
++        $$.redraw({
++            withTransition: false,
++            withY: config.zoom_rescale,
++            withSubchart: false,
++            withEventRect: false,
++            withDimension: false
++        });
++        if (d3.event.sourceEvent && d3.event.sourceEvent.type === 'mousemove') {
++            $$.cancelClick = true;
++        }
++        config.zoom_onzoom.call($$.api, x.orgDomain());
++    };
++
++    return c3;
++
++})));
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e6e7e302ebb099da4fdd34b18fe9b2c64cb31541
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17847 @@@
++// https://d3js.org Version 5.5.0. Copyright 2018 Mike Bostock.
++(function (global, factory) {
++      typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
++      typeof define === 'function' && define.amd ? define(['exports'], factory) :
++      (factory((global.d3 = global.d3 || {})));
++}(this, (function (exports) { 'use strict';
++
++var version = "5.5.0";
++
++function ascending(a, b) {
++  return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
++}
++
++function bisector(compare) {
++  if (compare.length === 1) compare = ascendingComparator(compare);
++  return {
++    left: function(a, x, lo, hi) {
++      if (lo == null) lo = 0;
++      if (hi == null) hi = a.length;
++      while (lo < hi) {
++        var mid = lo + hi >>> 1;
++        if (compare(a[mid], x) < 0) lo = mid + 1;
++        else hi = mid;
++      }
++      return lo;
++    },
++    right: function(a, x, lo, hi) {
++      if (lo == null) lo = 0;
++      if (hi == null) hi = a.length;
++      while (lo < hi) {
++        var mid = lo + hi >>> 1;
++        if (compare(a[mid], x) > 0) hi = mid;
++        else lo = mid + 1;
++      }
++      return lo;
++    }
++  };
++}
++
++function ascendingComparator(f) {
++  return function(d, x) {
++    return ascending(f(d), x);
++  };
++}
++
++var ascendingBisect = bisector(ascending);
++var bisectRight = ascendingBisect.right;
++var bisectLeft = ascendingBisect.left;
++
++function pairs(array, f) {
++  if (f == null) f = pair;
++  var i = 0, n = array.length - 1, p = array[0], pairs = new Array(n < 0 ? 0 : n);
++  while (i < n) pairs[i] = f(p, p = array[++i]);
++  return pairs;
++}
++
++function pair(a, b) {
++  return [a, b];
++}
++
++function cross(values0, values1, reduce) {
++  var n0 = values0.length,
++      n1 = values1.length,
++      values = new Array(n0 * n1),
++      i0,
++      i1,
++      i,
++      value0;
++
++  if (reduce == null) reduce = pair;
++
++  for (i0 = i = 0; i0 < n0; ++i0) {
++    for (value0 = values0[i0], i1 = 0; i1 < n1; ++i1, ++i) {
++      values[i] = reduce(value0, values1[i1]);
++    }
++  }
++
++  return values;
++}
++
++function descending(a, b) {
++  return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
++}
++
++function number(x) {
++  return x === null ? NaN : +x;
++}
++
++function variance(values, valueof) {
++  var n = values.length,
++      m = 0,
++      i = -1,
++      mean = 0,
++      value,
++      delta,
++      sum = 0;
++
++  if (valueof == null) {
++    while (++i < n) {
++      if (!isNaN(value = number(values[i]))) {
++        delta = value - mean;
++        mean += delta / ++m;
++        sum += delta * (value - mean);
++      }
++    }
++  }
++
++  else {
++    while (++i < n) {
++      if (!isNaN(value = number(valueof(values[i], i, values)))) {
++        delta = value - mean;
++        mean += delta / ++m;
++        sum += delta * (value - mean);
++      }
++    }
++  }
++
++  if (m > 1) return sum / (m - 1);
++}
++
++function deviation(array, f) {
++  var v = variance(array, f);
++  return v ? Math.sqrt(v) : v;
++}
++
++function extent(values, valueof) {
++  var n = values.length,
++      i = -1,
++      value,
++      min,
++      max;
++
++  if (valueof == null) {
++    while (++i < n) { // Find the first comparable value.
++      if ((value = values[i]) != null && value >= value) {
++        min = max = value;
++        while (++i < n) { // Compare the remaining values.
++          if ((value = values[i]) != null) {
++            if (min > value) min = value;
++            if (max < value) max = value;
++          }
++        }
++      }
++    }
++  }
++
++  else {
++    while (++i < n) { // Find the first comparable value.
++      if ((value = valueof(values[i], i, values)) != null && value >= value) {
++        min = max = value;
++        while (++i < n) { // Compare the remaining values.
++          if ((value = valueof(values[i], i, values)) != null) {
++            if (min > value) min = value;
++            if (max < value) max = value;
++          }
++        }
++      }
++    }
++  }
++
++  return [min, max];
++}
++
++var array = Array.prototype;
++
++var slice = array.slice;
++var map = array.map;
++
++function constant(x) {
++  return function() {
++    return x;
++  };
++}
++
++function identity(x) {
++  return x;
++}
++
++function sequence(start, stop, step) {
++  start = +start, stop = +stop, step = (n = arguments.length) < 2 ? (stop = start, start = 0, 1) : n < 3 ? 1 : +step;
++
++  var i = -1,
++      n = Math.max(0, Math.ceil((stop - start) / step)) | 0,
++      range = new Array(n);
++
++  while (++i < n) {
++    range[i] = start + i * step;
++  }
++
++  return range;
++}
++
++var e10 = Math.sqrt(50),
++    e5 = Math.sqrt(10),
++    e2 = Math.sqrt(2);
++
++function ticks(start, stop, count) {
++  var reverse,
++      i = -1,
++      n,
++      ticks,
++      step;
++
++  stop = +stop, start = +start, count = +count;
++  if (start === stop && count > 0) return [start];
++  if (reverse = stop < start) n = start, start = stop, stop = n;
++  if ((step = tickIncrement(start, stop, count)) === 0 || !isFinite(step)) return [];
++
++  if (step > 0) {
++    start = Math.ceil(start / step);
++    stop = Math.floor(stop / step);
++    ticks = new Array(n = Math.ceil(stop - start + 1));
++    while (++i < n) ticks[i] = (start + i) * step;
++  } else {
++    start = Math.floor(start * step);
++    stop = Math.ceil(stop * step);
++    ticks = new Array(n = Math.ceil(start - stop + 1));
++    while (++i < n) ticks[i] = (start - i) / step;
++  }
++
++  if (reverse) ticks.reverse();
++
++  return ticks;
++}
++
++function tickIncrement(start, stop, count) {
++  var step = (stop - start) / Math.max(0, count),
++      power = Math.floor(Math.log(step) / Math.LN10),
++      error = step / Math.pow(10, power);
++  return power >= 0
++      ? (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1) * Math.pow(10, power)
++      : -Math.pow(10, -power) / (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1);
++}
++
++function tickStep(start, stop, count) {
++  var step0 = Math.abs(stop - start) / Math.max(0, count),
++      step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)),
++      error = step0 / step1;
++  if (error >= e10) step1 *= 10;
++  else if (error >= e5) step1 *= 5;
++  else if (error >= e2) step1 *= 2;
++  return stop < start ? -step1 : step1;
++}
++
++function thresholdSturges(values) {
++  return Math.ceil(Math.log(values.length) / Math.LN2) + 1;
++}
++
++function histogram() {
++  var value = identity,
++      domain = extent,
++      threshold = thresholdSturges;
++
++  function histogram(data) {
++    var i,
++        n = data.length,
++        x,
++        values = new Array(n);
++
++    for (i = 0; i < n; ++i) {
++      values[i] = value(data[i], i, data);
++    }
++
++    var xz = domain(values),
++        x0 = xz[0],
++        x1 = xz[1],
++        tz = threshold(values, x0, x1);
++
++    // Convert number of thresholds into uniform thresholds.
++    if (!Array.isArray(tz)) {
++      tz = tickStep(x0, x1, tz);
++      tz = sequence(Math.ceil(x0 / tz) * tz, Math.floor(x1 / tz) * tz, tz); // exclusive
++    }
++
++    // Remove any thresholds outside the domain.
++    var m = tz.length;
++    while (tz[0] <= x0) tz.shift(), --m;
++    while (tz[m - 1] > x1) tz.pop(), --m;
++
++    var bins = new Array(m + 1),
++        bin;
++
++    // Initialize bins.
++    for (i = 0; i <= m; ++i) {
++      bin = bins[i] = [];
++      bin.x0 = i > 0 ? tz[i - 1] : x0;
++      bin.x1 = i < m ? tz[i] : x1;
++    }
++
++    // Assign data to bins by value, ignoring any outside the domain.
++    for (i = 0; i < n; ++i) {
++      x = values[i];
++      if (x0 <= x && x <= x1) {
++        bins[bisectRight(tz, x, 0, m)].push(data[i]);
++      }
++    }
++
++    return bins;
++  }
++
++  histogram.value = function(_) {
++    return arguments.length ? (value = typeof _ === "function" ? _ : constant(_), histogram) : value;
++  };
++
++  histogram.domain = function(_) {
++    return arguments.length ? (domain = typeof _ === "function" ? _ : constant([_[0], _[1]]), histogram) : domain;
++  };
++
++  histogram.thresholds = function(_) {
++    return arguments.length ? (threshold = typeof _ === "function" ? _ : Array.isArray(_) ? constant(slice.call(_)) : constant(_), histogram) : threshold;
++  };
++
++  return histogram;
++}
++
++function threshold(values, p, valueof) {
++  if (valueof == null) valueof = number;
++  if (!(n = values.length)) return;
++  if ((p = +p) <= 0 || n < 2) return +valueof(values[0], 0, values);
++  if (p >= 1) return +valueof(values[n - 1], n - 1, values);
++  var n,
++      i = (n - 1) * p,
++      i0 = Math.floor(i),
++      value0 = +valueof(values[i0], i0, values),
++      value1 = +valueof(values[i0 + 1], i0 + 1, values);
++  return value0 + (value1 - value0) * (i - i0);
++}
++
++function freedmanDiaconis(values, min, max) {
++  values = map.call(values, number).sort(ascending);
++  return Math.ceil((max - min) / (2 * (threshold(values, 0.75) - threshold(values, 0.25)) * Math.pow(values.length, -1 / 3)));
++}
++
++function scott(values, min, max) {
++  return Math.ceil((max - min) / (3.5 * deviation(values) * Math.pow(values.length, -1 / 3)));
++}
++
++function max(values, valueof) {
++  var n = values.length,
++      i = -1,
++      value,
++      max;
++
++  if (valueof == null) {
++    while (++i < n) { // Find the first comparable value.
++      if ((value = values[i]) != null && value >= value) {
++        max = value;
++        while (++i < n) { // Compare the remaining values.
++          if ((value = values[i]) != null && value > max) {
++            max = value;
++          }
++        }
++      }
++    }
++  }
++
++  else {
++    while (++i < n) { // Find the first comparable value.
++      if ((value = valueof(values[i], i, values)) != null && value >= value) {
++        max = value;
++        while (++i < n) { // Compare the remaining values.
++          if ((value = valueof(values[i], i, values)) != null && value > max) {
++            max = value;
++          }
++        }
++      }
++    }
++  }
++
++  return max;
++}
++
++function mean(values, valueof) {
++  var n = values.length,
++      m = n,
++      i = -1,
++      value,
++      sum = 0;
++
++  if (valueof == null) {
++    while (++i < n) {
++      if (!isNaN(value = number(values[i]))) sum += value;
++      else --m;
++    }
++  }
++
++  else {
++    while (++i < n) {
++      if (!isNaN(value = number(valueof(values[i], i, values)))) sum += value;
++      else --m;
++    }
++  }
++
++  if (m) return sum / m;
++}
++
++function median(values, valueof) {
++  var n = values.length,
++      i = -1,
++      value,
++      numbers = [];
++
++  if (valueof == null) {
++    while (++i < n) {
++      if (!isNaN(value = number(values[i]))) {
++        numbers.push(value);
++      }
++    }
++  }
++
++  else {
++    while (++i < n) {
++      if (!isNaN(value = number(valueof(values[i], i, values)))) {
++        numbers.push(value);
++      }
++    }
++  }
++
++  return threshold(numbers.sort(ascending), 0.5);
++}
++
++function merge(arrays) {
++  var n = arrays.length,
++      m,
++      i = -1,
++      j = 0,
++      merged,
++      array;
++
++  while (++i < n) j += arrays[i].length;
++  merged = new Array(j);
++
++  while (--n >= 0) {
++    array = arrays[n];
++    m = array.length;
++    while (--m >= 0) {
++      merged[--j] = array[m];
++    }
++  }
++
++  return merged;
++}
++
++function min(values, valueof) {
++  var n = values.length,
++      i = -1,
++      value,
++      min;
++
++  if (valueof == null) {
++    while (++i < n) { // Find the first comparable value.
++      if ((value = values[i]) != null && value >= value) {
++        min = value;
++        while (++i < n) { // Compare the remaining values.
++          if ((value = values[i]) != null && min > value) {
++            min = value;
++          }
++        }
++      }
++    }
++  }
++
++  else {
++    while (++i < n) { // Find the first comparable value.
++      if ((value = valueof(values[i], i, values)) != null && value >= value) {
++        min = value;
++        while (++i < n) { // Compare the remaining values.
++          if ((value = valueof(values[i], i, values)) != null && min > value) {
++            min = value;
++          }
++        }
++      }
++    }
++  }
++
++  return min;
++}
++
++function permute(array, indexes) {
++  var i = indexes.length, permutes = new Array(i);
++  while (i--) permutes[i] = array[indexes[i]];
++  return permutes;
++}
++
++function scan(values, compare) {
++  if (!(n = values.length)) return;
++  var n,
++      i = 0,
++      j = 0,
++      xi,
++      xj = values[j];
++
++  if (compare == null) compare = ascending;
++
++  while (++i < n) {
++    if (compare(xi = values[i], xj) < 0 || compare(xj, xj) !== 0) {
++      xj = xi, j = i;
++    }
++  }
++
++  if (compare(xj, xj) === 0) return j;
++}
++
++function shuffle(array, i0, i1) {
++  var m = (i1 == null ? array.length : i1) - (i0 = i0 == null ? 0 : +i0),
++      t,
++      i;
++
++  while (m) {
++    i = Math.random() * m-- | 0;
++    t = array[m + i0];
++    array[m + i0] = array[i + i0];
++    array[i + i0] = t;
++  }
++
++  return array;
++}
++
++function sum(values, valueof) {
++  var n = values.length,
++      i = -1,
++      value,
++      sum = 0;
++
++  if (valueof == null) {
++    while (++i < n) {
++      if (value = +values[i]) sum += value; // Note: zero and null are equivalent.
++    }
++  }
++
++  else {
++    while (++i < n) {
++      if (value = +valueof(values[i], i, values)) sum += value;
++    }
++  }
++
++  return sum;
++}
++
++function transpose(matrix) {
++  if (!(n = matrix.length)) return [];
++  for (var i = -1, m = min(matrix, length), transpose = new Array(m); ++i < m;) {
++    for (var j = -1, n, row = transpose[i] = new Array(n); ++j < n;) {
++      row[j] = matrix[j][i];
++    }
++  }
++  return transpose;
++}
++
++function length(d) {
++  return d.length;
++}
++
++function zip() {
++  return transpose(arguments);
++}
++
++var slice$1 = Array.prototype.slice;
++
++function identity$1(x) {
++  return x;
++}
++
++var top = 1,
++    right = 2,
++    bottom = 3,
++    left = 4,
++    epsilon = 1e-6;
++
++function translateX(x) {
++  return "translate(" + (x + 0.5) + ",0)";
++}
++
++function translateY(y) {
++  return "translate(0," + (y + 0.5) + ")";
++}
++
++function number$1(scale) {
++  return function(d) {
++    return +scale(d);
++  };
++}
++
++function center(scale) {
++  var offset = Math.max(0, scale.bandwidth() - 1) / 2; // Adjust for 0.5px offset.
++  if (scale.round()) offset = Math.round(offset);
++  return function(d) {
++    return +scale(d) + offset;
++  };
++}
++
++function entering() {
++  return !this.__axis;
++}
++
++function axis(orient, scale) {
++  var tickArguments = [],
++      tickValues = null,
++      tickFormat = null,
++      tickSizeInner = 6,
++      tickSizeOuter = 6,
++      tickPadding = 3,
++      k = orient === top || orient === left ? -1 : 1,
++      x = orient === left || orient === right ? "x" : "y",
++      transform = orient === top || orient === bottom ? translateX : translateY;
++
++  function axis(context) {
++    var values = tickValues == null ? (scale.ticks ? scale.ticks.apply(scale, tickArguments) : scale.domain()) : tickValues,
++        format = tickFormat == null ? (scale.tickFormat ? scale.tickFormat.apply(scale, tickArguments) : identity$1) : tickFormat,
++        spacing = Math.max(tickSizeInner, 0) + tickPadding,
++        range = scale.range(),
++        range0 = +range[0] + 0.5,
++        range1 = +range[range.length - 1] + 0.5,
++        position = (scale.bandwidth ? center : number$1)(scale.copy()),
++        selection = context.selection ? context.selection() : context,
++        path = selection.selectAll(".domain").data([null]),
++        tick = selection.selectAll(".tick").data(values, scale).order(),
++        tickExit = tick.exit(),
++        tickEnter = tick.enter().append("g").attr("class", "tick"),
++        line = tick.select("line"),
++        text = tick.select("text");
++
++    path = path.merge(path.enter().insert("path", ".tick")
++        .attr("class", "domain")
++        .attr("stroke", "#000"));
++
++    tick = tick.merge(tickEnter);
++
++    line = line.merge(tickEnter.append("line")
++        .attr("stroke", "#000")
++        .attr(x + "2", k * tickSizeInner));
++
++    text = text.merge(tickEnter.append("text")
++        .attr("fill", "#000")
++        .attr(x, k * spacing)
++        .attr("dy", orient === top ? "0em" : orient === bottom ? "0.71em" : "0.32em"));
++
++    if (context !== selection) {
++      path = path.transition(context);
++      tick = tick.transition(context);
++      line = line.transition(context);
++      text = text.transition(context);
++
++      tickExit = tickExit.transition(context)
++          .attr("opacity", epsilon)
++          .attr("transform", function(d) { return isFinite(d = position(d)) ? transform(d) : this.getAttribute("transform"); });
++
++      tickEnter
++          .attr("opacity", epsilon)
++          .attr("transform", function(d) { var p = this.parentNode.__axis; return transform(p && isFinite(p = p(d)) ? p : position(d)); });
++    }
++
++    tickExit.remove();
++
++    path
++        .attr("d", orient === left || orient == right
++            ? "M" + k * tickSizeOuter + "," + range0 + "H0.5V" + range1 + "H" + k * tickSizeOuter
++            : "M" + range0 + "," + k * tickSizeOuter + "V0.5H" + range1 + "V" + k * tickSizeOuter);
++
++    tick
++        .attr("opacity", 1)
++        .attr("transform", function(d) { return transform(position(d)); });
++
++    line
++        .attr(x + "2", k * tickSizeInner);
++
++    text
++        .attr(x, k * spacing)
++        .text(format);
++
++    selection.filter(entering)
++        .attr("fill", "none")
++        .attr("font-size", 10)
++        .attr("font-family", "sans-serif")
++        .attr("text-anchor", orient === right ? "start" : orient === left ? "end" : "middle");
++
++    selection
++        .each(function() { this.__axis = position; });
++  }
++
++  axis.scale = function(_) {
++    return arguments.length ? (scale = _, axis) : scale;
++  };
++
++  axis.ticks = function() {
++    return tickArguments = slice$1.call(arguments), axis;
++  };
++
++  axis.tickArguments = function(_) {
++    return arguments.length ? (tickArguments = _ == null ? [] : slice$1.call(_), axis) : tickArguments.slice();
++  };
++
++  axis.tickValues = function(_) {
++    return arguments.length ? (tickValues = _ == null ? null : slice$1.call(_), axis) : tickValues && tickValues.slice();
++  };
++
++  axis.tickFormat = function(_) {
++    return arguments.length ? (tickFormat = _, axis) : tickFormat;
++  };
++
++  axis.tickSize = function(_) {
++    return arguments.length ? (tickSizeInner = tickSizeOuter = +_, axis) : tickSizeInner;
++  };
++
++  axis.tickSizeInner = function(_) {
++    return arguments.length ? (tickSizeInner = +_, axis) : tickSizeInner;
++  };
++
++  axis.tickSizeOuter = function(_) {
++    return arguments.length ? (tickSizeOuter = +_, axis) : tickSizeOuter;
++  };
++
++  axis.tickPadding = function(_) {
++    return arguments.length ? (tickPadding = +_, axis) : tickPadding;
++  };
++
++  return axis;
++}
++
++function axisTop(scale) {
++  return axis(top, scale);
++}
++
++function axisRight(scale) {
++  return axis(right, scale);
++}
++
++function axisBottom(scale) {
++  return axis(bottom, scale);
++}
++
++function axisLeft(scale) {
++  return axis(left, scale);
++}
++
++var noop = {value: function() {}};
++
++function dispatch() {
++  for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) {
++    if (!(t = arguments[i] + "") || (t in _)) throw new Error("illegal type: " + t);
++    _[t] = [];
++  }
++  return new Dispatch(_);
++}
++
++function Dispatch(_) {
++  this._ = _;
++}
++
++function parseTypenames(typenames, types) {
++  return typenames.trim().split(/^|\s+/).map(function(t) {
++    var name = "", i = t.indexOf(".");
++    if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);
++    if (t && !types.hasOwnProperty(t)) throw new Error("unknown type: " + t);
++    return {type: t, name: name};
++  });
++}
++
++Dispatch.prototype = dispatch.prototype = {
++  constructor: Dispatch,
++  on: function(typename, callback) {
++    var _ = this._,
++        T = parseTypenames(typename + "", _),
++        t,
++        i = -1,
++        n = T.length;
++
++    // If no callback was specified, return the callback of the given type and name.
++    if (arguments.length < 2) {
++      while (++i < n) if ((t = (typename = T[i]).type) && (t = get(_[t], typename.name))) return t;
++      return;
++    }
++
++    // If a type was specified, set the callback for the given type and name.
++    // Otherwise, if a null callback was specified, remove callbacks of the given name.
++    if (callback != null && typeof callback !== "function") throw new Error("invalid callback: " + callback);
++    while (++i < n) {
++      if (t = (typename = T[i]).type) _[t] = set(_[t], typename.name, callback);
++      else if (callback == null) for (t in _) _[t] = set(_[t], typename.name, null);
++    }
++
++    return this;
++  },
++  copy: function() {
++    var copy = {}, _ = this._;
++    for (var t in _) copy[t] = _[t].slice();
++    return new Dispatch(copy);
++  },
++  call: function(type, that) {
++    if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) args[i] = arguments[i + 2];
++    if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type);
++    for (t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);
++  },
++  apply: function(type, that, args) {
++    if (!this._.hasOwnProperty(type)) throw new Error("unknown type: " + type);
++    for (var t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);
++  }
++};
++
++function get(type, name) {
++  for (var i = 0, n = type.length, c; i < n; ++i) {
++    if ((c = type[i]).name === name) {
++      return c.value;
++    }
++  }
++}
++
++function set(type, name, callback) {
++  for (var i = 0, n = type.length; i < n; ++i) {
++    if (type[i].name === name) {
++      type[i] = noop, type = type.slice(0, i).concat(type.slice(i + 1));
++      break;
++    }
++  }
++  if (callback != null) type.push({name: name, value: callback});
++  return type;
++}
++
++var xhtml = "http://www.w3.org/1999/xhtml";
++
++var namespaces = {
++  svg: "http://www.w3.org/2000/svg",
++  xhtml: xhtml,
++  xlink: "http://www.w3.org/1999/xlink",
++  xml: "http://www.w3.org/XML/1998/namespace",
++  xmlns: "http://www.w3.org/2000/xmlns/"
++};
++
++function namespace(name) {
++  var prefix = name += "", i = prefix.indexOf(":");
++  if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1);
++  return namespaces.hasOwnProperty(prefix) ? {space: namespaces[prefix], local: name} : name;
++}
++
++function creatorInherit(name) {
++  return function() {
++    var document = this.ownerDocument,
++        uri = this.namespaceURI;
++    return uri === xhtml && document.documentElement.namespaceURI === xhtml
++        ? document.createElement(name)
++        : document.createElementNS(uri, name);
++  };
++}
++
++function creatorFixed(fullname) {
++  return function() {
++    return this.ownerDocument.createElementNS(fullname.space, fullname.local);
++  };
++}
++
++function creator(name) {
++  var fullname = namespace(name);
++  return (fullname.local
++      ? creatorFixed
++      : creatorInherit)(fullname);
++}
++
++function none() {}
++
++function selector(selector) {
++  return selector == null ? none : function() {
++    return this.querySelector(selector);
++  };
++}
++
++function selection_select(select) {
++  if (typeof select !== "function") select = selector(select);
++
++  for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
++    for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {
++      if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {
++        if ("__data__" in node) subnode.__data__ = node.__data__;
++        subgroup[i] = subnode;
++      }
++    }
++  }
++
++  return new Selection(subgroups, this._parents);
++}
++
++function empty() {
++  return [];
++}
++
++function selectorAll(selector) {
++  return selector == null ? empty : function() {
++    return this.querySelectorAll(selector);
++  };
++}
++
++function selection_selectAll(select) {
++  if (typeof select !== "function") select = selectorAll(select);
++
++  for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {
++    for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
++      if (node = group[i]) {
++        subgroups.push(select.call(node, node.__data__, i, group));
++        parents.push(node);
++      }
++    }
++  }
++
++  return new Selection(subgroups, parents);
++}
++
++var matcher = function(selector) {
++  return function() {
++    return this.matches(selector);
++  };
++};
++
++if (typeof document !== "undefined") {
++  var element = document.documentElement;
++  if (!element.matches) {
++    var vendorMatches = element.webkitMatchesSelector
++        || element.msMatchesSelector
++        || element.mozMatchesSelector
++        || element.oMatchesSelector;
++    matcher = function(selector) {
++      return function() {
++        return vendorMatches.call(this, selector);
++      };
++    };
++  }
++}
++
++var matcher$1 = matcher;
++
++function selection_filter(match) {
++  if (typeof match !== "function") match = matcher$1(match);
++
++  for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
++    for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {
++      if ((node = group[i]) && match.call(node, node.__data__, i, group)) {
++        subgroup.push(node);
++      }
++    }
++  }
++
++  return new Selection(subgroups, this._parents);
++}
++
++function sparse(update) {
++  return new Array(update.length);
++}
++
++function selection_enter() {
++  return new Selection(this._enter || this._groups.map(sparse), this._parents);
++}
++
++function EnterNode(parent, datum) {
++  this.ownerDocument = parent.ownerDocument;
++  this.namespaceURI = parent.namespaceURI;
++  this._next = null;
++  this._parent = parent;
++  this.__data__ = datum;
++}
++
++EnterNode.prototype = {
++  constructor: EnterNode,
++  appendChild: function(child) { return this._parent.insertBefore(child, this._next); },
++  insertBefore: function(child, next) { return this._parent.insertBefore(child, next); },
++  querySelector: function(selector) { return this._parent.querySelector(selector); },
++  querySelectorAll: function(selector) { return this._parent.querySelectorAll(selector); }
++};
++
++function constant$1(x) {
++  return function() {
++    return x;
++  };
++}
++
++var keyPrefix = "$"; // Protect against keys like “__proto__”.
++
++function bindIndex(parent, group, enter, update, exit, data) {
++  var i = 0,
++      node,
++      groupLength = group.length,
++      dataLength = data.length;
++
++  // Put any non-null nodes that fit into update.
++  // Put any null nodes into enter.
++  // Put any remaining data into enter.
++  for (; i < dataLength; ++i) {
++    if (node = group[i]) {
++      node.__data__ = data[i];
++      update[i] = node;
++    } else {
++      enter[i] = new EnterNode(parent, data[i]);
++    }
++  }
++
++  // Put any non-null nodes that don’t fit into exit.
++  for (; i < groupLength; ++i) {
++    if (node = group[i]) {
++      exit[i] = node;
++    }
++  }
++}
++
++function bindKey(parent, group, enter, update, exit, data, key) {
++  var i,
++      node,
++      nodeByKeyValue = {},
++      groupLength = group.length,
++      dataLength = data.length,
++      keyValues = new Array(groupLength),
++      keyValue;
++
++  // Compute the key for each node.
++  // If multiple nodes have the same key, the duplicates are added to exit.
++  for (i = 0; i < groupLength; ++i) {
++    if (node = group[i]) {
++      keyValues[i] = keyValue = keyPrefix + key.call(node, node.__data__, i, group);
++      if (keyValue in nodeByKeyValue) {
++        exit[i] = node;
++      } else {
++        nodeByKeyValue[keyValue] = node;
++      }
++    }
++  }
++
++  // Compute the key for each datum.
++  // If there a node associated with this key, join and add it to update.
++  // If there is not (or the key is a duplicate), add it to enter.
++  for (i = 0; i < dataLength; ++i) {
++    keyValue = keyPrefix + key.call(parent, data[i], i, data);
++    if (node = nodeByKeyValue[keyValue]) {
++      update[i] = node;
++      node.__data__ = data[i];
++      nodeByKeyValue[keyValue] = null;
++    } else {
++      enter[i] = new EnterNode(parent, data[i]);
++    }
++  }
++
++  // Add any remaining nodes that were not bound to data to exit.
++  for (i = 0; i < groupLength; ++i) {
++    if ((node = group[i]) && (nodeByKeyValue[keyValues[i]] === node)) {
++      exit[i] = node;
++    }
++  }
++}
++
++function selection_data(value, key) {
++  if (!value) {
++    data = new Array(this.size()), j = -1;
++    this.each(function(d) { data[++j] = d; });
++    return data;
++  }
++
++  var bind = key ? bindKey : bindIndex,
++      parents = this._parents,
++      groups = this._groups;
++
++  if (typeof value !== "function") value = constant$1(value);
++
++  for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) {
++    var parent = parents[j],
++        group = groups[j],
++        groupLength = group.length,
++        data = value.call(parent, parent && parent.__data__, j, parents),
++        dataLength = data.length,
++        enterGroup = enter[j] = new Array(dataLength),
++        updateGroup = update[j] = new Array(dataLength),
++        exitGroup = exit[j] = new Array(groupLength);
++
++    bind(parent, group, enterGroup, updateGroup, exitGroup, data, key);
++
++    // Now connect the enter nodes to their following update node, such that
++    // appendChild can insert the materialized enter node before this node,
++    // rather than at the end of the parent node.
++    for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) {
++      if (previous = enterGroup[i0]) {
++        if (i0 >= i1) i1 = i0 + 1;
++        while (!(next = updateGroup[i1]) && ++i1 < dataLength);
++        previous._next = next || null;
++      }
++    }
++  }
++
++  update = new Selection(update, parents);
++  update._enter = enter;
++  update._exit = exit;
++  return update;
++}
++
++function selection_exit() {
++  return new Selection(this._exit || this._groups.map(sparse), this._parents);
++}
++
++function selection_merge(selection$$1) {
++
++  for (var groups0 = this._groups, groups1 = selection$$1._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) {
++    for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {
++      if (node = group0[i] || group1[i]) {
++        merge[i] = node;
++      }
++    }
++  }
++
++  for (; j < m0; ++j) {
++    merges[j] = groups0[j];
++  }
++
++  return new Selection(merges, this._parents);
++}
++
++function selection_order() {
++
++  for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) {
++    for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0;) {
++      if (node = group[i]) {
++        if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next);
++        next = node;
++      }
++    }
++  }
++
++  return this;
++}
++
++function selection_sort(compare) {
++  if (!compare) compare = ascending$1;
++
++  function compareNode(a, b) {
++    return a && b ? compare(a.__data__, b.__data__) : !a - !b;
++  }
++
++  for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) {
++    for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) {
++      if (node = group[i]) {
++        sortgroup[i] = node;
++      }
++    }
++    sortgroup.sort(compareNode);
++  }
++
++  return new Selection(sortgroups, this._parents).order();
++}
++
++function ascending$1(a, b) {
++  return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
++}
++
++function selection_call() {
++  var callback = arguments[0];
++  arguments[0] = this;
++  callback.apply(null, arguments);
++  return this;
++}
++
++function selection_nodes() {
++  var nodes = new Array(this.size()), i = -1;
++  this.each(function() { nodes[++i] = this; });
++  return nodes;
++}
++
++function selection_node() {
++
++  for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
++    for (var group = groups[j], i = 0, n = group.length; i < n; ++i) {
++      var node = group[i];
++      if (node) return node;
++    }
++  }
++
++  return null;
++}
++
++function selection_size() {
++  var size = 0;
++  this.each(function() { ++size; });
++  return size;
++}
++
++function selection_empty() {
++  return !this.node();
++}
++
++function selection_each(callback) {
++
++  for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
++    for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) {
++      if (node = group[i]) callback.call(node, node.__data__, i, group);
++    }
++  }
++
++  return this;
++}
++
++function attrRemove(name) {
++  return function() {
++    this.removeAttribute(name);
++  };
++}
++
++function attrRemoveNS(fullname) {
++  return function() {
++    this.removeAttributeNS(fullname.space, fullname.local);
++  };
++}
++
++function attrConstant(name, value) {
++  return function() {
++    this.setAttribute(name, value);
++  };
++}
++
++function attrConstantNS(fullname, value) {
++  return function() {
++    this.setAttributeNS(fullname.space, fullname.local, value);
++  };
++}
++
++function attrFunction(name, value) {
++  return function() {
++    var v = value.apply(this, arguments);
++    if (v == null) this.removeAttribute(name);
++    else this.setAttribute(name, v);
++  };
++}
++
++function attrFunctionNS(fullname, value) {
++  return function() {
++    var v = value.apply(this, arguments);
++    if (v == null) this.removeAttributeNS(fullname.space, fullname.local);
++    else this.setAttributeNS(fullname.space, fullname.local, v);
++  };
++}
++
++function selection_attr(name, value) {
++  var fullname = namespace(name);
++
++  if (arguments.length < 2) {
++    var node = this.node();
++    return fullname.local
++        ? node.getAttributeNS(fullname.space, fullname.local)
++        : node.getAttribute(fullname);
++  }
++
++  return this.each((value == null
++      ? (fullname.local ? attrRemoveNS : attrRemove) : (typeof value === "function"
++      ? (fullname.local ? attrFunctionNS : attrFunction)
++      : (fullname.local ? attrConstantNS : attrConstant)))(fullname, value));
++}
++
++function defaultView(node) {
++  return (node.ownerDocument && node.ownerDocument.defaultView) // node is a Node
++      || (node.document && node) // node is a Window
++      || node.defaultView; // node is a Document
++}
++
++function styleRemove(name) {
++  return function() {
++    this.style.removeProperty(name);
++  };
++}
++
++function styleConstant(name, value, priority) {
++  return function() {
++    this.style.setProperty(name, value, priority);
++  };
++}
++
++function styleFunction(name, value, priority) {
++  return function() {
++    var v = value.apply(this, arguments);
++    if (v == null) this.style.removeProperty(name);
++    else this.style.setProperty(name, v, priority);
++  };
++}
++
++function selection_style(name, value, priority) {
++  return arguments.length > 1
++      ? this.each((value == null
++            ? styleRemove : typeof value === "function"
++            ? styleFunction
++            : styleConstant)(name, value, priority == null ? "" : priority))
++      : styleValue(this.node(), name);
++}
++
++function styleValue(node, name) {
++  return node.style.getPropertyValue(name)
++      || defaultView(node).getComputedStyle(node, null).getPropertyValue(name);
++}
++
++function propertyRemove(name) {
++  return function() {
++    delete this[name];
++  };
++}
++
++function propertyConstant(name, value) {
++  return function() {
++    this[name] = value;
++  };
++}
++
++function propertyFunction(name, value) {
++  return function() {
++    var v = value.apply(this, arguments);
++    if (v == null) delete this[name];
++    else this[name] = v;
++  };
++}
++
++function selection_property(name, value) {
++  return arguments.length > 1
++      ? this.each((value == null
++          ? propertyRemove : typeof value === "function"
++          ? propertyFunction
++          : propertyConstant)(name, value))
++      : this.node()[name];
++}
++
++function classArray(string) {
++  return string.trim().split(/^|\s+/);
++}
++
++function classList(node) {
++  return node.classList || new ClassList(node);
++}
++
++function ClassList(node) {
++  this._node = node;
++  this._names = classArray(node.getAttribute("class") || "");
++}
++
++ClassList.prototype = {
++  add: function(name) {
++    var i = this._names.indexOf(name);
++    if (i < 0) {
++      this._names.push(name);
++      this._node.setAttribute("class", this._names.join(" "));
++    }
++  },
++  remove: function(name) {
++    var i = this._names.indexOf(name);
++    if (i >= 0) {
++      this._names.splice(i, 1);
++      this._node.setAttribute("class", this._names.join(" "));
++    }
++  },
++  contains: function(name) {
++    return this._names.indexOf(name) >= 0;
++  }
++};
++
++function classedAdd(node, names) {
++  var list = classList(node), i = -1, n = names.length;
++  while (++i < n) list.add(names[i]);
++}
++
++function classedRemove(node, names) {
++  var list = classList(node), i = -1, n = names.length;
++  while (++i < n) list.remove(names[i]);
++}
++
++function classedTrue(names) {
++  return function() {
++    classedAdd(this, names);
++  };
++}
++
++function classedFalse(names) {
++  return function() {
++    classedRemove(this, names);
++  };
++}
++
++function classedFunction(names, value) {
++  return function() {
++    (value.apply(this, arguments) ? classedAdd : classedRemove)(this, names);
++  };
++}
++
++function selection_classed(name, value) {
++  var names = classArray(name + "");
++
++  if (arguments.length < 2) {
++    var list = classList(this.node()), i = -1, n = names.length;
++    while (++i < n) if (!list.contains(names[i])) return false;
++    return true;
++  }
++
++  return this.each((typeof value === "function"
++      ? classedFunction : value
++      ? classedTrue
++      : classedFalse)(names, value));
++}
++
++function textRemove() {
++  this.textContent = "";
++}
++
++function textConstant(value) {
++  return function() {
++    this.textContent = value;
++  };
++}
++
++function textFunction(value) {
++  return function() {
++    var v = value.apply(this, arguments);
++    this.textContent = v == null ? "" : v;
++  };
++}
++
++function selection_text(value) {
++  return arguments.length
++      ? this.each(value == null
++          ? textRemove : (typeof value === "function"
++          ? textFunction
++          : textConstant)(value))
++      : this.node().textContent;
++}
++
++function htmlRemove() {
++  this.innerHTML = "";
++}
++
++function htmlConstant(value) {
++  return function() {
++    this.innerHTML = value;
++  };
++}
++
++function htmlFunction(value) {
++  return function() {
++    var v = value.apply(this, arguments);
++    this.innerHTML = v == null ? "" : v;
++  };
++}
++
++function selection_html(value) {
++  return arguments.length
++      ? this.each(value == null
++          ? htmlRemove : (typeof value === "function"
++          ? htmlFunction
++          : htmlConstant)(value))
++      : this.node().innerHTML;
++}
++
++function raise() {
++  if (this.nextSibling) this.parentNode.appendChild(this);
++}
++
++function selection_raise() {
++  return this.each(raise);
++}
++
++function lower() {
++  if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild);
++}
++
++function selection_lower() {
++  return this.each(lower);
++}
++
++function selection_append(name) {
++  var create = typeof name === "function" ? name : creator(name);
++  return this.select(function() {
++    return this.appendChild(create.apply(this, arguments));
++  });
++}
++
++function constantNull() {
++  return null;
++}
++
++function selection_insert(name, before) {
++  var create = typeof name === "function" ? name : creator(name),
++      select = before == null ? constantNull : typeof before === "function" ? before : selector(before);
++  return this.select(function() {
++    return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null);
++  });
++}
++
++function remove() {
++  var parent = this.parentNode;
++  if (parent) parent.removeChild(this);
++}
++
++function selection_remove() {
++  return this.each(remove);
++}
++
++function selection_cloneShallow() {
++  return this.parentNode.insertBefore(this.cloneNode(false), this.nextSibling);
++}
++
++function selection_cloneDeep() {
++  return this.parentNode.insertBefore(this.cloneNode(true), this.nextSibling);
++}
++
++function selection_clone(deep) {
++  return this.select(deep ? selection_cloneDeep : selection_cloneShallow);
++}
++
++function selection_datum(value) {
++  return arguments.length
++      ? this.property("__data__", value)
++      : this.node().__data__;
++}
++
++var filterEvents = {};
++
++exports.event = null;
++
++if (typeof document !== "undefined") {
++  var element$1 = document.documentElement;
++  if (!("onmouseenter" in element$1)) {
++    filterEvents = {mouseenter: "mouseover", mouseleave: "mouseout"};
++  }
++}
++
++function filterContextListener(listener, index, group) {
++  listener = contextListener(listener, index, group);
++  return function(event) {
++    var related = event.relatedTarget;
++    if (!related || (related !== this && !(related.compareDocumentPosition(this) & 8))) {
++      listener.call(this, event);
++    }
++  };
++}
++
++function contextListener(listener, index, group) {
++  return function(event1) {
++    var event0 = exports.event; // Events can be reentrant (e.g., focus).
++    exports.event = event1;
++    try {
++      listener.call(this, this.__data__, index, group);
++    } finally {
++      exports.event = event0;
++    }
++  };
++}
++
++function parseTypenames$1(typenames) {
++  return typenames.trim().split(/^|\s+/).map(function(t) {
++    var name = "", i = t.indexOf(".");
++    if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);
++    return {type: t, name: name};
++  });
++}
++
++function onRemove(typename) {
++  return function() {
++    var on = this.__on;
++    if (!on) return;
++    for (var j = 0, i = -1, m = on.length, o; j < m; ++j) {
++      if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) {
++        this.removeEventListener(o.type, o.listener, o.capture);
++      } else {
++        on[++i] = o;
++      }
++    }
++    if (++i) on.length = i;
++    else delete this.__on;
++  };
++}
++
++function onAdd(typename, value, capture) {
++  var wrap = filterEvents.hasOwnProperty(typename.type) ? filterContextListener : contextListener;
++  return function(d, i, group) {
++    var on = this.__on, o, listener = wrap(value, i, group);
++    if (on) for (var j = 0, m = on.length; j < m; ++j) {
++      if ((o = on[j]).type === typename.type && o.name === typename.name) {
++        this.removeEventListener(o.type, o.listener, o.capture);
++        this.addEventListener(o.type, o.listener = listener, o.capture = capture);
++        o.value = value;
++        return;
++      }
++    }
++    this.addEventListener(typename.type, listener, capture);
++    o = {type: typename.type, name: typename.name, value: value, listener: listener, capture: capture};
++    if (!on) this.__on = [o];
++    else on.push(o);
++  };
++}
++
++function selection_on(typename, value, capture) {
++  var typenames = parseTypenames$1(typename + ""), i, n = typenames.length, t;
++
++  if (arguments.length < 2) {
++    var on = this.node().__on;
++    if (on) for (var j = 0, m = on.length, o; j < m; ++j) {
++      for (i = 0, o = on[j]; i < n; ++i) {
++        if ((t = typenames[i]).type === o.type && t.name === o.name) {
++          return o.value;
++        }
++      }
++    }
++    return;
++  }
++
++  on = value ? onAdd : onRemove;
++  if (capture == null) capture = false;
++  for (i = 0; i < n; ++i) this.each(on(typenames[i], value, capture));
++  return this;
++}
++
++function customEvent(event1, listener, that, args) {
++  var event0 = exports.event;
++  event1.sourceEvent = exports.event;
++  exports.event = event1;
++  try {
++    return listener.apply(that, args);
++  } finally {
++    exports.event = event0;
++  }
++}
++
++function dispatchEvent(node, type, params) {
++  var window = defaultView(node),
++      event = window.CustomEvent;
++
++  if (typeof event === "function") {
++    event = new event(type, params);
++  } else {
++    event = window.document.createEvent("Event");
++    if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail;
++    else event.initEvent(type, false, false);
++  }
++
++  node.dispatchEvent(event);
++}
++
++function dispatchConstant(type, params) {
++  return function() {
++    return dispatchEvent(this, type, params);
++  };
++}
++
++function dispatchFunction(type, params) {
++  return function() {
++    return dispatchEvent(this, type, params.apply(this, arguments));
++  };
++}
++
++function selection_dispatch(type, params) {
++  return this.each((typeof params === "function"
++      ? dispatchFunction
++      : dispatchConstant)(type, params));
++}
++
++var root = [null];
++
++function Selection(groups, parents) {
++  this._groups = groups;
++  this._parents = parents;
++}
++
++function selection() {
++  return new Selection([[document.documentElement]], root);
++}
++
++Selection.prototype = selection.prototype = {
++  constructor: Selection,
++  select: selection_select,
++  selectAll: selection_selectAll,
++  filter: selection_filter,
++  data: selection_data,
++  enter: selection_enter,
++  exit: selection_exit,
++  merge: selection_merge,
++  order: selection_order,
++  sort: selection_sort,
++  call: selection_call,
++  nodes: selection_nodes,
++  node: selection_node,
++  size: selection_size,
++  empty: selection_empty,
++  each: selection_each,
++  attr: selection_attr,
++  style: selection_style,
++  property: selection_property,
++  classed: selection_classed,
++  text: selection_text,
++  html: selection_html,
++  raise: selection_raise,
++  lower: selection_lower,
++  append: selection_append,
++  insert: selection_insert,
++  remove: selection_remove,
++  clone: selection_clone,
++  datum: selection_datum,
++  on: selection_on,
++  dispatch: selection_dispatch
++};
++
++function select(selector) {
++  return typeof selector === "string"
++      ? new Selection([[document.querySelector(selector)]], [document.documentElement])
++      : new Selection([[selector]], root);
++}
++
++function create(name) {
++  return select(creator(name).call(document.documentElement));
++}
++
++var nextId = 0;
++
++function local() {
++  return new Local;
++}
++
++function Local() {
++  this._ = "@" + (++nextId).toString(36);
++}
++
++Local.prototype = local.prototype = {
++  constructor: Local,
++  get: function(node) {
++    var id = this._;
++    while (!(id in node)) if (!(node = node.parentNode)) return;
++    return node[id];
++  },
++  set: function(node, value) {
++    return node[this._] = value;
++  },
++  remove: function(node) {
++    return this._ in node && delete node[this._];
++  },
++  toString: function() {
++    return this._;
++  }
++};
++
++function sourceEvent() {
++  var current = exports.event, source;
++  while (source = current.sourceEvent) current = source;
++  return current;
++}
++
++function point(node, event) {
++  var svg = node.ownerSVGElement || node;
++
++  if (svg.createSVGPoint) {
++    var point = svg.createSVGPoint();
++    point.x = event.clientX, point.y = event.clientY;
++    point = point.matrixTransform(node.getScreenCTM().inverse());
++    return [point.x, point.y];
++  }
++
++  var rect = node.getBoundingClientRect();
++  return [event.clientX - rect.left - node.clientLeft, event.clientY - rect.top - node.clientTop];
++}
++
++function mouse(node) {
++  var event = sourceEvent();
++  if (event.changedTouches) event = event.changedTouches[0];
++  return point(node, event);
++}
++
++function selectAll(selector) {
++  return typeof selector === "string"
++      ? new Selection([document.querySelectorAll(selector)], [document.documentElement])
++      : new Selection([selector == null ? [] : selector], root);
++}
++
++function touch(node, touches, identifier) {
++  if (arguments.length < 3) identifier = touches, touches = sourceEvent().changedTouches;
++
++  for (var i = 0, n = touches ? touches.length : 0, touch; i < n; ++i) {
++    if ((touch = touches[i]).identifier === identifier) {
++      return point(node, touch);
++    }
++  }
++
++  return null;
++}
++
++function touches(node, touches) {
++  if (touches == null) touches = sourceEvent().touches;
++
++  for (var i = 0, n = touches ? touches.length : 0, points = new Array(n); i < n; ++i) {
++    points[i] = point(node, touches[i]);
++  }
++
++  return points;
++}
++
++function nopropagation() {
++  exports.event.stopImmediatePropagation();
++}
++
++function noevent() {
++  exports.event.preventDefault();
++  exports.event.stopImmediatePropagation();
++}
++
++function dragDisable(view) {
++  var root = view.document.documentElement,
++      selection$$1 = select(view).on("dragstart.drag", noevent, true);
++  if ("onselectstart" in root) {
++    selection$$1.on("selectstart.drag", noevent, true);
++  } else {
++    root.__noselect = root.style.MozUserSelect;
++    root.style.MozUserSelect = "none";
++  }
++}
++
++function yesdrag(view, noclick) {
++  var root = view.document.documentElement,
++      selection$$1 = select(view).on("dragstart.drag", null);
++  if (noclick) {
++    selection$$1.on("click.drag", noevent, true);
++    setTimeout(function() { selection$$1.on("click.drag", null); }, 0);
++  }
++  if ("onselectstart" in root) {
++    selection$$1.on("selectstart.drag", null);
++  } else {
++    root.style.MozUserSelect = root.__noselect;
++    delete root.__noselect;
++  }
++}
++
++function constant$2(x) {
++  return function() {
++    return x;
++  };
++}
++
++function DragEvent(target, type, subject, id, active, x, y, dx, dy, dispatch) {
++  this.target = target;
++  this.type = type;
++  this.subject = subject;
++  this.identifier = id;
++  this.active = active;
++  this.x = x;
++  this.y = y;
++  this.dx = dx;
++  this.dy = dy;
++  this._ = dispatch;
++}
++
++DragEvent.prototype.on = function() {
++  var value = this._.on.apply(this._, arguments);
++  return value === this._ ? this : value;
++};
++
++// Ignore right-click, since that should open the context menu.
++function defaultFilter() {
++  return !exports.event.button;
++}
++
++function defaultContainer() {
++  return this.parentNode;
++}
++
++function defaultSubject(d) {
++  return d == null ? {x: exports.event.x, y: exports.event.y} : d;
++}
++
++function defaultTouchable() {
++  return "ontouchstart" in this;
++}
++
++function drag() {
++  var filter = defaultFilter,
++      container = defaultContainer,
++      subject = defaultSubject,
++      touchable = defaultTouchable,
++      gestures = {},
++      listeners = dispatch("start", "drag", "end"),
++      active = 0,
++      mousedownx,
++      mousedowny,
++      mousemoving,
++      touchending,
++      clickDistance2 = 0;
++
++  function drag(selection$$1) {
++    selection$$1
++        .on("mousedown.drag", mousedowned)
++      .filter(touchable)
++        .on("touchstart.drag", touchstarted)
++        .on("touchmove.drag", touchmoved)
++        .on("touchend.drag touchcancel.drag", touchended)
++        .style("touch-action", "none")
++        .style("-webkit-tap-highlight-color", "rgba(0,0,0,0)");
++  }
++
++  function mousedowned() {
++    if (touchending || !filter.apply(this, arguments)) return;
++    var gesture = beforestart("mouse", container.apply(this, arguments), mouse, this, arguments);
++    if (!gesture) return;
++    select(exports.event.view).on("mousemove.drag", mousemoved, true).on("mouseup.drag", mouseupped, true);
++    dragDisable(exports.event.view);
++    nopropagation();
++    mousemoving = false;
++    mousedownx = exports.event.clientX;
++    mousedowny = exports.event.clientY;
++    gesture("start");
++  }
++
++  function mousemoved() {
++    noevent();
++    if (!mousemoving) {
++      var dx = exports.event.clientX - mousedownx, dy = exports.event.clientY - mousedowny;
++      mousemoving = dx * dx + dy * dy > clickDistance2;
++    }
++    gestures.mouse("drag");
++  }
++
++  function mouseupped() {
++    select(exports.event.view).on("mousemove.drag mouseup.drag", null);
++    yesdrag(exports.event.view, mousemoving);
++    noevent();
++    gestures.mouse("end");
++  }
++
++  function touchstarted() {
++    if (!filter.apply(this, arguments)) return;
++    var touches$$1 = exports.event.changedTouches,
++        c = container.apply(this, arguments),
++        n = touches$$1.length, i, gesture;
++
++    for (i = 0; i < n; ++i) {
++      if (gesture = beforestart(touches$$1[i].identifier, c, touch, this, arguments)) {
++        nopropagation();
++        gesture("start");
++      }
++    }
++  }
++
++  function touchmoved() {
++    var touches$$1 = exports.event.changedTouches,
++        n = touches$$1.length, i, gesture;
++
++    for (i = 0; i < n; ++i) {
++      if (gesture = gestures[touches$$1[i].identifier]) {
++        noevent();
++        gesture("drag");
++      }
++    }
++  }
++
++  function touchended() {
++    var touches$$1 = exports.event.changedTouches,
++        n = touches$$1.length, i, gesture;
++
++    if (touchending) clearTimeout(touchending);
++    touchending = setTimeout(function() { touchending = null; }, 500); // Ghost clicks are delayed!
++    for (i = 0; i < n; ++i) {
++      if (gesture = gestures[touches$$1[i].identifier]) {
++        nopropagation();
++        gesture("end");
++      }
++    }
++  }
++
++  function beforestart(id, container, point$$1, that, args) {
++    var p = point$$1(container, id), s, dx, dy,
++        sublisteners = listeners.copy();
++
++    if (!customEvent(new DragEvent(drag, "beforestart", s, id, active, p[0], p[1], 0, 0, sublisteners), function() {
++      if ((exports.event.subject = s = subject.apply(that, args)) == null) return false;
++      dx = s.x - p[0] || 0;
++      dy = s.y - p[1] || 0;
++      return true;
++    })) return;
++
++    return function gesture(type) {
++      var p0 = p, n;
++      switch (type) {
++        case "start": gestures[id] = gesture, n = active++; break;
++        case "end": delete gestures[id], --active; // nobreak
++        case "drag": p = point$$1(container, id), n = active; break;
++      }
++      customEvent(new DragEvent(drag, type, s, id, n, p[0] + dx, p[1] + dy, p[0] - p0[0], p[1] - p0[1], sublisteners), sublisteners.apply, sublisteners, [type, that, args]);
++    };
++  }
++
++  drag.filter = function(_) {
++    return arguments.length ? (filter = typeof _ === "function" ? _ : constant$2(!!_), drag) : filter;
++  };
++
++  drag.container = function(_) {
++    return arguments.length ? (container = typeof _ === "function" ? _ : constant$2(_), drag) : container;
++  };
++
++  drag.subject = function(_) {
++    return arguments.length ? (subject = typeof _ === "function" ? _ : constant$2(_), drag) : subject;
++  };
++
++  drag.touchable = function(_) {
++    return arguments.length ? (touchable = typeof _ === "function" ? _ : constant$2(!!_), drag) : touchable;
++  };
++
++  drag.on = function() {
++    var value = listeners.on.apply(listeners, arguments);
++    return value === listeners ? drag : value;
++  };
++
++  drag.clickDistance = function(_) {
++    return arguments.length ? (clickDistance2 = (_ = +_) * _, drag) : Math.sqrt(clickDistance2);
++  };
++
++  return drag;
++}
++
++function define(constructor, factory, prototype) {
++  constructor.prototype = factory.prototype = prototype;
++  prototype.constructor = constructor;
++}
++
++function extend(parent, definition) {
++  var prototype = Object.create(parent.prototype);
++  for (var key in definition) prototype[key] = definition[key];
++  return prototype;
++}
++
++function Color() {}
++
++var darker = 0.7;
++var brighter = 1 / darker;
++
++var reI = "\\s*([+-]?\\d+)\\s*",
++    reN = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*",
++    reP = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*",
++    reHex3 = /^#([0-9a-f]{3})$/,
++    reHex6 = /^#([0-9a-f]{6})$/,
++    reRgbInteger = new RegExp("^rgb\\(" + [reI, reI, reI] + "\\)$"),
++    reRgbPercent = new RegExp("^rgb\\(" + [reP, reP, reP] + "\\)$"),
++    reRgbaInteger = new RegExp("^rgba\\(" + [reI, reI, reI, reN] + "\\)$"),
++    reRgbaPercent = new RegExp("^rgba\\(" + [reP, reP, reP, reN] + "\\)$"),
++    reHslPercent = new RegExp("^hsl\\(" + [reN, reP, reP] + "\\)$"),
++    reHslaPercent = new RegExp("^hsla\\(" + [reN, reP, reP, reN] + "\\)$");
++
++var named = {
++  aliceblue: 0xf0f8ff,
++  antiquewhite: 0xfaebd7,
++  aqua: 0x00ffff,
++  aquamarine: 0x7fffd4,
++  azure: 0xf0ffff,
++  beige: 0xf5f5dc,
++  bisque: 0xffe4c4,
++  black: 0x000000,
++  blanchedalmond: 0xffebcd,
++  blue: 0x0000ff,
++  blueviolet: 0x8a2be2,
++  brown: 0xa52a2a,
++  burlywood: 0xdeb887,
++  cadetblue: 0x5f9ea0,
++  chartreuse: 0x7fff00,
++  chocolate: 0xd2691e,
++  coral: 0xff7f50,
++  cornflowerblue: 0x6495ed,
++  cornsilk: 0xfff8dc,
++  crimson: 0xdc143c,
++  cyan: 0x00ffff,
++  darkblue: 0x00008b,
++  darkcyan: 0x008b8b,
++  darkgoldenrod: 0xb8860b,
++  darkgray: 0xa9a9a9,
++  darkgreen: 0x006400,
++  darkgrey: 0xa9a9a9,
++  darkkhaki: 0xbdb76b,
++  darkmagenta: 0x8b008b,
++  darkolivegreen: 0x556b2f,
++  darkorange: 0xff8c00,
++  darkorchid: 0x9932cc,
++  darkred: 0x8b0000,
++  darksalmon: 0xe9967a,
++  darkseagreen: 0x8fbc8f,
++  darkslateblue: 0x483d8b,
++  darkslategray: 0x2f4f4f,
++  darkslategrey: 0x2f4f4f,
++  darkturquoise: 0x00ced1,
++  darkviolet: 0x9400d3,
++  deeppink: 0xff1493,
++  deepskyblue: 0x00bfff,
++  dimgray: 0x696969,
++  dimgrey: 0x696969,
++  dodgerblue: 0x1e90ff,
++  firebrick: 0xb22222,
++  floralwhite: 0xfffaf0,
++  forestgreen: 0x228b22,
++  fuchsia: 0xff00ff,
++  gainsboro: 0xdcdcdc,
++  ghostwhite: 0xf8f8ff,
++  gold: 0xffd700,
++  goldenrod: 0xdaa520,
++  gray: 0x808080,
++  green: 0x008000,
++  greenyellow: 0xadff2f,
++  grey: 0x808080,
++  honeydew: 0xf0fff0,
++  hotpink: 0xff69b4,
++  indianred: 0xcd5c5c,
++  indigo: 0x4b0082,
++  ivory: 0xfffff0,
++  khaki: 0xf0e68c,
++  lavender: 0xe6e6fa,
++  lavenderblush: 0xfff0f5,
++  lawngreen: 0x7cfc00,
++  lemonchiffon: 0xfffacd,
++  lightblue: 0xadd8e6,
++  lightcoral: 0xf08080,
++  lightcyan: 0xe0ffff,
++  lightgoldenrodyellow: 0xfafad2,
++  lightgray: 0xd3d3d3,
++  lightgreen: 0x90ee90,
++  lightgrey: 0xd3d3d3,
++  lightpink: 0xffb6c1,
++  lightsalmon: 0xffa07a,
++  lightseagreen: 0x20b2aa,
++  lightskyblue: 0x87cefa,
++  lightslategray: 0x778899,
++  lightslategrey: 0x778899,
++  lightsteelblue: 0xb0c4de,
++  lightyellow: 0xffffe0,
++  lime: 0x00ff00,
++  limegreen: 0x32cd32,
++  linen: 0xfaf0e6,
++  magenta: 0xff00ff,
++  maroon: 0x800000,
++  mediumaquamarine: 0x66cdaa,
++  mediumblue: 0x0000cd,
++  mediumorchid: 0xba55d3,
++  mediumpurple: 0x9370db,
++  mediumseagreen: 0x3cb371,
++  mediumslateblue: 0x7b68ee,
++  mediumspringgreen: 0x00fa9a,
++  mediumturquoise: 0x48d1cc,
++  mediumvioletred: 0xc71585,
++  midnightblue: 0x191970,
++  mintcream: 0xf5fffa,
++  mistyrose: 0xffe4e1,
++  moccasin: 0xffe4b5,
++  navajowhite: 0xffdead,
++  navy: 0x000080,
++  oldlace: 0xfdf5e6,
++  olive: 0x808000,
++  olivedrab: 0x6b8e23,
++  orange: 0xffa500,
++  orangered: 0xff4500,
++  orchid: 0xda70d6,
++  palegoldenrod: 0xeee8aa,
++  palegreen: 0x98fb98,
++  paleturquoise: 0xafeeee,
++  palevioletred: 0xdb7093,
++  papayawhip: 0xffefd5,
++  peachpuff: 0xffdab9,
++  peru: 0xcd853f,
++  pink: 0xffc0cb,
++  plum: 0xdda0dd,
++  powderblue: 0xb0e0e6,
++  purple: 0x800080,
++  rebeccapurple: 0x663399,
++  red: 0xff0000,
++  rosybrown: 0xbc8f8f,
++  royalblue: 0x4169e1,
++  saddlebrown: 0x8b4513,
++  salmon: 0xfa8072,
++  sandybrown: 0xf4a460,
++  seagreen: 0x2e8b57,
++  seashell: 0xfff5ee,
++  sienna: 0xa0522d,
++  silver: 0xc0c0c0,
++  skyblue: 0x87ceeb,
++  slateblue: 0x6a5acd,
++  slategray: 0x708090,
++  slategrey: 0x708090,
++  snow: 0xfffafa,
++  springgreen: 0x00ff7f,
++  steelblue: 0x4682b4,
++  tan: 0xd2b48c,
++  teal: 0x008080,
++  thistle: 0xd8bfd8,
++  tomato: 0xff6347,
++  turquoise: 0x40e0d0,
++  violet: 0xee82ee,
++  wheat: 0xf5deb3,
++  white: 0xffffff,
++  whitesmoke: 0xf5f5f5,
++  yellow: 0xffff00,
++  yellowgreen: 0x9acd32
++};
++
++define(Color, color, {
++  displayable: function() {
++    return this.rgb().displayable();
++  },
++  hex: function() {
++    return this.rgb().hex();
++  },
++  toString: function() {
++    return this.rgb() + "";
++  }
++});
++
++function color(format) {
++  var m;
++  format = (format + "").trim().toLowerCase();
++  return (m = reHex3.exec(format)) ? (m = parseInt(m[1], 16), new Rgb((m >> 8 & 0xf) | (m >> 4 & 0x0f0), (m >> 4 & 0xf) | (m & 0xf0), ((m & 0xf) << 4) | (m & 0xf), 1)) // #f00
++      : (m = reHex6.exec(format)) ? rgbn(parseInt(m[1], 16)) // #ff0000
++      : (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0)
++      : (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%)
++      : (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1)
++      : (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1)
++      : (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%)
++      : (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1)
++      : named.hasOwnProperty(format) ? rgbn(named[format])
++      : format === "transparent" ? new Rgb(NaN, NaN, NaN, 0)
++      : null;
++}
++
++function rgbn(n) {
++  return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1);
++}
++
++function rgba(r, g, b, a) {
++  if (a <= 0) r = g = b = NaN;
++  return new Rgb(r, g, b, a);
++}
++
++function rgbConvert(o) {
++  if (!(o instanceof Color)) o = color(o);
++  if (!o) return new Rgb;
++  o = o.rgb();
++  return new Rgb(o.r, o.g, o.b, o.opacity);
++}
++
++function rgb(r, g, b, opacity) {
++  return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity);
++}
++
++function Rgb(r, g, b, opacity) {
++  this.r = +r;
++  this.g = +g;
++  this.b = +b;
++  this.opacity = +opacity;
++}
++
++define(Rgb, rgb, extend(Color, {
++  brighter: function(k) {
++    k = k == null ? brighter : Math.pow(brighter, k);
++    return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
++  },
++  darker: function(k) {
++    k = k == null ? darker : Math.pow(darker, k);
++    return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
++  },
++  rgb: function() {
++    return this;
++  },
++  displayable: function() {
++    return (0 <= this.r && this.r <= 255)
++        && (0 <= this.g && this.g <= 255)
++        && (0 <= this.b && this.b <= 255)
++        && (0 <= this.opacity && this.opacity <= 1);
++  },
++  hex: function() {
++    return "#" + hex(this.r) + hex(this.g) + hex(this.b);
++  },
++  toString: function() {
++    var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));
++    return (a === 1 ? "rgb(" : "rgba(")
++        + Math.max(0, Math.min(255, Math.round(this.r) || 0)) + ", "
++        + Math.max(0, Math.min(255, Math.round(this.g) || 0)) + ", "
++        + Math.max(0, Math.min(255, Math.round(this.b) || 0))
++        + (a === 1 ? ")" : ", " + a + ")");
++  }
++}));
++
++function hex(value) {
++  value = Math.max(0, Math.min(255, Math.round(value) || 0));
++  return (value < 16 ? "0" : "") + value.toString(16);
++}
++
++function hsla(h, s, l, a) {
++  if (a <= 0) h = s = l = NaN;
++  else if (l <= 0 || l >= 1) h = s = NaN;
++  else if (s <= 0) h = NaN;
++  return new Hsl(h, s, l, a);
++}
++
++function hslConvert(o) {
++  if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity);
++  if (!(o instanceof Color)) o = color(o);
++  if (!o) return new Hsl;
++  if (o instanceof Hsl) return o;
++  o = o.rgb();
++  var r = o.r / 255,
++      g = o.g / 255,
++      b = o.b / 255,
++      min = Math.min(r, g, b),
++      max = Math.max(r, g, b),
++      h = NaN,
++      s = max - min,
++      l = (max + min) / 2;
++  if (s) {
++    if (r === max) h = (g - b) / s + (g < b) * 6;
++    else if (g === max) h = (b - r) / s + 2;
++    else h = (r - g) / s + 4;
++    s /= l < 0.5 ? max + min : 2 - max - min;
++    h *= 60;
++  } else {
++    s = l > 0 && l < 1 ? 0 : h;
++  }
++  return new Hsl(h, s, l, o.opacity);
++}
++
++function hsl(h, s, l, opacity) {
++  return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity);
++}
++
++function Hsl(h, s, l, opacity) {
++  this.h = +h;
++  this.s = +s;
++  this.l = +l;
++  this.opacity = +opacity;
++}
++
++define(Hsl, hsl, extend(Color, {
++  brighter: function(k) {
++    k = k == null ? brighter : Math.pow(brighter, k);
++    return new Hsl(this.h, this.s, this.l * k, this.opacity);
++  },
++  darker: function(k) {
++    k = k == null ? darker : Math.pow(darker, k);
++    return new Hsl(this.h, this.s, this.l * k, this.opacity);
++  },
++  rgb: function() {
++    var h = this.h % 360 + (this.h < 0) * 360,
++        s = isNaN(h) || isNaN(this.s) ? 0 : this.s,
++        l = this.l,
++        m2 = l + (l < 0.5 ? l : 1 - l) * s,
++        m1 = 2 * l - m2;
++    return new Rgb(
++      hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2),
++      hsl2rgb(h, m1, m2),
++      hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2),
++      this.opacity
++    );
++  },
++  displayable: function() {
++    return (0 <= this.s && this.s <= 1 || isNaN(this.s))
++        && (0 <= this.l && this.l <= 1)
++        && (0 <= this.opacity && this.opacity <= 1);
++  }
++}));
++
++/* From FvD 13.37, CSS Color Module Level 3 */
++function hsl2rgb(h, m1, m2) {
++  return (h < 60 ? m1 + (m2 - m1) * h / 60
++      : h < 180 ? m2
++      : h < 240 ? m1 + (m2 - m1) * (240 - h) / 60
++      : m1) * 255;
++}
++
++var deg2rad = Math.PI / 180;
++var rad2deg = 180 / Math.PI;
++
++// https://beta.observablehq.com/@mbostock/lab-and-rgb
++var K = 18,
++    Xn = 0.96422,
++    Yn = 1,
++    Zn = 0.82521,
++    t0 = 4 / 29,
++    t1 = 6 / 29,
++    t2 = 3 * t1 * t1,
++    t3 = t1 * t1 * t1;
++
++function labConvert(o) {
++  if (o instanceof Lab) return new Lab(o.l, o.a, o.b, o.opacity);
++  if (o instanceof Hcl) {
++    if (isNaN(o.h)) return new Lab(o.l, 0, 0, o.opacity);
++    var h = o.h * deg2rad;
++    return new Lab(o.l, Math.cos(h) * o.c, Math.sin(h) * o.c, o.opacity);
++  }
++  if (!(o instanceof Rgb)) o = rgbConvert(o);
++  var r = rgb2lrgb(o.r),
++      g = rgb2lrgb(o.g),
++      b = rgb2lrgb(o.b),
++      y = xyz2lab((0.2225045 * r + 0.7168786 * g + 0.0606169 * b) / Yn), x, z;
++  if (r === g && g === b) x = z = y; else {
++    x = xyz2lab((0.4360747 * r + 0.3850649 * g + 0.1430804 * b) / Xn);
++    z = xyz2lab((0.0139322 * r + 0.0971045 * g + 0.7141733 * b) / Zn);
++  }
++  return new Lab(116 * y - 16, 500 * (x - y), 200 * (y - z), o.opacity);
++}
++
++function gray(l, opacity) {
++  return new Lab(l, 0, 0, opacity == null ? 1 : opacity);
++}
++
++function lab(l, a, b, opacity) {
++  return arguments.length === 1 ? labConvert(l) : new Lab(l, a, b, opacity == null ? 1 : opacity);
++}
++
++function Lab(l, a, b, opacity) {
++  this.l = +l;
++  this.a = +a;
++  this.b = +b;
++  this.opacity = +opacity;
++}
++
++define(Lab, lab, extend(Color, {
++  brighter: function(k) {
++    return new Lab(this.l + K * (k == null ? 1 : k), this.a, this.b, this.opacity);
++  },
++  darker: function(k) {
++    return new Lab(this.l - K * (k == null ? 1 : k), this.a, this.b, this.opacity);
++  },
++  rgb: function() {
++    var y = (this.l + 16) / 116,
++        x = isNaN(this.a) ? y : y + this.a / 500,
++        z = isNaN(this.b) ? y : y - this.b / 200;
++    x = Xn * lab2xyz(x);
++    y = Yn * lab2xyz(y);
++    z = Zn * lab2xyz(z);
++    return new Rgb(
++      lrgb2rgb( 3.1338561 * x - 1.6168667 * y - 0.4906146 * z),
++      lrgb2rgb(-0.9787684 * x + 1.9161415 * y + 0.0334540 * z),
++      lrgb2rgb( 0.0719453 * x - 0.2289914 * y + 1.4052427 * z),
++      this.opacity
++    );
++  }
++}));
++
++function xyz2lab(t) {
++  return t > t3 ? Math.pow(t, 1 / 3) : t / t2 + t0;
++}
++
++function lab2xyz(t) {
++  return t > t1 ? t * t * t : t2 * (t - t0);
++}
++
++function lrgb2rgb(x) {
++  return 255 * (x <= 0.0031308 ? 12.92 * x : 1.055 * Math.pow(x, 1 / 2.4) - 0.055);
++}
++
++function rgb2lrgb(x) {
++  return (x /= 255) <= 0.04045 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4);
++}
++
++function hclConvert(o) {
++  if (o instanceof Hcl) return new Hcl(o.h, o.c, o.l, o.opacity);
++  if (!(o instanceof Lab)) o = labConvert(o);
++  if (o.a === 0 && o.b === 0) return new Hcl(NaN, 0, o.l, o.opacity);
++  var h = Math.atan2(o.b, o.a) * rad2deg;
++  return new Hcl(h < 0 ? h + 360 : h, Math.sqrt(o.a * o.a + o.b * o.b), o.l, o.opacity);
++}
++
++function lch(l, c, h, opacity) {
++  return arguments.length === 1 ? hclConvert(l) : new Hcl(h, c, l, opacity == null ? 1 : opacity);
++}
++
++function hcl(h, c, l, opacity) {
++  return arguments.length === 1 ? hclConvert(h) : new Hcl(h, c, l, opacity == null ? 1 : opacity);
++}
++
++function Hcl(h, c, l, opacity) {
++  this.h = +h;
++  this.c = +c;
++  this.l = +l;
++  this.opacity = +opacity;
++}
++
++define(Hcl, hcl, extend(Color, {
++  brighter: function(k) {
++    return new Hcl(this.h, this.c, this.l + K * (k == null ? 1 : k), this.opacity);
++  },
++  darker: function(k) {
++    return new Hcl(this.h, this.c, this.l - K * (k == null ? 1 : k), this.opacity);
++  },
++  rgb: function() {
++    return labConvert(this).rgb();
++  }
++}));
++
++var A = -0.14861,
++    B = +1.78277,
++    C = -0.29227,
++    D = -0.90649,
++    E = +1.97294,
++    ED = E * D,
++    EB = E * B,
++    BC_DA = B * C - D * A;
++
++function cubehelixConvert(o) {
++  if (o instanceof Cubehelix) return new Cubehelix(o.h, o.s, o.l, o.opacity);
++  if (!(o instanceof Rgb)) o = rgbConvert(o);
++  var r = o.r / 255,
++      g = o.g / 255,
++      b = o.b / 255,
++      l = (BC_DA * b + ED * r - EB * g) / (BC_DA + ED - EB),
++      bl = b - l,
++      k = (E * (g - l) - C * bl) / D,
++      s = Math.sqrt(k * k + bl * bl) / (E * l * (1 - l)), // NaN if l=0 or l=1
++      h = s ? Math.atan2(k, bl) * rad2deg - 120 : NaN;
++  return new Cubehelix(h < 0 ? h + 360 : h, s, l, o.opacity);
++}
++
++function cubehelix(h, s, l, opacity) {
++  return arguments.length === 1 ? cubehelixConvert(h) : new Cubehelix(h, s, l, opacity == null ? 1 : opacity);
++}
++
++function Cubehelix(h, s, l, opacity) {
++  this.h = +h;
++  this.s = +s;
++  this.l = +l;
++  this.opacity = +opacity;
++}
++
++define(Cubehelix, cubehelix, extend(Color, {
++  brighter: function(k) {
++    k = k == null ? brighter : Math.pow(brighter, k);
++    return new Cubehelix(this.h, this.s, this.l * k, this.opacity);
++  },
++  darker: function(k) {
++    k = k == null ? darker : Math.pow(darker, k);
++    return new Cubehelix(this.h, this.s, this.l * k, this.opacity);
++  },
++  rgb: function() {
++    var h = isNaN(this.h) ? 0 : (this.h + 120) * deg2rad,
++        l = +this.l,
++        a = isNaN(this.s) ? 0 : this.s * l * (1 - l),
++        cosh = Math.cos(h),
++        sinh = Math.sin(h);
++    return new Rgb(
++      255 * (l + a * (A * cosh + B * sinh)),
++      255 * (l + a * (C * cosh + D * sinh)),
++      255 * (l + a * (E * cosh)),
++      this.opacity
++    );
++  }
++}));
++
++function basis(t1, v0, v1, v2, v3) {
++  var t2 = t1 * t1, t3 = t2 * t1;
++  return ((1 - 3 * t1 + 3 * t2 - t3) * v0
++      + (4 - 6 * t2 + 3 * t3) * v1
++      + (1 + 3 * t1 + 3 * t2 - 3 * t3) * v2
++      + t3 * v3) / 6;
++}
++
++function basis$1(values) {
++  var n = values.length - 1;
++  return function(t) {
++    var i = t <= 0 ? (t = 0) : t >= 1 ? (t = 1, n - 1) : Math.floor(t * n),
++        v1 = values[i],
++        v2 = values[i + 1],
++        v0 = i > 0 ? values[i - 1] : 2 * v1 - v2,
++        v3 = i < n - 1 ? values[i + 2] : 2 * v2 - v1;
++    return basis((t - i / n) * n, v0, v1, v2, v3);
++  };
++}
++
++function basisClosed(values) {
++  var n = values.length;
++  return function(t) {
++    var i = Math.floor(((t %= 1) < 0 ? ++t : t) * n),
++        v0 = values[(i + n - 1) % n],
++        v1 = values[i % n],
++        v2 = values[(i + 1) % n],
++        v3 = values[(i + 2) % n];
++    return basis((t - i / n) * n, v0, v1, v2, v3);
++  };
++}
++
++function constant$3(x) {
++  return function() {
++    return x;
++  };
++}
++
++function linear(a, d) {
++  return function(t) {
++    return a + t * d;
++  };
++}
++
++function exponential(a, b, y) {
++  return a = Math.pow(a, y), b = Math.pow(b, y) - a, y = 1 / y, function(t) {
++    return Math.pow(a + t * b, y);
++  };
++}
++
++function hue(a, b) {
++  var d = b - a;
++  return d ? linear(a, d > 180 || d < -180 ? d - 360 * Math.round(d / 360) : d) : constant$3(isNaN(a) ? b : a);
++}
++
++function gamma(y) {
++  return (y = +y) === 1 ? nogamma : function(a, b) {
++    return b - a ? exponential(a, b, y) : constant$3(isNaN(a) ? b : a);
++  };
++}
++
++function nogamma(a, b) {
++  var d = b - a;
++  return d ? linear(a, d) : constant$3(isNaN(a) ? b : a);
++}
++
++var interpolateRgb = (function rgbGamma(y) {
++  var color$$1 = gamma(y);
++
++  function rgb$$1(start, end) {
++    var r = color$$1((start = rgb(start)).r, (end = rgb(end)).r),
++        g = color$$1(start.g, end.g),
++        b = color$$1(start.b, end.b),
++        opacity = nogamma(start.opacity, end.opacity);
++    return function(t) {
++      start.r = r(t);
++      start.g = g(t);
++      start.b = b(t);
++      start.opacity = opacity(t);
++      return start + "";
++    };
++  }
++
++  rgb$$1.gamma = rgbGamma;
++
++  return rgb$$1;
++})(1);
++
++function rgbSpline(spline) {
++  return function(colors) {
++    var n = colors.length,
++        r = new Array(n),
++        g = new Array(n),
++        b = new Array(n),
++        i, color$$1;
++    for (i = 0; i < n; ++i) {
++      color$$1 = rgb(colors[i]);
++      r[i] = color$$1.r || 0;
++      g[i] = color$$1.g || 0;
++      b[i] = color$$1.b || 0;
++    }
++    r = spline(r);
++    g = spline(g);
++    b = spline(b);
++    color$$1.opacity = 1;
++    return function(t) {
++      color$$1.r = r(t);
++      color$$1.g = g(t);
++      color$$1.b = b(t);
++      return color$$1 + "";
++    };
++  };
++}
++
++var rgbBasis = rgbSpline(basis$1);
++var rgbBasisClosed = rgbSpline(basisClosed);
++
++function array$1(a, b) {
++  var nb = b ? b.length : 0,
++      na = a ? Math.min(nb, a.length) : 0,
++      x = new Array(na),
++      c = new Array(nb),
++      i;
++
++  for (i = 0; i < na; ++i) x[i] = interpolateValue(a[i], b[i]);
++  for (; i < nb; ++i) c[i] = b[i];
++
++  return function(t) {
++    for (i = 0; i < na; ++i) c[i] = x[i](t);
++    return c;
++  };
++}
++
++function date(a, b) {
++  var d = new Date;
++  return a = +a, b -= a, function(t) {
++    return d.setTime(a + b * t), d;
++  };
++}
++
++function reinterpolate(a, b) {
++  return a = +a, b -= a, function(t) {
++    return a + b * t;
++  };
++}
++
++function object(a, b) {
++  var i = {},
++      c = {},
++      k;
++
++  if (a === null || typeof a !== "object") a = {};
++  if (b === null || typeof b !== "object") b = {};
++
++  for (k in b) {
++    if (k in a) {
++      i[k] = interpolateValue(a[k], b[k]);
++    } else {
++      c[k] = b[k];
++    }
++  }
++
++  return function(t) {
++    for (k in i) c[k] = i[k](t);
++    return c;
++  };
++}
++
++var reA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,
++    reB = new RegExp(reA.source, "g");
++
++function zero(b) {
++  return function() {
++    return b;
++  };
++}
++
++function one(b) {
++  return function(t) {
++    return b(t) + "";
++  };
++}
++
++function interpolateString(a, b) {
++  var bi = reA.lastIndex = reB.lastIndex = 0, // scan index for next number in b
++      am, // current match in a
++      bm, // current match in b
++      bs, // string preceding current number in b, if any
++      i = -1, // index in s
++      s = [], // string constants and placeholders
++      q = []; // number interpolators
++
++  // Coerce inputs to strings.
++  a = a + "", b = b + "";
++
++  // Interpolate pairs of numbers in a & b.
++  while ((am = reA.exec(a))
++      && (bm = reB.exec(b))) {
++    if ((bs = bm.index) > bi) { // a string precedes the next number in b
++      bs = b.slice(bi, bs);
++      if (s[i]) s[i] += bs; // coalesce with previous string
++      else s[++i] = bs;
++    }
++    if ((am = am[0]) === (bm = bm[0])) { // numbers in a & b match
++      if (s[i]) s[i] += bm; // coalesce with previous string
++      else s[++i] = bm;
++    } else { // interpolate non-matching numbers
++      s[++i] = null;
++      q.push({i: i, x: reinterpolate(am, bm)});
++    }
++    bi = reB.lastIndex;
++  }
++
++  // Add remains of b.
++  if (bi < b.length) {
++    bs = b.slice(bi);
++    if (s[i]) s[i] += bs; // coalesce with previous string
++    else s[++i] = bs;
++  }
++
++  // Special optimization for only a single match.
++  // Otherwise, interpolate each of the numbers and rejoin the string.
++  return s.length < 2 ? (q[0]
++      ? one(q[0].x)
++      : zero(b))
++      : (b = q.length, function(t) {
++          for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t);
++          return s.join("");
++        });
++}
++
++function interpolateValue(a, b) {
++  var t = typeof b, c;
++  return b == null || t === "boolean" ? constant$3(b)
++      : (t === "number" ? reinterpolate
++      : t === "string" ? ((c = color(b)) ? (b = c, interpolateRgb) : interpolateString)
++      : b instanceof color ? interpolateRgb
++      : b instanceof Date ? date
++      : Array.isArray(b) ? array$1
++      : typeof b.valueOf !== "function" && typeof b.toString !== "function" || isNaN(b) ? object
++      : reinterpolate)(a, b);
++}
++
++function interpolateRound(a, b) {
++  return a = +a, b -= a, function(t) {
++    return Math.round(a + b * t);
++  };
++}
++
++var degrees = 180 / Math.PI;
++
++var identity$2 = {
++  translateX: 0,
++  translateY: 0,
++  rotate: 0,
++  skewX: 0,
++  scaleX: 1,
++  scaleY: 1
++};
++
++function decompose(a, b, c, d, e, f) {
++  var scaleX, scaleY, skewX;
++  if (scaleX = Math.sqrt(a * a + b * b)) a /= scaleX, b /= scaleX;
++  if (skewX = a * c + b * d) c -= a * skewX, d -= b * skewX;
++  if (scaleY = Math.sqrt(c * c + d * d)) c /= scaleY, d /= scaleY, skewX /= scaleY;
++  if (a * d < b * c) a = -a, b = -b, skewX = -skewX, scaleX = -scaleX;
++  return {
++    translateX: e,
++    translateY: f,
++    rotate: Math.atan2(b, a) * degrees,
++    skewX: Math.atan(skewX) * degrees,
++    scaleX: scaleX,
++    scaleY: scaleY
++  };
++}
++
++var cssNode,
++    cssRoot,
++    cssView,
++    svgNode;
++
++function parseCss(value) {
++  if (value === "none") return identity$2;
++  if (!cssNode) cssNode = document.createElement("DIV"), cssRoot = document.documentElement, cssView = document.defaultView;
++  cssNode.style.transform = value;
++  value = cssView.getComputedStyle(cssRoot.appendChild(cssNode), null).getPropertyValue("transform");
++  cssRoot.removeChild(cssNode);
++  value = value.slice(7, -1).split(",");
++  return decompose(+value[0], +value[1], +value[2], +value[3], +value[4], +value[5]);
++}
++
++function parseSvg(value) {
++  if (value == null) return identity$2;
++  if (!svgNode) svgNode = document.createElementNS("http://www.w3.org/2000/svg", "g");
++  svgNode.setAttribute("transform", value);
++  if (!(value = svgNode.transform.baseVal.consolidate())) return identity$2;
++  value = value.matrix;
++  return decompose(value.a, value.b, value.c, value.d, value.e, value.f);
++}
++
++function interpolateTransform(parse, pxComma, pxParen, degParen) {
++
++  function pop(s) {
++    return s.length ? s.pop() + " " : "";
++  }
++
++  function translate(xa, ya, xb, yb, s, q) {
++    if (xa !== xb || ya !== yb) {
++      var i = s.push("translate(", null, pxComma, null, pxParen);
++      q.push({i: i - 4, x: reinterpolate(xa, xb)}, {i: i - 2, x: reinterpolate(ya, yb)});
++    } else if (xb || yb) {
++      s.push("translate(" + xb + pxComma + yb + pxParen);
++    }
++  }
++
++  function rotate(a, b, s, q) {
++    if (a !== b) {
++      if (a - b > 180) b += 360; else if (b - a > 180) a += 360; // shortest path
++      q.push({i: s.push(pop(s) + "rotate(", null, degParen) - 2, x: reinterpolate(a, b)});
++    } else if (b) {
++      s.push(pop(s) + "rotate(" + b + degParen);
++    }
++  }
++
++  function skewX(a, b, s, q) {
++    if (a !== b) {
++      q.push({i: s.push(pop(s) + "skewX(", null, degParen) - 2, x: reinterpolate(a, b)});
++    } else if (b) {
++      s.push(pop(s) + "skewX(" + b + degParen);
++    }
++  }
++
++  function scale(xa, ya, xb, yb, s, q) {
++    if (xa !== xb || ya !== yb) {
++      var i = s.push(pop(s) + "scale(", null, ",", null, ")");
++      q.push({i: i - 4, x: reinterpolate(xa, xb)}, {i: i - 2, x: reinterpolate(ya, yb)});
++    } else if (xb !== 1 || yb !== 1) {
++      s.push(pop(s) + "scale(" + xb + "," + yb + ")");
++    }
++  }
++
++  return function(a, b) {
++    var s = [], // string constants and placeholders
++        q = []; // number interpolators
++    a = parse(a), b = parse(b);
++    translate(a.translateX, a.translateY, b.translateX, b.translateY, s, q);
++    rotate(a.rotate, b.rotate, s, q);
++    skewX(a.skewX, b.skewX, s, q);
++    scale(a.scaleX, a.scaleY, b.scaleX, b.scaleY, s, q);
++    a = b = null; // gc
++    return function(t) {
++      var i = -1, n = q.length, o;
++      while (++i < n) s[(o = q[i]).i] = o.x(t);
++      return s.join("");
++    };
++  };
++}
++
++var interpolateTransformCss = interpolateTransform(parseCss, "px, ", "px)", "deg)");
++var interpolateTransformSvg = interpolateTransform(parseSvg, ", ", ")", ")");
++
++var rho = Math.SQRT2,
++    rho2 = 2,
++    rho4 = 4,
++    epsilon2 = 1e-12;
++
++function cosh(x) {
++  return ((x = Math.exp(x)) + 1 / x) / 2;
++}
++
++function sinh(x) {
++  return ((x = Math.exp(x)) - 1 / x) / 2;
++}
++
++function tanh(x) {
++  return ((x = Math.exp(2 * x)) - 1) / (x + 1);
++}
++
++// p0 = [ux0, uy0, w0]
++// p1 = [ux1, uy1, w1]
++function interpolateZoom(p0, p1) {
++  var ux0 = p0[0], uy0 = p0[1], w0 = p0[2],
++      ux1 = p1[0], uy1 = p1[1], w1 = p1[2],
++      dx = ux1 - ux0,
++      dy = uy1 - uy0,
++      d2 = dx * dx + dy * dy,
++      i,
++      S;
++
++  // Special case for u0 ≅ u1.
++  if (d2 < epsilon2) {
++    S = Math.log(w1 / w0) / rho;
++    i = function(t) {
++      return [
++        ux0 + t * dx,
++        uy0 + t * dy,
++        w0 * Math.exp(rho * t * S)
++      ];
++    };
++  }
++
++  // General case.
++  else {
++    var d1 = Math.sqrt(d2),
++        b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2 * w0 * rho2 * d1),
++        b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2 * w1 * rho2 * d1),
++        r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0),
++        r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1);
++    S = (r1 - r0) / rho;
++    i = function(t) {
++      var s = t * S,
++          coshr0 = cosh(r0),
++          u = w0 / (rho2 * d1) * (coshr0 * tanh(rho * s + r0) - sinh(r0));
++      return [
++        ux0 + u * dx,
++        uy0 + u * dy,
++        w0 * coshr0 / cosh(rho * s + r0)
++      ];
++    };
++  }
++
++  i.duration = S * 1000;
++
++  return i;
++}
++
++function hsl$1(hue$$1) {
++  return function(start, end) {
++    var h = hue$$1((start = hsl(start)).h, (end = hsl(end)).h),
++        s = nogamma(start.s, end.s),
++        l = nogamma(start.l, end.l),
++        opacity = nogamma(start.opacity, end.opacity);
++    return function(t) {
++      start.h = h(t);
++      start.s = s(t);
++      start.l = l(t);
++      start.opacity = opacity(t);
++      return start + "";
++    };
++  }
++}
++
++var hsl$2 = hsl$1(hue);
++var hslLong = hsl$1(nogamma);
++
++function lab$1(start, end) {
++  var l = nogamma((start = lab(start)).l, (end = lab(end)).l),
++      a = nogamma(start.a, end.a),
++      b = nogamma(start.b, end.b),
++      opacity = nogamma(start.opacity, end.opacity);
++  return function(t) {
++    start.l = l(t);
++    start.a = a(t);
++    start.b = b(t);
++    start.opacity = opacity(t);
++    return start + "";
++  };
++}
++
++function hcl$1(hue$$1) {
++  return function(start, end) {
++    var h = hue$$1((start = hcl(start)).h, (end = hcl(end)).h),
++        c = nogamma(start.c, end.c),
++        l = nogamma(start.l, end.l),
++        opacity = nogamma(start.opacity, end.opacity);
++    return function(t) {
++      start.h = h(t);
++      start.c = c(t);
++      start.l = l(t);
++      start.opacity = opacity(t);
++      return start + "";
++    };
++  }
++}
++
++var hcl$2 = hcl$1(hue);
++var hclLong = hcl$1(nogamma);
++
++function cubehelix$1(hue$$1) {
++  return (function cubehelixGamma(y) {
++    y = +y;
++
++    function cubehelix$$1(start, end) {
++      var h = hue$$1((start = cubehelix(start)).h, (end = cubehelix(end)).h),
++          s = nogamma(start.s, end.s),
++          l = nogamma(start.l, end.l),
++          opacity = nogamma(start.opacity, end.opacity);
++      return function(t) {
++        start.h = h(t);
++        start.s = s(t);
++        start.l = l(Math.pow(t, y));
++        start.opacity = opacity(t);
++        return start + "";
++      };
++    }
++
++    cubehelix$$1.gamma = cubehelixGamma;
++
++    return cubehelix$$1;
++  })(1);
++}
++
++var cubehelix$2 = cubehelix$1(hue);
++var cubehelixLong = cubehelix$1(nogamma);
++
++function piecewise(interpolate, values) {
++  var i = 0, n = values.length - 1, v = values[0], I = new Array(n < 0 ? 0 : n);
++  while (i < n) I[i] = interpolate(v, v = values[++i]);
++  return function(t) {
++    var i = Math.max(0, Math.min(n - 1, Math.floor(t *= n)));
++    return I[i](t - i);
++  };
++}
++
++function quantize(interpolator, n) {
++  var samples = new Array(n);
++  for (var i = 0; i < n; ++i) samples[i] = interpolator(i / (n - 1));
++  return samples;
++}
++
++var frame = 0, // is an animation frame pending?
++    timeout = 0, // is a timeout pending?
++    interval = 0, // are any timers active?
++    pokeDelay = 1000, // how frequently we check for clock skew
++    taskHead,
++    taskTail,
++    clockLast = 0,
++    clockNow = 0,
++    clockSkew = 0,
++    clock = typeof performance === "object" && performance.now ? performance : Date,
++    setFrame = typeof window === "object" && window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : function(f) { setTimeout(f, 17); };
++
++function now() {
++  return clockNow || (setFrame(clearNow), clockNow = clock.now() + clockSkew);
++}
++
++function clearNow() {
++  clockNow = 0;
++}
++
++function Timer() {
++  this._call =
++  this._time =
++  this._next = null;
++}
++
++Timer.prototype = timer.prototype = {
++  constructor: Timer,
++  restart: function(callback, delay, time) {
++    if (typeof callback !== "function") throw new TypeError("callback is not a function");
++    time = (time == null ? now() : +time) + (delay == null ? 0 : +delay);
++    if (!this._next && taskTail !== this) {
++      if (taskTail) taskTail._next = this;
++      else taskHead = this;
++      taskTail = this;
++    }
++    this._call = callback;
++    this._time = time;
++    sleep();
++  },
++  stop: function() {
++    if (this._call) {
++      this._call = null;
++      this._time = Infinity;
++      sleep();
++    }
++  }
++};
++
++function timer(callback, delay, time) {
++  var t = new Timer;
++  t.restart(callback, delay, time);
++  return t;
++}
++
++function timerFlush() {
++  now(); // Get the current time, if not already set.
++  ++frame; // Pretend we’ve set an alarm, if we haven’t already.
++  var t = taskHead, e;
++  while (t) {
++    if ((e = clockNow - t._time) >= 0) t._call.call(null, e);
++    t = t._next;
++  }
++  --frame;
++}
++
++function wake() {
++  clockNow = (clockLast = clock.now()) + clockSkew;
++  frame = timeout = 0;
++  try {
++    timerFlush();
++  } finally {
++    frame = 0;
++    nap();
++    clockNow = 0;
++  }
++}
++
++function poke() {
++  var now = clock.now(), delay = now - clockLast;
++  if (delay > pokeDelay) clockSkew -= delay, clockLast = now;
++}
++
++function nap() {
++  var t0, t1 = taskHead, t2, time = Infinity;
++  while (t1) {
++    if (t1._call) {
++      if (time > t1._time) time = t1._time;
++      t0 = t1, t1 = t1._next;
++    } else {
++      t2 = t1._next, t1._next = null;
++      t1 = t0 ? t0._next = t2 : taskHead = t2;
++    }
++  }
++  taskTail = t0;
++  sleep(time);
++}
++
++function sleep(time) {
++  if (frame) return; // Soonest alarm already set, or will be.
++  if (timeout) timeout = clearTimeout(timeout);
++  var delay = time - clockNow; // Strictly less than if we recomputed clockNow.
++  if (delay > 24) {
++    if (time < Infinity) timeout = setTimeout(wake, time - clock.now() - clockSkew);
++    if (interval) interval = clearInterval(interval);
++  } else {
++    if (!interval) clockLast = clock.now(), interval = setInterval(poke, pokeDelay);
++    frame = 1, setFrame(wake);
++  }
++}
++
++function timeout$1(callback, delay, time) {
++  var t = new Timer;
++  delay = delay == null ? 0 : +delay;
++  t.restart(function(elapsed) {
++    t.stop();
++    callback(elapsed + delay);
++  }, delay, time);
++  return t;
++}
++
++function interval$1(callback, delay, time) {
++  var t = new Timer, total = delay;
++  if (delay == null) return t.restart(callback, delay, time), t;
++  delay = +delay, time = time == null ? now() : +time;
++  t.restart(function tick(elapsed) {
++    elapsed += total;
++    t.restart(tick, total += delay, time);
++    callback(elapsed);
++  }, delay, time);
++  return t;
++}
++
++var emptyOn = dispatch("start", "end", "interrupt");
++var emptyTween = [];
++
++var CREATED = 0;
++var SCHEDULED = 1;
++var STARTING = 2;
++var STARTED = 3;
++var RUNNING = 4;
++var ENDING = 5;
++var ENDED = 6;
++
++function schedule(node, name, id, index, group, timing) {
++  var schedules = node.__transition;
++  if (!schedules) node.__transition = {};
++  else if (id in schedules) return;
++  create$1(node, id, {
++    name: name,
++    index: index, // For context during callback.
++    group: group, // For context during callback.
++    on: emptyOn,
++    tween: emptyTween,
++    time: timing.time,
++    delay: timing.delay,
++    duration: timing.duration,
++    ease: timing.ease,
++    timer: null,
++    state: CREATED
++  });
++}
++
++function init(node, id) {
++  var schedule = get$1(node, id);
++  if (schedule.state > CREATED) throw new Error("too late; already scheduled");
++  return schedule;
++}
++
++function set$1(node, id) {
++  var schedule = get$1(node, id);
++  if (schedule.state > STARTING) throw new Error("too late; already started");
++  return schedule;
++}
++
++function get$1(node, id) {
++  var schedule = node.__transition;
++  if (!schedule || !(schedule = schedule[id])) throw new Error("transition not found");
++  return schedule;
++}
++
++function create$1(node, id, self) {
++  var schedules = node.__transition,
++      tween;
++
++  // Initialize the self timer when the transition is created.
++  // Note the actual delay is not known until the first callback!
++  schedules[id] = self;
++  self.timer = timer(schedule, 0, self.time);
++
++  function schedule(elapsed) {
++    self.state = SCHEDULED;
++    self.timer.restart(start, self.delay, self.time);
++
++    // If the elapsed delay is less than our first sleep, start immediately.
++    if (self.delay <= elapsed) start(elapsed - self.delay);
++  }
++
++  function start(elapsed) {
++    var i, j, n, o;
++
++    // If the state is not SCHEDULED, then we previously errored on start.
++    if (self.state !== SCHEDULED) return stop();
++
++    for (i in schedules) {
++      o = schedules[i];
++      if (o.name !== self.name) continue;
++
++      // While this element already has a starting transition during this frame,
++      // defer starting an interrupting transition until that transition has a
++      // chance to tick (and possibly end); see d3/d3-transition#54!
++      if (o.state === STARTED) return timeout$1(start);
++
++      // Interrupt the active transition, if any.
++      // Dispatch the interrupt event.
++      if (o.state === RUNNING) {
++        o.state = ENDED;
++        o.timer.stop();
++        o.on.call("interrupt", node, node.__data__, o.index, o.group);
++        delete schedules[i];
++      }
++
++      // Cancel any pre-empted transitions. No interrupt event is dispatched
++      // because the cancelled transitions never started. Note that this also
++      // removes this transition from the pending list!
++      else if (+i < id) {
++        o.state = ENDED;
++        o.timer.stop();
++        delete schedules[i];
++      }
++    }
++
++    // Defer the first tick to end of the current frame; see d3/d3#1576.
++    // Note the transition may be canceled after start and before the first tick!
++    // Note this must be scheduled before the start event; see d3/d3-transition#16!
++    // Assuming this is successful, subsequent callbacks go straight to tick.
++    timeout$1(function() {
++      if (self.state === STARTED) {
++        self.state = RUNNING;
++        self.timer.restart(tick, self.delay, self.time);
++        tick(elapsed);
++      }
++    });
++
++    // Dispatch the start event.
++    // Note this must be done before the tween are initialized.
++    self.state = STARTING;
++    self.on.call("start", node, node.__data__, self.index, self.group);
++    if (self.state !== STARTING) return; // interrupted
++    self.state = STARTED;
++
++    // Initialize the tween, deleting null tween.
++    tween = new Array(n = self.tween.length);
++    for (i = 0, j = -1; i < n; ++i) {
++      if (o = self.tween[i].value.call(node, node.__data__, self.index, self.group)) {
++        tween[++j] = o;
++      }
++    }
++    tween.length = j + 1;
++  }
++
++  function tick(elapsed) {
++    var t = elapsed < self.duration ? self.ease.call(null, elapsed / self.duration) : (self.timer.restart(stop), self.state = ENDING, 1),
++        i = -1,
++        n = tween.length;
++
++    while (++i < n) {
++      tween[i].call(null, t);
++    }
++
++    // Dispatch the end event.
++    if (self.state === ENDING) {
++      self.on.call("end", node, node.__data__, self.index, self.group);
++      stop();
++    }
++  }
++
++  function stop() {
++    self.state = ENDED;
++    self.timer.stop();
++    delete schedules[id];
++    for (var i in schedules) return; // eslint-disable-line no-unused-vars
++    delete node.__transition;
++  }
++}
++
++function interrupt(node, name) {
++  var schedules = node.__transition,
++      schedule$$1,
++      active,
++      empty = true,
++      i;
++
++  if (!schedules) return;
++
++  name = name == null ? null : name + "";
++
++  for (i in schedules) {
++    if ((schedule$$1 = schedules[i]).name !== name) { empty = false; continue; }
++    active = schedule$$1.state > STARTING && schedule$$1.state < ENDING;
++    schedule$$1.state = ENDED;
++    schedule$$1.timer.stop();
++    if (active) schedule$$1.on.call("interrupt", node, node.__data__, schedule$$1.index, schedule$$1.group);
++    delete schedules[i];
++  }
++
++  if (empty) delete node.__transition;
++}
++
++function selection_interrupt(name) {
++  return this.each(function() {
++    interrupt(this, name);
++  });
++}
++
++function tweenRemove(id, name) {
++  var tween0, tween1;
++  return function() {
++    var schedule$$1 = set$1(this, id),
++        tween = schedule$$1.tween;
++
++    // If this node shared tween with the previous node,
++    // just assign the updated shared tween and we’re done!
++    // Otherwise, copy-on-write.
++    if (tween !== tween0) {
++      tween1 = tween0 = tween;
++      for (var i = 0, n = tween1.length; i < n; ++i) {
++        if (tween1[i].name === name) {
++          tween1 = tween1.slice();
++          tween1.splice(i, 1);
++          break;
++        }
++      }
++    }
++
++    schedule$$1.tween = tween1;
++  };
++}
++
++function tweenFunction(id, name, value) {
++  var tween0, tween1;
++  if (typeof value !== "function") throw new Error;
++  return function() {
++    var schedule$$1 = set$1(this, id),
++        tween = schedule$$1.tween;
++
++    // If this node shared tween with the previous node,
++    // just assign the updated shared tween and we’re done!
++    // Otherwise, copy-on-write.
++    if (tween !== tween0) {
++      tween1 = (tween0 = tween).slice();
++      for (var t = {name: name, value: value}, i = 0, n = tween1.length; i < n; ++i) {
++        if (tween1[i].name === name) {
++          tween1[i] = t;
++          break;
++        }
++      }
++      if (i === n) tween1.push(t);
++    }
++
++    schedule$$1.tween = tween1;
++  };
++}
++
++function transition_tween(name, value) {
++  var id = this._id;
++
++  name += "";
++
++  if (arguments.length < 2) {
++    var tween = get$1(this.node(), id).tween;
++    for (var i = 0, n = tween.length, t; i < n; ++i) {
++      if ((t = tween[i]).name === name) {
++        return t.value;
++      }
++    }
++    return null;
++  }
++
++  return this.each((value == null ? tweenRemove : tweenFunction)(id, name, value));
++}
++
++function tweenValue(transition, name, value) {
++  var id = transition._id;
++
++  transition.each(function() {
++    var schedule$$1 = set$1(this, id);
++    (schedule$$1.value || (schedule$$1.value = {}))[name] = value.apply(this, arguments);
++  });
++
++  return function(node) {
++    return get$1(node, id).value[name];
++  };
++}
++
++function interpolate(a, b) {
++  var c;
++  return (typeof b === "number" ? reinterpolate
++      : b instanceof color ? interpolateRgb
++      : (c = color(b)) ? (b = c, interpolateRgb)
++      : interpolateString)(a, b);
++}
++
++function attrRemove$1(name) {
++  return function() {
++    this.removeAttribute(name);
++  };
++}
++
++function attrRemoveNS$1(fullname) {
++  return function() {
++    this.removeAttributeNS(fullname.space, fullname.local);
++  };
++}
++
++function attrConstant$1(name, interpolate$$1, value1) {
++  var value00,
++      interpolate0;
++  return function() {
++    var value0 = this.getAttribute(name);
++    return value0 === value1 ? null
++        : value0 === value00 ? interpolate0
++        : interpolate0 = interpolate$$1(value00 = value0, value1);
++  };
++}
++
++function attrConstantNS$1(fullname, interpolate$$1, value1) {
++  var value00,
++      interpolate0;
++  return function() {
++    var value0 = this.getAttributeNS(fullname.space, fullname.local);
++    return value0 === value1 ? null
++        : value0 === value00 ? interpolate0
++        : interpolate0 = interpolate$$1(value00 = value0, value1);
++  };
++}
++
++function attrFunction$1(name, interpolate$$1, value) {
++  var value00,
++      value10,
++      interpolate0;
++  return function() {
++    var value0, value1 = value(this);
++    if (value1 == null) return void this.removeAttribute(name);
++    value0 = this.getAttribute(name);
++    return value0 === value1 ? null
++        : value0 === value00 && value1 === value10 ? interpolate0
++        : interpolate0 = interpolate$$1(value00 = value0, value10 = value1);
++  };
++}
++
++function attrFunctionNS$1(fullname, interpolate$$1, value) {
++  var value00,
++      value10,
++      interpolate0;
++  return function() {
++    var value0, value1 = value(this);
++    if (value1 == null) return void this.removeAttributeNS(fullname.space, fullname.local);
++    value0 = this.getAttributeNS(fullname.space, fullname.local);
++    return value0 === value1 ? null
++        : value0 === value00 && value1 === value10 ? interpolate0
++        : interpolate0 = interpolate$$1(value00 = value0, value10 = value1);
++  };
++}
++
++function transition_attr(name, value) {
++  var fullname = namespace(name), i = fullname === "transform" ? interpolateTransformSvg : interpolate;
++  return this.attrTween(name, typeof value === "function"
++      ? (fullname.local ? attrFunctionNS$1 : attrFunction$1)(fullname, i, tweenValue(this, "attr." + name, value))
++      : value == null ? (fullname.local ? attrRemoveNS$1 : attrRemove$1)(fullname)
++      : (fullname.local ? attrConstantNS$1 : attrConstant$1)(fullname, i, value + ""));
++}
++
++function attrTweenNS(fullname, value) {
++  function tween() {
++    var node = this, i = value.apply(node, arguments);
++    return i && function(t) {
++      node.setAttributeNS(fullname.space, fullname.local, i(t));
++    };
++  }
++  tween._value = value;
++  return tween;
++}
++
++function attrTween(name, value) {
++  function tween() {
++    var node = this, i = value.apply(node, arguments);
++    return i && function(t) {
++      node.setAttribute(name, i(t));
++    };
++  }
++  tween._value = value;
++  return tween;
++}
++
++function transition_attrTween(name, value) {
++  var key = "attr." + name;
++  if (arguments.length < 2) return (key = this.tween(key)) && key._value;
++  if (value == null) return this.tween(key, null);
++  if (typeof value !== "function") throw new Error;
++  var fullname = namespace(name);
++  return this.tween(key, (fullname.local ? attrTweenNS : attrTween)(fullname, value));
++}
++
++function delayFunction(id, value) {
++  return function() {
++    init(this, id).delay = +value.apply(this, arguments);
++  };
++}
++
++function delayConstant(id, value) {
++  return value = +value, function() {
++    init(this, id).delay = value;
++  };
++}
++
++function transition_delay(value) {
++  var id = this._id;
++
++  return arguments.length
++      ? this.each((typeof value === "function"
++          ? delayFunction
++          : delayConstant)(id, value))
++      : get$1(this.node(), id).delay;
++}
++
++function durationFunction(id, value) {
++  return function() {
++    set$1(this, id).duration = +value.apply(this, arguments);
++  };
++}
++
++function durationConstant(id, value) {
++  return value = +value, function() {
++    set$1(this, id).duration = value;
++  };
++}
++
++function transition_duration(value) {
++  var id = this._id;
++
++  return arguments.length
++      ? this.each((typeof value === "function"
++          ? durationFunction
++          : durationConstant)(id, value))
++      : get$1(this.node(), id).duration;
++}
++
++function easeConstant(id, value) {
++  if (typeof value !== "function") throw new Error;
++  return function() {
++    set$1(this, id).ease = value;
++  };
++}
++
++function transition_ease(value) {
++  var id = this._id;
++
++  return arguments.length
++      ? this.each(easeConstant(id, value))
++      : get$1(this.node(), id).ease;
++}
++
++function transition_filter(match) {
++  if (typeof match !== "function") match = matcher$1(match);
++
++  for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
++    for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {
++      if ((node = group[i]) && match.call(node, node.__data__, i, group)) {
++        subgroup.push(node);
++      }
++    }
++  }
++
++  return new Transition(subgroups, this._parents, this._name, this._id);
++}
++
++function transition_merge(transition$$1) {
++  if (transition$$1._id !== this._id) throw new Error;
++
++  for (var groups0 = this._groups, groups1 = transition$$1._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) {
++    for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {
++      if (node = group0[i] || group1[i]) {
++        merge[i] = node;
++      }
++    }
++  }
++
++  for (; j < m0; ++j) {
++    merges[j] = groups0[j];
++  }
++
++  return new Transition(merges, this._parents, this._name, this._id);
++}
++
++function start(name) {
++  return (name + "").trim().split(/^|\s+/).every(function(t) {
++    var i = t.indexOf(".");
++    if (i >= 0) t = t.slice(0, i);
++    return !t || t === "start";
++  });
++}
++
++function onFunction(id, name, listener) {
++  var on0, on1, sit = start(name) ? init : set$1;
++  return function() {
++    var schedule$$1 = sit(this, id),
++        on = schedule$$1.on;
++
++    // If this node shared a dispatch with the previous node,
++    // just assign the updated shared dispatch and we’re done!
++    // Otherwise, copy-on-write.
++    if (on !== on0) (on1 = (on0 = on).copy()).on(name, listener);
++
++    schedule$$1.on = on1;
++  };
++}
++
++function transition_on(name, listener) {
++  var id = this._id;
++
++  return arguments.length < 2
++      ? get$1(this.node(), id).on.on(name)
++      : this.each(onFunction(id, name, listener));
++}
++
++function removeFunction(id) {
++  return function() {
++    var parent = this.parentNode;
++    for (var i in this.__transition) if (+i !== id) return;
++    if (parent) parent.removeChild(this);
++  };
++}
++
++function transition_remove() {
++  return this.on("end.remove", removeFunction(this._id));
++}
++
++function transition_select(select$$1) {
++  var name = this._name,
++      id = this._id;
++
++  if (typeof select$$1 !== "function") select$$1 = selector(select$$1);
++
++  for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
++    for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {
++      if ((node = group[i]) && (subnode = select$$1.call(node, node.__data__, i, group))) {
++        if ("__data__" in node) subnode.__data__ = node.__data__;
++        subgroup[i] = subnode;
++        schedule(subgroup[i], name, id, i, subgroup, get$1(node, id));
++      }
++    }
++  }
++
++  return new Transition(subgroups, this._parents, name, id);
++}
++
++function transition_selectAll(select$$1) {
++  var name = this._name,
++      id = this._id;
++
++  if (typeof select$$1 !== "function") select$$1 = selectorAll(select$$1);
++
++  for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {
++    for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
++      if (node = group[i]) {
++        for (var children = select$$1.call(node, node.__data__, i, group), child, inherit = get$1(node, id), k = 0, l = children.length; k < l; ++k) {
++          if (child = children[k]) {
++            schedule(child, name, id, k, children, inherit);
++          }
++        }
++        subgroups.push(children);
++        parents.push(node);
++      }
++    }
++  }
++
++  return new Transition(subgroups, parents, name, id);
++}
++
++var Selection$1 = selection.prototype.constructor;
++
++function transition_selection() {
++  return new Selection$1(this._groups, this._parents);
++}
++
++function styleRemove$1(name, interpolate$$1) {
++  var value00,
++      value10,
++      interpolate0;
++  return function() {
++    var value0 = styleValue(this, name),
++        value1 = (this.style.removeProperty(name), styleValue(this, name));
++    return value0 === value1 ? null
++        : value0 === value00 && value1 === value10 ? interpolate0
++        : interpolate0 = interpolate$$1(value00 = value0, value10 = value1);
++  };
++}
++
++function styleRemoveEnd(name) {
++  return function() {
++    this.style.removeProperty(name);
++  };
++}
++
++function styleConstant$1(name, interpolate$$1, value1) {
++  var value00,
++      interpolate0;
++  return function() {
++    var value0 = styleValue(this, name);
++    return value0 === value1 ? null
++        : value0 === value00 ? interpolate0
++        : interpolate0 = interpolate$$1(value00 = value0, value1);
++  };
++}
++
++function styleFunction$1(name, interpolate$$1, value) {
++  var value00,
++      value10,
++      interpolate0;
++  return function() {
++    var value0 = styleValue(this, name),
++        value1 = value(this);
++    if (value1 == null) value1 = (this.style.removeProperty(name), styleValue(this, name));
++    return value0 === value1 ? null
++        : value0 === value00 && value1 === value10 ? interpolate0
++        : interpolate0 = interpolate$$1(value00 = value0, value10 = value1);
++  };
++}
++
++function transition_style(name, value, priority) {
++  var i = (name += "") === "transform" ? interpolateTransformCss : interpolate;
++  return value == null ? this
++          .styleTween(name, styleRemove$1(name, i))
++          .on("end.style." + name, styleRemoveEnd(name))
++      : this.styleTween(name, typeof value === "function"
++          ? styleFunction$1(name, i, tweenValue(this, "style." + name, value))
++          : styleConstant$1(name, i, value + ""), priority);
++}
++
++function styleTween(name, value, priority) {
++  function tween() {
++    var node = this, i = value.apply(node, arguments);
++    return i && function(t) {
++      node.style.setProperty(name, i(t), priority);
++    };
++  }
++  tween._value = value;
++  return tween;
++}
++
++function transition_styleTween(name, value, priority) {
++  var key = "style." + (name += "");
++  if (arguments.length < 2) return (key = this.tween(key)) && key._value;
++  if (value == null) return this.tween(key, null);
++  if (typeof value !== "function") throw new Error;
++  return this.tween(key, styleTween(name, value, priority == null ? "" : priority));
++}
++
++function textConstant$1(value) {
++  return function() {
++    this.textContent = value;
++  };
++}
++
++function textFunction$1(value) {
++  return function() {
++    var value1 = value(this);
++    this.textContent = value1 == null ? "" : value1;
++  };
++}
++
++function transition_text(value) {
++  return this.tween("text", typeof value === "function"
++      ? textFunction$1(tweenValue(this, "text", value))
++      : textConstant$1(value == null ? "" : value + ""));
++}
++
++function transition_transition() {
++  var name = this._name,
++      id0 = this._id,
++      id1 = newId();
++
++  for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {
++    for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
++      if (node = group[i]) {
++        var inherit = get$1(node, id0);
++        schedule(node, name, id1, i, group, {
++          time: inherit.time + inherit.delay + inherit.duration,
++          delay: 0,
++          duration: inherit.duration,
++          ease: inherit.ease
++        });
++      }
++    }
++  }
++
++  return new Transition(groups, this._parents, name, id1);
++}
++
++var id = 0;
++
++function Transition(groups, parents, name, id) {
++  this._groups = groups;
++  this._parents = parents;
++  this._name = name;
++  this._id = id;
++}
++
++function transition(name) {
++  return selection().transition(name);
++}
++
++function newId() {
++  return ++id;
++}
++
++var selection_prototype = selection.prototype;
++
++Transition.prototype = transition.prototype = {
++  constructor: Transition,
++  select: transition_select,
++  selectAll: transition_selectAll,
++  filter: transition_filter,
++  merge: transition_merge,
++  selection: transition_selection,
++  transition: transition_transition,
++  call: selection_prototype.call,
++  nodes: selection_prototype.nodes,
++  node: selection_prototype.node,
++  size: selection_prototype.size,
++  empty: selection_prototype.empty,
++  each: selection_prototype.each,
++  on: transition_on,
++  attr: transition_attr,
++  attrTween: transition_attrTween,
++  style: transition_style,
++  styleTween: transition_styleTween,
++  text: transition_text,
++  remove: transition_remove,
++  tween: transition_tween,
++  delay: transition_delay,
++  duration: transition_duration,
++  ease: transition_ease
++};
++
++function linear$1(t) {
++  return +t;
++}
++
++function quadIn(t) {
++  return t * t;
++}
++
++function quadOut(t) {
++  return t * (2 - t);
++}
++
++function quadInOut(t) {
++  return ((t *= 2) <= 1 ? t * t : --t * (2 - t) + 1) / 2;
++}
++
++function cubicIn(t) {
++  return t * t * t;
++}
++
++function cubicOut(t) {
++  return --t * t * t + 1;
++}
++
++function cubicInOut(t) {
++  return ((t *= 2) <= 1 ? t * t * t : (t -= 2) * t * t + 2) / 2;
++}
++
++var exponent = 3;
++
++var polyIn = (function custom(e) {
++  e = +e;
++
++  function polyIn(t) {
++    return Math.pow(t, e);
++  }
++
++  polyIn.exponent = custom;
++
++  return polyIn;
++})(exponent);
++
++var polyOut = (function custom(e) {
++  e = +e;
++
++  function polyOut(t) {
++    return 1 - Math.pow(1 - t, e);
++  }
++
++  polyOut.exponent = custom;
++
++  return polyOut;
++})(exponent);
++
++var polyInOut = (function custom(e) {
++  e = +e;
++
++  function polyInOut(t) {
++    return ((t *= 2) <= 1 ? Math.pow(t, e) : 2 - Math.pow(2 - t, e)) / 2;
++  }
++
++  polyInOut.exponent = custom;
++
++  return polyInOut;
++})(exponent);
++
++var pi = Math.PI,
++    halfPi = pi / 2;
++
++function sinIn(t) {
++  return 1 - Math.cos(t * halfPi);
++}
++
++function sinOut(t) {
++  return Math.sin(t * halfPi);
++}
++
++function sinInOut(t) {
++  return (1 - Math.cos(pi * t)) / 2;
++}
++
++function expIn(t) {
++  return Math.pow(2, 10 * t - 10);
++}
++
++function expOut(t) {
++  return 1 - Math.pow(2, -10 * t);
++}
++
++function expInOut(t) {
++  return ((t *= 2) <= 1 ? Math.pow(2, 10 * t - 10) : 2 - Math.pow(2, 10 - 10 * t)) / 2;
++}
++
++function circleIn(t) {
++  return 1 - Math.sqrt(1 - t * t);
++}
++
++function circleOut(t) {
++  return Math.sqrt(1 - --t * t);
++}
++
++function circleInOut(t) {
++  return ((t *= 2) <= 1 ? 1 - Math.sqrt(1 - t * t) : Math.sqrt(1 - (t -= 2) * t) + 1) / 2;
++}
++
++var b1 = 4 / 11,
++    b2 = 6 / 11,
++    b3 = 8 / 11,
++    b4 = 3 / 4,
++    b5 = 9 / 11,
++    b6 = 10 / 11,
++    b7 = 15 / 16,
++    b8 = 21 / 22,
++    b9 = 63 / 64,
++    b0 = 1 / b1 / b1;
++
++function bounceIn(t) {
++  return 1 - bounceOut(1 - t);
++}
++
++function bounceOut(t) {
++  return (t = +t) < b1 ? b0 * t * t : t < b3 ? b0 * (t -= b2) * t + b4 : t < b6 ? b0 * (t -= b5) * t + b7 : b0 * (t -= b8) * t + b9;
++}
++
++function bounceInOut(t) {
++  return ((t *= 2) <= 1 ? 1 - bounceOut(1 - t) : bounceOut(t - 1) + 1) / 2;
++}
++
++var overshoot = 1.70158;
++
++var backIn = (function custom(s) {
++  s = +s;
++
++  function backIn(t) {
++    return t * t * ((s + 1) * t - s);
++  }
++
++  backIn.overshoot = custom;
++
++  return backIn;
++})(overshoot);
++
++var backOut = (function custom(s) {
++  s = +s;
++
++  function backOut(t) {
++    return --t * t * ((s + 1) * t + s) + 1;
++  }
++
++  backOut.overshoot = custom;
++
++  return backOut;
++})(overshoot);
++
++var backInOut = (function custom(s) {
++  s = +s;
++
++  function backInOut(t) {
++    return ((t *= 2) < 1 ? t * t * ((s + 1) * t - s) : (t -= 2) * t * ((s + 1) * t + s) + 2) / 2;
++  }
++
++  backInOut.overshoot = custom;
++
++  return backInOut;
++})(overshoot);
++
++var tau = 2 * Math.PI,
++    amplitude = 1,
++    period = 0.3;
++
++var elasticIn = (function custom(a, p) {
++  var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau);
++
++  function elasticIn(t) {
++    return a * Math.pow(2, 10 * --t) * Math.sin((s - t) / p);
++  }
++
++  elasticIn.amplitude = function(a) { return custom(a, p * tau); };
++  elasticIn.period = function(p) { return custom(a, p); };
++
++  return elasticIn;
++})(amplitude, period);
++
++var elasticOut = (function custom(a, p) {
++  var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau);
++
++  function elasticOut(t) {
++    return 1 - a * Math.pow(2, -10 * (t = +t)) * Math.sin((t + s) / p);
++  }
++
++  elasticOut.amplitude = function(a) { return custom(a, p * tau); };
++  elasticOut.period = function(p) { return custom(a, p); };
++
++  return elasticOut;
++})(amplitude, period);
++
++var elasticInOut = (function custom(a, p) {
++  var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau);
++
++  function elasticInOut(t) {
++    return ((t = t * 2 - 1) < 0
++        ? a * Math.pow(2, 10 * t) * Math.sin((s - t) / p)
++        : 2 - a * Math.pow(2, -10 * t) * Math.sin((s + t) / p)) / 2;
++  }
++
++  elasticInOut.amplitude = function(a) { return custom(a, p * tau); };
++  elasticInOut.period = function(p) { return custom(a, p); };
++
++  return elasticInOut;
++})(amplitude, period);
++
++var defaultTiming = {
++  time: null, // Set on use.
++  delay: 0,
++  duration: 250,
++  ease: cubicInOut
++};
++
++function inherit(node, id) {
++  var timing;
++  while (!(timing = node.__transition) || !(timing = timing[id])) {
++    if (!(node = node.parentNode)) {
++      return defaultTiming.time = now(), defaultTiming;
++    }
++  }
++  return timing;
++}
++
++function selection_transition(name) {
++  var id,
++      timing;
++
++  if (name instanceof Transition) {
++    id = name._id, name = name._name;
++  } else {
++    id = newId(), (timing = defaultTiming).time = now(), name = name == null ? null : name + "";
++  }
++
++  for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {
++    for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
++      if (node = group[i]) {
++        schedule(node, name, id, i, group, timing || inherit(node, id));
++      }
++    }
++  }
++
++  return new Transition(groups, this._parents, name, id);
++}
++
++selection.prototype.interrupt = selection_interrupt;
++selection.prototype.transition = selection_transition;
++
++var root$1 = [null];
++
++function active(node, name) {
++  var schedules = node.__transition,
++      schedule$$1,
++      i;
++
++  if (schedules) {
++    name = name == null ? null : name + "";
++    for (i in schedules) {
++      if ((schedule$$1 = schedules[i]).state > SCHEDULED && schedule$$1.name === name) {
++        return new Transition([[node]], root$1, name, +i);
++      }
++    }
++  }
++
++  return null;
++}
++
++function constant$4(x) {
++  return function() {
++    return x;
++  };
++}
++
++function BrushEvent(target, type, selection) {
++  this.target = target;
++  this.type = type;
++  this.selection = selection;
++}
++
++function nopropagation$1() {
++  exports.event.stopImmediatePropagation();
++}
++
++function noevent$1() {
++  exports.event.preventDefault();
++  exports.event.stopImmediatePropagation();
++}
++
++var MODE_DRAG = {name: "drag"},
++    MODE_SPACE = {name: "space"},
++    MODE_HANDLE = {name: "handle"},
++    MODE_CENTER = {name: "center"};
++
++var X = {
++  name: "x",
++  handles: ["e", "w"].map(type),
++  input: function(x, e) { return x && [[x[0], e[0][1]], [x[1], e[1][1]]]; },
++  output: function(xy) { return xy && [xy[0][0], xy[1][0]]; }
++};
++
++var Y = {
++  name: "y",
++  handles: ["n", "s"].map(type),
++  input: function(y, e) { return y && [[e[0][0], y[0]], [e[1][0], y[1]]]; },
++  output: function(xy) { return xy && [xy[0][1], xy[1][1]]; }
++};
++
++var XY = {
++  name: "xy",
++  handles: ["n", "e", "s", "w", "nw", "ne", "se", "sw"].map(type),
++  input: function(xy) { return xy; },
++  output: function(xy) { return xy; }
++};
++
++var cursors = {
++  overlay: "crosshair",
++  selection: "move",
++  n: "ns-resize",
++  e: "ew-resize",
++  s: "ns-resize",
++  w: "ew-resize",
++  nw: "nwse-resize",
++  ne: "nesw-resize",
++  se: "nwse-resize",
++  sw: "nesw-resize"
++};
++
++var flipX = {
++  e: "w",
++  w: "e",
++  nw: "ne",
++  ne: "nw",
++  se: "sw",
++  sw: "se"
++};
++
++var flipY = {
++  n: "s",
++  s: "n",
++  nw: "sw",
++  ne: "se",
++  se: "ne",
++  sw: "nw"
++};
++
++var signsX = {
++  overlay: +1,
++  selection: +1,
++  n: null,
++  e: +1,
++  s: null,
++  w: -1,
++  nw: -1,
++  ne: +1,
++  se: +1,
++  sw: -1
++};
++
++var signsY = {
++  overlay: +1,
++  selection: +1,
++  n: -1,
++  e: null,
++  s: +1,
++  w: null,
++  nw: -1,
++  ne: -1,
++  se: +1,
++  sw: +1
++};
++
++function type(t) {
++  return {type: t};
++}
++
++// Ignore right-click, since that should open the context menu.
++function defaultFilter$1() {
++  return !exports.event.button;
++}
++
++function defaultExtent() {
++  var svg = this.ownerSVGElement || this;
++  return [[0, 0], [svg.width.baseVal.value, svg.height.baseVal.value]];
++}
++
++// Like d3.local, but with the name “__brush” rather than auto-generated.
++function local$1(node) {
++  while (!node.__brush) if (!(node = node.parentNode)) return;
++  return node.__brush;
++}
++
++function empty$1(extent) {
++  return extent[0][0] === extent[1][0]
++      || extent[0][1] === extent[1][1];
++}
++
++function brushSelection(node) {
++  var state = node.__brush;
++  return state ? state.dim.output(state.selection) : null;
++}
++
++function brushX() {
++  return brush$1(X);
++}
++
++function brushY() {
++  return brush$1(Y);
++}
++
++function brush() {
++  return brush$1(XY);
++}
++
++function brush$1(dim) {
++  var extent = defaultExtent,
++      filter = defaultFilter$1,
++      listeners = dispatch(brush, "start", "brush", "end"),
++      handleSize = 6,
++      touchending;
++
++  function brush(group) {
++    var overlay = group
++        .property("__brush", initialize)
++      .selectAll(".overlay")
++      .data([type("overlay")]);
++
++    overlay.enter().append("rect")
++        .attr("class", "overlay")
++        .attr("pointer-events", "all")
++        .attr("cursor", cursors.overlay)
++      .merge(overlay)
++        .each(function() {
++          var extent = local$1(this).extent;
++          select(this)
++              .attr("x", extent[0][0])
++              .attr("y", extent[0][1])
++              .attr("width", extent[1][0] - extent[0][0])
++              .attr("height", extent[1][1] - extent[0][1]);
++        });
++
++    group.selectAll(".selection")
++      .data([type("selection")])
++      .enter().append("rect")
++        .attr("class", "selection")
++        .attr("cursor", cursors.selection)
++        .attr("fill", "#777")
++        .attr("fill-opacity", 0.3)
++        .attr("stroke", "#fff")
++        .attr("shape-rendering", "crispEdges");
++
++    var handle = group.selectAll(".handle")
++      .data(dim.handles, function(d) { return d.type; });
++
++    handle.exit().remove();
++
++    handle.enter().append("rect")
++        .attr("class", function(d) { return "handle handle--" + d.type; })
++        .attr("cursor", function(d) { return cursors[d.type]; });
++
++    group
++        .each(redraw)
++        .attr("fill", "none")
++        .attr("pointer-events", "all")
++        .style("-webkit-tap-highlight-color", "rgba(0,0,0,0)")
++        .on("mousedown.brush touchstart.brush", started);
++  }
++
++  brush.move = function(group, selection$$1) {
++    if (group.selection) {
++      group
++          .on("start.brush", function() { emitter(this, arguments).beforestart().start(); })
++          .on("interrupt.brush end.brush", function() { emitter(this, arguments).end(); })
++          .tween("brush", function() {
++            var that = this,
++                state = that.__brush,
++                emit = emitter(that, arguments),
++                selection0 = state.selection,
++                selection1 = dim.input(typeof selection$$1 === "function" ? selection$$1.apply(this, arguments) : selection$$1, state.extent),
++                i = interpolateValue(selection0, selection1);
++
++            function tween(t) {
++              state.selection = t === 1 && empty$1(selection1) ? null : i(t);
++              redraw.call(that);
++              emit.brush();
++            }
++
++            return selection0 && selection1 ? tween : tween(1);
++          });
++    } else {
++      group
++          .each(function() {
++            var that = this,
++                args = arguments,
++                state = that.__brush,
++                selection1 = dim.input(typeof selection$$1 === "function" ? selection$$1.apply(that, args) : selection$$1, state.extent),
++                emit = emitter(that, args).beforestart();
++
++            interrupt(that);
++            state.selection = selection1 == null || empty$1(selection1) ? null : selection1;
++            redraw.call(that);
++            emit.start().brush().end();
++          });
++    }
++  };
++
++  function redraw() {
++    var group = select(this),
++        selection$$1 = local$1(this).selection;
++
++    if (selection$$1) {
++      group.selectAll(".selection")
++          .style("display", null)
++          .attr("x", selection$$1[0][0])
++          .attr("y", selection$$1[0][1])
++          .attr("width", selection$$1[1][0] - selection$$1[0][0])
++          .attr("height", selection$$1[1][1] - selection$$1[0][1]);
++
++      group.selectAll(".handle")
++          .style("display", null)
++          .attr("x", function(d) { return d.type[d.type.length - 1] === "e" ? selection$$1[1][0] - handleSize / 2 : selection$$1[0][0] - handleSize / 2; })
++          .attr("y", function(d) { return d.type[0] === "s" ? selection$$1[1][1] - handleSize / 2 : selection$$1[0][1] - handleSize / 2; })
++          .attr("width", function(d) { return d.type === "n" || d.type === "s" ? selection$$1[1][0] - selection$$1[0][0] + handleSize : handleSize; })
++          .attr("height", function(d) { return d.type === "e" || d.type === "w" ? selection$$1[1][1] - selection$$1[0][1] + handleSize : handleSize; });
++    }
++
++    else {
++      group.selectAll(".selection,.handle")
++          .style("display", "none")
++          .attr("x", null)
++          .attr("y", null)
++          .attr("width", null)
++          .attr("height", null);
++    }
++  }
++
++  function emitter(that, args) {
++    return that.__brush.emitter || new Emitter(that, args);
++  }
++
++  function Emitter(that, args) {
++    this.that = that;
++    this.args = args;
++    this.state = that.__brush;
++    this.active = 0;
++  }
++
++  Emitter.prototype = {
++    beforestart: function() {
++      if (++this.active === 1) this.state.emitter = this, this.starting = true;
++      return this;
++    },
++    start: function() {
++      if (this.starting) this.starting = false, this.emit("start");
++      return this;
++    },
++    brush: function() {
++      this.emit("brush");
++      return this;
++    },
++    end: function() {
++      if (--this.active === 0) delete this.state.emitter, this.emit("end");
++      return this;
++    },
++    emit: function(type) {
++      customEvent(new BrushEvent(brush, type, dim.output(this.state.selection)), listeners.apply, listeners, [type, this.that, this.args]);
++    }
++  };
++
++  function started() {
++    if (exports.event.touches) { if (exports.event.changedTouches.length < exports.event.touches.length) return noevent$1(); }
++    else if (touchending) return;
++    if (!filter.apply(this, arguments)) return;
++
++    var that = this,
++        type = exports.event.target.__data__.type,
++        mode = (exports.event.metaKey ? type = "overlay" : type) === "selection" ? MODE_DRAG : (exports.event.altKey ? MODE_CENTER : MODE_HANDLE),
++        signX = dim === Y ? null : signsX[type],
++        signY = dim === X ? null : signsY[type],
++        state = local$1(that),
++        extent = state.extent,
++        selection$$1 = state.selection,
++        W = extent[0][0], w0, w1,
++        N = extent[0][1], n0, n1,
++        E = extent[1][0], e0, e1,
++        S = extent[1][1], s0, s1,
++        dx,
++        dy,
++        moving,
++        shifting = signX && signY && exports.event.shiftKey,
++        lockX,
++        lockY,
++        point0 = mouse(that),
++        point$$1 = point0,
++        emit = emitter(that, arguments).beforestart();
++
++    if (type === "overlay") {
++      state.selection = selection$$1 = [
++        [w0 = dim === Y ? W : point0[0], n0 = dim === X ? N : point0[1]],
++        [e0 = dim === Y ? E : w0, s0 = dim === X ? S : n0]
++      ];
++    } else {
++      w0 = selection$$1[0][0];
++      n0 = selection$$1[0][1];
++      e0 = selection$$1[1][0];
++      s0 = selection$$1[1][1];
++    }
++
++    w1 = w0;
++    n1 = n0;
++    e1 = e0;
++    s1 = s0;
++
++    var group = select(that)
++        .attr("pointer-events", "none");
++
++    var overlay = group.selectAll(".overlay")
++        .attr("cursor", cursors[type]);
++
++    if (exports.event.touches) {
++      group
++          .on("touchmove.brush", moved, true)
++          .on("touchend.brush touchcancel.brush", ended, true);
++    } else {
++      var view = select(exports.event.view)
++          .on("keydown.brush", keydowned, true)
++          .on("keyup.brush", keyupped, true)
++          .on("mousemove.brush", moved, true)
++          .on("mouseup.brush", ended, true);
++
++      dragDisable(exports.event.view);
++    }
++
++    nopropagation$1();
++    interrupt(that);
++    redraw.call(that);
++    emit.start();
++
++    function moved() {
++      var point1 = mouse(that);
++      if (shifting && !lockX && !lockY) {
++        if (Math.abs(point1[0] - point$$1[0]) > Math.abs(point1[1] - point$$1[1])) lockY = true;
++        else lockX = true;
++      }
++      point$$1 = point1;
++      moving = true;
++      noevent$1();
++      move();
++    }
++
++    function move() {
++      var t;
++
++      dx = point$$1[0] - point0[0];
++      dy = point$$1[1] - point0[1];
++
++      switch (mode) {
++        case MODE_SPACE:
++        case MODE_DRAG: {
++          if (signX) dx = Math.max(W - w0, Math.min(E - e0, dx)), w1 = w0 + dx, e1 = e0 + dx;
++          if (signY) dy = Math.max(N - n0, Math.min(S - s0, dy)), n1 = n0 + dy, s1 = s0 + dy;
++          break;
++        }
++        case MODE_HANDLE: {
++          if (signX < 0) dx = Math.max(W - w0, Math.min(E - w0, dx)), w1 = w0 + dx, e1 = e0;
++          else if (signX > 0) dx = Math.max(W - e0, Math.min(E - e0, dx)), w1 = w0, e1 = e0 + dx;
++          if (signY < 0) dy = Math.max(N - n0, Math.min(S - n0, dy)), n1 = n0 + dy, s1 = s0;
++          else if (signY > 0) dy = Math.max(N - s0, Math.min(S - s0, dy)), n1 = n0, s1 = s0 + dy;
++          break;
++        }
++        case MODE_CENTER: {
++          if (signX) w1 = Math.max(W, Math.min(E, w0 - dx * signX)), e1 = Math.max(W, Math.min(E, e0 + dx * signX));
++          if (signY) n1 = Math.max(N, Math.min(S, n0 - dy * signY)), s1 = Math.max(N, Math.min(S, s0 + dy * signY));
++          break;
++        }
++      }
++
++      if (e1 < w1) {
++        signX *= -1;
++        t = w0, w0 = e0, e0 = t;
++        t = w1, w1 = e1, e1 = t;
++        if (type in flipX) overlay.attr("cursor", cursors[type = flipX[type]]);
++      }
++
++      if (s1 < n1) {
++        signY *= -1;
++        t = n0, n0 = s0, s0 = t;
++        t = n1, n1 = s1, s1 = t;
++        if (type in flipY) overlay.attr("cursor", cursors[type = flipY[type]]);
++      }
++
++      if (state.selection) selection$$1 = state.selection; // May be set by brush.move!
++      if (lockX) w1 = selection$$1[0][0], e1 = selection$$1[1][0];
++      if (lockY) n1 = selection$$1[0][1], s1 = selection$$1[1][1];
++
++      if (selection$$1[0][0] !== w1
++          || selection$$1[0][1] !== n1
++          || selection$$1[1][0] !== e1
++          || selection$$1[1][1] !== s1) {
++        state.selection = [[w1, n1], [e1, s1]];
++        redraw.call(that);
++        emit.brush();
++      }
++    }
++
++    function ended() {
++      nopropagation$1();
++      if (exports.event.touches) {
++        if (exports.event.touches.length) return;
++        if (touchending) clearTimeout(touchending);
++        touchending = setTimeout(function() { touchending = null; }, 500); // Ghost clicks are delayed!
++        group.on("touchmove.brush touchend.brush touchcancel.brush", null);
++      } else {
++        yesdrag(exports.event.view, moving);
++        view.on("keydown.brush keyup.brush mousemove.brush mouseup.brush", null);
++      }
++      group.attr("pointer-events", "all");
++      overlay.attr("cursor", cursors.overlay);
++      if (state.selection) selection$$1 = state.selection; // May be set by brush.move (on start)!
++      if (empty$1(selection$$1)) state.selection = null, redraw.call(that);
++      emit.end();
++    }
++
++    function keydowned() {
++      switch (exports.event.keyCode) {
++        case 16: { // SHIFT
++          shifting = signX && signY;
++          break;
++        }
++        case 18: { // ALT
++          if (mode === MODE_HANDLE) {
++            if (signX) e0 = e1 - dx * signX, w0 = w1 + dx * signX;
++            if (signY) s0 = s1 - dy * signY, n0 = n1 + dy * signY;
++            mode = MODE_CENTER;
++            move();
++          }
++          break;
++        }
++        case 32: { // SPACE; takes priority over ALT
++          if (mode === MODE_HANDLE || mode === MODE_CENTER) {
++            if (signX < 0) e0 = e1 - dx; else if (signX > 0) w0 = w1 - dx;
++            if (signY < 0) s0 = s1 - dy; else if (signY > 0) n0 = n1 - dy;
++            mode = MODE_SPACE;
++            overlay.attr("cursor", cursors.selection);
++            move();
++          }
++          break;
++        }
++        default: return;
++      }
++      noevent$1();
++    }
++
++    function keyupped() {
++      switch (exports.event.keyCode) {
++        case 16: { // SHIFT
++          if (shifting) {
++            lockX = lockY = shifting = false;
++            move();
++          }
++          break;
++        }
++        case 18: { // ALT
++          if (mode === MODE_CENTER) {
++            if (signX < 0) e0 = e1; else if (signX > 0) w0 = w1;
++            if (signY < 0) s0 = s1; else if (signY > 0) n0 = n1;
++            mode = MODE_HANDLE;
++            move();
++          }
++          break;
++        }
++        case 32: { // SPACE
++          if (mode === MODE_SPACE) {
++            if (exports.event.altKey) {
++              if (signX) e0 = e1 - dx * signX, w0 = w1 + dx * signX;
++              if (signY) s0 = s1 - dy * signY, n0 = n1 + dy * signY;
++              mode = MODE_CENTER;
++            } else {
++              if (signX < 0) e0 = e1; else if (signX > 0) w0 = w1;
++              if (signY < 0) s0 = s1; else if (signY > 0) n0 = n1;
++              mode = MODE_HANDLE;
++            }
++            overlay.attr("cursor", cursors[type]);
++            move();
++          }
++          break;
++        }
++        default: return;
++      }
++      noevent$1();
++    }
++  }
++
++  function initialize() {
++    var state = this.__brush || {selection: null};
++    state.extent = extent.apply(this, arguments);
++    state.dim = dim;
++    return state;
++  }
++
++  brush.extent = function(_) {
++    return arguments.length ? (extent = typeof _ === "function" ? _ : constant$4([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), brush) : extent;
++  };
++
++  brush.filter = function(_) {
++    return arguments.length ? (filter = typeof _ === "function" ? _ : constant$4(!!_), brush) : filter;
++  };
++
++  brush.handleSize = function(_) {
++    return arguments.length ? (handleSize = +_, brush) : handleSize;
++  };
++
++  brush.on = function() {
++    var value = listeners.on.apply(listeners, arguments);
++    return value === listeners ? brush : value;
++  };
++
++  return brush;
++}
++
++var cos = Math.cos;
++var sin = Math.sin;
++var pi$1 = Math.PI;
++var halfPi$1 = pi$1 / 2;
++var tau$1 = pi$1 * 2;
++var max$1 = Math.max;
++
++function compareValue(compare) {
++  return function(a, b) {
++    return compare(
++      a.source.value + a.target.value,
++      b.source.value + b.target.value
++    );
++  };
++}
++
++function chord() {
++  var padAngle = 0,
++      sortGroups = null,
++      sortSubgroups = null,
++      sortChords = null;
++
++  function chord(matrix) {
++    var n = matrix.length,
++        groupSums = [],
++        groupIndex = sequence(n),
++        subgroupIndex = [],
++        chords = [],
++        groups = chords.groups = new Array(n),
++        subgroups = new Array(n * n),
++        k,
++        x,
++        x0,
++        dx,
++        i,
++        j;
++
++    // Compute the sum.
++    k = 0, i = -1; while (++i < n) {
++      x = 0, j = -1; while (++j < n) {
++        x += matrix[i][j];
++      }
++      groupSums.push(x);
++      subgroupIndex.push(sequence(n));
++      k += x;
++    }
++
++    // Sort groups…
++    if (sortGroups) groupIndex.sort(function(a, b) {
++      return sortGroups(groupSums[a], groupSums[b]);
++    });
++
++    // Sort subgroups…
++    if (sortSubgroups) subgroupIndex.forEach(function(d, i) {
++      d.sort(function(a, b) {
++        return sortSubgroups(matrix[i][a], matrix[i][b]);
++      });
++    });
++
++    // Convert the sum to scaling factor for [0, 2pi].
++    // TODO Allow start and end angle to be specified?
++    // TODO Allow padding to be specified as percentage?
++    k = max$1(0, tau$1 - padAngle * n) / k;
++    dx = k ? padAngle : tau$1 / n;
++
++    // Compute the start and end angle for each group and subgroup.
++    // Note: Opera has a bug reordering object literal properties!
++    x = 0, i = -1; while (++i < n) {
++      x0 = x, j = -1; while (++j < n) {
++        var di = groupIndex[i],
++            dj = subgroupIndex[di][j],
++            v = matrix[di][dj],
++            a0 = x,
++            a1 = x += v * k;
++        subgroups[dj * n + di] = {
++          index: di,
++          subindex: dj,
++          startAngle: a0,
++          endAngle: a1,
++          value: v
++        };
++      }
++      groups[di] = {
++        index: di,
++        startAngle: x0,
++        endAngle: x,
++        value: groupSums[di]
++      };
++      x += dx;
++    }
++
++    // Generate chords for each (non-empty) subgroup-subgroup link.
++    i = -1; while (++i < n) {
++      j = i - 1; while (++j < n) {
++        var source = subgroups[j * n + i],
++            target = subgroups[i * n + j];
++        if (source.value || target.value) {
++          chords.push(source.value < target.value
++              ? {source: target, target: source}
++              : {source: source, target: target});
++        }
++      }
++    }
++
++    return sortChords ? chords.sort(sortChords) : chords;
++  }
++
++  chord.padAngle = function(_) {
++    return arguments.length ? (padAngle = max$1(0, _), chord) : padAngle;
++  };
++
++  chord.sortGroups = function(_) {
++    return arguments.length ? (sortGroups = _, chord) : sortGroups;
++  };
++
++  chord.sortSubgroups = function(_) {
++    return arguments.length ? (sortSubgroups = _, chord) : sortSubgroups;
++  };
++
++  chord.sortChords = function(_) {
++    return arguments.length ? (_ == null ? sortChords = null : (sortChords = compareValue(_))._ = _, chord) : sortChords && sortChords._;
++  };
++
++  return chord;
++}
++
++var slice$2 = Array.prototype.slice;
++
++function constant$5(x) {
++  return function() {
++    return x;
++  };
++}
++
++var pi$2 = Math.PI,
++    tau$2 = 2 * pi$2,
++    epsilon$1 = 1e-6,
++    tauEpsilon = tau$2 - epsilon$1;
++
++function Path() {
++  this._x0 = this._y0 = // start of current subpath
++  this._x1 = this._y1 = null; // end of current subpath
++  this._ = "";
++}
++
++function path() {
++  return new Path;
++}
++
++Path.prototype = path.prototype = {
++  constructor: Path,
++  moveTo: function(x, y) {
++    this._ += "M" + (this._x0 = this._x1 = +x) + "," + (this._y0 = this._y1 = +y);
++  },
++  closePath: function() {
++    if (this._x1 !== null) {
++      this._x1 = this._x0, this._y1 = this._y0;
++      this._ += "Z";
++    }
++  },
++  lineTo: function(x, y) {
++    this._ += "L" + (this._x1 = +x) + "," + (this._y1 = +y);
++  },
++  quadraticCurveTo: function(x1, y1, x, y) {
++    this._ += "Q" + (+x1) + "," + (+y1) + "," + (this._x1 = +x) + "," + (this._y1 = +y);
++  },
++  bezierCurveTo: function(x1, y1, x2, y2, x, y) {
++    this._ += "C" + (+x1) + "," + (+y1) + "," + (+x2) + "," + (+y2) + "," + (this._x1 = +x) + "," + (this._y1 = +y);
++  },
++  arcTo: function(x1, y1, x2, y2, r) {
++    x1 = +x1, y1 = +y1, x2 = +x2, y2 = +y2, r = +r;
++    var x0 = this._x1,
++        y0 = this._y1,
++        x21 = x2 - x1,
++        y21 = y2 - y1,
++        x01 = x0 - x1,
++        y01 = y0 - y1,
++        l01_2 = x01 * x01 + y01 * y01;
++
++    // Is the radius negative? Error.
++    if (r < 0) throw new Error("negative radius: " + r);
++
++    // Is this path empty? Move to (x1,y1).
++    if (this._x1 === null) {
++      this._ += "M" + (this._x1 = x1) + "," + (this._y1 = y1);
++    }
++
++    // Or, is (x1,y1) coincident with (x0,y0)? Do nothing.
++    else if (!(l01_2 > epsilon$1)) {}
++
++    // Or, are (x0,y0), (x1,y1) and (x2,y2) collinear?
++    // Equivalently, is (x1,y1) coincident with (x2,y2)?
++    // Or, is the radius zero? Line to (x1,y1).
++    else if (!(Math.abs(y01 * x21 - y21 * x01) > epsilon$1) || !r) {
++      this._ += "L" + (this._x1 = x1) + "," + (this._y1 = y1);
++    }
++
++    // Otherwise, draw an arc!
++    else {
++      var x20 = x2 - x0,
++          y20 = y2 - y0,
++          l21_2 = x21 * x21 + y21 * y21,
++          l20_2 = x20 * x20 + y20 * y20,
++          l21 = Math.sqrt(l21_2),
++          l01 = Math.sqrt(l01_2),
++          l = r * Math.tan((pi$2 - Math.acos((l21_2 + l01_2 - l20_2) / (2 * l21 * l01))) / 2),
++          t01 = l / l01,
++          t21 = l / l21;
++
++      // If the start tangent is not coincident with (x0,y0), line to.
++      if (Math.abs(t01 - 1) > epsilon$1) {
++        this._ += "L" + (x1 + t01 * x01) + "," + (y1 + t01 * y01);
++      }
++
++      this._ += "A" + r + "," + r + ",0,0," + (+(y01 * x20 > x01 * y20)) + "," + (this._x1 = x1 + t21 * x21) + "," + (this._y1 = y1 + t21 * y21);
++    }
++  },
++  arc: function(x, y, r, a0, a1, ccw) {
++    x = +x, y = +y, r = +r;
++    var dx = r * Math.cos(a0),
++        dy = r * Math.sin(a0),
++        x0 = x + dx,
++        y0 = y + dy,
++        cw = 1 ^ ccw,
++        da = ccw ? a0 - a1 : a1 - a0;
++
++    // Is the radius negative? Error.
++    if (r < 0) throw new Error("negative radius: " + r);
++
++    // Is this path empty? Move to (x0,y0).
++    if (this._x1 === null) {
++      this._ += "M" + x0 + "," + y0;
++    }
++
++    // Or, is (x0,y0) not coincident with the previous point? Line to (x0,y0).
++    else if (Math.abs(this._x1 - x0) > epsilon$1 || Math.abs(this._y1 - y0) > epsilon$1) {
++      this._ += "L" + x0 + "," + y0;
++    }
++
++    // Is this arc empty? We’re done.
++    if (!r) return;
++
++    // Does the angle go the wrong way? Flip the direction.
++    if (da < 0) da = da % tau$2 + tau$2;
++
++    // Is this a complete circle? Draw two arcs to complete the circle.
++    if (da > tauEpsilon) {
++      this._ += "A" + r + "," + r + ",0,1," + cw + "," + (x - dx) + "," + (y - dy) + "A" + r + "," + r + ",0,1," + cw + "," + (this._x1 = x0) + "," + (this._y1 = y0);
++    }
++
++    // Is this arc non-empty? Draw an arc!
++    else if (da > epsilon$1) {
++      this._ += "A" + r + "," + r + ",0," + (+(da >= pi$2)) + "," + cw + "," + (this._x1 = x + r * Math.cos(a1)) + "," + (this._y1 = y + r * Math.sin(a1));
++    }
++  },
++  rect: function(x, y, w, h) {
++    this._ += "M" + (this._x0 = this._x1 = +x) + "," + (this._y0 = this._y1 = +y) + "h" + (+w) + "v" + (+h) + "h" + (-w) + "Z";
++  },
++  toString: function() {
++    return this._;
++  }
++};
++
++function defaultSource(d) {
++  return d.source;
++}
++
++function defaultTarget(d) {
++  return d.target;
++}
++
++function defaultRadius(d) {
++  return d.radius;
++}
++
++function defaultStartAngle(d) {
++  return d.startAngle;
++}
++
++function defaultEndAngle(d) {
++  return d.endAngle;
++}
++
++function ribbon() {
++  var source = defaultSource,
++      target = defaultTarget,
++      radius = defaultRadius,
++      startAngle = defaultStartAngle,
++      endAngle = defaultEndAngle,
++      context = null;
++
++  function ribbon() {
++    var buffer,
++        argv = slice$2.call(arguments),
++        s = source.apply(this, argv),
++        t = target.apply(this, argv),
++        sr = +radius.apply(this, (argv[0] = s, argv)),
++        sa0 = startAngle.apply(this, argv) - halfPi$1,
++        sa1 = endAngle.apply(this, argv) - halfPi$1,
++        sx0 = sr * cos(sa0),
++        sy0 = sr * sin(sa0),
++        tr = +radius.apply(this, (argv[0] = t, argv)),
++        ta0 = startAngle.apply(this, argv) - halfPi$1,
++        ta1 = endAngle.apply(this, argv) - halfPi$1;
++
++    if (!context) context = buffer = path();
++
++    context.moveTo(sx0, sy0);
++    context.arc(0, 0, sr, sa0, sa1);
++    if (sa0 !== ta0 || sa1 !== ta1) { // TODO sr !== tr?
++      context.quadraticCurveTo(0, 0, tr * cos(ta0), tr * sin(ta0));
++      context.arc(0, 0, tr, ta0, ta1);
++    }
++    context.quadraticCurveTo(0, 0, sx0, sy0);
++    context.closePath();
++
++    if (buffer) return context = null, buffer + "" || null;
++  }
++
++  ribbon.radius = function(_) {
++    return arguments.length ? (radius = typeof _ === "function" ? _ : constant$5(+_), ribbon) : radius;
++  };
++
++  ribbon.startAngle = function(_) {
++    return arguments.length ? (startAngle = typeof _ === "function" ? _ : constant$5(+_), ribbon) : startAngle;
++  };
++
++  ribbon.endAngle = function(_) {
++    return arguments.length ? (endAngle = typeof _ === "function" ? _ : constant$5(+_), ribbon) : endAngle;
++  };
++
++  ribbon.source = function(_) {
++    return arguments.length ? (source = _, ribbon) : source;
++  };
++
++  ribbon.target = function(_) {
++    return arguments.length ? (target = _, ribbon) : target;
++  };
++
++  ribbon.context = function(_) {
++    return arguments.length ? (context = _ == null ? null : _, ribbon) : context;
++  };
++
++  return ribbon;
++}
++
++var prefix = "$";
++
++function Map() {}
++
++Map.prototype = map$1.prototype = {
++  constructor: Map,
++  has: function(key) {
++    return (prefix + key) in this;
++  },
++  get: function(key) {
++    return this[prefix + key];
++  },
++  set: function(key, value) {
++    this[prefix + key] = value;
++    return this;
++  },
++  remove: function(key) {
++    var property = prefix + key;
++    return property in this && delete this[property];
++  },
++  clear: function() {
++    for (var property in this) if (property[0] === prefix) delete this[property];
++  },
++  keys: function() {
++    var keys = [];
++    for (var property in this) if (property[0] === prefix) keys.push(property.slice(1));
++    return keys;
++  },
++  values: function() {
++    var values = [];
++    for (var property in this) if (property[0] === prefix) values.push(this[property]);
++    return values;
++  },
++  entries: function() {
++    var entries = [];
++    for (var property in this) if (property[0] === prefix) entries.push({key: property.slice(1), value: this[property]});
++    return entries;
++  },
++  size: function() {
++    var size = 0;
++    for (var property in this) if (property[0] === prefix) ++size;
++    return size;
++  },
++  empty: function() {
++    for (var property in this) if (property[0] === prefix) return false;
++    return true;
++  },
++  each: function(f) {
++    for (var property in this) if (property[0] === prefix) f(this[property], property.slice(1), this);
++  }
++};
++
++function map$1(object, f) {
++  var map = new Map;
++
++  // Copy constructor.
++  if (object instanceof Map) object.each(function(value, key) { map.set(key, value); });
++
++  // Index array by numeric index or specified key function.
++  else if (Array.isArray(object)) {
++    var i = -1,
++        n = object.length,
++        o;
++
++    if (f == null) while (++i < n) map.set(i, object[i]);
++    else while (++i < n) map.set(f(o = object[i], i, object), o);
++  }
++
++  // Convert object to map.
++  else if (object) for (var key in object) map.set(key, object[key]);
++
++  return map;
++}
++
++function nest() {
++  var keys = [],
++      sortKeys = [],
++      sortValues,
++      rollup,
++      nest;
++
++  function apply(array, depth, createResult, setResult) {
++    if (depth >= keys.length) {
++      if (sortValues != null) array.sort(sortValues);
++      return rollup != null ? rollup(array) : array;
++    }
++
++    var i = -1,
++        n = array.length,
++        key = keys[depth++],
++        keyValue,
++        value,
++        valuesByKey = map$1(),
++        values,
++        result = createResult();
++
++    while (++i < n) {
++      if (values = valuesByKey.get(keyValue = key(value = array[i]) + "")) {
++        values.push(value);
++      } else {
++        valuesByKey.set(keyValue, [value]);
++      }
++    }
++
++    valuesByKey.each(function(values, key) {
++      setResult(result, key, apply(values, depth, createResult, setResult));
++    });
++
++    return result;
++  }
++
++  function entries(map, depth) {
++    if (++depth > keys.length) return map;
++    var array, sortKey = sortKeys[depth - 1];
++    if (rollup != null && depth >= keys.length) array = map.entries();
++    else array = [], map.each(function(v, k) { array.push({key: k, values: entries(v, depth)}); });
++    return sortKey != null ? array.sort(function(a, b) { return sortKey(a.key, b.key); }) : array;
++  }
++
++  return nest = {
++    object: function(array) { return apply(array, 0, createObject, setObject); },
++    map: function(array) { return apply(array, 0, createMap, setMap); },
++    entries: function(array) { return entries(apply(array, 0, createMap, setMap), 0); },
++    key: function(d) { keys.push(d); return nest; },
++    sortKeys: function(order) { sortKeys[keys.length - 1] = order; return nest; },
++    sortValues: function(order) { sortValues = order; return nest; },
++    rollup: function(f) { rollup = f; return nest; }
++  };
++}
++
++function createObject() {
++  return {};
++}
++
++function setObject(object, key, value) {
++  object[key] = value;
++}
++
++function createMap() {
++  return map$1();
++}
++
++function setMap(map, key, value) {
++  map.set(key, value);
++}
++
++function Set() {}
++
++var proto = map$1.prototype;
++
++Set.prototype = set$2.prototype = {
++  constructor: Set,
++  has: proto.has,
++  add: function(value) {
++    value += "";
++    this[prefix + value] = value;
++    return this;
++  },
++  remove: proto.remove,
++  clear: proto.clear,
++  values: proto.keys,
++  size: proto.size,
++  empty: proto.empty,
++  each: proto.each
++};
++
++function set$2(object, f) {
++  var set = new Set;
++
++  // Copy constructor.
++  if (object instanceof Set) object.each(function(value) { set.add(value); });
++
++  // Otherwise, assume it’s an array.
++  else if (object) {
++    var i = -1, n = object.length;
++    if (f == null) while (++i < n) set.add(object[i]);
++    else while (++i < n) set.add(f(object[i], i, object));
++  }
++
++  return set;
++}
++
++function keys(map) {
++  var keys = [];
++  for (var key in map) keys.push(key);
++  return keys;
++}
++
++function values(map) {
++  var values = [];
++  for (var key in map) values.push(map[key]);
++  return values;
++}
++
++function entries(map) {
++  var entries = [];
++  for (var key in map) entries.push({key: key, value: map[key]});
++  return entries;
++}
++
++var array$2 = Array.prototype;
++
++var slice$3 = array$2.slice;
++
++function ascending$2(a, b) {
++  return a - b;
++}
++
++function area(ring) {
++  var i = 0, n = ring.length, area = ring[n - 1][1] * ring[0][0] - ring[n - 1][0] * ring[0][1];
++  while (++i < n) area += ring[i - 1][1] * ring[i][0] - ring[i - 1][0] * ring[i][1];
++  return area;
++}
++
++function constant$6(x) {
++  return function() {
++    return x;
++  };
++}
++
++function contains(ring, hole) {
++  var i = -1, n = hole.length, c;
++  while (++i < n) if (c = ringContains(ring, hole[i])) return c;
++  return 0;
++}
++
++function ringContains(ring, point) {
++  var x = point[0], y = point[1], contains = -1;
++  for (var i = 0, n = ring.length, j = n - 1; i < n; j = i++) {
++    var pi = ring[i], xi = pi[0], yi = pi[1], pj = ring[j], xj = pj[0], yj = pj[1];
++    if (segmentContains(pi, pj, point)) return 0;
++    if (((yi > y) !== (yj > y)) && ((x < (xj - xi) * (y - yi) / (yj - yi) + xi))) contains = -contains;
++  }
++  return contains;
++}
++
++function segmentContains(a, b, c) {
++  var i; return collinear(a, b, c) && within(a[i = +(a[0] === b[0])], c[i], b[i]);
++}
++
++function collinear(a, b, c) {
++  return (b[0] - a[0]) * (c[1] - a[1]) === (c[0] - a[0]) * (b[1] - a[1]);
++}
++
++function within(p, q, r) {
++  return p <= q && q <= r || r <= q && q <= p;
++}
++
++function noop$1() {}
++
++var cases = [
++  [],
++  [[[1.0, 1.5], [0.5, 1.0]]],
++  [[[1.5, 1.0], [1.0, 1.5]]],
++  [[[1.5, 1.0], [0.5, 1.0]]],
++  [[[1.0, 0.5], [1.5, 1.0]]],
++  [[[1.0, 1.5], [0.5, 1.0]], [[1.0, 0.5], [1.5, 1.0]]],
++  [[[1.0, 0.5], [1.0, 1.5]]],
++  [[[1.0, 0.5], [0.5, 1.0]]],
++  [[[0.5, 1.0], [1.0, 0.5]]],
++  [[[1.0, 1.5], [1.0, 0.5]]],
++  [[[0.5, 1.0], [1.0, 0.5]], [[1.5, 1.0], [1.0, 1.5]]],
++  [[[1.5, 1.0], [1.0, 0.5]]],
++  [[[0.5, 1.0], [1.5, 1.0]]],
++  [[[1.0, 1.5], [1.5, 1.0]]],
++  [[[0.5, 1.0], [1.0, 1.5]]],
++  []
++];
++
++function contours() {
++  var dx = 1,
++      dy = 1,
++      threshold$$1 = thresholdSturges,
++      smooth = smoothLinear;
++
++  function contours(values) {
++    var tz = threshold$$1(values);
++
++    // Convert number of thresholds into uniform thresholds.
++    if (!Array.isArray(tz)) {
++      var domain = extent(values), start = domain[0], stop = domain[1];
++      tz = tickStep(start, stop, tz);
++      tz = sequence(Math.floor(start / tz) * tz, Math.floor(stop / tz) * tz, tz);
++    } else {
++      tz = tz.slice().sort(ascending$2);
++    }
++
++    return tz.map(function(value) {
++      return contour(values, value);
++    });
++  }
++
++  // Accumulate, smooth contour rings, assign holes to exterior rings.
++  // Based on https://github.com/mbostock/shapefile/blob/v0.6.2/shp/polygon.js
++  function contour(values, value) {
++    var polygons = [],
++        holes = [];
++
++    isorings(values, value, function(ring) {
++      smooth(ring, values, value);
++      if (area(ring) > 0) polygons.push([ring]);
++      else holes.push(ring);
++    });
++
++    holes.forEach(function(hole) {
++      for (var i = 0, n = polygons.length, polygon; i < n; ++i) {
++        if (contains((polygon = polygons[i])[0], hole) !== -1) {
++          polygon.push(hole);
++          return;
++        }
++      }
++    });
++
++    return {
++      type: "MultiPolygon",
++      value: value,
++      coordinates: polygons
++    };
++  }
++
++  // Marching squares with isolines stitched into rings.
++  // Based on https://github.com/topojson/topojson-client/blob/v3.0.0/src/stitch.js
++  function isorings(values, value, callback) {
++    var fragmentByStart = new Array,
++        fragmentByEnd = new Array,
++        x, y, t0, t1, t2, t3;
++
++    // Special case for the first row (y = -1, t2 = t3 = 0).
++    x = y = -1;
++    t1 = values[0] >= value;
++    cases[t1 << 1].forEach(stitch);
++    while (++x < dx - 1) {
++      t0 = t1, t1 = values[x + 1] >= value;
++      cases[t0 | t1 << 1].forEach(stitch);
++    }
++    cases[t1 << 0].forEach(stitch);
++
++    // General case for the intermediate rows.
++    while (++y < dy - 1) {
++      x = -1;
++      t1 = values[y * dx + dx] >= value;
++      t2 = values[y * dx] >= value;
++      cases[t1 << 1 | t2 << 2].forEach(stitch);
++      while (++x < dx - 1) {
++        t0 = t1, t1 = values[y * dx + dx + x + 1] >= value;
++        t3 = t2, t2 = values[y * dx + x + 1] >= value;
++        cases[t0 | t1 << 1 | t2 << 2 | t3 << 3].forEach(stitch);
++      }
++      cases[t1 | t2 << 3].forEach(stitch);
++    }
++
++    // Special case for the last row (y = dy - 1, t0 = t1 = 0).
++    x = -1;
++    t2 = values[y * dx] >= value;
++    cases[t2 << 2].forEach(stitch);
++    while (++x < dx - 1) {
++      t3 = t2, t2 = values[y * dx + x + 1] >= value;
++      cases[t2 << 2 | t3 << 3].forEach(stitch);
++    }
++    cases[t2 << 3].forEach(stitch);
++
++    function stitch(line) {
++      var start = [line[0][0] + x, line[0][1] + y],
++          end = [line[1][0] + x, line[1][1] + y],
++          startIndex = index(start),
++          endIndex = index(end),
++          f, g;
++      if (f = fragmentByEnd[startIndex]) {
++        if (g = fragmentByStart[endIndex]) {
++          delete fragmentByEnd[f.end];
++          delete fragmentByStart[g.start];
++          if (f === g) {
++            f.ring.push(end);
++            callback(f.ring);
++          } else {
++            fragmentByStart[f.start] = fragmentByEnd[g.end] = {start: f.start, end: g.end, ring: f.ring.concat(g.ring)};
++          }
++        } else {
++          delete fragmentByEnd[f.end];
++          f.ring.push(end);
++          fragmentByEnd[f.end = endIndex] = f;
++        }
++      } else if (f = fragmentByStart[endIndex]) {
++        if (g = fragmentByEnd[startIndex]) {
++          delete fragmentByStart[f.start];
++          delete fragmentByEnd[g.end];
++          if (f === g) {
++            f.ring.push(end);
++            callback(f.ring);
++          } else {
++            fragmentByStart[g.start] = fragmentByEnd[f.end] = {start: g.start, end: f.end, ring: g.ring.concat(f.ring)};
++          }
++        } else {
++          delete fragmentByStart[f.start];
++          f.ring.unshift(start);
++          fragmentByStart[f.start = startIndex] = f;
++        }
++      } else {
++        fragmentByStart[startIndex] = fragmentByEnd[endIndex] = {start: startIndex, end: endIndex, ring: [start, end]};
++      }
++    }
++  }
++
++  function index(point) {
++    return point[0] * 2 + point[1] * (dx + 1) * 4;
++  }
++
++  function smoothLinear(ring, values, value) {
++    ring.forEach(function(point) {
++      var x = point[0],
++          y = point[1],
++          xt = x | 0,
++          yt = y | 0,
++          v0,
++          v1 = values[yt * dx + xt];
++      if (x > 0 && x < dx && xt === x) {
++        v0 = values[yt * dx + xt - 1];
++        point[0] = x + (value - v0) / (v1 - v0) - 0.5;
++      }
++      if (y > 0 && y < dy && yt === y) {
++        v0 = values[(yt - 1) * dx + xt];
++        point[1] = y + (value - v0) / (v1 - v0) - 0.5;
++      }
++    });
++  }
++
++  contours.contour = contour;
++
++  contours.size = function(_) {
++    if (!arguments.length) return [dx, dy];
++    var _0 = Math.ceil(_[0]), _1 = Math.ceil(_[1]);
++    if (!(_0 > 0) || !(_1 > 0)) throw new Error("invalid size");
++    return dx = _0, dy = _1, contours;
++  };
++
++  contours.thresholds = function(_) {
++    return arguments.length ? (threshold$$1 = typeof _ === "function" ? _ : Array.isArray(_) ? constant$6(slice$3.call(_)) : constant$6(_), contours) : threshold$$1;
++  };
++
++  contours.smooth = function(_) {
++    return arguments.length ? (smooth = _ ? smoothLinear : noop$1, contours) : smooth === smoothLinear;
++  };
++
++  return contours;
++}
++
++// TODO Optimize edge cases.
++// TODO Optimize index calculation.
++// TODO Optimize arguments.
++function blurX(source, target, r) {
++  var n = source.width,
++      m = source.height,
++      w = (r << 1) + 1;
++  for (var j = 0; j < m; ++j) {
++    for (var i = 0, sr = 0; i < n + r; ++i) {
++      if (i < n) {
++        sr += source.data[i + j * n];
++      }
++      if (i >= r) {
++        if (i >= w) {
++          sr -= source.data[i - w + j * n];
++        }
++        target.data[i - r + j * n] = sr / Math.min(i + 1, n - 1 + w - i, w);
++      }
++    }
++  }
++}
++
++// TODO Optimize edge cases.
++// TODO Optimize index calculation.
++// TODO Optimize arguments.
++function blurY(source, target, r) {
++  var n = source.width,
++      m = source.height,
++      w = (r << 1) + 1;
++  for (var i = 0; i < n; ++i) {
++    for (var j = 0, sr = 0; j < m + r; ++j) {
++      if (j < m) {
++        sr += source.data[i + j * n];
++      }
++      if (j >= r) {
++        if (j >= w) {
++          sr -= source.data[i + (j - w) * n];
++        }
++        target.data[i + (j - r) * n] = sr / Math.min(j + 1, m - 1 + w - j, w);
++      }
++    }
++  }
++}
++
++function defaultX(d) {
++  return d[0];
++}
++
++function defaultY(d) {
++  return d[1];
++}
++
++function density() {
++  var x = defaultX,
++      y = defaultY,
++      dx = 960,
++      dy = 500,
++      r = 20, // blur radius
++      k = 2, // log2(grid cell size)
++      o = r * 3, // grid offset, to pad for blur
++      n = (dx + o * 2) >> k, // grid width
++      m = (dy + o * 2) >> k, // grid height
++      threshold$$1 = constant$6(20);
++
++  function density(data) {
++    var values0 = new Float32Array(n * m),
++        values1 = new Float32Array(n * m);
++
++    data.forEach(function(d, i, data) {
++      var xi = (x(d, i, data) + o) >> k,
++          yi = (y(d, i, data) + o) >> k;
++      if (xi >= 0 && xi < n && yi >= 0 && yi < m) {
++        ++values0[xi + yi * n];
++      }
++    });
++
++    // TODO Optimize.
++    blurX({width: n, height: m, data: values0}, {width: n, height: m, data: values1}, r >> k);
++    blurY({width: n, height: m, data: values1}, {width: n, height: m, data: values0}, r >> k);
++    blurX({width: n, height: m, data: values0}, {width: n, height: m, data: values1}, r >> k);
++    blurY({width: n, height: m, data: values1}, {width: n, height: m, data: values0}, r >> k);
++    blurX({width: n, height: m, data: values0}, {width: n, height: m, data: values1}, r >> k);
++    blurY({width: n, height: m, data: values1}, {width: n, height: m, data: values0}, r >> k);
++
++    var tz = threshold$$1(values0);
++
++    // Convert number of thresholds into uniform thresholds.
++    if (!Array.isArray(tz)) {
++      var stop = max(values0);
++      tz = tickStep(0, stop, tz);
++      tz = sequence(0, Math.floor(stop / tz) * tz, tz);
++      tz.shift();
++    }
++
++    return contours()
++        .thresholds(tz)
++        .size([n, m])
++      (values0)
++        .map(transform);
++  }
++
++  function transform(geometry) {
++    geometry.value *= Math.pow(2, -2 * k); // Density in points per square pixel.
++    geometry.coordinates.forEach(transformPolygon);
++    return geometry;
++  }
++
++  function transformPolygon(coordinates) {
++    coordinates.forEach(transformRing);
++  }
++
++  function transformRing(coordinates) {
++    coordinates.forEach(transformPoint);
++  }
++
++  // TODO Optimize.
++  function transformPoint(coordinates) {
++    coordinates[0] = coordinates[0] * Math.pow(2, k) - o;
++    coordinates[1] = coordinates[1] * Math.pow(2, k) - o;
++  }
++
++  function resize() {
++    o = r * 3;
++    n = (dx + o * 2) >> k;
++    m = (dy + o * 2) >> k;
++    return density;
++  }
++
++  density.x = function(_) {
++    return arguments.length ? (x = typeof _ === "function" ? _ : constant$6(+_), density) : x;
++  };
++
++  density.y = function(_) {
++    return arguments.length ? (y = typeof _ === "function" ? _ : constant$6(+_), density) : y;
++  };
++
++  density.size = function(_) {
++    if (!arguments.length) return [dx, dy];
++    var _0 = Math.ceil(_[0]), _1 = Math.ceil(_[1]);
++    if (!(_0 >= 0) && !(_0 >= 0)) throw new Error("invalid size");
++    return dx = _0, dy = _1, resize();
++  };
++
++  density.cellSize = function(_) {
++    if (!arguments.length) return 1 << k;
++    if (!((_ = +_) >= 1)) throw new Error("invalid cell size");
++    return k = Math.floor(Math.log(_) / Math.LN2), resize();
++  };
++
++  density.thresholds = function(_) {
++    return arguments.length ? (threshold$$1 = typeof _ === "function" ? _ : Array.isArray(_) ? constant$6(slice$3.call(_)) : constant$6(_), density) : threshold$$1;
++  };
++
++  density.bandwidth = function(_) {
++    if (!arguments.length) return Math.sqrt(r * (r + 1));
++    if (!((_ = +_) >= 0)) throw new Error("invalid bandwidth");
++    return r = Math.round((Math.sqrt(4 * _ * _ + 1) - 1) / 2), resize();
++  };
++
++  return density;
++}
++
++var EOL = {},
++    EOF = {},
++    QUOTE = 34,
++    NEWLINE = 10,
++    RETURN = 13;
++
++function objectConverter(columns) {
++  return new Function("d", "return {" + columns.map(function(name, i) {
++    return JSON.stringify(name) + ": d[" + i + "]";
++  }).join(",") + "}");
++}
++
++function customConverter(columns, f) {
++  var object = objectConverter(columns);
++  return function(row, i) {
++    return f(object(row), i, columns);
++  };
++}
++
++// Compute unique columns in order of discovery.
++function inferColumns(rows) {
++  var columnSet = Object.create(null),
++      columns = [];
++
++  rows.forEach(function(row) {
++    for (var column in row) {
++      if (!(column in columnSet)) {
++        columns.push(columnSet[column] = column);
++      }
++    }
++  });
++
++  return columns;
++}
++
++function dsvFormat(delimiter) {
++  var reFormat = new RegExp("[\"" + delimiter + "\n\r]"),
++      DELIMITER = delimiter.charCodeAt(0);
++
++  function parse(text, f) {
++    var convert, columns, rows = parseRows(text, function(row, i) {
++      if (convert) return convert(row, i - 1);
++      columns = row, convert = f ? customConverter(row, f) : objectConverter(row);
++    });
++    rows.columns = columns || [];
++    return rows;
++  }
++
++  function parseRows(text, f) {
++    var rows = [], // output rows
++        N = text.length,
++        I = 0, // current character index
++        n = 0, // current line number
++        t, // current token
++        eof = N <= 0, // current token followed by EOF?
++        eol = false; // current token followed by EOL?
++
++    // Strip the trailing newline.
++    if (text.charCodeAt(N - 1) === NEWLINE) --N;
++    if (text.charCodeAt(N - 1) === RETURN) --N;
++
++    function token() {
++      if (eof) return EOF;
++      if (eol) return eol = false, EOL;
++
++      // Unescape quotes.
++      var i, j = I, c;
++      if (text.charCodeAt(j) === QUOTE) {
++        while (I++ < N && text.charCodeAt(I) !== QUOTE || text.charCodeAt(++I) === QUOTE);
++        if ((i = I) >= N) eof = true;
++        else if ((c = text.charCodeAt(I++)) === NEWLINE) eol = true;
++        else if (c === RETURN) { eol = true; if (text.charCodeAt(I) === NEWLINE) ++I; }
++        return text.slice(j + 1, i - 1).replace(/""/g, "\"");
++      }
++
++      // Find next delimiter or newline.
++      while (I < N) {
++        if ((c = text.charCodeAt(i = I++)) === NEWLINE) eol = true;
++        else if (c === RETURN) { eol = true; if (text.charCodeAt(I) === NEWLINE) ++I; }
++        else if (c !== DELIMITER) continue;
++        return text.slice(j, i);
++      }
++
++      // Return last token before EOF.
++      return eof = true, text.slice(j, N);
++    }
++
++    while ((t = token()) !== EOF) {
++      var row = [];
++      while (t !== EOL && t !== EOF) row.push(t), t = token();
++      if (f && (row = f(row, n++)) == null) continue;
++      rows.push(row);
++    }
++
++    return rows;
++  }
++
++  function format(rows, columns) {
++    if (columns == null) columns = inferColumns(rows);
++    return [columns.map(formatValue).join(delimiter)].concat(rows.map(function(row) {
++      return columns.map(function(column) {
++        return formatValue(row[column]);
++      }).join(delimiter);
++    })).join("\n");
++  }
++
++  function formatRows(rows) {
++    return rows.map(formatRow).join("\n");
++  }
++
++  function formatRow(row) {
++    return row.map(formatValue).join(delimiter);
++  }
++
++  function formatValue(text) {
++    return text == null ? ""
++        : reFormat.test(text += "") ? "\"" + text.replace(/"/g, "\"\"") + "\""
++        : text;
++  }
++
++  return {
++    parse: parse,
++    parseRows: parseRows,
++    format: format,
++    formatRows: formatRows
++  };
++}
++
++var csv = dsvFormat(",");
++
++var csvParse = csv.parse;
++var csvParseRows = csv.parseRows;
++var csvFormat = csv.format;
++var csvFormatRows = csv.formatRows;
++
++var tsv = dsvFormat("\t");
++
++var tsvParse = tsv.parse;
++var tsvParseRows = tsv.parseRows;
++var tsvFormat = tsv.format;
++var tsvFormatRows = tsv.formatRows;
++
++function responseBlob(response) {
++  if (!response.ok) throw new Error(response.status + " " + response.statusText);
++  return response.blob();
++}
++
++function blob(input, init) {
++  return fetch(input, init).then(responseBlob);
++}
++
++function responseArrayBuffer(response) {
++  if (!response.ok) throw new Error(response.status + " " + response.statusText);
++  return response.arrayBuffer();
++}
++
++function buffer(input, init) {
++  return fetch(input, init).then(responseArrayBuffer);
++}
++
++function responseText(response) {
++  if (!response.ok) throw new Error(response.status + " " + response.statusText);
++  return response.text();
++}
++
++function text(input, init) {
++  return fetch(input, init).then(responseText);
++}
++
++function dsvParse(parse) {
++  return function(input, init, row) {
++    if (arguments.length === 2 && typeof init === "function") row = init, init = undefined;
++    return text(input, init).then(function(response) {
++      return parse(response, row);
++    });
++  };
++}
++
++function dsv(delimiter, input, init, row) {
++  if (arguments.length === 3 && typeof init === "function") row = init, init = undefined;
++  var format = dsvFormat(delimiter);
++  return text(input, init).then(function(response) {
++    return format.parse(response, row);
++  });
++}
++
++var csv$1 = dsvParse(csvParse);
++var tsv$1 = dsvParse(tsvParse);
++
++function image(input, init) {
++  return new Promise(function(resolve, reject) {
++    var image = new Image;
++    for (var key in init) image[key] = init[key];
++    image.onerror = reject;
++    image.onload = function() { resolve(image); };
++    image.src = input;
++  });
++}
++
++function responseJson(response) {
++  if (!response.ok) throw new Error(response.status + " " + response.statusText);
++  return response.json();
++}
++
++function json(input, init) {
++  return fetch(input, init).then(responseJson);
++}
++
++function parser(type) {
++  return function(input, init)  {
++    return text(input, init).then(function(text$$1) {
++      return (new DOMParser).parseFromString(text$$1, type);
++    });
++  };
++}
++
++var xml = parser("application/xml");
++
++var html = parser("text/html");
++
++var svg = parser("image/svg+xml");
++
++function center$1(x, y) {
++  var nodes;
++
++  if (x == null) x = 0;
++  if (y == null) y = 0;
++
++  function force() {
++    var i,
++        n = nodes.length,
++        node,
++        sx = 0,
++        sy = 0;
++
++    for (i = 0; i < n; ++i) {
++      node = nodes[i], sx += node.x, sy += node.y;
++    }
++
++    for (sx = sx / n - x, sy = sy / n - y, i = 0; i < n; ++i) {
++      node = nodes[i], node.x -= sx, node.y -= sy;
++    }
++  }
++
++  force.initialize = function(_) {
++    nodes = _;
++  };
++
++  force.x = function(_) {
++    return arguments.length ? (x = +_, force) : x;
++  };
++
++  force.y = function(_) {
++    return arguments.length ? (y = +_, force) : y;
++  };
++
++  return force;
++}
++
++function constant$7(x) {
++  return function() {
++    return x;
++  };
++}
++
++function jiggle() {
++  return (Math.random() - 0.5) * 1e-6;
++}
++
++function tree_add(d) {
++  var x = +this._x.call(null, d),
++      y = +this._y.call(null, d);
++  return add(this.cover(x, y), x, y, d);
++}
++
++function add(tree, x, y, d) {
++  if (isNaN(x) || isNaN(y)) return tree; // ignore invalid points
++
++  var parent,
++      node = tree._root,
++      leaf = {data: d},
++      x0 = tree._x0,
++      y0 = tree._y0,
++      x1 = tree._x1,
++      y1 = tree._y1,
++      xm,
++      ym,
++      xp,
++      yp,
++      right,
++      bottom,
++      i,
++      j;
++
++  // If the tree is empty, initialize the root as a leaf.
++  if (!node) return tree._root = leaf, tree;
++
++  // Find the existing leaf for the new point, or add it.
++  while (node.length) {
++    if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;
++    if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym;
++    if (parent = node, !(node = node[i = bottom << 1 | right])) return parent[i] = leaf, tree;
++  }
++
++  // Is the new point is exactly coincident with the existing point?
++  xp = +tree._x.call(null, node.data);
++  yp = +tree._y.call(null, node.data);
++  if (x === xp && y === yp) return leaf.next = node, parent ? parent[i] = leaf : tree._root = leaf, tree;
++
++  // Otherwise, split the leaf node until the old and new point are separated.
++  do {
++    parent = parent ? parent[i] = new Array(4) : tree._root = new Array(4);
++    if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;
++    if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym;
++  } while ((i = bottom << 1 | right) === (j = (yp >= ym) << 1 | (xp >= xm)));
++  return parent[j] = node, parent[i] = leaf, tree;
++}
++
++function addAll(data) {
++  var d, i, n = data.length,
++      x,
++      y,
++      xz = new Array(n),
++      yz = new Array(n),
++      x0 = Infinity,
++      y0 = Infinity,
++      x1 = -Infinity,
++      y1 = -Infinity;
++
++  // Compute the points and their extent.
++  for (i = 0; i < n; ++i) {
++    if (isNaN(x = +this._x.call(null, d = data[i])) || isNaN(y = +this._y.call(null, d))) continue;
++    xz[i] = x;
++    yz[i] = y;
++    if (x < x0) x0 = x;
++    if (x > x1) x1 = x;
++    if (y < y0) y0 = y;
++    if (y > y1) y1 = y;
++  }
++
++  // If there were no (valid) points, inherit the existing extent.
++  if (x1 < x0) x0 = this._x0, x1 = this._x1;
++  if (y1 < y0) y0 = this._y0, y1 = this._y1;
++
++  // Expand the tree to cover the new points.
++  this.cover(x0, y0).cover(x1, y1);
++
++  // Add the new points.
++  for (i = 0; i < n; ++i) {
++    add(this, xz[i], yz[i], data[i]);
++  }
++
++  return this;
++}
++
++function tree_cover(x, y) {
++  if (isNaN(x = +x) || isNaN(y = +y)) return this; // ignore invalid points
++
++  var x0 = this._x0,
++      y0 = this._y0,
++      x1 = this._x1,
++      y1 = this._y1;
++
++  // If the quadtree has no extent, initialize them.
++  // Integer extent are necessary so that if we later double the extent,
++  // the existing quadrant boundaries don’t change due to floating point error!
++  if (isNaN(x0)) {
++    x1 = (x0 = Math.floor(x)) + 1;
++    y1 = (y0 = Math.floor(y)) + 1;
++  }
++
++  // Otherwise, double repeatedly to cover.
++  else if (x0 > x || x > x1 || y0 > y || y > y1) {
++    var z = x1 - x0,
++        node = this._root,
++        parent,
++        i;
++
++    switch (i = (y < (y0 + y1) / 2) << 1 | (x < (x0 + x1) / 2)) {
++      case 0: {
++        do parent = new Array(4), parent[i] = node, node = parent;
++        while (z *= 2, x1 = x0 + z, y1 = y0 + z, x > x1 || y > y1);
++        break;
++      }
++      case 1: {
++        do parent = new Array(4), parent[i] = node, node = parent;
++        while (z *= 2, x0 = x1 - z, y1 = y0 + z, x0 > x || y > y1);
++        break;
++      }
++      case 2: {
++        do parent = new Array(4), parent[i] = node, node = parent;
++        while (z *= 2, x1 = x0 + z, y0 = y1 - z, x > x1 || y0 > y);
++        break;
++      }
++      case 3: {
++        do parent = new Array(4), parent[i] = node, node = parent;
++        while (z *= 2, x0 = x1 - z, y0 = y1 - z, x0 > x || y0 > y);
++        break;
++      }
++    }
++
++    if (this._root && this._root.length) this._root = node;
++  }
++
++  // If the quadtree covers the point already, just return.
++  else return this;
++
++  this._x0 = x0;
++  this._y0 = y0;
++  this._x1 = x1;
++  this._y1 = y1;
++  return this;
++}
++
++function tree_data() {
++  var data = [];
++  this.visit(function(node) {
++    if (!node.length) do data.push(node.data); while (node = node.next)
++  });
++  return data;
++}
++
++function tree_extent(_) {
++  return arguments.length
++      ? this.cover(+_[0][0], +_[0][1]).cover(+_[1][0], +_[1][1])
++      : isNaN(this._x0) ? undefined : [[this._x0, this._y0], [this._x1, this._y1]];
++}
++
++function Quad(node, x0, y0, x1, y1) {
++  this.node = node;
++  this.x0 = x0;
++  this.y0 = y0;
++  this.x1 = x1;
++  this.y1 = y1;
++}
++
++function tree_find(x, y, radius) {
++  var data,
++      x0 = this._x0,
++      y0 = this._y0,
++      x1,
++      y1,
++      x2,
++      y2,
++      x3 = this._x1,
++      y3 = this._y1,
++      quads = [],
++      node = this._root,
++      q,
++      i;
++
++  if (node) quads.push(new Quad(node, x0, y0, x3, y3));
++  if (radius == null) radius = Infinity;
++  else {
++    x0 = x - radius, y0 = y - radius;
++    x3 = x + radius, y3 = y + radius;
++    radius *= radius;
++  }
++
++  while (q = quads.pop()) {
++
++    // Stop searching if this quadrant can’t contain a closer node.
++    if (!(node = q.node)
++        || (x1 = q.x0) > x3
++        || (y1 = q.y0) > y3
++        || (x2 = q.x1) < x0
++        || (y2 = q.y1) < y0) continue;
++
++    // Bisect the current quadrant.
++    if (node.length) {
++      var xm = (x1 + x2) / 2,
++          ym = (y1 + y2) / 2;
++
++      quads.push(
++        new Quad(node[3], xm, ym, x2, y2),
++        new Quad(node[2], x1, ym, xm, y2),
++        new Quad(node[1], xm, y1, x2, ym),
++        new Quad(node[0], x1, y1, xm, ym)
++      );
++
++      // Visit the closest quadrant first.
++      if (i = (y >= ym) << 1 | (x >= xm)) {
++        q = quads[quads.length - 1];
++        quads[quads.length - 1] = quads[quads.length - 1 - i];
++        quads[quads.length - 1 - i] = q;
++      }
++    }
++
++    // Visit this point. (Visiting coincident points isn’t necessary!)
++    else {
++      var dx = x - +this._x.call(null, node.data),
++          dy = y - +this._y.call(null, node.data),
++          d2 = dx * dx + dy * dy;
++      if (d2 < radius) {
++        var d = Math.sqrt(radius = d2);
++        x0 = x - d, y0 = y - d;
++        x3 = x + d, y3 = y + d;
++        data = node.data;
++      }
++    }
++  }
++
++  return data;
++}
++
++function tree_remove(d) {
++  if (isNaN(x = +this._x.call(null, d)) || isNaN(y = +this._y.call(null, d))) return this; // ignore invalid points
++
++  var parent,
++      node = this._root,
++      retainer,
++      previous,
++      next,
++      x0 = this._x0,
++      y0 = this._y0,
++      x1 = this._x1,
++      y1 = this._y1,
++      x,
++      y,
++      xm,
++      ym,
++      right,
++      bottom,
++      i,
++      j;
++
++  // If the tree is empty, initialize the root as a leaf.
++  if (!node) return this;
++
++  // Find the leaf node for the point.
++  // While descending, also retain the deepest parent with a non-removed sibling.
++  if (node.length) while (true) {
++    if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;
++    if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym;
++    if (!(parent = node, node = node[i = bottom << 1 | right])) return this;
++    if (!node.length) break;
++    if (parent[(i + 1) & 3] || parent[(i + 2) & 3] || parent[(i + 3) & 3]) retainer = parent, j = i;
++  }
++
++  // Find the point to remove.
++  while (node.data !== d) if (!(previous = node, node = node.next)) return this;
++  if (next = node.next) delete node.next;
++
++  // If there are multiple coincident points, remove just the point.
++  if (previous) return next ? previous.next = next : delete previous.next, this;
++
++  // If this is the root point, remove it.
++  if (!parent) return this._root = next, this;
++
++  // Remove this leaf.
++  next ? parent[i] = next : delete parent[i];
++
++  // If the parent now contains exactly one leaf, collapse superfluous parents.
++  if ((node = parent[0] || parent[1] || parent[2] || parent[3])
++      && node === (parent[3] || parent[2] || parent[1] || parent[0])
++      && !node.length) {
++    if (retainer) retainer[j] = node;
++    else this._root = node;
++  }
++
++  return this;
++}
++
++function removeAll(data) {
++  for (var i = 0, n = data.length; i < n; ++i) this.remove(data[i]);
++  return this;
++}
++
++function tree_root() {
++  return this._root;
++}
++
++function tree_size() {
++  var size = 0;
++  this.visit(function(node) {
++    if (!node.length) do ++size; while (node = node.next)
++  });
++  return size;
++}
++
++function tree_visit(callback) {
++  var quads = [], q, node = this._root, child, x0, y0, x1, y1;
++  if (node) quads.push(new Quad(node, this._x0, this._y0, this._x1, this._y1));
++  while (q = quads.pop()) {
++    if (!callback(node = q.node, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1) && node.length) {
++      var xm = (x0 + x1) / 2, ym = (y0 + y1) / 2;
++      if (child = node[3]) quads.push(new Quad(child, xm, ym, x1, y1));
++      if (child = node[2]) quads.push(new Quad(child, x0, ym, xm, y1));
++      if (child = node[1]) quads.push(new Quad(child, xm, y0, x1, ym));
++      if (child = node[0]) quads.push(new Quad(child, x0, y0, xm, ym));
++    }
++  }
++  return this;
++}
++
++function tree_visitAfter(callback) {
++  var quads = [], next = [], q;
++  if (this._root) quads.push(new Quad(this._root, this._x0, this._y0, this._x1, this._y1));
++  while (q = quads.pop()) {
++    var node = q.node;
++    if (node.length) {
++      var child, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1, xm = (x0 + x1) / 2, ym = (y0 + y1) / 2;
++      if (child = node[0]) quads.push(new Quad(child, x0, y0, xm, ym));
++      if (child = node[1]) quads.push(new Quad(child, xm, y0, x1, ym));
++      if (child = node[2]) quads.push(new Quad(child, x0, ym, xm, y1));
++      if (child = node[3]) quads.push(new Quad(child, xm, ym, x1, y1));
++    }
++    next.push(q);
++  }
++  while (q = next.pop()) {
++    callback(q.node, q.x0, q.y0, q.x1, q.y1);
++  }
++  return this;
++}
++
++function defaultX$1(d) {
++  return d[0];
++}
++
++function tree_x(_) {
++  return arguments.length ? (this._x = _, this) : this._x;
++}
++
++function defaultY$1(d) {
++  return d[1];
++}
++
++function tree_y(_) {
++  return arguments.length ? (this._y = _, this) : this._y;
++}
++
++function quadtree(nodes, x, y) {
++  var tree = new Quadtree(x == null ? defaultX$1 : x, y == null ? defaultY$1 : y, NaN, NaN, NaN, NaN);
++  return nodes == null ? tree : tree.addAll(nodes);
++}
++
++function Quadtree(x, y, x0, y0, x1, y1) {
++  this._x = x;
++  this._y = y;
++  this._x0 = x0;
++  this._y0 = y0;
++  this._x1 = x1;
++  this._y1 = y1;
++  this._root = undefined;
++}
++
++function leaf_copy(leaf) {
++  var copy = {data: leaf.data}, next = copy;
++  while (leaf = leaf.next) next = next.next = {data: leaf.data};
++  return copy;
++}
++
++var treeProto = quadtree.prototype = Quadtree.prototype;
++
++treeProto.copy = function() {
++  var copy = new Quadtree(this._x, this._y, this._x0, this._y0, this._x1, this._y1),
++      node = this._root,
++      nodes,
++      child;
++
++  if (!node) return copy;
++
++  if (!node.length) return copy._root = leaf_copy(node), copy;
++
++  nodes = [{source: node, target: copy._root = new Array(4)}];
++  while (node = nodes.pop()) {
++    for (var i = 0; i < 4; ++i) {
++      if (child = node.source[i]) {
++        if (child.length) nodes.push({source: child, target: node.target[i] = new Array(4)});
++        else node.target[i] = leaf_copy(child);
++      }
++    }
++  }
++
++  return copy;
++};
++
++treeProto.add = tree_add;
++treeProto.addAll = addAll;
++treeProto.cover = tree_cover;
++treeProto.data = tree_data;
++treeProto.extent = tree_extent;
++treeProto.find = tree_find;
++treeProto.remove = tree_remove;
++treeProto.removeAll = removeAll;
++treeProto.root = tree_root;
++treeProto.size = tree_size;
++treeProto.visit = tree_visit;
++treeProto.visitAfter = tree_visitAfter;
++treeProto.x = tree_x;
++treeProto.y = tree_y;
++
++function x(d) {
++  return d.x + d.vx;
++}
++
++function y(d) {
++  return d.y + d.vy;
++}
++
++function collide(radius) {
++  var nodes,
++      radii,
++      strength = 1,
++      iterations = 1;
++
++  if (typeof radius !== "function") radius = constant$7(radius == null ? 1 : +radius);
++
++  function force() {
++    var i, n = nodes.length,
++        tree,
++        node,
++        xi,
++        yi,
++        ri,
++        ri2;
++
++    for (var k = 0; k < iterations; ++k) {
++      tree = quadtree(nodes, x, y).visitAfter(prepare);
++      for (i = 0; i < n; ++i) {
++        node = nodes[i];
++        ri = radii[node.index], ri2 = ri * ri;
++        xi = node.x + node.vx;
++        yi = node.y + node.vy;
++        tree.visit(apply);
++      }
++    }
++
++    function apply(quad, x0, y0, x1, y1) {
++      var data = quad.data, rj = quad.r, r = ri + rj;
++      if (data) {
++        if (data.index > node.index) {
++          var x = xi - data.x - data.vx,
++              y = yi - data.y - data.vy,
++              l = x * x + y * y;
++          if (l < r * r) {
++            if (x === 0) x = jiggle(), l += x * x;
++            if (y === 0) y = jiggle(), l += y * y;
++            l = (r - (l = Math.sqrt(l))) / l * strength;
++            node.vx += (x *= l) * (r = (rj *= rj) / (ri2 + rj));
++            node.vy += (y *= l) * r;
++            data.vx -= x * (r = 1 - r);
++            data.vy -= y * r;
++          }
++        }
++        return;
++      }
++      return x0 > xi + r || x1 < xi - r || y0 > yi + r || y1 < yi - r;
++    }
++  }
++
++  function prepare(quad) {
++    if (quad.data) return quad.r = radii[quad.data.index];
++    for (var i = quad.r = 0; i < 4; ++i) {
++      if (quad[i] && quad[i].r > quad.r) {
++        quad.r = quad[i].r;
++      }
++    }
++  }
++
++  function initialize() {
++    if (!nodes) return;
++    var i, n = nodes.length, node;
++    radii = new Array(n);
++    for (i = 0; i < n; ++i) node = nodes[i], radii[node.index] = +radius(node, i, nodes);
++  }
++
++  force.initialize = function(_) {
++    nodes = _;
++    initialize();
++  };
++
++  force.iterations = function(_) {
++    return arguments.length ? (iterations = +_, force) : iterations;
++  };
++
++  force.strength = function(_) {
++    return arguments.length ? (strength = +_, force) : strength;
++  };
++
++  force.radius = function(_) {
++    return arguments.length ? (radius = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : radius;
++  };
++
++  return force;
++}
++
++function index(d) {
++  return d.index;
++}
++
++function find(nodeById, nodeId) {
++  var node = nodeById.get(nodeId);
++  if (!node) throw new Error("missing: " + nodeId);
++  return node;
++}
++
++function link(links) {
++  var id = index,
++      strength = defaultStrength,
++      strengths,
++      distance = constant$7(30),
++      distances,
++      nodes,
++      count,
++      bias,
++      iterations = 1;
++
++  if (links == null) links = [];
++
++  function defaultStrength(link) {
++    return 1 / Math.min(count[link.source.index], count[link.target.index]);
++  }
++
++  function force(alpha) {
++    for (var k = 0, n = links.length; k < iterations; ++k) {
++      for (var i = 0, link, source, target, x, y, l, b; i < n; ++i) {
++        link = links[i], source = link.source, target = link.target;
++        x = target.x + target.vx - source.x - source.vx || jiggle();
++        y = target.y + target.vy - source.y - source.vy || jiggle();
++        l = Math.sqrt(x * x + y * y);
++        l = (l - distances[i]) / l * alpha * strengths[i];
++        x *= l, y *= l;
++        target.vx -= x * (b = bias[i]);
++        target.vy -= y * b;
++        source.vx += x * (b = 1 - b);
++        source.vy += y * b;
++      }
++    }
++  }
++
++  function initialize() {
++    if (!nodes) return;
++
++    var i,
++        n = nodes.length,
++        m = links.length,
++        nodeById = map$1(nodes, id),
++        link;
++
++    for (i = 0, count = new Array(n); i < m; ++i) {
++      link = links[i], link.index = i;
++      if (typeof link.source !== "object") link.source = find(nodeById, link.source);
++      if (typeof link.target !== "object") link.target = find(nodeById, link.target);
++      count[link.source.index] = (count[link.source.index] || 0) + 1;
++      count[link.target.index] = (count[link.target.index] || 0) + 1;
++    }
++
++    for (i = 0, bias = new Array(m); i < m; ++i) {
++      link = links[i], bias[i] = count[link.source.index] / (count[link.source.index] + count[link.target.index]);
++    }
++
++    strengths = new Array(m), initializeStrength();
++    distances = new Array(m), initializeDistance();
++  }
++
++  function initializeStrength() {
++    if (!nodes) return;
++
++    for (var i = 0, n = links.length; i < n; ++i) {
++      strengths[i] = +strength(links[i], i, links);
++    }
++  }
++
++  function initializeDistance() {
++    if (!nodes) return;
++
++    for (var i = 0, n = links.length; i < n; ++i) {
++      distances[i] = +distance(links[i], i, links);
++    }
++  }
++
++  force.initialize = function(_) {
++    nodes = _;
++    initialize();
++  };
++
++  force.links = function(_) {
++    return arguments.length ? (links = _, initialize(), force) : links;
++  };
++
++  force.id = function(_) {
++    return arguments.length ? (id = _, force) : id;
++  };
++
++  force.iterations = function(_) {
++    return arguments.length ? (iterations = +_, force) : iterations;
++  };
++
++  force.strength = function(_) {
++    return arguments.length ? (strength = typeof _ === "function" ? _ : constant$7(+_), initializeStrength(), force) : strength;
++  };
++
++  force.distance = function(_) {
++    return arguments.length ? (distance = typeof _ === "function" ? _ : constant$7(+_), initializeDistance(), force) : distance;
++  };
++
++  return force;
++}
++
++function x$1(d) {
++  return d.x;
++}
++
++function y$1(d) {
++  return d.y;
++}
++
++var initialRadius = 10,
++    initialAngle = Math.PI * (3 - Math.sqrt(5));
++
++function simulation(nodes) {
++  var simulation,
++      alpha = 1,
++      alphaMin = 0.001,
++      alphaDecay = 1 - Math.pow(alphaMin, 1 / 300),
++      alphaTarget = 0,
++      velocityDecay = 0.6,
++      forces = map$1(),
++      stepper = timer(step),
++      event = dispatch("tick", "end");
++
++  if (nodes == null) nodes = [];
++
++  function step() {
++    tick();
++    event.call("tick", simulation);
++    if (alpha < alphaMin) {
++      stepper.stop();
++      event.call("end", simulation);
++    }
++  }
++
++  function tick() {
++    var i, n = nodes.length, node;
++
++    alpha += (alphaTarget - alpha) * alphaDecay;
++
++    forces.each(function(force) {
++      force(alpha);
++    });
++
++    for (i = 0; i < n; ++i) {
++      node = nodes[i];
++      if (node.fx == null) node.x += node.vx *= velocityDecay;
++      else node.x = node.fx, node.vx = 0;
++      if (node.fy == null) node.y += node.vy *= velocityDecay;
++      else node.y = node.fy, node.vy = 0;
++    }
++  }
++
++  function initializeNodes() {
++    for (var i = 0, n = nodes.length, node; i < n; ++i) {
++      node = nodes[i], node.index = i;
++      if (isNaN(node.x) || isNaN(node.y)) {
++        var radius = initialRadius * Math.sqrt(i), angle = i * initialAngle;
++        node.x = radius * Math.cos(angle);
++        node.y = radius * Math.sin(angle);
++      }
++      if (isNaN(node.vx) || isNaN(node.vy)) {
++        node.vx = node.vy = 0;
++      }
++    }
++  }
++
++  function initializeForce(force) {
++    if (force.initialize) force.initialize(nodes);
++    return force;
++  }
++
++  initializeNodes();
++
++  return simulation = {
++    tick: tick,
++
++    restart: function() {
++      return stepper.restart(step), simulation;
++    },
++
++    stop: function() {
++      return stepper.stop(), simulation;
++    },
++
++    nodes: function(_) {
++      return arguments.length ? (nodes = _, initializeNodes(), forces.each(initializeForce), simulation) : nodes;
++    },
++
++    alpha: function(_) {
++      return arguments.length ? (alpha = +_, simulation) : alpha;
++    },
++
++    alphaMin: function(_) {
++      return arguments.length ? (alphaMin = +_, simulation) : alphaMin;
++    },
++
++    alphaDecay: function(_) {
++      return arguments.length ? (alphaDecay = +_, simulation) : +alphaDecay;
++    },
++
++    alphaTarget: function(_) {
++      return arguments.length ? (alphaTarget = +_, simulation) : alphaTarget;
++    },
++
++    velocityDecay: function(_) {
++      return arguments.length ? (velocityDecay = 1 - _, simulation) : 1 - velocityDecay;
++    },
++
++    force: function(name, _) {
++      return arguments.length > 1 ? (_ == null ? forces.remove(name) : forces.set(name, initializeForce(_)), simulation) : forces.get(name);
++    },
++
++    find: function(x, y, radius) {
++      var i = 0,
++          n = nodes.length,
++          dx,
++          dy,
++          d2,
++          node,
++          closest;
++
++      if (radius == null) radius = Infinity;
++      else radius *= radius;
++
++      for (i = 0; i < n; ++i) {
++        node = nodes[i];
++        dx = x - node.x;
++        dy = y - node.y;
++        d2 = dx * dx + dy * dy;
++        if (d2 < radius) closest = node, radius = d2;
++      }
++
++      return closest;
++    },
++
++    on: function(name, _) {
++      return arguments.length > 1 ? (event.on(name, _), simulation) : event.on(name);
++    }
++  };
++}
++
++function manyBody() {
++  var nodes,
++      node,
++      alpha,
++      strength = constant$7(-30),
++      strengths,
++      distanceMin2 = 1,
++      distanceMax2 = Infinity,
++      theta2 = 0.81;
++
++  function force(_) {
++    var i, n = nodes.length, tree = quadtree(nodes, x$1, y$1).visitAfter(accumulate);
++    for (alpha = _, i = 0; i < n; ++i) node = nodes[i], tree.visit(apply);
++  }
++
++  function initialize() {
++    if (!nodes) return;
++    var i, n = nodes.length, node;
++    strengths = new Array(n);
++    for (i = 0; i < n; ++i) node = nodes[i], strengths[node.index] = +strength(node, i, nodes);
++  }
++
++  function accumulate(quad) {
++    var strength = 0, q, c, weight = 0, x, y, i;
++
++    // For internal nodes, accumulate forces from child quadrants.
++    if (quad.length) {
++      for (x = y = i = 0; i < 4; ++i) {
++        if ((q = quad[i]) && (c = Math.abs(q.value))) {
++          strength += q.value, weight += c, x += c * q.x, y += c * q.y;
++        }
++      }
++      quad.x = x / weight;
++      quad.y = y / weight;
++    }
++
++    // For leaf nodes, accumulate forces from coincident quadrants.
++    else {
++      q = quad;
++      q.x = q.data.x;
++      q.y = q.data.y;
++      do strength += strengths[q.data.index];
++      while (q = q.next);
++    }
++
++    quad.value = strength;
++  }
++
++  function apply(quad, x1, _, x2) {
++    if (!quad.value) return true;
++
++    var x = quad.x - node.x,
++        y = quad.y - node.y,
++        w = x2 - x1,
++        l = x * x + y * y;
++
++    // Apply the Barnes-Hut approximation if possible.
++    // Limit forces for very close nodes; randomize direction if coincident.
++    if (w * w / theta2 < l) {
++      if (l < distanceMax2) {
++        if (x === 0) x = jiggle(), l += x * x;
++        if (y === 0) y = jiggle(), l += y * y;
++        if (l < distanceMin2) l = Math.sqrt(distanceMin2 * l);
++        node.vx += x * quad.value * alpha / l;
++        node.vy += y * quad.value * alpha / l;
++      }
++      return true;
++    }
++
++    // Otherwise, process points directly.
++    else if (quad.length || l >= distanceMax2) return;
++
++    // Limit forces for very close nodes; randomize direction if coincident.
++    if (quad.data !== node || quad.next) {
++      if (x === 0) x = jiggle(), l += x * x;
++      if (y === 0) y = jiggle(), l += y * y;
++      if (l < distanceMin2) l = Math.sqrt(distanceMin2 * l);
++    }
++
++    do if (quad.data !== node) {
++      w = strengths[quad.data.index] * alpha / l;
++      node.vx += x * w;
++      node.vy += y * w;
++    } while (quad = quad.next);
++  }
++
++  force.initialize = function(_) {
++    nodes = _;
++    initialize();
++  };
++
++  force.strength = function(_) {
++    return arguments.length ? (strength = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : strength;
++  };
++
++  force.distanceMin = function(_) {
++    return arguments.length ? (distanceMin2 = _ * _, force) : Math.sqrt(distanceMin2);
++  };
++
++  force.distanceMax = function(_) {
++    return arguments.length ? (distanceMax2 = _ * _, force) : Math.sqrt(distanceMax2);
++  };
++
++  force.theta = function(_) {
++    return arguments.length ? (theta2 = _ * _, force) : Math.sqrt(theta2);
++  };
++
++  return force;
++}
++
++function radial(radius, x, y) {
++  var nodes,
++      strength = constant$7(0.1),
++      strengths,
++      radiuses;
++
++  if (typeof radius !== "function") radius = constant$7(+radius);
++  if (x == null) x = 0;
++  if (y == null) y = 0;
++
++  function force(alpha) {
++    for (var i = 0, n = nodes.length; i < n; ++i) {
++      var node = nodes[i],
++          dx = node.x - x || 1e-6,
++          dy = node.y - y || 1e-6,
++          r = Math.sqrt(dx * dx + dy * dy),
++          k = (radiuses[i] - r) * strengths[i] * alpha / r;
++      node.vx += dx * k;
++      node.vy += dy * k;
++    }
++  }
++
++  function initialize() {
++    if (!nodes) return;
++    var i, n = nodes.length;
++    strengths = new Array(n);
++    radiuses = new Array(n);
++    for (i = 0; i < n; ++i) {
++      radiuses[i] = +radius(nodes[i], i, nodes);
++      strengths[i] = isNaN(radiuses[i]) ? 0 : +strength(nodes[i], i, nodes);
++    }
++  }
++
++  force.initialize = function(_) {
++    nodes = _, initialize();
++  };
++
++  force.strength = function(_) {
++    return arguments.length ? (strength = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : strength;
++  };
++
++  force.radius = function(_) {
++    return arguments.length ? (radius = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : radius;
++  };
++
++  force.x = function(_) {
++    return arguments.length ? (x = +_, force) : x;
++  };
++
++  force.y = function(_) {
++    return arguments.length ? (y = +_, force) : y;
++  };
++
++  return force;
++}
++
++function x$2(x) {
++  var strength = constant$7(0.1),
++      nodes,
++      strengths,
++      xz;
++
++  if (typeof x !== "function") x = constant$7(x == null ? 0 : +x);
++
++  function force(alpha) {
++    for (var i = 0, n = nodes.length, node; i < n; ++i) {
++      node = nodes[i], node.vx += (xz[i] - node.x) * strengths[i] * alpha;
++    }
++  }
++
++  function initialize() {
++    if (!nodes) return;
++    var i, n = nodes.length;
++    strengths = new Array(n);
++    xz = new Array(n);
++    for (i = 0; i < n; ++i) {
++      strengths[i] = isNaN(xz[i] = +x(nodes[i], i, nodes)) ? 0 : +strength(nodes[i], i, nodes);
++    }
++  }
++
++  force.initialize = function(_) {
++    nodes = _;
++    initialize();
++  };
++
++  force.strength = function(_) {
++    return arguments.length ? (strength = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : strength;
++  };
++
++  force.x = function(_) {
++    return arguments.length ? (x = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : x;
++  };
++
++  return force;
++}
++
++function y$2(y) {
++  var strength = constant$7(0.1),
++      nodes,
++      strengths,
++      yz;
++
++  if (typeof y !== "function") y = constant$7(y == null ? 0 : +y);
++
++  function force(alpha) {
++    for (var i = 0, n = nodes.length, node; i < n; ++i) {
++      node = nodes[i], node.vy += (yz[i] - node.y) * strengths[i] * alpha;
++    }
++  }
++
++  function initialize() {
++    if (!nodes) return;
++    var i, n = nodes.length;
++    strengths = new Array(n);
++    yz = new Array(n);
++    for (i = 0; i < n; ++i) {
++      strengths[i] = isNaN(yz[i] = +y(nodes[i], i, nodes)) ? 0 : +strength(nodes[i], i, nodes);
++    }
++  }
++
++  force.initialize = function(_) {
++    nodes = _;
++    initialize();
++  };
++
++  force.strength = function(_) {
++    return arguments.length ? (strength = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : strength;
++  };
++
++  force.y = function(_) {
++    return arguments.length ? (y = typeof _ === "function" ? _ : constant$7(+_), initialize(), force) : y;
++  };
++
++  return force;
++}
++
++// Computes the decimal coefficient and exponent of the specified number x with
++// significant digits p, where x is positive and p is in [1, 21] or undefined.
++// For example, formatDecimal(1.23) returns ["123", 0].
++function formatDecimal(x, p) {
++  if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf("e")) < 0) return null; // NaN, ±Infinity
++  var i, coefficient = x.slice(0, i);
++
++  // The string returned by toExponential either has the form \d\.\d+e[-+]\d+
++  // (e.g., 1.2e+3) or the form \de[-+]\d+ (e.g., 1e+3).
++  return [
++    coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient,
++    +x.slice(i + 1)
++  ];
++}
++
++function exponent$1(x) {
++  return x = formatDecimal(Math.abs(x)), x ? x[1] : NaN;
++}
++
++function formatGroup(grouping, thousands) {
++  return function(value, width) {
++    var i = value.length,
++        t = [],
++        j = 0,
++        g = grouping[0],
++        length = 0;
++
++    while (i > 0 && g > 0) {
++      if (length + g + 1 > width) g = Math.max(1, width - length);
++      t.push(value.substring(i -= g, i + g));
++      if ((length += g + 1) > width) break;
++      g = grouping[j = (j + 1) % grouping.length];
++    }
++
++    return t.reverse().join(thousands);
++  };
++}
++
++function formatNumerals(numerals) {
++  return function(value) {
++    return value.replace(/[0-9]/g, function(i) {
++      return numerals[+i];
++    });
++  };
++}
++
++// [[fill]align][sign][symbol][0][width][,][.precision][~][type]
++var re = /^(?:(.)?([<>=^]))?([+\-\( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;
++
++function formatSpecifier(specifier) {
++  return new FormatSpecifier(specifier);
++}
++
++formatSpecifier.prototype = FormatSpecifier.prototype; // instanceof
++
++function FormatSpecifier(specifier) {
++  if (!(match = re.exec(specifier))) throw new Error("invalid format: " + specifier);
++  var match;
++  this.fill = match[1] || " ";
++  this.align = match[2] || ">";
++  this.sign = match[3] || "-";
++  this.symbol = match[4] || "";
++  this.zero = !!match[5];
++  this.width = match[6] && +match[6];
++  this.comma = !!match[7];
++  this.precision = match[8] && +match[8].slice(1);
++  this.trim = !!match[9];
++  this.type = match[10] || "";
++}
++
++FormatSpecifier.prototype.toString = function() {
++  return this.fill
++      + this.align
++      + this.sign
++      + this.symbol
++      + (this.zero ? "0" : "")
++      + (this.width == null ? "" : Math.max(1, this.width | 0))
++      + (this.comma ? "," : "")
++      + (this.precision == null ? "" : "." + Math.max(0, this.precision | 0))
++      + (this.trim ? "~" : "")
++      + this.type;
++};
++
++// Trims insignificant zeros, e.g., replaces 1.2000k with 1.2k.
++function formatTrim(s) {
++  out: for (var n = s.length, i = 1, i0 = -1, i1; i < n; ++i) {
++    switch (s[i]) {
++      case ".": i0 = i1 = i; break;
++      case "0": if (i0 === 0) i0 = i; i1 = i; break;
++      default: if (i0 > 0) { if (!+s[i]) break out; i0 = 0; } break;
++    }
++  }
++  return i0 > 0 ? s.slice(0, i0) + s.slice(i1 + 1) : s;
++}
++
++var prefixExponent;
++
++function formatPrefixAuto(x, p) {
++  var d = formatDecimal(x, p);
++  if (!d) return x + "";
++  var coefficient = d[0],
++      exponent = d[1],
++      i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1,
++      n = coefficient.length;
++  return i === n ? coefficient
++      : i > n ? coefficient + new Array(i - n + 1).join("0")
++      : i > 0 ? coefficient.slice(0, i) + "." + coefficient.slice(i)
++      : "0." + new Array(1 - i).join("0") + formatDecimal(x, Math.max(0, p + i - 1))[0]; // less than 1y!
++}
++
++function formatRounded(x, p) {
++  var d = formatDecimal(x, p);
++  if (!d) return x + "";
++  var coefficient = d[0],
++      exponent = d[1];
++  return exponent < 0 ? "0." + new Array(-exponent).join("0") + coefficient
++      : coefficient.length > exponent + 1 ? coefficient.slice(0, exponent + 1) + "." + coefficient.slice(exponent + 1)
++      : coefficient + new Array(exponent - coefficient.length + 2).join("0");
++}
++
++var formatTypes = {
++  "%": function(x, p) { return (x * 100).toFixed(p); },
++  "b": function(x) { return Math.round(x).toString(2); },
++  "c": function(x) { return x + ""; },
++  "d": function(x) { return Math.round(x).toString(10); },
++  "e": function(x, p) { return x.toExponential(p); },
++  "f": function(x, p) { return x.toFixed(p); },
++  "g": function(x, p) { return x.toPrecision(p); },
++  "o": function(x) { return Math.round(x).toString(8); },
++  "p": function(x, p) { return formatRounded(x * 100, p); },
++  "r": formatRounded,
++  "s": formatPrefixAuto,
++  "X": function(x) { return Math.round(x).toString(16).toUpperCase(); },
++  "x": function(x) { return Math.round(x).toString(16); }
++};
++
++function identity$3(x) {
++  return x;
++}
++
++var prefixes = ["y","z","a","f","p","n","\xB5","m","","k","M","G","T","P","E","Z","Y"];
++
++function formatLocale(locale) {
++  var group = locale.grouping && locale.thousands ? formatGroup(locale.grouping, locale.thousands) : identity$3,
++      currency = locale.currency,
++      decimal = locale.decimal,
++      numerals = locale.numerals ? formatNumerals(locale.numerals) : identity$3,
++      percent = locale.percent || "%";
++
++  function newFormat(specifier) {
++    specifier = formatSpecifier(specifier);
++
++    var fill = specifier.fill,
++        align = specifier.align,
++        sign = specifier.sign,
++        symbol = specifier.symbol,
++        zero = specifier.zero,
++        width = specifier.width,
++        comma = specifier.comma,
++        precision = specifier.precision,
++        trim = specifier.trim,
++        type = specifier.type;
++
++    // The "n" type is an alias for ",g".
++    if (type === "n") comma = true, type = "g";
++
++    // The "" type, and any invalid type, is an alias for ".12~g".
++    else if (!formatTypes[type]) precision == null && (precision = 12), trim = true, type = "g";
++
++    // If zero fill is specified, padding goes after sign and before digits.
++    if (zero || (fill === "0" && align === "=")) zero = true, fill = "0", align = "=";
++
++    // Compute the prefix and suffix.
++    // For SI-prefix, the suffix is lazily computed.
++    var prefix = symbol === "$" ? currency[0] : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : "",
++        suffix = symbol === "$" ? currency[1] : /[%p]/.test(type) ? percent : "";
++
++    // What format function should we use?
++    // Is this an integer type?
++    // Can this type generate exponential notation?
++    var formatType = formatTypes[type],
++        maybeSuffix = /[defgprs%]/.test(type);
++
++    // Set the default precision if not specified,
++    // or clamp the specified precision to the supported range.
++    // For significant precision, it must be in [1, 21].
++    // For fixed precision, it must be in [0, 20].
++    precision = precision == null ? 6
++        : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision))
++        : Math.max(0, Math.min(20, precision));
++
++    function format(value) {
++      var valuePrefix = prefix,
++          valueSuffix = suffix,
++          i, n, c;
++
++      if (type === "c") {
++        valueSuffix = formatType(value) + valueSuffix;
++        value = "";
++      } else {
++        value = +value;
++
++        // Perform the initial formatting.
++        var valueNegative = value < 0;
++        value = formatType(Math.abs(value), precision);
++
++        // Trim insignificant zeros.
++        if (trim) value = formatTrim(value);
++
++        // If a negative value rounds to zero during formatting, treat as positive.
++        if (valueNegative && +value === 0) valueNegative = false;
++
++        // Compute the prefix and suffix.
++        valuePrefix = (valueNegative ? (sign === "(" ? sign : "-") : sign === "-" || sign === "(" ? "" : sign) + valuePrefix;
++        valueSuffix = (type === "s" ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign === "(" ? ")" : "");
++
++        // Break the formatted value into the integer “value” part that can be
++        // grouped, and fractional or exponential “suffix” part that is not.
++        if (maybeSuffix) {
++          i = -1, n = value.length;
++          while (++i < n) {
++            if (c = value.charCodeAt(i), 48 > c || c > 57) {
++              valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix;
++              value = value.slice(0, i);
++              break;
++            }
++          }
++        }
++      }
++
++      // If the fill character is not "0", grouping is applied before padding.
++      if (comma && !zero) value = group(value, Infinity);
++
++      // Compute the padding.
++      var length = valuePrefix.length + value.length + valueSuffix.length,
++          padding = length < width ? new Array(width - length + 1).join(fill) : "";
++
++      // If the fill character is "0", grouping is applied after padding.
++      if (comma && zero) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = "";
++
++      // Reconstruct the final output based on the desired alignment.
++      switch (align) {
++        case "<": value = valuePrefix + value + valueSuffix + padding; break;
++        case "=": value = valuePrefix + padding + value + valueSuffix; break;
++        case "^": value = padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length); break;
++        default: value = padding + valuePrefix + value + valueSuffix; break;
++      }
++
++      return numerals(value);
++    }
++
++    format.toString = function() {
++      return specifier + "";
++    };
++
++    return format;
++  }
++
++  function formatPrefix(specifier, value) {
++    var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier)),
++        e = Math.max(-8, Math.min(8, Math.floor(exponent$1(value) / 3))) * 3,
++        k = Math.pow(10, -e),
++        prefix = prefixes[8 + e / 3];
++    return function(value) {
++      return f(k * value) + prefix;
++    };
++  }
++
++  return {
++    format: newFormat,
++    formatPrefix: formatPrefix
++  };
++}
++
++var locale;
++
++defaultLocale({
++  decimal: ".",
++  thousands: ",",
++  grouping: [3],
++  currency: ["$", ""]
++});
++
++function defaultLocale(definition) {
++  locale = formatLocale(definition);
++  exports.format = locale.format;
++  exports.formatPrefix = locale.formatPrefix;
++  return locale;
++}
++
++function precisionFixed(step) {
++  return Math.max(0, -exponent$1(Math.abs(step)));
++}
++
++function precisionPrefix(step, value) {
++  return Math.max(0, Math.max(-8, Math.min(8, Math.floor(exponent$1(value) / 3))) * 3 - exponent$1(Math.abs(step)));
++}
++
++function precisionRound(step, max) {
++  step = Math.abs(step), max = Math.abs(max) - step;
++  return Math.max(0, exponent$1(max) - exponent$1(step)) + 1;
++}
++
++// Adds floating point numbers with twice the normal precision.
++// Reference: J. R. Shewchuk, Adaptive Precision Floating-Point Arithmetic and
++// Fast Robust Geometric Predicates, Discrete & Computational Geometry 18(3)
++// 305–363 (1997).
++// Code adapted from GeographicLib by Charles F. F. Karney,
++// http://geographiclib.sourceforge.net/
++
++function adder() {
++  return new Adder;
++}
++
++function Adder() {
++  this.reset();
++}
++
++Adder.prototype = {
++  constructor: Adder,
++  reset: function() {
++    this.s = // rounded value
++    this.t = 0; // exact error
++  },
++  add: function(y) {
++    add$1(temp, y, this.t);
++    add$1(this, temp.s, this.s);
++    if (this.s) this.t += temp.t;
++    else this.s = temp.t;
++  },
++  valueOf: function() {
++    return this.s;
++  }
++};
++
++var temp = new Adder;
++
++function add$1(adder, a, b) {
++  var x = adder.s = a + b,
++      bv = x - a,
++      av = x - bv;
++  adder.t = (a - av) + (b - bv);
++}
++
++var epsilon$2 = 1e-6;
++var epsilon2$1 = 1e-12;
++var pi$3 = Math.PI;
++var halfPi$2 = pi$3 / 2;
++var quarterPi = pi$3 / 4;
++var tau$3 = pi$3 * 2;
++
++var degrees$1 = 180 / pi$3;
++var radians = pi$3 / 180;
++
++var abs = Math.abs;
++var atan = Math.atan;
++var atan2 = Math.atan2;
++var cos$1 = Math.cos;
++var ceil = Math.ceil;
++var exp = Math.exp;
++var log = Math.log;
++var pow = Math.pow;
++var sin$1 = Math.sin;
++var sign = Math.sign || function(x) { return x > 0 ? 1 : x < 0 ? -1 : 0; };
++var sqrt = Math.sqrt;
++var tan = Math.tan;
++
++function acos(x) {
++  return x > 1 ? 0 : x < -1 ? pi$3 : Math.acos(x);
++}
++
++function asin(x) {
++  return x > 1 ? halfPi$2 : x < -1 ? -halfPi$2 : Math.asin(x);
++}
++
++function haversin(x) {
++  return (x = sin$1(x / 2)) * x;
++}
++
++function noop$2() {}
++
++function streamGeometry(geometry, stream) {
++  if (geometry && streamGeometryType.hasOwnProperty(geometry.type)) {
++    streamGeometryType[geometry.type](geometry, stream);
++  }
++}
++
++var streamObjectType = {
++  Feature: function(object, stream) {
++    streamGeometry(object.geometry, stream);
++  },
++  FeatureCollection: function(object, stream) {
++    var features = object.features, i = -1, n = features.length;
++    while (++i < n) streamGeometry(features[i].geometry, stream);
++  }
++};
++
++var streamGeometryType = {
++  Sphere: function(object, stream) {
++    stream.sphere();
++  },
++  Point: function(object, stream) {
++    object = object.coordinates;
++    stream.point(object[0], object[1], object[2]);
++  },
++  MultiPoint: function(object, stream) {
++    var coordinates = object.coordinates, i = -1, n = coordinates.length;
++    while (++i < n) object = coordinates[i], stream.point(object[0], object[1], object[2]);
++  },
++  LineString: function(object, stream) {
++    streamLine(object.coordinates, stream, 0);
++  },
++  MultiLineString: function(object, stream) {
++    var coordinates = object.coordinates, i = -1, n = coordinates.length;
++    while (++i < n) streamLine(coordinates[i], stream, 0);
++  },
++  Polygon: function(object, stream) {
++    streamPolygon(object.coordinates, stream);
++  },
++  MultiPolygon: function(object, stream) {
++    var coordinates = object.coordinates, i = -1, n = coordinates.length;
++    while (++i < n) streamPolygon(coordinates[i], stream);
++  },
++  GeometryCollection: function(object, stream) {
++    var geometries = object.geometries, i = -1, n = geometries.length;
++    while (++i < n) streamGeometry(geometries[i], stream);
++  }
++};
++
++function streamLine(coordinates, stream, closed) {
++  var i = -1, n = coordinates.length - closed, coordinate;
++  stream.lineStart();
++  while (++i < n) coordinate = coordinates[i], stream.point(coordinate[0], coordinate[1], coordinate[2]);
++  stream.lineEnd();
++}
++
++function streamPolygon(coordinates, stream) {
++  var i = -1, n = coordinates.length;
++  stream.polygonStart();
++  while (++i < n) streamLine(coordinates[i], stream, 1);
++  stream.polygonEnd();
++}
++
++function geoStream(object, stream) {
++  if (object && streamObjectType.hasOwnProperty(object.type)) {
++    streamObjectType[object.type](object, stream);
++  } else {
++    streamGeometry(object, stream);
++  }
++}
++
++var areaRingSum = adder();
++
++var areaSum = adder(),
++    lambda00,
++    phi00,
++    lambda0,
++    cosPhi0,
++    sinPhi0;
++
++var areaStream = {
++  point: noop$2,
++  lineStart: noop$2,
++  lineEnd: noop$2,
++  polygonStart: function() {
++    areaRingSum.reset();
++    areaStream.lineStart = areaRingStart;
++    areaStream.lineEnd = areaRingEnd;
++  },
++  polygonEnd: function() {
++    var areaRing = +areaRingSum;
++    areaSum.add(areaRing < 0 ? tau$3 + areaRing : areaRing);
++    this.lineStart = this.lineEnd = this.point = noop$2;
++  },
++  sphere: function() {
++    areaSum.add(tau$3);
++  }
++};
++
++function areaRingStart() {
++  areaStream.point = areaPointFirst;
++}
++
++function areaRingEnd() {
++  areaPoint(lambda00, phi00);
++}
++
++function areaPointFirst(lambda, phi) {
++  areaStream.point = areaPoint;
++  lambda00 = lambda, phi00 = phi;
++  lambda *= radians, phi *= radians;
++  lambda0 = lambda, cosPhi0 = cos$1(phi = phi / 2 + quarterPi), sinPhi0 = sin$1(phi);
++}
++
++function areaPoint(lambda, phi) {
++  lambda *= radians, phi *= radians;
++  phi = phi / 2 + quarterPi; // half the angular distance from south pole
++
++  // Spherical excess E for a spherical triangle with vertices: south pole,
++  // previous point, current point.  Uses a formula derived from Cagnoli’s
++  // theorem.  See Todhunter, Spherical Trig. (1871), Sec. 103, Eq. (2).
++  var dLambda = lambda - lambda0,
++      sdLambda = dLambda >= 0 ? 1 : -1,
++      adLambda = sdLambda * dLambda,
++      cosPhi = cos$1(phi),
++      sinPhi = sin$1(phi),
++      k = sinPhi0 * sinPhi,
++      u = cosPhi0 * cosPhi + k * cos$1(adLambda),
++      v = k * sdLambda * sin$1(adLambda);
++  areaRingSum.add(atan2(v, u));
++
++  // Advance the previous points.
++  lambda0 = lambda, cosPhi0 = cosPhi, sinPhi0 = sinPhi;
++}
++
++function area$1(object) {
++  areaSum.reset();
++  geoStream(object, areaStream);
++  return areaSum * 2;
++}
++
++function spherical(cartesian) {
++  return [atan2(cartesian[1], cartesian[0]), asin(cartesian[2])];
++}
++
++function cartesian(spherical) {
++  var lambda = spherical[0], phi = spherical[1], cosPhi = cos$1(phi);
++  return [cosPhi * cos$1(lambda), cosPhi * sin$1(lambda), sin$1(phi)];
++}
++
++function cartesianDot(a, b) {
++  return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
++}
++
++function cartesianCross(a, b) {
++  return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]];
++}
++
++// TODO return a
++function cartesianAddInPlace(a, b) {
++  a[0] += b[0], a[1] += b[1], a[2] += b[2];
++}
++
++function cartesianScale(vector, k) {
++  return [vector[0] * k, vector[1] * k, vector[2] * k];
++}
++
++// TODO return d
++function cartesianNormalizeInPlace(d) {
++  var l = sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
++  d[0] /= l, d[1] /= l, d[2] /= l;
++}
++
++var lambda0$1, phi0, lambda1, phi1, // bounds
++    lambda2, // previous lambda-coordinate
++    lambda00$1, phi00$1, // first point
++    p0, // previous 3D point
++    deltaSum = adder(),
++    ranges,
++    range;
++
++var boundsStream = {
++  point: boundsPoint,
++  lineStart: boundsLineStart,
++  lineEnd: boundsLineEnd,
++  polygonStart: function() {
++    boundsStream.point = boundsRingPoint;
++    boundsStream.lineStart = boundsRingStart;
++    boundsStream.lineEnd = boundsRingEnd;
++    deltaSum.reset();
++    areaStream.polygonStart();
++  },
++  polygonEnd: function() {
++    areaStream.polygonEnd();
++    boundsStream.point = boundsPoint;
++    boundsStream.lineStart = boundsLineStart;
++    boundsStream.lineEnd = boundsLineEnd;
++    if (areaRingSum < 0) lambda0$1 = -(lambda1 = 180), phi0 = -(phi1 = 90);
++    else if (deltaSum > epsilon$2) phi1 = 90;
++    else if (deltaSum < -epsilon$2) phi0 = -90;
++    range[0] = lambda0$1, range[1] = lambda1;
++  }
++};
++
++function boundsPoint(lambda, phi) {
++  ranges.push(range = [lambda0$1 = lambda, lambda1 = lambda]);
++  if (phi < phi0) phi0 = phi;
++  if (phi > phi1) phi1 = phi;
++}
++
++function linePoint(lambda, phi) {
++  var p = cartesian([lambda * radians, phi * radians]);
++  if (p0) {
++    var normal = cartesianCross(p0, p),
++        equatorial = [normal[1], -normal[0], 0],
++        inflection = cartesianCross(equatorial, normal);
++    cartesianNormalizeInPlace(inflection);
++    inflection = spherical(inflection);
++    var delta = lambda - lambda2,
++        sign$$1 = delta > 0 ? 1 : -1,
++        lambdai = inflection[0] * degrees$1 * sign$$1,
++        phii,
++        antimeridian = abs(delta) > 180;
++    if (antimeridian ^ (sign$$1 * lambda2 < lambdai && lambdai < sign$$1 * lambda)) {
++      phii = inflection[1] * degrees$1;
++      if (phii > phi1) phi1 = phii;
++    } else if (lambdai = (lambdai + 360) % 360 - 180, antimeridian ^ (sign$$1 * lambda2 < lambdai && lambdai < sign$$1 * lambda)) {
++      phii = -inflection[1] * degrees$1;
++      if (phii < phi0) phi0 = phii;
++    } else {
++      if (phi < phi0) phi0 = phi;
++      if (phi > phi1) phi1 = phi;
++    }
++    if (antimeridian) {
++      if (lambda < lambda2) {
++        if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) lambda1 = lambda;
++      } else {
++        if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) lambda0$1 = lambda;
++      }
++    } else {
++      if (lambda1 >= lambda0$1) {
++        if (lambda < lambda0$1) lambda0$1 = lambda;
++        if (lambda > lambda1) lambda1 = lambda;
++      } else {
++        if (lambda > lambda2) {
++          if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) lambda1 = lambda;
++        } else {
++          if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) lambda0$1 = lambda;
++        }
++      }
++    }
++  } else {
++    ranges.push(range = [lambda0$1 = lambda, lambda1 = lambda]);
++  }
++  if (phi < phi0) phi0 = phi;
++  if (phi > phi1) phi1 = phi;
++  p0 = p, lambda2 = lambda;
++}
++
++function boundsLineStart() {
++  boundsStream.point = linePoint;
++}
++
++function boundsLineEnd() {
++  range[0] = lambda0$1, range[1] = lambda1;
++  boundsStream.point = boundsPoint;
++  p0 = null;
++}
++
++function boundsRingPoint(lambda, phi) {
++  if (p0) {
++    var delta = lambda - lambda2;
++    deltaSum.add(abs(delta) > 180 ? delta + (delta > 0 ? 360 : -360) : delta);
++  } else {
++    lambda00$1 = lambda, phi00$1 = phi;
++  }
++  areaStream.point(lambda, phi);
++  linePoint(lambda, phi);
++}
++
++function boundsRingStart() {
++  areaStream.lineStart();
++}
++
++function boundsRingEnd() {
++  boundsRingPoint(lambda00$1, phi00$1);
++  areaStream.lineEnd();
++  if (abs(deltaSum) > epsilon$2) lambda0$1 = -(lambda1 = 180);
++  range[0] = lambda0$1, range[1] = lambda1;
++  p0 = null;
++}
++
++// Finds the left-right distance between two longitudes.
++// This is almost the same as (lambda1 - lambda0 + 360°) % 360°, except that we want
++// the distance between ±180° to be 360°.
++function angle(lambda0, lambda1) {
++  return (lambda1 -= lambda0) < 0 ? lambda1 + 360 : lambda1;
++}
++
++function rangeCompare(a, b) {
++  return a[0] - b[0];
++}
++
++function rangeContains(range, x) {
++  return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x;
++}
++
++function bounds(feature) {
++  var i, n, a, b, merged, deltaMax, delta;
++
++  phi1 = lambda1 = -(lambda0$1 = phi0 = Infinity);
++  ranges = [];
++  geoStream(feature, boundsStream);
++
++  // First, sort ranges by their minimum longitudes.
++  if (n = ranges.length) {
++    ranges.sort(rangeCompare);
++
++    // Then, merge any ranges that overlap.
++    for (i = 1, a = ranges[0], merged = [a]; i < n; ++i) {
++      b = ranges[i];
++      if (rangeContains(a, b[0]) || rangeContains(a, b[1])) {
++        if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1];
++        if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0];
++      } else {
++        merged.push(a = b);
++      }
++    }
++
++    // Finally, find the largest gap between the merged ranges.
++    // The final bounding box will be the inverse of this gap.
++    for (deltaMax = -Infinity, n = merged.length - 1, i = 0, a = merged[n]; i <= n; a = b, ++i) {
++      b = merged[i];
++      if ((delta = angle(a[1], b[0])) > deltaMax) deltaMax = delta, lambda0$1 = b[0], lambda1 = a[1];
++    }
++  }
++
++  ranges = range = null;
++
++  return lambda0$1 === Infinity || phi0 === Infinity
++      ? [[NaN, NaN], [NaN, NaN]]
++      : [[lambda0$1, phi0], [lambda1, phi1]];
++}
++
++var W0, W1,
++    X0, Y0, Z0,
++    X1, Y1, Z1,
++    X2, Y2, Z2,
++    lambda00$2, phi00$2, // first point
++    x0, y0, z0; // previous point
++
++var centroidStream = {
++  sphere: noop$2,
++  point: centroidPoint,
++  lineStart: centroidLineStart,
++  lineEnd: centroidLineEnd,
++  polygonStart: function() {
++    centroidStream.lineStart = centroidRingStart;
++    centroidStream.lineEnd = centroidRingEnd;
++  },
++  polygonEnd: function() {
++    centroidStream.lineStart = centroidLineStart;
++    centroidStream.lineEnd = centroidLineEnd;
++  }
++};
++
++// Arithmetic mean of Cartesian vectors.
++function centroidPoint(lambda, phi) {
++  lambda *= radians, phi *= radians;
++  var cosPhi = cos$1(phi);
++  centroidPointCartesian(cosPhi * cos$1(lambda), cosPhi * sin$1(lambda), sin$1(phi));
++}
++
++function centroidPointCartesian(x, y, z) {
++  ++W0;
++  X0 += (x - X0) / W0;
++  Y0 += (y - Y0) / W0;
++  Z0 += (z - Z0) / W0;
++}
++
++function centroidLineStart() {
++  centroidStream.point = centroidLinePointFirst;
++}
++
++function centroidLinePointFirst(lambda, phi) {
++  lambda *= radians, phi *= radians;
++  var cosPhi = cos$1(phi);
++  x0 = cosPhi * cos$1(lambda);
++  y0 = cosPhi * sin$1(lambda);
++  z0 = sin$1(phi);
++  centroidStream.point = centroidLinePoint;
++  centroidPointCartesian(x0, y0, z0);
++}
++
++function centroidLinePoint(lambda, phi) {
++  lambda *= radians, phi *= radians;
++  var cosPhi = cos$1(phi),
++      x = cosPhi * cos$1(lambda),
++      y = cosPhi * sin$1(lambda),
++      z = sin$1(phi),
++      w = atan2(sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), x0 * x + y0 * y + z0 * z);
++  W1 += w;
++  X1 += w * (x0 + (x0 = x));
++  Y1 += w * (y0 + (y0 = y));
++  Z1 += w * (z0 + (z0 = z));
++  centroidPointCartesian(x0, y0, z0);
++}
++
++function centroidLineEnd() {
++  centroidStream.point = centroidPoint;
++}
++
++// See J. E. Brock, The Inertia Tensor for a Spherical Triangle,
++// J. Applied Mechanics 42, 239 (1975).
++function centroidRingStart() {
++  centroidStream.point = centroidRingPointFirst;
++}
++
++function centroidRingEnd() {
++  centroidRingPoint(lambda00$2, phi00$2);
++  centroidStream.point = centroidPoint;
++}
++
++function centroidRingPointFirst(lambda, phi) {
++  lambda00$2 = lambda, phi00$2 = phi;
++  lambda *= radians, phi *= radians;
++  centroidStream.point = centroidRingPoint;
++  var cosPhi = cos$1(phi);
++  x0 = cosPhi * cos$1(lambda);
++  y0 = cosPhi * sin$1(lambda);
++  z0 = sin$1(phi);
++  centroidPointCartesian(x0, y0, z0);
++}
++
++function centroidRingPoint(lambda, phi) {
++  lambda *= radians, phi *= radians;
++  var cosPhi = cos$1(phi),
++      x = cosPhi * cos$1(lambda),
++      y = cosPhi * sin$1(lambda),
++      z = sin$1(phi),
++      cx = y0 * z - z0 * y,
++      cy = z0 * x - x0 * z,
++      cz = x0 * y - y0 * x,
++      m = sqrt(cx * cx + cy * cy + cz * cz),
++      w = asin(m), // line weight = angle
++      v = m && -w / m; // area weight multiplier
++  X2 += v * cx;
++  Y2 += v * cy;
++  Z2 += v * cz;
++  W1 += w;
++  X1 += w * (x0 + (x0 = x));
++  Y1 += w * (y0 + (y0 = y));
++  Z1 += w * (z0 + (z0 = z));
++  centroidPointCartesian(x0, y0, z0);
++}
++
++function centroid(object) {
++  W0 = W1 =
++  X0 = Y0 = Z0 =
++  X1 = Y1 = Z1 =
++  X2 = Y2 = Z2 = 0;
++  geoStream(object, centroidStream);
++
++  var x = X2,
++      y = Y2,
++      z = Z2,
++      m = x * x + y * y + z * z;
++
++  // If the area-weighted ccentroid is undefined, fall back to length-weighted ccentroid.
++  if (m < epsilon2$1) {
++    x = X1, y = Y1, z = Z1;
++    // If the feature has zero length, fall back to arithmetic mean of point vectors.
++    if (W1 < epsilon$2) x = X0, y = Y0, z = Z0;
++    m = x * x + y * y + z * z;
++    // If the feature still has an undefined ccentroid, then return.
++    if (m < epsilon2$1) return [NaN, NaN];
++  }
++
++  return [atan2(y, x) * degrees$1, asin(z / sqrt(m)) * degrees$1];
++}
++
++function constant$8(x) {
++  return function() {
++    return x;
++  };
++}
++
++function compose(a, b) {
++
++  function compose(x, y) {
++    return x = a(x, y), b(x[0], x[1]);
++  }
++
++  if (a.invert && b.invert) compose.invert = function(x, y) {
++    return x = b.invert(x, y), x && a.invert(x[0], x[1]);
++  };
++
++  return compose;
++}
++
++function rotationIdentity(lambda, phi) {
++  return [lambda > pi$3 ? lambda - tau$3 : lambda < -pi$3 ? lambda + tau$3 : lambda, phi];
++}
++
++rotationIdentity.invert = rotationIdentity;
++
++function rotateRadians(deltaLambda, deltaPhi, deltaGamma) {
++  return (deltaLambda %= tau$3) ? (deltaPhi || deltaGamma ? compose(rotationLambda(deltaLambda), rotationPhiGamma(deltaPhi, deltaGamma))
++    : rotationLambda(deltaLambda))
++    : (deltaPhi || deltaGamma ? rotationPhiGamma(deltaPhi, deltaGamma)
++    : rotationIdentity);
++}
++
++function forwardRotationLambda(deltaLambda) {
++  return function(lambda, phi) {
++    return lambda += deltaLambda, [lambda > pi$3 ? lambda - tau$3 : lambda < -pi$3 ? lambda + tau$3 : lambda, phi];
++  };
++}
++
++function rotationLambda(deltaLambda) {
++  var rotation = forwardRotationLambda(deltaLambda);
++  rotation.invert = forwardRotationLambda(-deltaLambda);
++  return rotation;
++}
++
++function rotationPhiGamma(deltaPhi, deltaGamma) {
++  var cosDeltaPhi = cos$1(deltaPhi),
++      sinDeltaPhi = sin$1(deltaPhi),
++      cosDeltaGamma = cos$1(deltaGamma),
++      sinDeltaGamma = sin$1(deltaGamma);
++
++  function rotation(lambda, phi) {
++    var cosPhi = cos$1(phi),
++        x = cos$1(lambda) * cosPhi,
++        y = sin$1(lambda) * cosPhi,
++        z = sin$1(phi),
++        k = z * cosDeltaPhi + x * sinDeltaPhi;
++    return [
++      atan2(y * cosDeltaGamma - k * sinDeltaGamma, x * cosDeltaPhi - z * sinDeltaPhi),
++      asin(k * cosDeltaGamma + y * sinDeltaGamma)
++    ];
++  }
++
++  rotation.invert = function(lambda, phi) {
++    var cosPhi = cos$1(phi),
++        x = cos$1(lambda) * cosPhi,
++        y = sin$1(lambda) * cosPhi,
++        z = sin$1(phi),
++        k = z * cosDeltaGamma - y * sinDeltaGamma;
++    return [
++      atan2(y * cosDeltaGamma + z * sinDeltaGamma, x * cosDeltaPhi + k * sinDeltaPhi),
++      asin(k * cosDeltaPhi - x * sinDeltaPhi)
++    ];
++  };
++
++  return rotation;
++}
++
++function rotation(rotate) {
++  rotate = rotateRadians(rotate[0] * radians, rotate[1] * radians, rotate.length > 2 ? rotate[2] * radians : 0);
++
++  function forward(coordinates) {
++    coordinates = rotate(coordinates[0] * radians, coordinates[1] * radians);
++    return coordinates[0] *= degrees$1, coordinates[1] *= degrees$1, coordinates;
++  }
++
++  forward.invert = function(coordinates) {
++    coordinates = rotate.invert(coordinates[0] * radians, coordinates[1] * radians);
++    return coordinates[0] *= degrees$1, coordinates[1] *= degrees$1, coordinates;
++  };
++
++  return forward;
++}
++
++// Generates a circle centered at [0°, 0°], with a given radius and precision.
++function circleStream(stream, radius, delta, direction, t0, t1) {
++  if (!delta) return;
++  var cosRadius = cos$1(radius),
++      sinRadius = sin$1(radius),
++      step = direction * delta;
++  if (t0 == null) {
++    t0 = radius + direction * tau$3;
++    t1 = radius - step / 2;
++  } else {
++    t0 = circleRadius(cosRadius, t0);
++    t1 = circleRadius(cosRadius, t1);
++    if (direction > 0 ? t0 < t1 : t0 > t1) t0 += direction * tau$3;
++  }
++  for (var point, t = t0; direction > 0 ? t > t1 : t < t1; t -= step) {
++    point = spherical([cosRadius, -sinRadius * cos$1(t), -sinRadius * sin$1(t)]);
++    stream.point(point[0], point[1]);
++  }
++}
++
++// Returns the signed angle of a cartesian point relative to [cosRadius, 0, 0].
++function circleRadius(cosRadius, point) {
++  point = cartesian(point), point[0] -= cosRadius;
++  cartesianNormalizeInPlace(point);
++  var radius = acos(-point[1]);
++  return ((-point[2] < 0 ? -radius : radius) + tau$3 - epsilon$2) % tau$3;
++}
++
++function circle() {
++  var center = constant$8([0, 0]),
++      radius = constant$8(90),
++      precision = constant$8(6),
++      ring,
++      rotate,
++      stream = {point: point};
++
++  function point(x, y) {
++    ring.push(x = rotate(x, y));
++    x[0] *= degrees$1, x[1] *= degrees$1;
++  }
++
++  function circle() {
++    var c = center.apply(this, arguments),
++        r = radius.apply(this, arguments) * radians,
++        p = precision.apply(this, arguments) * radians;
++    ring = [];
++    rotate = rotateRadians(-c[0] * radians, -c[1] * radians, 0).invert;
++    circleStream(stream, r, p, 1);
++    c = {type: "Polygon", coordinates: [ring]};
++    ring = rotate = null;
++    return c;
++  }
++
++  circle.center = function(_) {
++    return arguments.length ? (center = typeof _ === "function" ? _ : constant$8([+_[0], +_[1]]), circle) : center;
++  };
++
++  circle.radius = function(_) {
++    return arguments.length ? (radius = typeof _ === "function" ? _ : constant$8(+_), circle) : radius;
++  };
++
++  circle.precision = function(_) {
++    return arguments.length ? (precision = typeof _ === "function" ? _ : constant$8(+_), circle) : precision;
++  };
++
++  return circle;
++}
++
++function clipBuffer() {
++  var lines = [],
++      line;
++  return {
++    point: function(x, y) {
++      line.push([x, y]);
++    },
++    lineStart: function() {
++      lines.push(line = []);
++    },
++    lineEnd: noop$2,
++    rejoin: function() {
++      if (lines.length > 1) lines.push(lines.pop().concat(lines.shift()));
++    },
++    result: function() {
++      var result = lines;
++      lines = [];
++      line = null;
++      return result;
++    }
++  };
++}
++
++function pointEqual(a, b) {
++  return abs(a[0] - b[0]) < epsilon$2 && abs(a[1] - b[1]) < epsilon$2;
++}
++
++function Intersection(point, points, other, entry) {
++  this.x = point;
++  this.z = points;
++  this.o = other; // another intersection
++  this.e = entry; // is an entry?
++  this.v = false; // visited
++  this.n = this.p = null; // next & previous
++}
++
++// A generalized polygon clipping algorithm: given a polygon that has been cut
++// into its visible line segments, and rejoins the segments by interpolating
++// along the clip edge.
++function clipRejoin(segments, compareIntersection, startInside, interpolate, stream) {
++  var subject = [],
++      clip = [],
++      i,
++      n;
++
++  segments.forEach(function(segment) {
++    if ((n = segment.length - 1) <= 0) return;
++    var n, p0 = segment[0], p1 = segment[n], x;
++
++    // If the first and last points of a segment are coincident, then treat as a
++    // closed ring. TODO if all rings are closed, then the winding order of the
++    // exterior ring should be checked.
++    if (pointEqual(p0, p1)) {
++      stream.lineStart();
++      for (i = 0; i < n; ++i) stream.point((p0 = segment[i])[0], p0[1]);
++      stream.lineEnd();
++      return;
++    }
++
++    subject.push(x = new Intersection(p0, segment, null, true));
++    clip.push(x.o = new Intersection(p0, null, x, false));
++    subject.push(x = new Intersection(p1, segment, null, false));
++    clip.push(x.o = new Intersection(p1, null, x, true));
++  });
++
++  if (!subject.length) return;
++
++  clip.sort(compareIntersection);
++  link$1(subject);
++  link$1(clip);
++
++  for (i = 0, n = clip.length; i < n; ++i) {
++    clip[i].e = startInside = !startInside;
++  }
++
++  var start = subject[0],
++      points,
++      point;
++
++  while (1) {
++    // Find first unvisited intersection.
++    var current = start,
++        isSubject = true;
++    while (current.v) if ((current = current.n) === start) return;
++    points = current.z;
++    stream.lineStart();
++    do {
++      current.v = current.o.v = true;
++      if (current.e) {
++        if (isSubject) {
++          for (i = 0, n = points.length; i < n; ++i) stream.point((point = points[i])[0], point[1]);
++        } else {
++          interpolate(current.x, current.n.x, 1, stream);
++        }
++        current = current.n;
++      } else {
++        if (isSubject) {
++          points = current.p.z;
++          for (i = points.length - 1; i >= 0; --i) stream.point((point = points[i])[0], point[1]);
++        } else {
++          interpolate(current.x, current.p.x, -1, stream);
++        }
++        current = current.p;
++      }
++      current = current.o;
++      points = current.z;
++      isSubject = !isSubject;
++    } while (!current.v);
++    stream.lineEnd();
++  }
++}
++
++function link$1(array) {
++  if (!(n = array.length)) return;
++  var n,
++      i = 0,
++      a = array[0],
++      b;
++  while (++i < n) {
++    a.n = b = array[i];
++    b.p = a;
++    a = b;
++  }
++  a.n = b = array[0];
++  b.p = a;
++}
++
++var sum$1 = adder();
++
++function polygonContains(polygon, point) {
++  var lambda = point[0],
++      phi = point[1],
++      sinPhi = sin$1(phi),
++      normal = [sin$1(lambda), -cos$1(lambda), 0],
++      angle = 0,
++      winding = 0;
++
++  sum$1.reset();
++
++  if (sinPhi === 1) phi = halfPi$2 + epsilon$2;
++  else if (sinPhi === -1) phi = -halfPi$2 - epsilon$2;
++
++  for (var i = 0, n = polygon.length; i < n; ++i) {
++    if (!(m = (ring = polygon[i]).length)) continue;
++    var ring,
++        m,
++        point0 = ring[m - 1],
++        lambda0 = point0[0],
++        phi0 = point0[1] / 2 + quarterPi,
++        sinPhi0 = sin$1(phi0),
++        cosPhi0 = cos$1(phi0);
++
++    for (var j = 0; j < m; ++j, lambda0 = lambda1, sinPhi0 = sinPhi1, cosPhi0 = cosPhi1, point0 = point1) {
++      var point1 = ring[j],
++          lambda1 = point1[0],
++          phi1 = point1[1] / 2 + quarterPi,
++          sinPhi1 = sin$1(phi1),
++          cosPhi1 = cos$1(phi1),
++          delta = lambda1 - lambda0,
++          sign$$1 = delta >= 0 ? 1 : -1,
++          absDelta = sign$$1 * delta,
++          antimeridian = absDelta > pi$3,
++          k = sinPhi0 * sinPhi1;
++
++      sum$1.add(atan2(k * sign$$1 * sin$1(absDelta), cosPhi0 * cosPhi1 + k * cos$1(absDelta)));
++      angle += antimeridian ? delta + sign$$1 * tau$3 : delta;
++
++      // Are the longitudes either side of the point’s meridian (lambda),
++      // and are the latitudes smaller than the parallel (phi)?
++      if (antimeridian ^ lambda0 >= lambda ^ lambda1 >= lambda) {
++        var arc = cartesianCross(cartesian(point0), cartesian(point1));
++        cartesianNormalizeInPlace(arc);
++        var intersection = cartesianCross(normal, arc);
++        cartesianNormalizeInPlace(intersection);
++        var phiArc = (antimeridian ^ delta >= 0 ? -1 : 1) * asin(intersection[2]);
++        if (phi > phiArc || phi === phiArc && (arc[0] || arc[1])) {
++          winding += antimeridian ^ delta >= 0 ? 1 : -1;
++        }
++      }
++    }
++  }
++
++  // First, determine whether the South pole is inside or outside:
++  //
++  // It is inside if:
++  // * the polygon winds around it in a clockwise direction.
++  // * the polygon does not (cumulatively) wind around it, but has a negative
++  //   (counter-clockwise) area.
++  //
++  // Second, count the (signed) number of times a segment crosses a lambda
++  // from the point to the South pole.  If it is zero, then the point is the
++  // same side as the South pole.
++
++  return (angle < -epsilon$2 || angle < epsilon$2 && sum$1 < -epsilon$2) ^ (winding & 1);
++}
++
++function clip(pointVisible, clipLine, interpolate, start) {
++  return function(sink) {
++    var line = clipLine(sink),
++        ringBuffer = clipBuffer(),
++        ringSink = clipLine(ringBuffer),
++        polygonStarted = false,
++        polygon,
++        segments,
++        ring;
++
++    var clip = {
++      point: point,
++      lineStart: lineStart,
++      lineEnd: lineEnd,
++      polygonStart: function() {
++        clip.point = pointRing;
++        clip.lineStart = ringStart;
++        clip.lineEnd = ringEnd;
++        segments = [];
++        polygon = [];
++      },
++      polygonEnd: function() {
++        clip.point = point;
++        clip.lineStart = lineStart;
++        clip.lineEnd = lineEnd;
++        segments = merge(segments);
++        var startInside = polygonContains(polygon, start);
++        if (segments.length) {
++          if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
++          clipRejoin(segments, compareIntersection, startInside, interpolate, sink);
++        } else if (startInside) {
++          if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
++          sink.lineStart();
++          interpolate(null, null, 1, sink);
++          sink.lineEnd();
++        }
++        if (polygonStarted) sink.polygonEnd(), polygonStarted = false;
++        segments = polygon = null;
++      },
++      sphere: function() {
++        sink.polygonStart();
++        sink.lineStart();
++        interpolate(null, null, 1, sink);
++        sink.lineEnd();
++        sink.polygonEnd();
++      }
++    };
++
++    function point(lambda, phi) {
++      if (pointVisible(lambda, phi)) sink.point(lambda, phi);
++    }
++
++    function pointLine(lambda, phi) {
++      line.point(lambda, phi);
++    }
++
++    function lineStart() {
++      clip.point = pointLine;
++      line.lineStart();
++    }
++
++    function lineEnd() {
++      clip.point = point;
++      line.lineEnd();
++    }
++
++    function pointRing(lambda, phi) {
++      ring.push([lambda, phi]);
++      ringSink.point(lambda, phi);
++    }
++
++    function ringStart() {
++      ringSink.lineStart();
++      ring = [];
++    }
++
++    function ringEnd() {
++      pointRing(ring[0][0], ring[0][1]);
++      ringSink.lineEnd();
++
++      var clean = ringSink.clean(),
++          ringSegments = ringBuffer.result(),
++          i, n = ringSegments.length, m,
++          segment,
++          point;
++
++      ring.pop();
++      polygon.push(ring);
++      ring = null;
++
++      if (!n) return;
++
++      // No intersections.
++      if (clean & 1) {
++        segment = ringSegments[0];
++        if ((m = segment.length - 1) > 0) {
++          if (!polygonStarted) sink.polygonStart(), polygonStarted = true;
++          sink.lineStart();
++          for (i = 0; i < m; ++i) sink.point((point = segment[i])[0], point[1]);
++          sink.lineEnd();
++        }
++        return;
++      }
++
++      // Rejoin connected segments.
++      // TODO reuse ringBuffer.rejoin()?
++      if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
++
++      segments.push(ringSegments.filter(validSegment));
++    }
++
++    return clip;
++  };
++}
++
++function validSegment(segment) {
++  return segment.length > 1;
++}
++
++// Intersections are sorted along the clip edge. For both antimeridian cutting
++// and circle clipping, the same comparison is used.
++function compareIntersection(a, b) {
++  return ((a = a.x)[0] < 0 ? a[1] - halfPi$2 - epsilon$2 : halfPi$2 - a[1])
++       - ((b = b.x)[0] < 0 ? b[1] - halfPi$2 - epsilon$2 : halfPi$2 - b[1]);
++}
++
++var clipAntimeridian = clip(
++  function() { return true; },
++  clipAntimeridianLine,
++  clipAntimeridianInterpolate,
++  [-pi$3, -halfPi$2]
++);
++
++// Takes a line and cuts into visible segments. Return values: 0 - there were
++// intersections or the line was empty; 1 - no intersections; 2 - there were
++// intersections, and the first and last segments should be rejoined.
++function clipAntimeridianLine(stream) {
++  var lambda0 = NaN,
++      phi0 = NaN,
++      sign0 = NaN,
++      clean; // no intersections
++
++  return {
++    lineStart: function() {
++      stream.lineStart();
++      clean = 1;
++    },
++    point: function(lambda1, phi1) {
++      var sign1 = lambda1 > 0 ? pi$3 : -pi$3,
++          delta = abs(lambda1 - lambda0);
++      if (abs(delta - pi$3) < epsilon$2) { // line crosses a pole
++        stream.point(lambda0, phi0 = (phi0 + phi1) / 2 > 0 ? halfPi$2 : -halfPi$2);
++        stream.point(sign0, phi0);
++        stream.lineEnd();
++        stream.lineStart();
++        stream.point(sign1, phi0);
++        stream.point(lambda1, phi0);
++        clean = 0;
++      } else if (sign0 !== sign1 && delta >= pi$3) { // line crosses antimeridian
++        if (abs(lambda0 - sign0) < epsilon$2) lambda0 -= sign0 * epsilon$2; // handle degeneracies
++        if (abs(lambda1 - sign1) < epsilon$2) lambda1 -= sign1 * epsilon$2;
++        phi0 = clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1);
++        stream.point(sign0, phi0);
++        stream.lineEnd();
++        stream.lineStart();
++        stream.point(sign1, phi0);
++        clean = 0;
++      }
++      stream.point(lambda0 = lambda1, phi0 = phi1);
++      sign0 = sign1;
++    },
++    lineEnd: function() {
++      stream.lineEnd();
++      lambda0 = phi0 = NaN;
++    },
++    clean: function() {
++      return 2 - clean; // if intersections, rejoin first and last segments
++    }
++  };
++}
++
++function clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1) {
++  var cosPhi0,
++      cosPhi1,
++      sinLambda0Lambda1 = sin$1(lambda0 - lambda1);
++  return abs(sinLambda0Lambda1) > epsilon$2
++      ? atan((sin$1(phi0) * (cosPhi1 = cos$1(phi1)) * sin$1(lambda1)
++          - sin$1(phi1) * (cosPhi0 = cos$1(phi0)) * sin$1(lambda0))
++          / (cosPhi0 * cosPhi1 * sinLambda0Lambda1))
++      : (phi0 + phi1) / 2;
++}
++
++function clipAntimeridianInterpolate(from, to, direction, stream) {
++  var phi;
++  if (from == null) {
++    phi = direction * halfPi$2;
++    stream.point(-pi$3, phi);
++    stream.point(0, phi);
++    stream.point(pi$3, phi);
++    stream.point(pi$3, 0);
++    stream.point(pi$3, -phi);
++    stream.point(0, -phi);
++    stream.point(-pi$3, -phi);
++    stream.point(-pi$3, 0);
++    stream.point(-pi$3, phi);
++  } else if (abs(from[0] - to[0]) > epsilon$2) {
++    var lambda = from[0] < to[0] ? pi$3 : -pi$3;
++    phi = direction * lambda / 2;
++    stream.point(-lambda, phi);
++    stream.point(0, phi);
++    stream.point(lambda, phi);
++  } else {
++    stream.point(to[0], to[1]);
++  }
++}
++
++function clipCircle(radius) {
++  var cr = cos$1(radius),
++      delta = 6 * radians,
++      smallRadius = cr > 0,
++      notHemisphere = abs(cr) > epsilon$2; // TODO optimise for this common case
++
++  function interpolate(from, to, direction, stream) {
++    circleStream(stream, radius, delta, direction, from, to);
++  }
++
++  function visible(lambda, phi) {
++    return cos$1(lambda) * cos$1(phi) > cr;
++  }
++
++  // Takes a line and cuts into visible segments. Return values used for polygon
++  // clipping: 0 - there were intersections or the line was empty; 1 - no
++  // intersections 2 - there were intersections, and the first and last segments
++  // should be rejoined.
++  function clipLine(stream) {
++    var point0, // previous point
++        c0, // code for previous point
++        v0, // visibility of previous point
++        v00, // visibility of first point
++        clean; // no intersections
++    return {
++      lineStart: function() {
++        v00 = v0 = false;
++        clean = 1;
++      },
++      point: function(lambda, phi) {
++        var point1 = [lambda, phi],
++            point2,
++            v = visible(lambda, phi),
++            c = smallRadius
++              ? v ? 0 : code(lambda, phi)
++              : v ? code(lambda + (lambda < 0 ? pi$3 : -pi$3), phi) : 0;
++        if (!point0 && (v00 = v0 = v)) stream.lineStart();
++        // Handle degeneracies.
++        // TODO ignore if not clipping polygons.
++        if (v !== v0) {
++          point2 = intersect(point0, point1);
++          if (!point2 || pointEqual(point0, point2) || pointEqual(point1, point2)) {
++            point1[0] += epsilon$2;
++            point1[1] += epsilon$2;
++            v = visible(point1[0], point1[1]);
++          }
++        }
++        if (v !== v0) {
++          clean = 0;
++          if (v) {
++            // outside going in
++            stream.lineStart();
++            point2 = intersect(point1, point0);
++            stream.point(point2[0], point2[1]);
++          } else {
++            // inside going out
++            point2 = intersect(point0, point1);
++            stream.point(point2[0], point2[1]);
++            stream.lineEnd();
++          }
++          point0 = point2;
++        } else if (notHemisphere && point0 && smallRadius ^ v) {
++          var t;
++          // If the codes for two points are different, or are both zero,
++          // and there this segment intersects with the small circle.
++          if (!(c & c0) && (t = intersect(point1, point0, true))) {
++            clean = 0;
++            if (smallRadius) {
++              stream.lineStart();
++              stream.point(t[0][0], t[0][1]);
++              stream.point(t[1][0], t[1][1]);
++              stream.lineEnd();
++            } else {
++              stream.point(t[1][0], t[1][1]);
++              stream.lineEnd();
++              stream.lineStart();
++              stream.point(t[0][0], t[0][1]);
++            }
++          }
++        }
++        if (v && (!point0 || !pointEqual(point0, point1))) {
++          stream.point(point1[0], point1[1]);
++        }
++        point0 = point1, v0 = v, c0 = c;
++      },
++      lineEnd: function() {
++        if (v0) stream.lineEnd();
++        point0 = null;
++      },
++      // Rejoin first and last segments if there were intersections and the first
++      // and last points were visible.
++      clean: function() {
++        return clean | ((v00 && v0) << 1);
++      }
++    };
++  }
++
++  // Intersects the great circle between a and b with the clip circle.
++  function intersect(a, b, two) {
++    var pa = cartesian(a),
++        pb = cartesian(b);
++
++    // We have two planes, n1.p = d1 and n2.p = d2.
++    // Find intersection line p(t) = c1 n1 + c2 n2 + t (n1 ⨯ n2).
++    var n1 = [1, 0, 0], // normal
++        n2 = cartesianCross(pa, pb),
++        n2n2 = cartesianDot(n2, n2),
++        n1n2 = n2[0], // cartesianDot(n1, n2),
++        determinant = n2n2 - n1n2 * n1n2;
++
++    // Two polar points.
++    if (!determinant) return !two && a;
++
++    var c1 =  cr * n2n2 / determinant,
++        c2 = -cr * n1n2 / determinant,
++        n1xn2 = cartesianCross(n1, n2),
++        A = cartesianScale(n1, c1),
++        B = cartesianScale(n2, c2);
++    cartesianAddInPlace(A, B);
++
++    // Solve |p(t)|^2 = 1.
++    var u = n1xn2,
++        w = cartesianDot(A, u),
++        uu = cartesianDot(u, u),
++        t2 = w * w - uu * (cartesianDot(A, A) - 1);
++
++    if (t2 < 0) return;
++
++    var t = sqrt(t2),
++        q = cartesianScale(u, (-w - t) / uu);
++    cartesianAddInPlace(q, A);
++    q = spherical(q);
++
++    if (!two) return q;
++
++    // Two intersection points.
++    var lambda0 = a[0],
++        lambda1 = b[0],
++        phi0 = a[1],
++        phi1 = b[1],
++        z;
++
++    if (lambda1 < lambda0) z = lambda0, lambda0 = lambda1, lambda1 = z;
++
++    var delta = lambda1 - lambda0,
++        polar = abs(delta - pi$3) < epsilon$2,
++        meridian = polar || delta < epsilon$2;
++
++    if (!polar && phi1 < phi0) z = phi0, phi0 = phi1, phi1 = z;
++
++    // Check that the first point is between a and b.
++    if (meridian
++        ? polar
++          ? phi0 + phi1 > 0 ^ q[1] < (abs(q[0] - lambda0) < epsilon$2 ? phi0 : phi1)
++          : phi0 <= q[1] && q[1] <= phi1
++        : delta > pi$3 ^ (lambda0 <= q[0] && q[0] <= lambda1)) {
++      var q1 = cartesianScale(u, (-w + t) / uu);
++      cartesianAddInPlace(q1, A);
++      return [q, spherical(q1)];
++    }
++  }
++
++  // Generates a 4-bit vector representing the location of a point relative to
++  // the small circle's bounding box.
++  function code(lambda, phi) {
++    var r = smallRadius ? radius : pi$3 - radius,
++        code = 0;
++    if (lambda < -r) code |= 1; // left
++    else if (lambda > r) code |= 2; // right
++    if (phi < -r) code |= 4; // below
++    else if (phi > r) code |= 8; // above
++    return code;
++  }
++
++  return clip(visible, clipLine, interpolate, smallRadius ? [0, -radius] : [-pi$3, radius - pi$3]);
++}
++
++function clipLine(a, b, x0, y0, x1, y1) {
++  var ax = a[0],
++      ay = a[1],
++      bx = b[0],
++      by = b[1],
++      t0 = 0,
++      t1 = 1,
++      dx = bx - ax,
++      dy = by - ay,
++      r;
++
++  r = x0 - ax;
++  if (!dx && r > 0) return;
++  r /= dx;
++  if (dx < 0) {
++    if (r < t0) return;
++    if (r < t1) t1 = r;
++  } else if (dx > 0) {
++    if (r > t1) return;
++    if (r > t0) t0 = r;
++  }
++
++  r = x1 - ax;
++  if (!dx && r < 0) return;
++  r /= dx;
++  if (dx < 0) {
++    if (r > t1) return;
++    if (r > t0) t0 = r;
++  } else if (dx > 0) {
++    if (r < t0) return;
++    if (r < t1) t1 = r;
++  }
++
++  r = y0 - ay;
++  if (!dy && r > 0) return;
++  r /= dy;
++  if (dy < 0) {
++    if (r < t0) return;
++    if (r < t1) t1 = r;
++  } else if (dy > 0) {
++    if (r > t1) return;
++    if (r > t0) t0 = r;
++  }
++
++  r = y1 - ay;
++  if (!dy && r < 0) return;
++  r /= dy;
++  if (dy < 0) {
++    if (r > t1) return;
++    if (r > t0) t0 = r;
++  } else if (dy > 0) {
++    if (r < t0) return;
++    if (r < t1) t1 = r;
++  }
++
++  if (t0 > 0) a[0] = ax + t0 * dx, a[1] = ay + t0 * dy;
++  if (t1 < 1) b[0] = ax + t1 * dx, b[1] = ay + t1 * dy;
++  return true;
++}
++
++var clipMax = 1e9, clipMin = -clipMax;
++
++// TODO Use d3-polygon’s polygonContains here for the ring check?
++// TODO Eliminate duplicate buffering in clipBuffer and polygon.push?
++
++function clipRectangle(x0, y0, x1, y1) {
++
++  function visible(x, y) {
++    return x0 <= x && x <= x1 && y0 <= y && y <= y1;
++  }
++
++  function interpolate(from, to, direction, stream) {
++    var a = 0, a1 = 0;
++    if (from == null
++        || (a = corner(from, direction)) !== (a1 = corner(to, direction))
++        || comparePoint(from, to) < 0 ^ direction > 0) {
++      do stream.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0);
++      while ((a = (a + direction + 4) % 4) !== a1);
++    } else {
++      stream.point(to[0], to[1]);
++    }
++  }
++
++  function corner(p, direction) {
++    return abs(p[0] - x0) < epsilon$2 ? direction > 0 ? 0 : 3
++        : abs(p[0] - x1) < epsilon$2 ? direction > 0 ? 2 : 1
++        : abs(p[1] - y0) < epsilon$2 ? direction > 0 ? 1 : 0
++        : direction > 0 ? 3 : 2; // abs(p[1] - y1) < epsilon
++  }
++
++  function compareIntersection(a, b) {
++    return comparePoint(a.x, b.x);
++  }
++
++  function comparePoint(a, b) {
++    var ca = corner(a, 1),
++        cb = corner(b, 1);
++    return ca !== cb ? ca - cb
++        : ca === 0 ? b[1] - a[1]
++        : ca === 1 ? a[0] - b[0]
++        : ca === 2 ? a[1] - b[1]
++        : b[0] - a[0];
++  }
++
++  return function(stream) {
++    var activeStream = stream,
++        bufferStream = clipBuffer(),
++        segments,
++        polygon,
++        ring,
++        x__, y__, v__, // first point
++        x_, y_, v_, // previous point
++        first,
++        clean;
++
++    var clipStream = {
++      point: point,
++      lineStart: lineStart,
++      lineEnd: lineEnd,
++      polygonStart: polygonStart,
++      polygonEnd: polygonEnd
++    };
++
++    function point(x, y) {
++      if (visible(x, y)) activeStream.point(x, y);
++    }
++
++    function polygonInside() {
++      var winding = 0;
++
++      for (var i = 0, n = polygon.length; i < n; ++i) {
++        for (var ring = polygon[i], j = 1, m = ring.length, point = ring[0], a0, a1, b0 = point[0], b1 = point[1]; j < m; ++j) {
++          a0 = b0, a1 = b1, point = ring[j], b0 = point[0], b1 = point[1];
++          if (a1 <= y1) { if (b1 > y1 && (b0 - a0) * (y1 - a1) > (b1 - a1) * (x0 - a0)) ++winding; }
++          else { if (b1 <= y1 && (b0 - a0) * (y1 - a1) < (b1 - a1) * (x0 - a0)) --winding; }
++        }
++      }
++
++      return winding;
++    }
++
++    // Buffer geometry within a polygon and then clip it en masse.
++    function polygonStart() {
++      activeStream = bufferStream, segments = [], polygon = [], clean = true;
++    }
++
++    function polygonEnd() {
++      var startInside = polygonInside(),
++          cleanInside = clean && startInside,
++          visible = (segments = merge(segments)).length;
++      if (cleanInside || visible) {
++        stream.polygonStart();
++        if (cleanInside) {
++          stream.lineStart();
++          interpolate(null, null, 1, stream);
++          stream.lineEnd();
++        }
++        if (visible) {
++          clipRejoin(segments, compareIntersection, startInside, interpolate, stream);
++        }
++        stream.polygonEnd();
++      }
++      activeStream = stream, segments = polygon = ring = null;
++    }
++
++    function lineStart() {
++      clipStream.point = linePoint;
++      if (polygon) polygon.push(ring = []);
++      first = true;
++      v_ = false;
++      x_ = y_ = NaN;
++    }
++
++    // TODO rather than special-case polygons, simply handle them separately.
++    // Ideally, coincident intersection points should be jittered to avoid
++    // clipping issues.
++    function lineEnd() {
++      if (segments) {
++        linePoint(x__, y__);
++        if (v__ && v_) bufferStream.rejoin();
++        segments.push(bufferStream.result());
++      }
++      clipStream.point = point;
++      if (v_) activeStream.lineEnd();
++    }
++
++    function linePoint(x, y) {
++      var v = visible(x, y);
++      if (polygon) ring.push([x, y]);
++      if (first) {
++        x__ = x, y__ = y, v__ = v;
++        first = false;
++        if (v) {
++          activeStream.lineStart();
++          activeStream.point(x, y);
++        }
++      } else {
++        if (v && v_) activeStream.point(x, y);
++        else {
++          var a = [x_ = Math.max(clipMin, Math.min(clipMax, x_)), y_ = Math.max(clipMin, Math.min(clipMax, y_))],
++              b = [x = Math.max(clipMin, Math.min(clipMax, x)), y = Math.max(clipMin, Math.min(clipMax, y))];
++          if (clipLine(a, b, x0, y0, x1, y1)) {
++            if (!v_) {
++              activeStream.lineStart();
++              activeStream.point(a[0], a[1]);
++            }
++            activeStream.point(b[0], b[1]);
++            if (!v) activeStream.lineEnd();
++            clean = false;
++          } else if (v) {
++            activeStream.lineStart();
++            activeStream.point(x, y);
++            clean = false;
++          }
++        }
++      }
++      x_ = x, y_ = y, v_ = v;
++    }
++
++    return clipStream;
++  };
++}
++
++function extent$1() {
++  var x0 = 0,
++      y0 = 0,
++      x1 = 960,
++      y1 = 500,
++      cache,
++      cacheStream,
++      clip;
++
++  return clip = {
++    stream: function(stream) {
++      return cache && cacheStream === stream ? cache : cache = clipRectangle(x0, y0, x1, y1)(cacheStream = stream);
++    },
++    extent: function(_) {
++      return arguments.length ? (x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1], cache = cacheStream = null, clip) : [[x0, y0], [x1, y1]];
++    }
++  };
++}
++
++var lengthSum = adder(),
++    lambda0$2,
++    sinPhi0$1,
++    cosPhi0$1;
++
++var lengthStream = {
++  sphere: noop$2,
++  point: noop$2,
++  lineStart: lengthLineStart,
++  lineEnd: noop$2,
++  polygonStart: noop$2,
++  polygonEnd: noop$2
++};
++
++function lengthLineStart() {
++  lengthStream.point = lengthPointFirst;
++  lengthStream.lineEnd = lengthLineEnd;
++}
++
++function lengthLineEnd() {
++  lengthStream.point = lengthStream.lineEnd = noop$2;
++}
++
++function lengthPointFirst(lambda, phi) {
++  lambda *= radians, phi *= radians;
++  lambda0$2 = lambda, sinPhi0$1 = sin$1(phi), cosPhi0$1 = cos$1(phi);
++  lengthStream.point = lengthPoint;
++}
++
++function lengthPoint(lambda, phi) {
++  lambda *= radians, phi *= radians;
++  var sinPhi = sin$1(phi),
++      cosPhi = cos$1(phi),
++      delta = abs(lambda - lambda0$2),
++      cosDelta = cos$1(delta),
++      sinDelta = sin$1(delta),
++      x = cosPhi * sinDelta,
++      y = cosPhi0$1 * sinPhi - sinPhi0$1 * cosPhi * cosDelta,
++      z = sinPhi0$1 * sinPhi + cosPhi0$1 * cosPhi * cosDelta;
++  lengthSum.add(atan2(sqrt(x * x + y * y), z));
++  lambda0$2 = lambda, sinPhi0$1 = sinPhi, cosPhi0$1 = cosPhi;
++}
++
++function length$1(object) {
++  lengthSum.reset();
++  geoStream(object, lengthStream);
++  return +lengthSum;
++}
++
++var coordinates = [null, null],
++    object$1 = {type: "LineString", coordinates: coordinates};
++
++function distance(a, b) {
++  coordinates[0] = a;
++  coordinates[1] = b;
++  return length$1(object$1);
++}
++
++var containsObjectType = {
++  Feature: function(object, point) {
++    return containsGeometry(object.geometry, point);
++  },
++  FeatureCollection: function(object, point) {
++    var features = object.features, i = -1, n = features.length;
++    while (++i < n) if (containsGeometry(features[i].geometry, point)) return true;
++    return false;
++  }
++};
++
++var containsGeometryType = {
++  Sphere: function() {
++    return true;
++  },
++  Point: function(object, point) {
++    return containsPoint(object.coordinates, point);
++  },
++  MultiPoint: function(object, point) {
++    var coordinates = object.coordinates, i = -1, n = coordinates.length;
++    while (++i < n) if (containsPoint(coordinates[i], point)) return true;
++    return false;
++  },
++  LineString: function(object, point) {
++    return containsLine(object.coordinates, point);
++  },
++  MultiLineString: function(object, point) {
++    var coordinates = object.coordinates, i = -1, n = coordinates.length;
++    while (++i < n) if (containsLine(coordinates[i], point)) return true;
++    return false;
++  },
++  Polygon: function(object, point) {
++    return containsPolygon(object.coordinates, point);
++  },
++  MultiPolygon: function(object, point) {
++    var coordinates = object.coordinates, i = -1, n = coordinates.length;
++    while (++i < n) if (containsPolygon(coordinates[i], point)) return true;
++    return false;
++  },
++  GeometryCollection: function(object, point) {
++    var geometries = object.geometries, i = -1, n = geometries.length;
++    while (++i < n) if (containsGeometry(geometries[i], point)) return true;
++    return false;
++  }
++};
++
++function containsGeometry(geometry, point) {
++  return geometry && containsGeometryType.hasOwnProperty(geometry.type)
++      ? containsGeometryType[geometry.type](geometry, point)
++      : false;
++}
++
++function containsPoint(coordinates, point) {
++  return distance(coordinates, point) === 0;
++}
++
++function containsLine(coordinates, point) {
++  var ab = distance(coordinates[0], coordinates[1]),
++      ao = distance(coordinates[0], point),
++      ob = distance(point, coordinates[1]);
++  return ao + ob <= ab + epsilon$2;
++}
++
++function containsPolygon(coordinates, point) {
++  return !!polygonContains(coordinates.map(ringRadians), pointRadians(point));
++}
++
++function ringRadians(ring) {
++  return ring = ring.map(pointRadians), ring.pop(), ring;
++}
++
++function pointRadians(point) {
++  return [point[0] * radians, point[1] * radians];
++}
++
++function contains$1(object, point) {
++  return (object && containsObjectType.hasOwnProperty(object.type)
++      ? containsObjectType[object.type]
++      : containsGeometry)(object, point);
++}
++
++function graticuleX(y0, y1, dy) {
++  var y = sequence(y0, y1 - epsilon$2, dy).concat(y1);
++  return function(x) { return y.map(function(y) { return [x, y]; }); };
++}
++
++function graticuleY(x0, x1, dx) {
++  var x = sequence(x0, x1 - epsilon$2, dx).concat(x1);
++  return function(y) { return x.map(function(x) { return [x, y]; }); };
++}
++
++function graticule() {
++  var x1, x0, X1, X0,
++      y1, y0, Y1, Y0,
++      dx = 10, dy = dx, DX = 90, DY = 360,
++      x, y, X, Y,
++      precision = 2.5;
++
++  function graticule() {
++    return {type: "MultiLineString", coordinates: lines()};
++  }
++
++  function lines() {
++    return sequence(ceil(X0 / DX) * DX, X1, DX).map(X)
++        .concat(sequence(ceil(Y0 / DY) * DY, Y1, DY).map(Y))
++        .concat(sequence(ceil(x0 / dx) * dx, x1, dx).filter(function(x) { return abs(x % DX) > epsilon$2; }).map(x))
++        .concat(sequence(ceil(y0 / dy) * dy, y1, dy).filter(function(y) { return abs(y % DY) > epsilon$2; }).map(y));
++  }
++
++  graticule.lines = function() {
++    return lines().map(function(coordinates) { return {type: "LineString", coordinates: coordinates}; });
++  };
++
++  graticule.outline = function() {
++    return {
++      type: "Polygon",
++      coordinates: [
++        X(X0).concat(
++        Y(Y1).slice(1),
++        X(X1).reverse().slice(1),
++        Y(Y0).reverse().slice(1))
++      ]
++    };
++  };
++
++  graticule.extent = function(_) {
++    if (!arguments.length) return graticule.extentMinor();
++    return graticule.extentMajor(_).extentMinor(_);
++  };
++
++  graticule.extentMajor = function(_) {
++    if (!arguments.length) return [[X0, Y0], [X1, Y1]];
++    X0 = +_[0][0], X1 = +_[1][0];
++    Y0 = +_[0][1], Y1 = +_[1][1];
++    if (X0 > X1) _ = X0, X0 = X1, X1 = _;
++    if (Y0 > Y1) _ = Y0, Y0 = Y1, Y1 = _;
++    return graticule.precision(precision);
++  };
++
++  graticule.extentMinor = function(_) {
++    if (!arguments.length) return [[x0, y0], [x1, y1]];
++    x0 = +_[0][0], x1 = +_[1][0];
++    y0 = +_[0][1], y1 = +_[1][1];
++    if (x0 > x1) _ = x0, x0 = x1, x1 = _;
++    if (y0 > y1) _ = y0, y0 = y1, y1 = _;
++    return graticule.precision(precision);
++  };
++
++  graticule.step = function(_) {
++    if (!arguments.length) return graticule.stepMinor();
++    return graticule.stepMajor(_).stepMinor(_);
++  };
++
++  graticule.stepMajor = function(_) {
++    if (!arguments.length) return [DX, DY];
++    DX = +_[0], DY = +_[1];
++    return graticule;
++  };
++
++  graticule.stepMinor = function(_) {
++    if (!arguments.length) return [dx, dy];
++    dx = +_[0], dy = +_[1];
++    return graticule;
++  };
++
++  graticule.precision = function(_) {
++    if (!arguments.length) return precision;
++    precision = +_;
++    x = graticuleX(y0, y1, 90);
++    y = graticuleY(x0, x1, precision);
++    X = graticuleX(Y0, Y1, 90);
++    Y = graticuleY(X0, X1, precision);
++    return graticule;
++  };
++
++  return graticule
++      .extentMajor([[-180, -90 + epsilon$2], [180, 90 - epsilon$2]])
++      .extentMinor([[-180, -80 - epsilon$2], [180, 80 + epsilon$2]]);
++}
++
++function graticule10() {
++  return graticule()();
++}
++
++function interpolate$1(a, b) {
++  var x0 = a[0] * radians,
++      y0 = a[1] * radians,
++      x1 = b[0] * radians,
++      y1 = b[1] * radians,
++      cy0 = cos$1(y0),
++      sy0 = sin$1(y0),
++      cy1 = cos$1(y1),
++      sy1 = sin$1(y1),
++      kx0 = cy0 * cos$1(x0),
++      ky0 = cy0 * sin$1(x0),
++      kx1 = cy1 * cos$1(x1),
++      ky1 = cy1 * sin$1(x1),
++      d = 2 * asin(sqrt(haversin(y1 - y0) + cy0 * cy1 * haversin(x1 - x0))),
++      k = sin$1(d);
++
++  var interpolate = d ? function(t) {
++    var B = sin$1(t *= d) / k,
++        A = sin$1(d - t) / k,
++        x = A * kx0 + B * kx1,
++        y = A * ky0 + B * ky1,
++        z = A * sy0 + B * sy1;
++    return [
++      atan2(y, x) * degrees$1,
++      atan2(z, sqrt(x * x + y * y)) * degrees$1
++    ];
++  } : function() {
++    return [x0 * degrees$1, y0 * degrees$1];
++  };
++
++  interpolate.distance = d;
++
++  return interpolate;
++}
++
++function identity$4(x) {
++  return x;
++}
++
++var areaSum$1 = adder(),
++    areaRingSum$1 = adder(),
++    x00,
++    y00,
++    x0$1,
++    y0$1;
++
++var areaStream$1 = {
++  point: noop$2,
++  lineStart: noop$2,
++  lineEnd: noop$2,
++  polygonStart: function() {
++    areaStream$1.lineStart = areaRingStart$1;
++    areaStream$1.lineEnd = areaRingEnd$1;
++  },
++  polygonEnd: function() {
++    areaStream$1.lineStart = areaStream$1.lineEnd = areaStream$1.point = noop$2;
++    areaSum$1.add(abs(areaRingSum$1));
++    areaRingSum$1.reset();
++  },
++  result: function() {
++    var area = areaSum$1 / 2;
++    areaSum$1.reset();
++    return area;
++  }
++};
++
++function areaRingStart$1() {
++  areaStream$1.point = areaPointFirst$1;
++}
++
++function areaPointFirst$1(x, y) {
++  areaStream$1.point = areaPoint$1;
++  x00 = x0$1 = x, y00 = y0$1 = y;
++}
++
++function areaPoint$1(x, y) {
++  areaRingSum$1.add(y0$1 * x - x0$1 * y);
++  x0$1 = x, y0$1 = y;
++}
++
++function areaRingEnd$1() {
++  areaPoint$1(x00, y00);
++}
++
++var x0$2 = Infinity,
++    y0$2 = x0$2,
++    x1 = -x0$2,
++    y1 = x1;
++
++var boundsStream$1 = {
++  point: boundsPoint$1,
++  lineStart: noop$2,
++  lineEnd: noop$2,
++  polygonStart: noop$2,
++  polygonEnd: noop$2,
++  result: function() {
++    var bounds = [[x0$2, y0$2], [x1, y1]];
++    x1 = y1 = -(y0$2 = x0$2 = Infinity);
++    return bounds;
++  }
++};
++
++function boundsPoint$1(x, y) {
++  if (x < x0$2) x0$2 = x;
++  if (x > x1) x1 = x;
++  if (y < y0$2) y0$2 = y;
++  if (y > y1) y1 = y;
++}
++
++// TODO Enforce positive area for exterior, negative area for interior?
++
++var X0$1 = 0,
++    Y0$1 = 0,
++    Z0$1 = 0,
++    X1$1 = 0,
++    Y1$1 = 0,
++    Z1$1 = 0,
++    X2$1 = 0,
++    Y2$1 = 0,
++    Z2$1 = 0,
++    x00$1,
++    y00$1,
++    x0$3,
++    y0$3;
++
++var centroidStream$1 = {
++  point: centroidPoint$1,
++  lineStart: centroidLineStart$1,
++  lineEnd: centroidLineEnd$1,
++  polygonStart: function() {
++    centroidStream$1.lineStart = centroidRingStart$1;
++    centroidStream$1.lineEnd = centroidRingEnd$1;
++  },
++  polygonEnd: function() {
++    centroidStream$1.point = centroidPoint$1;
++    centroidStream$1.lineStart = centroidLineStart$1;
++    centroidStream$1.lineEnd = centroidLineEnd$1;
++  },
++  result: function() {
++    var centroid = Z2$1 ? [X2$1 / Z2$1, Y2$1 / Z2$1]
++        : Z1$1 ? [X1$1 / Z1$1, Y1$1 / Z1$1]
++        : Z0$1 ? [X0$1 / Z0$1, Y0$1 / Z0$1]
++        : [NaN, NaN];
++    X0$1 = Y0$1 = Z0$1 =
++    X1$1 = Y1$1 = Z1$1 =
++    X2$1 = Y2$1 = Z2$1 = 0;
++    return centroid;
++  }
++};
++
++function centroidPoint$1(x, y) {
++  X0$1 += x;
++  Y0$1 += y;
++  ++Z0$1;
++}
++
++function centroidLineStart$1() {
++  centroidStream$1.point = centroidPointFirstLine;
++}
++
++function centroidPointFirstLine(x, y) {
++  centroidStream$1.point = centroidPointLine;
++  centroidPoint$1(x0$3 = x, y0$3 = y);
++}
++
++function centroidPointLine(x, y) {
++  var dx = x - x0$3, dy = y - y0$3, z = sqrt(dx * dx + dy * dy);
++  X1$1 += z * (x0$3 + x) / 2;
++  Y1$1 += z * (y0$3 + y) / 2;
++  Z1$1 += z;
++  centroidPoint$1(x0$3 = x, y0$3 = y);
++}
++
++function centroidLineEnd$1() {
++  centroidStream$1.point = centroidPoint$1;
++}
++
++function centroidRingStart$1() {
++  centroidStream$1.point = centroidPointFirstRing;
++}
++
++function centroidRingEnd$1() {
++  centroidPointRing(x00$1, y00$1);
++}
++
++function centroidPointFirstRing(x, y) {
++  centroidStream$1.point = centroidPointRing;
++  centroidPoint$1(x00$1 = x0$3 = x, y00$1 = y0$3 = y);
++}
++
++function centroidPointRing(x, y) {
++  var dx = x - x0$3,
++      dy = y - y0$3,
++      z = sqrt(dx * dx + dy * dy);
++
++  X1$1 += z * (x0$3 + x) / 2;
++  Y1$1 += z * (y0$3 + y) / 2;
++  Z1$1 += z;
++
++  z = y0$3 * x - x0$3 * y;
++  X2$1 += z * (x0$3 + x);
++  Y2$1 += z * (y0$3 + y);
++  Z2$1 += z * 3;
++  centroidPoint$1(x0$3 = x, y0$3 = y);
++}
++
++function PathContext(context) {
++  this._context = context;
++}
++
++PathContext.prototype = {
++  _radius: 4.5,
++  pointRadius: function(_) {
++    return this._radius = _, this;
++  },
++  polygonStart: function() {
++    this._line = 0;
++  },
++  polygonEnd: function() {
++    this._line = NaN;
++  },
++  lineStart: function() {
++    this._point = 0;
++  },
++  lineEnd: function() {
++    if (this._line === 0) this._context.closePath();
++    this._point = NaN;
++  },
++  point: function(x, y) {
++    switch (this._point) {
++      case 0: {
++        this._context.moveTo(x, y);
++        this._point = 1;
++        break;
++      }
++      case 1: {
++        this._context.lineTo(x, y);
++        break;
++      }
++      default: {
++        this._context.moveTo(x + this._radius, y);
++        this._context.arc(x, y, this._radius, 0, tau$3);
++        break;
++      }
++    }
++  },
++  result: noop$2
++};
++
++var lengthSum$1 = adder(),
++    lengthRing,
++    x00$2,
++    y00$2,
++    x0$4,
++    y0$4;
++
++var lengthStream$1 = {
++  point: noop$2,
++  lineStart: function() {
++    lengthStream$1.point = lengthPointFirst$1;
++  },
++  lineEnd: function() {
++    if (lengthRing) lengthPoint$1(x00$2, y00$2);
++    lengthStream$1.point = noop$2;
++  },
++  polygonStart: function() {
++    lengthRing = true;
++  },
++  polygonEnd: function() {
++    lengthRing = null;
++  },
++  result: function() {
++    var length = +lengthSum$1;
++    lengthSum$1.reset();
++    return length;
++  }
++};
++
++function lengthPointFirst$1(x, y) {
++  lengthStream$1.point = lengthPoint$1;
++  x00$2 = x0$4 = x, y00$2 = y0$4 = y;
++}
++
++function lengthPoint$1(x, y) {
++  x0$4 -= x, y0$4 -= y;
++  lengthSum$1.add(sqrt(x0$4 * x0$4 + y0$4 * y0$4));
++  x0$4 = x, y0$4 = y;
++}
++
++function PathString() {
++  this._string = [];
++}
++
++PathString.prototype = {
++  _radius: 4.5,
++  _circle: circle$1(4.5),
++  pointRadius: function(_) {
++    if ((_ = +_) !== this._radius) this._radius = _, this._circle = null;
++    return this;
++  },
++  polygonStart: function() {
++    this._line = 0;
++  },
++  polygonEnd: function() {
++    this._line = NaN;
++  },
++  lineStart: function() {
++    this._point = 0;
++  },
++  lineEnd: function() {
++    if (this._line === 0) this._string.push("Z");
++    this._point = NaN;
++  },
++  point: function(x, y) {
++    switch (this._point) {
++      case 0: {
++        this._string.push("M", x, ",", y);
++        this._point = 1;
++        break;
++      }
++      case 1: {
++        this._string.push("L", x, ",", y);
++        break;
++      }
++      default: {
++        if (this._circle == null) this._circle = circle$1(this._radius);
++        this._string.push("M", x, ",", y, this._circle);
++        break;
++      }
++    }
++  },
++  result: function() {
++    if (this._string.length) {
++      var result = this._string.join("");
++      this._string = [];
++      return result;
++    } else {
++      return null;
++    }
++  }
++};
++
++function circle$1(radius) {
++  return "m0," + radius
++      + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius
++      + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius
++      + "z";
++}
++
++function index$1(projection, context) {
++  var pointRadius = 4.5,
++      projectionStream,
++      contextStream;
++
++  function path(object) {
++    if (object) {
++      if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments));
++      geoStream(object, projectionStream(contextStream));
++    }
++    return contextStream.result();
++  }
++
++  path.area = function(object) {
++    geoStream(object, projectionStream(areaStream$1));
++    return areaStream$1.result();
++  };
++
++  path.measure = function(object) {
++    geoStream(object, projectionStream(lengthStream$1));
++    return lengthStream$1.result();
++  };
++
++  path.bounds = function(object) {
++    geoStream(object, projectionStream(boundsStream$1));
++    return boundsStream$1.result();
++  };
++
++  path.centroid = function(object) {
++    geoStream(object, projectionStream(centroidStream$1));
++    return centroidStream$1.result();
++  };
++
++  path.projection = function(_) {
++    return arguments.length ? (projectionStream = _ == null ? (projection = null, identity$4) : (projection = _).stream, path) : projection;
++  };
++
++  path.context = function(_) {
++    if (!arguments.length) return context;
++    contextStream = _ == null ? (context = null, new PathString) : new PathContext(context = _);
++    if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius);
++    return path;
++  };
++
++  path.pointRadius = function(_) {
++    if (!arguments.length) return pointRadius;
++    pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_);
++    return path;
++  };
++
++  return path.projection(projection).context(context);
++}
++
++function transform(methods) {
++  return {
++    stream: transformer(methods)
++  };
++}
++
++function transformer(methods) {
++  return function(stream) {
++    var s = new TransformStream;
++    for (var key in methods) s[key] = methods[key];
++    s.stream = stream;
++    return s;
++  };
++}
++
++function TransformStream() {}
++
++TransformStream.prototype = {
++  constructor: TransformStream,
++  point: function(x, y) { this.stream.point(x, y); },
++  sphere: function() { this.stream.sphere(); },
++  lineStart: function() { this.stream.lineStart(); },
++  lineEnd: function() { this.stream.lineEnd(); },
++  polygonStart: function() { this.stream.polygonStart(); },
++  polygonEnd: function() { this.stream.polygonEnd(); }
++};
++
++function fit(projection, fitBounds, object) {
++  var clip = projection.clipExtent && projection.clipExtent();
++  projection.scale(150).translate([0, 0]);
++  if (clip != null) projection.clipExtent(null);
++  geoStream(object, projection.stream(boundsStream$1));
++  fitBounds(boundsStream$1.result());
++  if (clip != null) projection.clipExtent(clip);
++  return projection;
++}
++
++function fitExtent(projection, extent, object) {
++  return fit(projection, function(b) {
++    var w = extent[1][0] - extent[0][0],
++        h = extent[1][1] - extent[0][1],
++        k = Math.min(w / (b[1][0] - b[0][0]), h / (b[1][1] - b[0][1])),
++        x = +extent[0][0] + (w - k * (b[1][0] + b[0][0])) / 2,
++        y = +extent[0][1] + (h - k * (b[1][1] + b[0][1])) / 2;
++    projection.scale(150 * k).translate([x, y]);
++  }, object);
++}
++
++function fitSize(projection, size, object) {
++  return fitExtent(projection, [[0, 0], size], object);
++}
++
++function fitWidth(projection, width, object) {
++  return fit(projection, function(b) {
++    var w = +width,
++        k = w / (b[1][0] - b[0][0]),
++        x = (w - k * (b[1][0] + b[0][0])) / 2,
++        y = -k * b[0][1];
++    projection.scale(150 * k).translate([x, y]);
++  }, object);
++}
++
++function fitHeight(projection, height, object) {
++  return fit(projection, function(b) {
++    var h = +height,
++        k = h / (b[1][1] - b[0][1]),
++        x = -k * b[0][0],
++        y = (h - k * (b[1][1] + b[0][1])) / 2;
++    projection.scale(150 * k).translate([x, y]);
++  }, object);
++}
++
++var maxDepth = 16, // maximum depth of subdivision
++    cosMinDistance = cos$1(30 * radians); // cos(minimum angular distance)
++
++function resample(project, delta2) {
++  return +delta2 ? resample$1(project, delta2) : resampleNone(project);
++}
++
++function resampleNone(project) {
++  return transformer({
++    point: function(x, y) {
++      x = project(x, y);
++      this.stream.point(x[0], x[1]);
++    }
++  });
++}
++
++function resample$1(project, delta2) {
++
++  function resampleLineTo(x0, y0, lambda0, a0, b0, c0, x1, y1, lambda1, a1, b1, c1, depth, stream) {
++    var dx = x1 - x0,
++        dy = y1 - y0,
++        d2 = dx * dx + dy * dy;
++    if (d2 > 4 * delta2 && depth--) {
++      var a = a0 + a1,
++          b = b0 + b1,
++          c = c0 + c1,
++          m = sqrt(a * a + b * b + c * c),
++          phi2 = asin(c /= m),
++          lambda2 = abs(abs(c) - 1) < epsilon$2 || abs(lambda0 - lambda1) < epsilon$2 ? (lambda0 + lambda1) / 2 : atan2(b, a),
++          p = project(lambda2, phi2),
++          x2 = p[0],
++          y2 = p[1],
++          dx2 = x2 - x0,
++          dy2 = y2 - y0,
++          dz = dy * dx2 - dx * dy2;
++      if (dz * dz / d2 > delta2 // perpendicular projected distance
++          || abs((dx * dx2 + dy * dy2) / d2 - 0.5) > 0.3 // midpoint close to an end
++          || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) { // angular distance
++        resampleLineTo(x0, y0, lambda0, a0, b0, c0, x2, y2, lambda2, a /= m, b /= m, c, depth, stream);
++        stream.point(x2, y2);
++        resampleLineTo(x2, y2, lambda2, a, b, c, x1, y1, lambda1, a1, b1, c1, depth, stream);
++      }
++    }
++  }
++  return function(stream) {
++    var lambda00, x00, y00, a00, b00, c00, // first point
++        lambda0, x0, y0, a0, b0, c0; // previous point
++
++    var resampleStream = {
++      point: point,
++      lineStart: lineStart,
++      lineEnd: lineEnd,
++      polygonStart: function() { stream.polygonStart(); resampleStream.lineStart = ringStart; },
++      polygonEnd: function() { stream.polygonEnd(); resampleStream.lineStart = lineStart; }
++    };
++
++    function point(x, y) {
++      x = project(x, y);
++      stream.point(x[0], x[1]);
++    }
++
++    function lineStart() {
++      x0 = NaN;
++      resampleStream.point = linePoint;
++      stream.lineStart();
++    }
++
++    function linePoint(lambda, phi) {
++      var c = cartesian([lambda, phi]), p = project(lambda, phi);
++      resampleLineTo(x0, y0, lambda0, a0, b0, c0, x0 = p[0], y0 = p[1], lambda0 = lambda, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream);
++      stream.point(x0, y0);
++    }
++
++    function lineEnd() {
++      resampleStream.point = point;
++      stream.lineEnd();
++    }
++
++    function ringStart() {
++      lineStart();
++      resampleStream.point = ringPoint;
++      resampleStream.lineEnd = ringEnd;
++    }
++
++    function ringPoint(lambda, phi) {
++      linePoint(lambda00 = lambda, phi), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0;
++      resampleStream.point = linePoint;
++    }
++
++    function ringEnd() {
++      resampleLineTo(x0, y0, lambda0, a0, b0, c0, x00, y00, lambda00, a00, b00, c00, maxDepth, stream);
++      resampleStream.lineEnd = lineEnd;
++      lineEnd();
++    }
++
++    return resampleStream;
++  };
++}
++
++var transformRadians = transformer({
++  point: function(x, y) {
++    this.stream.point(x * radians, y * radians);
++  }
++});
++
++function transformRotate(rotate) {
++  return transformer({
++    point: function(x, y) {
++      var r = rotate(x, y);
++      return this.stream.point(r[0], r[1]);
++    }
++  });
++}
++
++function scaleTranslate(k, dx, dy) {
++  function transform$$1(x, y) {
++    return [dx + k * x, dy - k * y];
++  }
++  transform$$1.invert = function(x, y) {
++    return [(x - dx) / k, (dy - y) / k];
++  };
++  return transform$$1;
++}
++
++function scaleTranslateRotate(k, dx, dy, alpha) {
++  var cosAlpha = cos$1(alpha),
++      sinAlpha = sin$1(alpha),
++      a = cosAlpha * k,
++      b = sinAlpha * k,
++      ai = cosAlpha / k,
++      bi = sinAlpha / k,
++      ci = (sinAlpha * dy - cosAlpha * dx) / k,
++      fi = (sinAlpha * dx + cosAlpha * dy) / k;
++  function transform$$1(x, y) {
++    return [a * x - b * y + dx, dy - b * x - a * y];
++  }
++  transform$$1.invert = function(x, y) {
++    return [ai * x - bi * y + ci, fi - bi * x - ai * y];
++  };
++  return transform$$1;
++}
++
++function projection(project) {
++  return projectionMutator(function() { return project; })();
++}
++
++function projectionMutator(projectAt) {
++  var project,
++      k = 150, // scale
++      x = 480, y = 250, // translate
++      lambda = 0, phi = 0, // center
++      deltaLambda = 0, deltaPhi = 0, deltaGamma = 0, rotate, // pre-rotate
++      alpha = 0, // post-rotate
++      theta = null, preclip = clipAntimeridian, // pre-clip angle
++      x0 = null, y0, x1, y1, postclip = identity$4, // post-clip extent
++      delta2 = 0.5, // precision
++      projectResample,
++      projectTransform,
++      projectRotateTransform,
++      cache,
++      cacheStream;
++
++  function projection(point) {
++    return projectRotateTransform(point[0] * radians, point[1] * radians);
++  }
++
++  function invert(point) {
++    point = projectRotateTransform.invert(point[0], point[1]);
++    return point && [point[0] * degrees$1, point[1] * degrees$1];
++  }
++
++  projection.stream = function(stream) {
++    return cache && cacheStream === stream ? cache : cache = transformRadians(transformRotate(rotate)(preclip(projectResample(postclip(cacheStream = stream)))));
++  };
++
++  projection.preclip = function(_) {
++    return arguments.length ? (preclip = _, theta = undefined, reset()) : preclip;
++  };
++
++  projection.postclip = function(_) {
++    return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip;
++  };
++
++  projection.clipAngle = function(_) {
++    return arguments.length ? (preclip = +_ ? clipCircle(theta = _ * radians) : (theta = null, clipAntimeridian), reset()) : theta * degrees$1;
++  };
++
++  projection.clipExtent = function(_) {
++    return arguments.length ? (postclip = _ == null ? (x0 = y0 = x1 = y1 = null, identity$4) : clipRectangle(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reset()) : x0 == null ? null : [[x0, y0], [x1, y1]];
++  };
++
++  projection.scale = function(_) {
++    return arguments.length ? (k = +_, recenter()) : k;
++  };
++
++  projection.translate = function(_) {
++    return arguments.length ? (x = +_[0], y = +_[1], recenter()) : [x, y];
++  };
++
++  projection.center = function(_) {
++    return arguments.length ? (lambda = _[0] % 360 * radians, phi = _[1] % 360 * radians, recenter()) : [lambda * degrees$1, phi * degrees$1];
++  };
++
++  projection.rotate = function(_) {
++    return arguments.length ? (deltaLambda = _[0] % 360 * radians, deltaPhi = _[1] % 360 * radians, deltaGamma = _.length > 2 ? _[2] % 360 * radians : 0, recenter()) : [deltaLambda * degrees$1, deltaPhi * degrees$1, deltaGamma * degrees$1];
++  };
++
++  projection.angle = function(_) {
++    return arguments.length ? (alpha = _ % 360 * radians, recenter()) : alpha * degrees$1;
++  };
++
++  projection.precision = function(_) {
++    return arguments.length ? (projectResample = resample(projectTransform, delta2 = _ * _), reset()) : sqrt(delta2);
++  };
++
++  projection.fitExtent = function(extent, object) {
++    return fitExtent(projection, extent, object);
++  };
++
++  projection.fitSize = function(size, object) {
++    return fitSize(projection, size, object);
++  };
++
++  projection.fitWidth = function(width, object) {
++    return fitWidth(projection, width, object);
++  };
++
++  projection.fitHeight = function(height, object) {
++    return fitHeight(projection, height, object);
++  };
++
++  function recenter() {
++    var center = scaleTranslateRotate(k, 0, 0, alpha).apply(null, project(lambda, phi)),
++        transform$$1 = (alpha ? scaleTranslateRotate : scaleTranslate)(k, x - center[0], y - center[1], alpha);
++    rotate = rotateRadians(deltaLambda, deltaPhi, deltaGamma);
++    projectTransform = compose(project, transform$$1);
++    projectRotateTransform = compose(rotate, projectTransform);
++    projectResample = resample(projectTransform, delta2);
++    return reset();
++  }
++
++  function reset() {
++    cache = cacheStream = null;
++    return projection;
++  }
++
++  return function() {
++    project = projectAt.apply(this, arguments);
++    projection.invert = project.invert && invert;
++    return recenter();
++  };
++}
++
++function conicProjection(projectAt) {
++  var phi0 = 0,
++      phi1 = pi$3 / 3,
++      m = projectionMutator(projectAt),
++      p = m(phi0, phi1);
++
++  p.parallels = function(_) {
++    return arguments.length ? m(phi0 = _[0] * radians, phi1 = _[1] * radians) : [phi0 * degrees$1, phi1 * degrees$1];
++  };
++
++  return p;
++}
++
++function cylindricalEqualAreaRaw(phi0) {
++  var cosPhi0 = cos$1(phi0);
++
++  function forward(lambda, phi) {
++    return [lambda * cosPhi0, sin$1(phi) / cosPhi0];
++  }
++
++  forward.invert = function(x, y) {
++    return [x / cosPhi0, asin(y * cosPhi0)];
++  };
++
++  return forward;
++}
++
++function conicEqualAreaRaw(y0, y1) {
++  var sy0 = sin$1(y0), n = (sy0 + sin$1(y1)) / 2;
++
++  // Are the parallels symmetrical around the Equator?
++  if (abs(n) < epsilon$2) return cylindricalEqualAreaRaw(y0);
++
++  var c = 1 + sy0 * (2 * n - sy0), r0 = sqrt(c) / n;
++
++  function project(x, y) {
++    var r = sqrt(c - 2 * n * sin$1(y)) / n;
++    return [r * sin$1(x *= n), r0 - r * cos$1(x)];
++  }
++
++  project.invert = function(x, y) {
++    var r0y = r0 - y;
++    return [atan2(x, abs(r0y)) / n * sign(r0y), asin((c - (x * x + r0y * r0y) * n * n) / (2 * n))];
++  };
++
++  return project;
++}
++
++function conicEqualArea() {
++  return conicProjection(conicEqualAreaRaw)
++      .scale(155.424)
++      .center([0, 33.6442]);
++}
++
++function albers() {
++  return conicEqualArea()
++      .parallels([29.5, 45.5])
++      .scale(1070)
++      .translate([480, 250])
++      .rotate([96, 0])
++      .center([-0.6, 38.7]);
++}
++
++// The projections must have mutually exclusive clip regions on the sphere,
++// as this will avoid emitting interleaving lines and polygons.
++function multiplex(streams) {
++  var n = streams.length;
++  return {
++    point: function(x, y) { var i = -1; while (++i < n) streams[i].point(x, y); },
++    sphere: function() { var i = -1; while (++i < n) streams[i].sphere(); },
++    lineStart: function() { var i = -1; while (++i < n) streams[i].lineStart(); },
++    lineEnd: function() { var i = -1; while (++i < n) streams[i].lineEnd(); },
++    polygonStart: function() { var i = -1; while (++i < n) streams[i].polygonStart(); },
++    polygonEnd: function() { var i = -1; while (++i < n) streams[i].polygonEnd(); }
++  };
++}
++
++// A composite projection for the United States, configured by default for
++// 960×500. The projection also works quite well at 960×600 if you change the
++// scale to 1285 and adjust the translate accordingly. The set of standard
++// parallels for each region comes from USGS, which is published here:
++// http://egsc.usgs.gov/isb/pubs/MapProjections/projections.html#albers
++function albersUsa() {
++  var cache,
++      cacheStream,
++      lower48 = albers(), lower48Point,
++      alaska = conicEqualArea().rotate([154, 0]).center([-2, 58.5]).parallels([55, 65]), alaskaPoint, // EPSG:3338
++      hawaii = conicEqualArea().rotate([157, 0]).center([-3, 19.9]).parallels([8, 18]), hawaiiPoint, // ESRI:102007
++      point, pointStream = {point: function(x, y) { point = [x, y]; }};
++
++  function albersUsa(coordinates) {
++    var x = coordinates[0], y = coordinates[1];
++    return point = null, (lower48Point.point(x, y), point)
++        || (alaskaPoint.point(x, y), point)
++        || (hawaiiPoint.point(x, y), point);
++  }
++
++  albersUsa.invert = function(coordinates) {
++    var k = lower48.scale(),
++        t = lower48.translate(),
++        x = (coordinates[0] - t[0]) / k,
++        y = (coordinates[1] - t[1]) / k;
++    return (y >= 0.120 && y < 0.234 && x >= -0.425 && x < -0.214 ? alaska
++        : y >= 0.166 && y < 0.234 && x >= -0.214 && x < -0.115 ? hawaii
++        : lower48).invert(coordinates);
++  };
++
++  albersUsa.stream = function(stream) {
++    return cache && cacheStream === stream ? cache : cache = multiplex([lower48.stream(cacheStream = stream), alaska.stream(stream), hawaii.stream(stream)]);
++  };
++
++  albersUsa.precision = function(_) {
++    if (!arguments.length) return lower48.precision();
++    lower48.precision(_), alaska.precision(_), hawaii.precision(_);
++    return reset();
++  };
++
++  albersUsa.scale = function(_) {
++    if (!arguments.length) return lower48.scale();
++    lower48.scale(_), alaska.scale(_ * 0.35), hawaii.scale(_);
++    return albersUsa.translate(lower48.translate());
++  };
++
++  albersUsa.translate = function(_) {
++    if (!arguments.length) return lower48.translate();
++    var k = lower48.scale(), x = +_[0], y = +_[1];
++
++    lower48Point = lower48
++        .translate(_)
++        .clipExtent([[x - 0.455 * k, y - 0.238 * k], [x + 0.455 * k, y + 0.238 * k]])
++        .stream(pointStream);
++
++    alaskaPoint = alaska
++        .translate([x - 0.307 * k, y + 0.201 * k])
++        .clipExtent([[x - 0.425 * k + epsilon$2, y + 0.120 * k + epsilon$2], [x - 0.214 * k - epsilon$2, y + 0.234 * k - epsilon$2]])
++        .stream(pointStream);
++
++    hawaiiPoint = hawaii
++        .translate([x - 0.205 * k, y + 0.212 * k])
++        .clipExtent([[x - 0.214 * k + epsilon$2, y + 0.166 * k + epsilon$2], [x - 0.115 * k - epsilon$2, y + 0.234 * k - epsilon$2]])
++        .stream(pointStream);
++
++    return reset();
++  };
++
++  albersUsa.fitExtent = function(extent, object) {
++    return fitExtent(albersUsa, extent, object);
++  };
++
++  albersUsa.fitSize = function(size, object) {
++    return fitSize(albersUsa, size, object);
++  };
++
++  albersUsa.fitWidth = function(width, object) {
++    return fitWidth(albersUsa, width, object);
++  };
++
++  albersUsa.fitHeight = function(height, object) {
++    return fitHeight(albersUsa, height, object);
++  };
++
++  function reset() {
++    cache = cacheStream = null;
++    return albersUsa;
++  }
++
++  return albersUsa.scale(1070);
++}
++
++function azimuthalRaw(scale) {
++  return function(x, y) {
++    var cx = cos$1(x),
++        cy = cos$1(y),
++        k = scale(cx * cy);
++    return [
++      k * cy * sin$1(x),
++      k * sin$1(y)
++    ];
++  }
++}
++
++function azimuthalInvert(angle) {
++  return function(x, y) {
++    var z = sqrt(x * x + y * y),
++        c = angle(z),
++        sc = sin$1(c),
++        cc = cos$1(c);
++    return [
++      atan2(x * sc, z * cc),
++      asin(z && y * sc / z)
++    ];
++  }
++}
++
++var azimuthalEqualAreaRaw = azimuthalRaw(function(cxcy) {
++  return sqrt(2 / (1 + cxcy));
++});
++
++azimuthalEqualAreaRaw.invert = azimuthalInvert(function(z) {
++  return 2 * asin(z / 2);
++});
++
++function azimuthalEqualArea() {
++  return projection(azimuthalEqualAreaRaw)
++      .scale(124.75)
++      .clipAngle(180 - 1e-3);
++}
++
++var azimuthalEquidistantRaw = azimuthalRaw(function(c) {
++  return (c = acos(c)) && c / sin$1(c);
++});
++
++azimuthalEquidistantRaw.invert = azimuthalInvert(function(z) {
++  return z;
++});
++
++function azimuthalEquidistant() {
++  return projection(azimuthalEquidistantRaw)
++      .scale(79.4188)
++      .clipAngle(180 - 1e-3);
++}
++
++function mercatorRaw(lambda, phi) {
++  return [lambda, log(tan((halfPi$2 + phi) / 2))];
++}
++
++mercatorRaw.invert = function(x, y) {
++  return [x, 2 * atan(exp(y)) - halfPi$2];
++};
++
++function mercator() {
++  return mercatorProjection(mercatorRaw)
++      .scale(961 / tau$3);
++}
++
++function mercatorProjection(project) {
++  var m = projection(project),
++      center = m.center,
++      scale = m.scale,
++      translate = m.translate,
++      clipExtent = m.clipExtent,
++      x0 = null, y0, x1, y1; // clip extent
++
++  m.scale = function(_) {
++    return arguments.length ? (scale(_), reclip()) : scale();
++  };
++
++  m.translate = function(_) {
++    return arguments.length ? (translate(_), reclip()) : translate();
++  };
++
++  m.center = function(_) {
++    return arguments.length ? (center(_), reclip()) : center();
++  };
++
++  m.clipExtent = function(_) {
++    return arguments.length ? (_ == null ? x0 = y0 = x1 = y1 = null : (x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reclip()) : x0 == null ? null : [[x0, y0], [x1, y1]];
++  };
++
++  function reclip() {
++    var k = pi$3 * scale(),
++        t = m(rotation(m.rotate()).invert([0, 0]));
++    return clipExtent(x0 == null
++        ? [[t[0] - k, t[1] - k], [t[0] + k, t[1] + k]] : project === mercatorRaw
++        ? [[Math.max(t[0] - k, x0), y0], [Math.min(t[0] + k, x1), y1]]
++        : [[x0, Math.max(t[1] - k, y0)], [x1, Math.min(t[1] + k, y1)]]);
++  }
++
++  return reclip();
++}
++
++function tany(y) {
++  return tan((halfPi$2 + y) / 2);
++}
++
++function conicConformalRaw(y0, y1) {
++  var cy0 = cos$1(y0),
++      n = y0 === y1 ? sin$1(y0) : log(cy0 / cos$1(y1)) / log(tany(y1) / tany(y0)),
++      f = cy0 * pow(tany(y0), n) / n;
++
++  if (!n) return mercatorRaw;
++
++  function project(x, y) {
++    if (f > 0) { if (y < -halfPi$2 + epsilon$2) y = -halfPi$2 + epsilon$2; }
++    else { if (y > halfPi$2 - epsilon$2) y = halfPi$2 - epsilon$2; }
++    var r = f / pow(tany(y), n);
++    return [r * sin$1(n * x), f - r * cos$1(n * x)];
++  }
++
++  project.invert = function(x, y) {
++    var fy = f - y, r = sign(n) * sqrt(x * x + fy * fy);
++    return [atan2(x, abs(fy)) / n * sign(fy), 2 * atan(pow(f / r, 1 / n)) - halfPi$2];
++  };
++
++  return project;
++}
++
++function conicConformal() {
++  return conicProjection(conicConformalRaw)
++      .scale(109.5)
++      .parallels([30, 30]);
++}
++
++function equirectangularRaw(lambda, phi) {
++  return [lambda, phi];
++}
++
++equirectangularRaw.invert = equirectangularRaw;
++
++function equirectangular() {
++  return projection(equirectangularRaw)
++      .scale(152.63);
++}
++
++function conicEquidistantRaw(y0, y1) {
++  var cy0 = cos$1(y0),
++      n = y0 === y1 ? sin$1(y0) : (cy0 - cos$1(y1)) / (y1 - y0),
++      g = cy0 / n + y0;
++
++  if (abs(n) < epsilon$2) return equirectangularRaw;
++
++  function project(x, y) {
++    var gy = g - y, nx = n * x;
++    return [gy * sin$1(nx), g - gy * cos$1(nx)];
++  }
++
++  project.invert = function(x, y) {
++    var gy = g - y;
++    return [atan2(x, abs(gy)) / n * sign(gy), g - sign(n) * sqrt(x * x + gy * gy)];
++  };
++
++  return project;
++}
++
++function conicEquidistant() {
++  return conicProjection(conicEquidistantRaw)
++      .scale(131.154)
++      .center([0, 13.9389]);
++}
++
++function gnomonicRaw(x, y) {
++  var cy = cos$1(y), k = cos$1(x) * cy;
++  return [cy * sin$1(x) / k, sin$1(y) / k];
++}
++
++gnomonicRaw.invert = azimuthalInvert(atan);
++
++function gnomonic() {
++  return projection(gnomonicRaw)
++      .scale(144.049)
++      .clipAngle(60);
++}
++
++function scaleTranslate$1(kx, ky, tx, ty) {
++  return kx === 1 && ky === 1 && tx === 0 && ty === 0 ? identity$4 : transformer({
++    point: function(x, y) {
++      this.stream.point(x * kx + tx, y * ky + ty);
++    }
++  });
++}
++
++function identity$5() {
++  var k = 1, tx = 0, ty = 0, sx = 1, sy = 1, transform$$1 = identity$4, // scale, translate and reflect
++      x0 = null, y0, x1, y1, // clip extent
++      postclip = identity$4,
++      cache,
++      cacheStream,
++      projection;
++
++  function reset() {
++    cache = cacheStream = null;
++    return projection;
++  }
++
++  return projection = {
++    stream: function(stream) {
++      return cache && cacheStream === stream ? cache : cache = transform$$1(postclip(cacheStream = stream));
++    },
++    postclip: function(_) {
++      return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip;
++    },
++    clipExtent: function(_) {
++      return arguments.length ? (postclip = _ == null ? (x0 = y0 = x1 = y1 = null, identity$4) : clipRectangle(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reset()) : x0 == null ? null : [[x0, y0], [x1, y1]];
++    },
++    scale: function(_) {
++      return arguments.length ? (transform$$1 = scaleTranslate$1((k = +_) * sx, k * sy, tx, ty), reset()) : k;
++    },
++    translate: function(_) {
++      return arguments.length ? (transform$$1 = scaleTranslate$1(k * sx, k * sy, tx = +_[0], ty = +_[1]), reset()) : [tx, ty];
++    },
++    reflectX: function(_) {
++      return arguments.length ? (transform$$1 = scaleTranslate$1(k * (sx = _ ? -1 : 1), k * sy, tx, ty), reset()) : sx < 0;
++    },
++    reflectY: function(_) {
++      return arguments.length ? (transform$$1 = scaleTranslate$1(k * sx, k * (sy = _ ? -1 : 1), tx, ty), reset()) : sy < 0;
++    },
++    fitExtent: function(extent, object) {
++      return fitExtent(projection, extent, object);
++    },
++    fitSize: function(size, object) {
++      return fitSize(projection, size, object);
++    },
++    fitWidth: function(width, object) {
++      return fitWidth(projection, width, object);
++    },
++    fitHeight: function(height, object) {
++      return fitHeight(projection, height, object);
++    }
++  };
++}
++
++function naturalEarth1Raw(lambda, phi) {
++  var phi2 = phi * phi, phi4 = phi2 * phi2;
++  return [
++    lambda * (0.8707 - 0.131979 * phi2 + phi4 * (-0.013791 + phi4 * (0.003971 * phi2 - 0.001529 * phi4))),
++    phi * (1.007226 + phi2 * (0.015085 + phi4 * (-0.044475 + 0.028874 * phi2 - 0.005916 * phi4)))
++  ];
++}
++
++naturalEarth1Raw.invert = function(x, y) {
++  var phi = y, i = 25, delta;
++  do {
++    var phi2 = phi * phi, phi4 = phi2 * phi2;
++    phi -= delta = (phi * (1.007226 + phi2 * (0.015085 + phi4 * (-0.044475 + 0.028874 * phi2 - 0.005916 * phi4))) - y) /
++        (1.007226 + phi2 * (0.015085 * 3 + phi4 * (-0.044475 * 7 + 0.028874 * 9 * phi2 - 0.005916 * 11 * phi4)));
++  } while (abs(delta) > epsilon$2 && --i > 0);
++  return [
++    x / (0.8707 + (phi2 = phi * phi) * (-0.131979 + phi2 * (-0.013791 + phi2 * phi2 * phi2 * (0.003971 - 0.001529 * phi2)))),
++    phi
++  ];
++};
++
++function naturalEarth1() {
++  return projection(naturalEarth1Raw)
++      .scale(175.295);
++}
++
++function orthographicRaw(x, y) {
++  return [cos$1(y) * sin$1(x), sin$1(y)];
++}
++
++orthographicRaw.invert = azimuthalInvert(asin);
++
++function orthographic() {
++  return projection(orthographicRaw)
++      .scale(249.5)
++      .clipAngle(90 + epsilon$2);
++}
++
++function stereographicRaw(x, y) {
++  var cy = cos$1(y), k = 1 + cos$1(x) * cy;
++  return [cy * sin$1(x) / k, sin$1(y) / k];
++}
++
++stereographicRaw.invert = azimuthalInvert(function(z) {
++  return 2 * atan(z);
++});
++
++function stereographic() {
++  return projection(stereographicRaw)
++      .scale(250)
++      .clipAngle(142);
++}
++
++function transverseMercatorRaw(lambda, phi) {
++  return [log(tan((halfPi$2 + phi) / 2)), -lambda];
++}
++
++transverseMercatorRaw.invert = function(x, y) {
++  return [-y, 2 * atan(exp(x)) - halfPi$2];
++};
++
++function transverseMercator() {
++  var m = mercatorProjection(transverseMercatorRaw),
++      center = m.center,
++      rotate = m.rotate;
++
++  m.center = function(_) {
++    return arguments.length ? center([-_[1], _[0]]) : (_ = center(), [_[1], -_[0]]);
++  };
++
++  m.rotate = function(_) {
++    return arguments.length ? rotate([_[0], _[1], _.length > 2 ? _[2] + 90 : 90]) : (_ = rotate(), [_[0], _[1], _[2] - 90]);
++  };
++
++  return rotate([0, 0, 90])
++      .scale(159.155);
++}
++
++function defaultSeparation(a, b) {
++  return a.parent === b.parent ? 1 : 2;
++}
++
++function meanX(children) {
++  return children.reduce(meanXReduce, 0) / children.length;
++}
++
++function meanXReduce(x, c) {
++  return x + c.x;
++}
++
++function maxY(children) {
++  return 1 + children.reduce(maxYReduce, 0);
++}
++
++function maxYReduce(y, c) {
++  return Math.max(y, c.y);
++}
++
++function leafLeft(node) {
++  var children;
++  while (children = node.children) node = children[0];
++  return node;
++}
++
++function leafRight(node) {
++  var children;
++  while (children = node.children) node = children[children.length - 1];
++  return node;
++}
++
++function cluster() {
++  var separation = defaultSeparation,
++      dx = 1,
++      dy = 1,
++      nodeSize = false;
++
++  function cluster(root) {
++    var previousNode,
++        x = 0;
++
++    // First walk, computing the initial x & y values.
++    root.eachAfter(function(node) {
++      var children = node.children;
++      if (children) {
++        node.x = meanX(children);
++        node.y = maxY(children);
++      } else {
++        node.x = previousNode ? x += separation(node, previousNode) : 0;
++        node.y = 0;
++        previousNode = node;
++      }
++    });
++
++    var left = leafLeft(root),
++        right = leafRight(root),
++        x0 = left.x - separation(left, right) / 2,
++        x1 = right.x + separation(right, left) / 2;
++
++    // Second walk, normalizing x & y to the desired size.
++    return root.eachAfter(nodeSize ? function(node) {
++      node.x = (node.x - root.x) * dx;
++      node.y = (root.y - node.y) * dy;
++    } : function(node) {
++      node.x = (node.x - x0) / (x1 - x0) * dx;
++      node.y = (1 - (root.y ? node.y / root.y : 1)) * dy;
++    });
++  }
++
++  cluster.separation = function(x) {
++    return arguments.length ? (separation = x, cluster) : separation;
++  };
++
++  cluster.size = function(x) {
++    return arguments.length ? (nodeSize = false, dx = +x[0], dy = +x[1], cluster) : (nodeSize ? null : [dx, dy]);
++  };
++
++  cluster.nodeSize = function(x) {
++    return arguments.length ? (nodeSize = true, dx = +x[0], dy = +x[1], cluster) : (nodeSize ? [dx, dy] : null);
++  };
++
++  return cluster;
++}
++
++function count(node) {
++  var sum = 0,
++      children = node.children,
++      i = children && children.length;
++  if (!i) sum = 1;
++  else while (--i >= 0) sum += children[i].value;
++  node.value = sum;
++}
++
++function node_count() {
++  return this.eachAfter(count);
++}
++
++function node_each(callback) {
++  var node = this, current, next = [node], children, i, n;
++  do {
++    current = next.reverse(), next = [];
++    while (node = current.pop()) {
++      callback(node), children = node.children;
++      if (children) for (i = 0, n = children.length; i < n; ++i) {
++        next.push(children[i]);
++      }
++    }
++  } while (next.length);
++  return this;
++}
++
++function node_eachBefore(callback) {
++  var node = this, nodes = [node], children, i;
++  while (node = nodes.pop()) {
++    callback(node), children = node.children;
++    if (children) for (i = children.length - 1; i >= 0; --i) {
++      nodes.push(children[i]);
++    }
++  }
++  return this;
++}
++
++function node_eachAfter(callback) {
++  var node = this, nodes = [node], next = [], children, i, n;
++  while (node = nodes.pop()) {
++    next.push(node), children = node.children;
++    if (children) for (i = 0, n = children.length; i < n; ++i) {
++      nodes.push(children[i]);
++    }
++  }
++  while (node = next.pop()) {
++    callback(node);
++  }
++  return this;
++}
++
++function node_sum(value) {
++  return this.eachAfter(function(node) {
++    var sum = +value(node.data) || 0,
++        children = node.children,
++        i = children && children.length;
++    while (--i >= 0) sum += children[i].value;
++    node.value = sum;
++  });
++}
++
++function node_sort(compare) {
++  return this.eachBefore(function(node) {
++    if (node.children) {
++      node.children.sort(compare);
++    }
++  });
++}
++
++function node_path(end) {
++  var start = this,
++      ancestor = leastCommonAncestor(start, end),
++      nodes = [start];
++  while (start !== ancestor) {
++    start = start.parent;
++    nodes.push(start);
++  }
++  var k = nodes.length;
++  while (end !== ancestor) {
++    nodes.splice(k, 0, end);
++    end = end.parent;
++  }
++  return nodes;
++}
++
++function leastCommonAncestor(a, b) {
++  if (a === b) return a;
++  var aNodes = a.ancestors(),
++      bNodes = b.ancestors(),
++      c = null;
++  a = aNodes.pop();
++  b = bNodes.pop();
++  while (a === b) {
++    c = a;
++    a = aNodes.pop();
++    b = bNodes.pop();
++  }
++  return c;
++}
++
++function node_ancestors() {
++  var node = this, nodes = [node];
++  while (node = node.parent) {
++    nodes.push(node);
++  }
++  return nodes;
++}
++
++function node_descendants() {
++  var nodes = [];
++  this.each(function(node) {
++    nodes.push(node);
++  });
++  return nodes;
++}
++
++function node_leaves() {
++  var leaves = [];
++  this.eachBefore(function(node) {
++    if (!node.children) {
++      leaves.push(node);
++    }
++  });
++  return leaves;
++}
++
++function node_links() {
++  var root = this, links = [];
++  root.each(function(node) {
++    if (node !== root) { // Don’t include the root’s parent, if any.
++      links.push({source: node.parent, target: node});
++    }
++  });
++  return links;
++}
++
++function hierarchy(data, children) {
++  var root = new Node(data),
++      valued = +data.value && (root.value = data.value),
++      node,
++      nodes = [root],
++      child,
++      childs,
++      i,
++      n;
++
++  if (children == null) children = defaultChildren;
++
++  while (node = nodes.pop()) {
++    if (valued) node.value = +node.data.value;
++    if ((childs = children(node.data)) && (n = childs.length)) {
++      node.children = new Array(n);
++      for (i = n - 1; i >= 0; --i) {
++        nodes.push(child = node.children[i] = new Node(childs[i]));
++        child.parent = node;
++        child.depth = node.depth + 1;
++      }
++    }
++  }
++
++  return root.eachBefore(computeHeight);
++}
++
++function node_copy() {
++  return hierarchy(this).eachBefore(copyData);
++}
++
++function defaultChildren(d) {
++  return d.children;
++}
++
++function copyData(node) {
++  node.data = node.data.data;
++}
++
++function computeHeight(node) {
++  var height = 0;
++  do node.height = height;
++  while ((node = node.parent) && (node.height < ++height));
++}
++
++function Node(data) {
++  this.data = data;
++  this.depth =
++  this.height = 0;
++  this.parent = null;
++}
++
++Node.prototype = hierarchy.prototype = {
++  constructor: Node,
++  count: node_count,
++  each: node_each,
++  eachAfter: node_eachAfter,
++  eachBefore: node_eachBefore,
++  sum: node_sum,
++  sort: node_sort,
++  path: node_path,
++  ancestors: node_ancestors,
++  descendants: node_descendants,
++  leaves: node_leaves,
++  links: node_links,
++  copy: node_copy
++};
++
++var slice$4 = Array.prototype.slice;
++
++function shuffle$1(array) {
++  var m = array.length,
++      t,
++      i;
++
++  while (m) {
++    i = Math.random() * m-- | 0;
++    t = array[m];
++    array[m] = array[i];
++    array[i] = t;
++  }
++
++  return array;
++}
++
++function enclose(circles) {
++  var i = 0, n = (circles = shuffle$1(slice$4.call(circles))).length, B = [], p, e;
++
++  while (i < n) {
++    p = circles[i];
++    if (e && enclosesWeak(e, p)) ++i;
++    else e = encloseBasis(B = extendBasis(B, p)), i = 0;
++  }
++
++  return e;
++}
++
++function extendBasis(B, p) {
++  var i, j;
++
++  if (enclosesWeakAll(p, B)) return [p];
++
++  // If we get here then B must have at least one element.
++  for (i = 0; i < B.length; ++i) {
++    if (enclosesNot(p, B[i])
++        && enclosesWeakAll(encloseBasis2(B[i], p), B)) {
++      return [B[i], p];
++    }
++  }
++
++  // If we get here then B must have at least two elements.
++  for (i = 0; i < B.length - 1; ++i) {
++    for (j = i + 1; j < B.length; ++j) {
++      if (enclosesNot(encloseBasis2(B[i], B[j]), p)
++          && enclosesNot(encloseBasis2(B[i], p), B[j])
++          && enclosesNot(encloseBasis2(B[j], p), B[i])
++          && enclosesWeakAll(encloseBasis3(B[i], B[j], p), B)) {
++        return [B[i], B[j], p];
++      }
++    }
++  }
++
++  // If we get here then something is very wrong.
++  throw new Error;
++}
++
++function enclosesNot(a, b) {
++  var dr = a.r - b.r, dx = b.x - a.x, dy = b.y - a.y;
++  return dr < 0 || dr * dr < dx * dx + dy * dy;
++}
++
++function enclosesWeak(a, b) {
++  var dr = a.r - b.r + 1e-6, dx = b.x - a.x, dy = b.y - a.y;
++  return dr > 0 && dr * dr > dx * dx + dy * dy;
++}
++
++function enclosesWeakAll(a, B) {
++  for (var i = 0; i < B.length; ++i) {
++    if (!enclosesWeak(a, B[i])) {
++      return false;
++    }
++  }
++  return true;
++}
++
++function encloseBasis(B) {
++  switch (B.length) {
++    case 1: return encloseBasis1(B[0]);
++    case 2: return encloseBasis2(B[0], B[1]);
++    case 3: return encloseBasis3(B[0], B[1], B[2]);
++  }
++}
++
++function encloseBasis1(a) {
++  return {
++    x: a.x,
++    y: a.y,
++    r: a.r
++  };
++}
++
++function encloseBasis2(a, b) {
++  var x1 = a.x, y1 = a.y, r1 = a.r,
++      x2 = b.x, y2 = b.y, r2 = b.r,
++      x21 = x2 - x1, y21 = y2 - y1, r21 = r2 - r1,
++      l = Math.sqrt(x21 * x21 + y21 * y21);
++  return {
++    x: (x1 + x2 + x21 / l * r21) / 2,
++    y: (y1 + y2 + y21 / l * r21) / 2,
++    r: (l + r1 + r2) / 2
++  };
++}
++
++function encloseBasis3(a, b, c) {
++  var x1 = a.x, y1 = a.y, r1 = a.r,
++      x2 = b.x, y2 = b.y, r2 = b.r,
++      x3 = c.x, y3 = c.y, r3 = c.r,
++      a2 = x1 - x2,
++      a3 = x1 - x3,
++      b2 = y1 - y2,
++      b3 = y1 - y3,
++      c2 = r2 - r1,
++      c3 = r3 - r1,
++      d1 = x1 * x1 + y1 * y1 - r1 * r1,
++      d2 = d1 - x2 * x2 - y2 * y2 + r2 * r2,
++      d3 = d1 - x3 * x3 - y3 * y3 + r3 * r3,
++      ab = a3 * b2 - a2 * b3,
++      xa = (b2 * d3 - b3 * d2) / (ab * 2) - x1,
++      xb = (b3 * c2 - b2 * c3) / ab,
++      ya = (a3 * d2 - a2 * d3) / (ab * 2) - y1,
++      yb = (a2 * c3 - a3 * c2) / ab,
++      A = xb * xb + yb * yb - 1,
++      B = 2 * (r1 + xa * xb + ya * yb),
++      C = xa * xa + ya * ya - r1 * r1,
++      r = -(A ? (B + Math.sqrt(B * B - 4 * A * C)) / (2 * A) : C / B);
++  return {
++    x: x1 + xa + xb * r,
++    y: y1 + ya + yb * r,
++    r: r
++  };
++}
++
++function place(b, a, c) {
++  var dx = b.x - a.x, x, a2,
++      dy = b.y - a.y, y, b2,
++      d2 = dx * dx + dy * dy;
++  if (d2) {
++    a2 = a.r + c.r, a2 *= a2;
++    b2 = b.r + c.r, b2 *= b2;
++    if (a2 > b2) {
++      x = (d2 + b2 - a2) / (2 * d2);
++      y = Math.sqrt(Math.max(0, b2 / d2 - x * x));
++      c.x = b.x - x * dx - y * dy;
++      c.y = b.y - x * dy + y * dx;
++    } else {
++      x = (d2 + a2 - b2) / (2 * d2);
++      y = Math.sqrt(Math.max(0, a2 / d2 - x * x));
++      c.x = a.x + x * dx - y * dy;
++      c.y = a.y + x * dy + y * dx;
++    }
++  } else {
++    c.x = a.x + c.r;
++    c.y = a.y;
++  }
++}
++
++function intersects(a, b) {
++  var dr = a.r + b.r - 1e-6, dx = b.x - a.x, dy = b.y - a.y;
++  return dr > 0 && dr * dr > dx * dx + dy * dy;
++}
++
++function score(node) {
++  var a = node._,
++      b = node.next._,
++      ab = a.r + b.r,
++      dx = (a.x * b.r + b.x * a.r) / ab,
++      dy = (a.y * b.r + b.y * a.r) / ab;
++  return dx * dx + dy * dy;
++}
++
++function Node$1(circle) {
++  this._ = circle;
++  this.next = null;
++  this.previous = null;
++}
++
++function packEnclose(circles) {
++  if (!(n = circles.length)) return 0;
++
++  var a, b, c, n, aa, ca, i, j, k, sj, sk;
++
++  // Place the first circle.
++  a = circles[0], a.x = 0, a.y = 0;
++  if (!(n > 1)) return a.r;
++
++  // Place the second circle.
++  b = circles[1], a.x = -b.r, b.x = a.r, b.y = 0;
++  if (!(n > 2)) return a.r + b.r;
++
++  // Place the third circle.
++  place(b, a, c = circles[2]);
++
++  // Initialize the front-chain using the first three circles a, b and c.
++  a = new Node$1(a), b = new Node$1(b), c = new Node$1(c);
++  a.next = c.previous = b;
++  b.next = a.previous = c;
++  c.next = b.previous = a;
++
++  // Attempt to place each remaining circle…
++  pack: for (i = 3; i < n; ++i) {
++    place(a._, b._, c = circles[i]), c = new Node$1(c);
++
++    // Find the closest intersecting circle on the front-chain, if any.
++    // “Closeness” is determined by linear distance along the front-chain.
++    // “Ahead” or “behind” is likewise determined by linear distance.
++    j = b.next, k = a.previous, sj = b._.r, sk = a._.r;
++    do {
++      if (sj <= sk) {
++        if (intersects(j._, c._)) {
++          b = j, a.next = b, b.previous = a, --i;
++          continue pack;
++        }
++        sj += j._.r, j = j.next;
++      } else {
++        if (intersects(k._, c._)) {
++          a = k, a.next = b, b.previous = a, --i;
++          continue pack;
++        }
++        sk += k._.r, k = k.previous;
++      }
++    } while (j !== k.next);
++
++    // Success! Insert the new circle c between a and b.
++    c.previous = a, c.next = b, a.next = b.previous = b = c;
++
++    // Compute the new closest circle pair to the centroid.
++    aa = score(a);
++    while ((c = c.next) !== b) {
++      if ((ca = score(c)) < aa) {
++        a = c, aa = ca;
++      }
++    }
++    b = a.next;
++  }
++
++  // Compute the enclosing circle of the front chain.
++  a = [b._], c = b; while ((c = c.next) !== b) a.push(c._); c = enclose(a);
++
++  // Translate the circles to put the enclosing circle around the origin.
++  for (i = 0; i < n; ++i) a = circles[i], a.x -= c.x, a.y -= c.y;
++
++  return c.r;
++}
++
++function siblings(circles) {
++  packEnclose(circles);
++  return circles;
++}
++
++function optional(f) {
++  return f == null ? null : required(f);
++}
++
++function required(f) {
++  if (typeof f !== "function") throw new Error;
++  return f;
++}
++
++function constantZero() {
++  return 0;
++}
++
++function constant$9(x) {
++  return function() {
++    return x;
++  };
++}
++
++function defaultRadius$1(d) {
++  return Math.sqrt(d.value);
++}
++
++function index$2() {
++  var radius = null,
++      dx = 1,
++      dy = 1,
++      padding = constantZero;
++
++  function pack(root) {
++    root.x = dx / 2, root.y = dy / 2;
++    if (radius) {
++      root.eachBefore(radiusLeaf(radius))
++          .eachAfter(packChildren(padding, 0.5))
++          .eachBefore(translateChild(1));
++    } else {
++      root.eachBefore(radiusLeaf(defaultRadius$1))
++          .eachAfter(packChildren(constantZero, 1))
++          .eachAfter(packChildren(padding, root.r / Math.min(dx, dy)))
++          .eachBefore(translateChild(Math.min(dx, dy) / (2 * root.r)));
++    }
++    return root;
++  }
++
++  pack.radius = function(x) {
++    return arguments.length ? (radius = optional(x), pack) : radius;
++  };
++
++  pack.size = function(x) {
++    return arguments.length ? (dx = +x[0], dy = +x[1], pack) : [dx, dy];
++  };
++
++  pack.padding = function(x) {
++    return arguments.length ? (padding = typeof x === "function" ? x : constant$9(+x), pack) : padding;
++  };
++
++  return pack;
++}
++
++function radiusLeaf(radius) {
++  return function(node) {
++    if (!node.children) {
++      node.r = Math.max(0, +radius(node) || 0);
++    }
++  };
++}
++
++function packChildren(padding, k) {
++  return function(node) {
++    if (children = node.children) {
++      var children,
++          i,
++          n = children.length,
++          r = padding(node) * k || 0,
++          e;
++
++      if (r) for (i = 0; i < n; ++i) children[i].r += r;
++      e = packEnclose(children);
++      if (r) for (i = 0; i < n; ++i) children[i].r -= r;
++      node.r = e + r;
++    }
++  };
++}
++
++function translateChild(k) {
++  return function(node) {
++    var parent = node.parent;
++    node.r *= k;
++    if (parent) {
++      node.x = parent.x + k * node.x;
++      node.y = parent.y + k * node.y;
++    }
++  };
++}
++
++function roundNode(node) {
++  node.x0 = Math.round(node.x0);
++  node.y0 = Math.round(node.y0);
++  node.x1 = Math.round(node.x1);
++  node.y1 = Math.round(node.y1);
++}
++
++function treemapDice(parent, x0, y0, x1, y1) {
++  var nodes = parent.children,
++      node,
++      i = -1,
++      n = nodes.length,
++      k = parent.value && (x1 - x0) / parent.value;
++
++  while (++i < n) {
++    node = nodes[i], node.y0 = y0, node.y1 = y1;
++    node.x0 = x0, node.x1 = x0 += node.value * k;
++  }
++}
++
++function partition() {
++  var dx = 1,
++      dy = 1,
++      padding = 0,
++      round = false;
++
++  function partition(root) {
++    var n = root.height + 1;
++    root.x0 =
++    root.y0 = padding;
++    root.x1 = dx;
++    root.y1 = dy / n;
++    root.eachBefore(positionNode(dy, n));
++    if (round) root.eachBefore(roundNode);
++    return root;
++  }
++
++  function positionNode(dy, n) {
++    return function(node) {
++      if (node.children) {
++        treemapDice(node, node.x0, dy * (node.depth + 1) / n, node.x1, dy * (node.depth + 2) / n);
++      }
++      var x0 = node.x0,
++          y0 = node.y0,
++          x1 = node.x1 - padding,
++          y1 = node.y1 - padding;
++      if (x1 < x0) x0 = x1 = (x0 + x1) / 2;
++      if (y1 < y0) y0 = y1 = (y0 + y1) / 2;
++      node.x0 = x0;
++      node.y0 = y0;
++      node.x1 = x1;
++      node.y1 = y1;
++    };
++  }
++
++  partition.round = function(x) {
++    return arguments.length ? (round = !!x, partition) : round;
++  };
++
++  partition.size = function(x) {
++    return arguments.length ? (dx = +x[0], dy = +x[1], partition) : [dx, dy];
++  };
++
++  partition.padding = function(x) {
++    return arguments.length ? (padding = +x, partition) : padding;
++  };
++
++  return partition;
++}
++
++var keyPrefix$1 = "$", // Protect against keys like “__proto__”.
++    preroot = {depth: -1},
++    ambiguous = {};
++
++function defaultId(d) {
++  return d.id;
++}
++
++function defaultParentId(d) {
++  return d.parentId;
++}
++
++function stratify() {
++  var id = defaultId,
++      parentId = defaultParentId;
++
++  function stratify(data) {
++    var d,
++        i,
++        n = data.length,
++        root,
++        parent,
++        node,
++        nodes = new Array(n),
++        nodeId,
++        nodeKey,
++        nodeByKey = {};
++
++    for (i = 0; i < n; ++i) {
++      d = data[i], node = nodes[i] = new Node(d);
++      if ((nodeId = id(d, i, data)) != null && (nodeId += "")) {
++        nodeKey = keyPrefix$1 + (node.id = nodeId);
++        nodeByKey[nodeKey] = nodeKey in nodeByKey ? ambiguous : node;
++      }
++    }
++
++    for (i = 0; i < n; ++i) {
++      node = nodes[i], nodeId = parentId(data[i], i, data);
++      if (nodeId == null || !(nodeId += "")) {
++        if (root) throw new Error("multiple roots");
++        root = node;
++      } else {
++        parent = nodeByKey[keyPrefix$1 + nodeId];
++        if (!parent) throw new Error("missing: " + nodeId);
++        if (parent === ambiguous) throw new Error("ambiguous: " + nodeId);
++        if (parent.children) parent.children.push(node);
++        else parent.children = [node];
++        node.parent = parent;
++      }
++    }
++
++    if (!root) throw new Error("no root");
++    root.parent = preroot;
++    root.eachBefore(function(node) { node.depth = node.parent.depth + 1; --n; }).eachBefore(computeHeight);
++    root.parent = null;
++    if (n > 0) throw new Error("cycle");
++
++    return root;
++  }
++
++  stratify.id = function(x) {
++    return arguments.length ? (id = required(x), stratify) : id;
++  };
++
++  stratify.parentId = function(x) {
++    return arguments.length ? (parentId = required(x), stratify) : parentId;
++  };
++
++  return stratify;
++}
++
++function defaultSeparation$1(a, b) {
++  return a.parent === b.parent ? 1 : 2;
++}
++
++// function radialSeparation(a, b) {
++//   return (a.parent === b.parent ? 1 : 2) / a.depth;
++// }
++
++// This function is used to traverse the left contour of a subtree (or
++// subforest). It returns the successor of v on this contour. This successor is
++// either given by the leftmost child of v or by the thread of v. The function
++// returns null if and only if v is on the highest level of its subtree.
++function nextLeft(v) {
++  var children = v.children;
++  return children ? children[0] : v.t;
++}
++
++// This function works analogously to nextLeft.
++function nextRight(v) {
++  var children = v.children;
++  return children ? children[children.length - 1] : v.t;
++}
++
++// Shifts the current subtree rooted at w+. This is done by increasing
++// prelim(w+) and mod(w+) by shift.
++function moveSubtree(wm, wp, shift) {
++  var change = shift / (wp.i - wm.i);
++  wp.c -= change;
++  wp.s += shift;
++  wm.c += change;
++  wp.z += shift;
++  wp.m += shift;
++}
++
++// All other shifts, applied to the smaller subtrees between w- and w+, are
++// performed by this function. To prepare the shifts, we have to adjust
++// change(w+), shift(w+), and change(w-).
++function executeShifts(v) {
++  var shift = 0,
++      change = 0,
++      children = v.children,
++      i = children.length,
++      w;
++  while (--i >= 0) {
++    w = children[i];
++    w.z += shift;
++    w.m += shift;
++    shift += w.s + (change += w.c);
++  }
++}
++
++// If vi-’s ancestor is a sibling of v, returns vi-’s ancestor. Otherwise,
++// returns the specified (default) ancestor.
++function nextAncestor(vim, v, ancestor) {
++  return vim.a.parent === v.parent ? vim.a : ancestor;
++}
++
++function TreeNode(node, i) {
++  this._ = node;
++  this.parent = null;
++  this.children = null;
++  this.A = null; // default ancestor
++  this.a = this; // ancestor
++  this.z = 0; // prelim
++  this.m = 0; // mod
++  this.c = 0; // change
++  this.s = 0; // shift
++  this.t = null; // thread
++  this.i = i; // number
++}
++
++TreeNode.prototype = Object.create(Node.prototype);
++
++function treeRoot(root) {
++  var tree = new TreeNode(root, 0),
++      node,
++      nodes = [tree],
++      child,
++      children,
++      i,
++      n;
++
++  while (node = nodes.pop()) {
++    if (children = node._.children) {
++      node.children = new Array(n = children.length);
++      for (i = n - 1; i >= 0; --i) {
++        nodes.push(child = node.children[i] = new TreeNode(children[i], i));
++        child.parent = node;
++      }
++    }
++  }
++
++  (tree.parent = new TreeNode(null, 0)).children = [tree];
++  return tree;
++}
++
++// Node-link tree diagram using the Reingold-Tilford "tidy" algorithm
++function tree() {
++  var separation = defaultSeparation$1,
++      dx = 1,
++      dy = 1,
++      nodeSize = null;
++
++  function tree(root) {
++    var t = treeRoot(root);
++
++    // Compute the layout using Buchheim et al.’s algorithm.
++    t.eachAfter(firstWalk), t.parent.m = -t.z;
++    t.eachBefore(secondWalk);
++
++    // If a fixed node size is specified, scale x and y.
++    if (nodeSize) root.eachBefore(sizeNode);
++
++    // If a fixed tree size is specified, scale x and y based on the extent.
++    // Compute the left-most, right-most, and depth-most nodes for extents.
++    else {
++      var left = root,
++          right = root,
++          bottom = root;
++      root.eachBefore(function(node) {
++        if (node.x < left.x) left = node;
++        if (node.x > right.x) right = node;
++        if (node.depth > bottom.depth) bottom = node;
++      });
++      var s = left === right ? 1 : separation(left, right) / 2,
++          tx = s - left.x,
++          kx = dx / (right.x + s + tx),
++          ky = dy / (bottom.depth || 1);
++      root.eachBefore(function(node) {
++        node.x = (node.x + tx) * kx;
++        node.y = node.depth * ky;
++      });
++    }
++
++    return root;
++  }
++
++  // Computes a preliminary x-coordinate for v. Before that, FIRST WALK is
++  // applied recursively to the children of v, as well as the function
++  // APPORTION. After spacing out the children by calling EXECUTE SHIFTS, the
++  // node v is placed to the midpoint of its outermost children.
++  function firstWalk(v) {
++    var children = v.children,
++        siblings = v.parent.children,
++        w = v.i ? siblings[v.i - 1] : null;
++    if (children) {
++      executeShifts(v);
++      var midpoint = (children[0].z + children[children.length - 1].z) / 2;
++      if (w) {
++        v.z = w.z + separation(v._, w._);
++        v.m = v.z - midpoint;
++      } else {
++        v.z = midpoint;
++      }
++    } else if (w) {
++      v.z = w.z + separation(v._, w._);
++    }
++    v.parent.A = apportion(v, w, v.parent.A || siblings[0]);
++  }
++
++  // Computes all real x-coordinates by summing up the modifiers recursively.
++  function secondWalk(v) {
++    v._.x = v.z + v.parent.m;
++    v.m += v.parent.m;
++  }
++
++  // The core of the algorithm. Here, a new subtree is combined with the
++  // previous subtrees. Threads are used to traverse the inside and outside
++  // contours of the left and right subtree up to the highest common level. The
++  // vertices used for the traversals are vi+, vi-, vo-, and vo+, where the
++  // superscript o means outside and i means inside, the subscript - means left
++  // subtree and + means right subtree. For summing up the modifiers along the
++  // contour, we use respective variables si+, si-, so-, and so+. Whenever two
++  // nodes of the inside contours conflict, we compute the left one of the
++  // greatest uncommon ancestors using the function ANCESTOR and call MOVE
++  // SUBTREE to shift the subtree and prepare the shifts of smaller subtrees.
++  // Finally, we add a new thread (if necessary).
++  function apportion(v, w, ancestor) {
++    if (w) {
++      var vip = v,
++          vop = v,
++          vim = w,
++          vom = vip.parent.children[0],
++          sip = vip.m,
++          sop = vop.m,
++          sim = vim.m,
++          som = vom.m,
++          shift;
++      while (vim = nextRight(vim), vip = nextLeft(vip), vim && vip) {
++        vom = nextLeft(vom);
++        vop = nextRight(vop);
++        vop.a = v;
++        shift = vim.z + sim - vip.z - sip + separation(vim._, vip._);
++        if (shift > 0) {
++          moveSubtree(nextAncestor(vim, v, ancestor), v, shift);
++          sip += shift;
++          sop += shift;
++        }
++        sim += vim.m;
++        sip += vip.m;
++        som += vom.m;
++        sop += vop.m;
++      }
++      if (vim && !nextRight(vop)) {
++        vop.t = vim;
++        vop.m += sim - sop;
++      }
++      if (vip && !nextLeft(vom)) {
++        vom.t = vip;
++        vom.m += sip - som;
++        ancestor = v;
++      }
++    }
++    return ancestor;
++  }
++
++  function sizeNode(node) {
++    node.x *= dx;
++    node.y = node.depth * dy;
++  }
++
++  tree.separation = function(x) {
++    return arguments.length ? (separation = x, tree) : separation;
++  };
++
++  tree.size = function(x) {
++    return arguments.length ? (nodeSize = false, dx = +x[0], dy = +x[1], tree) : (nodeSize ? null : [dx, dy]);
++  };
++
++  tree.nodeSize = function(x) {
++    return arguments.length ? (nodeSize = true, dx = +x[0], dy = +x[1], tree) : (nodeSize ? [dx, dy] : null);
++  };
++
++  return tree;
++}
++
++function treemapSlice(parent, x0, y0, x1, y1) {
++  var nodes = parent.children,
++      node,
++      i = -1,
++      n = nodes.length,
++      k = parent.value && (y1 - y0) / parent.value;
++
++  while (++i < n) {
++    node = nodes[i], node.x0 = x0, node.x1 = x1;
++    node.y0 = y0, node.y1 = y0 += node.value * k;
++  }
++}
++
++var phi = (1 + Math.sqrt(5)) / 2;
++
++function squarifyRatio(ratio, parent, x0, y0, x1, y1) {
++  var rows = [],
++      nodes = parent.children,
++      row,
++      nodeValue,
++      i0 = 0,
++      i1 = 0,
++      n = nodes.length,
++      dx, dy,
++      value = parent.value,
++      sumValue,
++      minValue,
++      maxValue,
++      newRatio,
++      minRatio,
++      alpha,
++      beta;
++
++  while (i0 < n) {
++    dx = x1 - x0, dy = y1 - y0;
++
++    // Find the next non-empty node.
++    do sumValue = nodes[i1++].value; while (!sumValue && i1 < n);
++    minValue = maxValue = sumValue;
++    alpha = Math.max(dy / dx, dx / dy) / (value * ratio);
++    beta = sumValue * sumValue * alpha;
++    minRatio = Math.max(maxValue / beta, beta / minValue);
++
++    // Keep adding nodes while the aspect ratio maintains or improves.
++    for (; i1 < n; ++i1) {
++      sumValue += nodeValue = nodes[i1].value;
++      if (nodeValue < minValue) minValue = nodeValue;
++      if (nodeValue > maxValue) maxValue = nodeValue;
++      beta = sumValue * sumValue * alpha;
++      newRatio = Math.max(maxValue / beta, beta / minValue);
++      if (newRatio > minRatio) { sumValue -= nodeValue; break; }
++      minRatio = newRatio;
++    }
++
++    // Position and record the row orientation.
++    rows.push(row = {value: sumValue, dice: dx < dy, children: nodes.slice(i0, i1)});
++    if (row.dice) treemapDice(row, x0, y0, x1, value ? y0 += dy * sumValue / value : y1);
++    else treemapSlice(row, x0, y0, value ? x0 += dx * sumValue / value : x1, y1);
++    value -= sumValue, i0 = i1;
++  }
++
++  return rows;
++}
++
++var squarify = (function custom(ratio) {
++
++  function squarify(parent, x0, y0, x1, y1) {
++    squarifyRatio(ratio, parent, x0, y0, x1, y1);
++  }
++
++  squarify.ratio = function(x) {
++    return custom((x = +x) > 1 ? x : 1);
++  };
++
++  return squarify;
++})(phi);
++
++function index$3() {
++  var tile = squarify,
++      round = false,
++      dx = 1,
++      dy = 1,
++      paddingStack = [0],
++      paddingInner = constantZero,
++      paddingTop = constantZero,
++      paddingRight = constantZero,
++      paddingBottom = constantZero,
++      paddingLeft = constantZero;
++
++  function treemap(root) {
++    root.x0 =
++    root.y0 = 0;
++    root.x1 = dx;
++    root.y1 = dy;
++    root.eachBefore(positionNode);
++    paddingStack = [0];
++    if (round) root.eachBefore(roundNode);
++    return root;
++  }
++
++  function positionNode(node) {
++    var p = paddingStack[node.depth],
++        x0 = node.x0 + p,
++        y0 = node.y0 + p,
++        x1 = node.x1 - p,
++        y1 = node.y1 - p;
++    if (x1 < x0) x0 = x1 = (x0 + x1) / 2;
++    if (y1 < y0) y0 = y1 = (y0 + y1) / 2;
++    node.x0 = x0;
++    node.y0 = y0;
++    node.x1 = x1;
++    node.y1 = y1;
++    if (node.children) {
++      p = paddingStack[node.depth + 1] = paddingInner(node) / 2;
++      x0 += paddingLeft(node) - p;
++      y0 += paddingTop(node) - p;
++      x1 -= paddingRight(node) - p;
++      y1 -= paddingBottom(node) - p;
++      if (x1 < x0) x0 = x1 = (x0 + x1) / 2;
++      if (y1 < y0) y0 = y1 = (y0 + y1) / 2;
++      tile(node, x0, y0, x1, y1);
++    }
++  }
++
++  treemap.round = function(x) {
++    return arguments.length ? (round = !!x, treemap) : round;
++  };
++
++  treemap.size = function(x) {
++    return arguments.length ? (dx = +x[0], dy = +x[1], treemap) : [dx, dy];
++  };
++
++  treemap.tile = function(x) {
++    return arguments.length ? (tile = required(x), treemap) : tile;
++  };
++
++  treemap.padding = function(x) {
++    return arguments.length ? treemap.paddingInner(x).paddingOuter(x) : treemap.paddingInner();
++  };
++
++  treemap.paddingInner = function(x) {
++    return arguments.length ? (paddingInner = typeof x === "function" ? x : constant$9(+x), treemap) : paddingInner;
++  };
++
++  treemap.paddingOuter = function(x) {
++    return arguments.length ? treemap.paddingTop(x).paddingRight(x).paddingBottom(x).paddingLeft(x) : treemap.paddingTop();
++  };
++
++  treemap.paddingTop = function(x) {
++    return arguments.length ? (paddingTop = typeof x === "function" ? x : constant$9(+x), treemap) : paddingTop;
++  };
++
++  treemap.paddingRight = function(x) {
++    return arguments.length ? (paddingRight = typeof x === "function" ? x : constant$9(+x), treemap) : paddingRight;
++  };
++
++  treemap.paddingBottom = function(x) {
++    return arguments.length ? (paddingBottom = typeof x === "function" ? x : constant$9(+x), treemap) : paddingBottom;
++  };
++
++  treemap.paddingLeft = function(x) {
++    return arguments.length ? (paddingLeft = typeof x === "function" ? x : constant$9(+x), treemap) : paddingLeft;
++  };
++
++  return treemap;
++}
++
++function binary(parent, x0, y0, x1, y1) {
++  var nodes = parent.children,
++      i, n = nodes.length,
++      sum, sums = new Array(n + 1);
++
++  for (sums[0] = sum = i = 0; i < n; ++i) {
++    sums[i + 1] = sum += nodes[i].value;
++  }
++
++  partition(0, n, parent.value, x0, y0, x1, y1);
++
++  function partition(i, j, value, x0, y0, x1, y1) {
++    if (i >= j - 1) {
++      var node = nodes[i];
++      node.x0 = x0, node.y0 = y0;
++      node.x1 = x1, node.y1 = y1;
++      return;
++    }
++
++    var valueOffset = sums[i],
++        valueTarget = (value / 2) + valueOffset,
++        k = i + 1,
++        hi = j - 1;
++
++    while (k < hi) {
++      var mid = k + hi >>> 1;
++      if (sums[mid] < valueTarget) k = mid + 1;
++      else hi = mid;
++    }
++
++    if ((valueTarget - sums[k - 1]) < (sums[k] - valueTarget) && i + 1 < k) --k;
++
++    var valueLeft = sums[k] - valueOffset,
++        valueRight = value - valueLeft;
++
++    if ((x1 - x0) > (y1 - y0)) {
++      var xk = (x0 * valueRight + x1 * valueLeft) / value;
++      partition(i, k, valueLeft, x0, y0, xk, y1);
++      partition(k, j, valueRight, xk, y0, x1, y1);
++    } else {
++      var yk = (y0 * valueRight + y1 * valueLeft) / value;
++      partition(i, k, valueLeft, x0, y0, x1, yk);
++      partition(k, j, valueRight, x0, yk, x1, y1);
++    }
++  }
++}
++
++function sliceDice(parent, x0, y0, x1, y1) {
++  (parent.depth & 1 ? treemapSlice : treemapDice)(parent, x0, y0, x1, y1);
++}
++
++var resquarify = (function custom(ratio) {
++
++  function resquarify(parent, x0, y0, x1, y1) {
++    if ((rows = parent._squarify) && (rows.ratio === ratio)) {
++      var rows,
++          row,
++          nodes,
++          i,
++          j = -1,
++          n,
++          m = rows.length,
++          value = parent.value;
++
++      while (++j < m) {
++        row = rows[j], nodes = row.children;
++        for (i = row.value = 0, n = nodes.length; i < n; ++i) row.value += nodes[i].value;
++        if (row.dice) treemapDice(row, x0, y0, x1, y0 += (y1 - y0) * row.value / value);
++        else treemapSlice(row, x0, y0, x0 += (x1 - x0) * row.value / value, y1);
++        value -= row.value;
++      }
++    } else {
++      parent._squarify = rows = squarifyRatio(ratio, parent, x0, y0, x1, y1);
++      rows.ratio = ratio;
++    }
++  }
++
++  resquarify.ratio = function(x) {
++    return custom((x = +x) > 1 ? x : 1);
++  };
++
++  return resquarify;
++})(phi);
++
++function area$2(polygon) {
++  var i = -1,
++      n = polygon.length,
++      a,
++      b = polygon[n - 1],
++      area = 0;
++
++  while (++i < n) {
++    a = b;
++    b = polygon[i];
++    area += a[1] * b[0] - a[0] * b[1];
++  }
++
++  return area / 2;
++}
++
++function centroid$1(polygon) {
++  var i = -1,
++      n = polygon.length,
++      x = 0,
++      y = 0,
++      a,
++      b = polygon[n - 1],
++      c,
++      k = 0;
++
++  while (++i < n) {
++    a = b;
++    b = polygon[i];
++    k += c = a[0] * b[1] - b[0] * a[1];
++    x += (a[0] + b[0]) * c;
++    y += (a[1] + b[1]) * c;
++  }
++
++  return k *= 3, [x / k, y / k];
++}
++
++// Returns the 2D cross product of AB and AC vectors, i.e., the z-component of
++// the 3D cross product in a quadrant I Cartesian coordinate system (+x is
++// right, +y is up). Returns a positive value if ABC is counter-clockwise,
++// negative if clockwise, and zero if the points are collinear.
++function cross$1(a, b, c) {
++  return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
++}
++
++function lexicographicOrder(a, b) {
++  return a[0] - b[0] || a[1] - b[1];
++}
++
++// Computes the upper convex hull per the monotone chain algorithm.
++// Assumes points.length >= 3, is sorted by x, unique in y.
++// Returns an array of indices into points in left-to-right order.
++function computeUpperHullIndexes(points) {
++  var n = points.length,
++      indexes = [0, 1],
++      size = 2;
++
++  for (var i = 2; i < n; ++i) {
++    while (size > 1 && cross$1(points[indexes[size - 2]], points[indexes[size - 1]], points[i]) <= 0) --size;
++    indexes[size++] = i;
++  }
++
++  return indexes.slice(0, size); // remove popped points
++}
++
++function hull(points) {
++  if ((n = points.length) < 3) return null;
++
++  var i,
++      n,
++      sortedPoints = new Array(n),
++      flippedPoints = new Array(n);
++
++  for (i = 0; i < n; ++i) sortedPoints[i] = [+points[i][0], +points[i][1], i];
++  sortedPoints.sort(lexicographicOrder);
++  for (i = 0; i < n; ++i) flippedPoints[i] = [sortedPoints[i][0], -sortedPoints[i][1]];
++
++  var upperIndexes = computeUpperHullIndexes(sortedPoints),
++      lowerIndexes = computeUpperHullIndexes(flippedPoints);
++
++  // Construct the hull polygon, removing possible duplicate endpoints.
++  var skipLeft = lowerIndexes[0] === upperIndexes[0],
++      skipRight = lowerIndexes[lowerIndexes.length - 1] === upperIndexes[upperIndexes.length - 1],
++      hull = [];
++
++  // Add upper hull in right-to-l order.
++  // Then add lower hull in left-to-right order.
++  for (i = upperIndexes.length - 1; i >= 0; --i) hull.push(points[sortedPoints[upperIndexes[i]][2]]);
++  for (i = +skipLeft; i < lowerIndexes.length - skipRight; ++i) hull.push(points[sortedPoints[lowerIndexes[i]][2]]);
++
++  return hull;
++}
++
++function contains$2(polygon, point) {
++  var n = polygon.length,
++      p = polygon[n - 1],
++      x = point[0], y = point[1],
++      x0 = p[0], y0 = p[1],
++      x1, y1,
++      inside = false;
++
++  for (var i = 0; i < n; ++i) {
++    p = polygon[i], x1 = p[0], y1 = p[1];
++    if (((y1 > y) !== (y0 > y)) && (x < (x0 - x1) * (y - y1) / (y0 - y1) + x1)) inside = !inside;
++    x0 = x1, y0 = y1;
++  }
++
++  return inside;
++}
++
++function length$2(polygon) {
++  var i = -1,
++      n = polygon.length,
++      b = polygon[n - 1],
++      xa,
++      ya,
++      xb = b[0],
++      yb = b[1],
++      perimeter = 0;
++
++  while (++i < n) {
++    xa = xb;
++    ya = yb;
++    b = polygon[i];
++    xb = b[0];
++    yb = b[1];
++    xa -= xb;
++    ya -= yb;
++    perimeter += Math.sqrt(xa * xa + ya * ya);
++  }
++
++  return perimeter;
++}
++
++function defaultSource$1() {
++  return Math.random();
++}
++
++var uniform = (function sourceRandomUniform(source) {
++  function randomUniform(min, max) {
++    min = min == null ? 0 : +min;
++    max = max == null ? 1 : +max;
++    if (arguments.length === 1) max = min, min = 0;
++    else max -= min;
++    return function() {
++      return source() * max + min;
++    };
++  }
++
++  randomUniform.source = sourceRandomUniform;
++
++  return randomUniform;
++})(defaultSource$1);
++
++var normal = (function sourceRandomNormal(source) {
++  function randomNormal(mu, sigma) {
++    var x, r;
++    mu = mu == null ? 0 : +mu;
++    sigma = sigma == null ? 1 : +sigma;
++    return function() {
++      var y;
++
++      // If available, use the second previously-generated uniform random.
++      if (x != null) y = x, x = null;
++
++      // Otherwise, generate a new x and y.
++      else do {
++        x = source() * 2 - 1;
++        y = source() * 2 - 1;
++        r = x * x + y * y;
++      } while (!r || r > 1);
++
++      return mu + sigma * y * Math.sqrt(-2 * Math.log(r) / r);
++    };
++  }
++
++  randomNormal.source = sourceRandomNormal;
++
++  return randomNormal;
++})(defaultSource$1);
++
++var logNormal = (function sourceRandomLogNormal(source) {
++  function randomLogNormal() {
++    var randomNormal = normal.source(source).apply(this, arguments);
++    return function() {
++      return Math.exp(randomNormal());
++    };
++  }
++
++  randomLogNormal.source = sourceRandomLogNormal;
++
++  return randomLogNormal;
++})(defaultSource$1);
++
++var irwinHall = (function sourceRandomIrwinHall(source) {
++  function randomIrwinHall(n) {
++    return function() {
++      for (var sum = 0, i = 0; i < n; ++i) sum += source();
++      return sum;
++    };
++  }
++
++  randomIrwinHall.source = sourceRandomIrwinHall;
++
++  return randomIrwinHall;
++})(defaultSource$1);
++
++var bates = (function sourceRandomBates(source) {
++  function randomBates(n) {
++    var randomIrwinHall = irwinHall.source(source)(n);
++    return function() {
++      return randomIrwinHall() / n;
++    };
++  }
++
++  randomBates.source = sourceRandomBates;
++
++  return randomBates;
++})(defaultSource$1);
++
++var exponential$1 = (function sourceRandomExponential(source) {
++  function randomExponential(lambda) {
++    return function() {
++      return -Math.log(1 - source()) / lambda;
++    };
++  }
++
++  randomExponential.source = sourceRandomExponential;
++
++  return randomExponential;
++})(defaultSource$1);
++
++var array$3 = Array.prototype;
++
++var map$2 = array$3.map;
++var slice$5 = array$3.slice;
++
++var implicit = {name: "implicit"};
++
++function ordinal(range) {
++  var index = map$1(),
++      domain = [],
++      unknown = implicit;
++
++  range = range == null ? [] : slice$5.call(range);
++
++  function scale(d) {
++    var key = d + "", i = index.get(key);
++    if (!i) {
++      if (unknown !== implicit) return unknown;
++      index.set(key, i = domain.push(d));
++    }
++    return range[(i - 1) % range.length];
++  }
++
++  scale.domain = function(_) {
++    if (!arguments.length) return domain.slice();
++    domain = [], index = map$1();
++    var i = -1, n = _.length, d, key;
++    while (++i < n) if (!index.has(key = (d = _[i]) + "")) index.set(key, domain.push(d));
++    return scale;
++  };
++
++  scale.range = function(_) {
++    return arguments.length ? (range = slice$5.call(_), scale) : range.slice();
++  };
++
++  scale.unknown = function(_) {
++    return arguments.length ? (unknown = _, scale) : unknown;
++  };
++
++  scale.copy = function() {
++    return ordinal()
++        .domain(domain)
++        .range(range)
++        .unknown(unknown);
++  };
++
++  return scale;
++}
++
++function band() {
++  var scale = ordinal().unknown(undefined),
++      domain = scale.domain,
++      ordinalRange = scale.range,
++      range$$1 = [0, 1],
++      step,
++      bandwidth,
++      round = false,
++      paddingInner = 0,
++      paddingOuter = 0,
++      align = 0.5;
++
++  delete scale.unknown;
++
++  function rescale() {
++    var n = domain().length,
++        reverse = range$$1[1] < range$$1[0],
++        start = range$$1[reverse - 0],
++        stop = range$$1[1 - reverse];
++    step = (stop - start) / Math.max(1, n - paddingInner + paddingOuter * 2);
++    if (round) step = Math.floor(step);
++    start += (stop - start - step * (n - paddingInner)) * align;
++    bandwidth = step * (1 - paddingInner);
++    if (round) start = Math.round(start), bandwidth = Math.round(bandwidth);
++    var values = sequence(n).map(function(i) { return start + step * i; });
++    return ordinalRange(reverse ? values.reverse() : values);
++  }
++
++  scale.domain = function(_) {
++    return arguments.length ? (domain(_), rescale()) : domain();
++  };
++
++  scale.range = function(_) {
++    return arguments.length ? (range$$1 = [+_[0], +_[1]], rescale()) : range$$1.slice();
++  };
++
++  scale.rangeRound = function(_) {
++    return range$$1 = [+_[0], +_[1]], round = true, rescale();
++  };
++
++  scale.bandwidth = function() {
++    return bandwidth;
++  };
++
++  scale.step = function() {
++    return step;
++  };
++
++  scale.round = function(_) {
++    return arguments.length ? (round = !!_, rescale()) : round;
++  };
++
++  scale.padding = function(_) {
++    return arguments.length ? (paddingInner = paddingOuter = Math.max(0, Math.min(1, _)), rescale()) : paddingInner;
++  };
++
++  scale.paddingInner = function(_) {
++    return arguments.length ? (paddingInner = Math.max(0, Math.min(1, _)), rescale()) : paddingInner;
++  };
++
++  scale.paddingOuter = function(_) {
++    return arguments.length ? (paddingOuter = Math.max(0, Math.min(1, _)), rescale()) : paddingOuter;
++  };
++
++  scale.align = function(_) {
++    return arguments.length ? (align = Math.max(0, Math.min(1, _)), rescale()) : align;
++  };
++
++  scale.copy = function() {
++    return band()
++        .domain(domain())
++        .range(range$$1)
++        .round(round)
++        .paddingInner(paddingInner)
++        .paddingOuter(paddingOuter)
++        .align(align);
++  };
++
++  return rescale();
++}
++
++function pointish(scale) {
++  var copy = scale.copy;
++
++  scale.padding = scale.paddingOuter;
++  delete scale.paddingInner;
++  delete scale.paddingOuter;
++
++  scale.copy = function() {
++    return pointish(copy());
++  };
++
++  return scale;
++}
++
++function point$1() {
++  return pointish(band().paddingInner(1));
++}
++
++function constant$10(x) {
++  return function() {
++    return x;
++  };
++}
++
++function number$2(x) {
++  return +x;
++}
++
++var unit = [0, 1];
++
++function deinterpolateLinear(a, b) {
++  return (b -= (a = +a))
++      ? function(x) { return (x - a) / b; }
++      : constant$10(b);
++}
++
++function deinterpolateClamp(deinterpolate) {
++  return function(a, b) {
++    var d = deinterpolate(a = +a, b = +b);
++    return function(x) { return x <= a ? 0 : x >= b ? 1 : d(x); };
++  };
++}
++
++function reinterpolateClamp(reinterpolate$$1) {
++  return function(a, b) {
++    var r = reinterpolate$$1(a = +a, b = +b);
++    return function(t) { return t <= 0 ? a : t >= 1 ? b : r(t); };
++  };
++}
++
++function bimap(domain, range, deinterpolate, reinterpolate$$1) {
++  var d0 = domain[0], d1 = domain[1], r0 = range[0], r1 = range[1];
++  if (d1 < d0) d0 = deinterpolate(d1, d0), r0 = reinterpolate$$1(r1, r0);
++  else d0 = deinterpolate(d0, d1), r0 = reinterpolate$$1(r0, r1);
++  return function(x) { return r0(d0(x)); };
++}
++
++function polymap(domain, range, deinterpolate, reinterpolate$$1) {
++  var j = Math.min(domain.length, range.length) - 1,
++      d = new Array(j),
++      r = new Array(j),
++      i = -1;
++
++  // Reverse descending domains.
++  if (domain[j] < domain[0]) {
++    domain = domain.slice().reverse();
++    range = range.slice().reverse();
++  }
++
++  while (++i < j) {
++    d[i] = deinterpolate(domain[i], domain[i + 1]);
++    r[i] = reinterpolate$$1(range[i], range[i + 1]);
++  }
++
++  return function(x) {
++    var i = bisectRight(domain, x, 1, j) - 1;
++    return r[i](d[i](x));
++  };
++}
++
++function copy(source, target) {
++  return target
++      .domain(source.domain())
++      .range(source.range())
++      .interpolate(source.interpolate())
++      .clamp(source.clamp());
++}
++
++// deinterpolate(a, b)(x) takes a domain value x in [a,b] and returns the corresponding parameter t in [0,1].
++// reinterpolate(a, b)(t) takes a parameter t in [0,1] and returns the corresponding domain value x in [a,b].
++function continuous(deinterpolate, reinterpolate$$1) {
++  var domain = unit,
++      range = unit,
++      interpolate$$1 = interpolateValue,
++      clamp = false,
++      piecewise$$1,
++      output,
++      input;
++
++  function rescale() {
++    piecewise$$1 = Math.min(domain.length, range.length) > 2 ? polymap : bimap;
++    output = input = null;
++    return scale;
++  }
++
++  function scale(x) {
++    return (output || (output = piecewise$$1(domain, range, clamp ? deinterpolateClamp(deinterpolate) : deinterpolate, interpolate$$1)))(+x);
++  }
++
++  scale.invert = function(y) {
++    return (input || (input = piecewise$$1(range, domain, deinterpolateLinear, clamp ? reinterpolateClamp(reinterpolate$$1) : reinterpolate$$1)))(+y);
++  };
++
++  scale.domain = function(_) {
++    return arguments.length ? (domain = map$2.call(_, number$2), rescale()) : domain.slice();
++  };
++
++  scale.range = function(_) {
++    return arguments.length ? (range = slice$5.call(_), rescale()) : range.slice();
++  };
++
++  scale.rangeRound = function(_) {
++    return range = slice$5.call(_), interpolate$$1 = interpolateRound, rescale();
++  };
++
++  scale.clamp = function(_) {
++    return arguments.length ? (clamp = !!_, rescale()) : clamp;
++  };
++
++  scale.interpolate = function(_) {
++    return arguments.length ? (interpolate$$1 = _, rescale()) : interpolate$$1;
++  };
++
++  return rescale();
++}
++
++function tickFormat(domain, count, specifier) {
++  var start = domain[0],
++      stop = domain[domain.length - 1],
++      step = tickStep(start, stop, count == null ? 10 : count),
++      precision;
++  specifier = formatSpecifier(specifier == null ? ",f" : specifier);
++  switch (specifier.type) {
++    case "s": {
++      var value = Math.max(Math.abs(start), Math.abs(stop));
++      if (specifier.precision == null && !isNaN(precision = precisionPrefix(step, value))) specifier.precision = precision;
++      return exports.formatPrefix(specifier, value);
++    }
++    case "":
++    case "e":
++    case "g":
++    case "p":
++    case "r": {
++      if (specifier.precision == null && !isNaN(precision = precisionRound(step, Math.max(Math.abs(start), Math.abs(stop))))) specifier.precision = precision - (specifier.type === "e");
++      break;
++    }
++    case "f":
++    case "%": {
++      if (specifier.precision == null && !isNaN(precision = precisionFixed(step))) specifier.precision = precision - (specifier.type === "%") * 2;
++      break;
++    }
++  }
++  return exports.format(specifier);
++}
++
++function linearish(scale) {
++  var domain = scale.domain;
++
++  scale.ticks = function(count) {
++    var d = domain();
++    return ticks(d[0], d[d.length - 1], count == null ? 10 : count);
++  };
++
++  scale.tickFormat = function(count, specifier) {
++    return tickFormat(domain(), count, specifier);
++  };
++
++  scale.nice = function(count) {
++    if (count == null) count = 10;
++
++    var d = domain(),
++        i0 = 0,
++        i1 = d.length - 1,
++        start = d[i0],
++        stop = d[i1],
++        step;
++
++    if (stop < start) {
++      step = start, start = stop, stop = step;
++      step = i0, i0 = i1, i1 = step;
++    }
++
++    step = tickIncrement(start, stop, count);
++
++    if (step > 0) {
++      start = Math.floor(start / step) * step;
++      stop = Math.ceil(stop / step) * step;
++      step = tickIncrement(start, stop, count);
++    } else if (step < 0) {
++      start = Math.ceil(start * step) / step;
++      stop = Math.floor(stop * step) / step;
++      step = tickIncrement(start, stop, count);
++    }
++
++    if (step > 0) {
++      d[i0] = Math.floor(start / step) * step;
++      d[i1] = Math.ceil(stop / step) * step;
++      domain(d);
++    } else if (step < 0) {
++      d[i0] = Math.ceil(start * step) / step;
++      d[i1] = Math.floor(stop * step) / step;
++      domain(d);
++    }
++
++    return scale;
++  };
++
++  return scale;
++}
++
++function linear$2() {
++  var scale = continuous(deinterpolateLinear, reinterpolate);
++
++  scale.copy = function() {
++    return copy(scale, linear$2());
++  };
++
++  return linearish(scale);
++}
++
++function identity$6() {
++  var domain = [0, 1];
++
++  function scale(x) {
++    return +x;
++  }
++
++  scale.invert = scale;
++
++  scale.domain = scale.range = function(_) {
++    return arguments.length ? (domain = map$2.call(_, number$2), scale) : domain.slice();
++  };
++
++  scale.copy = function() {
++    return identity$6().domain(domain);
++  };
++
++  return linearish(scale);
++}
++
++function nice(domain, interval) {
++  domain = domain.slice();
++
++  var i0 = 0,
++      i1 = domain.length - 1,
++      x0 = domain[i0],
++      x1 = domain[i1],
++      t;
++
++  if (x1 < x0) {
++    t = i0, i0 = i1, i1 = t;
++    t = x0, x0 = x1, x1 = t;
++  }
++
++  domain[i0] = interval.floor(x0);
++  domain[i1] = interval.ceil(x1);
++  return domain;
++}
++
++function deinterpolate(a, b) {
++  return (b = Math.log(b / a))
++      ? function(x) { return Math.log(x / a) / b; }
++      : constant$10(b);
++}
++
++function reinterpolate$1(a, b) {
++  return a < 0
++      ? function(t) { return -Math.pow(-b, t) * Math.pow(-a, 1 - t); }
++      : function(t) { return Math.pow(b, t) * Math.pow(a, 1 - t); };
++}
++
++function pow10(x) {
++  return isFinite(x) ? +("1e" + x) : x < 0 ? 0 : x;
++}
++
++function powp(base) {
++  return base === 10 ? pow10
++      : base === Math.E ? Math.exp
++      : function(x) { return Math.pow(base, x); };
++}
++
++function logp(base) {
++  return base === Math.E ? Math.log
++      : base === 10 && Math.log10
++      || base === 2 && Math.log2
++      || (base = Math.log(base), function(x) { return Math.log(x) / base; });
++}
++
++function reflect(f) {
++  return function(x) {
++    return -f(-x);
++  };
++}
++
++function log$1() {
++  var scale = continuous(deinterpolate, reinterpolate$1).domain([1, 10]),
++      domain = scale.domain,
++      base = 10,
++      logs = logp(10),
++      pows = powp(10);
++
++  function rescale() {
++    logs = logp(base), pows = powp(base);
++    if (domain()[0] < 0) logs = reflect(logs), pows = reflect(pows);
++    return scale;
++  }
++
++  scale.base = function(_) {
++    return arguments.length ? (base = +_, rescale()) : base;
++  };
++
++  scale.domain = function(_) {
++    return arguments.length ? (domain(_), rescale()) : domain();
++  };
++
++  scale.ticks = function(count) {
++    var d = domain(),
++        u = d[0],
++        v = d[d.length - 1],
++        r;
++
++    if (r = v < u) i = u, u = v, v = i;
++
++    var i = logs(u),
++        j = logs(v),
++        p,
++        k,
++        t,
++        n = count == null ? 10 : +count,
++        z = [];
++
++    if (!(base % 1) && j - i < n) {
++      i = Math.round(i) - 1, j = Math.round(j) + 1;
++      if (u > 0) for (; i < j; ++i) {
++        for (k = 1, p = pows(i); k < base; ++k) {
++          t = p * k;
++          if (t < u) continue;
++          if (t > v) break;
++          z.push(t);
++        }
++      } else for (; i < j; ++i) {
++        for (k = base - 1, p = pows(i); k >= 1; --k) {
++          t = p * k;
++          if (t < u) continue;
++          if (t > v) break;
++          z.push(t);
++        }
++      }
++    } else {
++      z = ticks(i, j, Math.min(j - i, n)).map(pows);
++    }
++
++    return r ? z.reverse() : z;
++  };
++
++  scale.tickFormat = function(count, specifier) {
++    if (specifier == null) specifier = base === 10 ? ".0e" : ",";
++    if (typeof specifier !== "function") specifier = exports.format(specifier);
++    if (count === Infinity) return specifier;
++    if (count == null) count = 10;
++    var k = Math.max(1, base * count / scale.ticks().length); // TODO fast estimate?
++    return function(d) {
++      var i = d / pows(Math.round(logs(d)));
++      if (i * base < base - 0.5) i *= base;
++      return i <= k ? specifier(d) : "";
++    };
++  };
++
++  scale.nice = function() {
++    return domain(nice(domain(), {
++      floor: function(x) { return pows(Math.floor(logs(x))); },
++      ceil: function(x) { return pows(Math.ceil(logs(x))); }
++    }));
++  };
++
++  scale.copy = function() {
++    return copy(scale, log$1().base(base));
++  };
++
++  return scale;
++}
++
++function raise$1(x, exponent) {
++  return x < 0 ? -Math.pow(-x, exponent) : Math.pow(x, exponent);
++}
++
++function pow$1() {
++  var exponent = 1,
++      scale = continuous(deinterpolate, reinterpolate),
++      domain = scale.domain;
++
++  function deinterpolate(a, b) {
++    return (b = raise$1(b, exponent) - (a = raise$1(a, exponent)))
++        ? function(x) { return (raise$1(x, exponent) - a) / b; }
++        : constant$10(b);
++  }
++
++  function reinterpolate(a, b) {
++    b = raise$1(b, exponent) - (a = raise$1(a, exponent));
++    return function(t) { return raise$1(a + b * t, 1 / exponent); };
++  }
++
++  scale.exponent = function(_) {
++    return arguments.length ? (exponent = +_, domain(domain())) : exponent;
++  };
++
++  scale.copy = function() {
++    return copy(scale, pow$1().exponent(exponent));
++  };
++
++  return linearish(scale);
++}
++
++function sqrt$1() {
++  return pow$1().exponent(0.5);
++}
++
++function quantile$$1() {
++  var domain = [],
++      range = [],
++      thresholds = [];
++
++  function rescale() {
++    var i = 0, n = Math.max(1, range.length);
++    thresholds = new Array(n - 1);
++    while (++i < n) thresholds[i - 1] = threshold(domain, i / n);
++    return scale;
++  }
++
++  function scale(x) {
++    if (!isNaN(x = +x)) return range[bisectRight(thresholds, x)];
++  }
++
++  scale.invertExtent = function(y) {
++    var i = range.indexOf(y);
++    return i < 0 ? [NaN, NaN] : [
++      i > 0 ? thresholds[i - 1] : domain[0],
++      i < thresholds.length ? thresholds[i] : domain[domain.length - 1]
++    ];
++  };
++
++  scale.domain = function(_) {
++    if (!arguments.length) return domain.slice();
++    domain = [];
++    for (var i = 0, n = _.length, d; i < n; ++i) if (d = _[i], d != null && !isNaN(d = +d)) domain.push(d);
++    domain.sort(ascending);
++    return rescale();
++  };
++
++  scale.range = function(_) {
++    return arguments.length ? (range = slice$5.call(_), rescale()) : range.slice();
++  };
++
++  scale.quantiles = function() {
++    return thresholds.slice();
++  };
++
++  scale.copy = function() {
++    return quantile$$1()
++        .domain(domain)
++        .range(range);
++  };
++
++  return scale;
++}
++
++function quantize$1() {
++  var x0 = 0,
++      x1 = 1,
++      n = 1,
++      domain = [0.5],
++      range = [0, 1];
++
++  function scale(x) {
++    if (x <= x) return range[bisectRight(domain, x, 0, n)];
++  }
++
++  function rescale() {
++    var i = -1;
++    domain = new Array(n);
++    while (++i < n) domain[i] = ((i + 1) * x1 - (i - n) * x0) / (n + 1);
++    return scale;
++  }
++
++  scale.domain = function(_) {
++    return arguments.length ? (x0 = +_[0], x1 = +_[1], rescale()) : [x0, x1];
++  };
++
++  scale.range = function(_) {
++    return arguments.length ? (n = (range = slice$5.call(_)).length - 1, rescale()) : range.slice();
++  };
++
++  scale.invertExtent = function(y) {
++    var i = range.indexOf(y);
++    return i < 0 ? [NaN, NaN]
++        : i < 1 ? [x0, domain[0]]
++        : i >= n ? [domain[n - 1], x1]
++        : [domain[i - 1], domain[i]];
++  };
++
++  scale.copy = function() {
++    return quantize$1()
++        .domain([x0, x1])
++        .range(range);
++  };
++
++  return linearish(scale);
++}
++
++function threshold$1() {
++  var domain = [0.5],
++      range = [0, 1],
++      n = 1;
++
++  function scale(x) {
++    if (x <= x) return range[bisectRight(domain, x, 0, n)];
++  }
++
++  scale.domain = function(_) {
++    return arguments.length ? (domain = slice$5.call(_), n = Math.min(domain.length, range.length - 1), scale) : domain.slice();
++  };
++
++  scale.range = function(_) {
++    return arguments.length ? (range = slice$5.call(_), n = Math.min(domain.length, range.length - 1), scale) : range.slice();
++  };
++
++  scale.invertExtent = function(y) {
++    var i = range.indexOf(y);
++    return [domain[i - 1], domain[i]];
++  };
++
++  scale.copy = function() {
++    return threshold$1()
++        .domain(domain)
++        .range(range);
++  };
++
++  return scale;
++}
++
++var t0$1 = new Date,
++    t1$1 = new Date;
++
++function newInterval(floori, offseti, count, field) {
++
++  function interval(date) {
++    return floori(date = new Date(+date)), date;
++  }
++
++  interval.floor = interval;
++
++  interval.ceil = function(date) {
++    return floori(date = new Date(date - 1)), offseti(date, 1), floori(date), date;
++  };
++
++  interval.round = function(date) {
++    var d0 = interval(date),
++        d1 = interval.ceil(date);
++    return date - d0 < d1 - date ? d0 : d1;
++  };
++
++  interval.offset = function(date, step) {
++    return offseti(date = new Date(+date), step == null ? 1 : Math.floor(step)), date;
++  };
++
++  interval.range = function(start, stop, step) {
++    var range = [], previous;
++    start = interval.ceil(start);
++    step = step == null ? 1 : Math.floor(step);
++    if (!(start < stop) || !(step > 0)) return range; // also handles Invalid Date
++    do range.push(previous = new Date(+start)), offseti(start, step), floori(start);
++    while (previous < start && start < stop);
++    return range;
++  };
++
++  interval.filter = function(test) {
++    return newInterval(function(date) {
++      if (date >= date) while (floori(date), !test(date)) date.setTime(date - 1);
++    }, function(date, step) {
++      if (date >= date) {
++        if (step < 0) while (++step <= 0) {
++          while (offseti(date, -1), !test(date)) {} // eslint-disable-line no-empty
++        } else while (--step >= 0) {
++          while (offseti(date, +1), !test(date)) {} // eslint-disable-line no-empty
++        }
++      }
++    });
++  };
++
++  if (count) {
++    interval.count = function(start, end) {
++      t0$1.setTime(+start), t1$1.setTime(+end);
++      floori(t0$1), floori(t1$1);
++      return Math.floor(count(t0$1, t1$1));
++    };
++
++    interval.every = function(step) {
++      step = Math.floor(step);
++      return !isFinite(step) || !(step > 0) ? null
++          : !(step > 1) ? interval
++          : interval.filter(field
++              ? function(d) { return field(d) % step === 0; }
++              : function(d) { return interval.count(0, d) % step === 0; });
++    };
++  }
++
++  return interval;
++}
++
++var millisecond = newInterval(function() {
++  // noop
++}, function(date, step) {
++  date.setTime(+date + step);
++}, function(start, end) {
++  return end - start;
++});
++
++// An optimized implementation for this simple case.
++millisecond.every = function(k) {
++  k = Math.floor(k);
++  if (!isFinite(k) || !(k > 0)) return null;
++  if (!(k > 1)) return millisecond;
++  return newInterval(function(date) {
++    date.setTime(Math.floor(date / k) * k);
++  }, function(date, step) {
++    date.setTime(+date + step * k);
++  }, function(start, end) {
++    return (end - start) / k;
++  });
++};
++var milliseconds = millisecond.range;
++
++var durationSecond = 1e3;
++var durationMinute = 6e4;
++var durationHour = 36e5;
++var durationDay = 864e5;
++var durationWeek = 6048e5;
++
++var second = newInterval(function(date) {
++  date.setTime(Math.floor(date / durationSecond) * durationSecond);
++}, function(date, step) {
++  date.setTime(+date + step * durationSecond);
++}, function(start, end) {
++  return (end - start) / durationSecond;
++}, function(date) {
++  return date.getUTCSeconds();
++});
++var seconds = second.range;
++
++var minute = newInterval(function(date) {
++  date.setTime(Math.floor(date / durationMinute) * durationMinute);
++}, function(date, step) {
++  date.setTime(+date + step * durationMinute);
++}, function(start, end) {
++  return (end - start) / durationMinute;
++}, function(date) {
++  return date.getMinutes();
++});
++var minutes = minute.range;
++
++var hour = newInterval(function(date) {
++  var offset = date.getTimezoneOffset() * durationMinute % durationHour;
++  if (offset < 0) offset += durationHour;
++  date.setTime(Math.floor((+date - offset) / durationHour) * durationHour + offset);
++}, function(date, step) {
++  date.setTime(+date + step * durationHour);
++}, function(start, end) {
++  return (end - start) / durationHour;
++}, function(date) {
++  return date.getHours();
++});
++var hours = hour.range;
++
++var day = newInterval(function(date) {
++  date.setHours(0, 0, 0, 0);
++}, function(date, step) {
++  date.setDate(date.getDate() + step);
++}, function(start, end) {
++  return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute) / durationDay;
++}, function(date) {
++  return date.getDate() - 1;
++});
++var days = day.range;
++
++function weekday(i) {
++  return newInterval(function(date) {
++    date.setDate(date.getDate() - (date.getDay() + 7 - i) % 7);
++    date.setHours(0, 0, 0, 0);
++  }, function(date, step) {
++    date.setDate(date.getDate() + step * 7);
++  }, function(start, end) {
++    return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute) / durationWeek;
++  });
++}
++
++var sunday = weekday(0);
++var monday = weekday(1);
++var tuesday = weekday(2);
++var wednesday = weekday(3);
++var thursday = weekday(4);
++var friday = weekday(5);
++var saturday = weekday(6);
++
++var sundays = sunday.range;
++var mondays = monday.range;
++var tuesdays = tuesday.range;
++var wednesdays = wednesday.range;
++var thursdays = thursday.range;
++var fridays = friday.range;
++var saturdays = saturday.range;
++
++var month = newInterval(function(date) {
++  date.setDate(1);
++  date.setHours(0, 0, 0, 0);
++}, function(date, step) {
++  date.setMonth(date.getMonth() + step);
++}, function(start, end) {
++  return end.getMonth() - start.getMonth() + (end.getFullYear() - start.getFullYear()) * 12;
++}, function(date) {
++  return date.getMonth();
++});
++var months = month.range;
++
++var year = newInterval(function(date) {
++  date.setMonth(0, 1);
++  date.setHours(0, 0, 0, 0);
++}, function(date, step) {
++  date.setFullYear(date.getFullYear() + step);
++}, function(start, end) {
++  return end.getFullYear() - start.getFullYear();
++}, function(date) {
++  return date.getFullYear();
++});
++
++// An optimized implementation for this simple case.
++year.every = function(k) {
++  return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : newInterval(function(date) {
++    date.setFullYear(Math.floor(date.getFullYear() / k) * k);
++    date.setMonth(0, 1);
++    date.setHours(0, 0, 0, 0);
++  }, function(date, step) {
++    date.setFullYear(date.getFullYear() + step * k);
++  });
++};
++var years = year.range;
++
++var utcMinute = newInterval(function(date) {
++  date.setUTCSeconds(0, 0);
++}, function(date, step) {
++  date.setTime(+date + step * durationMinute);
++}, function(start, end) {
++  return (end - start) / durationMinute;
++}, function(date) {
++  return date.getUTCMinutes();
++});
++var utcMinutes = utcMinute.range;
++
++var utcHour = newInterval(function(date) {
++  date.setUTCMinutes(0, 0, 0);
++}, function(date, step) {
++  date.setTime(+date + step * durationHour);
++}, function(start, end) {
++  return (end - start) / durationHour;
++}, function(date) {
++  return date.getUTCHours();
++});
++var utcHours = utcHour.range;
++
++var utcDay = newInterval(function(date) {
++  date.setUTCHours(0, 0, 0, 0);
++}, function(date, step) {
++  date.setUTCDate(date.getUTCDate() + step);
++}, function(start, end) {
++  return (end - start) / durationDay;
++}, function(date) {
++  return date.getUTCDate() - 1;
++});
++var utcDays = utcDay.range;
++
++function utcWeekday(i) {
++  return newInterval(function(date) {
++    date.setUTCDate(date.getUTCDate() - (date.getUTCDay() + 7 - i) % 7);
++    date.setUTCHours(0, 0, 0, 0);
++  }, function(date, step) {
++    date.setUTCDate(date.getUTCDate() + step * 7);
++  }, function(start, end) {
++    return (end - start) / durationWeek;
++  });
++}
++
++var utcSunday = utcWeekday(0);
++var utcMonday = utcWeekday(1);
++var utcTuesday = utcWeekday(2);
++var utcWednesday = utcWeekday(3);
++var utcThursday = utcWeekday(4);
++var utcFriday = utcWeekday(5);
++var utcSaturday = utcWeekday(6);
++
++var utcSundays = utcSunday.range;
++var utcMondays = utcMonday.range;
++var utcTuesdays = utcTuesday.range;
++var utcWednesdays = utcWednesday.range;
++var utcThursdays = utcThursday.range;
++var utcFridays = utcFriday.range;
++var utcSaturdays = utcSaturday.range;
++
++var utcMonth = newInterval(function(date) {
++  date.setUTCDate(1);
++  date.setUTCHours(0, 0, 0, 0);
++}, function(date, step) {
++  date.setUTCMonth(date.getUTCMonth() + step);
++}, function(start, end) {
++  return end.getUTCMonth() - start.getUTCMonth() + (end.getUTCFullYear() - start.getUTCFullYear()) * 12;
++}, function(date) {
++  return date.getUTCMonth();
++});
++var utcMonths = utcMonth.range;
++
++var utcYear = newInterval(function(date) {
++  date.setUTCMonth(0, 1);
++  date.setUTCHours(0, 0, 0, 0);
++}, function(date, step) {
++  date.setUTCFullYear(date.getUTCFullYear() + step);
++}, function(start, end) {
++  return end.getUTCFullYear() - start.getUTCFullYear();
++}, function(date) {
++  return date.getUTCFullYear();
++});
++
++// An optimized implementation for this simple case.
++utcYear.every = function(k) {
++  return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : newInterval(function(date) {
++    date.setUTCFullYear(Math.floor(date.getUTCFullYear() / k) * k);
++    date.setUTCMonth(0, 1);
++    date.setUTCHours(0, 0, 0, 0);
++  }, function(date, step) {
++    date.setUTCFullYear(date.getUTCFullYear() + step * k);
++  });
++};
++var utcYears = utcYear.range;
++
++function localDate(d) {
++  if (0 <= d.y && d.y < 100) {
++    var date = new Date(-1, d.m, d.d, d.H, d.M, d.S, d.L);
++    date.setFullYear(d.y);
++    return date;
++  }
++  return new Date(d.y, d.m, d.d, d.H, d.M, d.S, d.L);
++}
++
++function utcDate(d) {
++  if (0 <= d.y && d.y < 100) {
++    var date = new Date(Date.UTC(-1, d.m, d.d, d.H, d.M, d.S, d.L));
++    date.setUTCFullYear(d.y);
++    return date;
++  }
++  return new Date(Date.UTC(d.y, d.m, d.d, d.H, d.M, d.S, d.L));
++}
++
++function newYear(y) {
++  return {y: y, m: 0, d: 1, H: 0, M: 0, S: 0, L: 0};
++}
++
++function formatLocale$1(locale) {
++  var locale_dateTime = locale.dateTime,
++      locale_date = locale.date,
++      locale_time = locale.time,
++      locale_periods = locale.periods,
++      locale_weekdays = locale.days,
++      locale_shortWeekdays = locale.shortDays,
++      locale_months = locale.months,
++      locale_shortMonths = locale.shortMonths;
++
++  var periodRe = formatRe(locale_periods),
++      periodLookup = formatLookup(locale_periods),
++      weekdayRe = formatRe(locale_weekdays),
++      weekdayLookup = formatLookup(locale_weekdays),
++      shortWeekdayRe = formatRe(locale_shortWeekdays),
++      shortWeekdayLookup = formatLookup(locale_shortWeekdays),
++      monthRe = formatRe(locale_months),
++      monthLookup = formatLookup(locale_months),
++      shortMonthRe = formatRe(locale_shortMonths),
++      shortMonthLookup = formatLookup(locale_shortMonths);
++
++  var formats = {
++    "a": formatShortWeekday,
++    "A": formatWeekday,
++    "b": formatShortMonth,
++    "B": formatMonth,
++    "c": null,
++    "d": formatDayOfMonth,
++    "e": formatDayOfMonth,
++    "f": formatMicroseconds,
++    "H": formatHour24,
++    "I": formatHour12,
++    "j": formatDayOfYear,
++    "L": formatMilliseconds,
++    "m": formatMonthNumber,
++    "M": formatMinutes,
++    "p": formatPeriod,
++    "Q": formatUnixTimestamp,
++    "s": formatUnixTimestampSeconds,
++    "S": formatSeconds,
++    "u": formatWeekdayNumberMonday,
++    "U": formatWeekNumberSunday,
++    "V": formatWeekNumberISO,
++    "w": formatWeekdayNumberSunday,
++    "W": formatWeekNumberMonday,
++    "x": null,
++    "X": null,
++    "y": formatYear,
++    "Y": formatFullYear,
++    "Z": formatZone,
++    "%": formatLiteralPercent
++  };
++
++  var utcFormats = {
++    "a": formatUTCShortWeekday,
++    "A": formatUTCWeekday,
++    "b": formatUTCShortMonth,
++    "B": formatUTCMonth,
++    "c": null,
++    "d": formatUTCDayOfMonth,
++    "e": formatUTCDayOfMonth,
++    "f": formatUTCMicroseconds,
++    "H": formatUTCHour24,
++    "I": formatUTCHour12,
++    "j": formatUTCDayOfYear,
++    "L": formatUTCMilliseconds,
++    "m": formatUTCMonthNumber,
++    "M": formatUTCMinutes,
++    "p": formatUTCPeriod,
++    "Q": formatUnixTimestamp,
++    "s": formatUnixTimestampSeconds,
++    "S": formatUTCSeconds,
++    "u": formatUTCWeekdayNumberMonday,
++    "U": formatUTCWeekNumberSunday,
++    "V": formatUTCWeekNumberISO,
++    "w": formatUTCWeekdayNumberSunday,
++    "W": formatUTCWeekNumberMonday,
++    "x": null,
++    "X": null,
++    "y": formatUTCYear,
++    "Y": formatUTCFullYear,
++    "Z": formatUTCZone,
++    "%": formatLiteralPercent
++  };
++
++  var parses = {
++    "a": parseShortWeekday,
++    "A": parseWeekday,
++    "b": parseShortMonth,
++    "B": parseMonth,
++    "c": parseLocaleDateTime,
++    "d": parseDayOfMonth,
++    "e": parseDayOfMonth,
++    "f": parseMicroseconds,
++    "H": parseHour24,
++    "I": parseHour24,
++    "j": parseDayOfYear,
++    "L": parseMilliseconds,
++    "m": parseMonthNumber,
++    "M": parseMinutes,
++    "p": parsePeriod,
++    "Q": parseUnixTimestamp,
++    "s": parseUnixTimestampSeconds,
++    "S": parseSeconds,
++    "u": parseWeekdayNumberMonday,
++    "U": parseWeekNumberSunday,
++    "V": parseWeekNumberISO,
++    "w": parseWeekdayNumberSunday,
++    "W": parseWeekNumberMonday,
++    "x": parseLocaleDate,
++    "X": parseLocaleTime,
++    "y": parseYear,
++    "Y": parseFullYear,
++    "Z": parseZone,
++    "%": parseLiteralPercent
++  };
++
++  // These recursive directive definitions must be deferred.
++  formats.x = newFormat(locale_date, formats);
++  formats.X = newFormat(locale_time, formats);
++  formats.c = newFormat(locale_dateTime, formats);
++  utcFormats.x = newFormat(locale_date, utcFormats);
++  utcFormats.X = newFormat(locale_time, utcFormats);
++  utcFormats.c = newFormat(locale_dateTime, utcFormats);
++
++  function newFormat(specifier, formats) {
++    return function(date) {
++      var string = [],
++          i = -1,
++          j = 0,
++          n = specifier.length,
++          c,
++          pad,
++          format;
++
++      if (!(date instanceof Date)) date = new Date(+date);
++
++      while (++i < n) {
++        if (specifier.charCodeAt(i) === 37) {
++          string.push(specifier.slice(j, i));
++          if ((pad = pads[c = specifier.charAt(++i)]) != null) c = specifier.charAt(++i);
++          else pad = c === "e" ? " " : "0";
++          if (format = formats[c]) c = format(date, pad);
++          string.push(c);
++          j = i + 1;
++        }
++      }
++
++      string.push(specifier.slice(j, i));
++      return string.join("");
++    };
++  }
++
++  function newParse(specifier, newDate) {
++    return function(string) {
++      var d = newYear(1900),
++          i = parseSpecifier(d, specifier, string += "", 0),
++          week, day$$1;
++      if (i != string.length) return null;
++
++      // If a UNIX timestamp is specified, return it.
++      if ("Q" in d) return new Date(d.Q);
++
++      // The am-pm flag is 0 for AM, and 1 for PM.
++      if ("p" in d) d.H = d.H % 12 + d.p * 12;
++
++      // Convert day-of-week and week-of-year to day-of-year.
++      if ("V" in d) {
++        if (d.V < 1 || d.V > 53) return null;
++        if (!("w" in d)) d.w = 1;
++        if ("Z" in d) {
++          week = utcDate(newYear(d.y)), day$$1 = week.getUTCDay();
++          week = day$$1 > 4 || day$$1 === 0 ? utcMonday.ceil(week) : utcMonday(week);
++          week = utcDay.offset(week, (d.V - 1) * 7);
++          d.y = week.getUTCFullYear();
++          d.m = week.getUTCMonth();
++          d.d = week.getUTCDate() + (d.w + 6) % 7;
++        } else {
++          week = newDate(newYear(d.y)), day$$1 = week.getDay();
++          week = day$$1 > 4 || day$$1 === 0 ? monday.ceil(week) : monday(week);
++          week = day.offset(week, (d.V - 1) * 7);
++          d.y = week.getFullYear();
++          d.m = week.getMonth();
++          d.d = week.getDate() + (d.w + 6) % 7;
++        }
++      } else if ("W" in d || "U" in d) {
++        if (!("w" in d)) d.w = "u" in d ? d.u % 7 : "W" in d ? 1 : 0;
++        day$$1 = "Z" in d ? utcDate(newYear(d.y)).getUTCDay() : newDate(newYear(d.y)).getDay();
++        d.m = 0;
++        d.d = "W" in d ? (d.w + 6) % 7 + d.W * 7 - (day$$1 + 5) % 7 : d.w + d.U * 7 - (day$$1 + 6) % 7;
++      }
++
++      // If a time zone is specified, all fields are interpreted as UTC and then
++      // offset according to the specified time zone.
++      if ("Z" in d) {
++        d.H += d.Z / 100 | 0;
++        d.M += d.Z % 100;
++        return utcDate(d);
++      }
++
++      // Otherwise, all fields are in local time.
++      return newDate(d);
++    };
++  }
++
++  function parseSpecifier(d, specifier, string, j) {
++    var i = 0,
++        n = specifier.length,
++        m = string.length,
++        c,
++        parse;
++
++    while (i < n) {
++      if (j >= m) return -1;
++      c = specifier.charCodeAt(i++);
++      if (c === 37) {
++        c = specifier.charAt(i++);
++        parse = parses[c in pads ? specifier.charAt(i++) : c];
++        if (!parse || ((j = parse(d, string, j)) < 0)) return -1;
++      } else if (c != string.charCodeAt(j++)) {
++        return -1;
++      }
++    }
++
++    return j;
++  }
++
++  function parsePeriod(d, string, i) {
++    var n = periodRe.exec(string.slice(i));
++    return n ? (d.p = periodLookup[n[0].toLowerCase()], i + n[0].length) : -1;
++  }
++
++  function parseShortWeekday(d, string, i) {
++    var n = shortWeekdayRe.exec(string.slice(i));
++    return n ? (d.w = shortWeekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1;
++  }
++
++  function parseWeekday(d, string, i) {
++    var n = weekdayRe.exec(string.slice(i));
++    return n ? (d.w = weekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1;
++  }
++
++  function parseShortMonth(d, string, i) {
++    var n = shortMonthRe.exec(string.slice(i));
++    return n ? (d.m = shortMonthLookup[n[0].toLowerCase()], i + n[0].length) : -1;
++  }
++
++  function parseMonth(d, string, i) {
++    var n = monthRe.exec(string.slice(i));
++    return n ? (d.m = monthLookup[n[0].toLowerCase()], i + n[0].length) : -1;
++  }
++
++  function parseLocaleDateTime(d, string, i) {
++    return parseSpecifier(d, locale_dateTime, string, i);
++  }
++
++  function parseLocaleDate(d, string, i) {
++    return parseSpecifier(d, locale_date, string, i);
++  }
++
++  function parseLocaleTime(d, string, i) {
++    return parseSpecifier(d, locale_time, string, i);
++  }
++
++  function formatShortWeekday(d) {
++    return locale_shortWeekdays[d.getDay()];
++  }
++
++  function formatWeekday(d) {
++    return locale_weekdays[d.getDay()];
++  }
++
++  function formatShortMonth(d) {
++    return locale_shortMonths[d.getMonth()];
++  }
++
++  function formatMonth(d) {
++    return locale_months[d.getMonth()];
++  }
++
++  function formatPeriod(d) {
++    return locale_periods[+(d.getHours() >= 12)];
++  }
++
++  function formatUTCShortWeekday(d) {
++    return locale_shortWeekdays[d.getUTCDay()];
++  }
++
++  function formatUTCWeekday(d) {
++    return locale_weekdays[d.getUTCDay()];
++  }
++
++  function formatUTCShortMonth(d) {
++    return locale_shortMonths[d.getUTCMonth()];
++  }
++
++  function formatUTCMonth(d) {
++    return locale_months[d.getUTCMonth()];
++  }
++
++  function formatUTCPeriod(d) {
++    return locale_periods[+(d.getUTCHours() >= 12)];
++  }
++
++  return {
++    format: function(specifier) {
++      var f = newFormat(specifier += "", formats);
++      f.toString = function() { return specifier; };
++      return f;
++    },
++    parse: function(specifier) {
++      var p = newParse(specifier += "", localDate);
++      p.toString = function() { return specifier; };
++      return p;
++    },
++    utcFormat: function(specifier) {
++      var f = newFormat(specifier += "", utcFormats);
++      f.toString = function() { return specifier; };
++      return f;
++    },
++    utcParse: function(specifier) {
++      var p = newParse(specifier, utcDate);
++      p.toString = function() { return specifier; };
++      return p;
++    }
++  };
++}
++
++var pads = {"-": "", "_": " ", "0": "0"},
++    numberRe = /^\s*\d+/, // note: ignores next directive
++    percentRe = /^%/,
++    requoteRe = /[\\^$*+?|[\]().{}]/g;
++
++function pad(value, fill, width) {
++  var sign = value < 0 ? "-" : "",
++      string = (sign ? -value : value) + "",
++      length = string.length;
++  return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string);
++}
++
++function requote(s) {
++  return s.replace(requoteRe, "\\$&");
++}
++
++function formatRe(names) {
++  return new RegExp("^(?:" + names.map(requote).join("|") + ")", "i");
++}
++
++function formatLookup(names) {
++  var map = {}, i = -1, n = names.length;
++  while (++i < n) map[names[i].toLowerCase()] = i;
++  return map;
++}
++
++function parseWeekdayNumberSunday(d, string, i) {
++  var n = numberRe.exec(string.slice(i, i + 1));
++  return n ? (d.w = +n[0], i + n[0].length) : -1;
++}
++
++function parseWeekdayNumberMonday(d, string, i) {
++  var n = numberRe.exec(string.slice(i, i + 1));
++  return n ? (d.u = +n[0], i + n[0].length) : -1;
++}
++
++function parseWeekNumberSunday(d, string, i) {
++  var n = numberRe.exec(string.slice(i, i + 2));
++  return n ? (d.U = +n[0], i + n[0].length) : -1;
++}
++
++function parseWeekNumberISO(d, string, i) {
++  var n = numberRe.exec(string.slice(i, i + 2));
++  return n ? (d.V = +n[0], i + n[0].length) : -1;
++}
++
++function parseWeekNumberMonday(d, string, i) {
++  var n = numberRe.exec(string.slice(i, i + 2));
++  return n ? (d.W = +n[0], i + n[0].length) : -1;
++}
++
++function parseFullYear(d, string, i) {
++  var n = numberRe.exec(string.slice(i, i + 4));
++  return n ? (d.y = +n[0], i + n[0].length) : -1;
++}
++
++function parseYear(d, string, i) {
++  var n = numberRe.exec(string.slice(i, i + 2));
++  return n ? (d.y = +n[0] + (+n[0] > 68 ? 1900 : 2000), i + n[0].length) : -1;
++}
++
++function parseZone(d, string, i) {
++  var n = /^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(string.slice(i, i + 6));
++  return n ? (d.Z = n[1] ? 0 : -(n[2] + (n[3] || "00")), i + n[0].length) : -1;
++}
++
++function parseMonthNumber(d, string, i) {
++  var n = numberRe.exec(string.slice(i, i + 2));
++  return n ? (d.m = n[0] - 1, i + n[0].length) : -1;
++}
++
++function parseDayOfMonth(d, string, i) {
++  var n = numberRe.exec(string.slice(i, i + 2));
++  return n ? (d.d = +n[0], i + n[0].length) : -1;
++}
++
++function parseDayOfYear(d, string, i) {
++  var n = numberRe.exec(string.slice(i, i + 3));
++  return n ? (d.m = 0, d.d = +n[0], i + n[0].length) : -1;
++}
++
++function parseHour24(d, string, i) {
++  var n = numberRe.exec(string.slice(i, i + 2));
++  return n ? (d.H = +n[0], i + n[0].length) : -1;
++}
++
++function parseMinutes(d, string, i) {
++  var n = numberRe.exec(string.slice(i, i + 2));
++  return n ? (d.M = +n[0], i + n[0].length) : -1;
++}
++
++function parseSeconds(d, string, i) {
++  var n = numberRe.exec(string.slice(i, i + 2));
++  return n ? (d.S = +n[0], i + n[0].length) : -1;
++}
++
++function parseMilliseconds(d, string, i) {
++  var n = numberRe.exec(string.slice(i, i + 3));
++  return n ? (d.L = +n[0], i + n[0].length) : -1;
++}
++
++function parseMicroseconds(d, string, i) {
++  var n = numberRe.exec(string.slice(i, i + 6));
++  return n ? (d.L = Math.floor(n[0] / 1000), i + n[0].length) : -1;
++}
++
++function parseLiteralPercent(d, string, i) {
++  var n = percentRe.exec(string.slice(i, i + 1));
++  return n ? i + n[0].length : -1;
++}
++
++function parseUnixTimestamp(d, string, i) {
++  var n = numberRe.exec(string.slice(i));
++  return n ? (d.Q = +n[0], i + n[0].length) : -1;
++}
++
++function parseUnixTimestampSeconds(d, string, i) {
++  var n = numberRe.exec(string.slice(i));
++  return n ? (d.Q = (+n[0]) * 1000, i + n[0].length) : -1;
++}
++
++function formatDayOfMonth(d, p) {
++  return pad(d.getDate(), p, 2);
++}
++
++function formatHour24(d, p) {
++  return pad(d.getHours(), p, 2);
++}
++
++function formatHour12(d, p) {
++  return pad(d.getHours() % 12 || 12, p, 2);
++}
++
++function formatDayOfYear(d, p) {
++  return pad(1 + day.count(year(d), d), p, 3);
++}
++
++function formatMilliseconds(d, p) {
++  return pad(d.getMilliseconds(), p, 3);
++}
++
++function formatMicroseconds(d, p) {
++  return formatMilliseconds(d, p) + "000";
++}
++
++function formatMonthNumber(d, p) {
++  return pad(d.getMonth() + 1, p, 2);
++}
++
++function formatMinutes(d, p) {
++  return pad(d.getMinutes(), p, 2);
++}
++
++function formatSeconds(d, p) {
++  return pad(d.getSeconds(), p, 2);
++}
++
++function formatWeekdayNumberMonday(d) {
++  var day$$1 = d.getDay();
++  return day$$1 === 0 ? 7 : day$$1;
++}
++
++function formatWeekNumberSunday(d, p) {
++  return pad(sunday.count(year(d), d), p, 2);
++}
++
++function formatWeekNumberISO(d, p) {
++  var day$$1 = d.getDay();
++  d = (day$$1 >= 4 || day$$1 === 0) ? thursday(d) : thursday.ceil(d);
++  return pad(thursday.count(year(d), d) + (year(d).getDay() === 4), p, 2);
++}
++
++function formatWeekdayNumberSunday(d) {
++  return d.getDay();
++}
++
++function formatWeekNumberMonday(d, p) {
++  return pad(monday.count(year(d), d), p, 2);
++}
++
++function formatYear(d, p) {
++  return pad(d.getFullYear() % 100, p, 2);
++}
++
++function formatFullYear(d, p) {
++  return pad(d.getFullYear() % 10000, p, 4);
++}
++
++function formatZone(d) {
++  var z = d.getTimezoneOffset();
++  return (z > 0 ? "-" : (z *= -1, "+"))
++      + pad(z / 60 | 0, "0", 2)
++      + pad(z % 60, "0", 2);
++}
++
++function formatUTCDayOfMonth(d, p) {
++  return pad(d.getUTCDate(), p, 2);
++}
++
++function formatUTCHour24(d, p) {
++  return pad(d.getUTCHours(), p, 2);
++}
++
++function formatUTCHour12(d, p) {
++  return pad(d.getUTCHours() % 12 || 12, p, 2);
++}
++
++function formatUTCDayOfYear(d, p) {
++  return pad(1 + utcDay.count(utcYear(d), d), p, 3);
++}
++
++function formatUTCMilliseconds(d, p) {
++  return pad(d.getUTCMilliseconds(), p, 3);
++}
++
++function formatUTCMicroseconds(d, p) {
++  return formatUTCMilliseconds(d, p) + "000";
++}
++
++function formatUTCMonthNumber(d, p) {
++  return pad(d.getUTCMonth() + 1, p, 2);
++}
++
++function formatUTCMinutes(d, p) {
++  return pad(d.getUTCMinutes(), p, 2);
++}
++
++function formatUTCSeconds(d, p) {
++  return pad(d.getUTCSeconds(), p, 2);
++}
++
++function formatUTCWeekdayNumberMonday(d) {
++  var dow = d.getUTCDay();
++  return dow === 0 ? 7 : dow;
++}
++
++function formatUTCWeekNumberSunday(d, p) {
++  return pad(utcSunday.count(utcYear(d), d), p, 2);
++}
++
++function formatUTCWeekNumberISO(d, p) {
++  var day$$1 = d.getUTCDay();
++  d = (day$$1 >= 4 || day$$1 === 0) ? utcThursday(d) : utcThursday.ceil(d);
++  return pad(utcThursday.count(utcYear(d), d) + (utcYear(d).getUTCDay() === 4), p, 2);
++}
++
++function formatUTCWeekdayNumberSunday(d) {
++  return d.getUTCDay();
++}
++
++function formatUTCWeekNumberMonday(d, p) {
++  return pad(utcMonday.count(utcYear(d), d), p, 2);
++}
++
++function formatUTCYear(d, p) {
++  return pad(d.getUTCFullYear() % 100, p, 2);
++}
++
++function formatUTCFullYear(d, p) {
++  return pad(d.getUTCFullYear() % 10000, p, 4);
++}
++
++function formatUTCZone() {
++  return "+0000";
++}
++
++function formatLiteralPercent() {
++  return "%";
++}
++
++function formatUnixTimestamp(d) {
++  return +d;
++}
++
++function formatUnixTimestampSeconds(d) {
++  return Math.floor(+d / 1000);
++}
++
++var locale$1;
++
++defaultLocale$1({
++  dateTime: "%x, %X",
++  date: "%-m/%-d/%Y",
++  time: "%-I:%M:%S %p",
++  periods: ["AM", "PM"],
++  days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
++  shortDays: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
++  months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
++  shortMonths: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
++});
++
++function defaultLocale$1(definition) {
++  locale$1 = formatLocale$1(definition);
++  exports.timeFormat = locale$1.format;
++  exports.timeParse = locale$1.parse;
++  exports.utcFormat = locale$1.utcFormat;
++  exports.utcParse = locale$1.utcParse;
++  return locale$1;
++}
++
++var isoSpecifier = "%Y-%m-%dT%H:%M:%S.%LZ";
++
++function formatIsoNative(date) {
++  return date.toISOString();
++}
++
++var formatIso = Date.prototype.toISOString
++    ? formatIsoNative
++    : exports.utcFormat(isoSpecifier);
++
++function parseIsoNative(string) {
++  var date = new Date(string);
++  return isNaN(date) ? null : date;
++}
++
++var parseIso = +new Date("2000-01-01T00:00:00.000Z")
++    ? parseIsoNative
++    : exports.utcParse(isoSpecifier);
++
++var durationSecond$1 = 1000,
++    durationMinute$1 = durationSecond$1 * 60,
++    durationHour$1 = durationMinute$1 * 60,
++    durationDay$1 = durationHour$1 * 24,
++    durationWeek$1 = durationDay$1 * 7,
++    durationMonth = durationDay$1 * 30,
++    durationYear = durationDay$1 * 365;
++
++function date$1(t) {
++  return new Date(t);
++}
++
++function number$3(t) {
++  return t instanceof Date ? +t : +new Date(+t);
++}
++
++function calendar(year$$1, month$$1, week, day$$1, hour$$1, minute$$1, second$$1, millisecond$$1, format) {
++  var scale = continuous(deinterpolateLinear, reinterpolate),
++      invert = scale.invert,
++      domain = scale.domain;
++
++  var formatMillisecond = format(".%L"),
++      formatSecond = format(":%S"),
++      formatMinute = format("%I:%M"),
++      formatHour = format("%I %p"),
++      formatDay = format("%a %d"),
++      formatWeek = format("%b %d"),
++      formatMonth = format("%B"),
++      formatYear = format("%Y");
++
++  var tickIntervals = [
++    [second$$1,  1,      durationSecond$1],
++    [second$$1,  5,  5 * durationSecond$1],
++    [second$$1, 15, 15 * durationSecond$1],
++    [second$$1, 30, 30 * durationSecond$1],
++    [minute$$1,  1,      durationMinute$1],
++    [minute$$1,  5,  5 * durationMinute$1],
++    [minute$$1, 15, 15 * durationMinute$1],
++    [minute$$1, 30, 30 * durationMinute$1],
++    [  hour$$1,  1,      durationHour$1  ],
++    [  hour$$1,  3,  3 * durationHour$1  ],
++    [  hour$$1,  6,  6 * durationHour$1  ],
++    [  hour$$1, 12, 12 * durationHour$1  ],
++    [   day$$1,  1,      durationDay$1   ],
++    [   day$$1,  2,  2 * durationDay$1   ],
++    [  week,  1,      durationWeek$1  ],
++    [ month$$1,  1,      durationMonth ],
++    [ month$$1,  3,  3 * durationMonth ],
++    [  year$$1,  1,      durationYear  ]
++  ];
++
++  function tickFormat(date$$1) {
++    return (second$$1(date$$1) < date$$1 ? formatMillisecond
++        : minute$$1(date$$1) < date$$1 ? formatSecond
++        : hour$$1(date$$1) < date$$1 ? formatMinute
++        : day$$1(date$$1) < date$$1 ? formatHour
++        : month$$1(date$$1) < date$$1 ? (week(date$$1) < date$$1 ? formatDay : formatWeek)
++        : year$$1(date$$1) < date$$1 ? formatMonth
++        : formatYear)(date$$1);
++  }
++
++  function tickInterval(interval, start, stop, step) {
++    if (interval == null) interval = 10;
++
++    // If a desired tick count is specified, pick a reasonable tick interval
++    // based on the extent of the domain and a rough estimate of tick size.
++    // Otherwise, assume interval is already a time interval and use it.
++    if (typeof interval === "number") {
++      var target = Math.abs(stop - start) / interval,
++          i = bisector(function(i) { return i[2]; }).right(tickIntervals, target);
++      if (i === tickIntervals.length) {
++        step = tickStep(start / durationYear, stop / durationYear, interval);
++        interval = year$$1;
++      } else if (i) {
++        i = tickIntervals[target / tickIntervals[i - 1][2] < tickIntervals[i][2] / target ? i - 1 : i];
++        step = i[1];
++        interval = i[0];
++      } else {
++        step = Math.max(tickStep(start, stop, interval), 1);
++        interval = millisecond$$1;
++      }
++    }
++
++    return step == null ? interval : interval.every(step);
++  }
++
++  scale.invert = function(y) {
++    return new Date(invert(y));
++  };
++
++  scale.domain = function(_) {
++    return arguments.length ? domain(map$2.call(_, number$3)) : domain().map(date$1);
++  };
++
++  scale.ticks = function(interval, step) {
++    var d = domain(),
++        t0 = d[0],
++        t1 = d[d.length - 1],
++        r = t1 < t0,
++        t;
++    if (r) t = t0, t0 = t1, t1 = t;
++    t = tickInterval(interval, t0, t1, step);
++    t = t ? t.range(t0, t1 + 1) : []; // inclusive stop
++    return r ? t.reverse() : t;
++  };
++
++  scale.tickFormat = function(count, specifier) {
++    return specifier == null ? tickFormat : format(specifier);
++  };
++
++  scale.nice = function(interval, step) {
++    var d = domain();
++    return (interval = tickInterval(interval, d[0], d[d.length - 1], step))
++        ? domain(nice(d, interval))
++        : scale;
++  };
++
++  scale.copy = function() {
++    return copy(scale, calendar(year$$1, month$$1, week, day$$1, hour$$1, minute$$1, second$$1, millisecond$$1, format));
++  };
++
++  return scale;
++}
++
++function time() {
++  return calendar(year, month, sunday, day, hour, minute, second, millisecond, exports.timeFormat).domain([new Date(2000, 0, 1), new Date(2000, 0, 2)]);
++}
++
++function utcTime() {
++  return calendar(utcYear, utcMonth, utcSunday, utcDay, utcHour, utcMinute, second, millisecond, exports.utcFormat).domain([Date.UTC(2000, 0, 1), Date.UTC(2000, 0, 2)]);
++}
++
++function sequential(interpolator) {
++  var x0 = 0,
++      x1 = 1,
++      k10 = 1,
++      clamp = false;
++
++  function scale(x) {
++    var t = (x - x0) * k10;
++    return interpolator(clamp ? Math.max(0, Math.min(1, t)) : t);
++  }
++
++  scale.domain = function(_) {
++    return arguments.length ? (x0 = +_[0], x1 = +_[1], k10 = x0 === x1 ? 0 : 1 / (x1 - x0), scale) : [x0, x1];
++  };
++
++  scale.clamp = function(_) {
++    return arguments.length ? (clamp = !!_, scale) : clamp;
++  };
++
++  scale.interpolator = function(_) {
++    return arguments.length ? (interpolator = _, scale) : interpolator;
++  };
++
++  scale.copy = function() {
++    return sequential(interpolator).domain([x0, x1]).clamp(clamp);
++  };
++
++  return linearish(scale);
++}
++
++function diverging(interpolator) {
++  var x0 = 0,
++      x1 = 0.5,
++      x2 = 1,
++      k10 = 1,
++      k21 = 1,
++      clamp = false;
++
++  function scale(x) {
++    var t = 0.5 + ((x = +x) - x1) * (x < x1 ? k10 : k21);
++    return interpolator(clamp ? Math.max(0, Math.min(1, t)) : t);
++  }
++
++  scale.domain = function(_) {
++    return arguments.length ? (x0 = +_[0], x1 = +_[1], x2 = +_[2], k10 = x0 === x1 ? 0 : 0.5 / (x1 - x0), k21 = x1 === x2 ? 0 : 0.5 / (x2 - x1), scale) : [x0, x1, x2];
++  };
++
++  scale.clamp = function(_) {
++    return arguments.length ? (clamp = !!_, scale) : clamp;
++  };
++
++  scale.interpolator = function(_) {
++    return arguments.length ? (interpolator = _, scale) : interpolator;
++  };
++
++  scale.copy = function() {
++    return diverging(interpolator).domain([x0, x1, x2]).clamp(clamp);
++  };
++
++  return linearish(scale);
++}
++
++function colors(specifier) {
++  var n = specifier.length / 6 | 0, colors = new Array(n), i = 0;
++  while (i < n) colors[i] = "#" + specifier.slice(i * 6, ++i * 6);
++  return colors;
++}
++
++var category10 = colors("1f77b4ff7f0e2ca02cd627289467bd8c564be377c27f7f7fbcbd2217becf");
++
++var Accent = colors("7fc97fbeaed4fdc086ffff99386cb0f0027fbf5b17666666");
++
++var Dark2 = colors("1b9e77d95f027570b3e7298a66a61ee6ab02a6761d666666");
++
++var Paired = colors("a6cee31f78b4b2df8a33a02cfb9a99e31a1cfdbf6fff7f00cab2d66a3d9affff99b15928");
++
++var Pastel1 = colors("fbb4aeb3cde3ccebc5decbe4fed9a6ffffcce5d8bdfddaecf2f2f2");
++
++var Pastel2 = colors("b3e2cdfdcdaccbd5e8f4cae4e6f5c9fff2aef1e2cccccccc");
++
++var Set1 = colors("e41a1c377eb84daf4a984ea3ff7f00ffff33a65628f781bf999999");
++
++var Set2 = colors("66c2a5fc8d628da0cbe78ac3a6d854ffd92fe5c494b3b3b3");
++
++var Set3 = colors("8dd3c7ffffb3bebadafb807280b1d3fdb462b3de69fccde5d9d9d9bc80bdccebc5ffed6f");
++
++function ramp(scheme) {
++  return rgbBasis(scheme[scheme.length - 1]);
++}
++
++var scheme = new Array(3).concat(
++  "d8b365f5f5f55ab4ac",
++  "a6611adfc27d80cdc1018571",
++  "a6611adfc27df5f5f580cdc1018571",
++  "8c510ad8b365f6e8c3c7eae55ab4ac01665e",
++  "8c510ad8b365f6e8c3f5f5f5c7eae55ab4ac01665e",
++  "8c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e",
++  "8c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e",
++  "5430058c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e003c30",
++  "5430058c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e003c30"
++).map(colors);
++
++var BrBG = ramp(scheme);
++
++var scheme$1 = new Array(3).concat(
++  "af8dc3f7f7f77fbf7b",
++  "7b3294c2a5cfa6dba0008837",
++  "7b3294c2a5cff7f7f7a6dba0008837",
++  "762a83af8dc3e7d4e8d9f0d37fbf7b1b7837",
++  "762a83af8dc3e7d4e8f7f7f7d9f0d37fbf7b1b7837",
++  "762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b7837",
++  "762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b7837",
++  "40004b762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b783700441b",
++  "40004b762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b783700441b"
++).map(colors);
++
++var PRGn = ramp(scheme$1);
++
++var scheme$2 = new Array(3).concat(
++  "e9a3c9f7f7f7a1d76a",
++  "d01c8bf1b6dab8e1864dac26",
++  "d01c8bf1b6daf7f7f7b8e1864dac26",
++  "c51b7de9a3c9fde0efe6f5d0a1d76a4d9221",
++  "c51b7de9a3c9fde0eff7f7f7e6f5d0a1d76a4d9221",
++  "c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221",
++  "c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221",
++  "8e0152c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221276419",
++  "8e0152c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221276419"
++).map(colors);
++
++var PiYG = ramp(scheme$2);
++
++var scheme$3 = new Array(3).concat(
++  "998ec3f7f7f7f1a340",
++  "5e3c99b2abd2fdb863e66101",
++  "5e3c99b2abd2f7f7f7fdb863e66101",
++  "542788998ec3d8daebfee0b6f1a340b35806",
++  "542788998ec3d8daebf7f7f7fee0b6f1a340b35806",
++  "5427888073acb2abd2d8daebfee0b6fdb863e08214b35806",
++  "5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b35806",
++  "2d004b5427888073acb2abd2d8daebfee0b6fdb863e08214b358067f3b08",
++  "2d004b5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b358067f3b08"
++).map(colors);
++
++var PuOr = ramp(scheme$3);
++
++var scheme$4 = new Array(3).concat(
++  "ef8a62f7f7f767a9cf",
++  "ca0020f4a58292c5de0571b0",
++  "ca0020f4a582f7f7f792c5de0571b0",
++  "b2182bef8a62fddbc7d1e5f067a9cf2166ac",
++  "b2182bef8a62fddbc7f7f7f7d1e5f067a9cf2166ac",
++  "b2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac",
++  "b2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac",
++  "67001fb2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac053061",
++  "67001fb2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac053061"
++).map(colors);
++
++var RdBu = ramp(scheme$4);
++
++var scheme$5 = new Array(3).concat(
++  "ef8a62ffffff999999",
++  "ca0020f4a582bababa404040",
++  "ca0020f4a582ffffffbababa404040",
++  "b2182bef8a62fddbc7e0e0e09999994d4d4d",
++  "b2182bef8a62fddbc7ffffffe0e0e09999994d4d4d",
++  "b2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d",
++  "b2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d",
++  "67001fb2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d1a1a1a",
++  "67001fb2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d1a1a1a"
++).map(colors);
++
++var RdGy = ramp(scheme$5);
++
++var scheme$6 = new Array(3).concat(
++  "fc8d59ffffbf91bfdb",
++  "d7191cfdae61abd9e92c7bb6",
++  "d7191cfdae61ffffbfabd9e92c7bb6",
++  "d73027fc8d59fee090e0f3f891bfdb4575b4",
++  "d73027fc8d59fee090ffffbfe0f3f891bfdb4575b4",
++  "d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4",
++  "d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4",
++  "a50026d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4313695",
++  "a50026d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4313695"
++).map(colors);
++
++var RdYlBu = ramp(scheme$6);
++
++var scheme$7 = new Array(3).concat(
++  "fc8d59ffffbf91cf60",
++  "d7191cfdae61a6d96a1a9641",
++  "d7191cfdae61ffffbfa6d96a1a9641",
++  "d73027fc8d59fee08bd9ef8b91cf601a9850",
++  "d73027fc8d59fee08bffffbfd9ef8b91cf601a9850",
++  "d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850",
++  "d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850",
++  "a50026d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850006837",
++  "a50026d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850006837"
++).map(colors);
++
++var RdYlGn = ramp(scheme$7);
++
++var scheme$8 = new Array(3).concat(
++  "fc8d59ffffbf99d594",
++  "d7191cfdae61abdda42b83ba",
++  "d7191cfdae61ffffbfabdda42b83ba",
++  "d53e4ffc8d59fee08be6f59899d5943288bd",
++  "d53e4ffc8d59fee08bffffbfe6f59899d5943288bd",
++  "d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd",
++  "d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd",
++  "9e0142d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd5e4fa2",
++  "9e0142d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd5e4fa2"
++).map(colors);
++
++var Spectral = ramp(scheme$8);
++
++var scheme$9 = new Array(3).concat(
++  "e5f5f999d8c92ca25f",
++  "edf8fbb2e2e266c2a4238b45",
++  "edf8fbb2e2e266c2a42ca25f006d2c",
++  "edf8fbccece699d8c966c2a42ca25f006d2c",
++  "edf8fbccece699d8c966c2a441ae76238b45005824",
++  "f7fcfde5f5f9ccece699d8c966c2a441ae76238b45005824",
++  "f7fcfde5f5f9ccece699d8c966c2a441ae76238b45006d2c00441b"
++).map(colors);
++
++var BuGn = ramp(scheme$9);
++
++var scheme$10 = new Array(3).concat(
++  "e0ecf49ebcda8856a7",
++  "edf8fbb3cde38c96c688419d",
++  "edf8fbb3cde38c96c68856a7810f7c",
++  "edf8fbbfd3e69ebcda8c96c68856a7810f7c",
++  "edf8fbbfd3e69ebcda8c96c68c6bb188419d6e016b",
++  "f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d6e016b",
++  "f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d810f7c4d004b"
++).map(colors);
++
++var BuPu = ramp(scheme$10);
++
++var scheme$11 = new Array(3).concat(
++  "e0f3dba8ddb543a2ca",
++  "f0f9e8bae4bc7bccc42b8cbe",
++  "f0f9e8bae4bc7bccc443a2ca0868ac",
++  "f0f9e8ccebc5a8ddb57bccc443a2ca0868ac",
++  "f0f9e8ccebc5a8ddb57bccc44eb3d32b8cbe08589e",
++  "f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe08589e",
++  "f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe0868ac084081"
++).map(colors);
++
++var GnBu = ramp(scheme$11);
++
++var scheme$12 = new Array(3).concat(
++  "fee8c8fdbb84e34a33",
++  "fef0d9fdcc8afc8d59d7301f",
++  "fef0d9fdcc8afc8d59e34a33b30000",
++  "fef0d9fdd49efdbb84fc8d59e34a33b30000",
++  "fef0d9fdd49efdbb84fc8d59ef6548d7301f990000",
++  "fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301f990000",
++  "fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301fb300007f0000"
++).map(colors);
++
++var OrRd = ramp(scheme$12);
++
++var scheme$13 = new Array(3).concat(
++  "ece2f0a6bddb1c9099",
++  "f6eff7bdc9e167a9cf02818a",
++  "f6eff7bdc9e167a9cf1c9099016c59",
++  "f6eff7d0d1e6a6bddb67a9cf1c9099016c59",
++  "f6eff7d0d1e6a6bddb67a9cf3690c002818a016450",
++  "fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016450",
++  "fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016c59014636"
++).map(colors);
++
++var PuBuGn = ramp(scheme$13);
++
++var scheme$14 = new Array(3).concat(
++  "ece7f2a6bddb2b8cbe",
++  "f1eef6bdc9e174a9cf0570b0",
++  "f1eef6bdc9e174a9cf2b8cbe045a8d",
++  "f1eef6d0d1e6a6bddb74a9cf2b8cbe045a8d",
++  "f1eef6d0d1e6a6bddb74a9cf3690c00570b0034e7b",
++  "fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0034e7b",
++  "fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0045a8d023858"
++).map(colors);
++
++var PuBu = ramp(scheme$14);
++
++var scheme$15 = new Array(3).concat(
++  "e7e1efc994c7dd1c77",
++  "f1eef6d7b5d8df65b0ce1256",
++  "f1eef6d7b5d8df65b0dd1c77980043",
++  "f1eef6d4b9dac994c7df65b0dd1c77980043",
++  "f1eef6d4b9dac994c7df65b0e7298ace125691003f",
++  "f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125691003f",
++  "f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125698004367001f"
++).map(colors);
++
++var PuRd = ramp(scheme$15);
++
++var scheme$16 = new Array(3).concat(
++  "fde0ddfa9fb5c51b8a",
++  "feebe2fbb4b9f768a1ae017e",
++  "feebe2fbb4b9f768a1c51b8a7a0177",
++  "feebe2fcc5c0fa9fb5f768a1c51b8a7a0177",
++  "feebe2fcc5c0fa9fb5f768a1dd3497ae017e7a0177",
++  "fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a0177",
++  "fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a017749006a"
++).map(colors);
++
++var RdPu = ramp(scheme$16);
++
++var scheme$17 = new Array(3).concat(
++  "edf8b17fcdbb2c7fb8",
++  "ffffcca1dab441b6c4225ea8",
++  "ffffcca1dab441b6c42c7fb8253494",
++  "ffffccc7e9b47fcdbb41b6c42c7fb8253494",
++  "ffffccc7e9b47fcdbb41b6c41d91c0225ea80c2c84",
++  "ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea80c2c84",
++  "ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea8253494081d58"
++).map(colors);
++
++var YlGnBu = ramp(scheme$17);
++
++var scheme$18 = new Array(3).concat(
++  "f7fcb9addd8e31a354",
++  "ffffccc2e69978c679238443",
++  "ffffccc2e69978c67931a354006837",
++  "ffffccd9f0a3addd8e78c67931a354006837",
++  "ffffccd9f0a3addd8e78c67941ab5d238443005a32",
++  "ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443005a32",
++  "ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443006837004529"
++).map(colors);
++
++var YlGn = ramp(scheme$18);
++
++var scheme$19 = new Array(3).concat(
++  "fff7bcfec44fd95f0e",
++  "ffffd4fed98efe9929cc4c02",
++  "ffffd4fed98efe9929d95f0e993404",
++  "ffffd4fee391fec44ffe9929d95f0e993404",
++  "ffffd4fee391fec44ffe9929ec7014cc4c028c2d04",
++  "ffffe5fff7bcfee391fec44ffe9929ec7014cc4c028c2d04",
++  "ffffe5fff7bcfee391fec44ffe9929ec7014cc4c02993404662506"
++).map(colors);
++
++var YlOrBr = ramp(scheme$19);
++
++var scheme$20 = new Array(3).concat(
++  "ffeda0feb24cf03b20",
++  "ffffb2fecc5cfd8d3ce31a1c",
++  "ffffb2fecc5cfd8d3cf03b20bd0026",
++  "ffffb2fed976feb24cfd8d3cf03b20bd0026",
++  "ffffb2fed976feb24cfd8d3cfc4e2ae31a1cb10026",
++  "ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cb10026",
++  "ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cbd0026800026"
++).map(colors);
++
++var YlOrRd = ramp(scheme$20);
++
++var scheme$21 = new Array(3).concat(
++  "deebf79ecae13182bd",
++  "eff3ffbdd7e76baed62171b5",
++  "eff3ffbdd7e76baed63182bd08519c",
++  "eff3ffc6dbef9ecae16baed63182bd08519c",
++  "eff3ffc6dbef9ecae16baed64292c62171b5084594",
++  "f7fbffdeebf7c6dbef9ecae16baed64292c62171b5084594",
++  "f7fbffdeebf7c6dbef9ecae16baed64292c62171b508519c08306b"
++).map(colors);
++
++var Blues = ramp(scheme$21);
++
++var scheme$22 = new Array(3).concat(
++  "e5f5e0a1d99b31a354",
++  "edf8e9bae4b374c476238b45",
++  "edf8e9bae4b374c47631a354006d2c",
++  "edf8e9c7e9c0a1d99b74c47631a354006d2c",
++  "edf8e9c7e9c0a1d99b74c47641ab5d238b45005a32",
++  "f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45005a32",
++  "f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45006d2c00441b"
++).map(colors);
++
++var Greens = ramp(scheme$22);
++
++var scheme$23 = new Array(3).concat(
++  "f0f0f0bdbdbd636363",
++  "f7f7f7cccccc969696525252",
++  "f7f7f7cccccc969696636363252525",
++  "f7f7f7d9d9d9bdbdbd969696636363252525",
++  "f7f7f7d9d9d9bdbdbd969696737373525252252525",
++  "fffffff0f0f0d9d9d9bdbdbd969696737373525252252525",
++  "fffffff0f0f0d9d9d9bdbdbd969696737373525252252525000000"
++).map(colors);
++
++var Greys = ramp(scheme$23);
++
++var scheme$24 = new Array(3).concat(
++  "efedf5bcbddc756bb1",
++  "f2f0f7cbc9e29e9ac86a51a3",
++  "f2f0f7cbc9e29e9ac8756bb154278f",
++  "f2f0f7dadaebbcbddc9e9ac8756bb154278f",
++  "f2f0f7dadaebbcbddc9e9ac8807dba6a51a34a1486",
++  "fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a34a1486",
++  "fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a354278f3f007d"
++).map(colors);
++
++var Purples = ramp(scheme$24);
++
++var scheme$25 = new Array(3).concat(
++  "fee0d2fc9272de2d26",
++  "fee5d9fcae91fb6a4acb181d",
++  "fee5d9fcae91fb6a4ade2d26a50f15",
++  "fee5d9fcbba1fc9272fb6a4ade2d26a50f15",
++  "fee5d9fcbba1fc9272fb6a4aef3b2ccb181d99000d",
++  "fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181d99000d",
++  "fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181da50f1567000d"
++).map(colors);
++
++var Reds = ramp(scheme$25);
++
++var scheme$26 = new Array(3).concat(
++  "fee6cefdae6be6550d",
++  "feeddefdbe85fd8d3cd94701",
++  "feeddefdbe85fd8d3ce6550da63603",
++  "feeddefdd0a2fdae6bfd8d3ce6550da63603",
++  "feeddefdd0a2fdae6bfd8d3cf16913d948018c2d04",
++  "fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d948018c2d04",
++  "fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d94801a636037f2704"
++).map(colors);
++
++var Oranges = ramp(scheme$26);
++
++var cubehelix$3 = cubehelixLong(cubehelix(300, 0.5, 0.0), cubehelix(-240, 0.5, 1.0));
++
++var warm = cubehelixLong(cubehelix(-100, 0.75, 0.35), cubehelix(80, 1.50, 0.8));
++
++var cool = cubehelixLong(cubehelix(260, 0.75, 0.35), cubehelix(80, 1.50, 0.8));
++
++var c = cubehelix();
++
++function rainbow(t) {
++  if (t < 0 || t > 1) t -= Math.floor(t);
++  var ts = Math.abs(t - 0.5);
++  c.h = 360 * t - 100;
++  c.s = 1.5 - 1.5 * ts;
++  c.l = 0.8 - 0.9 * ts;
++  return c + "";
++}
++
++var c$1 = rgb(),
++    pi_1_3 = Math.PI / 3,
++    pi_2_3 = Math.PI * 2 / 3;
++
++function sinebow(t) {
++  var x;
++  t = (0.5 - t) * Math.PI;
++  c$1.r = 255 * (x = Math.sin(t)) * x;
++  c$1.g = 255 * (x = Math.sin(t + pi_1_3)) * x;
++  c$1.b = 255 * (x = Math.sin(t + pi_2_3)) * x;
++  return c$1 + "";
++}
++
++function ramp$1(range) {
++  var n = range.length;
++  return function(t) {
++    return range[Math.max(0, Math.min(n - 1, Math.floor(t * n)))];
++  };
++}
++
++var viridis = ramp$1(colors("44015444025645045745055946075a46085c460a5d460b5e470d60470e6147106347116447136548146748166848176948186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f854240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f881fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad8128ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc743fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc8635ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21ad8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725"));
++
++var magma = ramp$1(colors("00000401000501010601010802010902020b02020d03030f03031204041405041606051806051a07061c08071e0907200a08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d34140e36150e38160f3b180f3d19103f1a10421c10441d11471e114920114b21114e22115024125325125527125829115a2a115c2c115f2d11612f116331116533106734106936106b38106c390f6e3b0f703d0f713f0f72400f74420f75440f764510774710784910784a10794c117a4e117b4f127b51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f601880621980641a80651a80671b80681c816a1c816b1d816d1d816e1e81701f81721f817320817521817621817822817922827b23827c23827e24828025828125818326818426818627818827818928818b29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f9c2e7f9e2f7fa02f7fa1307ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb3367ab5367ab73779b83779ba3878bc3978bd3977bf3a77c03a76c23b75c43c75c53c74c73d73c83e73ca3e72cc3f71cd4071cf4070d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476adc4869de4968df4a68e04c67e24d66e34e65e44f64e55064e75263e85362e95462ea5661eb5760ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675cf4695cf56b5cf66c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835ffb8560fb8761fc8961fc8a62fc8c63fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b6bfe9d6cfe9f6dfea16efea36ffea571fea772fea973feaa74feac76feae77feb078feb27afeb47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287fec488fec68afec88cfeca8dfecc8ffecd90fecf92fed194fed395fed597fed799fed89afdda9cfddc9efddea0fde0a1fde2a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fcf9bbfcfbbdfcfdbf"));
++
++var inferno = ramp$1(colors("00000401000501010601010802010a02020c02020e03021004031204031405041706041907051b08051d09061f0a07220b07240c08260d08290e092b10092d110a30120a32140b34150b37160b39180c3c190c3e1b0c411c0c431e0c451f0c48210c4a230c4c240c4f260c51280b53290b552b0b572d0b592f0a5b310a5c320a5e340a5f3609613809623909633b09643d09653e0966400a67420a68440a68450a69470b6a490b6a4a0c6b4c0c6b4d0d6c4f0d6c510e6c520e6d540f6d550f6d57106e59106e5a116e5c126e5d126e5f136e61136e62146e64156e65156e67166e69166e6a176e6c186e6d186e6f196e71196e721a6e741a6e751b6e771c6d781c6d7a1d6d7c1d6d7d1e6d7f1e6c801f6c82206c84206b85216b87216b88226a8a226a8c23698d23698f24699025689225689326679526679727669827669a28659b29649d29649f2a63a02a63a22b62a32c61a52c60a62d60a82e5fa92e5eab2f5ead305dae305cb0315bb1325ab3325ab43359b63458b73557b93556ba3655bc3754bd3853bf3952c03a51c13a50c33b4fc43c4ec63d4dc73e4cc83f4bca404acb4149cc4248ce4347cf4446d04545d24644d34743d44842d54a41d74b3fd84c3ed94d3dda4e3cdb503bdd513ade5238df5337e05536e15635e25734e35933e45a31e55c30e65d2fe75e2ee8602de9612bea632aeb6429eb6628ec6726ed6925ee6a24ef6c23ef6e21f06f20f1711ff1731df2741cf3761bf37819f47918f57b17f57d15f67e14f68013f78212f78410f8850ff8870ef8890cf98b0bf98c0af98e09fa9008fa9207fa9407fb9606fb9706fb9906fb9b06fb9d07fc9f07fca108fca309fca50afca60cfca80dfcaa0ffcac11fcae12fcb014fcb216fcb418fbb61afbb81dfbba1ffbbc21fbbe23fac026fac228fac42afac62df9c72ff9c932f9cb35f8cd37f8cf3af7d13df7d340f6d543f6d746f5d949f5db4cf4dd4ff4df53f4e156f3e35af3e55df2e661f2e865f2ea69f1ec6df1ed71f1ef75f1f179f2f27df2f482f3f586f3f68af4f88ef5f992f6fa96f8fb9af9fc9dfafda1fcffa4"));
++
++var plasma = ramp$1(colors("0d088710078813078916078a19068c1b068d1d068e20068f2206902406912605912805922a05932c05942e05952f059631059733059735049837049938049a3a049a3c049b3e049c3f049c41049d43039e44039e46039f48039f4903a04b03a14c02a14e02a25002a25102a35302a35502a45601a45801a45901a55b01a55c01a65e01a66001a66100a76300a76400a76600a76700a86900a86a00a86c00a86e00a86f00a87100a87201a87401a87501a87701a87801a87a02a87b02a87d03a87e03a88004a88104a78305a78405a78606a68707a68808a68a09a58b0aa58d0ba58e0ca48f0da4910ea3920fa39410a29511a19613a19814a099159f9a169f9c179e9d189d9e199da01a9ca11b9ba21d9aa31e9aa51f99a62098a72197a82296aa2395ab2494ac2694ad2793ae2892b02991b12a90b22b8fb32c8eb42e8db52f8cb6308bb7318ab83289ba3388bb3488bc3587bd3786be3885bf3984c03a83c13b82c23c81c33d80c43e7fc5407ec6417dc7427cc8437bc9447aca457acb4679cc4778cc4977cd4a76ce4b75cf4c74d04d73d14e72d24f71d35171d45270d5536fd5546ed6556dd7566cd8576bd9586ada5a6ada5b69db5c68dc5d67dd5e66de5f65de6164df6263e06363e16462e26561e26660e3685fe4695ee56a5de56b5de66c5ce76e5be76f5ae87059e97158e97257ea7457eb7556eb7655ec7754ed7953ed7a52ee7b51ef7c51ef7e50f07f4ff0804ef1814df1834cf2844bf3854bf3874af48849f48948f58b47f58c46f68d45f68f44f79044f79143f79342f89441f89540f9973ff9983ef99a3efa9b3dfa9c3cfa9e3bfb9f3afba139fba238fca338fca537fca636fca835fca934fdab33fdac33fdae32fdaf31fdb130fdb22ffdb42ffdb52efeb72dfeb82cfeba2cfebb2bfebd2afebe2afec029fdc229fdc328fdc527fdc627fdc827fdca26fdcb26fccd25fcce25fcd025fcd225fbd324fbd524fbd724fad824fada24f9dc24f9dd25f8df25f8e125f7e225f7e425f6e626f6e826f5e926f5eb27f4ed27f3ee27f3f027f2f227f1f426f1f525f0f724f0f921"));
++
++function constant$11(x) {
++  return function constant() {
++    return x;
++  };
++}
++
++var abs$1 = Math.abs;
++var atan2$1 = Math.atan2;
++var cos$2 = Math.cos;
++var max$2 = Math.max;
++var min$1 = Math.min;
++var sin$2 = Math.sin;
++var sqrt$2 = Math.sqrt;
++
++var epsilon$3 = 1e-12;
++var pi$4 = Math.PI;
++var halfPi$3 = pi$4 / 2;
++var tau$4 = 2 * pi$4;
++
++function acos$1(x) {
++  return x > 1 ? 0 : x < -1 ? pi$4 : Math.acos(x);
++}
++
++function asin$1(x) {
++  return x >= 1 ? halfPi$3 : x <= -1 ? -halfPi$3 : Math.asin(x);
++}
++
++function arcInnerRadius(d) {
++  return d.innerRadius;
++}
++
++function arcOuterRadius(d) {
++  return d.outerRadius;
++}
++
++function arcStartAngle(d) {
++  return d.startAngle;
++}
++
++function arcEndAngle(d) {
++  return d.endAngle;
++}
++
++function arcPadAngle(d) {
++  return d && d.padAngle; // Note: optional!
++}
++
++function intersect(x0, y0, x1, y1, x2, y2, x3, y3) {
++  var x10 = x1 - x0, y10 = y1 - y0,
++      x32 = x3 - x2, y32 = y3 - y2,
++      t = (x32 * (y0 - y2) - y32 * (x0 - x2)) / (y32 * x10 - x32 * y10);
++  return [x0 + t * x10, y0 + t * y10];
++}
++
++// Compute perpendicular offset line of length rc.
++// http://mathworld.wolfram.com/Circle-LineIntersection.html
++function cornerTangents(x0, y0, x1, y1, r1, rc, cw) {
++  var x01 = x0 - x1,
++      y01 = y0 - y1,
++      lo = (cw ? rc : -rc) / sqrt$2(x01 * x01 + y01 * y01),
++      ox = lo * y01,
++      oy = -lo * x01,
++      x11 = x0 + ox,
++      y11 = y0 + oy,
++      x10 = x1 + ox,
++      y10 = y1 + oy,
++      x00 = (x11 + x10) / 2,
++      y00 = (y11 + y10) / 2,
++      dx = x10 - x11,
++      dy = y10 - y11,
++      d2 = dx * dx + dy * dy,
++      r = r1 - rc,
++      D = x11 * y10 - x10 * y11,
++      d = (dy < 0 ? -1 : 1) * sqrt$2(max$2(0, r * r * d2 - D * D)),
++      cx0 = (D * dy - dx * d) / d2,
++      cy0 = (-D * dx - dy * d) / d2,
++      cx1 = (D * dy + dx * d) / d2,
++      cy1 = (-D * dx + dy * d) / d2,
++      dx0 = cx0 - x00,
++      dy0 = cy0 - y00,
++      dx1 = cx1 - x00,
++      dy1 = cy1 - y00;
++
++  // Pick the closer of the two intersection points.
++  // TODO Is there a faster way to determine which intersection to use?
++  if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) cx0 = cx1, cy0 = cy1;
++
++  return {
++    cx: cx0,
++    cy: cy0,
++    x01: -ox,
++    y01: -oy,
++    x11: cx0 * (r1 / r - 1),
++    y11: cy0 * (r1 / r - 1)
++  };
++}
++
++function arc() {
++  var innerRadius = arcInnerRadius,
++      outerRadius = arcOuterRadius,
++      cornerRadius = constant$11(0),
++      padRadius = null,
++      startAngle = arcStartAngle,
++      endAngle = arcEndAngle,
++      padAngle = arcPadAngle,
++      context = null;
++
++  function arc() {
++    var buffer,
++        r,
++        r0 = +innerRadius.apply(this, arguments),
++        r1 = +outerRadius.apply(this, arguments),
++        a0 = startAngle.apply(this, arguments) - halfPi$3,
++        a1 = endAngle.apply(this, arguments) - halfPi$3,
++        da = abs$1(a1 - a0),
++        cw = a1 > a0;
++
++    if (!context) context = buffer = path();
++
++    // Ensure that the outer radius is always larger than the inner radius.
++    if (r1 < r0) r = r1, r1 = r0, r0 = r;
++
++    // Is it a point?
++    if (!(r1 > epsilon$3)) context.moveTo(0, 0);
++
++    // Or is it a circle or annulus?
++    else if (da > tau$4 - epsilon$3) {
++      context.moveTo(r1 * cos$2(a0), r1 * sin$2(a0));
++      context.arc(0, 0, r1, a0, a1, !cw);
++      if (r0 > epsilon$3) {
++        context.moveTo(r0 * cos$2(a1), r0 * sin$2(a1));
++        context.arc(0, 0, r0, a1, a0, cw);
++      }
++    }
++
++    // Or is it a circular or annular sector?
++    else {
++      var a01 = a0,
++          a11 = a1,
++          a00 = a0,
++          a10 = a1,
++          da0 = da,
++          da1 = da,
++          ap = padAngle.apply(this, arguments) / 2,
++          rp = (ap > epsilon$3) && (padRadius ? +padRadius.apply(this, arguments) : sqrt$2(r0 * r0 + r1 * r1)),
++          rc = min$1(abs$1(r1 - r0) / 2, +cornerRadius.apply(this, arguments)),
++          rc0 = rc,
++          rc1 = rc,
++          t0,
++          t1;
++
++      // Apply padding? Note that since r1 ≥ r0, da1 ≥ da0.
++      if (rp > epsilon$3) {
++        var p0 = asin$1(rp / r0 * sin$2(ap)),
++            p1 = asin$1(rp / r1 * sin$2(ap));
++        if ((da0 -= p0 * 2) > epsilon$3) p0 *= (cw ? 1 : -1), a00 += p0, a10 -= p0;
++        else da0 = 0, a00 = a10 = (a0 + a1) / 2;
++        if ((da1 -= p1 * 2) > epsilon$3) p1 *= (cw ? 1 : -1), a01 += p1, a11 -= p1;
++        else da1 = 0, a01 = a11 = (a0 + a1) / 2;
++      }
++
++      var x01 = r1 * cos$2(a01),
++          y01 = r1 * sin$2(a01),
++          x10 = r0 * cos$2(a10),
++          y10 = r0 * sin$2(a10);
++
++      // Apply rounded corners?
++      if (rc > epsilon$3) {
++        var x11 = r1 * cos$2(a11),
++            y11 = r1 * sin$2(a11),
++            x00 = r0 * cos$2(a00),
++            y00 = r0 * sin$2(a00);
++
++        // Restrict the corner radius according to the sector angle.
++        if (da < pi$4) {
++          var oc = da0 > epsilon$3 ? intersect(x01, y01, x00, y00, x11, y11, x10, y10) : [x10, y10],
++              ax = x01 - oc[0],
++              ay = y01 - oc[1],
++              bx = x11 - oc[0],
++              by = y11 - oc[1],
++              kc = 1 / sin$2(acos$1((ax * bx + ay * by) / (sqrt$2(ax * ax + ay * ay) * sqrt$2(bx * bx + by * by))) / 2),
++              lc = sqrt$2(oc[0] * oc[0] + oc[1] * oc[1]);
++          rc0 = min$1(rc, (r0 - lc) / (kc - 1));
++          rc1 = min$1(rc, (r1 - lc) / (kc + 1));
++        }
++      }
++
++      // Is the sector collapsed to a line?
++      if (!(da1 > epsilon$3)) context.moveTo(x01, y01);
++
++      // Does the sector’s outer ring have rounded corners?
++      else if (rc1 > epsilon$3) {
++        t0 = cornerTangents(x00, y00, x01, y01, r1, rc1, cw);
++        t1 = cornerTangents(x11, y11, x10, y10, r1, rc1, cw);
++
++        context.moveTo(t0.cx + t0.x01, t0.cy + t0.y01);
++
++        // Have the corners merged?
++        if (rc1 < rc) context.arc(t0.cx, t0.cy, rc1, atan2$1(t0.y01, t0.x01), atan2$1(t1.y01, t1.x01), !cw);
++
++        // Otherwise, draw the two corners and the ring.
++        else {
++          context.arc(t0.cx, t0.cy, rc1, atan2$1(t0.y01, t0.x01), atan2$1(t0.y11, t0.x11), !cw);
++          context.arc(0, 0, r1, atan2$1(t0.cy + t0.y11, t0.cx + t0.x11), atan2$1(t1.cy + t1.y11, t1.cx + t1.x11), !cw);
++          context.arc(t1.cx, t1.cy, rc1, atan2$1(t1.y11, t1.x11), atan2$1(t1.y01, t1.x01), !cw);
++        }
++      }
++
++      // Or is the outer ring just a circular arc?
++      else context.moveTo(x01, y01), context.arc(0, 0, r1, a01, a11, !cw);
++
++      // Is there no inner ring, and it’s a circular sector?
++      // Or perhaps it’s an annular sector collapsed due to padding?
++      if (!(r0 > epsilon$3) || !(da0 > epsilon$3)) context.lineTo(x10, y10);
++
++      // Does the sector’s inner ring (or point) have rounded corners?
++      else if (rc0 > epsilon$3) {
++        t0 = cornerTangents(x10, y10, x11, y11, r0, -rc0, cw);
++        t1 = cornerTangents(x01, y01, x00, y00, r0, -rc0, cw);
++
++        context.lineTo(t0.cx + t0.x01, t0.cy + t0.y01);
++
++        // Have the corners merged?
++        if (rc0 < rc) context.arc(t0.cx, t0.cy, rc0, atan2$1(t0.y01, t0.x01), atan2$1(t1.y01, t1.x01), !cw);
++
++        // Otherwise, draw the two corners and the ring.
++        else {
++          context.arc(t0.cx, t0.cy, rc0, atan2$1(t0.y01, t0.x01), atan2$1(t0.y11, t0.x11), !cw);
++          context.arc(0, 0, r0, atan2$1(t0.cy + t0.y11, t0.cx + t0.x11), atan2$1(t1.cy + t1.y11, t1.cx + t1.x11), cw);
++          context.arc(t1.cx, t1.cy, rc0, atan2$1(t1.y11, t1.x11), atan2$1(t1.y01, t1.x01), !cw);
++        }
++      }
++
++      // Or is the inner ring just a circular arc?
++      else context.arc(0, 0, r0, a10, a00, cw);
++    }
++
++    context.closePath();
++
++    if (buffer) return context = null, buffer + "" || null;
++  }
++
++  arc.centroid = function() {
++    var r = (+innerRadius.apply(this, arguments) + +outerRadius.apply(this, arguments)) / 2,
++        a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - pi$4 / 2;
++    return [cos$2(a) * r, sin$2(a) * r];
++  };
++
++  arc.innerRadius = function(_) {
++    return arguments.length ? (innerRadius = typeof _ === "function" ? _ : constant$11(+_), arc) : innerRadius;
++  };
++
++  arc.outerRadius = function(_) {
++    return arguments.length ? (outerRadius = typeof _ === "function" ? _ : constant$11(+_), arc) : outerRadius;
++  };
++
++  arc.cornerRadius = function(_) {
++    return arguments.length ? (cornerRadius = typeof _ === "function" ? _ : constant$11(+_), arc) : cornerRadius;
++  };
++
++  arc.padRadius = function(_) {
++    return arguments.length ? (padRadius = _ == null ? null : typeof _ === "function" ? _ : constant$11(+_), arc) : padRadius;
++  };
++
++  arc.startAngle = function(_) {
++    return arguments.length ? (startAngle = typeof _ === "function" ? _ : constant$11(+_), arc) : startAngle;
++  };
++
++  arc.endAngle = function(_) {
++    return arguments.length ? (endAngle = typeof _ === "function" ? _ : constant$11(+_), arc) : endAngle;
++  };
++
++  arc.padAngle = function(_) {
++    return arguments.length ? (padAngle = typeof _ === "function" ? _ : constant$11(+_), arc) : padAngle;
++  };
++
++  arc.context = function(_) {
++    return arguments.length ? (context = _ == null ? null : _, arc) : context;
++  };
++
++  return arc;
++}
++
++function Linear(context) {
++  this._context = context;
++}
++
++Linear.prototype = {
++  areaStart: function() {
++    this._line = 0;
++  },
++  areaEnd: function() {
++    this._line = NaN;
++  },
++  lineStart: function() {
++    this._point = 0;
++  },
++  lineEnd: function() {
++    if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
++    this._line = 1 - this._line;
++  },
++  point: function(x, y) {
++    x = +x, y = +y;
++    switch (this._point) {
++      case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
++      case 1: this._point = 2; // proceed
++      default: this._context.lineTo(x, y); break;
++    }
++  }
++};
++
++function curveLinear(context) {
++  return new Linear(context);
++}
++
++function x$3(p) {
++  return p[0];
++}
++
++function y$3(p) {
++  return p[1];
++}
++
++function line() {
++  var x$$1 = x$3,
++      y$$1 = y$3,
++      defined = constant$11(true),
++      context = null,
++      curve = curveLinear,
++      output = null;
++
++  function line(data) {
++    var i,
++        n = data.length,
++        d,
++        defined0 = false,
++        buffer;
++
++    if (context == null) output = curve(buffer = path());
++
++    for (i = 0; i <= n; ++i) {
++      if (!(i < n && defined(d = data[i], i, data)) === defined0) {
++        if (defined0 = !defined0) output.lineStart();
++        else output.lineEnd();
++      }
++      if (defined0) output.point(+x$$1(d, i, data), +y$$1(d, i, data));
++    }
++
++    if (buffer) return output = null, buffer + "" || null;
++  }
++
++  line.x = function(_) {
++    return arguments.length ? (x$$1 = typeof _ === "function" ? _ : constant$11(+_), line) : x$$1;
++  };
++
++  line.y = function(_) {
++    return arguments.length ? (y$$1 = typeof _ === "function" ? _ : constant$11(+_), line) : y$$1;
++  };
++
++  line.defined = function(_) {
++    return arguments.length ? (defined = typeof _ === "function" ? _ : constant$11(!!_), line) : defined;
++  };
++
++  line.curve = function(_) {
++    return arguments.length ? (curve = _, context != null && (output = curve(context)), line) : curve;
++  };
++
++  line.context = function(_) {
++    return arguments.length ? (_ == null ? context = output = null : output = curve(context = _), line) : context;
++  };
++
++  return line;
++}
++
++function area$3() {
++  var x0 = x$3,
++      x1 = null,
++      y0 = constant$11(0),
++      y1 = y$3,
++      defined = constant$11(true),
++      context = null,
++      curve = curveLinear,
++      output = null;
++
++  function area(data) {
++    var i,
++        j,
++        k,
++        n = data.length,
++        d,
++        defined0 = false,
++        buffer,
++        x0z = new Array(n),
++        y0z = new Array(n);
++
++    if (context == null) output = curve(buffer = path());
++
++    for (i = 0; i <= n; ++i) {
++      if (!(i < n && defined(d = data[i], i, data)) === defined0) {
++        if (defined0 = !defined0) {
++          j = i;
++          output.areaStart();
++          output.lineStart();
++        } else {
++          output.lineEnd();
++          output.lineStart();
++          for (k = i - 1; k >= j; --k) {
++            output.point(x0z[k], y0z[k]);
++          }
++          output.lineEnd();
++          output.areaEnd();
++        }
++      }
++      if (defined0) {
++        x0z[i] = +x0(d, i, data), y0z[i] = +y0(d, i, data);
++        output.point(x1 ? +x1(d, i, data) : x0z[i], y1 ? +y1(d, i, data) : y0z[i]);
++      }
++    }
++
++    if (buffer) return output = null, buffer + "" || null;
++  }
++
++  function arealine() {
++    return line().defined(defined).curve(curve).context(context);
++  }
++
++  area.x = function(_) {
++    return arguments.length ? (x0 = typeof _ === "function" ? _ : constant$11(+_), x1 = null, area) : x0;
++  };
++
++  area.x0 = function(_) {
++    return arguments.length ? (x0 = typeof _ === "function" ? _ : constant$11(+_), area) : x0;
++  };
++
++  area.x1 = function(_) {
++    return arguments.length ? (x1 = _ == null ? null : typeof _ === "function" ? _ : constant$11(+_), area) : x1;
++  };
++
++  area.y = function(_) {
++    return arguments.length ? (y0 = typeof _ === "function" ? _ : constant$11(+_), y1 = null, area) : y0;
++  };
++
++  area.y0 = function(_) {
++    return arguments.length ? (y0 = typeof _ === "function" ? _ : constant$11(+_), area) : y0;
++  };
++
++  area.y1 = function(_) {
++    return arguments.length ? (y1 = _ == null ? null : typeof _ === "function" ? _ : constant$11(+_), area) : y1;
++  };
++
++  area.lineX0 =
++  area.lineY0 = function() {
++    return arealine().x(x0).y(y0);
++  };
++
++  area.lineY1 = function() {
++    return arealine().x(x0).y(y1);
++  };
++
++  area.lineX1 = function() {
++    return arealine().x(x1).y(y0);
++  };
++
++  area.defined = function(_) {
++    return arguments.length ? (defined = typeof _ === "function" ? _ : constant$11(!!_), area) : defined;
++  };
++
++  area.curve = function(_) {
++    return arguments.length ? (curve = _, context != null && (output = curve(context)), area) : curve;
++  };
++
++  area.context = function(_) {
++    return arguments.length ? (_ == null ? context = output = null : output = curve(context = _), area) : context;
++  };
++
++  return area;
++}
++
++function descending$1(a, b) {
++  return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
++}
++
++function identity$7(d) {
++  return d;
++}
++
++function pie() {
++  var value = identity$7,
++      sortValues = descending$1,
++      sort = null,
++      startAngle = constant$11(0),
++      endAngle = constant$11(tau$4),
++      padAngle = constant$11(0);
++
++  function pie(data) {
++    var i,
++        n = data.length,
++        j,
++        k,
++        sum = 0,
++        index = new Array(n),
++        arcs = new Array(n),
++        a0 = +startAngle.apply(this, arguments),
++        da = Math.min(tau$4, Math.max(-tau$4, endAngle.apply(this, arguments) - a0)),
++        a1,
++        p = Math.min(Math.abs(da) / n, padAngle.apply(this, arguments)),
++        pa = p * (da < 0 ? -1 : 1),
++        v;
++
++    for (i = 0; i < n; ++i) {
++      if ((v = arcs[index[i] = i] = +value(data[i], i, data)) > 0) {
++        sum += v;
++      }
++    }
++
++    // Optionally sort the arcs by previously-computed values or by data.
++    if (sortValues != null) index.sort(function(i, j) { return sortValues(arcs[i], arcs[j]); });
++    else if (sort != null) index.sort(function(i, j) { return sort(data[i], data[j]); });
++
++    // Compute the arcs! They are stored in the original data's order.
++    for (i = 0, k = sum ? (da - n * pa) / sum : 0; i < n; ++i, a0 = a1) {
++      j = index[i], v = arcs[j], a1 = a0 + (v > 0 ? v * k : 0) + pa, arcs[j] = {
++        data: data[j],
++        index: i,
++        value: v,
++        startAngle: a0,
++        endAngle: a1,
++        padAngle: p
++      };
++    }
++
++    return arcs;
++  }
++
++  pie.value = function(_) {
++    return arguments.length ? (value = typeof _ === "function" ? _ : constant$11(+_), pie) : value;
++  };
++
++  pie.sortValues = function(_) {
++    return arguments.length ? (sortValues = _, sort = null, pie) : sortValues;
++  };
++
++  pie.sort = function(_) {
++    return arguments.length ? (sort = _, sortValues = null, pie) : sort;
++  };
++
++  pie.startAngle = function(_) {
++    return arguments.length ? (startAngle = typeof _ === "function" ? _ : constant$11(+_), pie) : startAngle;
++  };
++
++  pie.endAngle = function(_) {
++    return arguments.length ? (endAngle = typeof _ === "function" ? _ : constant$11(+_), pie) : endAngle;
++  };
++
++  pie.padAngle = function(_) {
++    return arguments.length ? (padAngle = typeof _ === "function" ? _ : constant$11(+_), pie) : padAngle;
++  };
++
++  return pie;
++}
++
++var curveRadialLinear = curveRadial(curveLinear);
++
++function Radial(curve) {
++  this._curve = curve;
++}
++
++Radial.prototype = {
++  areaStart: function() {
++    this._curve.areaStart();
++  },
++  areaEnd: function() {
++    this._curve.areaEnd();
++  },
++  lineStart: function() {
++    this._curve.lineStart();
++  },
++  lineEnd: function() {
++    this._curve.lineEnd();
++  },
++  point: function(a, r) {
++    this._curve.point(r * Math.sin(a), r * -Math.cos(a));
++  }
++};
++
++function curveRadial(curve) {
++
++  function radial(context) {
++    return new Radial(curve(context));
++  }
++
++  radial._curve = curve;
++
++  return radial;
++}
++
++function lineRadial(l) {
++  var c = l.curve;
++
++  l.angle = l.x, delete l.x;
++  l.radius = l.y, delete l.y;
++
++  l.curve = function(_) {
++    return arguments.length ? c(curveRadial(_)) : c()._curve;
++  };
++
++  return l;
++}
++
++function lineRadial$1() {
++  return lineRadial(line().curve(curveRadialLinear));
++}
++
++function areaRadial() {
++  var a = area$3().curve(curveRadialLinear),
++      c = a.curve,
++      x0 = a.lineX0,
++      x1 = a.lineX1,
++      y0 = a.lineY0,
++      y1 = a.lineY1;
++
++  a.angle = a.x, delete a.x;
++  a.startAngle = a.x0, delete a.x0;
++  a.endAngle = a.x1, delete a.x1;
++  a.radius = a.y, delete a.y;
++  a.innerRadius = a.y0, delete a.y0;
++  a.outerRadius = a.y1, delete a.y1;
++  a.lineStartAngle = function() { return lineRadial(x0()); }, delete a.lineX0;
++  a.lineEndAngle = function() { return lineRadial(x1()); }, delete a.lineX1;
++  a.lineInnerRadius = function() { return lineRadial(y0()); }, delete a.lineY0;
++  a.lineOuterRadius = function() { return lineRadial(y1()); }, delete a.lineY1;
++
++  a.curve = function(_) {
++    return arguments.length ? c(curveRadial(_)) : c()._curve;
++  };
++
++  return a;
++}
++
++function pointRadial(x, y) {
++  return [(y = +y) * Math.cos(x -= Math.PI / 2), y * Math.sin(x)];
++}
++
++var slice$6 = Array.prototype.slice;
++
++function linkSource(d) {
++  return d.source;
++}
++
++function linkTarget(d) {
++  return d.target;
++}
++
++function link$2(curve) {
++  var source = linkSource,
++      target = linkTarget,
++      x$$1 = x$3,
++      y$$1 = y$3,
++      context = null;
++
++  function link() {
++    var buffer, argv = slice$6.call(arguments), s = source.apply(this, argv), t = target.apply(this, argv);
++    if (!context) context = buffer = path();
++    curve(context, +x$$1.apply(this, (argv[0] = s, argv)), +y$$1.apply(this, argv), +x$$1.apply(this, (argv[0] = t, argv)), +y$$1.apply(this, argv));
++    if (buffer) return context = null, buffer + "" || null;
++  }
++
++  link.source = function(_) {
++    return arguments.length ? (source = _, link) : source;
++  };
++
++  link.target = function(_) {
++    return arguments.length ? (target = _, link) : target;
++  };
++
++  link.x = function(_) {
++    return arguments.length ? (x$$1 = typeof _ === "function" ? _ : constant$11(+_), link) : x$$1;
++  };
++
++  link.y = function(_) {
++    return arguments.length ? (y$$1 = typeof _ === "function" ? _ : constant$11(+_), link) : y$$1;
++  };
++
++  link.context = function(_) {
++    return arguments.length ? (context = _ == null ? null : _, link) : context;
++  };
++
++  return link;
++}
++
++function curveHorizontal(context, x0, y0, x1, y1) {
++  context.moveTo(x0, y0);
++  context.bezierCurveTo(x0 = (x0 + x1) / 2, y0, x0, y1, x1, y1);
++}
++
++function curveVertical(context, x0, y0, x1, y1) {
++  context.moveTo(x0, y0);
++  context.bezierCurveTo(x0, y0 = (y0 + y1) / 2, x1, y0, x1, y1);
++}
++
++function curveRadial$1(context, x0, y0, x1, y1) {
++  var p0 = pointRadial(x0, y0),
++      p1 = pointRadial(x0, y0 = (y0 + y1) / 2),
++      p2 = pointRadial(x1, y0),
++      p3 = pointRadial(x1, y1);
++  context.moveTo(p0[0], p0[1]);
++  context.bezierCurveTo(p1[0], p1[1], p2[0], p2[1], p3[0], p3[1]);
++}
++
++function linkHorizontal() {
++  return link$2(curveHorizontal);
++}
++
++function linkVertical() {
++  return link$2(curveVertical);
++}
++
++function linkRadial() {
++  var l = link$2(curveRadial$1);
++  l.angle = l.x, delete l.x;
++  l.radius = l.y, delete l.y;
++  return l;
++}
++
++var circle$2 = {
++  draw: function(context, size) {
++    var r = Math.sqrt(size / pi$4);
++    context.moveTo(r, 0);
++    context.arc(0, 0, r, 0, tau$4);
++  }
++};
++
++var cross$2 = {
++  draw: function(context, size) {
++    var r = Math.sqrt(size / 5) / 2;
++    context.moveTo(-3 * r, -r);
++    context.lineTo(-r, -r);
++    context.lineTo(-r, -3 * r);
++    context.lineTo(r, -3 * r);
++    context.lineTo(r, -r);
++    context.lineTo(3 * r, -r);
++    context.lineTo(3 * r, r);
++    context.lineTo(r, r);
++    context.lineTo(r, 3 * r);
++    context.lineTo(-r, 3 * r);
++    context.lineTo(-r, r);
++    context.lineTo(-3 * r, r);
++    context.closePath();
++  }
++};
++
++var tan30 = Math.sqrt(1 / 3),
++    tan30_2 = tan30 * 2;
++
++var diamond = {
++  draw: function(context, size) {
++    var y = Math.sqrt(size / tan30_2),
++        x = y * tan30;
++    context.moveTo(0, -y);
++    context.lineTo(x, 0);
++    context.lineTo(0, y);
++    context.lineTo(-x, 0);
++    context.closePath();
++  }
++};
++
++var ka = 0.89081309152928522810,
++    kr = Math.sin(pi$4 / 10) / Math.sin(7 * pi$4 / 10),
++    kx = Math.sin(tau$4 / 10) * kr,
++    ky = -Math.cos(tau$4 / 10) * kr;
++
++var star = {
++  draw: function(context, size) {
++    var r = Math.sqrt(size * ka),
++        x = kx * r,
++        y = ky * r;
++    context.moveTo(0, -r);
++    context.lineTo(x, y);
++    for (var i = 1; i < 5; ++i) {
++      var a = tau$4 * i / 5,
++          c = Math.cos(a),
++          s = Math.sin(a);
++      context.lineTo(s * r, -c * r);
++      context.lineTo(c * x - s * y, s * x + c * y);
++    }
++    context.closePath();
++  }
++};
++
++var square = {
++  draw: function(context, size) {
++    var w = Math.sqrt(size),
++        x = -w / 2;
++    context.rect(x, x, w, w);
++  }
++};
++
++var sqrt3 = Math.sqrt(3);
++
++var triangle = {
++  draw: function(context, size) {
++    var y = -Math.sqrt(size / (sqrt3 * 3));
++    context.moveTo(0, y * 2);
++    context.lineTo(-sqrt3 * y, -y);
++    context.lineTo(sqrt3 * y, -y);
++    context.closePath();
++  }
++};
++
++var c$2 = -0.5,
++    s = Math.sqrt(3) / 2,
++    k = 1 / Math.sqrt(12),
++    a = (k / 2 + 1) * 3;
++
++var wye = {
++  draw: function(context, size) {
++    var r = Math.sqrt(size / a),
++        x0 = r / 2,
++        y0 = r * k,
++        x1 = x0,
++        y1 = r * k + r,
++        x2 = -x1,
++        y2 = y1;
++    context.moveTo(x0, y0);
++    context.lineTo(x1, y1);
++    context.lineTo(x2, y2);
++    context.lineTo(c$2 * x0 - s * y0, s * x0 + c$2 * y0);
++    context.lineTo(c$2 * x1 - s * y1, s * x1 + c$2 * y1);
++    context.lineTo(c$2 * x2 - s * y2, s * x2 + c$2 * y2);
++    context.lineTo(c$2 * x0 + s * y0, c$2 * y0 - s * x0);
++    context.lineTo(c$2 * x1 + s * y1, c$2 * y1 - s * x1);
++    context.lineTo(c$2 * x2 + s * y2, c$2 * y2 - s * x2);
++    context.closePath();
++  }
++};
++
++var symbols = [
++  circle$2,
++  cross$2,
++  diamond,
++  square,
++  star,
++  triangle,
++  wye
++];
++
++function symbol() {
++  var type = constant$11(circle$2),
++      size = constant$11(64),
++      context = null;
++
++  function symbol() {
++    var buffer;
++    if (!context) context = buffer = path();
++    type.apply(this, arguments).draw(context, +size.apply(this, arguments));
++    if (buffer) return context = null, buffer + "" || null;
++  }
++
++  symbol.type = function(_) {
++    return arguments.length ? (type = typeof _ === "function" ? _ : constant$11(_), symbol) : type;
++  };
++
++  symbol.size = function(_) {
++    return arguments.length ? (size = typeof _ === "function" ? _ : constant$11(+_), symbol) : size;
++  };
++
++  symbol.context = function(_) {
++    return arguments.length ? (context = _ == null ? null : _, symbol) : context;
++  };
++
++  return symbol;
++}
++
++function noop$3() {}
++
++function point$2(that, x, y) {
++  that._context.bezierCurveTo(
++    (2 * that._x0 + that._x1) / 3,
++    (2 * that._y0 + that._y1) / 3,
++    (that._x0 + 2 * that._x1) / 3,
++    (that._y0 + 2 * that._y1) / 3,
++    (that._x0 + 4 * that._x1 + x) / 6,
++    (that._y0 + 4 * that._y1 + y) / 6
++  );
++}
++
++function Basis(context) {
++  this._context = context;
++}
++
++Basis.prototype = {
++  areaStart: function() {
++    this._line = 0;
++  },
++  areaEnd: function() {
++    this._line = NaN;
++  },
++  lineStart: function() {
++    this._x0 = this._x1 =
++    this._y0 = this._y1 = NaN;
++    this._point = 0;
++  },
++  lineEnd: function() {
++    switch (this._point) {
++      case 3: point$2(this, this._x1, this._y1); // proceed
++      case 2: this._context.lineTo(this._x1, this._y1); break;
++    }
++    if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
++    this._line = 1 - this._line;
++  },
++  point: function(x, y) {
++    x = +x, y = +y;
++    switch (this._point) {
++      case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
++      case 1: this._point = 2; break;
++      case 2: this._point = 3; this._context.lineTo((5 * this._x0 + this._x1) / 6, (5 * this._y0 + this._y1) / 6); // proceed
++      default: point$2(this, x, y); break;
++    }
++    this._x0 = this._x1, this._x1 = x;
++    this._y0 = this._y1, this._y1 = y;
++  }
++};
++
++function basis$2(context) {
++  return new Basis(context);
++}
++
++function BasisClosed(context) {
++  this._context = context;
++}
++
++BasisClosed.prototype = {
++  areaStart: noop$3,
++  areaEnd: noop$3,
++  lineStart: function() {
++    this._x0 = this._x1 = this._x2 = this._x3 = this._x4 =
++    this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = NaN;
++    this._point = 0;
++  },
++  lineEnd: function() {
++    switch (this._point) {
++      case 1: {
++        this._context.moveTo(this._x2, this._y2);
++        this._context.closePath();
++        break;
++      }
++      case 2: {
++        this._context.moveTo((this._x2 + 2 * this._x3) / 3, (this._y2 + 2 * this._y3) / 3);
++        this._context.lineTo((this._x3 + 2 * this._x2) / 3, (this._y3 + 2 * this._y2) / 3);
++        this._context.closePath();
++        break;
++      }
++      case 3: {
++        this.point(this._x2, this._y2);
++        this.point(this._x3, this._y3);
++        this.point(this._x4, this._y4);
++        break;
++      }
++    }
++  },
++  point: function(x, y) {
++    x = +x, y = +y;
++    switch (this._point) {
++      case 0: this._point = 1; this._x2 = x, this._y2 = y; break;
++      case 1: this._point = 2; this._x3 = x, this._y3 = y; break;
++      case 2: this._point = 3; this._x4 = x, this._y4 = y; this._context.moveTo((this._x0 + 4 * this._x1 + x) / 6, (this._y0 + 4 * this._y1 + y) / 6); break;
++      default: point$2(this, x, y); break;
++    }
++    this._x0 = this._x1, this._x1 = x;
++    this._y0 = this._y1, this._y1 = y;
++  }
++};
++
++function basisClosed$1(context) {
++  return new BasisClosed(context);
++}
++
++function BasisOpen(context) {
++  this._context = context;
++}
++
++BasisOpen.prototype = {
++  areaStart: function() {
++    this._line = 0;
++  },
++  areaEnd: function() {
++    this._line = NaN;
++  },
++  lineStart: function() {
++    this._x0 = this._x1 =
++    this._y0 = this._y1 = NaN;
++    this._point = 0;
++  },
++  lineEnd: function() {
++    if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath();
++    this._line = 1 - this._line;
++  },
++  point: function(x, y) {
++    x = +x, y = +y;
++    switch (this._point) {
++      case 0: this._point = 1; break;
++      case 1: this._point = 2; break;
++      case 2: this._point = 3; var x0 = (this._x0 + 4 * this._x1 + x) / 6, y0 = (this._y0 + 4 * this._y1 + y) / 6; this._line ? this._context.lineTo(x0, y0) : this._context.moveTo(x0, y0); break;
++      case 3: this._point = 4; // proceed
++      default: point$2(this, x, y); break;
++    }
++    this._x0 = this._x1, this._x1 = x;
++    this._y0 = this._y1, this._y1 = y;
++  }
++};
++
++function basisOpen(context) {
++  return new BasisOpen(context);
++}
++
++function Bundle(context, beta) {
++  this._basis = new Basis(context);
++  this._beta = beta;
++}
++
++Bundle.prototype = {
++  lineStart: function() {
++    this._x = [];
++    this._y = [];
++    this._basis.lineStart();
++  },
++  lineEnd: function() {
++    var x = this._x,
++        y = this._y,
++        j = x.length - 1;
++
++    if (j > 0) {
++      var x0 = x[0],
++          y0 = y[0],
++          dx = x[j] - x0,
++          dy = y[j] - y0,
++          i = -1,
++          t;
++
++      while (++i <= j) {
++        t = i / j;
++        this._basis.point(
++          this._beta * x[i] + (1 - this._beta) * (x0 + t * dx),
++          this._beta * y[i] + (1 - this._beta) * (y0 + t * dy)
++        );
++      }
++    }
++
++    this._x = this._y = null;
++    this._basis.lineEnd();
++  },
++  point: function(x, y) {
++    this._x.push(+x);
++    this._y.push(+y);
++  }
++};
++
++var bundle = (function custom(beta) {
++
++  function bundle(context) {
++    return beta === 1 ? new Basis(context) : new Bundle(context, beta);
++  }
++
++  bundle.beta = function(beta) {
++    return custom(+beta);
++  };
++
++  return bundle;
++})(0.85);
++
++function point$3(that, x, y) {
++  that._context.bezierCurveTo(
++    that._x1 + that._k * (that._x2 - that._x0),
++    that._y1 + that._k * (that._y2 - that._y0),
++    that._x2 + that._k * (that._x1 - x),
++    that._y2 + that._k * (that._y1 - y),
++    that._x2,
++    that._y2
++  );
++}
++
++function Cardinal(context, tension) {
++  this._context = context;
++  this._k = (1 - tension) / 6;
++}
++
++Cardinal.prototype = {
++  areaStart: function() {
++    this._line = 0;
++  },
++  areaEnd: function() {
++    this._line = NaN;
++  },
++  lineStart: function() {
++    this._x0 = this._x1 = this._x2 =
++    this._y0 = this._y1 = this._y2 = NaN;
++    this._point = 0;
++  },
++  lineEnd: function() {
++    switch (this._point) {
++      case 2: this._context.lineTo(this._x2, this._y2); break;
++      case 3: point$3(this, this._x1, this._y1); break;
++    }
++    if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
++    this._line = 1 - this._line;
++  },
++  point: function(x, y) {
++    x = +x, y = +y;
++    switch (this._point) {
++      case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
++      case 1: this._point = 2; this._x1 = x, this._y1 = y; break;
++      case 2: this._point = 3; // proceed
++      default: point$3(this, x, y); break;
++    }
++    this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
++    this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
++  }
++};
++
++var cardinal = (function custom(tension) {
++
++  function cardinal(context) {
++    return new Cardinal(context, tension);
++  }
++
++  cardinal.tension = function(tension) {
++    return custom(+tension);
++  };
++
++  return cardinal;
++})(0);
++
++function CardinalClosed(context, tension) {
++  this._context = context;
++  this._k = (1 - tension) / 6;
++}
++
++CardinalClosed.prototype = {
++  areaStart: noop$3,
++  areaEnd: noop$3,
++  lineStart: function() {
++    this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = this._x5 =
++    this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = this._y5 = NaN;
++    this._point = 0;
++  },
++  lineEnd: function() {
++    switch (this._point) {
++      case 1: {
++        this._context.moveTo(this._x3, this._y3);
++        this._context.closePath();
++        break;
++      }
++      case 2: {
++        this._context.lineTo(this._x3, this._y3);
++        this._context.closePath();
++        break;
++      }
++      case 3: {
++        this.point(this._x3, this._y3);
++        this.point(this._x4, this._y4);
++        this.point(this._x5, this._y5);
++        break;
++      }
++    }
++  },
++  point: function(x, y) {
++    x = +x, y = +y;
++    switch (this._point) {
++      case 0: this._point = 1; this._x3 = x, this._y3 = y; break;
++      case 1: this._point = 2; this._context.moveTo(this._x4 = x, this._y4 = y); break;
++      case 2: this._point = 3; this._x5 = x, this._y5 = y; break;
++      default: point$3(this, x, y); break;
++    }
++    this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
++    this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
++  }
++};
++
++var cardinalClosed = (function custom(tension) {
++
++  function cardinal$$1(context) {
++    return new CardinalClosed(context, tension);
++  }
++
++  cardinal$$1.tension = function(tension) {
++    return custom(+tension);
++  };
++
++  return cardinal$$1;
++})(0);
++
++function CardinalOpen(context, tension) {
++  this._context = context;
++  this._k = (1 - tension) / 6;
++}
++
++CardinalOpen.prototype = {
++  areaStart: function() {
++    this._line = 0;
++  },
++  areaEnd: function() {
++    this._line = NaN;
++  },
++  lineStart: function() {
++    this._x0 = this._x1 = this._x2 =
++    this._y0 = this._y1 = this._y2 = NaN;
++    this._point = 0;
++  },
++  lineEnd: function() {
++    if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath();
++    this._line = 1 - this._line;
++  },
++  point: function(x, y) {
++    x = +x, y = +y;
++    switch (this._point) {
++      case 0: this._point = 1; break;
++      case 1: this._point = 2; break;
++      case 2: this._point = 3; this._line ? this._context.lineTo(this._x2, this._y2) : this._context.moveTo(this._x2, this._y2); break;
++      case 3: this._point = 4; // proceed
++      default: point$3(this, x, y); break;
++    }
++    this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
++    this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
++  }
++};
++
++var cardinalOpen = (function custom(tension) {
++
++  function cardinal$$1(context) {
++    return new CardinalOpen(context, tension);
++  }
++
++  cardinal$$1.tension = function(tension) {
++    return custom(+tension);
++  };
++
++  return cardinal$$1;
++})(0);
++
++function point$4(that, x, y) {
++  var x1 = that._x1,
++      y1 = that._y1,
++      x2 = that._x2,
++      y2 = that._y2;
++
++  if (that._l01_a > epsilon$3) {
++    var a = 2 * that._l01_2a + 3 * that._l01_a * that._l12_a + that._l12_2a,
++        n = 3 * that._l01_a * (that._l01_a + that._l12_a);
++    x1 = (x1 * a - that._x0 * that._l12_2a + that._x2 * that._l01_2a) / n;
++    y1 = (y1 * a - that._y0 * that._l12_2a + that._y2 * that._l01_2a) / n;
++  }
++
++  if (that._l23_a > epsilon$3) {
++    var b = 2 * that._l23_2a + 3 * that._l23_a * that._l12_a + that._l12_2a,
++        m = 3 * that._l23_a * (that._l23_a + that._l12_a);
++    x2 = (x2 * b + that._x1 * that._l23_2a - x * that._l12_2a) / m;
++    y2 = (y2 * b + that._y1 * that._l23_2a - y * that._l12_2a) / m;
++  }
++
++  that._context.bezierCurveTo(x1, y1, x2, y2, that._x2, that._y2);
++}
++
++function CatmullRom(context, alpha) {
++  this._context = context;
++  this._alpha = alpha;
++}
++
++CatmullRom.prototype = {
++  areaStart: function() {
++    this._line = 0;
++  },
++  areaEnd: function() {
++    this._line = NaN;
++  },
++  lineStart: function() {
++    this._x0 = this._x1 = this._x2 =
++    this._y0 = this._y1 = this._y2 = NaN;
++    this._l01_a = this._l12_a = this._l23_a =
++    this._l01_2a = this._l12_2a = this._l23_2a =
++    this._point = 0;
++  },
++  lineEnd: function() {
++    switch (this._point) {
++      case 2: this._context.lineTo(this._x2, this._y2); break;
++      case 3: this.point(this._x2, this._y2); break;
++    }
++    if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
++    this._line = 1 - this._line;
++  },
++  point: function(x, y) {
++    x = +x, y = +y;
++
++    if (this._point) {
++      var x23 = this._x2 - x,
++          y23 = this._y2 - y;
++      this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));
++    }
++
++    switch (this._point) {
++      case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
++      case 1: this._point = 2; break;
++      case 2: this._point = 3; // proceed
++      default: point$4(this, x, y); break;
++    }
++
++    this._l01_a = this._l12_a, this._l12_a = this._l23_a;
++    this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;
++    this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
++    this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
++  }
++};
++
++var catmullRom = (function custom(alpha) {
++
++  function catmullRom(context) {
++    return alpha ? new CatmullRom(context, alpha) : new Cardinal(context, 0);
++  }
++
++  catmullRom.alpha = function(alpha) {
++    return custom(+alpha);
++  };
++
++  return catmullRom;
++})(0.5);
++
++function CatmullRomClosed(context, alpha) {
++  this._context = context;
++  this._alpha = alpha;
++}
++
++CatmullRomClosed.prototype = {
++  areaStart: noop$3,
++  areaEnd: noop$3,
++  lineStart: function() {
++    this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = this._x5 =
++    this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = this._y5 = NaN;
++    this._l01_a = this._l12_a = this._l23_a =
++    this._l01_2a = this._l12_2a = this._l23_2a =
++    this._point = 0;
++  },
++  lineEnd: function() {
++    switch (this._point) {
++      case 1: {
++        this._context.moveTo(this._x3, this._y3);
++        this._context.closePath();
++        break;
++      }
++      case 2: {
++        this._context.lineTo(this._x3, this._y3);
++        this._context.closePath();
++        break;
++      }
++      case 3: {
++        this.point(this._x3, this._y3);
++        this.point(this._x4, this._y4);
++        this.point(this._x5, this._y5);
++        break;
++      }
++    }
++  },
++  point: function(x, y) {
++    x = +x, y = +y;
++
++    if (this._point) {
++      var x23 = this._x2 - x,
++          y23 = this._y2 - y;
++      this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));
++    }
++
++    switch (this._point) {
++      case 0: this._point = 1; this._x3 = x, this._y3 = y; break;
++      case 1: this._point = 2; this._context.moveTo(this._x4 = x, this._y4 = y); break;
++      case 2: this._point = 3; this._x5 = x, this._y5 = y; break;
++      default: point$4(this, x, y); break;
++    }
++
++    this._l01_a = this._l12_a, this._l12_a = this._l23_a;
++    this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;
++    this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
++    this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
++  }
++};
++
++var catmullRomClosed = (function custom(alpha) {
++
++  function catmullRom$$1(context) {
++    return alpha ? new CatmullRomClosed(context, alpha) : new CardinalClosed(context, 0);
++  }
++
++  catmullRom$$1.alpha = function(alpha) {
++    return custom(+alpha);
++  };
++
++  return catmullRom$$1;
++})(0.5);
++
++function CatmullRomOpen(context, alpha) {
++  this._context = context;
++  this._alpha = alpha;
++}
++
++CatmullRomOpen.prototype = {
++  areaStart: function() {
++    this._line = 0;
++  },
++  areaEnd: function() {
++    this._line = NaN;
++  },
++  lineStart: function() {
++    this._x0 = this._x1 = this._x2 =
++    this._y0 = this._y1 = this._y2 = NaN;
++    this._l01_a = this._l12_a = this._l23_a =
++    this._l01_2a = this._l12_2a = this._l23_2a =
++    this._point = 0;
++  },
++  lineEnd: function() {
++    if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath();
++    this._line = 1 - this._line;
++  },
++  point: function(x, y) {
++    x = +x, y = +y;
++
++    if (this._point) {
++      var x23 = this._x2 - x,
++          y23 = this._y2 - y;
++      this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));
++    }
++
++    switch (this._point) {
++      case 0: this._point = 1; break;
++      case 1: this._point = 2; break;
++      case 2: this._point = 3; this._line ? this._context.lineTo(this._x2, this._y2) : this._context.moveTo(this._x2, this._y2); break;
++      case 3: this._point = 4; // proceed
++      default: point$4(this, x, y); break;
++    }
++
++    this._l01_a = this._l12_a, this._l12_a = this._l23_a;
++    this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;
++    this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;
++    this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;
++  }
++};
++
++var catmullRomOpen = (function custom(alpha) {
++
++  function catmullRom$$1(context) {
++    return alpha ? new CatmullRomOpen(context, alpha) : new CardinalOpen(context, 0);
++  }
++
++  catmullRom$$1.alpha = function(alpha) {
++    return custom(+alpha);
++  };
++
++  return catmullRom$$1;
++})(0.5);
++
++function LinearClosed(context) {
++  this._context = context;
++}
++
++LinearClosed.prototype = {
++  areaStart: noop$3,
++  areaEnd: noop$3,
++  lineStart: function() {
++    this._point = 0;
++  },
++  lineEnd: function() {
++    if (this._point) this._context.closePath();
++  },
++  point: function(x, y) {
++    x = +x, y = +y;
++    if (this._point) this._context.lineTo(x, y);
++    else this._point = 1, this._context.moveTo(x, y);
++  }
++};
++
++function linearClosed(context) {
++  return new LinearClosed(context);
++}
++
++function sign$1(x) {
++  return x < 0 ? -1 : 1;
++}
++
++// Calculate the slopes of the tangents (Hermite-type interpolation) based on
++// the following paper: Steffen, M. 1990. A Simple Method for Monotonic
++// Interpolation in One Dimension. Astronomy and Astrophysics, Vol. 239, NO.
++// NOV(II), P. 443, 1990.
++function slope3(that, x2, y2) {
++  var h0 = that._x1 - that._x0,
++      h1 = x2 - that._x1,
++      s0 = (that._y1 - that._y0) / (h0 || h1 < 0 && -0),
++      s1 = (y2 - that._y1) / (h1 || h0 < 0 && -0),
++      p = (s0 * h1 + s1 * h0) / (h0 + h1);
++  return (sign$1(s0) + sign$1(s1)) * Math.min(Math.abs(s0), Math.abs(s1), 0.5 * Math.abs(p)) || 0;
++}
++
++// Calculate a one-sided slope.
++function slope2(that, t) {
++  var h = that._x1 - that._x0;
++  return h ? (3 * (that._y1 - that._y0) / h - t) / 2 : t;
++}
++
++// According to https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Representations
++// "you can express cubic Hermite interpolation in terms of cubic Bézier curves
++// with respect to the four values p0, p0 + m0 / 3, p1 - m1 / 3, p1".
++function point$5(that, t0, t1) {
++  var x0 = that._x0,
++      y0 = that._y0,
++      x1 = that._x1,
++      y1 = that._y1,
++      dx = (x1 - x0) / 3;
++  that._context.bezierCurveTo(x0 + dx, y0 + dx * t0, x1 - dx, y1 - dx * t1, x1, y1);
++}
++
++function MonotoneX(context) {
++  this._context = context;
++}
++
++MonotoneX.prototype = {
++  areaStart: function() {
++    this._line = 0;
++  },
++  areaEnd: function() {
++    this._line = NaN;
++  },
++  lineStart: function() {
++    this._x0 = this._x1 =
++    this._y0 = this._y1 =
++    this._t0 = NaN;
++    this._point = 0;
++  },
++  lineEnd: function() {
++    switch (this._point) {
++      case 2: this._context.lineTo(this._x1, this._y1); break;
++      case 3: point$5(this, this._t0, slope2(this, this._t0)); break;
++    }
++    if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
++    this._line = 1 - this._line;
++  },
++  point: function(x, y) {
++    var t1 = NaN;
++
++    x = +x, y = +y;
++    if (x === this._x1 && y === this._y1) return; // Ignore coincident points.
++    switch (this._point) {
++      case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
++      case 1: this._point = 2; break;
++      case 2: this._point = 3; point$5(this, slope2(this, t1 = slope3(this, x, y)), t1); break;
++      default: point$5(this, this._t0, t1 = slope3(this, x, y)); break;
++    }
++
++    this._x0 = this._x1, this._x1 = x;
++    this._y0 = this._y1, this._y1 = y;
++    this._t0 = t1;
++  }
++};
++
++function MonotoneY(context) {
++  this._context = new ReflectContext(context);
++}
++
++(MonotoneY.prototype = Object.create(MonotoneX.prototype)).point = function(x, y) {
++  MonotoneX.prototype.point.call(this, y, x);
++};
++
++function ReflectContext(context) {
++  this._context = context;
++}
++
++ReflectContext.prototype = {
++  moveTo: function(x, y) { this._context.moveTo(y, x); },
++  closePath: function() { this._context.closePath(); },
++  lineTo: function(x, y) { this._context.lineTo(y, x); },
++  bezierCurveTo: function(x1, y1, x2, y2, x, y) { this._context.bezierCurveTo(y1, x1, y2, x2, y, x); }
++};
++
++function monotoneX(context) {
++  return new MonotoneX(context);
++}
++
++function monotoneY(context) {
++  return new MonotoneY(context);
++}
++
++function Natural(context) {
++  this._context = context;
++}
++
++Natural.prototype = {
++  areaStart: function() {
++    this._line = 0;
++  },
++  areaEnd: function() {
++    this._line = NaN;
++  },
++  lineStart: function() {
++    this._x = [];
++    this._y = [];
++  },
++  lineEnd: function() {
++    var x = this._x,
++        y = this._y,
++        n = x.length;
++
++    if (n) {
++      this._line ? this._context.lineTo(x[0], y[0]) : this._context.moveTo(x[0], y[0]);
++      if (n === 2) {
++        this._context.lineTo(x[1], y[1]);
++      } else {
++        var px = controlPoints(x),
++            py = controlPoints(y);
++        for (var i0 = 0, i1 = 1; i1 < n; ++i0, ++i1) {
++          this._context.bezierCurveTo(px[0][i0], py[0][i0], px[1][i0], py[1][i0], x[i1], y[i1]);
++        }
++      }
++    }
++
++    if (this._line || (this._line !== 0 && n === 1)) this._context.closePath();
++    this._line = 1 - this._line;
++    this._x = this._y = null;
++  },
++  point: function(x, y) {
++    this._x.push(+x);
++    this._y.push(+y);
++  }
++};
++
++// See https://www.particleincell.com/2012/bezier-splines/ for derivation.
++function controlPoints(x) {
++  var i,
++      n = x.length - 1,
++      m,
++      a = new Array(n),
++      b = new Array(n),
++      r = new Array(n);
++  a[0] = 0, b[0] = 2, r[0] = x[0] + 2 * x[1];
++  for (i = 1; i < n - 1; ++i) a[i] = 1, b[i] = 4, r[i] = 4 * x[i] + 2 * x[i + 1];
++  a[n - 1] = 2, b[n - 1] = 7, r[n - 1] = 8 * x[n - 1] + x[n];
++  for (i = 1; i < n; ++i) m = a[i] / b[i - 1], b[i] -= m, r[i] -= m * r[i - 1];
++  a[n - 1] = r[n - 1] / b[n - 1];
++  for (i = n - 2; i >= 0; --i) a[i] = (r[i] - a[i + 1]) / b[i];
++  b[n - 1] = (x[n] + a[n - 1]) / 2;
++  for (i = 0; i < n - 1; ++i) b[i] = 2 * x[i + 1] - a[i + 1];
++  return [a, b];
++}
++
++function natural(context) {
++  return new Natural(context);
++}
++
++function Step(context, t) {
++  this._context = context;
++  this._t = t;
++}
++
++Step.prototype = {
++  areaStart: function() {
++    this._line = 0;
++  },
++  areaEnd: function() {
++    this._line = NaN;
++  },
++  lineStart: function() {
++    this._x = this._y = NaN;
++    this._point = 0;
++  },
++  lineEnd: function() {
++    if (0 < this._t && this._t < 1 && this._point === 2) this._context.lineTo(this._x, this._y);
++    if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();
++    if (this._line >= 0) this._t = 1 - this._t, this._line = 1 - this._line;
++  },
++  point: function(x, y) {
++    x = +x, y = +y;
++    switch (this._point) {
++      case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;
++      case 1: this._point = 2; // proceed
++      default: {
++        if (this._t <= 0) {
++          this._context.lineTo(this._x, y);
++          this._context.lineTo(x, y);
++        } else {
++          var x1 = this._x * (1 - this._t) + x * this._t;
++          this._context.lineTo(x1, this._y);
++          this._context.lineTo(x1, y);
++        }
++        break;
++      }
++    }
++    this._x = x, this._y = y;
++  }
++};
++
++function step(context) {
++  return new Step(context, 0.5);
++}
++
++function stepBefore(context) {
++  return new Step(context, 0);
++}
++
++function stepAfter(context) {
++  return new Step(context, 1);
++}
++
++function none$1(series, order) {
++  if (!((n = series.length) > 1)) return;
++  for (var i = 1, j, s0, s1 = series[order[0]], n, m = s1.length; i < n; ++i) {
++    s0 = s1, s1 = series[order[i]];
++    for (j = 0; j < m; ++j) {
++      s1[j][1] += s1[j][0] = isNaN(s0[j][1]) ? s0[j][0] : s0[j][1];
++    }
++  }
++}
++
++function none$2(series) {
++  var n = series.length, o = new Array(n);
++  while (--n >= 0) o[n] = n;
++  return o;
++}
++
++function stackValue(d, key) {
++  return d[key];
++}
++
++function stack() {
++  var keys = constant$11([]),
++      order = none$2,
++      offset = none$1,
++      value = stackValue;
++
++  function stack(data) {
++    var kz = keys.apply(this, arguments),
++        i,
++        m = data.length,
++        n = kz.length,
++        sz = new Array(n),
++        oz;
++
++    for (i = 0; i < n; ++i) {
++      for (var ki = kz[i], si = sz[i] = new Array(m), j = 0, sij; j < m; ++j) {
++        si[j] = sij = [0, +value(data[j], ki, j, data)];
++        sij.data = data[j];
++      }
++      si.key = ki;
++    }
++
++    for (i = 0, oz = order(sz); i < n; ++i) {
++      sz[oz[i]].index = i;
++    }
++
++    offset(sz, oz);
++    return sz;
++  }
++
++  stack.keys = function(_) {
++    return arguments.length ? (keys = typeof _ === "function" ? _ : constant$11(slice$6.call(_)), stack) : keys;
++  };
++
++  stack.value = function(_) {
++    return arguments.length ? (value = typeof _ === "function" ? _ : constant$11(+_), stack) : value;
++  };
++
++  stack.order = function(_) {
++    return arguments.length ? (order = _ == null ? none$2 : typeof _ === "function" ? _ : constant$11(slice$6.call(_)), stack) : order;
++  };
++
++  stack.offset = function(_) {
++    return arguments.length ? (offset = _ == null ? none$1 : _, stack) : offset;
++  };
++
++  return stack;
++}
++
++function expand(series, order) {
++  if (!((n = series.length) > 0)) return;
++  for (var i, n, j = 0, m = series[0].length, y; j < m; ++j) {
++    for (y = i = 0; i < n; ++i) y += series[i][j][1] || 0;
++    if (y) for (i = 0; i < n; ++i) series[i][j][1] /= y;
++  }
++  none$1(series, order);
++}
++
++function diverging$1(series, order) {
++  if (!((n = series.length) > 1)) return;
++  for (var i, j = 0, d, dy, yp, yn, n, m = series[order[0]].length; j < m; ++j) {
++    for (yp = yn = 0, i = 0; i < n; ++i) {
++      if ((dy = (d = series[order[i]][j])[1] - d[0]) >= 0) {
++        d[0] = yp, d[1] = yp += dy;
++      } else if (dy < 0) {
++        d[1] = yn, d[0] = yn += dy;
++      } else {
++        d[0] = yp;
++      }
++    }
++  }
++}
++
++function silhouette(series, order) {
++  if (!((n = series.length) > 0)) return;
++  for (var j = 0, s0 = series[order[0]], n, m = s0.length; j < m; ++j) {
++    for (var i = 0, y = 0; i < n; ++i) y += series[i][j][1] || 0;
++    s0[j][1] += s0[j][0] = -y / 2;
++  }
++  none$1(series, order);
++}
++
++function wiggle(series, order) {
++  if (!((n = series.length) > 0) || !((m = (s0 = series[order[0]]).length) > 0)) return;
++  for (var y = 0, j = 1, s0, m, n; j < m; ++j) {
++    for (var i = 0, s1 = 0, s2 = 0; i < n; ++i) {
++      var si = series[order[i]],
++          sij0 = si[j][1] || 0,
++          sij1 = si[j - 1][1] || 0,
++          s3 = (sij0 - sij1) / 2;
++      for (var k = 0; k < i; ++k) {
++        var sk = series[order[k]],
++            skj0 = sk[j][1] || 0,
++            skj1 = sk[j - 1][1] || 0;
++        s3 += skj0 - skj1;
++      }
++      s1 += sij0, s2 += s3 * sij0;
++    }
++    s0[j - 1][1] += s0[j - 1][0] = y;
++    if (s1) y -= s2 / s1;
++  }
++  s0[j - 1][1] += s0[j - 1][0] = y;
++  none$1(series, order);
++}
++
++function ascending$3(series) {
++  var sums = series.map(sum$2);
++  return none$2(series).sort(function(a, b) { return sums[a] - sums[b]; });
++}
++
++function sum$2(series) {
++  var s = 0, i = -1, n = series.length, v;
++  while (++i < n) if (v = +series[i][1]) s += v;
++  return s;
++}
++
++function descending$2(series) {
++  return ascending$3(series).reverse();
++}
++
++function insideOut(series) {
++  var n = series.length,
++      i,
++      j,
++      sums = series.map(sum$2),
++      order = none$2(series).sort(function(a, b) { return sums[b] - sums[a]; }),
++      top = 0,
++      bottom = 0,
++      tops = [],
++      bottoms = [];
++
++  for (i = 0; i < n; ++i) {
++    j = order[i];
++    if (top < bottom) {
++      top += sums[j];
++      tops.push(j);
++    } else {
++      bottom += sums[j];
++      bottoms.push(j);
++    }
++  }
++
++  return bottoms.reverse().concat(tops);
++}
++
++function reverse(series) {
++  return none$2(series).reverse();
++}
++
++function constant$12(x) {
++  return function() {
++    return x;
++  };
++}
++
++function x$4(d) {
++  return d[0];
++}
++
++function y$4(d) {
++  return d[1];
++}
++
++function RedBlackTree() {
++  this._ = null; // root node
++}
++
++function RedBlackNode(node) {
++  node.U = // parent node
++  node.C = // color - true for red, false for black
++  node.L = // left node
++  node.R = // right node
++  node.P = // previous node
++  node.N = null; // next node
++}
++
++RedBlackTree.prototype = {
++  constructor: RedBlackTree,
++
++  insert: function(after, node) {
++    var parent, grandpa, uncle;
++
++    if (after) {
++      node.P = after;
++      node.N = after.N;
++      if (after.N) after.N.P = node;
++      after.N = node;
++      if (after.R) {
++        after = after.R;
++        while (after.L) after = after.L;
++        after.L = node;
++      } else {
++        after.R = node;
++      }
++      parent = after;
++    } else if (this._) {
++      after = RedBlackFirst(this._);
++      node.P = null;
++      node.N = after;
++      after.P = after.L = node;
++      parent = after;
++    } else {
++      node.P = node.N = null;
++      this._ = node;
++      parent = null;
++    }
++    node.L = node.R = null;
++    node.U = parent;
++    node.C = true;
++
++    after = node;
++    while (parent && parent.C) {
++      grandpa = parent.U;
++      if (parent === grandpa.L) {
++        uncle = grandpa.R;
++        if (uncle && uncle.C) {
++          parent.C = uncle.C = false;
++          grandpa.C = true;
++          after = grandpa;
++        } else {
++          if (after === parent.R) {
++            RedBlackRotateLeft(this, parent);
++            after = parent;
++            parent = after.U;
++          }
++          parent.C = false;
++          grandpa.C = true;
++          RedBlackRotateRight(this, grandpa);
++        }
++      } else {
++        uncle = grandpa.L;
++        if (uncle && uncle.C) {
++          parent.C = uncle.C = false;
++          grandpa.C = true;
++          after = grandpa;
++        } else {
++          if (after === parent.L) {
++            RedBlackRotateRight(this, parent);
++            after = parent;
++            parent = after.U;
++          }
++          parent.C = false;
++          grandpa.C = true;
++          RedBlackRotateLeft(this, grandpa);
++        }
++      }
++      parent = after.U;
++    }
++    this._.C = false;
++  },
++
++  remove: function(node) {
++    if (node.N) node.N.P = node.P;
++    if (node.P) node.P.N = node.N;
++    node.N = node.P = null;
++
++    var parent = node.U,
++        sibling,
++        left = node.L,
++        right = node.R,
++        next,
++        red;
++
++    if (!left) next = right;
++    else if (!right) next = left;
++    else next = RedBlackFirst(right);
++
++    if (parent) {
++      if (parent.L === node) parent.L = next;
++      else parent.R = next;
++    } else {
++      this._ = next;
++    }
++
++    if (left && right) {
++      red = next.C;
++      next.C = node.C;
++      next.L = left;
++      left.U = next;
++      if (next !== right) {
++        parent = next.U;
++        next.U = node.U;
++        node = next.R;
++        parent.L = node;
++        next.R = right;
++        right.U = next;
++      } else {
++        next.U = parent;
++        parent = next;
++        node = next.R;
++      }
++    } else {
++      red = node.C;
++      node = next;
++    }
++
++    if (node) node.U = parent;
++    if (red) return;
++    if (node && node.C) { node.C = false; return; }
++
++    do {
++      if (node === this._) break;
++      if (node === parent.L) {
++        sibling = parent.R;
++        if (sibling.C) {
++          sibling.C = false;
++          parent.C = true;
++          RedBlackRotateLeft(this, parent);
++          sibling = parent.R;
++        }
++        if ((sibling.L && sibling.L.C)
++            || (sibling.R && sibling.R.C)) {
++          if (!sibling.R || !sibling.R.C) {
++            sibling.L.C = false;
++            sibling.C = true;
++            RedBlackRotateRight(this, sibling);
++            sibling = parent.R;
++          }
++          sibling.C = parent.C;
++          parent.C = sibling.R.C = false;
++          RedBlackRotateLeft(this, parent);
++          node = this._;
++          break;
++        }
++      } else {
++        sibling = parent.L;
++        if (sibling.C) {
++          sibling.C = false;
++          parent.C = true;
++          RedBlackRotateRight(this, parent);
++          sibling = parent.L;
++        }
++        if ((sibling.L && sibling.L.C)
++          || (sibling.R && sibling.R.C)) {
++          if (!sibling.L || !sibling.L.C) {
++            sibling.R.C = false;
++            sibling.C = true;
++            RedBlackRotateLeft(this, sibling);
++            sibling = parent.L;
++          }
++          sibling.C = parent.C;
++          parent.C = sibling.L.C = false;
++          RedBlackRotateRight(this, parent);
++          node = this._;
++          break;
++        }
++      }
++      sibling.C = true;
++      node = parent;
++      parent = parent.U;
++    } while (!node.C);
++
++    if (node) node.C = false;
++  }
++};
++
++function RedBlackRotateLeft(tree, node) {
++  var p = node,
++      q = node.R,
++      parent = p.U;
++
++  if (parent) {
++    if (parent.L === p) parent.L = q;
++    else parent.R = q;
++  } else {
++    tree._ = q;
++  }
++
++  q.U = parent;
++  p.U = q;
++  p.R = q.L;
++  if (p.R) p.R.U = p;
++  q.L = p;
++}
++
++function RedBlackRotateRight(tree, node) {
++  var p = node,
++      q = node.L,
++      parent = p.U;
++
++  if (parent) {
++    if (parent.L === p) parent.L = q;
++    else parent.R = q;
++  } else {
++    tree._ = q;
++  }
++
++  q.U = parent;
++  p.U = q;
++  p.L = q.R;
++  if (p.L) p.L.U = p;
++  q.R = p;
++}
++
++function RedBlackFirst(node) {
++  while (node.L) node = node.L;
++  return node;
++}
++
++function createEdge(left, right, v0, v1) {
++  var edge = [null, null],
++      index = edges.push(edge) - 1;
++  edge.left = left;
++  edge.right = right;
++  if (v0) setEdgeEnd(edge, left, right, v0);
++  if (v1) setEdgeEnd(edge, right, left, v1);
++  cells[left.index].halfedges.push(index);
++  cells[right.index].halfedges.push(index);
++  return edge;
++}
++
++function createBorderEdge(left, v0, v1) {
++  var edge = [v0, v1];
++  edge.left = left;
++  return edge;
++}
++
++function setEdgeEnd(edge, left, right, vertex) {
++  if (!edge[0] && !edge[1]) {
++    edge[0] = vertex;
++    edge.left = left;
++    edge.right = right;
++  } else if (edge.left === right) {
++    edge[1] = vertex;
++  } else {
++    edge[0] = vertex;
++  }
++}
++
++// Liang–Barsky line clipping.
++function clipEdge(edge, x0, y0, x1, y1) {
++  var a = edge[0],
++      b = edge[1],
++      ax = a[0],
++      ay = a[1],
++      bx = b[0],
++      by = b[1],
++      t0 = 0,
++      t1 = 1,
++      dx = bx - ax,
++      dy = by - ay,
++      r;
++
++  r = x0 - ax;
++  if (!dx && r > 0) return;
++  r /= dx;
++  if (dx < 0) {
++    if (r < t0) return;
++    if (r < t1) t1 = r;
++  } else if (dx > 0) {
++    if (r > t1) return;
++    if (r > t0) t0 = r;
++  }
++
++  r = x1 - ax;
++  if (!dx && r < 0) return;
++  r /= dx;
++  if (dx < 0) {
++    if (r > t1) return;
++    if (r > t0) t0 = r;
++  } else if (dx > 0) {
++    if (r < t0) return;
++    if (r < t1) t1 = r;
++  }
++
++  r = y0 - ay;
++  if (!dy && r > 0) return;
++  r /= dy;
++  if (dy < 0) {
++    if (r < t0) return;
++    if (r < t1) t1 = r;
++  } else if (dy > 0) {
++    if (r > t1) return;
++    if (r > t0) t0 = r;
++  }
++
++  r = y1 - ay;
++  if (!dy && r < 0) return;
++  r /= dy;
++  if (dy < 0) {
++    if (r > t1) return;
++    if (r > t0) t0 = r;
++  } else if (dy > 0) {
++    if (r < t0) return;
++    if (r < t1) t1 = r;
++  }
++
++  if (!(t0 > 0) && !(t1 < 1)) return true; // TODO Better check?
++
++  if (t0 > 0) edge[0] = [ax + t0 * dx, ay + t0 * dy];
++  if (t1 < 1) edge[1] = [ax + t1 * dx, ay + t1 * dy];
++  return true;
++}
++
++function connectEdge(edge, x0, y0, x1, y1) {
++  var v1 = edge[1];
++  if (v1) return true;
++
++  var v0 = edge[0],
++      left = edge.left,
++      right = edge.right,
++      lx = left[0],
++      ly = left[1],
++      rx = right[0],
++      ry = right[1],
++      fx = (lx + rx) / 2,
++      fy = (ly + ry) / 2,
++      fm,
++      fb;
++
++  if (ry === ly) {
++    if (fx < x0 || fx >= x1) return;
++    if (lx > rx) {
++      if (!v0) v0 = [fx, y0];
++      else if (v0[1] >= y1) return;
++      v1 = [fx, y1];
++    } else {
++      if (!v0) v0 = [fx, y1];
++      else if (v0[1] < y0) return;
++      v1 = [fx, y0];
++    }
++  } else {
++    fm = (lx - rx) / (ry - ly);
++    fb = fy - fm * fx;
++    if (fm < -1 || fm > 1) {
++      if (lx > rx) {
++        if (!v0) v0 = [(y0 - fb) / fm, y0];
++        else if (v0[1] >= y1) return;
++        v1 = [(y1 - fb) / fm, y1];
++      } else {
++        if (!v0) v0 = [(y1 - fb) / fm, y1];
++        else if (v0[1] < y0) return;
++        v1 = [(y0 - fb) / fm, y0];
++      }
++    } else {
++      if (ly < ry) {
++        if (!v0) v0 = [x0, fm * x0 + fb];
++        else if (v0[0] >= x1) return;
++        v1 = [x1, fm * x1 + fb];
++      } else {
++        if (!v0) v0 = [x1, fm * x1 + fb];
++        else if (v0[0] < x0) return;
++        v1 = [x0, fm * x0 + fb];
++      }
++    }
++  }
++
++  edge[0] = v0;
++  edge[1] = v1;
++  return true;
++}
++
++function clipEdges(x0, y0, x1, y1) {
++  var i = edges.length,
++      edge;
++
++  while (i--) {
++    if (!connectEdge(edge = edges[i], x0, y0, x1, y1)
++        || !clipEdge(edge, x0, y0, x1, y1)
++        || !(Math.abs(edge[0][0] - edge[1][0]) > epsilon$4
++            || Math.abs(edge[0][1] - edge[1][1]) > epsilon$4)) {
++      delete edges[i];
++    }
++  }
++}
++
++function createCell(site) {
++  return cells[site.index] = {
++    site: site,
++    halfedges: []
++  };
++}
++
++function cellHalfedgeAngle(cell, edge) {
++  var site = cell.site,
++      va = edge.left,
++      vb = edge.right;
++  if (site === vb) vb = va, va = site;
++  if (vb) return Math.atan2(vb[1] - va[1], vb[0] - va[0]);
++  if (site === va) va = edge[1], vb = edge[0];
++  else va = edge[0], vb = edge[1];
++  return Math.atan2(va[0] - vb[0], vb[1] - va[1]);
++}
++
++function cellHalfedgeStart(cell, edge) {
++  return edge[+(edge.left !== cell.site)];
++}
++
++function cellHalfedgeEnd(cell, edge) {
++  return edge[+(edge.left === cell.site)];
++}
++
++function sortCellHalfedges() {
++  for (var i = 0, n = cells.length, cell, halfedges, j, m; i < n; ++i) {
++    if ((cell = cells[i]) && (m = (halfedges = cell.halfedges).length)) {
++      var index = new Array(m),
++          array = new Array(m);
++      for (j = 0; j < m; ++j) index[j] = j, array[j] = cellHalfedgeAngle(cell, edges[halfedges[j]]);
++      index.sort(function(i, j) { return array[j] - array[i]; });
++      for (j = 0; j < m; ++j) array[j] = halfedges[index[j]];
++      for (j = 0; j < m; ++j) halfedges[j] = array[j];
++    }
++  }
++}
++
++function clipCells(x0, y0, x1, y1) {
++  var nCells = cells.length,
++      iCell,
++      cell,
++      site,
++      iHalfedge,
++      halfedges,
++      nHalfedges,
++      start,
++      startX,
++      startY,
++      end,
++      endX,
++      endY,
++      cover = true;
++
++  for (iCell = 0; iCell < nCells; ++iCell) {
++    if (cell = cells[iCell]) {
++      site = cell.site;
++      halfedges = cell.halfedges;
++      iHalfedge = halfedges.length;
++
++      // Remove any dangling clipped edges.
++      while (iHalfedge--) {
++        if (!edges[halfedges[iHalfedge]]) {
++          halfedges.splice(iHalfedge, 1);
++        }
++      }
++
++      // Insert any border edges as necessary.
++      iHalfedge = 0, nHalfedges = halfedges.length;
++      while (iHalfedge < nHalfedges) {
++        end = cellHalfedgeEnd(cell, edges[halfedges[iHalfedge]]), endX = end[0], endY = end[1];
++        start = cellHalfedgeStart(cell, edges[halfedges[++iHalfedge % nHalfedges]]), startX = start[0], startY = start[1];
++        if (Math.abs(endX - startX) > epsilon$4 || Math.abs(endY - startY) > epsilon$4) {
++          halfedges.splice(iHalfedge, 0, edges.push(createBorderEdge(site, end,
++              Math.abs(endX - x0) < epsilon$4 && y1 - endY > epsilon$4 ? [x0, Math.abs(startX - x0) < epsilon$4 ? startY : y1]
++              : Math.abs(endY - y1) < epsilon$4 && x1 - endX > epsilon$4 ? [Math.abs(startY - y1) < epsilon$4 ? startX : x1, y1]
++              : Math.abs(endX - x1) < epsilon$4 && endY - y0 > epsilon$4 ? [x1, Math.abs(startX - x1) < epsilon$4 ? startY : y0]
++              : Math.abs(endY - y0) < epsilon$4 && endX - x0 > epsilon$4 ? [Math.abs(startY - y0) < epsilon$4 ? startX : x0, y0]
++              : null)) - 1);
++          ++nHalfedges;
++        }
++      }
++
++      if (nHalfedges) cover = false;
++    }
++  }
++
++  // If there weren’t any edges, have the closest site cover the extent.
++  // It doesn’t matter which corner of the extent we measure!
++  if (cover) {
++    var dx, dy, d2, dc = Infinity;
++
++    for (iCell = 0, cover = null; iCell < nCells; ++iCell) {
++      if (cell = cells[iCell]) {
++        site = cell.site;
++        dx = site[0] - x0;
++        dy = site[1] - y0;
++        d2 = dx * dx + dy * dy;
++        if (d2 < dc) dc = d2, cover = cell;
++      }
++    }
++
++    if (cover) {
++      var v00 = [x0, y0], v01 = [x0, y1], v11 = [x1, y1], v10 = [x1, y0];
++      cover.halfedges.push(
++        edges.push(createBorderEdge(site = cover.site, v00, v01)) - 1,
++        edges.push(createBorderEdge(site, v01, v11)) - 1,
++        edges.push(createBorderEdge(site, v11, v10)) - 1,
++        edges.push(createBorderEdge(site, v10, v00)) - 1
++      );
++    }
++  }
++
++  // Lastly delete any cells with no edges; these were entirely clipped.
++  for (iCell = 0; iCell < nCells; ++iCell) {
++    if (cell = cells[iCell]) {
++      if (!cell.halfedges.length) {
++        delete cells[iCell];
++      }
++    }
++  }
++}
++
++var circlePool = [];
++
++var firstCircle;
++
++function Circle() {
++  RedBlackNode(this);
++  this.x =
++  this.y =
++  this.arc =
++  this.site =
++  this.cy = null;
++}
++
++function attachCircle(arc) {
++  var lArc = arc.P,
++      rArc = arc.N;
++
++  if (!lArc || !rArc) return;
++
++  var lSite = lArc.site,
++      cSite = arc.site,
++      rSite = rArc.site;
++
++  if (lSite === rSite) return;
++
++  var bx = cSite[0],
++      by = cSite[1],
++      ax = lSite[0] - bx,
++      ay = lSite[1] - by,
++      cx = rSite[0] - bx,
++      cy = rSite[1] - by;
++
++  var d = 2 * (ax * cy - ay * cx);
++  if (d >= -epsilon2$2) return;
++
++  var ha = ax * ax + ay * ay,
++      hc = cx * cx + cy * cy,
++      x = (cy * ha - ay * hc) / d,
++      y = (ax * hc - cx * ha) / d;
++
++  var circle = circlePool.pop() || new Circle;
++  circle.arc = arc;
++  circle.site = cSite;
++  circle.x = x + bx;
++  circle.y = (circle.cy = y + by) + Math.sqrt(x * x + y * y); // y bottom
++
++  arc.circle = circle;
++
++  var before = null,
++      node = circles._;
++
++  while (node) {
++    if (circle.y < node.y || (circle.y === node.y && circle.x <= node.x)) {
++      if (node.L) node = node.L;
++      else { before = node.P; break; }
++    } else {
++      if (node.R) node = node.R;
++      else { before = node; break; }
++    }
++  }
++
++  circles.insert(before, circle);
++  if (!before) firstCircle = circle;
++}
++
++function detachCircle(arc) {
++  var circle = arc.circle;
++  if (circle) {
++    if (!circle.P) firstCircle = circle.N;
++    circles.remove(circle);
++    circlePool.push(circle);
++    RedBlackNode(circle);
++    arc.circle = null;
++  }
++}
++
++var beachPool = [];
++
++function Beach() {
++  RedBlackNode(this);
++  this.edge =
++  this.site =
++  this.circle = null;
++}
++
++function createBeach(site) {
++  var beach = beachPool.pop() || new Beach;
++  beach.site = site;
++  return beach;
++}
++
++function detachBeach(beach) {
++  detachCircle(beach);
++  beaches.remove(beach);
++  beachPool.push(beach);
++  RedBlackNode(beach);
++}
++
++function removeBeach(beach) {
++  var circle = beach.circle,
++      x = circle.x,
++      y = circle.cy,
++      vertex = [x, y],
++      previous = beach.P,
++      next = beach.N,
++      disappearing = [beach];
++
++  detachBeach(beach);
++
++  var lArc = previous;
++  while (lArc.circle
++      && Math.abs(x - lArc.circle.x) < epsilon$4
++      && Math.abs(y - lArc.circle.cy) < epsilon$4) {
++    previous = lArc.P;
++    disappearing.unshift(lArc);
++    detachBeach(lArc);
++    lArc = previous;
++  }
++
++  disappearing.unshift(lArc);
++  detachCircle(lArc);
++
++  var rArc = next;
++  while (rArc.circle
++      && Math.abs(x - rArc.circle.x) < epsilon$4
++      && Math.abs(y - rArc.circle.cy) < epsilon$4) {
++    next = rArc.N;
++    disappearing.push(rArc);
++    detachBeach(rArc);
++    rArc = next;
++  }
++
++  disappearing.push(rArc);
++  detachCircle(rArc);
++
++  var nArcs = disappearing.length,
++      iArc;
++  for (iArc = 1; iArc < nArcs; ++iArc) {
++    rArc = disappearing[iArc];
++    lArc = disappearing[iArc - 1];
++    setEdgeEnd(rArc.edge, lArc.site, rArc.site, vertex);
++  }
++
++  lArc = disappearing[0];
++  rArc = disappearing[nArcs - 1];
++  rArc.edge = createEdge(lArc.site, rArc.site, null, vertex);
++
++  attachCircle(lArc);
++  attachCircle(rArc);
++}
++
++function addBeach(site) {
++  var x = site[0],
++      directrix = site[1],
++      lArc,
++      rArc,
++      dxl,
++      dxr,
++      node = beaches._;
++
++  while (node) {
++    dxl = leftBreakPoint(node, directrix) - x;
++    if (dxl > epsilon$4) node = node.L; else {
++      dxr = x - rightBreakPoint(node, directrix);
++      if (dxr > epsilon$4) {
++        if (!node.R) {
++          lArc = node;
++          break;
++        }
++        node = node.R;
++      } else {
++        if (dxl > -epsilon$4) {
++          lArc = node.P;
++          rArc = node;
++        } else if (dxr > -epsilon$4) {
++          lArc = node;
++          rArc = node.N;
++        } else {
++          lArc = rArc = node;
++        }
++        break;
++      }
++    }
++  }
++
++  createCell(site);
++  var newArc = createBeach(site);
++  beaches.insert(lArc, newArc);
++
++  if (!lArc && !rArc) return;
++
++  if (lArc === rArc) {
++    detachCircle(lArc);
++    rArc = createBeach(lArc.site);
++    beaches.insert(newArc, rArc);
++    newArc.edge = rArc.edge = createEdge(lArc.site, newArc.site);
++    attachCircle(lArc);
++    attachCircle(rArc);
++    return;
++  }
++
++  if (!rArc) { // && lArc
++    newArc.edge = createEdge(lArc.site, newArc.site);
++    return;
++  }
++
++  // else lArc !== rArc
++  detachCircle(lArc);
++  detachCircle(rArc);
++
++  var lSite = lArc.site,
++      ax = lSite[0],
++      ay = lSite[1],
++      bx = site[0] - ax,
++      by = site[1] - ay,
++      rSite = rArc.site,
++      cx = rSite[0] - ax,
++      cy = rSite[1] - ay,
++      d = 2 * (bx * cy - by * cx),
++      hb = bx * bx + by * by,
++      hc = cx * cx + cy * cy,
++      vertex = [(cy * hb - by * hc) / d + ax, (bx * hc - cx * hb) / d + ay];
++
++  setEdgeEnd(rArc.edge, lSite, rSite, vertex);
++  newArc.edge = createEdge(lSite, site, null, vertex);
++  rArc.edge = createEdge(site, rSite, null, vertex);
++  attachCircle(lArc);
++  attachCircle(rArc);
++}
++
++function leftBreakPoint(arc, directrix) {
++  var site = arc.site,
++      rfocx = site[0],
++      rfocy = site[1],
++      pby2 = rfocy - directrix;
++
++  if (!pby2) return rfocx;
++
++  var lArc = arc.P;
++  if (!lArc) return -Infinity;
++
++  site = lArc.site;
++  var lfocx = site[0],
++      lfocy = site[1],
++      plby2 = lfocy - directrix;
++
++  if (!plby2) return lfocx;
++
++  var hl = lfocx - rfocx,
++      aby2 = 1 / pby2 - 1 / plby2,
++      b = hl / plby2;
++
++  if (aby2) return (-b + Math.sqrt(b * b - 2 * aby2 * (hl * hl / (-2 * plby2) - lfocy + plby2 / 2 + rfocy - pby2 / 2))) / aby2 + rfocx;
++
++  return (rfocx + lfocx) / 2;
++}
++
++function rightBreakPoint(arc, directrix) {
++  var rArc = arc.N;
++  if (rArc) return leftBreakPoint(rArc, directrix);
++  var site = arc.site;
++  return site[1] === directrix ? site[0] : Infinity;
++}
++
++var epsilon$4 = 1e-6;
++var epsilon2$2 = 1e-12;
++var beaches;
++var cells;
++var circles;
++var edges;
++
++function triangleArea(a, b, c) {
++  return (a[0] - c[0]) * (b[1] - a[1]) - (a[0] - b[0]) * (c[1] - a[1]);
++}
++
++function lexicographic(a, b) {
++  return b[1] - a[1]
++      || b[0] - a[0];
++}
++
++function Diagram(sites, extent) {
++  var site = sites.sort(lexicographic).pop(),
++      x,
++      y,
++      circle;
++
++  edges = [];
++  cells = new Array(sites.length);
++  beaches = new RedBlackTree;
++  circles = new RedBlackTree;
++
++  while (true) {
++    circle = firstCircle;
++    if (site && (!circle || site[1] < circle.y || (site[1] === circle.y && site[0] < circle.x))) {
++      if (site[0] !== x || site[1] !== y) {
++        addBeach(site);
++        x = site[0], y = site[1];
++      }
++      site = sites.pop();
++    } else if (circle) {
++      removeBeach(circle.arc);
++    } else {
++      break;
++    }
++  }
++
++  sortCellHalfedges();
++
++  if (extent) {
++    var x0 = +extent[0][0],
++        y0 = +extent[0][1],
++        x1 = +extent[1][0],
++        y1 = +extent[1][1];
++    clipEdges(x0, y0, x1, y1);
++    clipCells(x0, y0, x1, y1);
++  }
++
++  this.edges = edges;
++  this.cells = cells;
++
++  beaches =
++  circles =
++  edges =
++  cells = null;
++}
++
++Diagram.prototype = {
++  constructor: Diagram,
++
++  polygons: function() {
++    var edges = this.edges;
++
++    return this.cells.map(function(cell) {
++      var polygon = cell.halfedges.map(function(i) { return cellHalfedgeStart(cell, edges[i]); });
++      polygon.data = cell.site.data;
++      return polygon;
++    });
++  },
++
++  triangles: function() {
++    var triangles = [],
++        edges = this.edges;
++
++    this.cells.forEach(function(cell, i) {
++      if (!(m = (halfedges = cell.halfedges).length)) return;
++      var site = cell.site,
++          halfedges,
++          j = -1,
++          m,
++          s0,
++          e1 = edges[halfedges[m - 1]],
++          s1 = e1.left === site ? e1.right : e1.left;
++
++      while (++j < m) {
++        s0 = s1;
++        e1 = edges[halfedges[j]];
++        s1 = e1.left === site ? e1.right : e1.left;
++        if (s0 && s1 && i < s0.index && i < s1.index && triangleArea(site, s0, s1) < 0) {
++          triangles.push([site.data, s0.data, s1.data]);
++        }
++      }
++    });
++
++    return triangles;
++  },
++
++  links: function() {
++    return this.edges.filter(function(edge) {
++      return edge.right;
++    }).map(function(edge) {
++      return {
++        source: edge.left.data,
++        target: edge.right.data
++      };
++    });
++  },
++
++  find: function(x, y, radius) {
++    var that = this, i0, i1 = that._found || 0, n = that.cells.length, cell;
++
++    // Use the previously-found cell, or start with an arbitrary one.
++    while (!(cell = that.cells[i1])) if (++i1 >= n) return null;
++    var dx = x - cell.site[0], dy = y - cell.site[1], d2 = dx * dx + dy * dy;
++
++    // Traverse the half-edges to find a closer cell, if any.
++    do {
++      cell = that.cells[i0 = i1], i1 = null;
++      cell.halfedges.forEach(function(e) {
++        var edge = that.edges[e], v = edge.left;
++        if ((v === cell.site || !v) && !(v = edge.right)) return;
++        var vx = x - v[0], vy = y - v[1], v2 = vx * vx + vy * vy;
++        if (v2 < d2) d2 = v2, i1 = v.index;
++      });
++    } while (i1 !== null);
++
++    that._found = i0;
++
++    return radius == null || d2 <= radius * radius ? cell.site : null;
++  }
++};
++
++function voronoi() {
++  var x$$1 = x$4,
++      y$$1 = y$4,
++      extent = null;
++
++  function voronoi(data) {
++    return new Diagram(data.map(function(d, i) {
++      var s = [Math.round(x$$1(d, i, data) / epsilon$4) * epsilon$4, Math.round(y$$1(d, i, data) / epsilon$4) * epsilon$4];
++      s.index = i;
++      s.data = d;
++      return s;
++    }), extent);
++  }
++
++  voronoi.polygons = function(data) {
++    return voronoi(data).polygons();
++  };
++
++  voronoi.links = function(data) {
++    return voronoi(data).links();
++  };
++
++  voronoi.triangles = function(data) {
++    return voronoi(data).triangles();
++  };
++
++  voronoi.x = function(_) {
++    return arguments.length ? (x$$1 = typeof _ === "function" ? _ : constant$12(+_), voronoi) : x$$1;
++  };
++
++  voronoi.y = function(_) {
++    return arguments.length ? (y$$1 = typeof _ === "function" ? _ : constant$12(+_), voronoi) : y$$1;
++  };
++
++  voronoi.extent = function(_) {
++    return arguments.length ? (extent = _ == null ? null : [[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]], voronoi) : extent && [[extent[0][0], extent[0][1]], [extent[1][0], extent[1][1]]];
++  };
++
++  voronoi.size = function(_) {
++    return arguments.length ? (extent = _ == null ? null : [[0, 0], [+_[0], +_[1]]], voronoi) : extent && [extent[1][0] - extent[0][0], extent[1][1] - extent[0][1]];
++  };
++
++  return voronoi;
++}
++
++function constant$13(x) {
++  return function() {
++    return x;
++  };
++}
++
++function ZoomEvent(target, type, transform) {
++  this.target = target;
++  this.type = type;
++  this.transform = transform;
++}
++
++function Transform(k, x, y) {
++  this.k = k;
++  this.x = x;
++  this.y = y;
++}
++
++Transform.prototype = {
++  constructor: Transform,
++  scale: function(k) {
++    return k === 1 ? this : new Transform(this.k * k, this.x, this.y);
++  },
++  translate: function(x, y) {
++    return x === 0 & y === 0 ? this : new Transform(this.k, this.x + this.k * x, this.y + this.k * y);
++  },
++  apply: function(point) {
++    return [point[0] * this.k + this.x, point[1] * this.k + this.y];
++  },
++  applyX: function(x) {
++    return x * this.k + this.x;
++  },
++  applyY: function(y) {
++    return y * this.k + this.y;
++  },
++  invert: function(location) {
++    return [(location[0] - this.x) / this.k, (location[1] - this.y) / this.k];
++  },
++  invertX: function(x) {
++    return (x - this.x) / this.k;
++  },
++  invertY: function(y) {
++    return (y - this.y) / this.k;
++  },
++  rescaleX: function(x) {
++    return x.copy().domain(x.range().map(this.invertX, this).map(x.invert, x));
++  },
++  rescaleY: function(y) {
++    return y.copy().domain(y.range().map(this.invertY, this).map(y.invert, y));
++  },
++  toString: function() {
++    return "translate(" + this.x + "," + this.y + ") scale(" + this.k + ")";
++  }
++};
++
++var identity$8 = new Transform(1, 0, 0);
++
++transform$1.prototype = Transform.prototype;
++
++function transform$1(node) {
++  return node.__zoom || identity$8;
++}
++
++function nopropagation$2() {
++  exports.event.stopImmediatePropagation();
++}
++
++function noevent$2() {
++  exports.event.preventDefault();
++  exports.event.stopImmediatePropagation();
++}
++
++// Ignore right-click, since that should open the context menu.
++function defaultFilter$2() {
++  return !exports.event.button;
++}
++
++function defaultExtent$1() {
++  var e = this, w, h;
++  if (e instanceof SVGElement) {
++    e = e.ownerSVGElement || e;
++    w = e.width.baseVal.value;
++    h = e.height.baseVal.value;
++  } else {
++    w = e.clientWidth;
++    h = e.clientHeight;
++  }
++  return [[0, 0], [w, h]];
++}
++
++function defaultTransform() {
++  return this.__zoom || identity$8;
++}
++
++function defaultWheelDelta() {
++  return -exports.event.deltaY * (exports.event.deltaMode ? 120 : 1) / 500;
++}
++
++function defaultTouchable$1() {
++  return "ontouchstart" in this;
++}
++
++function defaultConstrain(transform, extent, translateExtent) {
++  var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0],
++      dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0],
++      dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1],
++      dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1];
++  return transform.translate(
++    dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1),
++    dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1)
++  );
++}
++
++function zoom() {
++  var filter = defaultFilter$2,
++      extent = defaultExtent$1,
++      constrain = defaultConstrain,
++      wheelDelta = defaultWheelDelta,
++      touchable = defaultTouchable$1,
++      scaleExtent = [0, Infinity],
++      translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]],
++      duration = 250,
++      interpolate = interpolateZoom,
++      gestures = [],
++      listeners = dispatch("start", "zoom", "end"),
++      touchstarting,
++      touchending,
++      touchDelay = 500,
++      wheelDelay = 150,
++      clickDistance2 = 0;
++
++  function zoom(selection$$1) {
++    selection$$1
++        .property("__zoom", defaultTransform)
++        .on("wheel.zoom", wheeled)
++        .on("mousedown.zoom", mousedowned)
++        .on("dblclick.zoom", dblclicked)
++      .filter(touchable)
++        .on("touchstart.zoom", touchstarted)
++        .on("touchmove.zoom", touchmoved)
++        .on("touchend.zoom touchcancel.zoom", touchended)
++        .style("touch-action", "none")
++        .style("-webkit-tap-highlight-color", "rgba(0,0,0,0)");
++  }
++
++  zoom.transform = function(collection, transform) {
++    var selection$$1 = collection.selection ? collection.selection() : collection;
++    selection$$1.property("__zoom", defaultTransform);
++    if (collection !== selection$$1) {
++      schedule(collection, transform);
++    } else {
++      selection$$1.interrupt().each(function() {
++        gesture(this, arguments)
++            .start()
++            .zoom(null, typeof transform === "function" ? transform.apply(this, arguments) : transform)
++            .end();
++      });
++    }
++  };
++
++  zoom.scaleBy = function(selection$$1, k) {
++    zoom.scaleTo(selection$$1, function() {
++      var k0 = this.__zoom.k,
++          k1 = typeof k === "function" ? k.apply(this, arguments) : k;
++      return k0 * k1;
++    });
++  };
++
++  zoom.scaleTo = function(selection$$1, k) {
++    zoom.transform(selection$$1, function() {
++      var e = extent.apply(this, arguments),
++          t0 = this.__zoom,
++          p0 = centroid(e),
++          p1 = t0.invert(p0),
++          k1 = typeof k === "function" ? k.apply(this, arguments) : k;
++      return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent);
++    });
++  };
++
++  zoom.translateBy = function(selection$$1, x, y) {
++    zoom.transform(selection$$1, function() {
++      return constrain(this.__zoom.translate(
++        typeof x === "function" ? x.apply(this, arguments) : x,
++        typeof y === "function" ? y.apply(this, arguments) : y
++      ), extent.apply(this, arguments), translateExtent);
++    });
++  };
++
++  zoom.translateTo = function(selection$$1, x, y) {
++    zoom.transform(selection$$1, function() {
++      var e = extent.apply(this, arguments),
++          t = this.__zoom,
++          p = centroid(e);
++      return constrain(identity$8.translate(p[0], p[1]).scale(t.k).translate(
++        typeof x === "function" ? -x.apply(this, arguments) : -x,
++        typeof y === "function" ? -y.apply(this, arguments) : -y
++      ), e, translateExtent);
++    });
++  };
++
++  function scale(transform, k) {
++    k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k));
++    return k === transform.k ? transform : new Transform(k, transform.x, transform.y);
++  }
++
++  function translate(transform, p0, p1) {
++    var x = p0[0] - p1[0] * transform.k, y = p0[1] - p1[1] * transform.k;
++    return x === transform.x && y === transform.y ? transform : new Transform(transform.k, x, y);
++  }
++
++  function centroid(extent) {
++    return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2];
++  }
++
++  function schedule(transition$$1, transform, center) {
++    transition$$1
++        .on("start.zoom", function() { gesture(this, arguments).start(); })
++        .on("interrupt.zoom end.zoom", function() { gesture(this, arguments).end(); })
++        .tween("zoom", function() {
++          var that = this,
++              args = arguments,
++              g = gesture(that, args),
++              e = extent.apply(that, args),
++              p = center || centroid(e),
++              w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]),
++              a = that.__zoom,
++              b = typeof transform === "function" ? transform.apply(that, args) : transform,
++              i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k));
++          return function(t) {
++            if (t === 1) t = b; // Avoid rounding error on end.
++            else { var l = i(t), k = w / l[2]; t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k); }
++            g.zoom(null, t);
++          };
++        });
++  }
++
++  function gesture(that, args) {
++    for (var i = 0, n = gestures.length, g; i < n; ++i) {
++      if ((g = gestures[i]).that === that) {
++        return g;
++      }
++    }
++    return new Gesture(that, args);
++  }
++
++  function Gesture(that, args) {
++    this.that = that;
++    this.args = args;
++    this.index = -1;
++    this.active = 0;
++    this.extent = extent.apply(that, args);
++  }
++
++  Gesture.prototype = {
++    start: function() {
++      if (++this.active === 1) {
++        this.index = gestures.push(this) - 1;
++        this.emit("start");
++      }
++      return this;
++    },
++    zoom: function(key, transform) {
++      if (this.mouse && key !== "mouse") this.mouse[1] = transform.invert(this.mouse[0]);
++      if (this.touch0 && key !== "touch") this.touch0[1] = transform.invert(this.touch0[0]);
++      if (this.touch1 && key !== "touch") this.touch1[1] = transform.invert(this.touch1[0]);
++      this.that.__zoom = transform;
++      this.emit("zoom");
++      return this;
++    },
++    end: function() {
++      if (--this.active === 0) {
++        gestures.splice(this.index, 1);
++        this.index = -1;
++        this.emit("end");
++      }
++      return this;
++    },
++    emit: function(type) {
++      customEvent(new ZoomEvent(zoom, type, this.that.__zoom), listeners.apply, listeners, [type, this.that, this.args]);
++    }
++  };
++
++  function wheeled() {
++    if (!filter.apply(this, arguments)) return;
++    var g = gesture(this, arguments),
++        t = this.__zoom,
++        k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))),
++        p = mouse(this);
++
++    // If the mouse is in the same location as before, reuse it.
++    // If there were recent wheel events, reset the wheel idle timeout.
++    if (g.wheel) {
++      if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) {
++        g.mouse[1] = t.invert(g.mouse[0] = p);
++      }
++      clearTimeout(g.wheel);
++    }
++
++    // If this wheel event won’t trigger a transform change, ignore it.
++    else if (t.k === k) return;
++
++    // Otherwise, capture the mouse point and location at the start.
++    else {
++      g.mouse = [p, t.invert(p)];
++      interrupt(this);
++      g.start();
++    }
++
++    noevent$2();
++    g.wheel = setTimeout(wheelidled, wheelDelay);
++    g.zoom("mouse", constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent));
++
++    function wheelidled() {
++      g.wheel = null;
++      g.end();
++    }
++  }
++
++  function mousedowned() {
++    if (touchending || !filter.apply(this, arguments)) return;
++    var g = gesture(this, arguments),
++        v = select(exports.event.view).on("mousemove.zoom", mousemoved, true).on("mouseup.zoom", mouseupped, true),
++        p = mouse(this),
++        x0 = exports.event.clientX,
++        y0 = exports.event.clientY;
++
++    dragDisable(exports.event.view);
++    nopropagation$2();
++    g.mouse = [p, this.__zoom.invert(p)];
++    interrupt(this);
++    g.start();
++
++    function mousemoved() {
++      noevent$2();
++      if (!g.moved) {
++        var dx = exports.event.clientX - x0, dy = exports.event.clientY - y0;
++        g.moved = dx * dx + dy * dy > clickDistance2;
++      }
++      g.zoom("mouse", constrain(translate(g.that.__zoom, g.mouse[0] = mouse(g.that), g.mouse[1]), g.extent, translateExtent));
++    }
++
++    function mouseupped() {
++      v.on("mousemove.zoom mouseup.zoom", null);
++      yesdrag(exports.event.view, g.moved);
++      noevent$2();
++      g.end();
++    }
++  }
++
++  function dblclicked() {
++    if (!filter.apply(this, arguments)) return;
++    var t0 = this.__zoom,
++        p0 = mouse(this),
++        p1 = t0.invert(p0),
++        k1 = t0.k * (exports.event.shiftKey ? 0.5 : 2),
++        t1 = constrain(translate(scale(t0, k1), p0, p1), extent.apply(this, arguments), translateExtent);
++
++    noevent$2();
++    if (duration > 0) select(this).transition().duration(duration).call(schedule, t1, p0);
++    else select(this).call(zoom.transform, t1);
++  }
++
++  function touchstarted() {
++    if (!filter.apply(this, arguments)) return;
++    var g = gesture(this, arguments),
++        touches$$1 = exports.event.changedTouches,
++        started,
++        n = touches$$1.length, i, t, p;
++
++    nopropagation$2();
++    for (i = 0; i < n; ++i) {
++      t = touches$$1[i], p = touch(this, touches$$1, t.identifier);
++      p = [p, this.__zoom.invert(p), t.identifier];
++      if (!g.touch0) g.touch0 = p, started = true;
++      else if (!g.touch1) g.touch1 = p;
++    }
++
++    // If this is a dbltap, reroute to the (optional) dblclick.zoom handler.
++    if (touchstarting) {
++      touchstarting = clearTimeout(touchstarting);
++      if (!g.touch1) {
++        g.end();
++        p = select(this).on("dblclick.zoom");
++        if (p) p.apply(this, arguments);
++        return;
++      }
++    }
++
++    if (started) {
++      touchstarting = setTimeout(function() { touchstarting = null; }, touchDelay);
++      interrupt(this);
++      g.start();
++    }
++  }
++
++  function touchmoved() {
++    var g = gesture(this, arguments),
++        touches$$1 = exports.event.changedTouches,
++        n = touches$$1.length, i, t, p, l;
++
++    noevent$2();
++    if (touchstarting) touchstarting = clearTimeout(touchstarting);
++    for (i = 0; i < n; ++i) {
++      t = touches$$1[i], p = touch(this, touches$$1, t.identifier);
++      if (g.touch0 && g.touch0[2] === t.identifier) g.touch0[0] = p;
++      else if (g.touch1 && g.touch1[2] === t.identifier) g.touch1[0] = p;
++    }
++    t = g.that.__zoom;
++    if (g.touch1) {
++      var p0 = g.touch0[0], l0 = g.touch0[1],
++          p1 = g.touch1[0], l1 = g.touch1[1],
++          dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp,
++          dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl;
++      t = scale(t, Math.sqrt(dp / dl));
++      p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];
++      l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];
++    }
++    else if (g.touch0) p = g.touch0[0], l = g.touch0[1];
++    else return;
++    g.zoom("touch", constrain(translate(t, p, l), g.extent, translateExtent));
++  }
++
++  function touchended() {
++    var g = gesture(this, arguments),
++        touches$$1 = exports.event.changedTouches,
++        n = touches$$1.length, i, t;
++
++    nopropagation$2();
++    if (touchending) clearTimeout(touchending);
++    touchending = setTimeout(function() { touchending = null; }, touchDelay);
++    for (i = 0; i < n; ++i) {
++      t = touches$$1[i];
++      if (g.touch0 && g.touch0[2] === t.identifier) delete g.touch0;
++      else if (g.touch1 && g.touch1[2] === t.identifier) delete g.touch1;
++    }
++    if (g.touch1 && !g.touch0) g.touch0 = g.touch1, delete g.touch1;
++    if (g.touch0) g.touch0[1] = this.__zoom.invert(g.touch0[0]);
++    else g.end();
++  }
++
++  zoom.wheelDelta = function(_) {
++    return arguments.length ? (wheelDelta = typeof _ === "function" ? _ : constant$13(+_), zoom) : wheelDelta;
++  };
++
++  zoom.filter = function(_) {
++    return arguments.length ? (filter = typeof _ === "function" ? _ : constant$13(!!_), zoom) : filter;
++  };
++
++  zoom.touchable = function(_) {
++    return arguments.length ? (touchable = typeof _ === "function" ? _ : constant$13(!!_), zoom) : touchable;
++  };
++
++  zoom.extent = function(_) {
++    return arguments.length ? (extent = typeof _ === "function" ? _ : constant$13([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent;
++  };
++
++  zoom.scaleExtent = function(_) {
++    return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]];
++  };
++
++  zoom.translateExtent = function(_) {
++    return arguments.length ? (translateExtent[0][0] = +_[0][0], translateExtent[1][0] = +_[1][0], translateExtent[0][1] = +_[0][1], translateExtent[1][1] = +_[1][1], zoom) : [[translateExtent[0][0], translateExtent[0][1]], [translateExtent[1][0], translateExtent[1][1]]];
++  };
++
++  zoom.constrain = function(_) {
++    return arguments.length ? (constrain = _, zoom) : constrain;
++  };
++
++  zoom.duration = function(_) {
++    return arguments.length ? (duration = +_, zoom) : duration;
++  };
++
++  zoom.interpolate = function(_) {
++    return arguments.length ? (interpolate = _, zoom) : interpolate;
++  };
++
++  zoom.on = function() {
++    var value = listeners.on.apply(listeners, arguments);
++    return value === listeners ? zoom : value;
++  };
++
++  zoom.clickDistance = function(_) {
++    return arguments.length ? (clickDistance2 = (_ = +_) * _, zoom) : Math.sqrt(clickDistance2);
++  };
++
++  return zoom;
++}
++
++exports.version = version;
++exports.bisect = bisectRight;
++exports.bisectRight = bisectRight;
++exports.bisectLeft = bisectLeft;
++exports.ascending = ascending;
++exports.bisector = bisector;
++exports.cross = cross;
++exports.descending = descending;
++exports.deviation = deviation;
++exports.extent = extent;
++exports.histogram = histogram;
++exports.thresholdFreedmanDiaconis = freedmanDiaconis;
++exports.thresholdScott = scott;
++exports.thresholdSturges = thresholdSturges;
++exports.max = max;
++exports.mean = mean;
++exports.median = median;
++exports.merge = merge;
++exports.min = min;
++exports.pairs = pairs;
++exports.permute = permute;
++exports.quantile = threshold;
++exports.range = sequence;
++exports.scan = scan;
++exports.shuffle = shuffle;
++exports.sum = sum;
++exports.ticks = ticks;
++exports.tickIncrement = tickIncrement;
++exports.tickStep = tickStep;
++exports.transpose = transpose;
++exports.variance = variance;
++exports.zip = zip;
++exports.axisTop = axisTop;
++exports.axisRight = axisRight;
++exports.axisBottom = axisBottom;
++exports.axisLeft = axisLeft;
++exports.brush = brush;
++exports.brushX = brushX;
++exports.brushY = brushY;
++exports.brushSelection = brushSelection;
++exports.chord = chord;
++exports.ribbon = ribbon;
++exports.nest = nest;
++exports.set = set$2;
++exports.map = map$1;
++exports.keys = keys;
++exports.values = values;
++exports.entries = entries;
++exports.color = color;
++exports.rgb = rgb;
++exports.hsl = hsl;
++exports.lab = lab;
++exports.hcl = hcl;
++exports.lch = lch;
++exports.gray = gray;
++exports.cubehelix = cubehelix;
++exports.contours = contours;
++exports.contourDensity = density;
++exports.dispatch = dispatch;
++exports.drag = drag;
++exports.dragDisable = dragDisable;
++exports.dragEnable = yesdrag;
++exports.dsvFormat = dsvFormat;
++exports.csvParse = csvParse;
++exports.csvParseRows = csvParseRows;
++exports.csvFormat = csvFormat;
++exports.csvFormatRows = csvFormatRows;
++exports.tsvParse = tsvParse;
++exports.tsvParseRows = tsvParseRows;
++exports.tsvFormat = tsvFormat;
++exports.tsvFormatRows = tsvFormatRows;
++exports.easeLinear = linear$1;
++exports.easeQuad = quadInOut;
++exports.easeQuadIn = quadIn;
++exports.easeQuadOut = quadOut;
++exports.easeQuadInOut = quadInOut;
++exports.easeCubic = cubicInOut;
++exports.easeCubicIn = cubicIn;
++exports.easeCubicOut = cubicOut;
++exports.easeCubicInOut = cubicInOut;
++exports.easePoly = polyInOut;
++exports.easePolyIn = polyIn;
++exports.easePolyOut = polyOut;
++exports.easePolyInOut = polyInOut;
++exports.easeSin = sinInOut;
++exports.easeSinIn = sinIn;
++exports.easeSinOut = sinOut;
++exports.easeSinInOut = sinInOut;
++exports.easeExp = expInOut;
++exports.easeExpIn = expIn;
++exports.easeExpOut = expOut;
++exports.easeExpInOut = expInOut;
++exports.easeCircle = circleInOut;
++exports.easeCircleIn = circleIn;
++exports.easeCircleOut = circleOut;
++exports.easeCircleInOut = circleInOut;
++exports.easeBounce = bounceOut;
++exports.easeBounceIn = bounceIn;
++exports.easeBounceOut = bounceOut;
++exports.easeBounceInOut = bounceInOut;
++exports.easeBack = backInOut;
++exports.easeBackIn = backIn;
++exports.easeBackOut = backOut;
++exports.easeBackInOut = backInOut;
++exports.easeElastic = elasticOut;
++exports.easeElasticIn = elasticIn;
++exports.easeElasticOut = elasticOut;
++exports.easeElasticInOut = elasticInOut;
++exports.blob = blob;
++exports.buffer = buffer;
++exports.dsv = dsv;
++exports.csv = csv$1;
++exports.tsv = tsv$1;
++exports.image = image;
++exports.json = json;
++exports.text = text;
++exports.xml = xml;
++exports.html = html;
++exports.svg = svg;
++exports.forceCenter = center$1;
++exports.forceCollide = collide;
++exports.forceLink = link;
++exports.forceManyBody = manyBody;
++exports.forceRadial = radial;
++exports.forceSimulation = simulation;
++exports.forceX = x$2;
++exports.forceY = y$2;
++exports.formatDefaultLocale = defaultLocale;
++exports.formatLocale = formatLocale;
++exports.formatSpecifier = formatSpecifier;
++exports.precisionFixed = precisionFixed;
++exports.precisionPrefix = precisionPrefix;
++exports.precisionRound = precisionRound;
++exports.geoArea = area$1;
++exports.geoBounds = bounds;
++exports.geoCentroid = centroid;
++exports.geoCircle = circle;
++exports.geoClipAntimeridian = clipAntimeridian;
++exports.geoClipCircle = clipCircle;
++exports.geoClipExtent = extent$1;
++exports.geoClipRectangle = clipRectangle;
++exports.geoContains = contains$1;
++exports.geoDistance = distance;
++exports.geoGraticule = graticule;
++exports.geoGraticule10 = graticule10;
++exports.geoInterpolate = interpolate$1;
++exports.geoLength = length$1;
++exports.geoPath = index$1;
++exports.geoAlbers = albers;
++exports.geoAlbersUsa = albersUsa;
++exports.geoAzimuthalEqualArea = azimuthalEqualArea;
++exports.geoAzimuthalEqualAreaRaw = azimuthalEqualAreaRaw;
++exports.geoAzimuthalEquidistant = azimuthalEquidistant;
++exports.geoAzimuthalEquidistantRaw = azimuthalEquidistantRaw;
++exports.geoConicConformal = conicConformal;
++exports.geoConicConformalRaw = conicConformalRaw;
++exports.geoConicEqualArea = conicEqualArea;
++exports.geoConicEqualAreaRaw = conicEqualAreaRaw;
++exports.geoConicEquidistant = conicEquidistant;
++exports.geoConicEquidistantRaw = conicEquidistantRaw;
++exports.geoEquirectangular = equirectangular;
++exports.geoEquirectangularRaw = equirectangularRaw;
++exports.geoGnomonic = gnomonic;
++exports.geoGnomonicRaw = gnomonicRaw;
++exports.geoIdentity = identity$5;
++exports.geoProjection = projection;
++exports.geoProjectionMutator = projectionMutator;
++exports.geoMercator = mercator;
++exports.geoMercatorRaw = mercatorRaw;
++exports.geoNaturalEarth1 = naturalEarth1;
++exports.geoNaturalEarth1Raw = naturalEarth1Raw;
++exports.geoOrthographic = orthographic;
++exports.geoOrthographicRaw = orthographicRaw;
++exports.geoStereographic = stereographic;
++exports.geoStereographicRaw = stereographicRaw;
++exports.geoTransverseMercator = transverseMercator;
++exports.geoTransverseMercatorRaw = transverseMercatorRaw;
++exports.geoRotation = rotation;
++exports.geoStream = geoStream;
++exports.geoTransform = transform;
++exports.cluster = cluster;
++exports.hierarchy = hierarchy;
++exports.pack = index$2;
++exports.packSiblings = siblings;
++exports.packEnclose = enclose;
++exports.partition = partition;
++exports.stratify = stratify;
++exports.tree = tree;
++exports.treemap = index$3;
++exports.treemapBinary = binary;
++exports.treemapDice = treemapDice;
++exports.treemapSlice = treemapSlice;
++exports.treemapSliceDice = sliceDice;
++exports.treemapSquarify = squarify;
++exports.treemapResquarify = resquarify;
++exports.interpolate = interpolateValue;
++exports.interpolateArray = array$1;
++exports.interpolateBasis = basis$1;
++exports.interpolateBasisClosed = basisClosed;
++exports.interpolateDate = date;
++exports.interpolateNumber = reinterpolate;
++exports.interpolateObject = object;
++exports.interpolateRound = interpolateRound;
++exports.interpolateString = interpolateString;
++exports.interpolateTransformCss = interpolateTransformCss;
++exports.interpolateTransformSvg = interpolateTransformSvg;
++exports.interpolateZoom = interpolateZoom;
++exports.interpolateRgb = interpolateRgb;
++exports.interpolateRgbBasis = rgbBasis;
++exports.interpolateRgbBasisClosed = rgbBasisClosed;
++exports.interpolateHsl = hsl$2;
++exports.interpolateHslLong = hslLong;
++exports.interpolateLab = lab$1;
++exports.interpolateHcl = hcl$2;
++exports.interpolateHclLong = hclLong;
++exports.interpolateCubehelix = cubehelix$2;
++exports.interpolateCubehelixLong = cubehelixLong;
++exports.piecewise = piecewise;
++exports.quantize = quantize;
++exports.path = path;
++exports.polygonArea = area$2;
++exports.polygonCentroid = centroid$1;
++exports.polygonHull = hull;
++exports.polygonContains = contains$2;
++exports.polygonLength = length$2;
++exports.quadtree = quadtree;
++exports.randomUniform = uniform;
++exports.randomNormal = normal;
++exports.randomLogNormal = logNormal;
++exports.randomBates = bates;
++exports.randomIrwinHall = irwinHall;
++exports.randomExponential = exponential$1;
++exports.scaleBand = band;
++exports.scalePoint = point$1;
++exports.scaleIdentity = identity$6;
++exports.scaleLinear = linear$2;
++exports.scaleLog = log$1;
++exports.scaleOrdinal = ordinal;
++exports.scaleImplicit = implicit;
++exports.scalePow = pow$1;
++exports.scaleSqrt = sqrt$1;
++exports.scaleQuantile = quantile$$1;
++exports.scaleQuantize = quantize$1;
++exports.scaleThreshold = threshold$1;
++exports.scaleTime = time;
++exports.scaleUtc = utcTime;
++exports.scaleSequential = sequential;
++exports.scaleDiverging = diverging;
++exports.schemeCategory10 = category10;
++exports.schemeAccent = Accent;
++exports.schemeDark2 = Dark2;
++exports.schemePaired = Paired;
++exports.schemePastel1 = Pastel1;
++exports.schemePastel2 = Pastel2;
++exports.schemeSet1 = Set1;
++exports.schemeSet2 = Set2;
++exports.schemeSet3 = Set3;
++exports.interpolateBrBG = BrBG;
++exports.schemeBrBG = scheme;
++exports.interpolatePRGn = PRGn;
++exports.schemePRGn = scheme$1;
++exports.interpolatePiYG = PiYG;
++exports.schemePiYG = scheme$2;
++exports.interpolatePuOr = PuOr;
++exports.schemePuOr = scheme$3;
++exports.interpolateRdBu = RdBu;
++exports.schemeRdBu = scheme$4;
++exports.interpolateRdGy = RdGy;
++exports.schemeRdGy = scheme$5;
++exports.interpolateRdYlBu = RdYlBu;
++exports.schemeRdYlBu = scheme$6;
++exports.interpolateRdYlGn = RdYlGn;
++exports.schemeRdYlGn = scheme$7;
++exports.interpolateSpectral = Spectral;
++exports.schemeSpectral = scheme$8;
++exports.interpolateBuGn = BuGn;
++exports.schemeBuGn = scheme$9;
++exports.interpolateBuPu = BuPu;
++exports.schemeBuPu = scheme$10;
++exports.interpolateGnBu = GnBu;
++exports.schemeGnBu = scheme$11;
++exports.interpolateOrRd = OrRd;
++exports.schemeOrRd = scheme$12;
++exports.interpolatePuBuGn = PuBuGn;
++exports.schemePuBuGn = scheme$13;
++exports.interpolatePuBu = PuBu;
++exports.schemePuBu = scheme$14;
++exports.interpolatePuRd = PuRd;
++exports.schemePuRd = scheme$15;
++exports.interpolateRdPu = RdPu;
++exports.schemeRdPu = scheme$16;
++exports.interpolateYlGnBu = YlGnBu;
++exports.schemeYlGnBu = scheme$17;
++exports.interpolateYlGn = YlGn;
++exports.schemeYlGn = scheme$18;
++exports.interpolateYlOrBr = YlOrBr;
++exports.schemeYlOrBr = scheme$19;
++exports.interpolateYlOrRd = YlOrRd;
++exports.schemeYlOrRd = scheme$20;
++exports.interpolateBlues = Blues;
++exports.schemeBlues = scheme$21;
++exports.interpolateGreens = Greens;
++exports.schemeGreens = scheme$22;
++exports.interpolateGreys = Greys;
++exports.schemeGreys = scheme$23;
++exports.interpolatePurples = Purples;
++exports.schemePurples = scheme$24;
++exports.interpolateReds = Reds;
++exports.schemeReds = scheme$25;
++exports.interpolateOranges = Oranges;
++exports.schemeOranges = scheme$26;
++exports.interpolateCubehelixDefault = cubehelix$3;
++exports.interpolateRainbow = rainbow;
++exports.interpolateWarm = warm;
++exports.interpolateCool = cool;
++exports.interpolateSinebow = sinebow;
++exports.interpolateViridis = viridis;
++exports.interpolateMagma = magma;
++exports.interpolateInferno = inferno;
++exports.interpolatePlasma = plasma;
++exports.create = create;
++exports.creator = creator;
++exports.local = local;
++exports.matcher = matcher$1;
++exports.mouse = mouse;
++exports.namespace = namespace;
++exports.namespaces = namespaces;
++exports.clientPoint = point;
++exports.select = select;
++exports.selectAll = selectAll;
++exports.selection = selection;
++exports.selector = selector;
++exports.selectorAll = selectorAll;
++exports.style = styleValue;
++exports.touch = touch;
++exports.touches = touches;
++exports.window = defaultView;
++exports.customEvent = customEvent;
++exports.arc = arc;
++exports.area = area$3;
++exports.line = line;
++exports.pie = pie;
++exports.areaRadial = areaRadial;
++exports.radialArea = areaRadial;
++exports.lineRadial = lineRadial$1;
++exports.radialLine = lineRadial$1;
++exports.pointRadial = pointRadial;
++exports.linkHorizontal = linkHorizontal;
++exports.linkVertical = linkVertical;
++exports.linkRadial = linkRadial;
++exports.symbol = symbol;
++exports.symbols = symbols;
++exports.symbolCircle = circle$2;
++exports.symbolCross = cross$2;
++exports.symbolDiamond = diamond;
++exports.symbolSquare = square;
++exports.symbolStar = star;
++exports.symbolTriangle = triangle;
++exports.symbolWye = wye;
++exports.curveBasisClosed = basisClosed$1;
++exports.curveBasisOpen = basisOpen;
++exports.curveBasis = basis$2;
++exports.curveBundle = bundle;
++exports.curveCardinalClosed = cardinalClosed;
++exports.curveCardinalOpen = cardinalOpen;
++exports.curveCardinal = cardinal;
++exports.curveCatmullRomClosed = catmullRomClosed;
++exports.curveCatmullRomOpen = catmullRomOpen;
++exports.curveCatmullRom = catmullRom;
++exports.curveLinearClosed = linearClosed;
++exports.curveLinear = curveLinear;
++exports.curveMonotoneX = monotoneX;
++exports.curveMonotoneY = monotoneY;
++exports.curveNatural = natural;
++exports.curveStep = step;
++exports.curveStepAfter = stepAfter;
++exports.curveStepBefore = stepBefore;
++exports.stack = stack;
++exports.stackOffsetExpand = expand;
++exports.stackOffsetDiverging = diverging$1;
++exports.stackOffsetNone = none$1;
++exports.stackOffsetSilhouette = silhouette;
++exports.stackOffsetWiggle = wiggle;
++exports.stackOrderAscending = ascending$3;
++exports.stackOrderDescending = descending$2;
++exports.stackOrderInsideOut = insideOut;
++exports.stackOrderNone = none$2;
++exports.stackOrderReverse = reverse;
++exports.timeInterval = newInterval;
++exports.timeMillisecond = millisecond;
++exports.timeMilliseconds = milliseconds;
++exports.utcMillisecond = millisecond;
++exports.utcMilliseconds = milliseconds;
++exports.timeSecond = second;
++exports.timeSeconds = seconds;
++exports.utcSecond = second;
++exports.utcSeconds = seconds;
++exports.timeMinute = minute;
++exports.timeMinutes = minutes;
++exports.timeHour = hour;
++exports.timeHours = hours;
++exports.timeDay = day;
++exports.timeDays = days;
++exports.timeWeek = sunday;
++exports.timeWeeks = sundays;
++exports.timeSunday = sunday;
++exports.timeSundays = sundays;
++exports.timeMonday = monday;
++exports.timeMondays = mondays;
++exports.timeTuesday = tuesday;
++exports.timeTuesdays = tuesdays;
++exports.timeWednesday = wednesday;
++exports.timeWednesdays = wednesdays;
++exports.timeThursday = thursday;
++exports.timeThursdays = thursdays;
++exports.timeFriday = friday;
++exports.timeFridays = fridays;
++exports.timeSaturday = saturday;
++exports.timeSaturdays = saturdays;
++exports.timeMonth = month;
++exports.timeMonths = months;
++exports.timeYear = year;
++exports.timeYears = years;
++exports.utcMinute = utcMinute;
++exports.utcMinutes = utcMinutes;
++exports.utcHour = utcHour;
++exports.utcHours = utcHours;
++exports.utcDay = utcDay;
++exports.utcDays = utcDays;
++exports.utcWeek = utcSunday;
++exports.utcWeeks = utcSundays;
++exports.utcSunday = utcSunday;
++exports.utcSundays = utcSundays;
++exports.utcMonday = utcMonday;
++exports.utcMondays = utcMondays;
++exports.utcTuesday = utcTuesday;
++exports.utcTuesdays = utcTuesdays;
++exports.utcWednesday = utcWednesday;
++exports.utcWednesdays = utcWednesdays;
++exports.utcThursday = utcThursday;
++exports.utcThursdays = utcThursdays;
++exports.utcFriday = utcFriday;
++exports.utcFridays = utcFridays;
++exports.utcSaturday = utcSaturday;
++exports.utcSaturdays = utcSaturdays;
++exports.utcMonth = utcMonth;
++exports.utcMonths = utcMonths;
++exports.utcYear = utcYear;
++exports.utcYears = utcYears;
++exports.timeFormatDefaultLocale = defaultLocale$1;
++exports.timeFormatLocale = formatLocale$1;
++exports.isoFormat = formatIso;
++exports.isoParse = parseIso;
++exports.now = now;
++exports.timer = timer;
++exports.timerFlush = timerFlush;
++exports.timeout = timeout$1;
++exports.interval = interval$1;
++exports.transition = transition;
++exports.active = active;
++exports.interrupt = interrupt;
++exports.voronoi = voronoi;
++exports.zoom = zoom;
++exports.zoomTransform = transform$1;
++exports.zoomIdentity = identity$8;
++
++Object.defineProperty(exports, '__esModule', { value: true });
++
++})));
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7fc60fca78b6201f814da4b2906cae2b69ee9610
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,11008 @@@
++/*!
++ * jQuery JavaScript Library v1.12.4
++ * http://jquery.com/
++ *
++ * Includes Sizzle.js
++ * http://sizzlejs.com/
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license
++ * http://jquery.org/license
++ *
++ * Date: 2016-05-20T17:17Z
++ */
++
++(function( global, factory ) {
++
++      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 );
++      }
++
++// Pass this if window is not defined yet
++}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
++
++// Support: Firefox 18+
++// Can't be in strict mode, several libs including ASP.NET trace
++// the stack via arguments.caller.callee and Firefox dies if
++// you try to trace through "use strict" call chains. (#13335)
++//"use strict";
++var deletedIds = [];
++
++var document = window.document;
++
++var slice = deletedIds.slice;
++
++var concat = deletedIds.concat;
++
++var push = deletedIds.push;
++
++var indexOf = deletedIds.indexOf;
++
++var class2type = {};
++
++var toString = class2type.toString;
++
++var hasOwn = class2type.hasOwnProperty;
++
++var support = {};
++
++
++
++var
++      version = "1.12.4",
++
++      // Define a local copy of jQuery
++      jQuery = function( selector, context ) {
++
++              // The jQuery object is actually just the init constructor 'enhanced'
++              // Need init if jQuery is called (just allow error to be thrown if not included)
++              return new jQuery.fn.init( selector, context );
++      },
++
++      // Support: Android<4.1, IE<9
++      // Make sure we trim BOM and NBSP
++      rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
++
++      // Matches dashed string for camelizing
++      rmsPrefix = /^-ms-/,
++      rdashAlpha = /-([\da-z])/gi,
++
++      // Used by jQuery.camelCase as callback to replace()
++      fcamelCase = function( all, letter ) {
++              return letter.toUpperCase();
++      };
++
++jQuery.fn = jQuery.prototype = {
++
++      // The current version of jQuery being used
++      jquery: version,
++
++      constructor: jQuery,
++
++      // Start with an empty selector
++      selector: "",
++
++      // The default length of a jQuery object is 0
++      length: 0,
++
++      toArray: function() {
++              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 just the one element from the set
++                      ( num < 0 ? this[ num + this.length ] : this[ num ] ) :
++
++                      // Return all the elements in a clean array
++                      slice.call( this );
++      },
++
++      // Take an array of elements and push it onto the stack
++      // (returning the new matched element set)
++      pushStack: function( elems ) {
++
++              // Build a new jQuery matched element set
++              var ret = jQuery.merge( this.constructor(), elems );
++
++              // 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.
++      each: function( callback ) {
++              return jQuery.each( this, callback );
++      },
++
++      map: function( callback ) {
++              return this.pushStack( jQuery.map( this, function( elem, i ) {
++                      return callback.call( elem, i, elem );
++              } ) );
++      },
++
++      slice: function() {
++              return this.pushStack( slice.apply( this, arguments ) );
++      },
++
++      first: function() {
++              return this.eq( 0 );
++      },
++
++      last: function() {
++              return this.eq( -1 );
++      },
++
++      eq: function( i ) {
++              var len = this.length,
++                      j = +i + ( i < 0 ? len : 0 );
++              return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] );
++      },
++
++      end: function() {
++              return this.prevObject || this.constructor();
++      },
++
++      // For internal use only.
++      // Behaves like an Array's method, not like a jQuery method.
++      push: push,
++      sort: deletedIds.sort,
++      splice: deletedIds.splice
++};
++
++jQuery.extend = jQuery.fn.extend = function() {
++      var src, copyIsArray, copy, name, options, clone,
++              target = arguments[ 0 ] || {},
++              i = 1,
++              length = arguments.length,
++              deep = false;
++
++      // Handle a deep copy situation
++      if ( typeof target === "boolean" ) {
++              deep = target;
++
++              // 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 ) ) {
++              target = {};
++      }
++
++      // extend jQuery itself if only one argument is passed
++      if ( i === length ) {
++              target = this;
++              i--;
++      }
++
++      for ( ; i < length; i++ ) {
++
++              // Only deal with non-null/undefined values
++              if ( ( options = arguments[ i ] ) != null ) {
++
++                      // Extend the base object
++                      for ( name in options ) {
++                              src = target[ name ];
++                              copy = options[ name ];
++
++                              // Prevent never-ending loop
++                              if ( 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 : [];
++
++                                      } else {
++                                              clone = src && jQuery.isPlainObject( src ) ? src : {};
++                                      }
++
++                                      // Never move original objects, clone them
++                                      target[ name ] = jQuery.extend( deep, clone, copy );
++
++                              // Don't bring in undefined values
++                              } else if ( copy !== undefined ) {
++                                      target[ name ] = copy;
++                              }
++                      }
++              }
++      }
++
++      // Return the modified object
++      return target;
++};
++
++jQuery.extend( {
++
++      // Unique for each copy of jQuery on the page
++      expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),
++
++      // Assume jQuery is ready without the ready module
++      isReady: true,
++
++      error: function( msg ) {
++              throw new Error( msg );
++      },
++
++      noop: function() {},
++
++      // 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";
++      },
++
++      isArray: Array.isArray || function( obj ) {
++              return jQuery.type( obj ) === "array";
++      },
++
++      isWindow: function( obj ) {
++              /* jshint eqeqeq: false */
++              return obj != null && obj == obj.window;
++      },
++
++      isNumeric: function( obj ) {
++
++              // parseFloat NaNs numeric-cast false positives (null|true|false|"")
++              // ...but misinterprets leading-number strings, particularly hex literals ("0x...")
++              // subtraction forces infinities to NaN
++              // adding 1 corrects loss of precision from parseFloat (#15100)
++              var realStringObj = obj && obj.toString();
++              return !jQuery.isArray( obj ) && ( realStringObj - parseFloat( realStringObj ) + 1 ) >= 0;
++      },
++
++      isEmptyObject: function( obj ) {
++              var name;
++              for ( name in obj ) {
++                      return false;
++              }
++              return true;
++      },
++
++      isPlainObject: function( obj ) {
++              var key;
++
++              // 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 ) ) {
++                      return false;
++              }
++
++              try {
++
++                      // Not own constructor property must be Object
++                      if ( obj.constructor &&
++                              !hasOwn.call( obj, "constructor" ) &&
++                              !hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) {
++                              return false;
++                      }
++              } catch ( e ) {
++
++                      // IE8,9 Will throw exceptions on certain host objects #9897
++                      return false;
++              }
++
++              // Support: IE<9
++              // Handle iteration over inherited properties before own properties.
++              if ( !support.ownFirst ) {
++                      for ( key in obj ) {
++                              return hasOwn.call( obj, key );
++                      }
++              }
++
++              // 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 || hasOwn.call( obj, key );
++      },
++
++      type: function( obj ) {
++              if ( obj == null ) {
++                      return obj + "";
++              }
++              return typeof obj === "object" || typeof obj === "function" ?
++                      class2type[ toString.call( obj ) ] || "object" :
++                      typeof obj;
++      },
++
++      // 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 ); // jscs:ignore requireDotNotation
++                      } )( data );
++              }
++      },
++
++      // 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();
++      },
++
++      each: function( obj, callback ) {
++              var length, i = 0;
++
++              if ( isArrayLike( obj ) ) {
++                      length = obj.length;
++                      for ( ; i < length; i++ ) {
++                              if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
++                                      break;
++                              }
++                      }
++              } else {
++                      for ( i in obj ) {
++                              if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
++                                      break;
++                              }
++                      }
++              }
++
++              return obj;
++      },
++
++      // Support: Android<4.1, IE<9
++      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 ) ) ) {
++                              jQuery.merge( ret,
++                                      typeof arr === "string" ?
++                                      [ arr ] : arr
++                              );
++                      } else {
++                              push.call( ret, arr );
++                      }
++              }
++
++              return ret;
++      },
++
++      inArray: function( elem, arr, i ) {
++              var len;
++
++              if ( arr ) {
++                      if ( indexOf ) {
++                              return indexOf.call( arr, elem, i );
++                      }
++
++                      len = arr.length;
++                      i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
++
++                      for ( ; i < len; i++ ) {
++
++                              // Skip accessing in sparse arrays
++                              if ( i in arr && arr[ i ] === elem ) {
++                                      return i;
++                              }
++                      }
++              }
++
++              return -1;
++      },
++
++      merge: function( first, second ) {
++              var len = +second.length,
++                      j = 0,
++                      i = first.length;
++
++              while ( j < len ) {
++                      first[ i++ ] = second[ j++ ];
++              }
++
++              // Support: IE<9
++              // Workaround casting of .length to NaN on otherwise arraylike objects (e.g., NodeLists)
++              if ( len !== len ) {
++                      while ( second[ j ] !== undefined ) {
++                              first[ i++ ] = second[ j++ ];
++                      }
++              }
++
++              first.length = i;
++
++              return first;
++      },
++
++      grep: function( elems, callback, invert ) {
++              var callbackInverse,
++                      matches = [],
++                      i = 0,
++                      length = elems.length,
++                      callbackExpect = !invert;
++
++              // Go through the array, only saving the items
++              // that pass the validator function
++              for ( ; i < length; i++ ) {
++                      callbackInverse = !callback( elems[ i ], i );
++                      if ( callbackInverse !== callbackExpect ) {
++                              matches.push( elems[ i ] );
++                      }
++              }
++
++              return matches;
++      },
++
++      // arg is for internal usage only
++      map: function( elems, callback, arg ) {
++              var length, value,
++                      i = 0,
++                      ret = [];
++
++              // 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.push( value );
++                              }
++                      }
++
++              // Go through every key on the object,
++              } else {
++                      for ( i in elems ) {
++                              value = callback( elems[ i ], i, arg );
++
++                              if ( value != null ) {
++                                      ret.push( value );
++                              }
++                      }
++              }
++
++              // Flatten any nested arrays
++              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 = slice.call( arguments, 2 );
++              proxy = function() {
++                      return fn.apply( context || this, args.concat( slice.call( arguments ) ) );
++              };
++
++              // Set the guid of unique handler to the same of original handler, so it can be removed
++              proxy.guid = fn.guid = fn.guid || jQuery.guid++;
++
++              return proxy;
++      },
++
++      now: function() {
++              return +( new Date() );
++      },
++
++      // jQuery.support is not used in Core but other projects attach their
++      // properties to it so it needs to exist.
++      support: support
++} );
++
++// JSHint would error on this code due to the Symbol not being defined in ES5.
++// Defining this global in .jshintrc would create a danger of using the global
++// unguarded in another place, it seems safer to just disable JSHint for these
++// three lines.
++/* jshint ignore: start */
++if ( typeof Symbol === "function" ) {
++      jQuery.fn[ Symbol.iterator ] = deletedIds[ Symbol.iterator ];
++}
++/* jshint ignore: end */
++
++// Populate the class2type map
++jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ),
++function( i, name ) {
++      class2type[ "[object " + name + "]" ] = name.toLowerCase();
++} );
++
++function isArrayLike( obj ) {
++
++      // Support: iOS 8.2 (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 = jQuery.type( obj );
++
++      if ( type === "function" || jQuery.isWindow( obj ) ) {
++              return false;
++      }
++
++      return type === "array" || length === 0 ||
++              typeof length === "number" && length > 0 && ( length - 1 ) in obj;
++}
++var Sizzle =
++/*!
++ * Sizzle CSS Selector Engine v2.2.1
++ * http://sizzlejs.com/
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license
++ * http://jquery.org/license
++ *
++ * Date: 2015-10-17
++ */
++(function( window ) {
++
++var i,
++      support,
++      Expr,
++      getText,
++      isXML,
++      tokenize,
++      compile,
++      select,
++      outermostContext,
++      sortInput,
++      hasDuplicate,
++
++      // Local document vars
++      setDocument,
++      document,
++      docElem,
++      documentIsHTML,
++      rbuggyQSA,
++      rbuggyMatches,
++      matches,
++      contains,
++
++      // Instance-specific data
++      expando = "sizzle" + 1 * new Date(),
++      preferredDoc = window.document,
++      dirruns = 0,
++      done = 0,
++      classCache = createCache(),
++      tokenCache = createCache(),
++      compilerCache = createCache(),
++      sortOrder = function( a, b ) {
++              if ( a === b ) {
++                      hasDuplicate = true;
++              }
++              return 0;
++      },
++
++      // General-purpose constants
++      MAX_NEGATIVE = 1 << 31,
++
++      // Instance methods
++      hasOwn = ({}).hasOwnProperty,
++      arr = [],
++      pop = arr.pop,
++      push_native = arr.push,
++      push = arr.push,
++      slice = arr.slice,
++      // Use a stripped-down indexOf as it's faster than native
++      // http://jsperf.com/thor-indexof-vs-for/5
++      indexOf = function( list, elem ) {
++              var i = 0,
++                      len = list.length;
++              for ( ; i < len; i++ ) {
++                      if ( list[i] === elem ) {
++                              return i;
++                      }
++              }
++              return -1;
++      },
++
++      booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
++
++      // Regular expressions
++
++      // http://www.w3.org/TR/css3-selectors/#whitespace
++      whitespace = "[\\x20\\t\\r\\n\\f]",
++
++      // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
++      identifier = "(?:\\\\.|[\\w-]|[^\\x00-\\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 + "*" ),
++
++      rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ),
++
++      rpseudo = new RegExp( pseudos ),
++      ridentifier = new RegExp( "^" + identifier + "$" ),
++
++      matchExpr = {
++              "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 +
++                      "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
++                      "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
++              "bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
++              // For use in libraries implementing .is()
++              // We use this for POS matching in `select`
++              "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
++                      whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
++      },
++
++      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-]+))$/,
++
++      rsibling = /[+~]/,
++      rescape = /'|\\/g,
++
++      // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
++      runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
++      funescape = function( _, escaped, escapedWhitespace ) {
++              var high = "0x" + escaped - 0x10000;
++              // NaN means non-codepoint
++              // Support: Firefox<24
++              // Workaround erroneous numeric interpretation of +"0x"
++              return high !== high || escapedWhitespace ?
++                      escaped :
++                      high < 0 ?
++                              // BMP codepoint
++                              String.fromCharCode( high + 0x10000 ) :
++                              // Supplemental Plane codepoint (surrogate pair)
++                              String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
++      },
++
++      // Used for iframes
++      // See setDocument()
++      // Removing the function wrapper causes a "Permission Denied"
++      // error in IE
++      unloadHandler = function() {
++              setDocument();
++      };
++
++// Optimize for push.apply( _, NodeList )
++try {
++      push.apply(
++              (arr = slice.call( preferredDoc.childNodes )),
++              preferredDoc.childNodes
++      );
++      // Support: Android<4.0
++      // Detect silently failing push.apply
++      arr[ preferredDoc.childNodes.length ].nodeType;
++} catch ( e ) {
++      push = { apply: arr.length ?
++
++              // Leverage slice if possible
++              function( target, els ) {
++                      push_native.apply( target, slice.call(els) );
++              } :
++
++              // Support: IE<9
++              // Otherwise append directly
++              function( target, els ) {
++                      var j = target.length,
++                              i = 0;
++                      // Can't trust NodeList.length
++                      while ( (target[j++] = els[i++]) ) {}
++                      target.length = j - 1;
++              }
++      };
++}
++
++function Sizzle( selector, context, results, seed ) {
++      var m, i, elem, nid, nidselect, match, groups, newSelector,
++              newContext = context && context.ownerDocument,
++
++              // nodeType defaults to 9, since context defaults to document
++              nodeType = context ? context.nodeType : 9;
++
++      results = results || [];
++
++      // Return early from calls with invalid selector or context
++      if ( typeof selector !== "string" || !selector ||
++              nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) {
++
++              return results;
++      }
++
++      // Try to shortcut find operations (as opposed to filters) in HTML documents
++      if ( !seed ) {
++
++              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 {
++
++                                              // 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;
++                                              }
++                                      }
++
++                              // Type selector
++                              } else if ( match[2] ) {
++                                      push.apply( results, context.getElementsByTagName( selector ) );
++                                      return results;
++
++                              // Class selector
++                              } else if ( (m = match[3]) && support.getElementsByClassName &&
++                                      context.getElementsByClassName ) {
++
++                                      push.apply( results, context.getElementsByClassName( m ) );
++                                      return results;
++                              }
++                      }
++
++                      // Take advantage of querySelectorAll
++                      if ( support.qsa &&
++                              !compilerCache[ selector + " " ] &&
++                              (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
++
++                              if ( nodeType !== 1 ) {
++                                      newContext = context;
++                                      newSelector = selector;
++
++                              // qSA looks outside Element context, which is not what we want
++                              // Thanks to Andrew Dupont for this workaround technique
++                              // Support: IE <=8
++                              // Exclude object elements
++                              } else if ( context.nodeName.toLowerCase() !== "object" ) {
++
++                                      // Capture the context ID, setting it first if necessary
++                                      if ( (nid = context.getAttribute( "id" )) ) {
++                                              nid = nid.replace( rescape, "\\$&" );
++                                      } else {
++                                              context.setAttribute( "id", (nid = expando) );
++                                      }
++
++                                      // Prefix every selector in the list
++                                      groups = tokenize( selector );
++                                      i = groups.length;
++                                      nidselect = ridentifier.test( nid ) ? "#" + nid : "[id='" + nid + "']";
++                                      while ( i-- ) {
++                                              groups[i] = nidselect + " " + toSelector( groups[i] );
++                                      }
++                                      newSelector = groups.join( "," );
++
++                                      // Expand context for sibling selectors
++                                      newContext = rsibling.test( selector ) && testContext( context.parentNode ) ||
++                                              context;
++                              }
++
++                              if ( newSelector ) {
++                                      try {
++                                              push.apply( results,
++                                                      newContext.querySelectorAll( newSelector )
++                                              );
++                                              return results;
++                                      } catch ( qsaError ) {
++                                      } finally {
++                                              if ( nid === expando ) {
++                                                      context.removeAttribute( "id" );
++                                              }
++                                      }
++                              }
++                      }
++              }
++      }
++
++      // All others
++      return select( selector.replace( rtrim, "$1" ), context, results, seed );
++}
++
++/**
++ * Create key-value caches of limited size
++ * @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
++ */
++function createCache() {
++      var keys = [];
++
++      function cache( key, value ) {
++              // Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
++              if ( keys.push( key + " " ) > Expr.cacheLength ) {
++                      // Only keep the most recent entries
++                      delete cache[ keys.shift() ];
++              }
++              return (cache[ key + " " ] = value);
++      }
++      return cache;
++}
++
++/**
++ * Mark a function for special use by Sizzle
++ * @param {Function} fn The function to mark
++ */
++function markFunction( fn ) {
++      fn[ expando ] = true;
++      return fn;
++}
++
++/**
++ * Support testing using an element
++ * @param {Function} fn Passed the created div and expects a boolean result
++ */
++function assert( fn ) {
++      var div = document.createElement("div");
++
++      try {
++              return !!fn( div );
++      } catch (e) {
++              return false;
++      } finally {
++              // Remove from its parent by default
++              if ( div.parentNode ) {
++                      div.parentNode.removeChild( div );
++              }
++              // release memory in IE
++              div = null;
++      }
++}
++
++/**
++ * Adds the same handler for all of the specified attrs
++ * @param {String} attrs Pipe-separated list of attributes
++ * @param {Function} handler The method that will be applied
++ */
++function addHandle( attrs, handler ) {
++      var arr = attrs.split("|"),
++              i = arr.length;
++
++      while ( i-- ) {
++              Expr.attrHandle[ arr[i] ] = handler;
++      }
++}
++
++/**
++ * Checks document order of two siblings
++ * @param {Element} a
++ * @param {Element} b
++ * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
++ */
++function siblingCheck( a, b ) {
++      var cur = b && a,
++              diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
++                      ( ~b.sourceIndex || MAX_NEGATIVE ) -
++                      ( ~a.sourceIndex || MAX_NEGATIVE );
++
++      // Use IE sourceIndex if available on both nodes
++      if ( diff ) {
++              return diff;
++      }
++
++      // Check if b follows a
++      if ( cur ) {
++              while ( (cur = cur.nextSibling) ) {
++                      if ( cur === b ) {
++                              return -1;
++                      }
++              }
++      }
++
++      return a ? 1 : -1;
++}
++
++/**
++ * Returns a function to use in pseudos for input types
++ * @param {String} type
++ */
++function createInputPseudo( type ) {
++      return function( elem ) {
++              var name = elem.nodeName.toLowerCase();
++              return name === "input" && elem.type === type;
++      };
++}
++
++/**
++ * Returns a function to use in pseudos for buttons
++ * @param {String} type
++ */
++function createButtonPseudo( type ) {
++      return function( elem ) {
++              var name = elem.nodeName.toLowerCase();
++              return (name === "input" || name === "button") && elem.type === type;
++      };
++}
++
++/**
++ * Returns a function to use in pseudos for positionals
++ * @param {Function} fn
++ */
++function createPositionalPseudo( fn ) {
++      return markFunction(function( argument ) {
++              argument = +argument;
++              return markFunction(function( seed, matches ) {
++                      var j,
++                              matchIndexes = fn( [], seed.length, argument ),
++                              i = matchIndexes.length;
++
++                      // Match elements found at the specified indexes
++                      while ( i-- ) {
++                              if ( seed[ (j = matchIndexes[i]) ] ) {
++                                      seed[j] = !(matches[j] = seed[j]);
++                              }
++                      }
++              });
++      });
++}
++
++/**
++ * 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
++ */
++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 ) {
++      // 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;
++};
++
++/**
++ * 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 hasCompare, parent,
++              doc = node ? node.ownerDocument || node : preferredDoc;
++
++      // Return early if doc is invalid or already selected
++      if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
++              return document;
++      }
++
++      // Update global variables
++      document = doc;
++      docElem = document.documentElement;
++      documentIsHTML = !isXML( document );
++
++      // Support: IE 9-11, Edge
++      // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936)
++      if ( (parent = document.defaultView) && parent.top !== parent ) {
++              // Support: IE 11
++              if ( parent.addEventListener ) {
++                      parent.addEventListener( "unload", unloadHandler, false );
++
++              // Support: IE 9 - 10 only
++              } else if ( parent.attachEvent ) {
++                      parent.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");
++      });
++
++      /* getElement(s)By*
++      ---------------------------------------------------------------------- */
++
++      // Check if getElementsByTagName("*") returns only elements
++      support.getElementsByTagName = assert(function( div ) {
++              div.appendChild( document.createComment("") );
++              return !div.getElementsByTagName("*").length;
++      });
++
++      // 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,
++      // so use a roundabout getElementsByName test
++      support.getById = assert(function( div ) {
++              docElem.appendChild( div ).id = expando;
++              return !document.getElementsByName || !document.getElementsByName( expando ).length;
++      });
++
++      // ID find and filter
++      if ( support.getById ) {
++              Expr.find["ID"] = function( id, context ) {
++                      if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
++                              var m = context.getElementById( id );
++                              return m ? [ m ] : [];
++                      }
++              };
++              Expr.filter["ID"] = function( id ) {
++                      var attrId = id.replace( runescape, funescape );
++                      return function( elem ) {
++                              return elem.getAttribute("id") === attrId;
++                      };
++              };
++      } 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 !== "undefined" &&
++                                      elem.getAttributeNode("id");
++                              return node && node.value === attrId;
++                      };
++              };
++      }
++
++      // Tag
++      Expr.find["TAG"] = support.getElementsByTagName ?
++              function( tag, context ) {
++                      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
++                      if ( tag === "*" ) {
++                              while ( (elem = results[i++]) ) {
++                                      if ( elem.nodeType === 1 ) {
++                                              tmp.push( elem );
++                                      }
++                              }
++
++                              return tmp;
++                      }
++                      return results;
++              };
++
++      // Class
++      Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
++              if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) {
++                      return context.getElementsByClassName( className );
++              }
++      };
++
++      /* QSA/matchesSelector
++      ---------------------------------------------------------------------- */
++
++      // QSA and matchesSelector support
++
++      // matchesSelector(:active) reports false when true (IE9/Opera 11.5)
++      rbuggyMatches = [];
++
++      // qSa(:focus) reports false when true (Chrome 21)
++      // 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
++      rbuggyQSA = [];
++
++      if ( (support.qsa = rnative.test( document.querySelectorAll )) ) {
++              // Build QSA regex
++              // Regex strategy adopted from Diego Perini
++              assert(function( div ) {
++                      // 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
++                      docElem.appendChild( div ).innerHTML = "<a id='" + expando + "'></a>" +
++                              "<select id='" + expando + "-\r\\' msallowcapture=''>" +
++                              "<option selected=''></option></select>";
++
++                      // Support: IE8, Opera 11-12.16
++                      // Nothing should be selected when empty strings follow ^= or $= or *=
++                      // The test attribute must be unknown in Opera but "safe" for WinRT
++                      // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section
++                      if ( div.querySelectorAll("[msallowcapture^='']").length ) {
++                              rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
++                      }
++
++                      // Support: IE8
++                      // Boolean attributes and "value" are not treated correctly
++                      if ( !div.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 ( !div.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 ) {
++                              rbuggyQSA.push(":checked");
++                      }
++
++                      // Support: Safari 8+, iOS 8+
++                      // https://bugs.webkit.org/show_bug.cgi?id=136851
++                      // In-page `selector#id sibing-combinator selector` fails
++                      if ( !div.querySelectorAll( "a#" + expando + "+*" ).length ) {
++                              rbuggyQSA.push(".#.+[+~]");
++                      }
++              });
++
++              assert(function( div ) {
++                      // Support: Windows 8 Native Apps
++                      // The type and name attributes are restricted during .innerHTML assignment
++                      var input = document.createElement("input");
++                      input.setAttribute( "type", "hidden" );
++                      div.appendChild( input ).setAttribute( "name", "D" );
++
++                      // Support: IE8
++                      // Enforce case-sensitivity of name attribute
++                      if ( div.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 ) {
++                              rbuggyQSA.push( ":enabled", ":disabled" );
++                      }
++
++                      // Opera 10-11 does not throw on post-comma invalid pseudos
++                      div.querySelectorAll("*,:x");
++                      rbuggyQSA.push(",.*:");
++              });
++      }
++
++      if ( (support.matchesSelector = rnative.test( (matches = docElem.matches ||
++              docElem.webkitMatchesSelector ||
++              docElem.mozMatchesSelector ||
++              docElem.oMatchesSelector ||
++              docElem.msMatchesSelector) )) ) {
++
++              assert(function( div ) {
++                      // Check to see if it's possible to do matchesSelector
++                      // on a disconnected node (IE 9)
++                      support.disconnectedMatch = matches.call( div, "div" );
++
++                      // This should fail with an exception
++                      // Gecko does not error, returns false instead
++                      matches.call( div, "[s!='']:x" );
++                      rbuggyMatches.push( "!=", pseudos );
++              });
++      }
++
++      rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
++      rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );
++
++      /* Contains
++      ---------------------------------------------------------------------- */
++      hasCompare = rnative.test( docElem.compareDocumentPosition );
++
++      // Element contains another
++      // Purposefully self-exclusive
++      // As in, an element does not contain itself
++      contains = hasCompare || rnative.test( docElem.contains ) ?
++              function( a, b ) {
++                      var adown = a.nodeType === 9 ? a.documentElement : a,
++                              bup = b && b.parentNode;
++                      return a === bup || !!( bup && bup.nodeType === 1 && (
++                              adown.contains ?
++                                      adown.contains( bup ) :
++                                      a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
++                      ));
++              } :
++              function( a, b ) {
++                      if ( b ) {
++                              while ( (b = b.parentNode) ) {
++                                      if ( b === a ) {
++                                              return true;
++                                      }
++                              }
++                      }
++                      return false;
++              };
++
++      /* Sorting
++      ---------------------------------------------------------------------- */
++
++      // Document order sorting
++      sortOrder = hasCompare ?
++      function( a, b ) {
++
++              // Flag for duplicate removal
++              if ( a === b ) {
++                      hasDuplicate = true;
++                      return 0;
++              }
++
++              // Sort on method existence if only one input has compareDocumentPosition
++              var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
++              if ( compare ) {
++                      return compare;
++              }
++
++              // Calculate position if both inputs belong to the same document
++              compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ?
++                      a.compareDocumentPosition( b ) :
++
++                      // 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;
++                      }
++
++                      // Maintain original order
++                      return sortInput ?
++                              ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
++                              0;
++              }
++
++              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,
++                      bup = b.parentNode,
++                      ap = [ a ],
++                      bp = [ b ];
++
++              // Parentless nodes are either documents or disconnected
++              if ( !aup || !bup ) {
++                      return a === document ? -1 :
++                              b === document ? 1 :
++                              aup ? -1 :
++                              bup ? 1 :
++                              sortInput ?
++                              ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
++                              0;
++
++              // If the nodes are siblings, we can do a quick check
++              } else if ( aup === bup ) {
++                      return siblingCheck( a, b );
++              }
++
++              // Otherwise we need full lists of their ancestors for comparison
++              cur = a;
++              while ( (cur = cur.parentNode) ) {
++                      ap.unshift( cur );
++              }
++              cur = b;
++              while ( (cur = cur.parentNode) ) {
++                      bp.unshift( cur );
++              }
++
++              // Walk down the tree looking for a discrepancy
++              while ( ap[i] === bp[i] ) {
++                      i++;
++              }
++
++              return i ?
++                      // Do a sibling check if the nodes have a common ancestor
++                      siblingCheck( ap[i], bp[i] ) :
++
++                      // Otherwise nodes in our document sort first
++                      ap[i] === preferredDoc ? -1 :
++                      bp[i] === preferredDoc ? 1 :
++                      0;
++      };
++
++      return document;
++};
++
++Sizzle.matches = function( expr, elements ) {
++      return Sizzle( expr, null, null, elements );
++};
++
++Sizzle.matchesSelector = function( elem, expr ) {
++      // Set document vars if needed
++      if ( ( elem.ownerDocument || elem ) !== document ) {
++              setDocument( elem );
++      }
++
++      // Make sure that attribute selectors are quoted
++      expr = expr.replace( rattributeQuotes, "='$1']" );
++
++      if ( support.matchesSelector && documentIsHTML &&
++              !compilerCache[ expr + " " ] &&
++              ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
++              ( !rbuggyQSA     || !rbuggyQSA.test( expr ) ) ) {
++
++              try {
++                      var ret = matches.call( elem, expr );
++
++                      // IE 9's matchesSelector returns false on disconnected nodes
++                      if ( ret || support.disconnectedMatch ||
++                                      // As well, disconnected nodes are said to be in a document
++                                      // fragment in IE 9
++                                      elem.document && elem.document.nodeType !== 11 ) {
++                              return ret;
++                      }
++              } catch (e) {}
++      }
++
++      return Sizzle( expr, document, null, [ elem ] ).length > 0;
++};
++
++Sizzle.contains = function( context, elem ) {
++      // Set document vars if needed
++      if ( ( context.ownerDocument || context ) !== document ) {
++              setDocument( context );
++      }
++      return contains( context, elem );
++};
++
++Sizzle.attr = function( elem, name ) {
++      // Set document vars if needed
++      if ( ( elem.ownerDocument || elem ) !== document ) {
++              setDocument( elem );
++      }
++
++      var fn = Expr.attrHandle[ name.toLowerCase() ],
++              // Don't get fooled by Object.prototype properties (jQuery #13807)
++              val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
++                      fn( elem, name, !documentIsHTML ) :
++                      undefined;
++
++      return val !== undefined ?
++              val :
++              support.attributes || !documentIsHTML ?
++                      elem.getAttribute( name ) :
++                      (val = elem.getAttributeNode(name)) && val.specified ?
++                              val.value :
++                              null;
++};
++
++Sizzle.error = function( msg ) {
++      throw new Error( "Syntax error, unrecognized expression: " + msg );
++};
++
++/**
++ * Document sorting and removing duplicates
++ * @param {ArrayLike} results
++ */
++Sizzle.uniqueSort = function( results ) {
++      var elem,
++              duplicates = [],
++              j = 0,
++              i = 0;
++
++      // Unless we *know* we can detect duplicates, assume their presence
++      hasDuplicate = !support.detectDuplicates;
++      sortInput = !support.sortStable && results.slice( 0 );
++      results.sort( sortOrder );
++
++      if ( hasDuplicate ) {
++              while ( (elem = results[i++]) ) {
++                      if ( elem === results[ i ] ) {
++                              j = duplicates.push( i );
++                      }
++              }
++              while ( j-- ) {
++                      results.splice( duplicates[ j ], 1 );
++              }
++      }
++
++      // Clear input after sorting to release objects
++      // See https://github.com/jquery/sizzle/pull/225
++      sortInput = null;
++
++      return results;
++};
++
++/**
++ * Utility function for retrieving the text value of an array of DOM nodes
++ * @param {Array|Element} elem
++ */
++getText = Sizzle.getText = function( elem ) {
++      var node,
++              ret = "",
++              i = 0,
++              nodeType = elem.nodeType;
++
++      if ( !nodeType ) {
++              // If no nodeType, this is expected to be an array
++              while ( (node = elem[i++]) ) {
++                      // Do not traverse comment nodes
++                      ret += getText( node );
++              }
++      } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
++              // Use textContent for elements
++              // innerText usage removed for consistency of new lines (jQuery #11153)
++              if ( typeof elem.textContent === "string" ) {
++                      return elem.textContent;
++              } else {
++                      // Traverse its children
++                      for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
++                              ret += getText( elem );
++                      }
++              }
++      } else if ( nodeType === 3 || nodeType === 4 ) {
++              return elem.nodeValue;
++      }
++      // Do not include comment or processing instruction nodes
++
++      return ret;
++};
++
++Expr = Sizzle.selectors = {
++
++      // Can be adjusted by the user
++      cacheLength: 50,
++
++      createPseudo: markFunction,
++
++      match: matchExpr,
++
++      attrHandle: {},
++
++      find: {},
++
++      relative: {
++              ">": { dir: "parentNode", first: true },
++              " ": { dir: "parentNode" },
++              "+": { dir: "previousSibling", first: true },
++              "~": { dir: "previousSibling" }
++      },
++
++      preFilter: {
++              "ATTR": function( match ) {
++                      match[1] = match[1].replace( runescape, funescape );
++
++                      // Move the given value to match[3] whether quoted or unquoted
++                      match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape );
++
++                      if ( match[2] === "~=" ) {
++                              match[3] = " " + match[3] + " ";
++                      }
++
++                      return match.slice( 0, 4 );
++              },
++
++              "CHILD": function( match ) {
++                      /* matches from matchExpr["CHILD"]
++                              1 type (only|nth|...)
++                              2 what (child|of-type)
++                              3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
++                              4 xn-component of xn+y argument ([+-]?\d*n|)
++                              5 sign of xn-component
++                              6 x of xn-component
++                              7 sign of y-component
++                              8 y of y-component
++                      */
++                      match[1] = match[1].toLowerCase();
++
++                      if ( match[1].slice( 0, 3 ) === "nth" ) {
++                              // nth-* requires argument
++                              if ( !match[3] ) {
++                                      Sizzle.error( match[0] );
++                              }
++
++                              // numeric x and y parameters for Expr.filter.CHILD
++                              // remember that false/true cast respectively to 0/1
++                              match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
++                              match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
++
++                      // other types prohibit arguments
++                      } else if ( match[3] ) {
++                              Sizzle.error( match[0] );
++                      }
++
++                      return match;
++              },
++
++              "PSEUDO": function( match ) {
++                      var excess,
++                              unquoted = !match[6] && match[2];
++
++                      if ( matchExpr["CHILD"].test( match[0] ) ) {
++                              return null;
++                      }
++
++                      // Accept quoted arguments as-is
++                      if ( match[3] ) {
++                              match[2] = match[4] || match[5] || "";
++
++                      // Strip excess characters from unquoted arguments
++                      } else if ( unquoted && rpseudo.test( unquoted ) &&
++                              // Get excess from tokenize (recursively)
++                              (excess = tokenize( unquoted, true )) &&
++                              // advance to the next closing parenthesis
++                              (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
++
++                              // excess is a negative index
++                              match[0] = match[0].slice( 0, excess );
++                              match[2] = unquoted.slice( 0, excess );
++                      }
++
++                      // Return only captures needed by the pseudo filter method (type and argument)
++                      return match.slice( 0, 3 );
++              }
++      },
++
++      filter: {
++
++              "TAG": function( nodeNameSelector ) {
++                      var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
++                      return nodeNameSelector === "*" ?
++                              function() { return true; } :
++                              function( elem ) {
++                                      return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
++                              };
++              },
++
++              "CLASS": function( className ) {
++                      var pattern = classCache[ className + " " ];
++
++                      return pattern ||
++                              (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
++                              classCache( className, function( elem ) {
++                                      return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" );
++                              });
++              },
++
++              "ATTR": function( name, operator, check ) {
++                      return function( elem ) {
++                              var result = Sizzle.attr( elem, name );
++
++                              if ( result == null ) {
++                                      return operator === "!=";
++                              }
++                              if ( !operator ) {
++                                      return true;
++                              }
++
++                              result += "";
++
++                              return operator === "=" ? result === check :
++                                      operator === "!=" ? result !== check :
++                                      operator === "^=" ? check && result.indexOf( check ) === 0 :
++                                      operator === "*=" ? check && result.indexOf( check ) > -1 :
++                                      operator === "$=" ? check && result.slice( -check.length ) === check :
++                                      operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 :
++                                      operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
++                                      false;
++                      };
++              },
++
++              "CHILD": function( type, what, argument, first, last ) {
++                      var simple = type.slice( 0, 3 ) !== "nth",
++                              forward = type.slice( -4 ) !== "last",
++                              ofType = what === "of-type";
++
++                      return first === 1 && last === 0 ?
++
++                              // Shortcut for :nth-*(n)
++                              function( elem ) {
++                                      return !!elem.parentNode;
++                              } :
++
++                              function( elem, context, xml ) {
++                                      var cache, uniqueCache, outerCache, node, nodeIndex, start,
++                                              dir = simple !== forward ? "nextSibling" : "previousSibling",
++                                              parent = elem.parentNode,
++                                              name = ofType && elem.nodeName.toLowerCase(),
++                                              useCache = !xml && !ofType,
++                                              diff = false;
++
++                                      if ( parent ) {
++
++                                              // :(first|last|only)-(child|of-type)
++                                              if ( simple ) {
++                                                      while ( dir ) {
++                                                              node = elem;
++                                                              while ( (node = node[ dir ]) ) {
++                                                                      if ( ofType ?
++                                                                              node.nodeName.toLowerCase() === name :
++                                                                              node.nodeType === 1 ) {
++
++                                                                              return false;
++                                                                      }
++                                                              }
++                                                              // Reverse direction for :only-* (if we haven't yet done so)
++                                                              start = dir = type === "only" && !start && "nextSibling";
++                                                      }
++                                                      return true;
++                                              }
++
++                                              start = [ forward ? parent.firstChild : parent.lastChild ];
++
++                                              // non-xml :nth-child(...) stores cache data on `parent`
++                                              if ( forward && useCache ) {
++
++                                                      // Seek `elem` from a previously-cached index
++
++                                                      // ...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 ] ||
++
++                                                              // Fallback to seeking `elem` from the start
++                                                              (diff = nodeIndex = 0) || start.pop()) ) {
++
++                                                              // When found, cache indexes on `parent` and break
++                                                              if ( node.nodeType === 1 && ++diff && node === elem ) {
++                                                                      uniqueCache[ type ] = [ dirruns, nodeIndex, diff ];
++                                                                      break;
++                                                              }
++                                                      }
++
++                                              } else {
++                                                      // Use previously-cached element index if available
++                                                      if ( useCache ) {
++                                                              // ...in a gzip-friendly way
++                                                              node = elem;
++                                                              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;
++                                                      }
++
++                                                      // 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;
++                                                                              }
++                                                                      }
++                                                              }
++                                                      }
++                                              }
++
++                                              // Incorporate the offset, then check against cycle size
++                                              diff -= last;
++                                              return diff === first || ( diff % first === 0 && diff / first >= 0 );
++                                      }
++                              };
++              },
++
++              "PSEUDO": function( pseudo, argument ) {
++                      // pseudo-class names are case-insensitive
++                      // http://www.w3.org/TR/selectors/#pseudo-classes
++                      // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
++                      // Remember that setFilters inherits from pseudos
++                      var args,
++                              fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
++                                      Sizzle.error( "unsupported pseudo: " + pseudo );
++
++                      // The user may use createPseudo to indicate that
++                      // arguments are needed to create the filter function
++                      // just as Sizzle does
++                      if ( fn[ expando ] ) {
++                              return fn( argument );
++                      }
++
++                      // But maintain support for old signatures
++                      if ( fn.length > 1 ) {
++                              args = [ pseudo, pseudo, "", argument ];
++                              return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
++                                      markFunction(function( seed, matches ) {
++                                              var idx,
++                                                      matched = fn( seed, argument ),
++                                                      i = matched.length;
++                                              while ( i-- ) {
++                                                      idx = indexOf( seed, matched[i] );
++                                                      seed[ idx ] = !( matches[ idx ] = matched[i] );
++                                              }
++                                      }) :
++                                      function( elem ) {
++                                              return fn( elem, 0, args );
++                                      };
++                      }
++
++                      return fn;
++              }
++      },
++
++      pseudos: {
++              // Potentially complex pseudos
++              "not": markFunction(function( selector ) {
++                      // Trim the selector passed to compile
++                      // to avoid treating leading and trailing
++                      // spaces as combinators
++                      var input = [],
++                              results = [],
++                              matcher = compile( selector.replace( rtrim, "$1" ) );
++
++                      return matcher[ expando ] ?
++                              markFunction(function( seed, matches, context, xml ) {
++                                      var elem,
++                                              unmatched = matcher( seed, null, xml, [] ),
++                                              i = seed.length;
++
++                                      // Match elements unmatched by `matcher`
++                                      while ( i-- ) {
++                                              if ( (elem = unmatched[i]) ) {
++                                                      seed[i] = !(matches[i] = elem);
++                                              }
++                                      }
++                              }) :
++                              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();
++                              };
++              }),
++
++              "has": markFunction(function( selector ) {
++                      return function( elem ) {
++                              return Sizzle( selector, elem ).length > 0;
++                      };
++              }),
++
++              "contains": markFunction(function( text ) {
++                      text = text.replace( runescape, funescape );
++                      return function( elem ) {
++                              return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
++                      };
++              }),
++
++              // "Whether an element is represented by a :lang() selector
++              // is based solely on the element's language value
++              // being equal to the identifier C,
++              // or beginning with the identifier C immediately followed by "-".
++              // The matching of C against the element's language value is performed case-insensitively.
++              // The identifier C does not have to be a valid language name."
++              // http://www.w3.org/TR/selectors/#lang-pseudo
++              "lang": markFunction( function( lang ) {
++                      // lang value must be a valid identifier
++                      if ( !ridentifier.test(lang || "") ) {
++                              Sizzle.error( "unsupported lang: " + lang );
++                      }
++                      lang = lang.replace( runescape, funescape ).toLowerCase();
++                      return function( elem ) {
++                              var elemLang;
++                              do {
++                                      if ( (elemLang = documentIsHTML ?
++                                              elem.lang :
++                                              elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {
++
++                                              elemLang = elemLang.toLowerCase();
++                                              return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
++                                      }
++                              } while ( (elem = elem.parentNode) && elem.nodeType === 1 );
++                              return false;
++                      };
++              }),
++
++              // Miscellaneous
++              "target": function( elem ) {
++                      var hash = window.location && window.location.hash;
++                      return hash && hash.slice( 1 ) === elem.id;
++              },
++
++              "root": function( elem ) {
++                      return elem === docElem;
++              },
++
++              "focus": function( elem ) {
++                      return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
++              },
++
++              // Boolean properties
++              "enabled": function( elem ) {
++                      return elem.disabled === false;
++              },
++
++              "disabled": function( elem ) {
++                      return elem.disabled === true;
++              },
++
++              "checked": function( elem ) {
++                      // In CSS3, :checked should return both checked and selected elements
++                      // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
++                      var nodeName = elem.nodeName.toLowerCase();
++                      return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
++              },
++
++              "selected": function( elem ) {
++                      // Accessing this property makes selected-by-default
++                      // options in Safari work properly
++                      if ( elem.parentNode ) {
++                              elem.parentNode.selectedIndex;
++                      }
++
++                      return elem.selected === true;
++              },
++
++              // Contents
++              "empty": function( elem ) {
++                      // http://www.w3.org/TR/selectors/#empty-pseudo
++                      // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
++                      //   but not by others (comment: 8; processing instruction: 7; etc.)
++                      // nodeType < 6 works because attributes (2) do not appear as children
++                      for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
++                              if ( elem.nodeType < 6 ) {
++                                      return false;
++                              }
++                      }
++                      return true;
++              },
++
++              "parent": function( elem ) {
++                      return !Expr.pseudos["empty"]( elem );
++              },
++
++              // Element/input types
++              "header": function( elem ) {
++                      return rheader.test( elem.nodeName );
++              },
++
++              "input": function( elem ) {
++                      return rinputs.test( elem.nodeName );
++              },
++
++              "button": function( elem ) {
++                      var name = elem.nodeName.toLowerCase();
++                      return name === "input" && elem.type === "button" || name === "button";
++              },
++
++              "text": function( elem ) {
++                      var attr;
++                      return elem.nodeName.toLowerCase() === "input" &&
++                              elem.type === "text" &&
++
++                              // 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
++              "first": createPositionalPseudo(function() {
++                      return [ 0 ];
++              }),
++
++              "last": createPositionalPseudo(function( matchIndexes, length ) {
++                      return [ length - 1 ];
++              }),
++
++              "eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
++                      return [ argument < 0 ? argument + length : argument ];
++              }),
++
++              "even": createPositionalPseudo(function( matchIndexes, length ) {
++                      var i = 0;
++                      for ( ; i < length; i += 2 ) {
++                              matchIndexes.push( i );
++                      }
++                      return matchIndexes;
++              }),
++
++              "odd": createPositionalPseudo(function( matchIndexes, length ) {
++                      var i = 1;
++                      for ( ; i < length; i += 2 ) {
++                              matchIndexes.push( i );
++                      }
++                      return matchIndexes;
++              }),
++
++              "lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
++                      var i = argument < 0 ? argument + length : argument;
++                      for ( ; --i >= 0; ) {
++                              matchIndexes.push( i );
++                      }
++                      return matchIndexes;
++              }),
++
++              "gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
++                      var i = argument < 0 ? argument + length : argument;
++                      for ( ; ++i < length; ) {
++                              matchIndexes.push( i );
++                      }
++                      return matchIndexes;
++              })
++      }
++};
++
++Expr.pseudos["nth"] = Expr.pseudos["eq"];
++
++// Add button/input type pseudos
++for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
++      Expr.pseudos[ i ] = createInputPseudo( i );
++}
++for ( i in { submit: true, reset: true } ) {
++      Expr.pseudos[ i ] = createButtonPseudo( i );
++}
++
++// Easy API for creating new setFilters
++function setFilters() {}
++setFilters.prototype = Expr.filters = Expr.pseudos;
++Expr.setFilters = new setFilters();
++
++tokenize = Sizzle.tokenize = function( selector, parseOnly ) {
++      var matched, match, tokens, type,
++              soFar, groups, preFilters,
++              cached = tokenCache[ selector + " " ];
++
++      if ( cached ) {
++              return parseOnly ? 0 : cached.slice( 0 );
++      }
++
++      soFar = selector;
++      groups = [];
++      preFilters = Expr.preFilter;
++
++      while ( soFar ) {
++
++              // Comma and first run
++              if ( !matched || (match = rcomma.exec( soFar )) ) {
++                      if ( match ) {
++                              // Don't consume trailing commas as valid
++                              soFar = soFar.slice( match[0].length ) || soFar;
++                      }
++                      groups.push( (tokens = []) );
++              }
++
++              matched = false;
++
++              // Combinators
++              if ( (match = rcombinators.exec( soFar )) ) {
++                      matched = match.shift();
++                      tokens.push({
++                              value: matched,
++                              // Cast descendant combinators to space
++                              type: match[0].replace( rtrim, " " )
++                      });
++                      soFar = soFar.slice( matched.length );
++              }
++
++              // Filters
++              for ( type in Expr.filter ) {
++                      if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
++                              (match = preFilters[ type ]( match ))) ) {
++                              matched = match.shift();
++                              tokens.push({
++                                      value: matched,
++                                      type: type,
++                                      matches: match
++                              });
++                              soFar = soFar.slice( matched.length );
++                      }
++              }
++
++              if ( !matched ) {
++                      break;
++              }
++      }
++
++      // Return the length of the invalid excess
++      // if we're just parsing
++      // Otherwise, throw an error or return tokens
++      return parseOnly ?
++              soFar.length :
++              soFar ?
++                      Sizzle.error( selector ) :
++                      // Cache the tokens
++                      tokenCache( selector, groups ).slice( 0 );
++};
++
++function toSelector( tokens ) {
++      var i = 0,
++              len = tokens.length,
++              selector = "";
++      for ( ; i < len; i++ ) {
++              selector += tokens[i].value;
++      }
++      return selector;
++}
++
++function addCombinator( matcher, combinator, base ) {
++      var dir = combinator.dir,
++              checkNonElements = base && dir === "parentNode",
++              doneName = done++;
++
++      return combinator.first ?
++              // Check against closest ancestor/preceding element
++              function( elem, context, xml ) {
++                      while ( (elem = elem[ dir ]) ) {
++                              if ( elem.nodeType === 1 || checkNonElements ) {
++                                      return matcher( elem, context, xml );
++                              }
++                      }
++              } :
++
++              // Check against all ancestor/preceding elements
++              function( elem, context, xml ) {
++                      var oldCache, uniqueCache, outerCache,
++                              newCache = [ dirruns, doneName ];
++
++                      // 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 ) {
++                                              if ( matcher( elem, context, xml ) ) {
++                                                      return true;
++                                              }
++                                      }
++                              }
++                      } else {
++                              while ( (elem = elem[ dir ]) ) {
++                                      if ( elem.nodeType === 1 || checkNonElements ) {
++                                              outerCache = elem[ expando ] || (elem[ expando ] = {});
++
++                                              // Support: IE <9 only
++                                              // Defend against cloned attroperties (jQuery gh-1709)
++                                              uniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {});
++
++                                              if ( (oldCache = uniqueCache[ dir ]) &&
++                                                      oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {
++
++                                                      // Assign to newCache so results back-propagate to previous elements
++                                                      return (newCache[ 2 ] = oldCache[ 2 ]);
++                                              } else {
++                                                      // Reuse newcache so results back-propagate to previous elements
++                                                      uniqueCache[ dir ] = newCache;
++
++                                                      // A match means we're done; a fail means we have to keep checking
++                                                      if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) {
++                                                              return true;
++                                                      }
++                                              }
++                                      }
++                              }
++                      }
++              };
++}
++
++function elementMatcher( matchers ) {
++      return matchers.length > 1 ?
++              function( elem, context, xml ) {
++                      var i = matchers.length;
++                      while ( i-- ) {
++                              if ( !matchers[i]( elem, context, xml ) ) {
++                                      return false;
++                              }
++                      }
++                      return true;
++              } :
++              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 = [],
++              i = 0,
++              len = unmatched.length,
++              mapped = map != null;
++
++      for ( ; i < len; i++ ) {
++              if ( (elem = unmatched[i]) ) {
++                      if ( !filter || filter( elem, context, xml ) ) {
++                              newUnmatched.push( elem );
++                              if ( mapped ) {
++                                      map.push( i );
++                              }
++                      }
++              }
++      }
++
++      return newUnmatched;
++}
++
++function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
++      if ( postFilter && !postFilter[ expando ] ) {
++              postFilter = setMatcher( postFilter );
++      }
++      if ( postFinder && !postFinder[ expando ] ) {
++              postFinder = setMatcher( postFinder, postSelector );
++      }
++      return markFunction(function( seed, results, context, xml ) {
++              var temp, i, elem,
++                      preMap = [],
++                      postMap = [],
++                      preexisting = results.length,
++
++                      // Get initial elements from seed or context
++                      elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
++
++                      // Prefilter to get matcher input, preserving a map for seed-results synchronization
++                      matcherIn = preFilter && ( seed || !selector ) ?
++                              condense( elems, preMap, preFilter, context, xml ) :
++                              elems,
++
++                      matcherOut = matcher ?
++                              // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
++                              postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
++
++                                      // ...intermediate processing is necessary
++                                      [] :
++
++                                      // ...otherwise use results directly
++                                      results :
++                              matcherIn;
++
++              // Find primary matches
++              if ( matcher ) {
++                      matcher( matcherIn, matcherOut, context, xml );
++              }
++
++              // Apply postFilter
++              if ( postFilter ) {
++                      temp = condense( matcherOut, postMap );
++                      postFilter( temp, [], context, xml );
++
++                      // Un-match failing elements by moving them back to matcherIn
++                      i = temp.length;
++                      while ( i-- ) {
++                              if ( (elem = temp[i]) ) {
++                                      matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
++                              }
++                      }
++              }
++
++              if ( seed ) {
++                      if ( postFinder || preFilter ) {
++                              if ( postFinder ) {
++                                      // Get the final matcherOut by condensing this intermediate into postFinder contexts
++                                      temp = [];
++                                      i = matcherOut.length;
++                                      while ( i-- ) {
++                                              if ( (elem = matcherOut[i]) ) {
++                                                      // Restore matcherIn since elem is not yet a final match
++                                                      temp.push( (matcherIn[i] = elem) );
++                                              }
++                                      }
++                                      postFinder( null, (matcherOut = []), temp, xml );
++                              }
++
++                              // Move matched elements from seed to results to keep them synchronized
++                              i = matcherOut.length;
++                              while ( i-- ) {
++                                      if ( (elem = matcherOut[i]) &&
++                                              (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) {
++
++                                              seed[temp] = !(results[temp] = elem);
++                                      }
++                              }
++                      }
++
++              // Add elements to results, through postFinder if defined
++              } else {
++                      matcherOut = condense(
++                              matcherOut === results ?
++                                      matcherOut.splice( preexisting, matcherOut.length ) :
++                                      matcherOut
++                      );
++                      if ( postFinder ) {
++                              postFinder( null, results, matcherOut, xml );
++                      } else {
++                              push.apply( results, matcherOut );
++                      }
++              }
++      });
++}
++
++function matcherFromTokens( tokens ) {
++      var checkContext, matcher, j,
++              len = tokens.length,
++              leadingRelative = Expr.relative[ tokens[0].type ],
++              implicitRelative = leadingRelative || Expr.relative[" "],
++              i = leadingRelative ? 1 : 0,
++
++              // The foundational matcher ensures that elements are reachable from top-level context(s)
++              matchContext = addCombinator( function( elem ) {
++                      return elem === checkContext;
++              }, implicitRelative, true ),
++              matchAnyContext = addCombinator( function( elem ) {
++                      return indexOf( checkContext, elem ) > -1;
++              }, implicitRelative, true ),
++              matchers = [ function( elem, context, xml ) {
++                      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++ ) {
++              if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
++                      matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
++              } else {
++                      matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
++
++                      // Return special upon seeing a positional matcher
++                      if ( matcher[ expando ] ) {
++                              // Find the next relative operator (if any) for proper handling
++                              j = ++i;
++                              for ( ; j < len; j++ ) {
++                                      if ( Expr.relative[ tokens[j].type ] ) {
++                                              break;
++                                      }
++                              }
++                              return setMatcher(
++                                      i > 1 && elementMatcher( matchers ),
++                                      i > 1 && toSelector(
++                                              // If the preceding token was a descendant combinator, insert an implicit any-element `*`
++                                              tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" })
++                                      ).replace( rtrim, "$1" ),
++                                      matcher,
++                                      i < j && matcherFromTokens( tokens.slice( i, j ) ),
++                                      j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
++                                      j < len && toSelector( tokens )
++                              );
++                      }
++                      matchers.push( matcher );
++              }
++      }
++
++      return elementMatcher( matchers );
++}
++
++function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
++      var bySet = setMatchers.length > 0,
++              byElement = elementMatchers.length > 0,
++              superMatcher = function( seed, context, xml, results, outermost ) {
++                      var elem, j, matcher,
++                              matchedCount = 0,
++                              i = "0",
++                              unmatched = seed && [],
++                              setMatched = [],
++                              contextBackup = outermostContext,
++                              // 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),
++                              len = elems.length;
++
++                      if ( outermost ) {
++                              outermostContext = context === document || context || outermost;
++                      }
++
++                      // Add elements passing elementMatchers directly to results
++                      // Support: IE<9, Safari
++                      // Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id
++                      for ( ; i !== len && (elem = elems[i]) != null; i++ ) {
++                              if ( byElement && elem ) {
++                                      j = 0;
++                                      if ( !context && elem.ownerDocument !== document ) {
++                                              setDocument( elem );
++                                              xml = !documentIsHTML;
++                                      }
++                                      while ( (matcher = elementMatchers[j++]) ) {
++                                              if ( matcher( elem, context || document, xml) ) {
++                                                      results.push( elem );
++                                                      break;
++                                              }
++                                      }
++                                      if ( outermost ) {
++                                              dirruns = dirrunsUnique;
++                                      }
++                              }
++
++                              // Track unmatched elements for set filters
++                              if ( bySet ) {
++                                      // They will have gone through all possible matchers
++                                      if ( (elem = !matcher && elem) ) {
++                                              matchedCount--;
++                                      }
++
++                                      // Lengthen the array for every element, matched or not
++                                      if ( seed ) {
++                                              unmatched.push( elem );
++                                      }
++                              }
++                      }
++
++                      // `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++]) ) {
++                                      matcher( unmatched, setMatched, context, xml );
++                              }
++
++                              if ( seed ) {
++                                      // Reintegrate element matches to eliminate the need for sorting
++                                      if ( matchedCount > 0 ) {
++                                              while ( i-- ) {
++                                                      if ( !(unmatched[i] || setMatched[i]) ) {
++                                                              setMatched[i] = pop.call( results );
++                                                      }
++                                              }
++                                      }
++
++                                      // Discard index placeholder values to get only actual matches
++                                      setMatched = condense( setMatched );
++                              }
++
++                              // Add matches to results
++                              push.apply( results, setMatched );
++
++                              // Seedless set matches succeeding multiple successful matchers stipulate sorting
++                              if ( outermost && !seed && setMatched.length > 0 &&
++                                      ( matchedCount + setMatchers.length ) > 1 ) {
++
++                                      Sizzle.uniqueSort( results );
++                              }
++                      }
++
++                      // Override manipulation of globals by nested matchers
++                      if ( outermost ) {
++                              dirruns = dirrunsUnique;
++                              outermostContext = contextBackup;
++                      }
++
++                      return unmatched;
++              };
++
++      return bySet ?
++              markFunction( superMatcher ) :
++              superMatcher;
++}
++
++compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {
++      var i,
++              setMatchers = [],
++              elementMatchers = [],
++              cached = compilerCache[ selector + " " ];
++
++      if ( !cached ) {
++              // Generate a function of recursive functions that can be used to check each element
++              if ( !match ) {
++                      match = tokenize( selector );
++              }
++              i = match.length;
++              while ( i-- ) {
++                      cached = matcherFromTokens( match[i] );
++                      if ( cached[ expando ] ) {
++                              setMatchers.push( cached );
++                      } else {
++                              elementMatchers.push( cached );
++                      }
++              }
++
++              // Cache the compiled function
++              cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
++
++              // Save selector and tokenization
++              cached.selector = selector;
++      }
++      return cached;
++};
++
++/**
++ * 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,
++              compiled = typeof selector === "function" && selector,
++              match = !seed && tokenize( (selector = compiled.selector || selector) );
++
++      results = results || [];
++
++      // 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 ) {
++
++              // 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" &&
++                              support.getById && 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;
++                      }
++
++                      selector = selector.slice( tokens.shift().value.length );
++              }
++
++              // Fetch a seed set for right-to-left matching
++              i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
++              while ( i-- ) {
++                      token = tokens[i];
++
++                      // 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 if one is not provided
++      // Provide `match` to avoid retokenization if we modified the selector above
++      ( compiled || compile( selector, match ) )(
++              seed,
++              context,
++              !documentIsHTML,
++              results,
++              !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-35+
++// Always assume duplicates if they aren't passed to the comparison function
++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 ) {
++      // Should return 1, but returns 4 (following)
++      return div1.compareDocumentPosition( document.createElement("div") ) & 1;
++});
++
++// Support: IE<8
++// Prevent attribute/property "interpolation"
++// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
++if ( !assert(function( div ) {
++      div.innerHTML = "<a href='#'></a>";
++      return div.firstChild.getAttribute("href") === "#" ;
++}) ) {
++      addHandle( "type|href|height|width", function( elem, name, isXML ) {
++              if ( !isXML ) {
++                      return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
++              }
++      });
++}
++
++// Support: IE<9
++// Use defaultValue in place of getAttribute("value")
++if ( !support.attributes || !assert(function( div ) {
++      div.innerHTML = "<input/>";
++      div.firstChild.setAttribute( "value", "" );
++      return div.firstChild.getAttribute( "value" ) === "";
++}) ) {
++      addHandle( "value", function( elem, name, isXML ) {
++              if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
++                      return elem.defaultValue;
++              }
++      });
++}
++
++// Support: IE<9
++// Use getAttributeNode to fetch booleans when getAttribute lies
++if ( !assert(function( div ) {
++      return div.getAttribute("disabled") == null;
++}) ) {
++      addHandle( booleans, function( elem, name, isXML ) {
++              var val;
++              if ( !isXML ) {
++                      return elem[ name ] === true ? name.toLowerCase() :
++                                      (val = elem.getAttributeNode( name )) && val.specified ?
++                                      val.value :
++                              null;
++              }
++      });
++}
++
++return Sizzle;
++
++})( window );
++
++
++
++jQuery.find = Sizzle;
++jQuery.expr = Sizzle.selectors;
++jQuery.expr[ ":" ] = jQuery.expr.pseudos;
++jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort;
++jQuery.text = Sizzle.getText;
++jQuery.isXMLDoc = Sizzle.isXML;
++jQuery.contains = Sizzle.contains;
++
++
++
++var dir = function( elem, dir, until ) {
++      var matched = [],
++              truncate = until !== undefined;
++
++      while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) {
++              if ( elem.nodeType === 1 ) {
++                      if ( truncate && jQuery( elem ).is( until ) ) {
++                              break;
++                      }
++                      matched.push( elem );
++              }
++      }
++      return matched;
++};
++
++
++var siblings = function( n, elem ) {
++      var matched = [];
++
++      for ( ; n; n = n.nextSibling ) {
++              if ( n.nodeType === 1 && n !== elem ) {
++                      matched.push( n );
++              }
++      }
++
++      return matched;
++};
++
++
++var rneedsContext = jQuery.expr.match.needsContext;
++
++var rsingleTag = ( /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/ );
++
++
++
++var risSimple = /^.[^:#\[\.,]*$/;
++
++// 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 ( qualifier.nodeType ) {
++              return jQuery.grep( elements, function( elem ) {
++                      return ( elem === qualifier ) !== not;
++              } );
++
++      }
++
++      if ( typeof qualifier === "string" ) {
++              if ( risSimple.test( qualifier ) ) {
++                      return jQuery.filter( qualifier, elements, not );
++              }
++
++              qualifier = jQuery.filter( qualifier, elements );
++      }
++
++      return jQuery.grep( elements, function( elem ) {
++              return ( jQuery.inArray( elem, qualifier ) > -1 ) !== not;
++      } );
++}
++
++jQuery.filter = function( expr, elems, not ) {
++      var elem = elems[ 0 ];
++
++      if ( not ) {
++              expr = ":not(" + expr + ")";
++      }
++
++      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;
++              } ) );
++};
++
++jQuery.fn.extend( {
++      find: function( selector ) {
++              var i,
++                      ret = [],
++                      self = this,
++                      len = self.length;
++
++              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;
++                                      }
++                              }
++                      } ) );
++              }
++
++              for ( i = 0; i < len; i++ ) {
++                      jQuery.find( selector, self[ i ], ret );
++              }
++
++              // 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;
++      },
++      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,
++
++                      // 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;
++      }
++} );
++
++
++// Initialize a jQuery object
++
++
++// A central reference to the root jQuery(document)
++var rootjQuery,
++
++      // A simple way to check for HTML strings
++      // Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
++      // Strict HTML recognition (#11290: must start with <)
++      rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
++
++      init = jQuery.fn.init = function( selector, context, root ) {
++              var match, elem;
++
++              // HANDLE: $(""), $(null), $(undefined), $(false)
++              if ( !selector ) {
++                      return this;
++              }
++
++              // init accepts an alternate rootjQuery
++              // so migrate can support jQuery.sub (gh-2101)
++              root = root || rootjQuery;
++
++              // 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 );
++                      }
++
++                      // Match html or make sure no context is specified for #id
++                      if ( match && ( match[ 1 ] || !context ) ) {
++
++                              // HANDLE: $(html) -> $(array)
++                              if ( match[ 1 ] ) {
++                                      context = context instanceof jQuery ? context[ 0 ] : context;
++
++                                      // 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
++                                      ) );
++
++                                      // 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 ] );
++
++                                                      // ...and otherwise set as attributes
++                                                      } else {
++                                                              this.attr( match, context[ match ] );
++                                                      }
++                                              }
++                                      }
++
++                                      return this;
++
++                              // 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 );
++                                              }
++
++                                              // 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 || root ).find( selector );
++
++                      // HANDLE: $(expr, context)
++                      // (which is just equivalent to: $(context).find(expr)
++                      } else {
++                              return this.constructor( context ).find( selector );
++                      }
++
++              // HANDLE: $(DOMElement)
++              } else if ( selector.nodeType ) {
++                      this.context = this[ 0 ] = selector;
++                      this.length = 1;
++                      return this;
++
++              // HANDLE: $(function)
++              // Shortcut for document ready
++              } else if ( jQuery.isFunction( selector ) ) {
++                      return typeof root.ready !== "undefined" ?
++                              root.ready( selector ) :
++
++                              // Execute immediately if ready is not present
++                              selector( jQuery );
++              }
++
++              if ( selector.selector !== undefined ) {
++                      this.selector = selector.selector;
++                      this.context = selector.context;
++              }
++
++              return jQuery.makeArray( selector, this );
++      };
++
++// Give the init function the jQuery prototype for later instantiation
++init.prototype = jQuery.fn;
++
++// Initialize central reference
++rootjQuery = jQuery( document );
++
++
++var rparentsprev = /^(?:parents|prev(?:Until|All))/,
++
++      // methods guaranteed to produce a unique set when starting from a unique set
++      guaranteedUnique = {
++              children: true,
++              contents: true,
++              next: true,
++              prev: true
++      };
++
++jQuery.fn.extend( {
++      has: function( target ) {
++              var i,
++                      targets = jQuery( target, this ),
++                      len = targets.length;
++
++              return this.filter( function() {
++                      for ( i = 0; i < len; i++ ) {
++                              if ( jQuery.contains( this, targets[ i ] ) ) {
++                                      return true;
++                              }
++                      }
++              } );
++      },
++
++      closest: function( selectors, context ) {
++              var cur,
++                      i = 0,
++                      l = this.length,
++                      matched = [],
++                      pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ?
++                              jQuery( selectors, context || this.context ) :
++                              0;
++
++              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 :
++
++                                      // Don't pass non-elements to Sizzle
++                                      cur.nodeType === 1 &&
++                                              jQuery.find.matchesSelector( cur, selectors ) ) ) {
++
++                                      matched.push( cur );
++                                      break;
++                              }
++                      }
++              }
++
++              return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched );
++      },
++
++      // Determine the position of an element within
++      // the matched set of elements
++      index: function( elem ) {
++
++              // No argument, return index in parent
++              if ( !elem ) {
++                      return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
++              }
++
++              // index in selector
++              if ( typeof elem === "string" ) {
++                      return jQuery.inArray( this[ 0 ], jQuery( 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 );
++      },
++
++      add: function( selector, context ) {
++              return this.pushStack(
++                      jQuery.uniqueSort(
++                              jQuery.merge( this.get(), jQuery( selector, context ) )
++                      )
++              );
++      },
++
++      addBack: function( selector ) {
++              return this.add( selector == null ?
++                      this.prevObject : this.prevObject.filter( selector )
++              );
++      }
++} );
++
++function sibling( cur, dir ) {
++      do {
++              cur = cur[ dir ];
++      } while ( cur && cur.nodeType !== 1 );
++
++      return cur;
++}
++
++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 ) {
++              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 );
++
++              if ( name.slice( -5 ) !== "Until" ) {
++                      selector = until;
++              }
++
++              if ( selector && typeof selector === "string" ) {
++                      ret = jQuery.filter( selector, ret );
++              }
++
++              if ( this.length > 1 ) {
++
++                      // Remove duplicates
++                      if ( !guaranteedUnique[ name ] ) {
++                              ret = jQuery.uniqueSort( ret );
++                      }
++
++                      // Reverse order for parents* and prev-derivatives
++                      if ( rparentsprev.test( name ) ) {
++                              ret = ret.reverse();
++                      }
++              }
++
++              return this.pushStack( ret );
++      };
++} );
++var rnotwhite = ( /\S+/g );
++
++
++
++// Convert String-formatted options into Object-formatted ones
++function createOptions( options ) {
++      var object = {};
++      jQuery.each( options.match( 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" ?
++              createOptions( options ) :
++              jQuery.extend( {}, options );
++
++      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,
++
++              // Flag to prevent firing
++              locked,
++
++              // Actual callback list
++              list = [],
++
++              // Queue of execution data for repeatable lists
++              queue = [],
++
++              // Index of currently firing callback (modified by add/remove as needed)
++              firingIndex = -1,
++
++              // Fire callbacks
++              fire = function() {
++
++                      // Enforce single-firing
++                      locked = options.once;
++
++                      // 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 ) {
++
++                                      // Run callback and check for early termination
++                                      if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&
++                                              options.stopOnFalse ) {
++
++                                              // Jump to end and forget the data so .add doesn't re-fire
++                                              firingIndex = list.length;
++                                              memory = false;
++                                      }
++                              }
++                      }
++
++                      // Forget the data if we're done with it
++                      if ( !options.memory ) {
++                              memory = false;
++                      }
++
++                      firing = false;
++
++                      // Clean up if we're done firing for good
++                      if ( locked ) {
++
++                              // Keep an empty list if we have data for future add calls
++                              if ( memory ) {
++                                      list = [];
++
++                              // Otherwise, this object is spent
++                              } else {
++                                      list = "";
++                              }
++                      }
++              },
++
++              // Actual Callbacks object
++              self = {
++
++                      // Add a callback or a collection of callbacks to the list
++                      add: function() {
++                              if ( list ) {
++
++                                      // If we have memory from a past run, we should fire after adding
++                                      if ( memory && !firing ) {
++                                              firingIndex = list.length - 1;
++                                              queue.push( memory );
++                                      }
++
++                                      ( function add( args ) {
++                                              jQuery.each( args, function( _, arg ) {
++                                                      if ( jQuery.isFunction( arg ) ) {
++                                                              if ( !options.unique || !self.has( arg ) ) {
++                                                                      list.push( arg );
++                                                              }
++                                                      } else if ( arg && arg.length && jQuery.type( arg ) !== "string" ) {
++
++                                                              // Inspect recursively
++                                                              add( arg );
++                                                      }
++                                              } );
++                                      } )( arguments );
++
++                                      if ( memory && !firing ) {
++                                              fire();
++                                      }
++                              }
++                              return this;
++                      },
++
++                      // 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;
++                      },
++
++                      // 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;
++                      },
++
++                      // Remove all callbacks from the list
++                      empty: function() {
++                              if ( list ) {
++                                      list = [];
++                              }
++                              return this;
++                      },
++
++                      // 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;
++                      },
++
++                      // Disable .fire
++                      // Also disable .add unless we have memory (since it would have no effect)
++                      // Abort any pending executions
++                      lock: function() {
++                              locked = true;
++                              if ( !memory ) {
++                                      self.disable();
++                              }
++                              return this;
++                      },
++                      locked: function() {
++                              return !!locked;
++                      },
++
++                      // 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 self;
++};
++
++
++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 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()
++                                                                              .progress( newDefer.notify )
++                                                                              .done( newDefer.resolve )
++                                                                              .fail( newDefer.reject );
++                                                              } else {
++                                                                      newDefer[ tuple[ 0 ] + "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;
++
++              // Add list-specific methods
++              jQuery.each( tuples, function( i, tuple ) {
++                      var list = tuple[ 2 ],
++                              stateString = tuple[ 3 ];
++
++                      // promise[ done | fail | progress ] = list.add
++                      promise[ tuple[ 1 ] ] = list.add;
++
++                      // 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 );
++                      }
++
++                      // 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 );
++              }
++
++              // All done!
++              return deferred;
++      },
++
++      // Deferred helper
++      when: function( subordinate /* , ..., subordinateN */ ) {
++              var i = 0,
++                      resolveValues = 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 ? slice.call( arguments ) : value;
++                                      if ( values === progressValues ) {
++                                              deferred.notifyWith( contexts, values );
++
++                                      } else if ( !( --remaining ) ) {
++                                              deferred.resolveWith( contexts, values );
++                                      }
++                              };
++                      },
++
++                      progressValues, progressContexts, resolveContexts;
++
++              // 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()
++                                              .progress( updateFunc( i, progressContexts, progressValues ) )
++                                              .done( updateFunc( i, resolveContexts, resolveValues ) )
++                                              .fail( deferred.reject );
++                              } else {
++                                      --remaining;
++                              }
++                      }
++              }
++
++              // if we're not waiting on anything, resolve the master
++              if ( !remaining ) {
++                      deferred.resolveWith( resolveContexts, resolveValues );
++              }
++
++              return deferred.promise();
++      }
++} );
++
++
++// The deferred used on DOM ready
++var readyList;
++
++jQuery.fn.ready = function( fn ) {
++
++      // Add the callback
++      jQuery.ready.promise().done( fn );
++
++      return this;
++};
++
++jQuery.extend( {
++
++      // 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;
++              }
++
++              // 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.triggerHandler ) {
++                      jQuery( document ).triggerHandler( "ready" );
++                      jQuery( document ).off( "ready" );
++              }
++      }
++} );
++
++/**
++ * Clean-up method for dom ready events
++ */
++function detach() {
++      if ( document.addEventListener ) {
++              document.removeEventListener( "DOMContentLoaded", completed );
++              window.removeEventListener( "load", completed );
++
++      } else {
++              document.detachEvent( "onreadystatechange", completed );
++              window.detachEvent( "onload", completed );
++      }
++}
++
++/**
++ * The ready event handler and self cleanup method
++ */
++function completed() {
++
++      // readyState === "complete" is good enough for us to call the dom ready in oldIE
++      if ( document.addEventListener ||
++              window.event.type === "load" ||
++              document.readyState === "complete" ) {
++
++              detach();
++              jQuery.ready();
++      }
++}
++
++jQuery.ready.promise = function( obj ) {
++      if ( !readyList ) {
++
++              readyList = jQuery.Deferred();
++
++              // Catch cases where $(document).ready() is called
++              // after the browser event has already occurred.
++              // Support: IE6-10
++              // Older IE sometimes signals "interactive" too soon
++              if ( document.readyState === "complete" ||
++                      ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) {
++
++                      // Handle it asynchronously to allow scripts the opportunity to delay ready
++                      window.setTimeout( jQuery.ready );
++
++              // Standards-based browsers support DOMContentLoaded
++              } else if ( document.addEventListener ) {
++
++                      // Use the handy event callback
++                      document.addEventListener( "DOMContentLoaded", completed );
++
++                      // A fallback to window.onload, that will always work
++                      window.addEventListener( "load", completed );
++
++              // 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 window.setTimeout( doScrollCheck, 50 );
++                                              }
++
++                                              // detach all dom ready events
++                                              detach();
++
++                                              // and execute any waiting functions
++                                              jQuery.ready();
++                                      }
++                              } )();
++                      }
++              }
++      }
++      return readyList.promise( obj );
++};
++
++// Kick off the DOM ready check even if the user does not
++jQuery.ready.promise();
++
++
++
++
++// Support: IE<9
++// Iteration over object's inherited properties before its own
++var i;
++for ( i in jQuery( support ) ) {
++      break;
++}
++support.ownFirst = i === "0";
++
++// Note: most support tests are defined in their respective modules.
++// false until the test is run
++support.inlineBlockNeedsLayout = false;
++
++// Execute ASAP in case we need to set body.style.zoom
++jQuery( function() {
++
++      // Minified: var a,b,c,d
++      var val, div, body, container;
++
++      body = document.getElementsByTagName( "body" )[ 0 ];
++      if ( !body || !body.style ) {
++
++              // Return for frameset docs that don't have a body
++              return;
++      }
++
++      // Setup
++      div = document.createElement( "div" );
++      container = document.createElement( "div" );
++      container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px";
++      body.appendChild( container ).appendChild( div );
++
++      if ( typeof div.style.zoom !== "undefined" ) {
++
++              // 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.style.cssText = "display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1";
++
++              support.inlineBlockNeedsLayout = val = div.offsetWidth === 3;
++              if ( val ) {
++
++                      // 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;
++              }
++      }
++
++      body.removeChild( container );
++} );
++
++
++( function() {
++      var div = document.createElement( "div" );
++
++      // Support: IE<9
++      support.deleteExpando = true;
++      try {
++              delete div.test;
++      } catch ( e ) {
++              support.deleteExpando = false;
++      }
++
++      // Null elements to avoid leaks in IE.
++      div = null;
++} )();
++var acceptData = function( elem ) {
++      var noData = jQuery.noData[ ( elem.nodeName + " " ).toLowerCase() ],
++              nodeType = +elem.nodeType || 1;
++
++      // Do not set data on non-element DOM nodes because it will not be cleared (#8335).
++      return nodeType !== 1 && nodeType !== 9 ?
++              false :
++
++              // Nodes accept data unless otherwise specified; rejection can be conditional
++              !noData || noData !== true && elem.getAttribute( "classid" ) === noData;
++};
++
++
++
++
++var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
++      rmultiDash = /([A-Z])/g;
++
++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 ) {
++
++              var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
++
++              data = elem.getAttribute( name );
++
++              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 ) {}
++
++                      // Make sure we set the data so it isn't changed later
++                      jQuery.data( elem, key, data );
++
++              } else {
++                      data = undefined;
++              }
++      }
++
++      return data;
++}
++
++// checks a cache object for emptiness
++function isEmptyDataObject( obj ) {
++      var name;
++      for ( name in obj ) {
++
++              // 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;
++              }
++      }
++
++      return true;
++}
++
++function internalData( elem, name, data, pvt /* Internal Use Only */ ) {
++      if ( !acceptData( elem ) ) {
++              return;
++      }
++
++      var ret, thisCache,
++              internalKey = jQuery.expando,
++
++              // 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,
++
++              // 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,
++
++              // 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;
++
++      // 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;
++      }
++
++      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 ] = deletedIds.pop() || jQuery.guid++;
++              } else {
++                      id = internalKey;
++              }
++      }
++
++      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 };
++      }
++
++      // 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 );
++              }
++      }
++
++      thisCache = cache[ id ];
++
++      // 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 = {};
++              }
++
++              thisCache = thisCache.data;
++      }
++
++      if ( data !== undefined ) {
++              thisCache[ jQuery.camelCase( name ) ] = data;
++      }
++
++      // Check for both converted-to-camel and non-converted data property names
++      // If a data property was specified
++      if ( typeof name === "string" ) {
++
++              // First Try to find as-is property data
++              ret = thisCache[ name ];
++
++              // Test for null|undefined property data
++              if ( ret == null ) {
++
++                      // Try to find the camelCased property
++                      ret = thisCache[ jQuery.camelCase( name ) ];
++              }
++      } else {
++              ret = thisCache;
++      }
++
++      return ret;
++}
++
++function internalRemoveData( elem, name, pvt ) {
++      if ( !acceptData( elem ) ) {
++              return;
++      }
++
++      var thisCache, i,
++              isNode = elem.nodeType,
++
++              // See jQuery.data for more information
++              cache = isNode ? jQuery.cache : elem,
++              id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
++
++      // If there is already no cache entry for this object, there is no
++      // purpose in continuing
++      if ( !cache[ id ] ) {
++              return;
++      }
++
++      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 ) ) {
++
++                              // try the string as a key before any manipulation
++                              if ( name in thisCache ) {
++                                      name = [ name ];
++                              } else {
++
++                                      // 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 ) );
++                      }
++
++                      i = name.length;
++                      while ( i-- ) {
++                              delete thisCache[ name[ i ] ];
++                      }
++
++                      // 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;
++                      }
++              }
++      }
++
++      // See jQuery.data for more information
++      if ( !pvt ) {
++              delete cache[ id ].data;
++
++              // 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 ( support.deleteExpando || cache != cache.window ) {
++              /* jshint eqeqeq: true */
++              delete cache[ id ];
++
++      // When all else fails, undefined
++      } else {
++              cache[ id ] = undefined;
++      }
++}
++
++jQuery.extend( {
++      cache: {},
++
++      // The following elements (space-suffixed to avoid Object.prototype collisions)
++      // throw uncatchable exceptions if you attempt to set expando properties
++      noData: {
++              "applet ": true,
++              "embed ": true,
++
++              // ...but Flash objects (which have this classid) *can* 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 );
++      },
++
++      data: function( elem, name, data ) {
++              return internalData( elem, name, data );
++      },
++
++      removeData: function( elem, name ) {
++              return internalRemoveData( elem, name );
++      },
++
++      // For internal use only.
++      _data: function( elem, name, data ) {
++              return internalData( elem, name, data, true );
++      },
++
++      _removeData: function( elem, name ) {
++              return internalRemoveData( elem, name, true );
++      }
++} );
++
++jQuery.fn.extend( {
++      data: function( key, value ) {
++              var i, name, data,
++                      elem = this[ 0 ],
++                      attrs = elem && elem.attributes;
++
++              // Special expections of .data basically thwart jQuery.access,
++              // so implement the relevant behavior ourselves
++
++              // Gets all values
++              if ( key === undefined ) {
++                      if ( this.length ) {
++                              data = jQuery.data( elem );
++
++                              if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
++                                      i = attrs.length;
++                                      while ( i-- ) {
++
++                                              // Support: IE11+
++                                              // The attrs elements can be null (#14894)
++                                              if ( attrs[ i ] ) {
++                                                      name = attrs[ i ].name;
++                                                      if ( name.indexOf( "data-" ) === 0 ) {
++                                                              name = jQuery.camelCase( name.slice( 5 ) );
++                                                              dataAttr( elem, name, data[ name ] );
++                                                      }
++                                              }
++                                      }
++                                      jQuery._data( elem, "parsedAttrs", true );
++                              }
++                      }
++
++                      return data;
++              }
++
++              // Sets multiple values
++              if ( typeof key === "object" ) {
++                      return this.each( function() {
++                              jQuery.data( this, key );
++                      } );
++              }
++
++              return arguments.length > 1 ?
++
++                      // Sets one value
++                      this.each( function() {
++                              jQuery.data( this, key, value );
++                      } ) :
++
++                      // Gets one value
++                      // Try to fetch any internally stored data first
++                      elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : undefined;
++      },
++
++      removeData: function( key ) {
++              return this.each( function() {
++                      jQuery.removeData( this, key );
++              } );
++      }
++} );
++
++
++jQuery.extend( {
++      queue: function( elem, type, data ) {
++              var queue;
++
++              if ( elem ) {
++                      type = ( type || "fx" ) + "queue";
++                      queue = jQuery._data( elem, type );
++
++                      // 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 );
++                              }
++                      }
++                      return queue || [];
++              }
++      },
++
++      dequeue: function( elem, type ) {
++              type = type || "fx";
++
++              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--;
++              }
++
++              if ( fn ) {
++
++                      // 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 );
++              }
++
++              if ( !startLength && hooks ) {
++                      hooks.empty.fire();
++              }
++      },
++
++      // 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 );
++                      } )
++              } );
++      }
++} );
++
++jQuery.fn.extend( {
++      queue: function( type, data ) {
++              var setter = 2;
++
++              if ( typeof type !== "string" ) {
++                      data = type;
++                      type = "fx";
++                      setter--;
++              }
++
++              if ( arguments.length < setter ) {
++                      return jQuery.queue( this[ 0 ], type );
++              }
++
++              return data === undefined ?
++                      this :
++                      this.each( function() {
++                              var queue = jQuery.queue( this, type, data );
++
++                              // ensure a hooks for this queue
++                              jQuery._queueHooks( this, type );
++
++                              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", [] );
++      },
++
++      // 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 ] );
++                              }
++                      };
++
++              if ( typeof type !== "string" ) {
++                      obj = type;
++                      type = undefined;
++              }
++              type = type || "fx";
++
++              while ( i-- ) {
++                      tmp = jQuery._data( elements[ i ], type + "queueHooks" );
++                      if ( tmp && tmp.empty ) {
++                              count++;
++                              tmp.empty.add( resolve );
++                      }
++              }
++              resolve();
++              return defer.promise( obj );
++      }
++} );
++
++
++( function() {
++      var shrinkWrapBlocksVal;
++
++      support.shrinkWrapBlocks = function() {
++              if ( shrinkWrapBlocksVal != null ) {
++                      return shrinkWrapBlocksVal;
++              }
++
++              // Will be changed later if needed.
++              shrinkWrapBlocksVal = false;
++
++              // Minified: var b,c,d
++              var div, body, container;
++
++              body = document.getElementsByTagName( "body" )[ 0 ];
++              if ( !body || !body.style ) {
++
++                      // Test fired too early or in an unsupported environment, exit.
++                      return;
++              }
++
++              // Setup
++              div = document.createElement( "div" );
++              container = document.createElement( "div" );
++              container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px";
++              body.appendChild( container ).appendChild( div );
++
++              // Support: IE6
++              // Check if elements with layout shrink-wrap their children
++              if ( typeof div.style.zoom !== "undefined" ) {
++
++                      // Reset CSS: box-sizing; display; margin; border
++                      div.style.cssText =
++
++                              // Support: Firefox<29, Android 2.3
++                              // Vendor-prefix box-sizing
++                              "-webkit-box-sizing:content-box;-moz-box-sizing:content-box;" +
++                              "box-sizing:content-box;display:block;margin:0;border:0;" +
++                              "padding:1px;width:1px;zoom:1";
++                      div.appendChild( document.createElement( "div" ) ).style.width = "5px";
++                      shrinkWrapBlocksVal = div.offsetWidth !== 3;
++              }
++
++              body.removeChild( container );
++
++              return shrinkWrapBlocksVal;
++      };
++
++} )();
++var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source;
++
++var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" );
++
++
++var cssExpand = [ "Top", "Right", "Bottom", "Left" ];
++
++var isHidden = function( 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 );
++      };
++
++
++
++function adjustCSS( elem, prop, valueParts, tween ) {
++      var adjusted,
++              scale = 1,
++              maxIterations = 20,
++              currentValue = tween ?
++                      function() { return tween.cur(); } :
++                      function() { return jQuery.css( elem, prop, "" ); },
++              initial = currentValue(),
++              unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
++
++              // Starting value computation is required for potential unit mismatches
++              initialInUnit = ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) &&
++                      rcssNum.exec( jQuery.css( elem, prop ) );
++
++      if ( initialInUnit && initialInUnit[ 3 ] !== unit ) {
++
++              // Trust units reported by jQuery.css
++              unit = unit || initialInUnit[ 3 ];
++
++              // Make sure we update the tween properties later on
++              valueParts = valueParts || [];
++
++              // Iteratively approximate from a nonzero starting point
++              initialInUnit = +initial || 1;
++
++              do {
++
++                      // If previous iteration zeroed out, double until we get *something*.
++                      // Use string for doubling so we don't accidentally see scale as unchanged below
++                      scale = scale || ".5";
++
++                      // Adjust and apply
++                      initialInUnit = initialInUnit / scale;
++                      jQuery.style( elem, prop, initialInUnit + unit );
++
++              // Update scale, tolerating zero or NaN from tween.cur()
++              // Break the loop if scale is unchanged or perfect, or if we've just had enough.
++              } while (
++                      scale !== ( scale = currentValue() / initial ) && scale !== 1 && --maxIterations
++              );
++      }
++
++      if ( valueParts ) {
++              initialInUnit = +initialInUnit || +initial || 0;
++
++              // 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;
++              }
++      }
++      return adjusted;
++}
++
++
++// 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,
++              length = elems.length,
++              bulk = key == null;
++
++      // Sets many values
++      if ( jQuery.type( key ) === "object" ) {
++              chainable = true;
++              for ( i in key ) {
++                      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;
++};
++var rcheckableType = ( /^(?:checkbox|radio)$/i );
++
++var rtagName = ( /<([\w:-]+)/ );
++
++var rscriptType = ( /^$|\/(?:java|ecma)script/i );
++
++var rleadingWhitespace = ( /^\s+/ );
++
++var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|" +
++              "details|dialog|figcaption|figure|footer|header|hgroup|main|" +
++              "mark|meter|nav|output|picture|progress|section|summary|template|time|video";
++
++
++
++function createSafeFragment( document ) {
++      var list = nodeNames.split( "|" ),
++              safeFrag = document.createDocumentFragment();
++
++      if ( safeFrag.createElement ) {
++              while ( list.length ) {
++                      safeFrag.createElement(
++                              list.pop()
++                      );
++              }
++      }
++      return safeFrag;
++}
++
++
++( function() {
++      var div = document.createElement( "div" ),
++              fragment = document.createDocumentFragment(),
++              input = document.createElement( "input" );
++
++      // Setup
++      div.innerHTML = "  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>";
++
++      // IE strips leading whitespace when .innerHTML is used
++      support.leadingWhitespace = div.firstChild.nodeType === 3;
++
++      // Make sure that tbody elements aren't automatically inserted
++      // IE will insert them into empty tables
++      support.tbody = !div.getElementsByTagName( "tbody" ).length;
++
++      // Make sure that link elements get serialized correctly by innerHTML
++      // This requires a wrapper element in IE
++      support.htmlSerialize = !!div.getElementsByTagName( "link" ).length;
++
++      // Makes sure cloning an html5 element does not cause problems
++      // Where outerHTML is undefined, this still works
++      support.html5Clone =
++              document.createElement( "nav" ).cloneNode( true ).outerHTML !== "<:nav></:nav>";
++
++      // Check if a disconnected checkbox will retain its checked
++      // value of true after appended to the DOM (IE6/7)
++      input.type = "checkbox";
++      input.checked = true;
++      fragment.appendChild( input );
++      support.appendChecked = input.checked;
++
++      // Make sure textarea (and checkbox) defaultValue is properly cloned
++      // Support: IE6-IE11+
++      div.innerHTML = "<textarea>x</textarea>";
++      support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;
++
++      // #11217 - WebKit loses check when the name is after the checked attribute
++      fragment.appendChild( div );
++
++      // Support: Windows Web Apps (WWA)
++      // `name` and `type` must use .setAttribute for WWA (#14901)
++      input = document.createElement( "input" );
++      input.setAttribute( "type", "radio" );
++      input.setAttribute( "checked", "checked" );
++      input.setAttribute( "name", "t" );
++
++      div.appendChild( input );
++
++      // Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3
++      // old WebKit doesn't clone checked state correctly in fragments
++      support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;
++
++      // Support: IE<9
++      // Cloned elements keep attachEvent handlers, we use addEventListener on IE9+
++      support.noCloneEvent = !!div.addEventListener;
++
++      // Support: IE<9
++      // Since attributes and properties are the same in IE,
++      // cleanData must set properties to undefined rather than use removeAttribute
++      div[ jQuery.expando ] = 1;
++      support.attributes = !div.getAttribute( jQuery.expando );
++} )();
++
++
++// We have to close these tags to support XHTML (#13200)
++var wrapMap = {
++      option: [ 1, "<select multiple='multiple'>", "</select>" ],
++      legend: [ 1, "<fieldset>", "</fieldset>" ],
++      area: [ 1, "<map>", "</map>" ],
++
++      // Support: IE8
++      param: [ 1, "<object>", "</object>" ],
++      thead: [ 1, "<table>", "</table>" ],
++      tr: [ 2, "<table><tbody>", "</tbody></table>" ],
++      col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
++      td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
++
++      // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags,
++      // unless wrapped in a div with non-breaking characters in front of it.
++      _default: support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X<div>", "</div>" ]
++};
++
++// Support: IE8-IE9
++wrapMap.optgroup = wrapMap.option;
++
++wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
++wrapMap.th = wrapMap.td;
++
++
++function getAll( context, tag ) {
++      var elems, elem,
++              i = 0,
++              found = typeof context.getElementsByTagName !== "undefined" ?
++                      context.getElementsByTagName( tag || "*" ) :
++                      typeof context.querySelectorAll !== "undefined" ?
++                              context.querySelectorAll( tag || "*" ) :
++                              undefined;
++
++      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 ) );
++                      }
++              }
++      }
++
++      return tag === undefined || tag && jQuery.nodeName( context, tag ) ?
++              jQuery.merge( [ context ], found ) :
++              found;
++}
++
++
++// 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" )
++              );
++      }
++}
++
++
++var rhtml = /<|&#?\w+;/,
++      rtbody = /<tbody/i;
++
++function fixDefaultChecked( elem ) {
++      if ( rcheckableType.test( elem.type ) ) {
++              elem.defaultChecked = elem.checked;
++      }
++}
++
++function buildFragment( elems, context, scripts, selection, ignored ) {
++      var j, elem, contains,
++              tmp, tag, tbody, wrap,
++              l = elems.length,
++
++              // Ensure a safe fragment
++              safe = createSafeFragment( context ),
++
++              nodes = [],
++              i = 0;
++
++      for ( ; i < l; i++ ) {
++              elem = elems[ i ];
++
++              if ( elem || elem === 0 ) {
++
++                      // Add nodes directly
++                      if ( jQuery.type( elem ) === "object" ) {
++                              jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );
++
++                      // Convert non-html into a text node
++                      } else if ( !rhtml.test( elem ) ) {
++                              nodes.push( context.createTextNode( elem ) );
++
++                      // Convert html into DOM nodes
++                      } else {
++                              tmp = tmp || safe.appendChild( context.createElement( "div" ) );
++
++                              // Deserialize a standard representation
++                              tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase();
++                              wrap = wrapMap[ tag ] || wrapMap._default;
++
++                              tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ];
++
++                              // Descend through wrappers to the right content
++                              j = wrap[ 0 ];
++                              while ( j-- ) {
++                                      tmp = tmp.lastChild;
++                              }
++
++                              // Manually add leading whitespace removed by IE
++                              if ( !support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
++                                      nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[ 0 ] ) );
++                              }
++
++                              // Remove IE's autoinserted <tbody> from table fragments
++                              if ( !support.tbody ) {
++
++                                      // String was a <table>, *may* have spurious <tbody>
++                                      elem = tag === "table" && !rtbody.test( elem ) ?
++                                              tmp.firstChild :
++
++                                              // String was a bare <thead> or <tfoot>
++                                              wrap[ 1 ] === "<table>" && !rtbody.test( elem ) ?
++                                                      tmp :
++                                                      0;
++
++                                      j = elem && elem.childNodes.length;
++                                      while ( j-- ) {
++                                              if ( jQuery.nodeName( ( tbody = elem.childNodes[ j ] ), "tbody" ) &&
++                                                      !tbody.childNodes.length ) {
++
++                                                      elem.removeChild( tbody );
++                                              }
++                                      }
++                              }
++
++                              jQuery.merge( nodes, tmp.childNodes );
++
++                              // Fix #12392 for WebKit and IE > 9
++                              tmp.textContent = "";
++
++                              // Fix #12392 for oldIE
++                              while ( tmp.firstChild ) {
++                                      tmp.removeChild( tmp.firstChild );
++                              }
++
++                              // Remember the top-level container for proper cleanup
++                              tmp = safe.lastChild;
++                      }
++              }
++      }
++
++      // Fix #11356: Clear elements from fragment
++      if ( tmp ) {
++              safe.removeChild( tmp );
++      }
++
++      // Reset defaultChecked for any radios and checkboxes
++      // about to be appended to the DOM in IE 6/7 (#8060)
++      if ( !support.appendChecked ) {
++              jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked );
++      }
++
++      i = 0;
++      while ( ( elem = nodes[ i++ ] ) ) {
++
++              // Skip elements already in the context collection (trac-4087)
++              if ( selection && jQuery.inArray( elem, selection ) > -1 ) {
++                      if ( ignored ) {
++                              ignored.push( elem );
++                      }
++
++                      continue;
++              }
++
++              contains = jQuery.contains( elem.ownerDocument, elem );
++
++              // Append to fragment
++              tmp = getAll( safe.appendChild( elem ), "script" );
++
++              // Preserve script evaluation history
++              if ( contains ) {
++                      setGlobalEval( tmp );
++              }
++
++              // Capture executables
++              if ( scripts ) {
++                      j = 0;
++                      while ( ( elem = tmp[ j++ ] ) ) {
++                              if ( rscriptType.test( elem.type || "" ) ) {
++                                      scripts.push( elem );
++                              }
++                      }
++              }
++      }
++
++      tmp = null;
++
++      return safe;
++}
++
++
++( function() {
++      var i, eventName,
++              div = document.createElement( "div" );
++
++      // Support: IE<9 (lack submit/change bubble), Firefox (lack focus(in | out) events)
++      for ( i in { submit: true, change: true, focusin: true } ) {
++              eventName = "on" + i;
++
++              if ( !( support[ i ] = eventName in window ) ) {
++
++                      // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP)
++                      div.setAttribute( eventName, "t" );
++                      support[ i ] = div.attributes[ eventName ].expando === false;
++              }
++      }
++
++      // Null elements to avoid leaks in IE.
++      div = null;
++} )();
++
++
++var rformElems = /^(?:input|select|textarea)$/i,
++      rkeyEvent = /^key/,
++      rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/,
++      rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
++      rtypenamespace = /^([^.]*)(?:\.(.+)|)/;
++
++function returnTrue() {
++      return true;
++}
++
++function returnFalse() {
++      return false;
++}
++
++// Support: IE9
++// See #13393 for more info
++function safeActiveElement() {
++      try {
++              return document.activeElement;
++      } catch ( err ) { }
++}
++
++function on( elem, types, selector, data, fn, one ) {
++      var origFn, type;
++
++      // 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 ) {
++                      on( elem, type, selector, data, types[ type ], one );
++              }
++              return elem;
++      }
++
++      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 elem;
++      }
++
++      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 );
++      } );
++}
++
++/*
++ * 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 tmp, events, t, handleObjIn,
++                      special, eventHandle, handleObj,
++                      handlers, type, namespaces, origType,
++                      elemData = jQuery._data( elem );
++
++              // Don't attach events to noData or text/comment nodes (but allow plain objects)
++              if ( !elemData ) {
++                      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;
++              }
++
++              // Make sure that the handler has a unique ID, used to find/remove it later
++              if ( !handler.guid ) {
++                      handler.guid = jQuery.guid++;
++              }
++
++              // 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 !== "undefined" &&
++                                      ( !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;
++              }
++
++              // Handle multiple events separated by a space
++              types = ( types || "" ).match( rnotwhite ) || [ "" ];
++              t = types.length;
++              while ( t-- ) {
++                      tmp = rtypenamespace.exec( types[ t ] ) || [];
++                      type = origType = tmp[ 1 ];
++                      namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort();
++
++                      // There *must* be a type, no attaching namespace-only handlers
++                      if ( !type ) {
++                              continue;
++                      }
++
++                      // If event changes its type, use the special event handlers for the changed type
++                      special = jQuery.event.special[ type ] || {};
++
++                      // 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 );
++
++                      // Init the event handler queue if we're the first
++                      if ( !( handlers = events[ type ] ) ) {
++                              handlers = events[ type ] = [];
++                              handlers.delegateCount = 0;
++
++                              // 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 );
++
++                                      } else if ( elem.attachEvent ) {
++                                              elem.attachEvent( "on" + type, eventHandle );
++                                      }
++                              }
++                      }
++
++                      if ( special.add ) {
++                              special.add.call( elem, handleObj );
++
++                              if ( !handleObj.handler.guid ) {
++                                      handleObj.handler.guid = handler.guid;
++                              }
++                      }
++
++                      // Add to the element's handler list, delegates in front
++                      if ( selector ) {
++                              handlers.splice( handlers.delegateCount++, 0, handleObj );
++                      } else {
++                              handlers.push( handleObj );
++                      }
++
++                      // Keep track of which events have ever been used, for event optimization
++                      jQuery.event.global[ type ] = true;
++              }
++
++              // Nullify elem to prevent memory leaks in IE
++              elem = null;
++      },
++
++      // 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 );
++
++              if ( !elemData || !( events = elemData.events ) ) {
++                      return;
++              }
++
++              // Once for each type.namespace in types; type may be omitted
++              types = ( types || "" ).match( rnotwhite ) || [ "" ];
++              t = types.length;
++              while ( t-- ) {
++                      tmp = rtypenamespace.exec( types[ t ] ) || [];
++                      type = origType = tmp[ 1 ];
++                      namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort();
++
++                      // 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;
++                      }
++
++                      special = jQuery.event.special[ type ] || {};
++                      type = ( selector ? special.delegateType : special.bindType ) || type;
++                      handlers = events[ type ] || [];
++                      tmp = tmp[ 2 ] &&
++                              new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" );
++
++                      // Remove matching events
++                      origCount = j = handlers.length;
++                      while ( j-- ) {
++                              handleObj = handlers[ j ];
++
++                              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 ( handleObj.selector ) {
++                                              handlers.delegateCount--;
++                                      }
++                                      if ( special.remove ) {
++                                              special.remove.call( elem, handleObj );
++                                      }
++                              }
++                      }
++
++                      // 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 ];
++                      }
++              }
++
++              // 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" );
++              }
++      },
++
++      trigger: function( event, data, elem, onlyHandlers ) {
++              var handle, ontype, cur,
++                      bubbleType, special, tmp, i,
++                      eventPath = [ elem || document ],
++                      type = hasOwn.call( event, "type" ) ? event.type : event,
++                      namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : [];
++
++              cur = tmp = elem = elem || document;
++
++              // Don't do events on text and comment nodes
++              if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
++                      return;
++              }
++
++              // focus/blur morphs to focusin/out; ensure we're not firing them right now
++              if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
++                      return;
++              }
++
++              if ( type.indexOf( "." ) > -1 ) {
++
++                      // 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;
++
++              // 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 );
++
++              // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
++              event.isTrigger = onlyHandlers ? 2 : 3;
++              event.namespace = namespaces.join( "." );
++              event.rnamespace = event.namespace ?
++                      new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) :
++                      null;
++
++              // Clean up the event in case it is being reused
++              event.result = undefined;
++              if ( !event.target ) {
++                      event.target = elem;
++              }
++
++              // Clone any incoming data and prepend the event, creating the handler arg list
++              data = data == null ?
++                      [ event ] :
++                      jQuery.makeArray( data, [ event ] );
++
++              // 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 ) ) {
++
++                      bubbleType = special.delegateType || type;
++                      if ( !rfocusMorph.test( bubbleType + type ) ) {
++                              cur = cur.parentNode;
++                      }
++                      for ( ; cur; cur = cur.parentNode ) {
++                              eventPath.push( cur );
++                              tmp = cur;
++                      }
++
++                      // 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 );
++                      }
++              }
++
++              // Fire handlers on the event path
++              i = 0;
++              while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) {
++
++                      event.type = i > 1 ?
++                              bubbleType :
++                              special.bindType || type;
++
++                      // jQuery handler
++                      handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] &&
++                              jQuery._data( cur, "handle" );
++
++                      if ( handle ) {
++                              handle.apply( cur, data );
++                      }
++
++                      // Native handler
++                      handle = ontype && cur[ ontype ];
++                      if ( handle && handle.apply && acceptData( cur ) ) {
++                              event.result = handle.apply( cur, data );
++                              if ( event.result === false ) {
++                                      event.preventDefault();
++                              }
++                      }
++              }
++              event.type = type;
++
++              // If nobody prevented the default action, do it now
++              if ( !onlyHandlers && !event.isDefaultPrevented() ) {
++
++                      if (
++                              ( !special._default ||
++                               special._default.apply( eventPath.pop(), data ) === false
++                              ) && 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 ( tmp ) {
++                                              elem[ ontype ] = tmp;
++                                      }
++                              }
++                      }
++              }
++
++              return event.result;
++      },
++
++      dispatch: function( event ) {
++
++              // Make a writable jQuery.Event from the native event object
++              event = jQuery.event.fix( event );
++
++              var i, j, ret, matched, handleObj,
++                      handlerQueue = [],
++                      args = slice.call( arguments ),
++                      handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [],
++                      special = jQuery.event.special[ event.type ] || {};
++
++              // Use the fix-ed jQuery.Event rather than the (read-only) native event
++              args[ 0 ] = event;
++              event.delegateTarget = this;
++
++              // Call the preDispatch hook for the mapped type, and let it bail if desired
++              if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
++                      return;
++              }
++
++              // 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() ) {
++
++                              // 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.rnamespace || event.rnamespace.test( handleObj.namespace ) ) {
++
++                                      event.handleObj = handleObj;
++                                      event.data = handleObj.data;
++
++                                      ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle ||
++                                              handleObj.handler ).apply( matched.elem, args );
++
++                                      if ( ret !== undefined ) {
++                                              if ( ( event.result = ret ) === false ) {
++                                                      event.preventDefault();
++                                                      event.stopPropagation();
++                                              }
++                                      }
++                              }
++                      }
++              }
++
++              // Call the postDispatch hook for the mapped type
++              if ( special.postDispatch ) {
++                      special.postDispatch.call( this, event );
++              }
++
++              return event.result;
++      },
++
++      handlers: function( event, handlers ) {
++              var i, matches, sel, handleObj,
++                      handlerQueue = [],
++                      delegateCount = handlers.delegateCount,
++                      cur = event.target;
++
++              // Support (at least): Chrome, IE9
++              // Find delegate handlers
++              // Black-hole SVG <use> instance trees (#13180)
++              //
++              // Support: Firefox<=42+
++              // Avoid non-left-click in FF but don't block IE radio events (#3861, gh-2343)
++              if ( delegateCount && cur.nodeType &&
++                      ( event.type !== "click" || isNaN( event.button ) || event.button < 1 ) ) {
++
++                      /* jshint eqeqeq: false */
++                      for ( ; cur != this; cur = cur.parentNode || this ) {
++                              /* jshint eqeqeq: true */
++
++                              // 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 ];
++
++                                              // Don't conflict with Object.prototype properties (#13203)
++                                              sel = handleObj.selector + " ";
++
++                                              if ( matches[ sel ] === undefined ) {
++                                                      matches[ sel ] = handleObj.needsContext ?
++                                                              jQuery( sel, this ).index( cur ) > -1 :
++                                                              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 ) } );
++              }
++
++              return handlerQueue;
++      },
++
++      fix: function( event ) {
++              if ( event[ jQuery.expando ] ) {
++                      return event;
++              }
++
++              // 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 ];
++
++              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;
++
++              event = new jQuery.Event( originalEvent );
++
++              i = copy.length;
++              while ( i-- ) {
++                      prop = copy[ i ];
++                      event[ prop ] = originalEvent[ prop ];
++              }
++
++              // Support: IE<9
++              // Fix target property (#1925)
++              if ( !event.target ) {
++                      event.target = originalEvent.srcElement || document;
++              }
++
++              // Support: Safari 6-8+
++              // Target should not be a text node (#504, #13143)
++              if ( event.target.nodeType === 3 ) {
++                      event.target = event.target.parentNode;
++              }
++
++              // Support: IE<9
++              // For mouse/key events, metaKey==false if it's undefined (#3368, #11328)
++              event.metaKey = !!event.metaKey;
++
++              return fixHook.filter ? fixHook.filter( event, originalEvent ) : event;
++      },
++
++      // Includes some event props shared by KeyEvent and MouseEvent
++      props: ( "altKey bubbles cancelable ctrlKey currentTarget detail eventPhase " +
++              "metaKey relatedTarget shiftKey target timeStamp view which" ).split( " " ),
++
++      fixHooks: {},
++
++      keyHooks: {
++              props: "char charCode key keyCode".split( " " ),
++              filter: function( event, original ) {
++
++                      // Add which for key events
++                      if ( event.which == null ) {
++                              event.which = original.charCode != null ? original.charCode : original.keyCode;
++                      }
++
++                      return event;
++              }
++      },
++
++      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;
++
++                      // 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;
++
++                              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 );
++                      }
++
++                      // Add relatedTarget, if necessary
++                      if ( !event.relatedTarget && fromElement ) {
++                              event.relatedTarget = fromElement === event.target ?
++                                      original.toElement :
++                                      fromElement;
++                      }
++
++                      // 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 ) ) );
++                      }
++
++                      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" );
++                      }
++              },
++
++              beforeunload: {
++                      postDispatch: function( event ) {
++
++                              // 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;
++                              }
++                      }
++              }
++      },
++
++      // Piggyback on a donor event to simulate a different one
++      simulate: function( type, elem, event ) {
++              var e = jQuery.extend(
++                      new jQuery.Event(),
++                      event,
++                      {
++                              type: type,
++                              isSimulated: true
++
++                              // Previously, `originalEvent: {}` was set here, so stopPropagation call
++                              // would not be triggered on donor event, since in our own
++                              // jQuery.event.stopPropagation function we had a check for existence of
++                              // originalEvent.stopPropagation method, so, consequently it would be a noop.
++                              //
++                              // Guard for simulated events was moved to jQuery.event.stopPropagation function
++                              // since `originalEvent` should point to the original event for the
++                              // constancy with other events and for more focused logic
++                      }
++              );
++
++              jQuery.event.trigger( e, null, elem );
++
++              if ( e.isDefaultPrevented() ) {
++                      event.preventDefault();
++              }
++      }
++};
++
++jQuery.removeEvent = document.removeEventListener ?
++      function( elem, type, handle ) {
++
++              // This "if" is needed for plain objects
++              if ( elem.removeEventListener ) {
++                      elem.removeEventListener( type, handle );
++              }
++      } :
++      function( elem, type, handle ) {
++              var name = "on" + type;
++
++              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 ] === "undefined" ) {
++                              elem[ name ] = null;
++                      }
++
++                      elem.detachEvent( name, handle );
++              }
++      };
++
++jQuery.Event = function( src, props ) {
++
++      // Allow instantiation without the 'new' keyword
++      if ( !( this instanceof jQuery.Event ) ) {
++              return new jQuery.Event( src, props );
++      }
++
++      // 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.defaultPrevented === undefined &&
++
++                              // Support: IE < 9, Android < 4.0
++                              src.returnValue === false ?
++                      returnTrue :
++                      returnFalse;
++
++      // Event type
++      } else {
++              this.type = src;
++      }
++
++      // Put explicitly provided properties onto the event object
++      if ( props ) {
++              jQuery.extend( this, props );
++      }
++
++      // 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 = {
++      constructor: jQuery.Event,
++      isDefaultPrevented: returnFalse,
++      isPropagationStopped: returnFalse,
++      isImmediatePropagationStopped: returnFalse,
++
++      preventDefault: function() {
++              var e = this.originalEvent;
++
++              this.isDefaultPrevented = returnTrue;
++              if ( !e ) {
++                      return;
++              }
++
++              // If preventDefault exists, run it on the original event
++              if ( e.preventDefault ) {
++                      e.preventDefault();
++
++              // Support: IE
++              // Otherwise set the returnValue property of the original event to false
++              } else {
++                      e.returnValue = false;
++              }
++      },
++      stopPropagation: function() {
++              var e = this.originalEvent;
++
++              this.isPropagationStopped = returnTrue;
++
++              if ( !e || this.isSimulated ) {
++                      return;
++              }
++
++              // If stopPropagation exists, run it on the original event
++              if ( e.stopPropagation ) {
++                      e.stopPropagation();
++              }
++
++              // Support: IE
++              // Set the cancelBubble property of the original event to true
++              e.cancelBubble = true;
++      },
++      stopImmediatePropagation: function() {
++              var e = this.originalEvent;
++
++              this.isImmediatePropagationStopped = returnTrue;
++
++              if ( e && e.stopImmediatePropagation ) {
++                      e.stopImmediatePropagation();
++              }
++
++              this.stopPropagation();
++      }
++};
++
++// 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://code.google.com/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,
++
++              handle: function( event ) {
++                      var ret,
++                              target = this,
++                              related = event.relatedTarget,
++                              handleObj = event.handleObj;
++
++                      // 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;
++              }
++      };
++} );
++
++// IE submit delegation
++if ( !support.submit ) {
++
++      jQuery.event.special.submit = {
++              setup: function() {
++
++                      // Only need this for delegated form submit events
++                      if ( jQuery.nodeName( this, "form" ) ) {
++                              return false;
++                      }
++
++                      // 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" ) ?
++
++                                              // Support: IE <=8
++                                              // We use jQuery.prop instead of elem.form
++                                              // to allow fixing the IE8 delegated submit issue (gh-2332)
++                                              // by 3rd party polyfills/workarounds.
++                                              jQuery.prop( elem, "form" ) :
++                                              undefined;
++
++                              if ( form && !jQuery._data( form, "submit" ) ) {
++                                      jQuery.event.add( form, "submit._submit", function( event ) {
++                                              event._submitBubble = true;
++                                      } );
++                                      jQuery._data( form, "submit", 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._submitBubble ) {
++                              delete event._submitBubble;
++                              if ( this.parentNode && !event.isTrigger ) {
++                                      jQuery.event.simulate( "submit", this.parentNode, event );
++                              }
++                      }
++              },
++
++              teardown: function() {
++
++                      // Only need this for delegated form submit events
++                      if ( jQuery.nodeName( this, "form" ) ) {
++                              return false;
++                      }
++
++                      // Remove delegated handlers; cleanData eventually reaps submit handlers attached above
++                      jQuery.event.remove( this, "._submit" );
++              }
++      };
++}
++
++// IE change delegation and checkbox/radio fix
++if ( !support.change ) {
++
++      jQuery.event.special.change = {
++
++              setup: function() {
++
++                      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._justChanged = true;
++                                              }
++                                      } );
++                                      jQuery.event.add( this, "click._change", function( event ) {
++                                              if ( this._justChanged && !event.isTrigger ) {
++                                                      this._justChanged = false;
++                                              }
++
++                                              // Allow triggered, simulated change events (#11500)
++                                              jQuery.event.simulate( "change", this, event );
++                                      } );
++                              }
++                              return false;
++                      }
++
++                      // Delegated event; lazy-add a change handler on descendant inputs
++                      jQuery.event.add( this, "beforeactivate._change", function( e ) {
++                              var elem = e.target;
++
++                              if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "change" ) ) {
++                                      jQuery.event.add( elem, "change._change", function( event ) {
++                                              if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
++                                                      jQuery.event.simulate( "change", this.parentNode, event );
++                                              }
++                                      } );
++                                      jQuery._data( elem, "change", true );
++                              }
++                      } );
++              },
++
++              handle: function( event ) {
++                      var elem = event.target;
++
++                      // 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 );
++                      }
++              },
++
++              teardown: function() {
++                      jQuery.event.remove( this, "._change" );
++
++                      return !rformElems.test( this.nodeName );
++              }
++      };
++}
++
++// Support: Firefox
++// Firefox doesn't have focus(in | out) events
++// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787
++//
++// Support: Chrome, Safari
++// focus(in | out) events fire after focus & blur events,
++// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order
++// Related ticket - https://code.google.com/p/chromium/issues/detail?id=449857
++if ( !support.focusin ) {
++      jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) {
++
++              // Attach a single capturing handler on the document while someone wants focusin/focusout
++              var handler = function( event ) {
++                      jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) );
++              };
++
++              jQuery.event.special[ fix ] = {
++                      setup: function() {
++                              var doc = this.ownerDocument || this,
++                                      attaches = jQuery._data( doc, fix );
++
++                              if ( !attaches ) {
++                                      doc.addEventListener( orig, handler, true );
++                              }
++                              jQuery._data( doc, fix, ( attaches || 0 ) + 1 );
++                      },
++                      teardown: function() {
++                              var doc = this.ownerDocument || this,
++                                      attaches = jQuery._data( doc, fix ) - 1;
++
++                              if ( !attaches ) {
++                                      doc.removeEventListener( orig, handler, true );
++                                      jQuery._removeData( doc, fix );
++                              } else {
++                                      jQuery._data( doc, fix, attaches );
++                              }
++                      }
++              };
++      } );
++}
++
++jQuery.fn.extend( {
++
++      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 ) {
++
++                      // ( 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 );
++              } );
++      },
++
++      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 rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g,
++      rnoshimcache = new RegExp( "<(?:" + nodeNames + ")[\\s/>]", "i" ),
++      rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,
++
++      // Support: IE 10-11, Edge 10240+
++      // In IE/Edge using regex groups here causes severe slowdowns.
++      // See https://connect.microsoft.com/IE/feedback/details/1736512/
++      rnoInnerhtml = /<script|<style|<link/i,
++
++      // checked="checked" or checked
++      rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
++      rscriptTypeMasked = /^true\/(.*)/,
++      rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,
++      safeFragment = createSafeFragment( document ),
++      fragmentDiv = safeFragment.appendChild( document.createElement( "div" ) );
++
++// Support: IE<8
++// Manipulating tables requires a tbody
++function manipulationTarget( elem, content ) {
++      return jQuery.nodeName( elem, "table" ) &&
++              jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ?
++
++              elem.getElementsByTagName( "tbody" )[ 0 ] ||
++                      elem.appendChild( elem.ownerDocument.createElement( "tbody" ) ) :
++              elem;
++}
++
++// 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;
++}
++
++function cloneCopyEvent( src, dest ) {
++      if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
++              return;
++      }
++
++      var type, i, l,
++              oldData = jQuery._data( src ),
++              curData = jQuery._data( dest, oldData ),
++              events = oldData.events;
++
++      if ( events ) {
++              delete curData.handle;
++              curData.events = {};
++
++              for ( type in events ) {
++                      for ( i = 0, l = events[ type ].length; i < l; i++ ) {
++                              jQuery.event.add( dest, type, events[ type ][ i ] );
++                      }
++              }
++      }
++
++      // 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;
++
++      // We do not need to do anything for non-Elements
++      if ( dest.nodeType !== 1 ) {
++              return;
++      }
++
++      nodeName = dest.nodeName.toLowerCase();
++
++      // IE6-8 copies events bound via attachEvent when using cloneNode.
++      if ( !support.noCloneEvent && dest[ jQuery.expando ] ) {
++              data = jQuery._data( dest );
++
++              for ( e in data.events ) {
++                      jQuery.removeEvent( dest, e, data.handle );
++              }
++
++              // Event data gets referenced instead of copied if the expando gets copied too
++              dest.removeAttribute( jQuery.expando );
++      }
++
++      // 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 );
++
++      // 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;
++              }
++
++              // 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 ( support.html5Clone && ( src.innerHTML && !jQuery.trim( dest.innerHTML ) ) ) {
++                      dest.innerHTML = src.innerHTML;
++              }
++
++      } else if ( nodeName === "input" && 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
++
++              dest.defaultChecked = dest.checked = src.checked;
++
++              // 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;
++              }
++
++      // 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;
++
++      // 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;
++      }
++}
++
++function domManip( collection, args, callback, ignored ) {
++
++      // Flatten any nested arrays
++      args = concat.apply( [], args );
++
++      var first, node, hasScripts,
++              scripts, doc, fragment,
++              i = 0,
++              l = collection.length,
++              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" &&
++                              !support.checkClone && rchecked.test( value ) ) ) {
++              return collection.each( function( index ) {
++                      var self = collection.eq( index );
++                      if ( isFunction ) {
++                              args[ 0 ] = value.call( this, index, self.html() );
++                      }
++                      domManip( self, args, callback, ignored );
++              } );
++      }
++
++      if ( l ) {
++              fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored );
++              first = fragment.firstChild;
++
++              if ( fragment.childNodes.length === 1 ) {
++                      fragment = first;
++              }
++
++              // 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;
++
++                      // 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;
++
++                              if ( i !== iNoClone ) {
++                                      node = jQuery.clone( node, true, true );
++
++                                      // Keep references to cloned scripts for later restoration
++                                      if ( hasScripts ) {
++
++                                              // Support: Android<4.1, PhantomJS<2
++                                              // push.apply(_, arraylike) throws on ancient WebKit
++                                              jQuery.merge( scripts, getAll( node, "script" ) );
++                                      }
++                              }
++
++                              callback.call( collection[ i ], node, i );
++                      }
++
++                      if ( hasScripts ) {
++                              doc = scripts[ scripts.length - 1 ].ownerDocument;
++
++                              // Reenable scripts
++                              jQuery.map( scripts, restoreScript );
++
++                              // 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 ) ) {
++
++                                              if ( node.src ) {
++
++                                                      // Optional AJAX dependency, but won't run scripts if not present
++                                                      if ( jQuery._evalUrl ) {
++                                                              jQuery._evalUrl( node.src );
++                                                      }
++                                              } else {
++                                                      jQuery.globalEval(
++                                                              ( node.text || node.textContent || node.innerHTML || "" )
++                                                                      .replace( rcleanScript, "" )
++                                                      );
++                                              }
++                                      }
++                              }
++                      }
++
++                      // Fix #11809: Avoid leaking memory
++                      fragment = first = null;
++              }
++      }
++
++      return collection;
++}
++
++function remove( elem, selector, keepData ) {
++      var node,
++              elems = selector ? jQuery.filter( selector, elem ) : elem,
++              i = 0;
++
++      for ( ; ( node = elems[ i ] ) != null; i++ ) {
++
++              if ( !keepData && node.nodeType === 1 ) {
++                      jQuery.cleanData( getAll( node ) );
++              }
++
++              if ( node.parentNode ) {
++                      if ( keepData && jQuery.contains( node.ownerDocument, node ) ) {
++                              setGlobalEval( getAll( node, "script" ) );
++                      }
++                      node.parentNode.removeChild( node );
++              }
++      }
++
++      return elem;
++}
++
++jQuery.extend( {
++      htmlPrefilter: function( html ) {
++              return html.replace( rxhtmlTag, "<$1></$2>" );
++      },
++
++      clone: function( elem, dataAndEvents, deepDataAndEvents ) {
++              var destElements, node, clone, i, srcElements,
++                      inPage = jQuery.contains( elem.ownerDocument, elem );
++
++              if ( support.html5Clone || jQuery.isXMLDoc( elem ) ||
++                      !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) {
++
++                      clone = elem.cloneNode( true );
++
++              // IE<=8 does not properly clone detached, unknown element nodes
++              } else {
++                      fragmentDiv.innerHTML = elem.outerHTML;
++                      fragmentDiv.removeChild( clone = fragmentDiv.firstChild );
++              }
++
++              if ( ( !support.noCloneEvent || !support.noCloneChecked ) &&
++                              ( elem.nodeType === 1 || elem.nodeType === 11 ) && !jQuery.isXMLDoc( elem ) ) {
++
++                      // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2
++                      destElements = getAll( clone );
++                      srcElements = getAll( elem );
++
++                      // 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 ] );
++                              }
++                      }
++              }
++
++              // Copy the events from the original to the clone
++              if ( dataAndEvents ) {
++                      if ( deepDataAndEvents ) {
++                              srcElements = srcElements || getAll( elem );
++                              destElements = destElements || getAll( clone );
++
++                              for ( i = 0; ( node = srcElements[ i ] ) != null; i++ ) {
++                                      cloneCopyEvent( node, destElements[ i ] );
++                              }
++                      } else {
++                              cloneCopyEvent( elem, clone );
++                      }
++              }
++
++              // Preserve script evaluation history
++              destElements = getAll( clone, "script" );
++              if ( destElements.length > 0 ) {
++                      setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
++              }
++
++              destElements = srcElements = node = null;
++
++              // Return the cloned set
++              return clone;
++      },
++
++      cleanData: function( elems, /* internal */ forceAcceptData ) {
++              var elem, type, id, data,
++                      i = 0,
++                      internalKey = jQuery.expando,
++                      cache = jQuery.cache,
++                      attributes = support.attributes,
++                      special = jQuery.event.special;
++
++              for ( ; ( elem = elems[ i ] ) != null; i++ ) {
++                      if ( forceAcceptData || acceptData( elem ) ) {
++
++                              id = elem[ internalKey ];
++                              data = id && cache[ id ];
++
++                              if ( data ) {
++                                      if ( data.events ) {
++                                              for ( type in data.events ) {
++                                                      if ( special[ type ] ) {
++                                                              jQuery.event.remove( elem, type );
++
++                                                      // This is a shortcut to avoid jQuery.event.remove's overhead
++                                                      } else {
++                                                              jQuery.removeEvent( elem, type, data.handle );
++                                                      }
++                                              }
++                                      }
++
++                                      // Remove cache only if it was not already removed by jQuery.event.remove
++                                      if ( cache[ id ] ) {
++
++                                              delete cache[ id ];
++
++                                              // Support: IE<9
++                                              // IE does not allow us to delete expando properties from nodes
++                                              // IE creates expando attributes along with the property
++                                              // IE does not have a removeAttribute function on Document nodes
++                                              if ( !attributes && typeof elem.removeAttribute !== "undefined" ) {
++                                                      elem.removeAttribute( internalKey );
++
++                                              // Webkit & Blink performance suffers when deleting properties
++                                              // from DOM nodes, so set to undefined instead
++                                              // https://code.google.com/p/chromium/issues/detail?id=378607
++                                              } else {
++                                                      elem[ internalKey ] = undefined;
++                                              }
++
++                                              deletedIds.push( id );
++                                      }
++                              }
++                      }
++              }
++      }
++} );
++
++jQuery.fn.extend( {
++
++      // Keep domManip exposed until 3.0 (gh-2225)
++      domManip: domManip,
++
++      detach: function( selector ) {
++              return remove( this, selector, true );
++      },
++
++      remove: function( selector ) {
++              return remove( this, selector );
++      },
++
++      text: function( value ) {
++              return 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 );
++      },
++
++      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 );
++                      }
++              } );
++      },
++
++      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 );
++                      }
++              } );
++      },
++
++      before: function() {
++              return domManip( this, arguments, function( elem ) {
++                      if ( this.parentNode ) {
++                              this.parentNode.insertBefore( elem, this );
++                      }
++              } );
++      },
++
++      after: function() {
++              return domManip( this, arguments, function( elem ) {
++                      if ( this.parentNode ) {
++                              this.parentNode.insertBefore( elem, this.nextSibling );
++                      }
++              } );
++      },
++
++      empty: function() {
++              var elem,
++                      i = 0;
++
++              for ( ; ( elem = this[ i ] ) != null; i++ ) {
++
++                      // Remove element nodes and prevent memory leaks
++                      if ( elem.nodeType === 1 ) {
++                              jQuery.cleanData( getAll( elem, false ) );
++                      }
++
++                      // Remove any remaining nodes
++                      while ( elem.firstChild ) {
++                              elem.removeChild( elem.firstChild );
++                      }
++
++                      // 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;
++                      }
++              }
++
++              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 );
++              } );
++      },
++
++      html: function( value ) {
++              return access( this, function( value ) {
++                      var elem = this[ 0 ] || {},
++                              i = 0,
++                              l = this.length;
++
++                      if ( value === undefined ) {
++                              return elem.nodeType === 1 ?
++                                      elem.innerHTML.replace( rinlinejQuery, "" ) :
++                                      undefined;
++                      }
++
++                      // See if we can take a shortcut and just use innerHTML
++                      if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
++                              ( support.htmlSerialize || !rnoshimcache.test( value )  ) &&
++                              ( support.leadingWhitespace || !rleadingWhitespace.test( value ) ) &&
++                              !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) {
++
++                              value = jQuery.htmlPrefilter( value );
++
++                              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;
++                                              }
++                                      }
++
++                                      elem = 0;
++
++                              // If using innerHTML throws an exception, use the fallback method
++                              } catch ( e ) {}
++                      }
++
++                      if ( elem ) {
++                              this.empty().append( value );
++                      }
++              }, null, value, arguments.length );
++      },
++
++      replaceWith: function() {
++              var ignored = [];
++
++              // Make the changes, replacing each non-ignored context element with the new content
++              return domManip( this, arguments, function( elem ) {
++                      var parent = this.parentNode;
++
++                      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,
++                      i = 0,
++                      ret = [],
++                      insert = jQuery( selector ),
++                      last = insert.length - 1;
++
++              for ( ; i <= last; i++ ) {
++                      elems = i === last ? this : this.clone( true );
++                      jQuery( insert[ i ] )[ original ]( elems );
++
++                      // Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get()
++                      push.apply( ret, elems.get() );
++              }
++
++              return this.pushStack( ret );
++      };
++} );
++
++
++var iframe,
++      elemdisplay = {
++
++              // Support: Firefox
++              // We have to pre-define these values for FF (#10227)
++              HTML: "block",
++              BODY: "block"
++      };
++
++/**
++ * Retrieve the actual display of a element
++ * @param {String} name nodeName of the element
++ * @param {Object} doc Document object
++ */
++
++// Called only from within defaultDisplay
++function actualDisplay( name, doc ) {
++      var elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ),
++
++              display = jQuery.css( elem[ 0 ], "display" );
++
++      // We don't have any data stored on the element,
++      // so use "detach" method as fast way to get rid of the element
++      elem.detach();
++
++      return display;
++}
++
++/**
++ * Try to determine the default display value of an element
++ * @param {String} nodeName
++ */
++function defaultDisplay( nodeName ) {
++      var doc = document,
++              display = elemdisplay[ nodeName ];
++
++      if ( !display ) {
++              display = actualDisplay( nodeName, doc );
++
++              // If the simple way fails, read from inside an iframe
++              if ( display === "none" || !display ) {
++
++                      // Use the already-created iframe if possible
++                      iframe = ( iframe || jQuery( "<iframe frameborder='0' width='0' height='0'/>" ) )
++                              .appendTo( doc.documentElement );
++
++                      // Always write a new HTML skeleton so Webkit and Firefox don't choke on reuse
++                      doc = ( iframe[ 0 ].contentWindow || iframe[ 0 ].contentDocument ).document;
++
++                      // Support: IE
++                      doc.write();
++                      doc.close();
++
++                      display = actualDisplay( nodeName, doc );
++                      iframe.detach();
++              }
++
++              // Store the correct default display
++              elemdisplay[ nodeName ] = display;
++      }
++
++      return display;
++}
++var rmargin = ( /^margin/ );
++
++var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" );
++
++var swap = function( elem, options, callback, args ) {
++      var ret, name,
++              old = {};
++
++      // Remember the old values, and insert the new ones
++      for ( name in options ) {
++              old[ name ] = elem.style[ name ];
++              elem.style[ name ] = options[ name ];
++      }
++
++      ret = callback.apply( elem, args || [] );
++
++      // Revert the old values
++      for ( name in options ) {
++              elem.style[ name ] = old[ name ];
++      }
++
++      return ret;
++};
++
++
++var documentElement = document.documentElement;
++
++
++
++( function() {
++      var pixelPositionVal, pixelMarginRightVal, boxSizingReliableVal,
++              reliableHiddenOffsetsVal, reliableMarginRightVal, reliableMarginLeftVal,
++              container = document.createElement( "div" ),
++              div = document.createElement( "div" );
++
++      // Finish early in limited (non-browser) environments
++      if ( !div.style ) {
++              return;
++      }
++
++      div.style.cssText = "float:left;opacity:.5";
++
++      // Support: IE<9
++      // Make sure that element opacity exists (as opposed to filter)
++      support.opacity = div.style.opacity === "0.5";
++
++      // Verify style float existence
++      // (IE uses styleFloat instead of cssFloat)
++      support.cssFloat = !!div.style.cssFloat;
++
++      div.style.backgroundClip = "content-box";
++      div.cloneNode( true ).style.backgroundClip = "";
++      support.clearCloneStyle = div.style.backgroundClip === "content-box";
++
++      container = document.createElement( "div" );
++      container.style.cssText = "border:0;width:8px;height:0;top:0;left:-9999px;" +
++              "padding:0;margin-top:1px;position:absolute";
++      div.innerHTML = "";
++      container.appendChild( div );
++
++      // Support: Firefox<29, Android 2.3
++      // Vendor-prefix box-sizing
++      support.boxSizing = div.style.boxSizing === "" || div.style.MozBoxSizing === "" ||
++              div.style.WebkitBoxSizing === "";
++
++      jQuery.extend( support, {
++              reliableHiddenOffsets: function() {
++                      if ( pixelPositionVal == null ) {
++                              computeStyleTests();
++                      }
++                      return reliableHiddenOffsetsVal;
++              },
++
++              boxSizingReliable: function() {
++
++                      // We're checking for pixelPositionVal here instead of boxSizingReliableVal
++                      // since that compresses better and they're computed together anyway.
++                      if ( pixelPositionVal == null ) {
++                              computeStyleTests();
++                      }
++                      return boxSizingReliableVal;
++              },
++
++              pixelMarginRight: function() {
++
++                      // Support: Android 4.0-4.3
++                      if ( pixelPositionVal == null ) {
++                              computeStyleTests();
++                      }
++                      return pixelMarginRightVal;
++              },
++
++              pixelPosition: function() {
++                      if ( pixelPositionVal == null ) {
++                              computeStyleTests();
++                      }
++                      return pixelPositionVal;
++              },
++
++              reliableMarginRight: function() {
++
++                      // Support: Android 2.3
++                      if ( pixelPositionVal == null ) {
++                              computeStyleTests();
++                      }
++                      return reliableMarginRightVal;
++              },
++
++              reliableMarginLeft: function() {
++
++                      // Support: IE <=8 only, Android 4.0 - 4.3 only, Firefox <=3 - 37
++                      if ( pixelPositionVal == null ) {
++                              computeStyleTests();
++                      }
++                      return reliableMarginLeftVal;
++              }
++      } );
++
++      function computeStyleTests() {
++              var contents, divStyle,
++                      documentElement = document.documentElement;
++
++              // Setup
++              documentElement.appendChild( container );
++
++              div.style.cssText =
++
++                      // Support: Android 2.3
++                      // Vendor-prefix box-sizing
++                      "-webkit-box-sizing:border-box;box-sizing:border-box;" +
++                      "position:relative;display:block;" +
++                      "margin:auto;border:1px;padding:1px;" +
++                      "top:1%;width:50%";
++
++              // Support: IE<9
++              // Assume reasonable values in the absence of getComputedStyle
++              pixelPositionVal = boxSizingReliableVal = reliableMarginLeftVal = false;
++              pixelMarginRightVal = reliableMarginRightVal = true;
++
++              // Check for getComputedStyle so that this code is not run in IE<9.
++              if ( window.getComputedStyle ) {
++                      divStyle = window.getComputedStyle( div );
++                      pixelPositionVal = ( divStyle || {} ).top !== "1%";
++                      reliableMarginLeftVal = ( divStyle || {} ).marginLeft === "2px";
++                      boxSizingReliableVal = ( divStyle || { width: "4px" } ).width === "4px";
++
++                      // Support: Android 4.0 - 4.3 only
++                      // Some styles come back with percentage values, even though they shouldn't
++                      div.style.marginRight = "50%";
++                      pixelMarginRightVal = ( divStyle || { marginRight: "4px" } ).marginRight === "4px";
++
++                      // Support: Android 2.3 only
++                      // Div with explicit width and no margin-right incorrectly
++                      // gets computed margin-right based on width of container (#3333)
++                      // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
++                      contents = div.appendChild( document.createElement( "div" ) );
++
++                      // Reset CSS: box-sizing; display; margin; border; padding
++                      contents.style.cssText = div.style.cssText =
++
++                              // Support: Android 2.3
++                              // Vendor-prefix box-sizing
++                              "-webkit-box-sizing:content-box;-moz-box-sizing:content-box;" +
++                              "box-sizing:content-box;display:block;margin:0;border:0;padding:0";
++                      contents.style.marginRight = contents.style.width = "0";
++                      div.style.width = "1px";
++
++                      reliableMarginRightVal =
++                              !parseFloat( ( window.getComputedStyle( contents ) || {} ).marginRight );
++
++                      div.removeChild( contents );
++              }
++
++              // Support: IE6-8
++              // First check that getClientRects works as expected
++              // 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.style.display = "none";
++              reliableHiddenOffsetsVal = div.getClientRects().length === 0;
++              if ( reliableHiddenOffsetsVal ) {
++                      div.style.display = "";
++                      div.innerHTML = "<table><tr><td></td><td>t</td></tr></table>";
++                      div.childNodes[ 0 ].style.borderCollapse = "separate";
++                      contents = div.getElementsByTagName( "td" );
++                      contents[ 0 ].style.cssText = "margin:0;border:0;padding:0;display:none";
++                      reliableHiddenOffsetsVal = contents[ 0 ].offsetHeight === 0;
++                      if ( reliableHiddenOffsetsVal ) {
++                              contents[ 0 ].style.display = "";
++                              contents[ 1 ].style.display = "none";
++                              reliableHiddenOffsetsVal = contents[ 0 ].offsetHeight === 0;
++                      }
++              }
++
++              // Teardown
++              documentElement.removeChild( container );
++      }
++
++} )();
++
++
++var getStyles, curCSS,
++      rposition = /^(top|right|bottom|left)$/;
++
++if ( window.getComputedStyle ) {
++      getStyles = function( elem ) {
++
++              // Support: IE<=11+, 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;
++              }
++
++              return view.getComputedStyle( elem );
++      };
++
++      curCSS = function( elem, name, computed ) {
++              var width, minWidth, maxWidth, ret,
++                      style = elem.style;
++
++              computed = computed || getStyles( elem );
++
++              // getPropertyValue is only needed for .css('filter') in IE9, see #12537
++              ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined;
++
++              // Support: Opera 12.1x only
++              // Fall back to style even without computed
++              // computed is undefined for elems on document fragments
++              if ( ( ret === "" || ret === undefined ) && !jQuery.contains( elem.ownerDocument, elem ) ) {
++                      ret = jQuery.style( elem, name );
++              }
++
++              if ( computed ) {
++
++                      // 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 ( !support.pixelMarginRight() && rnumnonpx.test( ret ) && rmargin.test( name ) ) {
++
++                              // Remember the original values
++                              width = style.width;
++                              minWidth = style.minWidth;
++                              maxWidth = style.maxWidth;
++
++                              // Put in the new values to get a computed value out
++                              style.minWidth = style.maxWidth = style.width = ret;
++                              ret = computed.width;
++
++                              // Revert the changed values
++                              style.width = width;
++                              style.minWidth = minWidth;
++                              style.maxWidth = maxWidth;
++                      }
++              }
++
++              // Support: IE
++              // IE returns zIndex value as an integer.
++              return ret === undefined ?
++                      ret :
++                      ret + "";
++      };
++} else if ( documentElement.currentStyle ) {
++      getStyles = function( elem ) {
++              return elem.currentStyle;
++      };
++
++      curCSS = function( elem, name, computed ) {
++              var left, rs, rsLeft, ret,
++                      style = elem.style;
++
++              computed = computed || getStyles( elem );
++              ret = computed ? computed[ name ] : undefined;
++
++              // Avoid setting ret to empty string here
++              // so we don't default to auto
++              if ( ret == null && style && style[ name ] ) {
++                      ret = style[ name ];
++              }
++
++              // From the awesome hack by Dean Edwards
++              // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
++
++              // 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";
++
++                      // Revert the changed values
++                      style.left = left;
++                      if ( rsLeft ) {
++                              rs.left = rsLeft;
++                      }
++              }
++
++              // Support: IE
++              // IE returns zIndex value as an integer.
++              return ret === undefined ?
++                      ret :
++                      ret + "" || "auto";
++      };
++}
++
++
++
++
++function addGetHookIf( conditionFn, hookFn ) {
++
++      // Define the hook, we'll check on the first run if it's really needed.
++      return {
++              get: function() {
++                      if ( conditionFn() ) {
++
++                              // Hook not needed (or it's not possible to use it due
++                              // to missing dependency), remove it.
++                              delete this.get;
++                              return;
++                      }
++
++                      // Hook needed; redefine it so that the support test is not executed again.
++                      return ( this.get = hookFn ).apply( this, arguments );
++              }
++      };
++}
++
++
++var
++
++              ralpha = /alpha\([^)]*\)/i,
++      ropacity = /opacity\s*=\s*([^)]*)/i,
++
++      // 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]).+)/,
++      rnumsplit = new RegExp( "^(" + pnum + ")(.*)$", "i" ),
++
++      cssShow = { position: "absolute", visibility: "hidden", display: "block" },
++      cssNormalTransform = {
++              letterSpacing: "0",
++              fontWeight: "400"
++      },
++
++      cssPrefixes = [ "Webkit", "O", "Moz", "ms" ],
++      emptyStyle = document.createElement( "div" ).style;
++
++
++// return a css property mapped to a potentially vendor prefixed property
++function vendorPropName( name ) {
++
++      // shortcut for names that are not vendor prefixed
++      if ( name in emptyStyle ) {
++              return name;
++      }
++
++      // check for vendor prefixed names
++      var capName = name.charAt( 0 ).toUpperCase() + name.slice( 1 ),
++              i = cssPrefixes.length;
++
++      while ( i-- ) {
++              name = cssPrefixes[ i ] + capName;
++              if ( name in emptyStyle ) {
++                      return name;
++              }
++      }
++}
++
++function showHide( elements, show ) {
++      var display, elem, hidden,
++              values = [],
++              index = 0,
++              length = elements.length;
++
++      for ( ; index < length; index++ ) {
++              elem = elements[ index ];
++              if ( !elem.style ) {
++                      continue;
++              }
++
++              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 = "";
++                      }
++
++                      // 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", defaultDisplay( elem.nodeName ) );
++                      }
++              } else {
++                      hidden = isHidden( elem );
++
++                      if ( display && display !== "none" || !hidden ) {
++                              jQuery._data(
++                                      elem,
++                                      "olddisplay",
++                                      hidden ? display : jQuery.css( elem, "display" )
++                              );
++                      }
++              }
++      }
++
++      // 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;
++              }
++              if ( !show || elem.style.display === "none" || elem.style.display === "" ) {
++                      elem.style.display = show ? values[ index ] || "" : "none";
++              }
++      }
++
++      return elements;
++}
++
++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;
++}
++
++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,
++
++              val = 0;
++
++      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 );
++              }
++
++              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 );
++                      }
++
++                      // 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 );
++
++                      // 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 );
++                      }
++              }
++      }
++
++      return val;
++}
++
++function getWidthOrHeight( elem, name, extra ) {
++
++      // 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 = support.boxSizing &&
++                      jQuery.css( elem, "boxSizing", false, styles ) === "border-box";
++
++      // 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 ];
++              }
++
++              // Computed unit is not pixels. Stop here and return.
++              if ( rnumnonpx.test( val ) ) {
++                      return val;
++              }
++
++              // 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 &&
++                      ( support.boxSizingReliable() || val === elem.style[ name ] );
++
++              // Normalize "", auto, and prepare for extra
++              val = parseFloat( val ) || 0;
++      }
++
++      // use the active box-sizing model to add/subtract irrelevant styles
++      return ( val +
++              augmentWidthOrHeight(
++                      elem,
++                      name,
++                      extra || ( isBorderBox ? "border" : "content" ),
++                      valueIsBorderBox,
++                      styles
++              )
++      ) + "px";
++}
++
++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;
++                              }
++                      }
++              }
++      },
++
++      // Don't automatically add "px" to these possibly-unitless properties
++      cssNumber: {
++              "animationIterationCount": true,
++              "columnCount": true,
++              "fillOpacity": true,
++              "flexGrow": true,
++              "flexShrink": true,
++              "fontWeight": true,
++              "lineHeight": true,
++              "opacity": true,
++              "order": true,
++              "orphans": true,
++              "widows": true,
++              "zIndex": true,
++              "zoom": true
++      },
++
++      // Add in properties whose names you wish to fix before
++      // setting or getting the value
++      cssProps: {
++
++              // normalize float css property
++              "float": support.cssFloat ? "cssFloat" : "styleFloat"
++      },
++
++      // 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;
++
++              name = jQuery.cssProps[ origName ] ||
++                      ( jQuery.cssProps[ origName ] = vendorPropName( origName ) || origName );
++
++              // gets hook for the prefixed version
++              // followed by the unprefixed version
++              hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
++
++              // Check if we're setting a value
++              if ( value !== undefined ) {
++                      type = typeof value;
++
++                      // Convert "+=" or "-=" to relative numbers (#7345)
++                      if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) {
++                              value = adjustCSS( elem, name, ret );
++
++                              // Fixes bug #9237
++                              type = "number";
++                      }
++
++                      // Make sure that null and NaN values aren't set. See: #7116
++                      if ( value == null || value !== value ) {
++                              return;
++                      }
++
++                      // If a number was passed in, add the unit (except for certain CSS properties)
++                      if ( type === "number" ) {
++                              value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" );
++                      }
++
++                      // 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 ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) {
++                              style[ name ] = "inherit";
++                      }
++
++                      // 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 ) {
++
++                              // Support: IE
++                              // Swallow errors from 'invalid' CSS values (#5509)
++                              try {
++                                      style[ name ] = value;
++                              } catch ( e ) {}
++                      }
++
++              } 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;
++                      }
++
++                      // Otherwise just get the value from the style object
++                      return style[ name ];
++              }
++      },
++
++      css: function( elem, name, extra, styles ) {
++              var num, val, hooks,
++                      origName = jQuery.camelCase( name );
++
++              // Make sure that we're working with the right name
++              name = jQuery.cssProps[ origName ] ||
++                      ( jQuery.cssProps[ origName ] = vendorPropName( origName ) || origName );
++
++              // gets hook for the prefixed version
++              // followed by the unprefixed version
++              hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
++
++              // If a hook was provided get the computed value from there
++              if ( hooks && "get" in hooks ) {
++                      val = hooks.get( elem, true, extra );
++              }
++
++              // Otherwise, if a way to get the computed value exists, use that
++              if ( val === undefined ) {
++                      val = curCSS( elem, name, styles );
++              }
++
++              //convert "normal" to computed value
++              if ( val === "normal" && name in cssNormalTransform ) {
++                      val = cssNormalTransform[ name ];
++              }
++
++              // Return, converting to number if forced or a qualifier was provided and val looks numeric
++              if ( extra === "" || extra ) {
++                      num = parseFloat( val );
++                      return extra === true || isFinite( num ) ? num || 0 : val;
++              }
++              return val;
++      }
++} );
++
++jQuery.each( [ "height", "width" ], function( i, name ) {
++      jQuery.cssHooks[ name ] = {
++              get: function( elem, computed, extra ) {
++                      if ( computed ) {
++
++                              // certain elements can have dimension info if we invisibly show them
++                              // however, it must have a current display style that would benefit from this
++                              return rdisplayswap.test( jQuery.css( elem, "display" ) ) &&
++                                      elem.offsetWidth === 0 ?
++                                              swap( elem, cssShow, function() {
++                                                      return getWidthOrHeight( elem, name, extra );
++                                              } ) :
++                                              getWidthOrHeight( elem, name, extra );
++                      }
++              },
++
++              set: function( elem, value, extra ) {
++                      var styles = extra && getStyles( elem );
++                      return setPositiveNumber( elem, value, extra ?
++                              augmentWidthOrHeight(
++                                      elem,
++                                      name,
++                                      extra,
++                                      support.boxSizing &&
++                                              jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
++                                      styles
++                              ) : 0
++                      );
++              }
++      };
++} );
++
++if ( !support.opacity ) {
++      jQuery.cssHooks.opacity = {
++              get: function( elem, computed ) {
++
++                      // IE uses filters for opacity
++                      return ropacity.test( ( computed && elem.currentStyle ?
++                              elem.currentStyle.filter :
++                              elem.style.filter ) || "" ) ?
++                                      ( 0.01 * parseFloat( RegExp.$1 ) ) + "" :
++                                      computed ? "1" : "";
++              },
++
++              set: function( elem, value ) {
++                      var style = elem.style,
++                              currentStyle = elem.currentStyle,
++                              opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "",
++                              filter = currentStyle && currentStyle.filter || style.filter || "";
++
++                      // IE has trouble with opacity if it does not have layout
++                      // Force it by setting the zoom level
++                      style.zoom = 1;
++
++                      // if setting opacity to 1, and no other filters exist -
++                      // attempt to remove filter attribute #6652
++                      // if value === "", then remove inline opacity #12685
++                      if ( ( value >= 1 || value === "" ) &&
++                                      jQuery.trim( filter.replace( ralpha, "" ) ) === "" &&
++                                      style.removeAttribute ) {
++
++                              // Setting style.filter to null, "" & " " still leave "filter:" in the cssText
++                              // if "filter:" is present at all, clearType is disabled, we want to avoid this
++                              // style.removeAttribute is IE Only, but so apparently is this code path...
++                              style.removeAttribute( "filter" );
++
++                              // if there is no filter style applied in a css rule
++                              // or unset inline opacity, we are done
++                              if ( value === "" || currentStyle && !currentStyle.filter ) {
++                                      return;
++                              }
++                      }
++
++                      // otherwise, set new filter values
++                      style.filter = ralpha.test( filter ) ?
++                              filter.replace( ralpha, opacity ) :
++                              filter + " " + opacity;
++              }
++      };
++}
++
++jQuery.cssHooks.marginRight = addGetHookIf( support.reliableMarginRight,
++      function( elem, computed ) {
++              if ( computed ) {
++                      return swap( elem, { "display": "inline-block" },
++                              curCSS, [ elem, "marginRight" ] );
++              }
++      }
++);
++
++jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft,
++      function( elem, computed ) {
++              if ( computed ) {
++                      return (
++                              parseFloat( curCSS( elem, "marginLeft" ) ) ||
++
++                              // Support: IE<=11+
++                              // Running getBoundingClientRect on a disconnected node in IE throws an error
++                              // Support: IE8 only
++                              // getClientRects() errors on disconnected elems
++                              ( jQuery.contains( elem.ownerDocument, elem ) ?
++                                      elem.getBoundingClientRect().left -
++                                              swap( elem, { marginLeft: 0 }, function() {
++                                                      return elem.getBoundingClientRect().left;
++                                              } ) :
++                                      0
++                              )
++                      ) + "px";
++              }
++      }
++);
++
++// These hooks are used by animate to expand properties
++jQuery.each( {
++      margin: "",
++      padding: "",
++      border: "Width"
++}, function( prefix, suffix ) {
++      jQuery.cssHooks[ prefix + suffix ] = {
++              expand: function( value ) {
++                      var i = 0,
++                              expanded = {},
++
++                              // assumes a single number if not a string
++                              parts = typeof value === "string" ? value.split( " " ) : [ value ];
++
++                      for ( ; i < 4; i++ ) {
++                              expanded[ prefix + cssExpand[ i ] + suffix ] =
++                                      parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
++                      }
++
++                      return expanded;
++              }
++      };
++
++      if ( !rmargin.test( prefix ) ) {
++              jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
++      }
++} );
++
++jQuery.fn.extend( {
++      css: function( name, value ) {
++              return access( this, function( elem, name, value ) {
++                      var styles, len,
++                              map = {},
++                              i = 0;
++
++                      if ( jQuery.isArray( name ) ) {
++                              styles = getStyles( elem );
++                              len = name.length;
++
++                              for ( ; i < len; i++ ) {
++                                      map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
++                              }
++
++                              return map;
++                      }
++
++                      return value !== undefined ?
++                              jQuery.style( elem, name, value ) :
++                              jQuery.css( elem, name );
++              }, name, value, arguments.length > 1 );
++      },
++      show: function() {
++              return showHide( this, true );
++      },
++      hide: function() {
++              return showHide( this );
++      },
++      toggle: function( state ) {
++              if ( typeof state === "boolean" ) {
++                      return state ? this.show() : this.hide();
++              }
++
++              return this.each( function() {
++                      if ( isHidden( this ) ) {
++                              jQuery( this ).show();
++                      } else {
++                              jQuery( this ).hide();
++                      }
++              } );
++      }
++} );
++
++
++function Tween( elem, options, prop, end, easing ) {
++      return new Tween.prototype.init( elem, options, prop, end, easing );
++}
++jQuery.Tween = Tween;
++
++Tween.prototype = {
++      constructor: Tween,
++      init: function( elem, options, prop, end, easing, unit ) {
++              this.elem = elem;
++              this.prop = prop;
++              this.easing = easing || jQuery.easing._default;
++              this.options = options;
++              this.start = this.now = this.cur();
++              this.end = end;
++              this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
++      },
++      cur: function() {
++              var hooks = Tween.propHooks[ this.prop ];
++
++              return hooks && hooks.get ?
++                      hooks.get( this ) :
++                      Tween.propHooks._default.get( this );
++      },
++      run: function( percent ) {
++              var eased,
++                      hooks = Tween.propHooks[ this.prop ];
++
++              if ( this.options.duration ) {
++                      this.pos = eased = jQuery.easing[ this.easing ](
++                              percent, this.options.duration * percent, 0, 1, this.options.duration
++                      );
++              } else {
++                      this.pos = eased = percent;
++              }
++              this.now = ( this.end - this.start ) * eased + this.start;
++
++              if ( this.options.step ) {
++                      this.options.step.call( this.elem, this.now, this );
++              }
++
++              if ( hooks && hooks.set ) {
++                      hooks.set( this );
++              } else {
++                      Tween.propHooks._default.set( this );
++              }
++              return this;
++      }
++};
++
++Tween.prototype.init.prototype = Tween.prototype;
++
++Tween.propHooks = {
++      _default: {
++              get: function( tween ) {
++                      var result;
++
++                      // Use a property on the element directly when it is not a DOM element,
++                      // or when there is no matching style property that exists.
++                      if ( tween.elem.nodeType !== 1 ||
++                              tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) {
++                              return tween.elem[ tween.prop ];
++                      }
++
++                      // passing an empty string as a 3rd parameter to .css will automatically
++                      // attempt a parseFloat and fallback to a string if the parse fails
++                      // so, simple values such as "10px" are parsed to Float.
++                      // complex values such as "rotate(1rad)" are returned as is.
++                      result = jQuery.css( tween.elem, tween.prop, "" );
++
++                      // Empty strings, null, undefined and "auto" are converted to 0.
++                      return !result || result === "auto" ? 0 : result;
++              },
++              set: function( tween ) {
++
++                      // use step hook for back compat - use cssHook if its there - use .style if its
++                      // available and use plain properties where available
++                      if ( jQuery.fx.step[ tween.prop ] ) {
++                              jQuery.fx.step[ tween.prop ]( tween );
++                      } else if ( tween.elem.nodeType === 1 &&
++                              ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null ||
++                                      jQuery.cssHooks[ tween.prop ] ) ) {
++                              jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
++                      } else {
++                              tween.elem[ tween.prop ] = tween.now;
++                      }
++              }
++      }
++};
++
++// Support: IE <=9
++// Panic based approach to setting things on disconnected nodes
++
++Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
++      set: function( tween ) {
++              if ( tween.elem.nodeType && tween.elem.parentNode ) {
++                      tween.elem[ tween.prop ] = tween.now;
++              }
++      }
++};
++
++jQuery.easing = {
++      linear: function( p ) {
++              return p;
++      },
++      swing: function( p ) {
++              return 0.5 - Math.cos( p * Math.PI ) / 2;
++      },
++      _default: "swing"
++};
++
++jQuery.fx = Tween.prototype.init;
++
++// Back Compat <1.8 extension point
++jQuery.fx.step = {};
++
++
++
++
++var
++      fxNow, timerId,
++      rfxtypes = /^(?:toggle|show|hide)$/,
++      rrun = /queueHooks$/;
++
++// Animations created synchronously will run synchronously
++function createFxNow() {
++      window.setTimeout( function() {
++              fxNow = undefined;
++      } );
++      return ( fxNow = jQuery.now() );
++}
++
++// Generate parameters to create a standard animation
++function genFx( type, includeWidth ) {
++      var which,
++              attrs = { height: type },
++              i = 0;
++
++      // if we include width, step value is 1 to do all cssExpand values,
++      // if we don't include width, step value is 2 to skip over Left and Right
++      includeWidth = includeWidth ? 1 : 0;
++      for ( ; i < 4 ; i += 2 - includeWidth ) {
++              which = cssExpand[ i ];
++              attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
++      }
++
++      if ( includeWidth ) {
++              attrs.opacity = attrs.width = type;
++      }
++
++      return attrs;
++}
++
++function createTween( value, prop, animation ) {
++      var tween,
++              collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ),
++              index = 0,
++              length = collection.length;
++      for ( ; index < length; index++ ) {
++              if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) {
++
++                      // we're done with this property
++                      return tween;
++              }
++      }
++}
++
++function defaultPrefilter( elem, props, opts ) {
++      /* jshint validthis: true */
++      var prop, value, toggle, tween, hooks, oldfire, display, checkDisplay,
++              anim = this,
++              orig = {},
++              style = elem.style,
++              hidden = elem.nodeType && isHidden( elem ),
++              dataShow = jQuery._data( elem, "fxshow" );
++
++      // handle queue: false promises
++      if ( !opts.queue ) {
++              hooks = jQuery._queueHooks( elem, "fx" );
++              if ( hooks.unqueued == null ) {
++                      hooks.unqueued = 0;
++                      oldfire = hooks.empty.fire;
++                      hooks.empty.fire = function() {
++                              if ( !hooks.unqueued ) {
++                                      oldfire();
++                              }
++                      };
++              }
++              hooks.unqueued++;
++
++              anim.always( function() {
++
++                      // doing this makes sure that the complete handler will be called
++                      // before this completes
++                      anim.always( function() {
++                              hooks.unqueued--;
++                              if ( !jQuery.queue( elem, "fx" ).length ) {
++                                      hooks.empty.fire();
++                              }
++                      } );
++              } );
++      }
++
++      // height/width overflow pass
++      if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {
++
++              // Make sure that nothing sneaks out
++              // Record all 3 overflow attributes because IE does not
++              // change the overflow attribute when overflowX and
++              // overflowY are set to the same value
++              opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
++
++              // Set display property to inline-block for height/width
++              // animations on inline elements that are having width/height animated
++              display = jQuery.css( elem, "display" );
++
++              // Test default display if display is currently "none"
++              checkDisplay = display === "none" ?
++                      jQuery._data( elem, "olddisplay" ) || defaultDisplay( elem.nodeName ) : display;
++
++              if ( checkDisplay === "inline" && jQuery.css( elem, "float" ) === "none" ) {
++
++                      // inline-level elements accept inline-block;
++                      // block-level elements need to be inline with layout
++                      if ( !support.inlineBlockNeedsLayout || defaultDisplay( elem.nodeName ) === "inline" ) {
++                              style.display = "inline-block";
++                      } else {
++                              style.zoom = 1;
++                      }
++              }
++      }
++
++      if ( opts.overflow ) {
++              style.overflow = "hidden";
++              if ( !support.shrinkWrapBlocks() ) {
++                      anim.always( function() {
++                              style.overflow = opts.overflow[ 0 ];
++                              style.overflowX = opts.overflow[ 1 ];
++                              style.overflowY = opts.overflow[ 2 ];
++                      } );
++              }
++      }
++
++      // show/hide pass
++      for ( prop in props ) {
++              value = props[ prop ];
++              if ( rfxtypes.exec( value ) ) {
++                      delete props[ prop ];
++                      toggle = toggle || value === "toggle";
++                      if ( value === ( hidden ? "hide" : "show" ) ) {
++
++                              // If there is dataShow left over from a stopped hide or show
++                              // and we are going to proceed with show, we should pretend to be hidden
++                              if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) {
++                                      hidden = true;
++                              } else {
++                                      continue;
++                              }
++                      }
++                      orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );
++
++              // Any non-fx value stops us from restoring the original display value
++              } else {
++                      display = undefined;
++              }
++      }
++
++      if ( !jQuery.isEmptyObject( orig ) ) {
++              if ( dataShow ) {
++                      if ( "hidden" in dataShow ) {
++                              hidden = dataShow.hidden;
++                      }
++              } else {
++                      dataShow = jQuery._data( elem, "fxshow", {} );
++              }
++
++              // store state if its toggle - enables .stop().toggle() to "reverse"
++              if ( toggle ) {
++                      dataShow.hidden = !hidden;
++              }
++              if ( hidden ) {
++                      jQuery( elem ).show();
++              } else {
++                      anim.done( function() {
++                              jQuery( elem ).hide();
++                      } );
++              }
++              anim.done( function() {
++                      var prop;
++                      jQuery._removeData( elem, "fxshow" );
++                      for ( prop in orig ) {
++                              jQuery.style( elem, prop, orig[ prop ] );
++                      }
++              } );
++              for ( prop in orig ) {
++                      tween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );
++
++                      if ( !( prop in dataShow ) ) {
++                              dataShow[ prop ] = tween.start;
++                              if ( hidden ) {
++                                      tween.end = tween.start;
++                                      tween.start = prop === "width" || prop === "height" ? 1 : 0;
++                              }
++                      }
++              }
++
++      // If this is a noop like .hide().hide(), restore an overwritten display value
++      } else if ( ( display === "none" ? defaultDisplay( elem.nodeName ) : display ) === "inline" ) {
++              style.display = display;
++      }
++}
++
++function propFilter( props, specialEasing ) {
++      var index, name, easing, value, hooks;
++
++      // camelCase, specialEasing and expand cssHook pass
++      for ( index in props ) {
++              name = jQuery.camelCase( index );
++              easing = specialEasing[ name ];
++              value = props[ index ];
++              if ( jQuery.isArray( value ) ) {
++                      easing = value[ 1 ];
++                      value = props[ index ] = value[ 0 ];
++              }
++
++              if ( index !== name ) {
++                      props[ name ] = value;
++                      delete props[ index ];
++              }
++
++              hooks = jQuery.cssHooks[ name ];
++              if ( hooks && "expand" in hooks ) {
++                      value = hooks.expand( value );
++                      delete props[ name ];
++
++                      // not quite $.extend, this wont overwrite keys already present.
++                      // also - reusing 'index' from above because we have the correct "name"
++                      for ( index in value ) {
++                              if ( !( index in props ) ) {
++                                      props[ index ] = value[ index ];
++                                      specialEasing[ index ] = easing;
++                              }
++                      }
++              } else {
++                      specialEasing[ name ] = easing;
++              }
++      }
++}
++
++function Animation( elem, properties, options ) {
++      var result,
++              stopped,
++              index = 0,
++              length = Animation.prefilters.length,
++              deferred = jQuery.Deferred().always( function() {
++
++                      // don't match elem in the :animated selector
++                      delete tick.elem;
++              } ),
++              tick = function() {
++                      if ( stopped ) {
++                              return false;
++                      }
++                      var currentTime = fxNow || createFxNow(),
++                              remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
++
++                              // Support: Android 2.3
++                              // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497)
++                              temp = remaining / animation.duration || 0,
++                              percent = 1 - temp,
++                              index = 0,
++                              length = animation.tweens.length;
++
++                      for ( ; index < length ; index++ ) {
++                              animation.tweens[ index ].run( percent );
++                      }
++
++                      deferred.notifyWith( elem, [ animation, percent, remaining ] );
++
++                      if ( percent < 1 && length ) {
++                              return remaining;
++                      } else {
++                              deferred.resolveWith( elem, [ animation ] );
++                              return false;
++                      }
++              },
++              animation = deferred.promise( {
++                      elem: elem,
++                      props: jQuery.extend( {}, properties ),
++                      opts: jQuery.extend( true, {
++                              specialEasing: {},
++                              easing: jQuery.easing._default
++                      }, options ),
++                      originalProperties: properties,
++                      originalOptions: options,
++                      startTime: fxNow || createFxNow(),
++                      duration: options.duration,
++                      tweens: [],
++                      createTween: function( prop, end ) {
++                              var tween = jQuery.Tween( elem, animation.opts, prop, end,
++                                              animation.opts.specialEasing[ prop ] || animation.opts.easing );
++                              animation.tweens.push( tween );
++                              return tween;
++                      },
++                      stop: function( gotoEnd ) {
++                              var index = 0,
++
++                                      // if we are going to the end, we want to run all the tweens
++                                      // otherwise we skip this part
++                                      length = gotoEnd ? animation.tweens.length : 0;
++                              if ( stopped ) {
++                                      return this;
++                              }
++                              stopped = true;
++                              for ( ; index < length ; index++ ) {
++                                      animation.tweens[ index ].run( 1 );
++                              }
++
++                              // resolve when we played the last frame
++                              // otherwise, reject
++                              if ( gotoEnd ) {
++                                      deferred.notifyWith( elem, [ animation, 1, 0 ] );
++                                      deferred.resolveWith( elem, [ animation, gotoEnd ] );
++                              } else {
++                                      deferred.rejectWith( elem, [ animation, gotoEnd ] );
++                              }
++                              return this;
++                      }
++              } ),
++              props = animation.props;
++
++      propFilter( props, animation.opts.specialEasing );
++
++      for ( ; index < length ; index++ ) {
++              result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts );
++              if ( result ) {
++                      if ( jQuery.isFunction( result.stop ) ) {
++                              jQuery._queueHooks( animation.elem, animation.opts.queue ).stop =
++                                      jQuery.proxy( result.stop, result );
++                      }
++                      return result;
++              }
++      }
++
++      jQuery.map( props, createTween, animation );
++
++      if ( jQuery.isFunction( animation.opts.start ) ) {
++              animation.opts.start.call( elem, animation );
++      }
++
++      jQuery.fx.timer(
++              jQuery.extend( tick, {
++                      elem: elem,
++                      anim: animation,
++                      queue: animation.opts.queue
++              } )
++      );
++
++      // attach callbacks from options
++      return animation.progress( animation.opts.progress )
++              .done( animation.opts.done, animation.opts.complete )
++              .fail( animation.opts.fail )
++              .always( animation.opts.always );
++}
++
++jQuery.Animation = jQuery.extend( Animation, {
++
++      tweeners: {
++              "*": [ function( prop, value ) {
++                      var tween = this.createTween( prop, value );
++                      adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween );
++                      return tween;
++              } ]
++      },
++
++      tweener: function( props, callback ) {
++              if ( jQuery.isFunction( props ) ) {
++                      callback = props;
++                      props = [ "*" ];
++              } else {
++                      props = props.match( rnotwhite );
++              }
++
++              var prop,
++                      index = 0,
++                      length = props.length;
++
++              for ( ; index < length ; index++ ) {
++                      prop = props[ index ];
++                      Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || [];
++                      Animation.tweeners[ prop ].unshift( callback );
++              }
++      },
++
++      prefilters: [ defaultPrefilter ],
++
++      prefilter: function( callback, prepend ) {
++              if ( prepend ) {
++                      Animation.prefilters.unshift( callback );
++              } else {
++                      Animation.prefilters.push( callback );
++              }
++      }
++} );
++
++jQuery.speed = function( speed, easing, fn ) {
++      var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
++              complete: fn || !fn && easing ||
++                      jQuery.isFunction( speed ) && speed,
++              duration: speed,
++              easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
++      };
++
++      opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
++              opt.duration in jQuery.fx.speeds ?
++                      jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
++
++      // normalize opt.queue - true/undefined/null -> "fx"
++      if ( opt.queue == null || opt.queue === true ) {
++              opt.queue = "fx";
++      }
++
++      // Queueing
++      opt.old = opt.complete;
++
++      opt.complete = function() {
++              if ( jQuery.isFunction( opt.old ) ) {
++                      opt.old.call( this );
++              }
++
++              if ( opt.queue ) {
++                      jQuery.dequeue( this, opt.queue );
++              }
++      };
++
++      return opt;
++};
++
++jQuery.fn.extend( {
++      fadeTo: function( speed, to, easing, callback ) {
++
++              // show any hidden elements after setting opacity to 0
++              return this.filter( isHidden ).css( "opacity", 0 ).show()
++
++                      // animate to the value specified
++                      .end().animate( { opacity: to }, speed, easing, callback );
++      },
++      animate: function( prop, speed, easing, callback ) {
++              var empty = jQuery.isEmptyObject( prop ),
++                      optall = jQuery.speed( speed, easing, callback ),
++                      doAnimation = function() {
++
++                              // Operate on a copy of prop so per-property easing won't be lost
++                              var anim = Animation( this, jQuery.extend( {}, prop ), optall );
++
++                              // Empty animations, or finishing resolves immediately
++                              if ( empty || jQuery._data( this, "finish" ) ) {
++                                      anim.stop( true );
++                              }
++                      };
++                      doAnimation.finish = doAnimation;
++
++              return empty || optall.queue === false ?
++                      this.each( doAnimation ) :
++                      this.queue( optall.queue, doAnimation );
++      },
++      stop: function( type, clearQueue, gotoEnd ) {
++              var stopQueue = function( hooks ) {
++                      var stop = hooks.stop;
++                      delete hooks.stop;
++                      stop( gotoEnd );
++              };
++
++              if ( typeof type !== "string" ) {
++                      gotoEnd = clearQueue;
++                      clearQueue = type;
++                      type = undefined;
++              }
++              if ( clearQueue && type !== false ) {
++                      this.queue( type || "fx", [] );
++              }
++
++              return this.each( function() {
++                      var dequeue = true,
++                              index = type != null && type + "queueHooks",
++                              timers = jQuery.timers,
++                              data = jQuery._data( this );
++
++                      if ( index ) {
++                              if ( data[ index ] && data[ index ].stop ) {
++                                      stopQueue( data[ index ] );
++                              }
++                      } else {
++                              for ( index in data ) {
++                                      if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
++                                              stopQueue( data[ index ] );
++                                      }
++                              }
++                      }
++
++                      for ( index = timers.length; index--; ) {
++                              if ( timers[ index ].elem === this &&
++                                      ( type == null || timers[ index ].queue === type ) ) {
++
++                                      timers[ index ].anim.stop( gotoEnd );
++                                      dequeue = false;
++                                      timers.splice( index, 1 );
++                              }
++                      }
++
++                      // start the next in the queue if the last step wasn't forced
++                      // timers currently will call their complete callbacks, which will dequeue
++                      // but only if they were gotoEnd
++                      if ( dequeue || !gotoEnd ) {
++                              jQuery.dequeue( this, type );
++                      }
++              } );
++      },
++      finish: function( type ) {
++              if ( type !== false ) {
++                      type = type || "fx";
++              }
++              return this.each( function() {
++                      var index,
++                              data = jQuery._data( this ),
++                              queue = data[ type + "queue" ],
++                              hooks = data[ type + "queueHooks" ],
++                              timers = jQuery.timers,
++                              length = queue ? queue.length : 0;
++
++                      // enable finishing flag on private data
++                      data.finish = true;
++
++                      // empty the queue first
++                      jQuery.queue( this, type, [] );
++
++                      if ( hooks && hooks.stop ) {
++                              hooks.stop.call( this, true );
++                      }
++
++                      // look for any active animations, and finish them
++                      for ( index = timers.length; index--; ) {
++                              if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
++                                      timers[ index ].anim.stop( true );
++                                      timers.splice( index, 1 );
++                              }
++                      }
++
++                      // look for any animations in the old queue and finish them
++                      for ( index = 0; index < length; index++ ) {
++                              if ( queue[ index ] && queue[ index ].finish ) {
++                                      queue[ index ].finish.call( this );
++                              }
++                      }
++
++                      // turn off finishing flag
++                      delete data.finish;
++              } );
++      }
++} );
++
++jQuery.each( [ "toggle", "show", "hide" ], function( i, name ) {
++      var cssFn = jQuery.fn[ name ];
++      jQuery.fn[ name ] = function( speed, easing, callback ) {
++              return speed == null || typeof speed === "boolean" ?
++                      cssFn.apply( this, arguments ) :
++                      this.animate( genFx( name, true ), speed, easing, callback );
++      };
++} );
++
++// Generate shortcuts for custom animations
++jQuery.each( {
++      slideDown: genFx( "show" ),
++      slideUp: genFx( "hide" ),
++      slideToggle: genFx( "toggle" ),
++      fadeIn: { opacity: "show" },
++      fadeOut: { opacity: "hide" },
++      fadeToggle: { opacity: "toggle" }
++}, function( name, props ) {
++      jQuery.fn[ name ] = function( speed, easing, callback ) {
++              return this.animate( props, speed, easing, callback );
++      };
++} );
++
++jQuery.timers = [];
++jQuery.fx.tick = function() {
++      var timer,
++              timers = jQuery.timers,
++              i = 0;
++
++      fxNow = jQuery.now();
++
++      for ( ; i < timers.length; i++ ) {
++              timer = timers[ i ];
++
++              // Checks the timer has not already been removed
++              if ( !timer() && timers[ i ] === timer ) {
++                      timers.splice( i--, 1 );
++              }
++      }
++
++      if ( !timers.length ) {
++              jQuery.fx.stop();
++      }
++      fxNow = undefined;
++};
++
++jQuery.fx.timer = function( timer ) {
++      jQuery.timers.push( timer );
++      if ( timer() ) {
++              jQuery.fx.start();
++      } else {
++              jQuery.timers.pop();
++      }
++};
++
++jQuery.fx.interval = 13;
++
++jQuery.fx.start = function() {
++      if ( !timerId ) {
++              timerId = window.setInterval( jQuery.fx.tick, jQuery.fx.interval );
++      }
++};
++
++jQuery.fx.stop = function() {
++      window.clearInterval( timerId );
++      timerId = null;
++};
++
++jQuery.fx.speeds = {
++      slow: 600,
++      fast: 200,
++
++      // Default speed
++      _default: 400
++};
++
++
++// Based off of the plugin by Clint Helfers, with permission.
++// http://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/
++jQuery.fn.delay = function( time, type ) {
++      time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
++      type = type || "fx";
++
++      return this.queue( type, function( next, hooks ) {
++              var timeout = window.setTimeout( next, time );
++              hooks.stop = function() {
++                      window.clearTimeout( timeout );
++              };
++      } );
++};
++
++
++( function() {
++      var a,
++              input = document.createElement( "input" ),
++              div = document.createElement( "div" ),
++              select = document.createElement( "select" ),
++              opt = select.appendChild( document.createElement( "option" ) );
++
++      // Setup
++      div = document.createElement( "div" );
++      div.setAttribute( "className", "t" );
++      div.innerHTML = "  <link/><table></table><a href='/a'>a</a><input type='checkbox'/>";
++      a = div.getElementsByTagName( "a" )[ 0 ];
++
++      // Support: Windows Web Apps (WWA)
++      // `type` must use .setAttribute for WWA (#14901)
++      input.setAttribute( "type", "checkbox" );
++      div.appendChild( input );
++
++      a = div.getElementsByTagName( "a" )[ 0 ];
++
++      // First batch of tests.
++      a.style.cssText = "top:1px";
++
++      // Test setAttribute on camelCase class.
++      // If it works, we need attrFixes when doing get/setAttribute (ie6/7)
++      support.getSetAttribute = div.className !== "t";
++
++      // Get the style information from getAttribute
++      // (IE uses .cssText instead)
++      support.style = /top/.test( a.getAttribute( "style" ) );
++
++      // Make sure that URLs aren't manipulated
++      // (IE normalizes it by default)
++      support.hrefNormalized = a.getAttribute( "href" ) === "/a";
++
++      // Check the default checkbox/radio value ("" on WebKit; "on" elsewhere)
++      support.checkOn = !!input.value;
++
++      // 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;
++
++      // Tests for enctype support on a form (#6743)
++      support.enctype = !!document.createElement( "form" ).enctype;
++
++      // 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: IE8 only
++      // Check if we can trust getAttribute("value")
++      input = document.createElement( "input" );
++      input.setAttribute( "value", "" );
++      support.input = input.getAttribute( "value" ) === "";
++
++      // Check if an input maintains its value after becoming a radio
++      input.value = "t";
++      input.setAttribute( "type", "radio" );
++      support.radioValue = input.value === "t";
++} )();
++
++
++var rreturn = /\r/g,
++      rspaces = /[\x20\t\r\n\f]+/g;
++
++jQuery.fn.extend( {
++      val: function( value ) {
++              var hooks, ret, isFunction,
++                      elem = this[ 0 ];
++
++              if ( !arguments.length ) {
++                      if ( elem ) {
++                              hooks = jQuery.valHooks[ elem.type ] ||
++                                      jQuery.valHooks[ elem.nodeName.toLowerCase() ];
++
++                              if (
++                                      hooks &&
++                                      "get" in hooks &&
++                                      ( ret = hooks.get( elem, "value" ) ) !== undefined
++                              ) {
++                                      return ret;
++                              }
++
++                              ret = elem.value;
++
++                              return typeof ret === "string" ?
++
++                                      // handle most common string cases
++                                      ret.replace( rreturn, "" ) :
++
++                                      // handle cases where value is null/undef or number
++                                      ret == null ? "" : ret;
++                      }
++
++                      return;
++              }
++
++              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;
++                      }
++
++                      // 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 ) {
++                              var val = jQuery.find.attr( elem, "value" );
++                              return val != null ?
++                                      val :
++
++                                      // Support: IE10-11+
++                                      // option.text throws exceptions (#14686, #14858)
++                                      // Strip and collapse whitespace
++                                      // https://html.spec.whatwg.org/#strip-and-collapse-whitespace
++                                      jQuery.trim( jQuery.text( elem ) ).replace( rspaces, " " );
++                      }
++              },
++              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;
++
++                              // Loop through all the selected options
++                              for ( ; i < max; i++ ) {
++                                      option = options[ i ];
++
++                                      // 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
++                                                      ( support.optDisabled ?
++                                                              !option.disabled :
++                                                              option.getAttribute( "disabled" ) === null ) &&
++                                                      ( !option.parentNode.disabled ||
++                                                              !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {
++
++                                              // Get the specific value for the option
++                                              value = jQuery( option ).val();
++
++                                              // We don't need an array for one selects
++                                              if ( one ) {
++                                                      return value;
++                                              }
++
++                                              // Multi-Selects return an array
++                                              values.push( value );
++                                      }
++                              }
++
++                              return values;
++                      },
++
++                      set: function( elem, value ) {
++                              var optionSet, option,
++                                      options = elem.options,
++                                      values = jQuery.makeArray( value ),
++                                      i = options.length;
++
++                              while ( i-- ) {
++                                      option = options[ i ];
++
++                                      if ( jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 ) {
++
++                                              // Support: IE6
++                                              // When new option element is added to select box we need to
++                                              // force reflow of newly added node in order to workaround delay
++                                              // of initialization properties
++                                              try {
++                                                      option.selected = optionSet = true;
++
++                                              } catch ( _ ) {
++
++                                                      // Will be executed only in IE6
++                                                      option.scrollHeight;
++                                              }
++
++                                      } else {
++                                              option.selected = false;
++                                      }
++                              }
++
++                              // Force browsers to behave consistently when non-matching value is set
++                              if ( !optionSet ) {
++                                      elem.selectedIndex = -1;
++                              }
++
++                              return options;
++                      }
++              }
++      }
++} );
++
++// 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 ) > -1 );
++                      }
++              }
++      };
++      if ( !support.checkOn ) {
++              jQuery.valHooks[ this ].get = function( elem ) {
++                      return elem.getAttribute( "value" ) === null ? "on" : elem.value;
++              };
++      }
++} );
++
++
++
++
++var nodeHook, boolHook,
++      attrHandle = jQuery.expr.attrHandle,
++      ruseDefault = /^(?:checked|selected)$/i,
++      getSetAttribute = support.getSetAttribute,
++      getSetInput = support.input;
++
++jQuery.fn.extend( {
++      attr: function( name, value ) {
++              return access( this, jQuery.attr, name, value, arguments.length > 1 );
++      },
++
++      removeAttr: function( name ) {
++              return this.each( function() {
++                      jQuery.removeAttr( this, name );
++              } );
++      }
++} );
++
++jQuery.extend( {
++      attr: function( elem, name, value ) {
++              var ret, hooks,
++                      nType = elem.nodeType;
++
++              // Don't get/set attributes on text, comment and attribute nodes
++              if ( nType === 3 || nType === 8 || nType === 2 ) {
++                      return;
++              }
++
++              // Fallback to prop when attributes are not supported
++              if ( typeof elem.getAttribute === "undefined" ) {
++                      return jQuery.prop( elem, name, value );
++              }
++
++              // 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 ( value !== undefined ) {
++                      if ( value === null ) {
++                              jQuery.removeAttr( elem, name );
++                              return;
++                      }
++
++                      if ( hooks && "set" in hooks &&
++                              ( ret = hooks.set( elem, value, name ) ) !== undefined ) {
++                              return ret;
++                      }
++
++                      elem.setAttribute( name, value + "" );
++                      return value;
++              }
++
++              if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {
++                      return ret;
++              }
++
++              ret = jQuery.find.attr( elem, name );
++
++              // Non-existent attributes return null, we normalize to undefined
++              return ret == null ? undefined : ret;
++      },
++
++      attrHooks: {
++              type: {
++                      set: function( elem, value ) {
++                              if ( !support.radioValue && value === "radio" &&
++                                      jQuery.nodeName( elem, "input" ) ) {
++
++                                      // Setting the type on a radio button after the value resets the value in IE8-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;
++                              }
++                      }
++              }
++      },
++
++      removeAttr: function( elem, value ) {
++              var name, propName,
++                      i = 0,
++                      attrNames = value && value.match( 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 );
++                      }
++              }
++      }
++} );
++
++// 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 );
++
++              } else {
++
++                      // Support: IE<9
++                      // Use defaultChecked and defaultSelected for oldIE
++                      elem[ jQuery.camelCase( "default-" + name ) ] = elem[ name ] = true;
++              }
++              return name;
++      }
++};
++
++jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) {
++      var getter = attrHandle[ name ] || jQuery.find.attr;
++
++      if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) {
++              attrHandle[ name ] = function( elem, name, isXML ) {
++                      var ret, handle;
++                      if ( !isXML ) {
++
++                              // Avoid an infinite loop by temporarily removing this function from the getter
++                              handle = attrHandle[ name ];
++                              attrHandle[ name ] = ret;
++                              ret = getter( elem, name, isXML ) != null ?
++                                      name.toLowerCase() :
++                                      null;
++                              attrHandle[ name ] = handle;
++                      }
++                      return ret;
++              };
++      } else {
++              attrHandle[ name ] = function( elem, name, isXML ) {
++                      if ( !isXML ) {
++                              return 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 );
++                      }
++              }
++      };
++}
++
++// 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 ) )
++                              );
++                      }
++
++                      ret.value = value += "";
++
++                      // Break association with cloned elements by also using setAttribute (#9646)
++                      if ( name === "value" || value === elem.getAttribute( name ) ) {
++                              return value;
++                      }
++              }
++      };
++
++      // Some attributes are constructed with empty-string values when not defined
++      attrHandle.id = attrHandle.name = attrHandle.coords =
++              function( elem, name, isXML ) {
++                      var ret;
++                      if ( !isXML ) {
++                              return ( ret = elem.getAttributeNode( name ) ) && ret.value !== "" ?
++                                      ret.value :
++                                      null;
++                      }
++              };
++
++      // Fixing value retrieval on a button requires this module
++      jQuery.valHooks.button = {
++              get: function( elem, name ) {
++                      var ret = elem.getAttributeNode( name );
++                      if ( ret && ret.specified ) {
++                              return ret.value;
++                      }
++              },
++              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;
++                              }
++                      }
++              };
++      } );
++}
++
++if ( !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 sensitivity in URL's, like in "background"
++                      return elem.style.cssText || undefined;
++              },
++              set: function( elem, value ) {
++                      return ( elem.style.cssText = value + "" );
++              }
++      };
++}
++
++
++
++
++var rfocusable = /^(?:input|select|textarea|button|object)$/i,
++      rclickable = /^(?:a|area)$/i;
++
++jQuery.fn.extend( {
++      prop: function( name, value ) {
++              return access( this, jQuery.prop, name, value, arguments.length > 1 );
++      },
++
++      removeProp: function( name ) {
++              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 ) {}
++              } );
++      }
++} );
++
++jQuery.extend( {
++      prop: function( elem, name, value ) {
++              var ret, hooks,
++                      nType = elem.nodeType;
++
++              // Don't get/set properties on text, comment and attribute nodes
++              if ( nType === 3 || nType === 8 || nType === 2 ) {
++                      return;
++              }
++
++              if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
++
++                      // Fix name and attach hooks
++                      name = jQuery.propFix[ name ] || name;
++                      hooks = jQuery.propHooks[ name ];
++              }
++
++              if ( value !== undefined ) {
++                      if ( hooks && "set" in hooks &&
++                              ( ret = hooks.set( elem, value, name ) ) !== undefined ) {
++                              return ret;
++                      }
++
++                      return ( elem[ name ] = value );
++              }
++
++              if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {
++                      return ret;
++              }
++
++              return elem[ name ];
++      },
++
++      propHooks: {
++              tabIndex: {
++                      get: function( elem ) {
++
++                              // 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;
++                      }
++              }
++      },
++
++      propFix: {
++              "for": "htmlFor",
++              "class": "className"
++      }
++} );
++
++// Some attributes require a special call on IE
++// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
++if ( !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 );
++                      }
++              };
++      } );
++}
++
++// Support: Safari, IE9+
++// Accessing the selectedIndex property
++// forces the browser to respect setting selected
++// on the option
++// The getter ensures a default option is selected
++// when in an optgroup
++if ( !support.optSelected ) {
++      jQuery.propHooks.selected = {
++              get: function( elem ) {
++                      var parent = elem.parentNode;
++
++                      if ( parent ) {
++                              parent.selectedIndex;
++
++                              // Make sure that it also works with optgroups, see #5701
++                              if ( parent.parentNode ) {
++                                      parent.parentNode.selectedIndex;
++                              }
++                      }
++                      return null;
++              },
++              set: function( elem ) {
++                      var parent = elem.parentNode;
++                      if ( parent ) {
++                              parent.selectedIndex;
++
++                              if ( parent.parentNode ) {
++                                      parent.parentNode.selectedIndex;
++                              }
++                      }
++              }
++      };
++}
++
++jQuery.each( [
++      "tabIndex",
++      "readOnly",
++      "maxLength",
++      "cellSpacing",
++      "cellPadding",
++      "rowSpan",
++      "colSpan",
++      "useMap",
++      "frameBorder",
++      "contentEditable"
++], function() {
++      jQuery.propFix[ this.toLowerCase() ] = this;
++} );
++
++// IE6/7 call enctype encoding
++if ( !support.enctype ) {
++      jQuery.propFix.enctype = "encoding";
++}
++
++
++
++
++var rclass = /[\t\r\n\f]/g;
++
++function getClass( elem ) {
++      return jQuery.attr( elem, "class" ) || "";
++}
++
++jQuery.fn.extend( {
++      addClass: function( value ) {
++              var classes, elem, cur, curValue, clazz, j, finalValue,
++                      i = 0;
++
++              if ( jQuery.isFunction( value ) ) {
++                      return this.each( function( j ) {
++                              jQuery( this ).addClass( value.call( this, j, getClass( this ) ) );
++                      } );
++              }
++
++              if ( typeof value === "string" && value ) {
++                      classes = value.match( rnotwhite ) || [];
++
++                      while ( ( elem = this[ i++ ] ) ) {
++                              curValue = getClass( elem );
++                              cur = elem.nodeType === 1 &&
++                                      ( " " + curValue + " " ).replace( rclass, " " );
++
++                              if ( cur ) {
++                                      j = 0;
++                                      while ( ( clazz = classes[ j++ ] ) ) {
++                                              if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
++                                                      cur += clazz + " ";
++                                              }
++                                      }
++
++                                      // only assign if different to avoid unneeded rendering.
++                                      finalValue = jQuery.trim( cur );
++                                      if ( curValue !== finalValue ) {
++                                              jQuery.attr( elem, "class", finalValue );
++                                      }
++                              }
++                      }
++              }
++
++              return this;
++      },
++
++      removeClass: function( value ) {
++              var classes, elem, cur, curValue, clazz, j, finalValue,
++                      i = 0;
++
++              if ( jQuery.isFunction( value ) ) {
++                      return this.each( function( j ) {
++                              jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) );
++                      } );
++              }
++
++              if ( !arguments.length ) {
++                      return this.attr( "class", "" );
++              }
++
++              if ( typeof value === "string" && value ) {
++                      classes = value.match( rnotwhite ) || [];
++
++                      while ( ( elem = this[ i++ ] ) ) {
++                              curValue = getClass( elem );
++
++                              // This expression is here for better compressibility (see addClass)
++                              cur = elem.nodeType === 1 &&
++                                      ( " " + curValue + " " ).replace( rclass, " " );
++
++                              if ( cur ) {
++                                      j = 0;
++                                      while ( ( clazz = classes[ j++ ] ) ) {
++
++                                              // Remove *all* instances
++                                              while ( cur.indexOf( " " + clazz + " " ) > -1 ) {
++                                                      cur = cur.replace( " " + clazz + " ", " " );
++                                              }
++                                      }
++
++                                      // Only assign if different to avoid unneeded rendering.
++                                      finalValue = jQuery.trim( cur );
++                                      if ( curValue !== finalValue ) {
++                                              jQuery.attr( elem, "class", finalValue );
++                                      }
++                              }
++                      }
++              }
++
++              return this;
++      },
++
++      toggleClass: function( value, stateVal ) {
++              var type = typeof value;
++
++              if ( typeof stateVal === "boolean" && type === "string" ) {
++                      return stateVal ? this.addClass( value ) : this.removeClass( value );
++              }
++
++              if ( jQuery.isFunction( value ) ) {
++                      return this.each( function( i ) {
++                              jQuery( this ).toggleClass(
++                                      value.call( this, i, getClass( this ), stateVal ),
++                                      stateVal
++                              );
++                      } );
++              }
++
++              return this.each( function() {
++                      var className, i, self, classNames;
++
++                      if ( type === "string" ) {
++
++                              // Toggle individual class names
++                              i = 0;
++                              self = jQuery( this );
++                              classNames = value.match( rnotwhite ) || [];
++
++                              while ( ( className = classNames[ i++ ] ) ) {
++
++                                      // Check each className given, space separated list
++                                      if ( self.hasClass( className ) ) {
++                                              self.removeClass( className );
++                                      } else {
++                                              self.addClass( className );
++                                      }
++                              }
++
++                      // Toggle whole class name
++                      } else if ( value === undefined || type === "boolean" ) {
++                              className = getClass( this );
++                              if ( className ) {
++
++                                      // store className if set
++                                      jQuery._data( this, "__className__", className );
++                              }
++
++                              // If the element has a class name or if we're passed "false",
++                              // then remove the whole classname (if there was one, the above saved it).
++                              // Otherwise bring back whatever was previously saved (if anything),
++                              // falling back to the empty string if nothing was stored.
++                              jQuery.attr( this, "class",
++                                      className || value === false ?
++                                      "" :
++                                      jQuery._data( this, "__className__" ) || ""
++                              );
++                      }
++              } );
++      },
++
++      hasClass: function( selector ) {
++              var className, elem,
++                      i = 0;
++
++              className = " " + selector + " ";
++              while ( ( elem = this[ i++ ] ) ) {
++                      if ( elem.nodeType === 1 &&
++                              ( " " + getClass( elem ) + " " ).replace( rclass, " " )
++                                      .indexOf( className ) > -1
++                      ) {
++                              return true;
++                      }
++              }
++
++              return false;
++      }
++} );
++
++
++
++
++// Return jQuery for attributes-only inclusion
++
++
++jQuery.each( ( "blur focus focusin focusout load resize scroll unload click dblclick " +
++      "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
++      "change select submit keydown keypress keyup error contextmenu" ).split( " " ),
++      function( i, name ) {
++
++      // Handle event binding
++      jQuery.fn[ name ] = function( data, fn ) {
++              return arguments.length > 0 ?
++                      this.on( name, null, data, fn ) :
++                      this.trigger( name );
++      };
++} );
++
++jQuery.fn.extend( {
++      hover: function( fnOver, fnOut ) {
++              return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
++      }
++} );
++
++
++var location = window.location;
++
++var nonce = jQuery.now();
++
++var rquery = ( /\?/ );
++
++
++
++var rvalidtokens = /(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;
++
++jQuery.parseJSON = function( data ) {
++
++      // Attempt to parse using the native JSON parser first
++      if ( window.JSON && window.JSON.parse ) {
++
++              // Support: Android 2.3
++              // Workaround failure to string-cast null input
++              return window.JSON.parse( data + "" );
++      }
++
++      var requireNonComma,
++              depth = null,
++              str = jQuery.trim( data + "" );
++
++      // Guard against invalid (and possibly dangerous) input by ensuring that nothing remains
++      // after removing valid tokens
++      return str && !jQuery.trim( str.replace( rvalidtokens, function( token, comma, open, close ) {
++
++              // Force termination if we see a misplaced comma
++              if ( requireNonComma && comma ) {
++                      depth = 0;
++              }
++
++              // Perform no more replacements after returning to outermost depth
++              if ( depth === 0 ) {
++                      return token;
++              }
++
++              // Commas must not follow "[", "{", or ","
++              requireNonComma = open || comma;
++
++              // Determine new depth
++              // array/object open ("[" or "{"): depth += true - false (increment)
++              // array/object close ("]" or "}"): depth += false - true (decrement)
++              // other cases ("," or primitive): depth += true - true (numeric cast)
++              depth += !close - !open;
++
++              // Remove this token
++              return "";
++      } ) ) ?
++              ( Function( "return " + str ) )() :
++              jQuery.error( "Invalid JSON: " + data );
++};
++
++
++// Cross-browser xml parsing
++jQuery.parseXML = function( data ) {
++      var xml, tmp;
++      if ( !data || typeof data !== "string" ) {
++              return null;
++      }
++      try {
++              if ( window.DOMParser ) { // Standard
++                      tmp = new window.DOMParser();
++                      xml = tmp.parseFromString( data, "text/xml" );
++              } else { // IE
++                      xml = new window.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;
++};
++
++
++var
++      rhash = /#.*$/,
++      rts = /([?&])_=[^&]*/,
++
++      // IE leaves an \r character at EOL
++      rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg,
++
++      // #7653, #8125, #8152: local protocol detection
++      rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
++      rnoContent = /^(?:GET|HEAD)$/,
++      rprotocol = /^\/\//,
++      rurl = /^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,
++
++      /* Prefilters
++       * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
++       * 2) These are called:
++       *    - BEFORE asking for a transport
++       *    - AFTER param serialization (s.data is a string if s.processData is true)
++       * 3) key is the dataType
++       * 4) the catchall symbol "*" can be used
++       * 5) execution will start with transport dataType and THEN continue down to "*" if needed
++       */
++      prefilters = {},
++
++      /* Transports bindings
++       * 1) key is the dataType
++       * 2) the catchall symbol "*" can be used
++       * 3) selection will start with transport dataType and THEN go to "*" if needed
++       */
++      transports = {},
++
++      // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
++      allTypes = "*/".concat( "*" ),
++
++      // Document location
++      ajaxLocation = location.href,
++
++      // Segment location into parts
++      ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
++
++// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
++function addToPrefiltersOrTransports( structure ) {
++
++      // dataTypeExpression is optional and defaults to "*"
++      return function( dataTypeExpression, func ) {
++
++              if ( typeof dataTypeExpression !== "string" ) {
++                      func = dataTypeExpression;
++                      dataTypeExpression = "*";
++              }
++
++              var dataType,
++                      i = 0,
++                      dataTypes = dataTypeExpression.toLowerCase().match( rnotwhite ) || [];
++
++              if ( jQuery.isFunction( func ) ) {
++
++                      // For each dataType in the dataTypeExpression
++                      while ( ( dataType = dataTypes[ i++ ] ) ) {
++
++                              // Prepend if requested
++                              if ( dataType.charAt( 0 ) === "+" ) {
++                                      dataType = dataType.slice( 1 ) || "*";
++                                      ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func );
++
++                              // Otherwise append
++                              } else {
++                                      ( structure[ dataType ] = structure[ dataType ] || [] ).push( func );
++                              }
++                      }
++              }
++      };
++}
++
++// Base inspection function for prefilters and transports
++function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {
++
++      var inspected = {},
++              seekingTransport = ( structure === transports );
++
++      function inspect( dataType ) {
++              var selected;
++              inspected[ dataType ] = true;
++              jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
++                      var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
++                      if ( typeof dataTypeOrTransport === "string" &&
++                              !seekingTransport && !inspected[ dataTypeOrTransport ] ) {
++
++                              options.dataTypes.unshift( dataTypeOrTransport );
++                              inspect( dataTypeOrTransport );
++                              return false;
++                      } else if ( seekingTransport ) {
++                              return !( selected = dataTypeOrTransport );
++                      }
++              } );
++              return selected;
++      }
++
++      return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
++}
++
++// A special extend for ajax options
++// that takes "flat" options (not to be deep extended)
++// Fixes #9887
++function ajaxExtend( target, src ) {
++      var deep, key,
++              flatOptions = jQuery.ajaxSettings.flatOptions || {};
++
++      for ( key in src ) {
++              if ( src[ key ] !== undefined ) {
++                      ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ];
++              }
++      }
++      if ( deep ) {
++              jQuery.extend( true, target, deep );
++      }
++
++      return target;
++}
++
++/* Handles responses to an ajax request:
++ * - finds the right dataType (mediates between content-type and expected dataType)
++ * - returns the corresponding response
++ */
++function ajaxHandleResponses( s, jqXHR, responses ) {
++      var firstDataType, ct, finalDataType, type,
++              contents = s.contents,
++              dataTypes = s.dataTypes;
++
++      // Remove auto dataType and get content-type in the process
++      while ( dataTypes[ 0 ] === "*" ) {
++              dataTypes.shift();
++              if ( ct === undefined ) {
++                      ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" );
++              }
++      }
++
++      // Check if we're dealing with a known content-type
++      if ( ct ) {
++              for ( type in contents ) {
++                      if ( contents[ type ] && contents[ type ].test( ct ) ) {
++                              dataTypes.unshift( type );
++                              break;
++                      }
++              }
++      }
++
++      // Check to see if we have a response for the expected dataType
++      if ( dataTypes[ 0 ] in responses ) {
++              finalDataType = dataTypes[ 0 ];
++      } else {
++
++              // Try convertible dataTypes
++              for ( type in responses ) {
++                      if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) {
++                              finalDataType = type;
++                              break;
++                      }
++                      if ( !firstDataType ) {
++                              firstDataType = type;
++                      }
++              }
++
++              // Or just use first one
++              finalDataType = finalDataType || firstDataType;
++      }
++
++      // If we found a dataType
++      // We add the dataType to the list if needed
++      // and return the corresponding response
++      if ( finalDataType ) {
++              if ( finalDataType !== dataTypes[ 0 ] ) {
++                      dataTypes.unshift( finalDataType );
++              }
++              return responses[ finalDataType ];
++      }
++}
++
++/* Chain conversions given the request and the original response
++ * Also sets the responseXXX fields on the jqXHR instance
++ */
++function ajaxConvert( s, response, jqXHR, isSuccess ) {
++      var conv2, current, conv, tmp, prev,
++              converters = {},
++
++              // Work with a copy of dataTypes in case we need to modify it for conversion
++              dataTypes = s.dataTypes.slice();
++
++      // Create converters map with lowercased keys
++      if ( dataTypes[ 1 ] ) {
++              for ( conv in s.converters ) {
++                      converters[ conv.toLowerCase() ] = s.converters[ conv ];
++              }
++      }
++
++      current = dataTypes.shift();
++
++      // Convert to each sequential dataType
++      while ( current ) {
++
++              if ( s.responseFields[ current ] ) {
++                      jqXHR[ s.responseFields[ current ] ] = response;
++              }
++
++              // Apply the dataFilter if provided
++              if ( !prev && isSuccess && s.dataFilter ) {
++                      response = s.dataFilter( response, s.dataType );
++              }
++
++              prev = current;
++              current = dataTypes.shift();
++
++              if ( current ) {
++
++                      // There's only work to do if current dataType is non-auto
++                      if ( current === "*" ) {
++
++                              current = prev;
++
++                      // Convert response if prev dataType is non-auto and differs from current
++                      } else if ( prev !== "*" && prev !== current ) {
++
++                              // Seek a direct converter
++                              conv = converters[ prev + " " + current ] || converters[ "* " + current ];
++
++                              // If none found, seek a pair
++                              if ( !conv ) {
++                                      for ( conv2 in converters ) {
++
++                                              // If conv2 outputs current
++                                              tmp = conv2.split( " " );
++                                              if ( tmp[ 1 ] === current ) {
++
++                                                      // If prev can be converted to accepted input
++                                                      conv = converters[ prev + " " + tmp[ 0 ] ] ||
++                                                              converters[ "* " + tmp[ 0 ] ];
++                                                      if ( conv ) {
++
++                                                              // Condense equivalence converters
++                                                              if ( conv === true ) {
++                                                                      conv = converters[ conv2 ];
++
++                                                              // Otherwise, insert the intermediate dataType
++                                                              } else if ( converters[ conv2 ] !== true ) {
++                                                                      current = tmp[ 0 ];
++                                                                      dataTypes.unshift( tmp[ 1 ] );
++                                                              }
++                                                              break;
++                                                      }
++                                              }
++                                      }
++                              }
++
++                              // Apply converter (if not an equivalence)
++                              if ( conv !== true ) {
++
++                                      // Unless errors are allowed to bubble, catch and return them
++                                      if ( conv && s[ "throws" ] ) { // jscs:ignore requireDotNotation
++                                              response = conv( response );
++                                      } else {
++                                              try {
++                                                      response = conv( response );
++                                              } catch ( e ) {
++                                                      return {
++                                                              state: "parsererror",
++                                                              error: conv ? e : "No conversion from " + prev + " to " + current
++                                                      };
++                                              }
++                                      }
++                              }
++                      }
++              }
++      }
++
++      return { state: "success", data: response };
++}
++
++jQuery.extend( {
++
++      // Counter for holding the number of active queries
++      active: 0,
++
++      // Last-Modified header cache for next request
++      lastModified: {},
++      etag: {},
++
++      ajaxSettings: {
++              url: ajaxLocation,
++              type: "GET",
++              isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
++              global: true,
++              processData: true,
++              async: true,
++              contentType: "application/x-www-form-urlencoded; charset=UTF-8",
++              /*
++              timeout: 0,
++              data: null,
++              dataType: null,
++              username: null,
++              password: null,
++              cache: null,
++              throws: false,
++              traditional: false,
++              headers: {},
++              */
++
++              accepts: {
++                      "*": allTypes,
++                      text: "text/plain",
++                      html: "text/html",
++                      xml: "application/xml, text/xml",
++                      json: "application/json, text/javascript"
++              },
++
++              contents: {
++                      xml: /\bxml\b/,
++                      html: /\bhtml/,
++                      json: /\bjson\b/
++              },
++
++              responseFields: {
++                      xml: "responseXML",
++                      text: "responseText",
++                      json: "responseJSON"
++              },
++
++              // Data converters
++              // Keys separate source (or catchall "*") and destination types with a single space
++              converters: {
++
++                      // Convert anything to text
++                      "* text": String,
++
++                      // Text to html (true = no transformation)
++                      "text html": true,
++
++                      // Evaluate text as a json expression
++                      "text json": jQuery.parseJSON,
++
++                      // Parse text as xml
++                      "text xml": jQuery.parseXML
++              },
++
++              // For options that shouldn't be deep extended:
++              // you can add your own custom options here if
++              // and when you create one that shouldn't be
++              // deep extended (see ajaxExtend)
++              flatOptions: {
++                      url: true,
++                      context: true
++              }
++      },
++
++      // Creates a full fledged settings object into target
++      // with both ajaxSettings and settings fields.
++      // If target is omitted, writes into ajaxSettings.
++      ajaxSetup: function( target, settings ) {
++              return settings ?
++
++                      // Building a settings object
++                      ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :
++
++                      // Extending ajaxSettings
++                      ajaxExtend( jQuery.ajaxSettings, target );
++      },
++
++      ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
++      ajaxTransport: addToPrefiltersOrTransports( transports ),
++
++      // Main method
++      ajax: function( url, options ) {
++
++              // If url is an object, simulate pre-1.5 signature
++              if ( typeof url === "object" ) {
++                      options = url;
++                      url = undefined;
++              }
++
++              // Force options to be an object
++              options = options || {};
++
++              var
++
++                      // Cross-domain detection vars
++                      parts,
++
++                      // Loop variable
++                      i,
++
++                      // URL without anti-cache param
++                      cacheURL,
++
++                      // Response headers as string
++                      responseHeadersString,
++
++                      // timeout handle
++                      timeoutTimer,
++
++                      // To know if global events are to be dispatched
++                      fireGlobals,
++
++                      transport,
++
++                      // Response headers
++                      responseHeaders,
++
++                      // Create the final options object
++                      s = jQuery.ajaxSetup( {}, options ),
++
++                      // Callbacks context
++                      callbackContext = s.context || s,
++
++                      // Context for global events is callbackContext if it is a DOM node or jQuery collection
++                      globalEventContext = s.context &&
++                              ( callbackContext.nodeType || callbackContext.jquery ) ?
++                                      jQuery( callbackContext ) :
++                                      jQuery.event,
++
++                      // Deferreds
++                      deferred = jQuery.Deferred(),
++                      completeDeferred = jQuery.Callbacks( "once memory" ),
++
++                      // Status-dependent callbacks
++                      statusCode = s.statusCode || {},
++
++                      // Headers (they are sent all at once)
++                      requestHeaders = {},
++                      requestHeadersNames = {},
++
++                      // The jqXHR state
++                      state = 0,
++
++                      // Default abort message
++                      strAbort = "canceled",
++
++                      // Fake xhr
++                      jqXHR = {
++                              readyState: 0,
++
++                              // Builds headers hashtable if needed
++                              getResponseHeader: function( key ) {
++                                      var match;
++                                      if ( state === 2 ) {
++                                              if ( !responseHeaders ) {
++                                                      responseHeaders = {};
++                                                      while ( ( match = rheaders.exec( responseHeadersString ) ) ) {
++                                                              responseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ];
++                                                      }
++                                              }
++                                              match = responseHeaders[ key.toLowerCase() ];
++                                      }
++                                      return match == null ? null : match;
++                              },
++
++                              // Raw string
++                              getAllResponseHeaders: function() {
++                                      return state === 2 ? responseHeadersString : null;
++                              },
++
++                              // Caches the header
++                              setRequestHeader: function( name, value ) {
++                                      var lname = name.toLowerCase();
++                                      if ( !state ) {
++                                              name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
++                                              requestHeaders[ name ] = value;
++                                      }
++                                      return this;
++                              },
++
++                              // Overrides response content-type header
++                              overrideMimeType: function( type ) {
++                                      if ( !state ) {
++                                              s.mimeType = type;
++                                      }
++                                      return this;
++                              },
++
++                              // Status-dependent callbacks
++                              statusCode: function( map ) {
++                                      var code;
++                                      if ( map ) {
++                                              if ( state < 2 ) {
++                                                      for ( code in map ) {
++
++                                                              // Lazy-add the new callback in a way that preserves old ones
++                                                              statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
++                                                      }
++                                              } else {
++
++                                                      // Execute the appropriate callbacks
++                                                      jqXHR.always( map[ jqXHR.status ] );
++                                              }
++                                      }
++                                      return this;
++                              },
++
++                              // Cancel the request
++                              abort: function( statusText ) {
++                                      var finalText = statusText || strAbort;
++                                      if ( transport ) {
++                                              transport.abort( finalText );
++                                      }
++                                      done( 0, finalText );
++                                      return this;
++                              }
++                      };
++
++              // Attach deferreds
++              deferred.promise( jqXHR ).complete = completeDeferred.add;
++              jqXHR.success = jqXHR.done;
++              jqXHR.error = jqXHR.fail;
++
++              // Remove hash character (#7531: and string promotion)
++              // Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
++              // Handle falsy url in the settings object (#10093: consistency with old signature)
++              // We also use the url parameter if available
++              s.url = ( ( url || s.url || ajaxLocation ) + "" )
++                      .replace( rhash, "" )
++                      .replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
++
++              // Alias method option to type as per ticket #12004
++              s.type = options.method || options.type || s.method || s.type;
++
++              // Extract dataTypes list
++              s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( rnotwhite ) || [ "" ];
++
++              // A cross-domain request is in order when we have a protocol:host:port mismatch
++              if ( s.crossDomain == null ) {
++                      parts = rurl.exec( s.url.toLowerCase() );
++                      s.crossDomain = !!( parts &&
++                              ( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||
++                                      ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? "80" : "443" ) ) !==
++                                              ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? "80" : "443" ) ) )
++                      );
++              }
++
++              // Convert data if not already a string
++              if ( s.data && s.processData && typeof s.data !== "string" ) {
++                      s.data = jQuery.param( s.data, s.traditional );
++              }
++
++              // Apply prefilters
++              inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
++
++              // If request was aborted inside a prefilter, stop there
++              if ( state === 2 ) {
++                      return jqXHR;
++              }
++
++              // We can fire global events as of now if asked to
++              // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118)
++              fireGlobals = jQuery.event && s.global;
++
++              // Watch for a new set of requests
++              if ( fireGlobals && jQuery.active++ === 0 ) {
++                      jQuery.event.trigger( "ajaxStart" );
++              }
++
++              // Uppercase the type
++              s.type = s.type.toUpperCase();
++
++              // Determine if request has content
++              s.hasContent = !rnoContent.test( s.type );
++
++              // Save the URL in case we're toying with the If-Modified-Since
++              // and/or If-None-Match header later on
++              cacheURL = s.url;
++
++              // More options handling for requests with no content
++              if ( !s.hasContent ) {
++
++                      // If data is available, append data to url
++                      if ( s.data ) {
++                              cacheURL = ( s.url += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
++
++                              // #9682: remove data so that it's not used in an eventual retry
++                              delete s.data;
++                      }
++
++                      // Add anti-cache in url if needed
++                      if ( s.cache === false ) {
++                              s.url = rts.test( cacheURL ) ?
++
++                                      // If there is already a '_' parameter, set its value
++                                      cacheURL.replace( rts, "$1_=" + nonce++ ) :
++
++                                      // Otherwise add one to the end
++                                      cacheURL + ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + nonce++;
++                      }
++              }
++
++              // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
++              if ( s.ifModified ) {
++                      if ( jQuery.lastModified[ cacheURL ] ) {
++                              jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
++                      }
++                      if ( jQuery.etag[ cacheURL ] ) {
++                              jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
++                      }
++              }
++
++              // Set the correct header, if data is being sent
++              if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
++                      jqXHR.setRequestHeader( "Content-Type", s.contentType );
++              }
++
++              // Set the Accepts header for the server, depending on the dataType
++              jqXHR.setRequestHeader(
++                      "Accept",
++                      s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ?
++                              s.accepts[ s.dataTypes[ 0 ] ] +
++                                      ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
++                              s.accepts[ "*" ]
++              );
++
++              // Check for headers option
++              for ( i in s.headers ) {
++                      jqXHR.setRequestHeader( i, s.headers[ i ] );
++              }
++
++              // Allow custom headers/mimetypes and early abort
++              if ( s.beforeSend &&
++                      ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
++
++                      // Abort if not done already and return
++                      return jqXHR.abort();
++              }
++
++              // aborting is no longer a cancellation
++              strAbort = "abort";
++
++              // Install callbacks on deferreds
++              for ( i in { success: 1, error: 1, complete: 1 } ) {
++                      jqXHR[ i ]( s[ i ] );
++              }
++
++              // Get transport
++              transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
++
++              // If no transport, we auto-abort
++              if ( !transport ) {
++                      done( -1, "No Transport" );
++              } else {
++                      jqXHR.readyState = 1;
++
++                      // Send global event
++                      if ( fireGlobals ) {
++                              globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
++                      }
++
++                      // If request was aborted inside ajaxSend, stop there
++                      if ( state === 2 ) {
++                              return jqXHR;
++                      }
++
++                      // Timeout
++                      if ( s.async && s.timeout > 0 ) {
++                              timeoutTimer = window.setTimeout( function() {
++                                      jqXHR.abort( "timeout" );
++                              }, s.timeout );
++                      }
++
++                      try {
++                              state = 1;
++                              transport.send( requestHeaders, done );
++                      } catch ( e ) {
++
++                              // Propagate exception as error if not done
++                              if ( state < 2 ) {
++                                      done( -1, e );
++
++                              // Simply rethrow otherwise
++                              } else {
++                                      throw e;
++                              }
++                      }
++              }
++
++              // Callback for when everything is done
++              function done( status, nativeStatusText, responses, headers ) {
++                      var isSuccess, success, error, response, modified,
++                              statusText = nativeStatusText;
++
++                      // Called once
++                      if ( state === 2 ) {
++                              return;
++                      }
++
++                      // State is "done" now
++                      state = 2;
++
++                      // Clear timeout if it exists
++                      if ( timeoutTimer ) {
++                              window.clearTimeout( timeoutTimer );
++                      }
++
++                      // Dereference transport for early garbage collection
++                      // (no matter how long the jqXHR object will be used)
++                      transport = undefined;
++
++                      // Cache response headers
++                      responseHeadersString = headers || "";
++
++                      // Set readyState
++                      jqXHR.readyState = status > 0 ? 4 : 0;
++
++                      // Determine if successful
++                      isSuccess = status >= 200 && status < 300 || status === 304;
++
++                      // Get response data
++                      if ( responses ) {
++                              response = ajaxHandleResponses( s, jqXHR, responses );
++                      }
++
++                      // Convert no matter what (that way responseXXX fields are always set)
++                      response = ajaxConvert( s, response, jqXHR, isSuccess );
++
++                      // If successful, handle type chaining
++                      if ( isSuccess ) {
++
++                              // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
++                              if ( s.ifModified ) {
++                                      modified = jqXHR.getResponseHeader( "Last-Modified" );
++                                      if ( modified ) {
++                                              jQuery.lastModified[ cacheURL ] = modified;
++                                      }
++                                      modified = jqXHR.getResponseHeader( "etag" );
++                                      if ( modified ) {
++                                              jQuery.etag[ cacheURL ] = modified;
++                                      }
++                              }
++
++                              // if no content
++                              if ( status === 204 || s.type === "HEAD" ) {
++                                      statusText = "nocontent";
++
++                              // if not modified
++                              } else if ( status === 304 ) {
++                                      statusText = "notmodified";
++
++                              // If we have data, let's convert it
++                              } else {
++                                      statusText = response.state;
++                                      success = response.data;
++                                      error = response.error;
++                                      isSuccess = !error;
++                              }
++                      } else {
++
++                              // We extract error from statusText
++                              // then normalize statusText and status for non-aborts
++                              error = statusText;
++                              if ( status || !statusText ) {
++                                      statusText = "error";
++                                      if ( status < 0 ) {
++                                              status = 0;
++                                      }
++                              }
++                      }
++
++                      // Set data for the fake xhr object
++                      jqXHR.status = status;
++                      jqXHR.statusText = ( nativeStatusText || statusText ) + "";
++
++                      // Success/Error
++                      if ( isSuccess ) {
++                              deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
++                      } else {
++                              deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
++                      }
++
++                      // Status-dependent callbacks
++                      jqXHR.statusCode( statusCode );
++                      statusCode = undefined;
++
++                      if ( fireGlobals ) {
++                              globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
++                                      [ jqXHR, s, isSuccess ? success : error ] );
++                      }
++
++                      // Complete
++                      completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
++
++                      if ( fireGlobals ) {
++                              globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
++
++                              // Handle the global AJAX counter
++                              if ( !( --jQuery.active ) ) {
++                                      jQuery.event.trigger( "ajaxStop" );
++                              }
++                      }
++              }
++
++              return jqXHR;
++      },
++
++      getJSON: function( url, data, callback ) {
++              return jQuery.get( url, data, callback, "json" );
++      },
++
++      getScript: function( url, callback ) {
++              return jQuery.get( url, undefined, callback, "script" );
++      }
++} );
++
++jQuery.each( [ "get", "post" ], function( i, method ) {
++      jQuery[ method ] = function( url, data, callback, type ) {
++
++              // shift arguments if data argument was omitted
++              if ( jQuery.isFunction( data ) ) {
++                      type = type || callback;
++                      callback = data;
++                      data = undefined;
++              }
++
++              // The url can be an options object (which then must have .url)
++              return jQuery.ajax( jQuery.extend( {
++                      url: url,
++                      type: method,
++                      dataType: type,
++                      data: data,
++                      success: callback
++              }, jQuery.isPlainObject( url ) && url ) );
++      };
++} );
++
++
++jQuery._evalUrl = function( url ) {
++      return jQuery.ajax( {
++              url: url,
++
++              // Make this explicit, since user can override this through ajaxSetup (#11264)
++              type: "GET",
++              dataType: "script",
++              cache: true,
++              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 ) );
++                      } );
++              }
++
++              if ( this[ 0 ] ) {
++
++                      // The elements to wrap the target around
++                      var wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true );
++
++                      if ( this[ 0 ].parentNode ) {
++                              wrap.insertBefore( this[ 0 ] );
++                      }
++
++                      wrap.map( function() {
++                              var elem = this;
++
++                              while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
++                                      elem = elem.firstChild;
++                              }
++
++                              return elem;
++                      } ).append( this );
++              }
++
++              return this;
++      },
++
++      wrapInner: function( html ) {
++              if ( jQuery.isFunction( html ) ) {
++                      return this.each( function( i ) {
++                              jQuery( this ).wrapInner( html.call( this, i ) );
++                      } );
++              }
++
++              return this.each( function() {
++                      var self = jQuery( this ),
++                              contents = self.contents();
++
++                      if ( contents.length ) {
++                              contents.wrapAll( html );
++
++                      } else {
++                              self.append( html );
++                      }
++              } );
++      },
++
++      wrap: function( html ) {
++              var isFunction = jQuery.isFunction( html );
++
++              return this.each( function( i ) {
++                      jQuery( this ).wrapAll( isFunction ? html.call( this, i ) : html );
++              } );
++      },
++
++      unwrap: function() {
++              return this.parent().each( function() {
++                      if ( !jQuery.nodeName( this, "body" ) ) {
++                              jQuery( this ).replaceWith( this.childNodes );
++                      }
++              } ).end();
++      }
++} );
++
++
++function getDisplay( elem ) {
++      return elem.style && elem.style.display || jQuery.css( elem, "display" );
++}
++
++function filterHidden( elem ) {
++
++      // Disconnected elements are considered hidden
++      if ( !jQuery.contains( elem.ownerDocument || document, elem ) ) {
++              return true;
++      }
++      while ( elem && elem.nodeType === 1 ) {
++              if ( getDisplay( elem ) === "none" || elem.type === "hidden" ) {
++                      return true;
++              }
++              elem = elem.parentNode;
++      }
++      return false;
++}
++
++jQuery.expr.filters.hidden = function( elem ) {
++
++      // Support: Opera <= 12.12
++      // Opera reports offsetWidths and offsetHeights less than zero on some elements
++      return support.reliableHiddenOffsets() ?
++              ( elem.offsetWidth <= 0 && elem.offsetHeight <= 0 &&
++                      !elem.getClientRects().length ) :
++                      filterHidden( elem );
++};
++
++jQuery.expr.filters.visible = function( elem ) {
++      return !jQuery.expr.filters.hidden( elem );
++};
++
++
++
++
++var r20 = /%20/g,
++      rbracket = /\[\]$/,
++      rCRLF = /\r?\n/g,
++      rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
++      rsubmittable = /^(?:input|select|textarea|keygen)/i;
++
++function buildParams( prefix, obj, traditional, add ) {
++      var name;
++
++      if ( jQuery.isArray( obj ) ) {
++
++              // Serialize array item.
++              jQuery.each( obj, function( i, v ) {
++                      if ( traditional || rbracket.test( prefix ) ) {
++
++                              // Treat each array item as a scalar.
++                              add( prefix, v );
++
++                      } else {
++
++                              // Item is non-scalar (array or object), encode its numeric index.
++                              buildParams(
++                                      prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]",
++                                      v,
++                                      traditional,
++                                      add
++                              );
++                      }
++              } );
++
++      } else if ( !traditional && jQuery.type( obj ) === "object" ) {
++
++              // Serialize object item.
++              for ( name in obj ) {
++                      buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
++              }
++
++      } else {
++
++              // Serialize scalar item.
++              add( prefix, obj );
++      }
++}
++
++// Serialize an array of form elements or a set of
++// key/values into a query string
++jQuery.param = function( a, traditional ) {
++      var prefix,
++              s = [],
++              add = function( key, value ) {
++
++                      // If value is a function, invoke it and return its value
++                      value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value );
++                      s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
++              };
++
++      // Set traditional to true for jQuery <= 1.3.2 behavior.
++      if ( traditional === undefined ) {
++              traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;
++      }
++
++      // If an array was passed in, assume that it is an array of form elements.
++      if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
++
++              // Serialize the form elements
++              jQuery.each( a, function() {
++                      add( this.name, this.value );
++              } );
++
++      } else {
++
++              // If traditional, encode the "old" way (the way 1.3.2 or older
++              // did it), otherwise encode params recursively.
++              for ( prefix in a ) {
++                      buildParams( prefix, a[ prefix ], traditional, add );
++              }
++      }
++
++      // Return the resulting serialization
++      return s.join( "&" ).replace( r20, "+" );
++};
++
++jQuery.fn.extend( {
++      serialize: function() {
++              return jQuery.param( this.serializeArray() );
++      },
++      serializeArray: function() {
++              return this.map( function() {
++
++                      // Can add propHook for "elements" to filter or add form elements
++                      var elements = jQuery.prop( this, "elements" );
++                      return elements ? jQuery.makeArray( elements ) : this;
++              } )
++              .filter( function() {
++                      var type = this.type;
++
++                      // Use .is(":disabled") so that fieldset[disabled] works
++                      return this.name && !jQuery( this ).is( ":disabled" ) &&
++                              rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
++                              ( this.checked || !rcheckableType.test( type ) );
++              } )
++              .map( function( i, elem ) {
++                      var val = jQuery( this ).val();
++
++                      return val == null ?
++                              null :
++                              jQuery.isArray( val ) ?
++                                      jQuery.map( val, function( val ) {
++                                              return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
++                                      } ) :
++                                      { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
++              } ).get();
++      }
++} );
++
++
++// Create the request object
++// (This is still attached to ajaxSettings for backward compatibility)
++jQuery.ajaxSettings.xhr = window.ActiveXObject !== undefined ?
++
++      // Support: IE6-IE8
++      function() {
++
++              // XHR cannot access local files, always use ActiveX for that case
++              if ( this.isLocal ) {
++                      return createActiveXHR();
++              }
++
++              // Support: IE 9-11
++              // IE seems to error on cross-domain PATCH requests when ActiveX XHR
++              // is used. In IE 9+ always use the native XHR.
++              // Note: this condition won't catch Edge as it doesn't define
++              // document.documentMode but it also doesn't support ActiveX so it won't
++              // reach this code.
++              if ( document.documentMode > 8 ) {
++                      return createStandardXHR();
++              }
++
++              // Support: IE<9
++              // oldIE XHR does not support non-RFC2616 methods (#13240)
++              // See http://msdn.microsoft.com/en-us/library/ie/ms536648(v=vs.85).aspx
++              // and http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9
++              // Although this check for six methods instead of eight
++              // since IE also does not support "trace" and "connect"
++              return /^(get|post|head|put|delete|options)$/i.test( this.type ) &&
++                      createStandardXHR() || createActiveXHR();
++      } :
++
++      // For all other browsers, use the standard XMLHttpRequest object
++      createStandardXHR;
++
++var xhrId = 0,
++      xhrCallbacks = {},
++      xhrSupported = jQuery.ajaxSettings.xhr();
++
++// Support: IE<10
++// Open requests must be manually aborted on unload (#5280)
++// See https://support.microsoft.com/kb/2856746 for more info
++if ( window.attachEvent ) {
++      window.attachEvent( "onunload", function() {
++              for ( var key in xhrCallbacks ) {
++                      xhrCallbacks[ key ]( undefined, true );
++              }
++      } );
++}
++
++// Determine support properties
++support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
++xhrSupported = support.ajax = !!xhrSupported;
++
++// Create transport if the browser can provide an xhr
++if ( xhrSupported ) {
++
++      jQuery.ajaxTransport( function( options ) {
++
++              // Cross domain only allowed if supported through XMLHttpRequest
++              if ( !options.crossDomain || support.cors ) {
++
++                      var callback;
++
++                      return {
++                              send: function( headers, complete ) {
++                                      var i,
++                                              xhr = options.xhr(),
++                                              id = ++xhrId;
++
++                                      // Open the socket
++                                      xhr.open(
++                                              options.type,
++                                              options.url,
++                                              options.async,
++                                              options.username,
++                                              options.password
++                                      );
++
++                                      // Apply custom fields if provided
++                                      if ( options.xhrFields ) {
++                                              for ( i in options.xhrFields ) {
++                                                      xhr[ i ] = options.xhrFields[ i ];
++                                              }
++                                      }
++
++                                      // Override mime type if needed
++                                      if ( options.mimeType && xhr.overrideMimeType ) {
++                                              xhr.overrideMimeType( options.mimeType );
++                                      }
++
++                                      // X-Requested-With header
++                                      // For cross-domain requests, seeing as conditions for a preflight are
++                                      // akin to a jigsaw puzzle, we simply never set it to be sure.
++                                      // (it can always be set on a per-request basis or even using ajaxSetup)
++                                      // For same-domain requests, won't change header if already provided.
++                                      if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) {
++                                              headers[ "X-Requested-With" ] = "XMLHttpRequest";
++                                      }
++
++                                      // Set headers
++                                      for ( i in headers ) {
++
++                                              // Support: IE<9
++                                              // IE's ActiveXObject throws a 'Type Mismatch' exception when setting
++                                              // request header to a null-value.
++                                              //
++                                              // To keep consistent with other XHR implementations, cast the value
++                                              // to string and ignore `undefined`.
++                                              if ( headers[ i ] !== undefined ) {
++                                                      xhr.setRequestHeader( i, headers[ i ] + "" );
++                                              }
++                                      }
++
++                                      // Do send the request
++                                      // This may raise an exception which is actually
++                                      // handled in jQuery.ajax (so no try/catch here)
++                                      xhr.send( ( options.hasContent && options.data ) || null );
++
++                                      // Listener
++                                      callback = function( _, isAbort ) {
++                                              var status, statusText, responses;
++
++                                              // Was never called and is aborted or complete
++                                              if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
++
++                                                      // Clean up
++                                                      delete xhrCallbacks[ id ];
++                                                      callback = undefined;
++                                                      xhr.onreadystatechange = jQuery.noop;
++
++                                                      // Abort manually if needed
++                                                      if ( isAbort ) {
++                                                              if ( xhr.readyState !== 4 ) {
++                                                                      xhr.abort();
++                                                              }
++                                                      } else {
++                                                              responses = {};
++                                                              status = xhr.status;
++
++                                                              // Support: IE<10
++                                                              // Accessing binary-data responseText throws an exception
++                                                              // (#11426)
++                                                              if ( typeof xhr.responseText === "string" ) {
++                                                                      responses.text = xhr.responseText;
++                                                              }
++
++                                                              // Firefox throws an exception when accessing
++                                                              // statusText for faulty cross-domain requests
++                                                              try {
++                                                                      statusText = xhr.statusText;
++                                                              } catch ( e ) {
++
++                                                                      // We normalize with Webkit giving an empty statusText
++                                                                      statusText = "";
++                                                              }
++
++                                                              // Filter status for non standard behaviors
++
++                                                              // If the request is local and we have data: assume a success
++                                                              // (success with no data won't get notified, that's the best we
++                                                              // can do given current implementations)
++                                                              if ( !status && options.isLocal && !options.crossDomain ) {
++                                                                      status = responses.text ? 200 : 404;
++
++                                                              // IE - #1450: sometimes returns 1223 when it should be 204
++                                                              } else if ( status === 1223 ) {
++                                                                      status = 204;
++                                                              }
++                                                      }
++                                              }
++
++                                              // Call complete if needed
++                                              if ( responses ) {
++                                                      complete( status, statusText, responses, xhr.getAllResponseHeaders() );
++                                              }
++                                      };
++
++                                      // Do send the request
++                                      // `xhr.send` may raise an exception, but it will be
++                                      // handled in jQuery.ajax (so no try/catch here)
++                                      if ( !options.async ) {
++
++                                              // If we're in sync mode we fire the callback
++                                              callback();
++                                      } else if ( xhr.readyState === 4 ) {
++
++                                              // (IE6 & IE7) if it's in cache and has been
++                                              // retrieved directly we need to fire the callback
++                                              window.setTimeout( callback );
++                                      } else {
++
++                                              // Register the callback, but delay it in case `xhr.send` throws
++                                              // Add to the list of active xhr callbacks
++                                              xhr.onreadystatechange = xhrCallbacks[ id ] = callback;
++                                      }
++                              },
++
++                              abort: function() {
++                                      if ( callback ) {
++                                              callback( undefined, true );
++                                      }
++                              }
++                      };
++              }
++      } );
++}
++
++// Functions to create xhrs
++function createStandardXHR() {
++      try {
++              return new window.XMLHttpRequest();
++      } catch ( e ) {}
++}
++
++function createActiveXHR() {
++      try {
++              return new window.ActiveXObject( "Microsoft.XMLHTTP" );
++      } catch ( e ) {}
++}
++
++
++
++
++// Install script dataType
++jQuery.ajaxSetup( {
++      accepts: {
++              script: "text/javascript, application/javascript, " +
++                      "application/ecmascript, application/x-ecmascript"
++      },
++      contents: {
++              script: /\b(?:java|ecma)script\b/
++      },
++      converters: {
++              "text script": function( text ) {
++                      jQuery.globalEval( text );
++                      return text;
++              }
++      }
++} );
++
++// Handle cache's special case and global
++jQuery.ajaxPrefilter( "script", function( s ) {
++      if ( s.cache === undefined ) {
++              s.cache = false;
++      }
++      if ( s.crossDomain ) {
++              s.type = "GET";
++              s.global = false;
++      }
++} );
++
++// Bind script tag hack transport
++jQuery.ajaxTransport( "script", function( s ) {
++
++      // This transport only deals with cross domain requests
++      if ( s.crossDomain ) {
++
++              var script,
++                      head = document.head || jQuery( "head" )[ 0 ] || document.documentElement;
++
++              return {
++
++                      send: function( _, callback ) {
++
++                              script = document.createElement( "script" );
++
++                              script.async = true;
++
++                              if ( s.scriptCharset ) {
++                                      script.charset = s.scriptCharset;
++                              }
++
++                              script.src = s.url;
++
++                              // Attach handlers for all browsers
++                              script.onload = script.onreadystatechange = function( _, isAbort ) {
++
++                                      if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {
++
++                                              // Handle memory leak in IE
++                                              script.onload = script.onreadystatechange = null;
++
++                                              // Remove the script
++                                              if ( script.parentNode ) {
++                                                      script.parentNode.removeChild( script );
++                                              }
++
++                                              // Dereference the script
++                                              script = null;
++
++                                              // Callback if not abort
++                                              if ( !isAbort ) {
++                                                      callback( 200, "success" );
++                                              }
++                                      }
++                              };
++
++                              // Circumvent IE6 bugs with base elements (#2709 and #4378) by prepending
++                              // Use native DOM manipulation to avoid our domManip AJAX trickery
++                              head.insertBefore( script, head.firstChild );
++                      },
++
++                      abort: function() {
++                              if ( script ) {
++                                      script.onload( undefined, true );
++                              }
++                      }
++              };
++      }
++} );
++
++
++
++
++var oldCallbacks = [],
++      rjsonp = /(=)\?(?=&|$)|\?\?/;
++
++// Default jsonp settings
++jQuery.ajaxSetup( {
++      jsonp: "callback",
++      jsonpCallback: function() {
++              var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) );
++              this[ callback ] = true;
++              return callback;
++      }
++} );
++
++// Detect, normalize options and install callbacks for jsonp requests
++jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
++
++      var callbackName, overwritten, responseContainer,
++              jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?
++                      "url" :
++                      typeof s.data === "string" &&
++                              ( s.contentType || "" )
++                                      .indexOf( "application/x-www-form-urlencoded" ) === 0 &&
++                              rjsonp.test( s.data ) && "data"
++              );
++
++      // Handle iff the expected data type is "jsonp" or we have a parameter to set
++      if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) {
++
++              // Get callback name, remembering preexisting value associated with it
++              callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ?
++                      s.jsonpCallback() :
++                      s.jsonpCallback;
++
++              // Insert callback into url or form data
++              if ( jsonProp ) {
++                      s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName );
++              } else if ( s.jsonp !== false ) {
++                      s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
++              }
++
++              // Use data converter to retrieve json after script execution
++              s.converters[ "script json" ] = function() {
++                      if ( !responseContainer ) {
++                              jQuery.error( callbackName + " was not called" );
++                      }
++                      return responseContainer[ 0 ];
++              };
++
++              // force json dataType
++              s.dataTypes[ 0 ] = "json";
++
++              // Install callback
++              overwritten = window[ callbackName ];
++              window[ callbackName ] = function() {
++                      responseContainer = arguments;
++              };
++
++              // Clean-up function (fires after converters)
++              jqXHR.always( function() {
++
++                      // If previous value didn't exist - remove it
++                      if ( overwritten === undefined ) {
++                              jQuery( window ).removeProp( callbackName );
++
++                      // Otherwise restore preexisting value
++                      } else {
++                              window[ callbackName ] = overwritten;
++                      }
++
++                      // Save back as free
++                      if ( s[ callbackName ] ) {
++
++                              // make sure that re-using the options doesn't screw things around
++                              s.jsonpCallback = originalSettings.jsonpCallback;
++
++                              // save the callback name for future use
++                              oldCallbacks.push( callbackName );
++                      }
++
++                      // Call if it was a function and we have a response
++                      if ( responseContainer && jQuery.isFunction( overwritten ) ) {
++                              overwritten( responseContainer[ 0 ] );
++                      }
++
++                      responseContainer = overwritten = undefined;
++              } );
++
++              // Delegate to script
++              return "script";
++      }
++} );
++
++
++
++
++// 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
++jQuery.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 = buildFragment( [ data ], context, scripts );
++
++      if ( scripts && scripts.length ) {
++              jQuery( scripts ).remove();
++      }
++
++      return jQuery.merge( [], parsed.childNodes );
++};
++
++
++// Keep a copy of the old load method
++var _load = jQuery.fn.load;
++
++/**
++ * Load a url into a page
++ */
++jQuery.fn.load = function( url, params, callback ) {
++      if ( typeof url !== "string" && _load ) {
++              return _load.apply( this, arguments );
++      }
++
++      var selector, type, response,
++              self = this,
++              off = url.indexOf( " " );
++
++      if ( off > -1 ) {
++              selector = jQuery.trim( url.slice( off, url.length ) );
++              url = url.slice( 0, off );
++      }
++
++      // If it's a function
++      if ( jQuery.isFunction( params ) ) {
++
++              // We assume that it's the callback
++              callback = params;
++              params = undefined;
++
++      // Otherwise, build a param string
++      } else if ( params && typeof params === "object" ) {
++              type = "POST";
++      }
++
++      // If we have elements to modify, make the request
++      if ( self.length > 0 ) {
++              jQuery.ajax( {
++                      url: url,
++
++                      // If "type" variable is undefined, then "GET" method will be used.
++                      // Make value of this field explicit since
++                      // user can override it through ajaxSetup method
++                      type: type || "GET",
++                      dataType: "html",
++                      data: params
++              } ).done( function( responseText ) {
++
++                      // Save response for use in complete callback
++                      response = arguments;
++
++                      self.html( selector ?
++
++                              // If a selector was specified, locate the right elements in a dummy div
++                              // Exclude scripts to avoid IE 'Permission Denied' errors
++                              jQuery( "<div>" ).append( jQuery.parseHTML( responseText ) ).find( selector ) :
++
++                              // Otherwise use the full result
++                              responseText );
++
++              // If the request succeeds, this function gets "data", "status", "jqXHR"
++              // but they are ignored because response was set above.
++              // If it fails, this function gets "jqXHR", "status", "error"
++              } ).always( callback && function( jqXHR, status ) {
++                      self.each( function() {
++                              callback.apply( this, response || [ jqXHR.responseText, status, jqXHR ] );
++                      } );
++              } );
++      }
++
++      return this;
++};
++
++
++
++
++// Attach a bunch of functions for handling common AJAX events
++jQuery.each( [
++      "ajaxStart",
++      "ajaxStop",
++      "ajaxComplete",
++      "ajaxError",
++      "ajaxSuccess",
++      "ajaxSend"
++], function( i, type ) {
++      jQuery.fn[ type ] = function( fn ) {
++              return this.on( type, fn );
++      };
++} );
++
++
++
++
++jQuery.expr.filters.animated = function( elem ) {
++      return jQuery.grep( jQuery.timers, function( fn ) {
++              return elem === fn.elem;
++      } ).length;
++};
++
++
++
++
++
++/**
++ * Gets a window from an element
++ */
++function getWindow( elem ) {
++      return jQuery.isWindow( elem ) ?
++              elem :
++              elem.nodeType === 9 ?
++                      elem.defaultView || elem.parentWindow :
++                      false;
++}
++
++jQuery.offset = {
++      setOffset: function( elem, options, i ) {
++              var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
++                      position = jQuery.css( elem, "position" ),
++                      curElem = jQuery( elem ),
++                      props = {};
++
++              // set position first, in-case top/left are set even on static elem
++              if ( position === "static" ) {
++                      elem.style.position = "relative";
++              }
++
++              curOffset = curElem.offset();
++              curCSSTop = jQuery.css( elem, "top" );
++              curCSSLeft = jQuery.css( elem, "left" );
++              calculatePosition = ( position === "absolute" || position === "fixed" ) &&
++                      jQuery.inArray( "auto", [ curCSSTop, curCSSLeft ] ) > -1;
++
++              // need to be able to calculate position if either top or left
++              // is auto and position is either absolute or fixed
++              if ( calculatePosition ) {
++                      curPosition = curElem.position();
++                      curTop = curPosition.top;
++                      curLeft = curPosition.left;
++              } else {
++                      curTop = parseFloat( curCSSTop ) || 0;
++                      curLeft = parseFloat( curCSSLeft ) || 0;
++              }
++
++              if ( jQuery.isFunction( options ) ) {
++
++                      // Use jQuery.extend here to allow modification of coordinates argument (gh-1848)
++                      options = options.call( elem, i, jQuery.extend( {}, curOffset ) );
++              }
++
++              if ( options.top != null ) {
++                      props.top = ( options.top - curOffset.top ) + curTop;
++              }
++              if ( options.left != null ) {
++                      props.left = ( options.left - curOffset.left ) + curLeft;
++              }
++
++              if ( "using" in options ) {
++                      options.using.call( elem, props );
++              } else {
++                      curElem.css( props );
++              }
++      }
++};
++
++jQuery.fn.extend( {
++      offset: function( options ) {
++              if ( arguments.length ) {
++                      return options === undefined ?
++                              this :
++                              this.each( function( i ) {
++                                      jQuery.offset.setOffset( this, options, i );
++                              } );
++              }
++
++              var docElem, win,
++                      box = { top: 0, left: 0 },
++                      elem = this[ 0 ],
++                      doc = elem && elem.ownerDocument;
++
++              if ( !doc ) {
++                      return;
++              }
++
++              docElem = doc.documentElement;
++
++              // Make sure it's not a disconnected DOM node
++              if ( !jQuery.contains( docElem, elem ) ) {
++                      return box;
++              }
++
++              // If we don't have gBCR, just use 0,0 rather than error
++              // BlackBerry 5, iOS 3 (original iPhone)
++              if ( typeof elem.getBoundingClientRect !== "undefined" ) {
++                      box = elem.getBoundingClientRect();
++              }
++              win = getWindow( doc );
++              return {
++                      top: box.top  + ( win.pageYOffset || docElem.scrollTop )  - ( docElem.clientTop  || 0 ),
++                      left: box.left + ( win.pageXOffset || docElem.scrollLeft ) - ( docElem.clientLeft || 0 )
++              };
++      },
++
++      position: function() {
++              if ( !this[ 0 ] ) {
++                      return;
++              }
++
++              var offsetParent, offset,
++                      parentOffset = { top: 0, left: 0 },
++                      elem = this[ 0 ];
++
++              // Fixed elements are offset from window (parentOffset = {top:0, left: 0},
++              // because it is its only offset parent
++              if ( jQuery.css( elem, "position" ) === "fixed" ) {
++
++                      // we assume that getBoundingClientRect is available when computed position is fixed
++                      offset = elem.getBoundingClientRect();
++              } else {
++
++                      // Get *real* offsetParent
++                      offsetParent = this.offsetParent();
++
++                      // Get correct offsets
++                      offset = this.offset();
++                      if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) {
++                              parentOffset = offsetParent.offset();
++                      }
++
++                      // Add offsetParent borders
++                      parentOffset.top  += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true );
++                      parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true );
++              }
++
++              // Subtract parent offsets and element margins
++              // note: when an element has margin: auto the offsetLeft and marginLeft
++              // are the same in Safari causing offset.left to incorrectly be 0
++              return {
++                      top:  offset.top  - parentOffset.top - jQuery.css( elem, "marginTop", true ),
++                      left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true )
++              };
++      },
++
++      offsetParent: function() {
++              return this.map( function() {
++                      var offsetParent = this.offsetParent;
++
++                      while ( offsetParent && ( !jQuery.nodeName( offsetParent, "html" ) &&
++                              jQuery.css( offsetParent, "position" ) === "static" ) ) {
++                              offsetParent = offsetParent.offsetParent;
++                      }
++                      return offsetParent || documentElement;
++              } );
++      }
++} );
++
++// Create scrollLeft and scrollTop methods
++jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) {
++      var top = /Y/.test( prop );
++
++      jQuery.fn[ method ] = function( val ) {
++              return access( this, function( elem, method, val ) {
++                      var win = getWindow( elem );
++
++                      if ( val === undefined ) {
++                              return win ? ( prop in win ) ? win[ prop ] :
++                                      win.document.documentElement[ method ] :
++                                      elem[ method ];
++                      }
++
++                      if ( win ) {
++                              win.scrollTo(
++                                      !top ? val : jQuery( win ).scrollLeft(),
++                                      top ? val : jQuery( win ).scrollTop()
++                              );
++
++                      } else {
++                              elem[ method ] = val;
++                      }
++              }, method, val, arguments.length, null );
++      };
++} );
++
++// Support: Safari<7-8+, Chrome<37-44+
++// Add the top/left cssHooks using jQuery.fn.position
++// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
++// getComputedStyle returns percent when specified for top/left/bottom/right
++// rather than make the css module depend on the offset module, we just check for it here
++jQuery.each( [ "top", "left" ], function( i, prop ) {
++      jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition,
++              function( elem, computed ) {
++                      if ( computed ) {
++                              computed = curCSS( elem, prop );
++
++                              // if curCSS returns percentage, fallback to offset
++                              return rnumnonpx.test( computed ) ?
++                                      jQuery( elem ).position()[ prop ] + "px" :
++                                      computed;
++                      }
++              }
++      );
++} );
++
++
++// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
++jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
++      jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name },
++      function( defaultExtra, funcName ) {
++
++              // margin is only for outerHeight, outerWidth
++              jQuery.fn[ funcName ] = function( margin, value ) {
++                      var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
++                              extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
++
++                      return access( this, function( elem, type, value ) {
++                              var doc;
++
++                              if ( jQuery.isWindow( elem ) ) {
++
++                                      // As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there
++                                      // isn't a whole lot we can do. See pull request at this URL for discussion:
++                                      // https://github.com/jquery/jquery/pull/764
++                                      return elem.document.documentElement[ "client" + name ];
++                              }
++
++                              // Get document width or height
++                              if ( elem.nodeType === 9 ) {
++                                      doc = elem.documentElement;
++
++                                      // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height],
++                                      // whichever is greatest
++                                      // unfortunately, this causes bug #3838 in IE6/8 only,
++                                      // but there is currently no good, small way to fix it.
++                                      return Math.max(
++                                              elem.body[ "scroll" + name ], doc[ "scroll" + name ],
++                                              elem.body[ "offset" + name ], doc[ "offset" + name ],
++                                              doc[ "client" + name ]
++                                      );
++                              }
++
++                              return value === undefined ?
++
++                                      // Get width or height on the element, requesting but not forcing parseFloat
++                                      jQuery.css( elem, type, extra ) :
++
++                                      // Set width or height on the element
++                                      jQuery.style( elem, type, value, extra );
++                      }, type, chainable ? margin : undefined, chainable, null );
++              };
++      } );
++} );
++
++
++jQuery.fn.extend( {
++
++      bind: function( types, data, fn ) {
++              return this.on( types, null, data, fn );
++      },
++      unbind: function( types, fn ) {
++              return this.off( types, null, fn );
++      },
++
++      delegate: function( selector, types, data, fn ) {
++              return this.on( types, selector, data, fn );
++      },
++      undelegate: function( selector, types, fn ) {
++
++              // ( namespace ) or ( selector, types [, fn] )
++              return arguments.length === 1 ?
++                      this.off( selector, "**" ) :
++                      this.off( types, selector || "**", fn );
++      }
++} );
++
++// The number of elements contained in the matched element set
++jQuery.fn.size = function() {
++      return this.length;
++};
++
++jQuery.fn.andSelf = jQuery.fn.addBack;
++
++
++
++
++// Register as a named AMD module, since jQuery can be concatenated with other
++// files that may use define, but not via a proper concatenation script that
++// understands anonymous AMD modules. A named AMD is safest and most robust
++// way to register. Lowercase jquery is used because AMD module names are
++// derived from file names, and jQuery is normally delivered in a lowercase
++// file name. Do this after creating the global so that if an AMD module wants
++// to call noConflict to hide this version of jQuery, it will work.
++
++// Note that for maximum portability, libraries that are not jQuery should
++// declare themselves as anonymous modules, and avoid setting a global if an
++// AMD loader is present. jQuery is a special case. For more information, see
++// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon
++
++if ( typeof define === "function" && define.amd ) {
++      define( "jquery", [], function() {
++              return jQuery;
++      } );
++}
++
++
++
++var
++
++      // Map over jQuery in case of overwrite
++      _jQuery = window.jQuery,
++
++      // Map over the $ in case of overwrite
++      _$ = window.$;
++
++jQuery.noConflict = function( deep ) {
++      if ( window.$ === jQuery ) {
++              window.$ = _$;
++      }
++
++      if ( deep && window.jQuery === jQuery ) {
++              window.jQuery = _jQuery;
++      }
++
++      return jQuery;
++};
++
++// Expose jQuery and $ identifiers, even in
++// AMD (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
++// and CommonJS for browser emulators (#13566)
++if ( !noGlobal ) {
++      window.jQuery = window.$ = jQuery;
++}
++
++return jQuery;
++}));
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9b5206bcc60781905d111863bdb6232d90490f33
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10364 @@@
++/*!
++ * jQuery JavaScript Library v3.3.1
++ * https://jquery.com/
++ *
++ * Includes Sizzle.js
++ * https://sizzlejs.com/
++ *
++ * Copyright JS Foundation and other contributors
++ * Released under the MIT license
++ * https://jquery.org/license
++ *
++ * Date: 2018-01-20T17:24Z
++ */
++( 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 );
++      }
++
++// Pass this if window is not defined yet
++} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
++
++// 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";
++
++var arr = [];
++
++var document = window.document;
++
++var getProto = Object.getPrototypeOf;
++
++var slice = arr.slice;
++
++var concat = arr.concat;
++
++var push = arr.push;
++
++var indexOf = arr.indexOf;
++
++var class2type = {};
++
++var toString = class2type.toString;
++
++var hasOwn = class2type.hasOwnProperty;
++
++var fnToString = hasOwn.toString;
++
++var ObjectFunctionString = fnToString.call( Object );
++
++var support = {};
++
++var isFunction = function isFunction( obj ) {
++
++      // Support: Chrome <=57, Firefox <=52
++      // In some browsers, typeof returns "function" for HTML <object> elements
++      // (i.e., `typeof document.createElement( "object" ) === "function"`).
++      // We don't want to classify *any* DOM node as a function.
++      return typeof obj === "function" && typeof obj.nodeType !== "number";
++  };
++
++
++var isWindow = function isWindow( obj ) {
++              return obj != null && obj === obj.window;
++      };
++
++
++
++
++      var preservedScriptAttributes = {
++              type: true,
++              src: true,
++              noModule: true
++      };
++
++      function DOMEval( code, doc, node ) {
++              doc = doc || document;
++
++              var i,
++                      script = doc.createElement( "script" );
++
++              script.text = code;
++              if ( node ) {
++                      for ( i in preservedScriptAttributes ) {
++                              if ( node[ i ] ) {
++                                      script[ i ] = node[ i ];
++                              }
++                      }
++              }
++              doc.head.appendChild( script ).parentNode.removeChild( script );
++      }
++
++
++function toType( obj ) {
++      if ( obj == null ) {
++              return obj + "";
++      }
++
++      // 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
++
++
++
++var
++      version = "3.3.1",
++
++      // Define a local copy of jQuery
++      jQuery = function( selector, context ) {
++
++              // The jQuery object is actually just the init constructor 'enhanced'
++              // Need init if jQuery is called (just allow error to be thrown if not included)
++              return new jQuery.fn.init( selector, context );
++      },
++
++      // Support: Android <=4.0 only
++      // Make sure we trim BOM and NBSP
++      rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;
++
++jQuery.fn = jQuery.prototype = {
++
++      // The current version of jQuery being used
++      jquery: version,
++
++      constructor: jQuery,
++
++      // The default length of a jQuery object is 0
++      length: 0,
++
++      toArray: function() {
++              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 all the elements in a clean array
++              if ( num == null ) {
++                      return slice.call( this );
++              }
++
++              // 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
++      // (returning the new matched element set)
++      pushStack: function( elems ) {
++
++              // Build a new jQuery matched element set
++              var ret = jQuery.merge( this.constructor(), elems );
++
++              // Add the old object onto the stack (as a reference)
++              ret.prevObject = this;
++
++              // Return the newly-formed element set
++              return ret;
++      },
++
++      // Execute a callback for every element in the matched set.
++      each: function( callback ) {
++              return jQuery.each( this, callback );
++      },
++
++      map: function( callback ) {
++              return this.pushStack( jQuery.map( this, function( elem, i ) {
++                      return callback.call( elem, i, elem );
++              } ) );
++      },
++
++      slice: function() {
++              return this.pushStack( slice.apply( this, arguments ) );
++      },
++
++      first: function() {
++              return this.eq( 0 );
++      },
++
++      last: function() {
++              return this.eq( -1 );
++      },
++
++      eq: function( i ) {
++              var len = this.length,
++                      j = +i + ( i < 0 ? len : 0 );
++              return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] );
++      },
++
++      end: function() {
++              return this.prevObject || this.constructor();
++      },
++
++      // For internal use only.
++      // Behaves like an Array's method, not like a jQuery method.
++      push: push,
++      sort: arr.sort,
++      splice: arr.splice
++};
++
++jQuery.extend = jQuery.fn.extend = function() {
++      var options, name, src, copy, copyIsArray, clone,
++              target = arguments[ 0 ] || {},
++              i = 1,
++              length = arguments.length,
++              deep = false;
++
++      // Handle a deep copy situation
++      if ( typeof target === "boolean" ) {
++              deep = target;
++
++              // 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" && !isFunction( target ) ) {
++              target = {};
++      }
++
++      // Extend jQuery itself if only one argument is passed
++      if ( i === length ) {
++              target = this;
++              i--;
++      }
++
++      for ( ; i < length; i++ ) {
++
++              // Only deal with non-null/undefined values
++              if ( ( options = arguments[ i ] ) != null ) {
++
++                      // Extend the base object
++                      for ( name in options ) {
++                              src = target[ name ];
++                              copy = options[ name ];
++
++                              // Prevent never-ending loop
++                              if ( target === copy ) {
++                                      continue;
++                              }
++
++                              // Recurse if we're merging plain objects or arrays
++                              if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
++                                      ( copyIsArray = Array.isArray( copy ) ) ) ) {
++
++                                      if ( copyIsArray ) {
++                                              copyIsArray = false;
++                                              clone = src && Array.isArray( src ) ? src : [];
++
++                                      } else {
++                                              clone = src && jQuery.isPlainObject( src ) ? src : {};
++                                      }
++
++                                      // Never move original objects, clone them
++                                      target[ name ] = jQuery.extend( deep, clone, copy );
++
++                              // Don't bring in undefined values
++                              } else if ( copy !== undefined ) {
++                                      target[ name ] = copy;
++                              }
++                      }
++              }
++      }
++
++      // Return the modified object
++      return target;
++};
++
++jQuery.extend( {
++
++      // Unique for each copy of jQuery on the page
++      expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),
++
++      // Assume jQuery is ready without the ready module
++      isReady: true,
++
++      error: function( msg ) {
++              throw new Error( msg );
++      },
++
++      noop: function() {},
++
++      isPlainObject: function( obj ) {
++              var proto, Ctor;
++
++              // Detect obvious negatives
++              // Use toString instead of jQuery.type to catch host objects
++              if ( !obj || toString.call( obj ) !== "[object Object]" ) {
++                      return false;
++              }
++
++              proto = getProto( obj );
++
++              // Objects with no prototype (e.g., `Object.create( null )`) are plain
++              if ( !proto ) {
++                      return true;
++              }
++
++              // 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 ) {
++
++              /* eslint-disable no-unused-vars */
++              // See https://github.com/eslint/eslint/issues/6125
++              var name;
++
++              for ( name in obj ) {
++                      return false;
++              }
++              return true;
++      },
++
++      // Evaluates a script in a global context
++      globalEval: function( code ) {
++              DOMEval( code );
++      },
++
++      each: function( obj, callback ) {
++              var length, i = 0;
++
++              if ( isArrayLike( obj ) ) {
++                      length = obj.length;
++                      for ( ; i < length; i++ ) {
++                              if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
++                                      break;
++                              }
++                      }
++              } else {
++                      for ( i in obj ) {
++                              if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
++                                      break;
++                              }
++                      }
++              }
++
++              return obj;
++      },
++
++      // Support: Android <=4.0 only
++      trim: function( text ) {
++              return text == null ?
++                      "" :
++                      ( text + "" ).replace( rtrim, "" );
++      },
++
++      // results is for internal usage only
++      makeArray: function( arr, results ) {
++              var ret = results || [];
++
++              if ( arr != null ) {
++                      if ( isArrayLike( Object( arr ) ) ) {
++                              jQuery.merge( ret,
++                                      typeof arr === "string" ?
++                                      [ arr ] : arr
++                              );
++                      } else {
++                              push.call( ret, arr );
++                      }
++              }
++
++              return ret;
++      },
++
++      inArray: function( elem, arr, i ) {
++              return arr == null ? -1 : indexOf.call( arr, elem, i );
++      },
++
++      // 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 ( ; j < len; j++ ) {
++                      first[ i++ ] = second[ j ];
++              }
++
++              first.length = i;
++
++              return first;
++      },
++
++      grep: function( elems, callback, invert ) {
++              var callbackInverse,
++                      matches = [],
++                      i = 0,
++                      length = elems.length,
++                      callbackExpect = !invert;
++
++              // Go through the array, only saving the items
++              // that pass the validator function
++              for ( ; i < length; i++ ) {
++                      callbackInverse = !callback( elems[ i ], i );
++                      if ( callbackInverse !== callbackExpect ) {
++                              matches.push( elems[ i ] );
++                      }
++              }
++
++              return matches;
++      },
++
++      // arg is for internal usage only
++      map: function( elems, callback, arg ) {
++              var length, value,
++                      i = 0,
++                      ret = [];
++
++              // 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.push( value );
++                              }
++                      }
++
++              // Go through every key on the object,
++              } else {
++                      for ( i in elems ) {
++                              value = callback( elems[ i ], i, arg );
++
++                              if ( value != null ) {
++                                      ret.push( value );
++                              }
++                      }
++              }
++
++              // Flatten any nested arrays
++              return concat.apply( [], ret );
++      },
++
++      // A global GUID counter for objects
++      guid: 1,
++
++      // jQuery.support is not used in Core but other projects attach their
++      // properties to it so it needs to exist.
++      support: support
++} );
++
++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 Symbol".split( " " ),
++function( i, name ) {
++      class2type[ "[object " + name + "]" ] = name.toLowerCase();
++} );
++
++function isArrayLike( obj ) {
++
++      // 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 ( isFunction( obj ) || isWindow( obj ) ) {
++              return false;
++      }
++
++      return type === "array" || length === 0 ||
++              typeof length === "number" && length > 0 && ( length - 1 ) in obj;
++}
++var Sizzle =
++/*!
++ * Sizzle CSS Selector Engine v2.3.3
++ * https://sizzlejs.com/
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license
++ * http://jquery.org/license
++ *
++ * Date: 2016-08-08
++ */
++(function( window ) {
++
++var i,
++      support,
++      Expr,
++      getText,
++      isXML,
++      tokenize,
++      compile,
++      select,
++      outermostContext,
++      sortInput,
++      hasDuplicate,
++
++      // Local document vars
++      setDocument,
++      document,
++      docElem,
++      documentIsHTML,
++      rbuggyQSA,
++      rbuggyMatches,
++      matches,
++      contains,
++
++      // Instance-specific data
++      expando = "sizzle" + 1 * new Date(),
++      preferredDoc = window.document,
++      dirruns = 0,
++      done = 0,
++      classCache = createCache(),
++      tokenCache = createCache(),
++      compilerCache = createCache(),
++      sortOrder = function( a, b ) {
++              if ( a === b ) {
++                      hasDuplicate = true;
++              }
++              return 0;
++      },
++
++      // Instance methods
++      hasOwn = ({}).hasOwnProperty,
++      arr = [],
++      pop = arr.pop,
++      push_native = arr.push,
++      push = arr.push,
++      slice = arr.slice,
++      // Use a stripped-down indexOf as it's faster than native
++      // https://jsperf.com/thor-indexof-vs-for/5
++      indexOf = function( list, elem ) {
++              var i = 0,
++                      len = list.length;
++              for ( ; i < len; i++ ) {
++                      if ( list[i] === elem ) {
++                              return i;
++                      }
++              }
++              return -1;
++      },
++
++      booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
++
++      // Regular expressions
++
++      // http://www.w3.org/TR/css3-selectors/#whitespace
++      whitespace = "[\\x20\\t\\r\\n\\f]",
++
++      // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
++      identifier = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+",
++
++      // 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 + "*" ),
++
++      rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ),
++
++      rpseudo = new RegExp( pseudos ),
++      ridentifier = new RegExp( "^" + identifier + "$" ),
++
++      matchExpr = {
++              "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 +
++                      "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
++                      "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
++              "bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
++              // For use in libraries implementing .is()
++              // We use this for POS matching in `select`
++              "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
++                      whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
++      },
++
++      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-]+))$/,
++
++      rsibling = /[+~]/,
++
++      // CSS escapes
++      // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
++      runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
++      funescape = function( _, escaped, escapedWhitespace ) {
++              var high = "0x" + escaped - 0x10000;
++              // NaN means non-codepoint
++              // Support: Firefox<24
++              // Workaround erroneous numeric interpretation of +"0x"
++              return high !== high || escapedWhitespace ?
++                      escaped :
++                      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();
++      },
++
++      disabledAncestor = addCombinator(
++              function( elem ) {
++                      return elem.disabled === true && ("form" in elem || "label" in elem);
++              },
++              { dir: "parentNode", next: "legend" }
++      );
++
++// Optimize for push.apply( _, NodeList )
++try {
++      push.apply(
++              (arr = slice.call( preferredDoc.childNodes )),
++              preferredDoc.childNodes
++      );
++      // Support: Android<4.0
++      // Detect silently failing push.apply
++      arr[ preferredDoc.childNodes.length ].nodeType;
++} catch ( e ) {
++      push = { apply: arr.length ?
++
++              // Leverage slice if possible
++              function( target, els ) {
++                      push_native.apply( target, slice.call(els) );
++              } :
++
++              // Support: IE<9
++              // Otherwise append directly
++              function( target, els ) {
++                      var j = target.length,
++                              i = 0;
++                      // Can't trust NodeList.length
++                      while ( (target[j++] = els[i++]) ) {}
++                      target.length = j - 1;
++              }
++      };
++}
++
++function Sizzle( selector, context, results, seed ) {
++      var m, i, elem, nid, match, groups, newSelector,
++              newContext = context && context.ownerDocument,
++
++              // nodeType defaults to 9, since context defaults to document
++              nodeType = context ? context.nodeType : 9;
++
++      results = results || [];
++
++      // Return early from calls with invalid selector or context
++      if ( typeof selector !== "string" || !selector ||
++              nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) {
++
++              return results;
++      }
++
++      // Try to shortcut find operations (as opposed to filters) in HTML documents
++      if ( !seed ) {
++
++              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 {
++
++                                              // 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;
++                                              }
++                                      }
++
++                              // Type selector
++                              } else if ( match[2] ) {
++                                      push.apply( results, context.getElementsByTagName( selector ) );
++                                      return results;
++
++                              // Class selector
++                              } else if ( (m = match[3]) && support.getElementsByClassName &&
++                                      context.getElementsByClassName ) {
++
++                                      push.apply( results, context.getElementsByClassName( m ) );
++                                      return results;
++                              }
++                      }
++
++                      // Take advantage of querySelectorAll
++                      if ( support.qsa &&
++                              !compilerCache[ selector + " " ] &&
++                              (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
++
++                              if ( nodeType !== 1 ) {
++                                      newContext = context;
++                                      newSelector = selector;
++
++                              // qSA looks outside Element context, which is not what we want
++                              // Thanks to Andrew Dupont for this workaround technique
++                              // Support: IE <=8
++                              // Exclude object elements
++                              } else if ( context.nodeName.toLowerCase() !== "object" ) {
++
++                                      // 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;
++                              }
++
++                              if ( newSelector ) {
++                                      try {
++                                              push.apply( results,
++                                                      newContext.querySelectorAll( newSelector )
++                                              );
++                                              return results;
++                                      } catch ( qsaError ) {
++                                      } finally {
++                                              if ( nid === expando ) {
++                                                      context.removeAttribute( "id" );
++                                              }
++                                      }
++                              }
++                      }
++              }
++      }
++
++      // All others
++      return select( selector.replace( rtrim, "$1" ), context, results, seed );
++}
++
++/**
++ * Create key-value caches of limited size
++ * @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
++ */
++function createCache() {
++      var keys = [];
++
++      function cache( key, value ) {
++              // Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
++              if ( keys.push( key + " " ) > Expr.cacheLength ) {
++                      // Only keep the most recent entries
++                      delete cache[ keys.shift() ];
++              }
++              return (cache[ key + " " ] = value);
++      }
++      return cache;
++}
++
++/**
++ * Mark a function for special use by Sizzle
++ * @param {Function} fn The function to mark
++ */
++function markFunction( fn ) {
++      fn[ expando ] = true;
++      return fn;
++}
++
++/**
++ * Support testing using an element
++ * @param {Function} fn Passed the created element and returns a boolean result
++ */
++function assert( fn ) {
++      var el = document.createElement("fieldset");
++
++      try {
++              return !!fn( el );
++      } catch (e) {
++              return false;
++      } finally {
++              // Remove from its parent by default
++              if ( el.parentNode ) {
++                      el.parentNode.removeChild( el );
++              }
++              // release memory in IE
++              el = null;
++      }
++}
++
++/**
++ * Adds the same handler for all of the specified attrs
++ * @param {String} attrs Pipe-separated list of attributes
++ * @param {Function} handler The method that will be applied
++ */
++function addHandle( attrs, handler ) {
++      var arr = attrs.split("|"),
++              i = arr.length;
++
++      while ( i-- ) {
++              Expr.attrHandle[ arr[i] ] = handler;
++      }
++}
++
++/**
++ * Checks document order of two siblings
++ * @param {Element} a
++ * @param {Element} b
++ * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
++ */
++function siblingCheck( a, b ) {
++      var cur = b && a,
++              diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
++                      a.sourceIndex - b.sourceIndex;
++
++      // Use IE sourceIndex if available on both nodes
++      if ( diff ) {
++              return diff;
++      }
++
++      // Check if b follows a
++      if ( cur ) {
++              while ( (cur = cur.nextSibling) ) {
++                      if ( cur === b ) {
++                              return -1;
++                      }
++              }
++      }
++
++      return a ? 1 : -1;
++}
++
++/**
++ * Returns a function to use in pseudos for input types
++ * @param {String} type
++ */
++function createInputPseudo( type ) {
++      return function( elem ) {
++              var name = elem.nodeName.toLowerCase();
++              return name === "input" && elem.type === type;
++      };
++}
++
++/**
++ * Returns a function to use in pseudos for buttons
++ * @param {String} type
++ */
++function createButtonPseudo( type ) {
++      return function( elem ) {
++              var name = elem.nodeName.toLowerCase();
++              return (name === "input" || name === "button") && elem.type === 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 &&
++                                              disabledAncestor( 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
++ */
++function createPositionalPseudo( fn ) {
++      return markFunction(function( argument ) {
++              argument = +argument;
++              return markFunction(function( seed, matches ) {
++                      var j,
++                              matchIndexes = fn( [], seed.length, argument ),
++                              i = matchIndexes.length;
++
++                      // Match elements found at the specified indexes
++                      while ( i-- ) {
++                              if ( seed[ (j = matchIndexes[i]) ] ) {
++                                      seed[j] = !(matches[j] = seed[j]);
++                              }
++                      }
++              });
++      });
++}
++
++/**
++ * 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
++ */
++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 ) {
++      // 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;
++};
++
++/**
++ * 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 hasCompare, subWindow,
++              doc = node ? node.ownerDocument || node : preferredDoc;
++
++      // Return early if doc is invalid or already selected
++      if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
++              return document;
++      }
++
++      // Update global variables
++      document = doc;
++      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( el ) {
++              el.className = "i";
++              return !el.getAttribute("className");
++      });
++
++      /* getElement(s)By*
++      ---------------------------------------------------------------------- */
++
++      // Check if getElementsByTagName("*") returns only elements
++      support.getElementsByTagName = assert(function( el ) {
++              el.appendChild( document.createComment("") );
++              return !el.getElementsByTagName("*").length;
++      });
++
++      // Support: 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 programmatically-set names,
++      // so use a roundabout getElementsByName test
++      support.getById = assert(function( el ) {
++              docElem.appendChild( el ).id = expando;
++              return !document.getElementsByName || !document.getElementsByName( expando ).length;
++      });
++
++      // ID filter and find
++      if ( support.getById ) {
++              Expr.filter["ID"] = function( id ) {
++                      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 {
++              Expr.filter["ID"] =  function( id ) {
++                      var attrId = id.replace( runescape, funescape );
++                      return function( elem ) {
++                              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 !== "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
++                      if ( tag === "*" ) {
++                              while ( (elem = results[i++]) ) {
++                                      if ( elem.nodeType === 1 ) {
++                                              tmp.push( elem );
++                                      }
++                              }
++
++                              return tmp;
++                      }
++                      return results;
++              };
++
++      // Class
++      Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
++              if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) {
++                      return context.getElementsByClassName( className );
++              }
++      };
++
++      /* QSA/matchesSelector
++      ---------------------------------------------------------------------- */
++
++      // QSA and matchesSelector support
++
++      // matchesSelector(:active) reports false when true (IE9/Opera 11.5)
++      rbuggyMatches = [];
++
++      // qSa(:focus) reports false when true (Chrome 21)
++      // 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 https://bugs.jquery.com/ticket/13378
++      rbuggyQSA = [];
++
++      if ( (support.qsa = rnative.test( document.querySelectorAll )) ) {
++              // Build QSA regex
++              // Regex strategy adopted from Diego Perini
++              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
++                      // https://bugs.jquery.com/ticket/12359
++                      docElem.appendChild( el ).innerHTML = "<a id='" + expando + "'></a>" +
++                              "<select id='" + expando + "-\r\\' msallowcapture=''>" +
++                              "<option selected=''></option></select>";
++
++                      // Support: IE8, Opera 11-12.16
++                      // Nothing should be selected when empty strings follow ^= or $= or *=
++                      // The test attribute must be unknown in Opera but "safe" for WinRT
++                      // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section
++                      if ( el.querySelectorAll("[msallowcapture^='']").length ) {
++                              rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
++                      }
++
++                      // Support: IE8
++                      // Boolean attributes and "value" are not treated correctly
++                      if ( !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 ( !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( el ) {
++                      el.innerHTML = "<a href='' disabled='disabled'></a>" +
++                              "<select disabled='disabled'><option/></select>";
++
++                      // Support: Windows 8 Native Apps
++                      // The type and name attributes are restricted during .innerHTML assignment
++                      var input = document.createElement("input");
++                      input.setAttribute( "type", "hidden" );
++                      el.appendChild( input ).setAttribute( "name", "D" );
++
++                      // 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 ( 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
++                      el.querySelectorAll("*,:x");
++                      rbuggyQSA.push(",.*:");
++              });
++      }
++
++      if ( (support.matchesSelector = rnative.test( (matches = docElem.matches ||
++              docElem.webkitMatchesSelector ||
++              docElem.mozMatchesSelector ||
++              docElem.oMatchesSelector ||
++              docElem.msMatchesSelector) )) ) {
++
++              assert(function( el ) {
++                      // Check to see if it's possible to do matchesSelector
++                      // on a disconnected node (IE 9)
++                      support.disconnectedMatch = matches.call( el, "*" );
++
++                      // This should fail with an exception
++                      // Gecko does not error, returns false instead
++                      matches.call( el, "[s!='']:x" );
++                      rbuggyMatches.push( "!=", pseudos );
++              });
++      }
++
++      rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
++      rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );
++
++      /* Contains
++      ---------------------------------------------------------------------- */
++      hasCompare = rnative.test( docElem.compareDocumentPosition );
++
++      // Element contains another
++      // Purposefully self-exclusive
++      // As in, an element does not contain itself
++      contains = hasCompare || rnative.test( docElem.contains ) ?
++              function( a, b ) {
++                      var adown = a.nodeType === 9 ? a.documentElement : a,
++                              bup = b && b.parentNode;
++                      return a === bup || !!( bup && bup.nodeType === 1 && (
++                              adown.contains ?
++                                      adown.contains( bup ) :
++                                      a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
++                      ));
++              } :
++              function( a, b ) {
++                      if ( b ) {
++                              while ( (b = b.parentNode) ) {
++                                      if ( b === a ) {
++                                              return true;
++                                      }
++                              }
++                      }
++                      return false;
++              };
++
++      /* Sorting
++      ---------------------------------------------------------------------- */
++
++      // Document order sorting
++      sortOrder = hasCompare ?
++      function( a, b ) {
++
++              // Flag for duplicate removal
++              if ( a === b ) {
++                      hasDuplicate = true;
++                      return 0;
++              }
++
++              // Sort on method existence if only one input has compareDocumentPosition
++              var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
++              if ( compare ) {
++                      return compare;
++              }
++
++              // Calculate position if both inputs belong to the same document
++              compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ?
++                      a.compareDocumentPosition( b ) :
++
++                      // 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;
++                      }
++
++                      // Maintain original order
++                      return sortInput ?
++                              ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
++                              0;
++              }
++
++              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,
++                      bup = b.parentNode,
++                      ap = [ a ],
++                      bp = [ b ];
++
++              // Parentless nodes are either documents or disconnected
++              if ( !aup || !bup ) {
++                      return a === document ? -1 :
++                              b === document ? 1 :
++                              aup ? -1 :
++                              bup ? 1 :
++                              sortInput ?
++                              ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
++                              0;
++
++              // If the nodes are siblings, we can do a quick check
++              } else if ( aup === bup ) {
++                      return siblingCheck( a, b );
++              }
++
++              // Otherwise we need full lists of their ancestors for comparison
++              cur = a;
++              while ( (cur = cur.parentNode) ) {
++                      ap.unshift( cur );
++              }
++              cur = b;
++              while ( (cur = cur.parentNode) ) {
++                      bp.unshift( cur );
++              }
++
++              // Walk down the tree looking for a discrepancy
++              while ( ap[i] === bp[i] ) {
++                      i++;
++              }
++
++              return i ?
++                      // Do a sibling check if the nodes have a common ancestor
++                      siblingCheck( ap[i], bp[i] ) :
++
++                      // Otherwise nodes in our document sort first
++                      ap[i] === preferredDoc ? -1 :
++                      bp[i] === preferredDoc ? 1 :
++                      0;
++      };
++
++      return document;
++};
++
++Sizzle.matches = function( expr, elements ) {
++      return Sizzle( expr, null, null, elements );
++};
++
++Sizzle.matchesSelector = function( elem, expr ) {
++      // Set document vars if needed
++      if ( ( elem.ownerDocument || elem ) !== document ) {
++              setDocument( elem );
++      }
++
++      // Make sure that attribute selectors are quoted
++      expr = expr.replace( rattributeQuotes, "='$1']" );
++
++      if ( support.matchesSelector && documentIsHTML &&
++              !compilerCache[ expr + " " ] &&
++              ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
++              ( !rbuggyQSA     || !rbuggyQSA.test( expr ) ) ) {
++
++              try {
++                      var ret = matches.call( elem, expr );
++
++                      // IE 9's matchesSelector returns false on disconnected nodes
++                      if ( ret || support.disconnectedMatch ||
++                                      // As well, disconnected nodes are said to be in a document
++                                      // fragment in IE 9
++                                      elem.document && elem.document.nodeType !== 11 ) {
++                              return ret;
++                      }
++              } catch (e) {}
++      }
++
++      return Sizzle( expr, document, null, [ elem ] ).length > 0;
++};
++
++Sizzle.contains = function( context, elem ) {
++      // Set document vars if needed
++      if ( ( context.ownerDocument || context ) !== document ) {
++              setDocument( context );
++      }
++      return contains( context, elem );
++};
++
++Sizzle.attr = function( elem, name ) {
++      // Set document vars if needed
++      if ( ( elem.ownerDocument || elem ) !== document ) {
++              setDocument( elem );
++      }
++
++      var fn = Expr.attrHandle[ name.toLowerCase() ],
++              // Don't get fooled by Object.prototype properties (jQuery #13807)
++              val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
++                      fn( elem, name, !documentIsHTML ) :
++                      undefined;
++
++      return val !== undefined ?
++              val :
++              support.attributes || !documentIsHTML ?
++                      elem.getAttribute( name ) :
++                      (val = elem.getAttributeNode(name)) && val.specified ?
++                              val.value :
++                              null;
++};
++
++Sizzle.escape = function( sel ) {
++      return (sel + "").replace( rcssescape, fcssescape );
++};
++
++Sizzle.error = function( msg ) {
++      throw new Error( "Syntax error, unrecognized expression: " + msg );
++};
++
++/**
++ * Document sorting and removing duplicates
++ * @param {ArrayLike} results
++ */
++Sizzle.uniqueSort = function( results ) {
++      var elem,
++              duplicates = [],
++              j = 0,
++              i = 0;
++
++      // Unless we *know* we can detect duplicates, assume their presence
++      hasDuplicate = !support.detectDuplicates;
++      sortInput = !support.sortStable && results.slice( 0 );
++      results.sort( sortOrder );
++
++      if ( hasDuplicate ) {
++              while ( (elem = results[i++]) ) {
++                      if ( elem === results[ i ] ) {
++                              j = duplicates.push( i );
++                      }
++              }
++              while ( j-- ) {
++                      results.splice( duplicates[ j ], 1 );
++              }
++      }
++
++      // Clear input after sorting to release objects
++      // See https://github.com/jquery/sizzle/pull/225
++      sortInput = null;
++
++      return results;
++};
++
++/**
++ * Utility function for retrieving the text value of an array of DOM nodes
++ * @param {Array|Element} elem
++ */
++getText = Sizzle.getText = function( elem ) {
++      var node,
++              ret = "",
++              i = 0,
++              nodeType = elem.nodeType;
++
++      if ( !nodeType ) {
++              // If no nodeType, this is expected to be an array
++              while ( (node = elem[i++]) ) {
++                      // Do not traverse comment nodes
++                      ret += getText( node );
++              }
++      } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
++              // Use textContent for elements
++              // innerText usage removed for consistency of new lines (jQuery #11153)
++              if ( typeof elem.textContent === "string" ) {
++                      return elem.textContent;
++              } else {
++                      // Traverse its children
++                      for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
++                              ret += getText( elem );
++                      }
++              }
++      } else if ( nodeType === 3 || nodeType === 4 ) {
++              return elem.nodeValue;
++      }
++      // Do not include comment or processing instruction nodes
++
++      return ret;
++};
++
++Expr = Sizzle.selectors = {
++
++      // Can be adjusted by the user
++      cacheLength: 50,
++
++      createPseudo: markFunction,
++
++      match: matchExpr,
++
++      attrHandle: {},
++
++      find: {},
++
++      relative: {
++              ">": { dir: "parentNode", first: true },
++              " ": { dir: "parentNode" },
++              "+": { dir: "previousSibling", first: true },
++              "~": { dir: "previousSibling" }
++      },
++
++      preFilter: {
++              "ATTR": function( match ) {
++                      match[1] = match[1].replace( runescape, funescape );
++
++                      // Move the given value to match[3] whether quoted or unquoted
++                      match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape );
++
++                      if ( match[2] === "~=" ) {
++                              match[3] = " " + match[3] + " ";
++                      }
++
++                      return match.slice( 0, 4 );
++              },
++
++              "CHILD": function( match ) {
++                      /* matches from matchExpr["CHILD"]
++                              1 type (only|nth|...)
++                              2 what (child|of-type)
++                              3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
++                              4 xn-component of xn+y argument ([+-]?\d*n|)
++                              5 sign of xn-component
++                              6 x of xn-component
++                              7 sign of y-component
++                              8 y of y-component
++                      */
++                      match[1] = match[1].toLowerCase();
++
++                      if ( match[1].slice( 0, 3 ) === "nth" ) {
++                              // nth-* requires argument
++                              if ( !match[3] ) {
++                                      Sizzle.error( match[0] );
++                              }
++
++                              // numeric x and y parameters for Expr.filter.CHILD
++                              // remember that false/true cast respectively to 0/1
++                              match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
++                              match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
++
++                      // other types prohibit arguments
++                      } else if ( match[3] ) {
++                              Sizzle.error( match[0] );
++                      }
++
++                      return match;
++              },
++
++              "PSEUDO": function( match ) {
++                      var excess,
++                              unquoted = !match[6] && match[2];
++
++                      if ( matchExpr["CHILD"].test( match[0] ) ) {
++                              return null;
++                      }
++
++                      // Accept quoted arguments as-is
++                      if ( match[3] ) {
++                              match[2] = match[4] || match[5] || "";
++
++                      // Strip excess characters from unquoted arguments
++                      } else if ( unquoted && rpseudo.test( unquoted ) &&
++                              // Get excess from tokenize (recursively)
++                              (excess = tokenize( unquoted, true )) &&
++                              // advance to the next closing parenthesis
++                              (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
++
++                              // excess is a negative index
++                              match[0] = match[0].slice( 0, excess );
++                              match[2] = unquoted.slice( 0, excess );
++                      }
++
++                      // Return only captures needed by the pseudo filter method (type and argument)
++                      return match.slice( 0, 3 );
++              }
++      },
++
++      filter: {
++
++              "TAG": function( nodeNameSelector ) {
++                      var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
++                      return nodeNameSelector === "*" ?
++                              function() { return true; } :
++                              function( elem ) {
++                                      return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
++                              };
++              },
++
++              "CLASS": function( className ) {
++                      var pattern = classCache[ className + " " ];
++
++                      return pattern ||
++                              (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
++                              classCache( className, function( elem ) {
++                                      return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" );
++                              });
++              },
++
++              "ATTR": function( name, operator, check ) {
++                      return function( elem ) {
++                              var result = Sizzle.attr( elem, name );
++
++                              if ( result == null ) {
++                                      return operator === "!=";
++                              }
++                              if ( !operator ) {
++                                      return true;
++                              }
++
++                              result += "";
++
++                              return operator === "=" ? result === check :
++                                      operator === "!=" ? result !== check :
++                                      operator === "^=" ? check && result.indexOf( check ) === 0 :
++                                      operator === "*=" ? check && result.indexOf( check ) > -1 :
++                                      operator === "$=" ? check && result.slice( -check.length ) === check :
++                                      operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 :
++                                      operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
++                                      false;
++                      };
++              },
++
++              "CHILD": function( type, what, argument, first, last ) {
++                      var simple = type.slice( 0, 3 ) !== "nth",
++                              forward = type.slice( -4 ) !== "last",
++                              ofType = what === "of-type";
++
++                      return first === 1 && last === 0 ?
++
++                              // Shortcut for :nth-*(n)
++                              function( elem ) {
++                                      return !!elem.parentNode;
++                              } :
++
++                              function( elem, context, xml ) {
++                                      var cache, uniqueCache, outerCache, node, nodeIndex, start,
++                                              dir = simple !== forward ? "nextSibling" : "previousSibling",
++                                              parent = elem.parentNode,
++                                              name = ofType && elem.nodeName.toLowerCase(),
++                                              useCache = !xml && !ofType,
++                                              diff = false;
++
++                                      if ( parent ) {
++
++                                              // :(first|last|only)-(child|of-type)
++                                              if ( simple ) {
++                                                      while ( dir ) {
++                                                              node = elem;
++                                                              while ( (node = node[ dir ]) ) {
++                                                                      if ( ofType ?
++                                                                              node.nodeName.toLowerCase() === name :
++                                                                              node.nodeType === 1 ) {
++
++                                                                              return false;
++                                                                      }
++                                                              }
++                                                              // Reverse direction for :only-* (if we haven't yet done so)
++                                                              start = dir = type === "only" && !start && "nextSibling";
++                                                      }
++                                                      return true;
++                                              }
++
++                                              start = [ forward ? parent.firstChild : parent.lastChild ];
++
++                                              // non-xml :nth-child(...) stores cache data on `parent`
++                                              if ( forward && useCache ) {
++
++                                                      // Seek `elem` from a previously-cached index
++
++                                                      // ...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 ] ||
++
++                                                              // Fallback to seeking `elem` from the start
++                                                              (diff = nodeIndex = 0) || start.pop()) ) {
++
++                                                              // When found, cache indexes on `parent` and break
++                                                              if ( node.nodeType === 1 && ++diff && node === elem ) {
++                                                                      uniqueCache[ type ] = [ dirruns, nodeIndex, diff ];
++                                                                      break;
++                                                              }
++                                                      }
++
++                                              } else {
++                                                      // Use previously-cached element index if available
++                                                      if ( useCache ) {
++                                                              // ...in a gzip-friendly way
++                                                              node = elem;
++                                                              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;
++                                                      }
++
++                                                      // 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;
++                                                                              }
++                                                                      }
++                                                              }
++                                                      }
++                                              }
++
++                                              // Incorporate the offset, then check against cycle size
++                                              diff -= last;
++                                              return diff === first || ( diff % first === 0 && diff / first >= 0 );
++                                      }
++                              };
++              },
++
++              "PSEUDO": function( pseudo, argument ) {
++                      // pseudo-class names are case-insensitive
++                      // http://www.w3.org/TR/selectors/#pseudo-classes
++                      // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
++                      // Remember that setFilters inherits from pseudos
++                      var args,
++                              fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
++                                      Sizzle.error( "unsupported pseudo: " + pseudo );
++
++                      // The user may use createPseudo to indicate that
++                      // arguments are needed to create the filter function
++                      // just as Sizzle does
++                      if ( fn[ expando ] ) {
++                              return fn( argument );
++                      }
++
++                      // But maintain support for old signatures
++                      if ( fn.length > 1 ) {
++                              args = [ pseudo, pseudo, "", argument ];
++                              return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
++                                      markFunction(function( seed, matches ) {
++                                              var idx,
++                                                      matched = fn( seed, argument ),
++                                                      i = matched.length;
++                                              while ( i-- ) {
++                                                      idx = indexOf( seed, matched[i] );
++                                                      seed[ idx ] = !( matches[ idx ] = matched[i] );
++                                              }
++                                      }) :
++                                      function( elem ) {
++                                              return fn( elem, 0, args );
++                                      };
++                      }
++
++                      return fn;
++              }
++      },
++
++      pseudos: {
++              // Potentially complex pseudos
++              "not": markFunction(function( selector ) {
++                      // Trim the selector passed to compile
++                      // to avoid treating leading and trailing
++                      // spaces as combinators
++                      var input = [],
++                              results = [],
++                              matcher = compile( selector.replace( rtrim, "$1" ) );
++
++                      return matcher[ expando ] ?
++                              markFunction(function( seed, matches, context, xml ) {
++                                      var elem,
++                                              unmatched = matcher( seed, null, xml, [] ),
++                                              i = seed.length;
++
++                                      // Match elements unmatched by `matcher`
++                                      while ( i-- ) {
++                                              if ( (elem = unmatched[i]) ) {
++                                                      seed[i] = !(matches[i] = elem);
++                                              }
++                                      }
++                              }) :
++                              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();
++                              };
++              }),
++
++              "has": markFunction(function( selector ) {
++                      return function( elem ) {
++                              return Sizzle( selector, elem ).length > 0;
++                      };
++              }),
++
++              "contains": markFunction(function( text ) {
++                      text = text.replace( runescape, funescape );
++                      return function( elem ) {
++                              return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
++                      };
++              }),
++
++              // "Whether an element is represented by a :lang() selector
++              // is based solely on the element's language value
++              // being equal to the identifier C,
++              // or beginning with the identifier C immediately followed by "-".
++              // The matching of C against the element's language value is performed case-insensitively.
++              // The identifier C does not have to be a valid language name."
++              // http://www.w3.org/TR/selectors/#lang-pseudo
++              "lang": markFunction( function( lang ) {
++                      // lang value must be a valid identifier
++                      if ( !ridentifier.test(lang || "") ) {
++                              Sizzle.error( "unsupported lang: " + lang );
++                      }
++                      lang = lang.replace( runescape, funescape ).toLowerCase();
++                      return function( elem ) {
++                              var elemLang;
++                              do {
++                                      if ( (elemLang = documentIsHTML ?
++                                              elem.lang :
++                                              elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {
++
++                                              elemLang = elemLang.toLowerCase();
++                                              return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
++                                      }
++                              } while ( (elem = elem.parentNode) && elem.nodeType === 1 );
++                              return false;
++                      };
++              }),
++
++              // Miscellaneous
++              "target": function( elem ) {
++                      var hash = window.location && window.location.hash;
++                      return hash && hash.slice( 1 ) === elem.id;
++              },
++
++              "root": function( elem ) {
++                      return elem === docElem;
++              },
++
++              "focus": function( elem ) {
++                      return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
++              },
++
++              // Boolean properties
++              "enabled": createDisabledPseudo( false ),
++              "disabled": createDisabledPseudo( true ),
++
++              "checked": function( elem ) {
++                      // In CSS3, :checked should return both checked and selected elements
++                      // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
++                      var nodeName = elem.nodeName.toLowerCase();
++                      return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
++              },
++
++              "selected": function( elem ) {
++                      // Accessing this property makes selected-by-default
++                      // options in Safari work properly
++                      if ( elem.parentNode ) {
++                              elem.parentNode.selectedIndex;
++                      }
++
++                      return elem.selected === true;
++              },
++
++              // Contents
++              "empty": function( elem ) {
++                      // http://www.w3.org/TR/selectors/#empty-pseudo
++                      // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
++                      //   but not by others (comment: 8; processing instruction: 7; etc.)
++                      // nodeType < 6 works because attributes (2) do not appear as children
++                      for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
++                              if ( elem.nodeType < 6 ) {
++                                      return false;
++                              }
++                      }
++                      return true;
++              },
++
++              "parent": function( elem ) {
++                      return !Expr.pseudos["empty"]( elem );
++              },
++
++              // Element/input types
++              "header": function( elem ) {
++                      return rheader.test( elem.nodeName );
++              },
++
++              "input": function( elem ) {
++                      return rinputs.test( elem.nodeName );
++              },
++
++              "button": function( elem ) {
++                      var name = elem.nodeName.toLowerCase();
++                      return name === "input" && elem.type === "button" || name === "button";
++              },
++
++              "text": function( elem ) {
++                      var attr;
++                      return elem.nodeName.toLowerCase() === "input" &&
++                              elem.type === "text" &&
++
++                              // 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
++              "first": createPositionalPseudo(function() {
++                      return [ 0 ];
++              }),
++
++              "last": createPositionalPseudo(function( matchIndexes, length ) {
++                      return [ length - 1 ];
++              }),
++
++              "eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
++                      return [ argument < 0 ? argument + length : argument ];
++              }),
++
++              "even": createPositionalPseudo(function( matchIndexes, length ) {
++                      var i = 0;
++                      for ( ; i < length; i += 2 ) {
++                              matchIndexes.push( i );
++                      }
++                      return matchIndexes;
++              }),
++
++              "odd": createPositionalPseudo(function( matchIndexes, length ) {
++                      var i = 1;
++                      for ( ; i < length; i += 2 ) {
++                              matchIndexes.push( i );
++                      }
++                      return matchIndexes;
++              }),
++
++              "lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
++                      var i = argument < 0 ? argument + length : argument;
++                      for ( ; --i >= 0; ) {
++                              matchIndexes.push( i );
++                      }
++                      return matchIndexes;
++              }),
++
++              "gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
++                      var i = argument < 0 ? argument + length : argument;
++                      for ( ; ++i < length; ) {
++                              matchIndexes.push( i );
++                      }
++                      return matchIndexes;
++              })
++      }
++};
++
++Expr.pseudos["nth"] = Expr.pseudos["eq"];
++
++// Add button/input type pseudos
++for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
++      Expr.pseudos[ i ] = createInputPseudo( i );
++}
++for ( i in { submit: true, reset: true } ) {
++      Expr.pseudos[ i ] = createButtonPseudo( i );
++}
++
++// Easy API for creating new setFilters
++function setFilters() {}
++setFilters.prototype = Expr.filters = Expr.pseudos;
++Expr.setFilters = new setFilters();
++
++tokenize = Sizzle.tokenize = function( selector, parseOnly ) {
++      var matched, match, tokens, type,
++              soFar, groups, preFilters,
++              cached = tokenCache[ selector + " " ];
++
++      if ( cached ) {
++              return parseOnly ? 0 : cached.slice( 0 );
++      }
++
++      soFar = selector;
++      groups = [];
++      preFilters = Expr.preFilter;
++
++      while ( soFar ) {
++
++              // Comma and first run
++              if ( !matched || (match = rcomma.exec( soFar )) ) {
++                      if ( match ) {
++                              // Don't consume trailing commas as valid
++                              soFar = soFar.slice( match[0].length ) || soFar;
++                      }
++                      groups.push( (tokens = []) );
++              }
++
++              matched = false;
++
++              // Combinators
++              if ( (match = rcombinators.exec( soFar )) ) {
++                      matched = match.shift();
++                      tokens.push({
++                              value: matched,
++                              // Cast descendant combinators to space
++                              type: match[0].replace( rtrim, " " )
++                      });
++                      soFar = soFar.slice( matched.length );
++              }
++
++              // Filters
++              for ( type in Expr.filter ) {
++                      if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
++                              (match = preFilters[ type ]( match ))) ) {
++                              matched = match.shift();
++                              tokens.push({
++                                      value: matched,
++                                      type: type,
++                                      matches: match
++                              });
++                              soFar = soFar.slice( matched.length );
++                      }
++              }
++
++              if ( !matched ) {
++                      break;
++              }
++      }
++
++      // Return the length of the invalid excess
++      // if we're just parsing
++      // Otherwise, throw an error or return tokens
++      return parseOnly ?
++              soFar.length :
++              soFar ?
++                      Sizzle.error( selector ) :
++                      // Cache the tokens
++                      tokenCache( selector, groups ).slice( 0 );
++};
++
++function toSelector( tokens ) {
++      var i = 0,
++              len = tokens.length,
++              selector = "";
++      for ( ; i < len; i++ ) {
++              selector += tokens[i].value;
++      }
++      return selector;
++}
++
++function addCombinator( matcher, combinator, base ) {
++      var dir = combinator.dir,
++              skip = combinator.next,
++              key = skip || dir,
++              checkNonElements = base && key === "parentNode",
++              doneName = done++;
++
++      return combinator.first ?
++              // Check against closest ancestor/preceding element
++              function( elem, context, xml ) {
++                      while ( (elem = elem[ dir ]) ) {
++                              if ( elem.nodeType === 1 || checkNonElements ) {
++                                      return matcher( elem, context, xml );
++                              }
++                      }
++                      return false;
++              } :
++
++              // Check against all ancestor/preceding elements
++              function( elem, context, xml ) {
++                      var oldCache, uniqueCache, outerCache,
++                              newCache = [ dirruns, doneName ];
++
++                      // 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 ) {
++                                              if ( matcher( elem, context, xml ) ) {
++                                                      return true;
++                                              }
++                                      }
++                              }
++                      } else {
++                              while ( (elem = elem[ dir ]) ) {
++                                      if ( elem.nodeType === 1 || checkNonElements ) {
++                                              outerCache = elem[ expando ] || (elem[ expando ] = {});
++
++                                              // 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 {
++                                                      // 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;
++              };
++}
++
++function elementMatcher( matchers ) {
++      return matchers.length > 1 ?
++              function( elem, context, xml ) {
++                      var i = matchers.length;
++                      while ( i-- ) {
++                              if ( !matchers[i]( elem, context, xml ) ) {
++                                      return false;
++                              }
++                      }
++                      return true;
++              } :
++              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 = [],
++              i = 0,
++              len = unmatched.length,
++              mapped = map != null;
++
++      for ( ; i < len; i++ ) {
++              if ( (elem = unmatched[i]) ) {
++                      if ( !filter || filter( elem, context, xml ) ) {
++                              newUnmatched.push( elem );
++                              if ( mapped ) {
++                                      map.push( i );
++                              }
++                      }
++              }
++      }
++
++      return newUnmatched;
++}
++
++function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
++      if ( postFilter && !postFilter[ expando ] ) {
++              postFilter = setMatcher( postFilter );
++      }
++      if ( postFinder && !postFinder[ expando ] ) {
++              postFinder = setMatcher( postFinder, postSelector );
++      }
++      return markFunction(function( seed, results, context, xml ) {
++              var temp, i, elem,
++                      preMap = [],
++                      postMap = [],
++                      preexisting = results.length,
++
++                      // Get initial elements from seed or context
++                      elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
++
++                      // Prefilter to get matcher input, preserving a map for seed-results synchronization
++                      matcherIn = preFilter && ( seed || !selector ) ?
++                              condense( elems, preMap, preFilter, context, xml ) :
++                              elems,
++
++                      matcherOut = matcher ?
++                              // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
++                              postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
++
++                                      // ...intermediate processing is necessary
++                                      [] :
++
++                                      // ...otherwise use results directly
++                                      results :
++                              matcherIn;
++
++              // Find primary matches
++              if ( matcher ) {
++                      matcher( matcherIn, matcherOut, context, xml );
++              }
++
++              // Apply postFilter
++              if ( postFilter ) {
++                      temp = condense( matcherOut, postMap );
++                      postFilter( temp, [], context, xml );
++
++                      // Un-match failing elements by moving them back to matcherIn
++                      i = temp.length;
++                      while ( i-- ) {
++                              if ( (elem = temp[i]) ) {
++                                      matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
++                              }
++                      }
++              }
++
++              if ( seed ) {
++                      if ( postFinder || preFilter ) {
++                              if ( postFinder ) {
++                                      // Get the final matcherOut by condensing this intermediate into postFinder contexts
++                                      temp = [];
++                                      i = matcherOut.length;
++                                      while ( i-- ) {
++                                              if ( (elem = matcherOut[i]) ) {
++                                                      // Restore matcherIn since elem is not yet a final match
++                                                      temp.push( (matcherIn[i] = elem) );
++                                              }
++                                      }
++                                      postFinder( null, (matcherOut = []), temp, xml );
++                              }
++
++                              // Move matched elements from seed to results to keep them synchronized
++                              i = matcherOut.length;
++                              while ( i-- ) {
++                                      if ( (elem = matcherOut[i]) &&
++                                              (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) {
++
++                                              seed[temp] = !(results[temp] = elem);
++                                      }
++                              }
++                      }
++
++              // Add elements to results, through postFinder if defined
++              } else {
++                      matcherOut = condense(
++                              matcherOut === results ?
++                                      matcherOut.splice( preexisting, matcherOut.length ) :
++                                      matcherOut
++                      );
++                      if ( postFinder ) {
++                              postFinder( null, results, matcherOut, xml );
++                      } else {
++                              push.apply( results, matcherOut );
++                      }
++              }
++      });
++}
++
++function matcherFromTokens( tokens ) {
++      var checkContext, matcher, j,
++              len = tokens.length,
++              leadingRelative = Expr.relative[ tokens[0].type ],
++              implicitRelative = leadingRelative || Expr.relative[" "],
++              i = leadingRelative ? 1 : 0,
++
++              // The foundational matcher ensures that elements are reachable from top-level context(s)
++              matchContext = addCombinator( function( elem ) {
++                      return elem === checkContext;
++              }, implicitRelative, true ),
++              matchAnyContext = addCombinator( function( elem ) {
++                      return indexOf( checkContext, elem ) > -1;
++              }, implicitRelative, true ),
++              matchers = [ function( elem, context, xml ) {
++                      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++ ) {
++              if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
++                      matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
++              } else {
++                      matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
++
++                      // Return special upon seeing a positional matcher
++                      if ( matcher[ expando ] ) {
++                              // Find the next relative operator (if any) for proper handling
++                              j = ++i;
++                              for ( ; j < len; j++ ) {
++                                      if ( Expr.relative[ tokens[j].type ] ) {
++                                              break;
++                                      }
++                              }
++                              return setMatcher(
++                                      i > 1 && elementMatcher( matchers ),
++                                      i > 1 && toSelector(
++                                              // If the preceding token was a descendant combinator, insert an implicit any-element `*`
++                                              tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" })
++                                      ).replace( rtrim, "$1" ),
++                                      matcher,
++                                      i < j && matcherFromTokens( tokens.slice( i, j ) ),
++                                      j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
++                                      j < len && toSelector( tokens )
++                              );
++                      }
++                      matchers.push( matcher );
++              }
++      }
++
++      return elementMatcher( matchers );
++}
++
++function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
++      var bySet = setMatchers.length > 0,
++              byElement = elementMatchers.length > 0,
++              superMatcher = function( seed, context, xml, results, outermost ) {
++                      var elem, j, matcher,
++                              matchedCount = 0,
++                              i = "0",
++                              unmatched = seed && [],
++                              setMatched = [],
++                              contextBackup = outermostContext,
++                              // 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),
++                              len = elems.length;
++
++                      if ( outermost ) {
++                              outermostContext = context === document || context || outermost;
++                      }
++
++                      // Add elements passing elementMatchers directly to results
++                      // Support: IE<9, Safari
++                      // Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id
++                      for ( ; i !== len && (elem = elems[i]) != null; i++ ) {
++                              if ( byElement && elem ) {
++                                      j = 0;
++                                      if ( !context && elem.ownerDocument !== document ) {
++                                              setDocument( elem );
++                                              xml = !documentIsHTML;
++                                      }
++                                      while ( (matcher = elementMatchers[j++]) ) {
++                                              if ( matcher( elem, context || document, xml) ) {
++                                                      results.push( elem );
++                                                      break;
++                                              }
++                                      }
++                                      if ( outermost ) {
++                                              dirruns = dirrunsUnique;
++                                      }
++                              }
++
++                              // Track unmatched elements for set filters
++                              if ( bySet ) {
++                                      // They will have gone through all possible matchers
++                                      if ( (elem = !matcher && elem) ) {
++                                              matchedCount--;
++                                      }
++
++                                      // Lengthen the array for every element, matched or not
++                                      if ( seed ) {
++                                              unmatched.push( elem );
++                                      }
++                              }
++                      }
++
++                      // `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++]) ) {
++                                      matcher( unmatched, setMatched, context, xml );
++                              }
++
++                              if ( seed ) {
++                                      // Reintegrate element matches to eliminate the need for sorting
++                                      if ( matchedCount > 0 ) {
++                                              while ( i-- ) {
++                                                      if ( !(unmatched[i] || setMatched[i]) ) {
++                                                              setMatched[i] = pop.call( results );
++                                                      }
++                                              }
++                                      }
++
++                                      // Discard index placeholder values to get only actual matches
++                                      setMatched = condense( setMatched );
++                              }
++
++                              // Add matches to results
++                              push.apply( results, setMatched );
++
++                              // Seedless set matches succeeding multiple successful matchers stipulate sorting
++                              if ( outermost && !seed && setMatched.length > 0 &&
++                                      ( matchedCount + setMatchers.length ) > 1 ) {
++
++                                      Sizzle.uniqueSort( results );
++                              }
++                      }
++
++                      // Override manipulation of globals by nested matchers
++                      if ( outermost ) {
++                              dirruns = dirrunsUnique;
++                              outermostContext = contextBackup;
++                      }
++
++                      return unmatched;
++              };
++
++      return bySet ?
++              markFunction( superMatcher ) :
++              superMatcher;
++}
++
++compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {
++      var i,
++              setMatchers = [],
++              elementMatchers = [],
++              cached = compilerCache[ selector + " " ];
++
++      if ( !cached ) {
++              // Generate a function of recursive functions that can be used to check each element
++              if ( !match ) {
++                      match = tokenize( selector );
++              }
++              i = match.length;
++              while ( i-- ) {
++                      cached = matcherFromTokens( match[i] );
++                      if ( cached[ expando ] ) {
++                              setMatchers.push( cached );
++                      } else {
++                              elementMatchers.push( cached );
++                      }
++              }
++
++              // Cache the compiled function
++              cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
++
++              // Save selector and tokenization
++              cached.selector = selector;
++      }
++      return cached;
++};
++
++/**
++ * 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,
++              compiled = typeof selector === "function" && selector,
++              match = !seed && tokenize( (selector = compiled.selector || selector) );
++
++      results = results || [];
++
++      // 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 ) {
++
++              // 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;
++                      }
++
++                      selector = selector.slice( tokens.shift().value.length );
++              }
++
++              // Fetch a seed set for right-to-left matching
++              i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
++              while ( i-- ) {
++                      token = tokens[i];
++
++                      // 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 if one is not provided
++      // Provide `match` to avoid retokenization if we modified the selector above
++      ( compiled || compile( selector, match ) )(
++              seed,
++              context,
++              !documentIsHTML,
++              results,
++              !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-35+
++// Always assume duplicates if they aren't passed to the comparison function
++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( el ) {
++      // Should return 1, but returns 4 (following)
++      return el.compareDocumentPosition( document.createElement("fieldset") ) & 1;
++});
++
++// Support: IE<8
++// Prevent attribute/property "interpolation"
++// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
++if ( !assert(function( el ) {
++      el.innerHTML = "<a href='#'></a>";
++      return el.firstChild.getAttribute("href") === "#" ;
++}) ) {
++      addHandle( "type|href|height|width", function( elem, name, isXML ) {
++              if ( !isXML ) {
++                      return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
++              }
++      });
++}
++
++// Support: IE<9
++// Use defaultValue in place of getAttribute("value")
++if ( !support.attributes || !assert(function( el ) {
++      el.innerHTML = "<input/>";
++      el.firstChild.setAttribute( "value", "" );
++      return el.firstChild.getAttribute( "value" ) === "";
++}) ) {
++      addHandle( "value", function( elem, name, isXML ) {
++              if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
++                      return elem.defaultValue;
++              }
++      });
++}
++
++// Support: IE<9
++// Use getAttributeNode to fetch booleans when getAttribute lies
++if ( !assert(function( el ) {
++      return el.getAttribute("disabled") == null;
++}) ) {
++      addHandle( booleans, function( elem, name, isXML ) {
++              var val;
++              if ( !isXML ) {
++                      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;
++
++// 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;
++
++
++
++
++var dir = function( elem, dir, until ) {
++      var matched = [],
++              truncate = until !== undefined;
++
++      while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) {
++              if ( elem.nodeType === 1 ) {
++                      if ( truncate && jQuery( elem ).is( until ) ) {
++                              break;
++                      }
++                      matched.push( elem );
++              }
++      }
++      return matched;
++};
++
++
++var siblings = function( n, elem ) {
++      var matched = [];
++
++      for ( ; n; n = n.nextSibling ) {
++              if ( n.nodeType === 1 && n !== elem ) {
++                      matched.push( n );
++              }
++      }
++
++      return matched;
++};
++
++
++var rneedsContext = jQuery.expr.match.needsContext;
++
++
++
++function nodeName( elem, name ) {
++
++  return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
++
++};
++var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i );
++
++
++
++// 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;
++              } );
++      }
++
++      // Single element
++      if ( qualifier.nodeType ) {
++              return jQuery.grep( elements, function( elem ) {
++                      return ( elem === qualifier ) !== not;
++              } );
++      }
++
++      // Arraylike of elements (jQuery, arguments, Array)
++      if ( typeof qualifier !== "string" ) {
++              return jQuery.grep( elements, function( elem ) {
++                      return ( indexOf.call( qualifier, elem ) > -1 ) !== not;
++              } );
++      }
++
++      // Filtered directly for both simple and complex selectors
++      return jQuery.filter( qualifier, elements, not );
++}
++
++jQuery.filter = function( expr, elems, not ) {
++      var elem = elems[ 0 ];
++
++      if ( not ) {
++              expr = ":not(" + expr + ")";
++      }
++
++      if ( elems.length === 1 && elem.nodeType === 1 ) {
++              return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [];
++      }
++
++      return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
++              return elem.nodeType === 1;
++      } ) );
++};
++
++jQuery.fn.extend( {
++      find: function( selector ) {
++              var i, ret,
++                      len = this.length,
++                      self = this;
++
++              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;
++                                      }
++                              }
++                      } ) );
++              }
++
++              ret = this.pushStack( [] );
++
++              for ( i = 0; i < len; i++ ) {
++                      jQuery.find( selector, self[ i ], ret );
++              }
++
++              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,
++
++                      // 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;
++      }
++} );
++
++
++// Initialize a jQuery object
++
++
++// A central reference to the root jQuery(document)
++var rootjQuery,
++
++      // A simple way to check for HTML strings
++      // Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
++      // Strict HTML recognition (#11290: must start with <)
++      // Shortcut simple #id case for speed
++      rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,
++
++      init = jQuery.fn.init = function( selector, context, root ) {
++              var match, elem;
++
++              // HANDLE: $(""), $(null), $(undefined), $(false)
++              if ( !selector ) {
++                      return this;
++              }
++
++              // Method init() accepts an alternate rootjQuery
++              // so migrate can support jQuery.sub (gh-2101)
++              root = root || rootjQuery;
++
++              // Handle HTML strings
++              if ( typeof selector === "string" ) {
++                      if ( selector[ 0 ] === "<" &&
++                              selector[ 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 );
++                      }
++
++                      // Match html or make sure no context is specified for #id
++                      if ( match && ( match[ 1 ] || !context ) ) {
++
++                              // HANDLE: $(html) -> $(array)
++                              if ( match[ 1 ] ) {
++                                      context = context instanceof jQuery ? context[ 0 ] : context;
++
++                                      // 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
++                                      ) );
++
++                                      // 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 ( isFunction( this[ match ] ) ) {
++                                                              this[ match ]( context[ match ] );
++
++                                                      // ...and otherwise set as attributes
++                                                      } else {
++                                                              this.attr( match, context[ match ] );
++                                                      }
++                                              }
++                                      }
++
++                                      return this;
++
++                              // HANDLE: $(#id)
++                              } else {
++                                      elem = document.getElementById( match[ 2 ] );
++
++                                      if ( elem ) {
++
++                                              // Inject the element directly into the jQuery object
++                                              this[ 0 ] = elem;
++                                              this.length = 1;
++                                      }
++                                      return this;
++                              }
++
++                      // HANDLE: $(expr, $(...))
++                      } else if ( !context || context.jquery ) {
++                              return ( context || root ).find( selector );
++
++                      // HANDLE: $(expr, context)
++                      // (which is just equivalent to: $(context).find(expr)
++                      } else {
++                              return this.constructor( context ).find( selector );
++                      }
++
++              // HANDLE: $(DOMElement)
++              } else if ( selector.nodeType ) {
++                      this[ 0 ] = selector;
++                      this.length = 1;
++                      return this;
++
++              // HANDLE: $(function)
++              // Shortcut for document ready
++              } else if ( isFunction( selector ) ) {
++                      return root.ready !== undefined ?
++                              root.ready( selector ) :
++
++                              // Execute immediately if ready is not present
++                              selector( jQuery );
++              }
++
++              return jQuery.makeArray( selector, this );
++      };
++
++// Give the init function the jQuery prototype for later instantiation
++init.prototype = jQuery.fn;
++
++// Initialize central reference
++rootjQuery = jQuery( document );
++
++
++var rparentsprev = /^(?:parents|prev(?:Until|All))/,
++
++      // Methods guaranteed to produce a unique set when starting from a unique set
++      guaranteedUnique = {
++              children: true,
++              contents: true,
++              next: true,
++              prev: true
++      };
++
++jQuery.fn.extend( {
++      has: function( target ) {
++              var targets = jQuery( target, this ),
++                      l = targets.length;
++
++              return this.filter( function() {
++                      var i = 0;
++                      for ( ; i < l; i++ ) {
++                              if ( jQuery.contains( this, targets[ i ] ) ) {
++                                      return true;
++                              }
++                      }
++              } );
++      },
++
++      closest: function( selectors, context ) {
++              var cur,
++                      i = 0,
++                      l = this.length,
++                      matched = [],
++                      targets = typeof selectors !== "string" && jQuery( selectors );
++
++              // 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 ) {
++
++                                      // Always skip document fragments
++                                      if ( cur.nodeType < 11 && ( targets ?
++                                              targets.index( cur ) > -1 :
++
++                                              // Don't pass non-elements to Sizzle
++                                              cur.nodeType === 1 &&
++                                                      jQuery.find.matchesSelector( cur, selectors ) ) ) {
++
++                                              matched.push( cur );
++                                              break;
++                                      }
++                              }
++                      }
++              }
++
++              return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched );
++      },
++
++      // Determine the position of an element within the set
++      index: function( elem ) {
++
++              // No argument, return index in parent
++              if ( !elem ) {
++                      return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
++              }
++
++              // Index in selector
++              if ( typeof elem === "string" ) {
++                      return indexOf.call( jQuery( elem ), this[ 0 ] );
++              }
++
++              // Locate the position of the desired element
++              return indexOf.call( this,
++
++                      // If it receives a jQuery object, the first element is used
++                      elem.jquery ? elem[ 0 ] : elem
++              );
++      },
++
++      add: function( selector, context ) {
++              return this.pushStack(
++                      jQuery.uniqueSort(
++                              jQuery.merge( this.get(), jQuery( selector, context ) )
++                      )
++              );
++      },
++
++      addBack: function( selector ) {
++              return this.add( selector == null ?
++                      this.prevObject : this.prevObject.filter( selector )
++              );
++      }
++} );
++
++function sibling( cur, dir ) {
++      while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {}
++      return cur;
++}
++
++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 ( nodeName( elem, "iframe" ) ) {
++            return elem.contentDocument;
++        }
++
++        // 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;
++        }
++
++        return jQuery.merge( [], elem.childNodes );
++      }
++}, function( name, fn ) {
++      jQuery.fn[ name ] = function( until, selector ) {
++              var matched = jQuery.map( this, fn, until );
++
++              if ( name.slice( -5 ) !== "Until" ) {
++                      selector = until;
++              }
++
++              if ( selector && typeof selector === "string" ) {
++                      matched = jQuery.filter( selector, matched );
++              }
++
++              if ( this.length > 1 ) {
++
++                      // Remove duplicates
++                      if ( !guaranteedUnique[ name ] ) {
++                              jQuery.uniqueSort( matched );
++                      }
++
++                      // Reverse order for parents* and prev-derivatives
++                      if ( rparentsprev.test( name ) ) {
++                              matched.reverse();
++                      }
++              }
++
++              return this.pushStack( matched );
++      };
++} );
++var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g );
++
++
++
++// 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;
++}
++
++/*
++ * 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" ?
++              createOptions( options ) :
++              jQuery.extend( {}, options );
++
++      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,
++
++              // Flag to prevent firing
++              locked,
++
++              // Actual callback list
++              list = [],
++
++              // Queue of execution data for repeatable lists
++              queue = [],
++
++              // Index of currently firing callback (modified by add/remove as needed)
++              firingIndex = -1,
++
++              // Fire callbacks
++              fire = function() {
++
++                      // Enforce single-firing
++                      locked = locked || options.once;
++
++                      // 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 ) {
++
++                                      // Run callback and check for early termination
++                                      if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&
++                                              options.stopOnFalse ) {
++
++                                              // Jump to end and forget the data so .add doesn't re-fire
++                                              firingIndex = list.length;
++                                              memory = false;
++                                      }
++                              }
++                      }
++
++                      // Forget the data if we're done with it
++                      if ( !options.memory ) {
++                              memory = false;
++                      }
++
++                      firing = false;
++
++                      // Clean up if we're done firing for good
++                      if ( locked ) {
++
++                              // Keep an empty list if we have data for future add calls
++                              if ( memory ) {
++                                      list = [];
++
++                              // Otherwise, this object is spent
++                              } else {
++                                      list = "";
++                              }
++                      }
++              },
++
++              // Actual Callbacks object
++              self = {
++
++                      // Add a callback or a collection of callbacks to the list
++                      add: function() {
++                              if ( list ) {
++
++                                      // If we have memory from a past run, we should fire after adding
++                                      if ( memory && !firing ) {
++                                              firingIndex = list.length - 1;
++                                              queue.push( memory );
++                                      }
++
++                                      ( 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" ) {
++
++                                                              // Inspect recursively
++                                                              add( arg );
++                                                      }
++                                              } );
++                                      } )( arguments );
++
++                                      if ( memory && !firing ) {
++                                              fire();
++                                      }
++                              }
++                              return this;
++                      },
++
++                      // 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;
++                      },
++
++                      // 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;
++                      },
++
++                      // Remove all callbacks from the list
++                      empty: function() {
++                              if ( list ) {
++                                      list = [];
++                              }
++                              return this;
++                      },
++
++                      // 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;
++                      },
++
++                      // 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;
++                      },
++
++                      // 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 self;
++};
++
++
++function Identity( v ) {
++      return v;
++}
++function Thrower( ex ) {
++      throw ex;
++}
++
++function adoptValue( value, resolve, reject, noValue ) {
++      var method;
++
++      try {
++
++              // Check for promise aspect first to privilege synchronous behavior
++              if ( value && isFunction( ( method = value.promise ) ) ) {
++                      method.call( value ).done( resolve ).fail( reject );
++
++              // 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 ) );
++              }
++
++      // 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.extend( {
++
++      Deferred: function( func ) {
++              var tuples = [
++
++                              // 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 );
++                              },
++
++                              // Keep pipe for back-compat
++                              pipe: function( /* fnDone, fnFail, fnProgress */ ) {
++                                      var fns = arguments;
++
++                                      return jQuery.Deferred( function( newDefer ) {
++                                              jQuery.each( tuples, function( i, tuple ) {
++
++                                                      // Map tuples (progress, done, fail) to arguments (done, fail, progress)
++                                                      var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ];
++
++                                                      // 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;
++                                                                      }
++
++                                                                      returned = handler.apply( that, args );
++
++                                                                      // Support: Promises/A+ section 2.3.1
++                                                                      // https://promisesaplus.com/#point-48
++                                                                      if ( returned === deferred.promise() ) {
++                                                                              throw new TypeError( "Thenable self-resolution" );
++                                                                      }
++
++                                                                      // 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 {
++
++                                                              // 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 );
++                                                      }
++                                              };
++                                      }
++
++                                      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();
++                              },
++
++                              // 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 = {};
++
++              // Add list-specific methods
++              jQuery.each( tuples, function( i, tuple ) {
++                      var list = tuple[ 2 ],
++                              stateString = tuple[ 5 ];
++
++                      // promise.progress = list.add
++                      // promise.done = list.add
++                      // promise.fail = list.add
++                      promise[ tuple[ 1 ] ] = list.add;
++
++                      // Handle state
++                      if ( stateString ) {
++                              list.add(
++                                      function() {
++
++                                              // 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
++                              );
++                      }
++
++                      // progress_handlers.fire
++                      // fulfilled_handlers.fire
++                      // rejected_handlers.fire
++                      list.add( tuple[ 3 ].fire );
++
++                      // 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;
++                      };
++
++                      // deferred.notifyWith = list.fireWith
++                      // deferred.resolveWith = list.fireWith
++                      // deferred.rejectWith = list.fireWith
++                      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 );
++              }
++
++              // All done!
++              return deferred;
++      },
++
++      // Deferred helper
++      when: function( singleValue ) {
++              var
++
++                      // count of uncompleted subordinates
++                      remaining = arguments.length,
++
++                      // count of unprocessed arguments
++                      i = remaining,
++
++                      // subordinate fulfillment data
++                      resolveContexts = Array( i ),
++                      resolveValues = slice.call( arguments ),
++
++                      // 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 );
++                                      }
++                              };
++                      };
++
++              // Single- and empty arguments are adopted like Promise.resolve
++              if ( remaining <= 1 ) {
++                      adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject,
++                              !remaining );
++
++                      // Use .then() to unwrap secondary thenables (cf. gh-3000)
++                      if ( master.state() === "pending" ||
++                              isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) {
++
++                              return master.then();
++                      }
++              }
++
++              // Multiple arguments are aggregated like Promise.all array elements
++              while ( i-- ) {
++                      adoptValue( resolveValues[ i ], updateFunc( i ), master.reject );
++              }
++
++              return master.promise();
++      }
++} );
++
++
++// 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$/;
++
++jQuery.Deferred.exceptionHook = function( error, stack ) {
++
++      // 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 );
++      }
++};
++
++
++
++
++jQuery.readyException = function( error ) {
++      window.setTimeout( function() {
++              throw error;
++      } );
++};
++
++
++
++
++// The deferred used on DOM ready
++var readyList = jQuery.Deferred();
++
++jQuery.fn.ready = function( fn ) {
++
++      readyList
++              .then( fn )
++
++              // 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 );
++              } );
++
++      return this;
++};
++
++jQuery.extend( {
++
++      // 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,
++
++      // 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;
++              }
++
++              // 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 ] );
++      }
++} );
++
++jQuery.ready.then = readyList.then;
++
++// The ready event handler and self cleanup method
++function completed() {
++      document.removeEventListener( "DOMContentLoaded", completed );
++      window.removeEventListener( "load", completed );
++      jQuery.ready();
++}
++
++// 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 ) ) {
++
++      // Handle it asynchronously to allow scripts the opportunity to delay ready
++      window.setTimeout( jQuery.ready );
++
++} else {
++
++      // Use the handy event callback
++      document.addEventListener( "DOMContentLoaded", completed );
++
++      // A fallback to window.onload, that will always work
++      window.addEventListener( "load", completed );
++}
++
++
++
++
++// 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;
++
++      // Sets many values
++      if ( toType( key ) === "object" ) {
++              chainable = true;
++              for ( i in key ) {
++                      access( elems, fn, i, key[ i ], true, emptyGet, raw );
++              }
++
++      // Sets one value
++      } else if ( value !== undefined ) {
++              chainable = true;
++
++              if ( !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 < len; i++ ) {
++                              fn(
++                                      elems[ i ], key, raw ?
++                                      value :
++                                      value.call( elems[ i ], i, fn( elems[ i ], key ) )
++                              );
++                      }
++              }
++      }
++
++      if ( chainable ) {
++              return elems;
++      }
++
++      // Gets
++      if ( bulk ) {
++              return fn.call( elems );
++      }
++
++      return len ? fn( elems[ 0 ], key ) : emptyGet;
++};
++
++
++// 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();
++}
++
++// 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 );
++};
++
++
++
++
++function Data() {
++      this.expando = jQuery.expando + Data.uid++;
++}
++
++Data.uid = 1;
++
++Data.prototype = {
++
++      cache: function( owner ) {
++
++              // Check if the owner object already has a cache
++              var value = owner[ this.expando ];
++
++              // If not, create one
++              if ( !value ) {
++                      value = {};
++
++                      // 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 value;
++      },
++      set: function( owner, data, value ) {
++              var prop,
++                      cache = this.cache( owner );
++
++              // Handle: [ owner, key, value ] args
++              // Always use camelCase key (gh-2257)
++              if ( typeof data === "string" ) {
++                      cache[ camelCase( data ) ] = value;
++
++              // Handle: [ owner, { properties } ] args
++              } else {
++
++                      // Copy the properties one-by-one to the cache object
++                      for ( prop in data ) {
++                              cache[ camelCase( prop ) ] = data[ prop ];
++                      }
++              }
++              return cache;
++      },
++      get: function( owner, key ) {
++              return key === undefined ?
++                      this.cache( owner ) :
++
++                      // Always use camelCase key (gh-2257)
++                      owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ];
++      },
++      access: function( owner, key, value ) {
++
++              // 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 ) ) {
++
++                      return this.get( owner, key );
++              }
++
++              // 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 );
++
++              // 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 ];
++
++              if ( cache === undefined ) {
++                      return;
++              }
++
++              if ( key !== undefined ) {
++
++                      // Support array or space separated string of keys
++                      if ( Array.isArray( key ) ) {
++
++                              // 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 ] ];
++                      }
++              }
++
++              // Remove the expando if there's no more data
++              if ( key === undefined || jQuery.isEmptyObject( cache ) ) {
++
++                      // 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();
++
++var dataUser = new Data();
++
++
++
++//    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
++
++var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
++      rmultiDash = /[A-Z]/g;
++
++function getData( data ) {
++      if ( data === "true" ) {
++              return true;
++      }
++
++      if ( data === "false" ) {
++              return false;
++      }
++
++      if ( data === "null" ) {
++              return null;
++      }
++
++      // Only convert to a number if it doesn't change the string
++      if ( data === +data + "" ) {
++              return +data;
++      }
++
++      if ( rbrace.test( data ) ) {
++              return JSON.parse( data );
++      }
++
++      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;
++}
++
++jQuery.extend( {
++      hasData: function( elem ) {
++              return dataUser.hasData( elem ) || dataPriv.hasData( elem );
++      },
++
++      data: function( elem, name, data ) {
++              return dataUser.access( elem, name, data );
++      },
++
++      removeData: function( elem, name ) {
++              dataUser.remove( elem, name );
++      },
++
++      // 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 );
++      },
++
++      _removeData: function( elem, name ) {
++              dataPriv.remove( elem, name );
++      }
++} );
++
++jQuery.fn.extend( {
++      data: function( key, value ) {
++              var i, name, data,
++                      elem = this[ 0 ],
++                      attrs = elem && elem.attributes;
++
++              // Gets all values
++              if ( key === undefined ) {
++                      if ( this.length ) {
++                              data = dataUser.get( elem );
++
++                              if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) {
++                                      i = attrs.length;
++                                      while ( i-- ) {
++
++                                              // 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 );
++                              }
++                      }
++
++                      return data;
++              }
++
++              // Sets multiple values
++              if ( typeof key === "object" ) {
++                      return this.each( function() {
++                              dataUser.set( this, key );
++                      } );
++              }
++
++              return access( this, function( value ) {
++                      var data;
++
++                      // 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 ) {
++
++                              // 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;
++                              }
++
++                              // Attempt to "discover" the data in
++                              // HTML5 custom data-* attrs
++                              data = dataAttr( elem, key );
++                              if ( data !== undefined ) {
++                                      return data;
++                              }
++
++                              // We tried really hard, but the data doesn't exist.
++                              return;
++                      }
++
++                      // Set the data...
++                      this.each( function() {
++
++                              // We always store the camelCased key
++                              dataUser.set( this, key, value );
++                      } );
++              }, null, value, arguments.length > 1, null, true );
++      },
++
++      removeData: function( key ) {
++              return this.each( function() {
++                      dataUser.remove( this, key );
++              } );
++      }
++} );
++
++
++jQuery.extend( {
++      queue: function( elem, type, data ) {
++              var queue;
++
++              if ( elem ) {
++                      type = ( type || "fx" ) + "queue";
++                      queue = dataPriv.get( elem, type );
++
++                      // 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 || [];
++              }
++      },
++
++      dequeue: function( elem, type ) {
++              type = type || "fx";
++
++              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--;
++              }
++
++              if ( fn ) {
++
++                      // 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 );
++              }
++
++              if ( !startLength && hooks ) {
++                      hooks.empty.fire();
++              }
++      },
++
++      // 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 ] );
++                      } )
++              } );
++      }
++} );
++
++jQuery.fn.extend( {
++      queue: function( type, data ) {
++              var setter = 2;
++
++              if ( typeof type !== "string" ) {
++                      data = type;
++                      type = "fx";
++                      setter--;
++              }
++
++              if ( arguments.length < setter ) {
++                      return jQuery.queue( this[ 0 ], type );
++              }
++
++              return data === undefined ?
++                      this :
++                      this.each( function() {
++                              var queue = jQuery.queue( this, type, data );
++
++                              // Ensure a hooks for this queue
++                              jQuery._queueHooks( this, type );
++
++                              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", [] );
++      },
++
++      // 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 ] );
++                              }
++                      };
++
++              if ( typeof type !== "string" ) {
++                      obj = type;
++                      type = undefined;
++              }
++              type = type || "fx";
++
++              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;
++
++var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" );
++
++
++var cssExpand = [ "Top", "Right", "Bottom", "Left" ];
++
++var isHiddenWithinTree = function( elem, el ) {
++
++              // isHiddenWithinTree might be called from jQuery#filter function;
++              // in that case, element will be second argument
++              elem = el || elem;
++
++              // Inline style trumps all
++              return elem.style.display === "none" ||
++                      elem.style.display === "" &&
++
++                      // Otherwise, check computed style
++                      // Support: Firefox <=43 - 45
++                      // Disconnected elements can have computed display: none, so first confirm that elem is
++                      // in the document.
++                      jQuery.contains( elem.ownerDocument, elem ) &&
++
++                      jQuery.css( elem, "display" ) === "none";
++      };
++
++var swap = function( elem, options, callback, args ) {
++      var ret, name,
++              old = {};
++
++      // Remember the old values, and insert the new ones
++      for ( name in options ) {
++              old[ name ] = elem.style[ name ];
++              elem.style[ name ] = options[ name ];
++      }
++
++      ret = callback.apply( elem, args || [] );
++
++      // Revert the old values
++      for ( name in options ) {
++              elem.style[ name ] = old[ name ];
++      }
++
++      return ret;
++};
++
++
++
++
++function adjustCSS( elem, prop, valueParts, tween ) {
++      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" ),
++
++              // Starting value computation is required for potential unit mismatches
++              initialInUnit = ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) &&
++                      rcssNum.exec( jQuery.css( elem, prop ) );
++
++      if ( initialInUnit && initialInUnit[ 3 ] !== unit ) {
++
++              // Support: Firefox <=54
++              // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144)
++              initial = initial / 2;
++
++              // Trust units reported by jQuery.css
++              unit = unit || initialInUnit[ 3 ];
++
++              // Iteratively approximate from a nonzero starting point
++              initialInUnit = +initial || 1;
++
++              while ( maxIterations-- ) {
++
++                      // 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;
++
++              }
++
++              initialInUnit = initialInUnit * 2;
++              jQuery.style( elem, prop, initialInUnit + unit );
++
++              // Make sure we update the tween properties later on
++              valueParts = valueParts || [];
++      }
++
++      if ( valueParts ) {
++              initialInUnit = +initialInUnit || +initial || 0;
++
++              // 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;
++              }
++      }
++      return adjusted;
++}
++
++
++var defaultDisplayMap = {};
++
++function getDefaultDisplay( elem ) {
++      var temp,
++              doc = elem.ownerDocument,
++              nodeName = elem.nodeName,
++              display = defaultDisplayMap[ nodeName ];
++
++      if ( display ) {
++              return display;
++      }
++
++      temp = doc.body.appendChild( doc.createElement( nodeName ) );
++      display = jQuery.css( temp, "display" );
++
++      temp.parentNode.removeChild( temp );
++
++      if ( display === "none" ) {
++              display = "block";
++      }
++      defaultDisplayMap[ nodeName ] = display;
++
++      return display;
++}
++
++function showHide( elements, show ) {
++      var display, elem,
++              values = [],
++              index = 0,
++              length = elements.length;
++
++      // Determine new display value for elements that need to change
++      for ( ; index < length; index++ ) {
++              elem = elements[ index ];
++              if ( !elem.style ) {
++                      continue;
++              }
++
++              display = elem.style.display;
++              if ( show ) {
++
++                      // 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 {
++                      if ( display !== "none" ) {
++                              values[ index ] = "none";
++
++                              // Remember what we're overwriting
++                              dataPriv.set( elem, "display", display );
++                      }
++              }
++      }
++
++      // 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 ];
++              }
++      }
++
++      return elements;
++}
++
++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();
++              }
++
++              return this.each( function() {
++                      if ( isHiddenWithinTree( this ) ) {
++                              jQuery( this ).show();
++                      } else {
++                              jQuery( this ).hide();
++                      }
++              } );
++      }
++} );
++var rcheckableType = ( /^(?:checkbox|radio)$/i );
++
++var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]+)/i );
++
++var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i );
++
++
++
++// We have to close these tags to support XHTML (#13200)
++var wrapMap = {
++
++      // Support: IE <=9 only
++      option: [ 1, "<select multiple='multiple'>", "</select>" ],
++
++      // XHTML parsers do not magically insert elements in the
++      // same way that tag soup parsers do. So we cannot shorten
++      // this by omitting <tbody> or other required elements.
++      thead: [ 1, "<table>", "</table>" ],
++      col: [ 2, "<table><colgroup>", "</colgroup></table>" ],
++      tr: [ 2, "<table><tbody>", "</tbody></table>" ],
++      td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
++
++      _default: [ 0, "", "" ]
++};
++
++// Support: IE <=9 only
++wrapMap.optgroup = wrapMap.option;
++
++wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
++wrapMap.th = wrapMap.td;
++
++
++function getAll( context, tag ) {
++
++      // Support: IE <=9 - 11 only
++      // Use typeof to avoid zero-argument method invocation on host objects (#15151)
++      var ret;
++
++      if ( typeof context.getElementsByTagName !== "undefined" ) {
++              ret = context.getElementsByTagName( tag || "*" );
++
++      } else if ( typeof context.querySelectorAll !== "undefined" ) {
++              ret = context.querySelectorAll( tag || "*" );
++
++      } else {
++              ret = [];
++      }
++
++      if ( tag === undefined || tag && nodeName( context, tag ) ) {
++              return jQuery.merge( [ context ], ret );
++      }
++
++      return ret;
++}
++
++
++// Mark scripts as having already been evaluated
++function setGlobalEval( elems, refElements ) {
++      var i = 0,
++              l = elems.length;
++
++      for ( ; i < l; i++ ) {
++              dataPriv.set(
++                      elems[ i ],
++                      "globalEval",
++                      !refElements || dataPriv.get( refElements[ i ], "globalEval" )
++              );
++      }
++}
++
++
++var rhtml = /<|&#?\w+;/;
++
++function buildFragment( elems, context, scripts, selection, ignored ) {
++      var elem, tmp, tag, wrap, contains, j,
++              fragment = context.createDocumentFragment(),
++              nodes = [],
++              i = 0,
++              l = elems.length;
++
++      for ( ; i < l; i++ ) {
++              elem = elems[ i ];
++
++              if ( elem || elem === 0 ) {
++
++                      // Add nodes directly
++                      if ( toType( elem ) === "object" ) {
++
++                              // Support: Android <=4.0 only, PhantomJS 1 only
++                              // push.apply(_, arraylike) throws on ancient WebKit
++                              jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );
++
++                      // Convert non-html into a text node
++                      } else if ( !rhtml.test( elem ) ) {
++                              nodes.push( context.createTextNode( elem ) );
++
++                      // Convert html into DOM nodes
++                      } else {
++                              tmp = tmp || fragment.appendChild( context.createElement( "div" ) );
++
++                              // Deserialize a standard representation
++                              tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase();
++                              wrap = wrapMap[ tag ] || wrapMap._default;
++                              tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ];
++
++                              // Descend through wrappers to the right content
++                              j = wrap[ 0 ];
++                              while ( j-- ) {
++                                      tmp = tmp.lastChild;
++                              }
++
++                              // Support: Android <=4.0 only, PhantomJS 1 only
++                              // push.apply(_, arraylike) throws on ancient WebKit
++                              jQuery.merge( nodes, tmp.childNodes );
++
++                              // Remember the top-level container
++                              tmp = fragment.firstChild;
++
++                              // Ensure the created nodes are orphaned (#12392)
++                              tmp.textContent = "";
++                      }
++              }
++      }
++
++      // Remove wrapper from fragment
++      fragment.textContent = "";
++
++      i = 0;
++      while ( ( elem = nodes[ i++ ] ) ) {
++
++              // Skip elements already in the context collection (trac-4087)
++              if ( selection && jQuery.inArray( elem, selection ) > -1 ) {
++                      if ( ignored ) {
++                              ignored.push( elem );
++                      }
++                      continue;
++              }
++
++              contains = jQuery.contains( elem.ownerDocument, elem );
++
++              // Append to fragment
++              tmp = getAll( fragment.appendChild( elem ), "script" );
++
++              // Preserve script evaluation history
++              if ( contains ) {
++                      setGlobalEval( tmp );
++              }
++
++              // Capture executables
++              if ( scripts ) {
++                      j = 0;
++                      while ( ( elem = tmp[ j++ ] ) ) {
++                              if ( rscriptType.test( elem.type || "" ) ) {
++                                      scripts.push( elem );
++                              }
++                      }
++              }
++      }
++
++      return fragment;
++}
++
++
++( function() {
++      var fragment = document.createDocumentFragment(),
++              div = fragment.appendChild( document.createElement( "div" ) ),
++              input = document.createElement( "input" );
++
++      // Support: Android 4.0 - 4.3 only
++      // Check state lost if the name is set (#11217)
++      // Support: Windows Web Apps (WWA)
++      // `name` and `type` must use .setAttribute for WWA (#14901)
++      input.setAttribute( "type", "radio" );
++      input.setAttribute( "checked", "checked" );
++      input.setAttribute( "name", "t" );
++
++      div.appendChild( input );
++
++      // Support: Android <=4.1 only
++      // Older WebKit doesn't clone checked state correctly in fragments
++      support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;
++
++      // Support: IE <=11 only
++      // Make sure textarea (and checkbox) defaultValue is properly cloned
++      div.innerHTML = "<textarea>x</textarea>";
++      support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;
++} )();
++var documentElement = document.documentElement;
++
++
++
++var
++      rkeyEvent = /^key/,
++      rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/,
++      rtypenamespace = /^([^.]*)(?:\.(.+)|)/;
++
++function returnTrue() {
++      return true;
++}
++
++function returnFalse() {
++      return false;
++}
++
++// Support: IE <=9 only
++// See #13393 for more info
++function safeActiveElement() {
++      try {
++              return document.activeElement;
++      } catch ( err ) { }
++}
++
++function on( elem, types, selector, data, fn, one ) {
++      var origFn, type;
++
++      // 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 ) {
++                      on( elem, type, selector, data, types[ type ], one );
++              }
++              return elem;
++      }
++
++      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 elem;
++      }
++
++      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 );
++      } );
++}
++
++/*
++ * 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 );
++
++              // Don't attach events to noData or text/comment nodes (but allow plain objects)
++              if ( !elemData ) {
++                      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;
++              }
++
++              // 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 );
++              }
++
++              // Make sure that the handler has a unique ID, used to find/remove it later
++              if ( !handler.guid ) {
++                      handler.guid = jQuery.guid++;
++              }
++
++              // 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 !== "undefined" && jQuery.event.triggered !== e.type ?
++                                      jQuery.event.dispatch.apply( elem, arguments ) : undefined;
++                      };
++              }
++
++              // 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();
++
++                      // There *must* be a type, no attaching namespace-only handlers
++                      if ( !type ) {
++                              continue;
++                      }
++
++                      // If event changes its type, use the special event handlers for the changed type
++                      special = jQuery.event.special[ type ] || {};
++
++                      // 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 );
++
++                      // Init the event handler queue if we're the first
++                      if ( !( handlers = events[ type ] ) ) {
++                              handlers = events[ type ] = [];
++                              handlers.delegateCount = 0;
++
++                              // Only use addEventListener if the special events handler returns false
++                              if ( !special.setup ||
++                                      special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
++
++                                      if ( elem.addEventListener ) {
++                                              elem.addEventListener( type, eventHandle );
++                                      }
++                              }
++                      }
++
++                      if ( special.add ) {
++                              special.add.call( elem, handleObj );
++
++                              if ( !handleObj.handler.guid ) {
++                                      handleObj.handler.guid = handler.guid;
++                              }
++                      }
++
++                      // Add to the element's handler list, delegates in front
++                      if ( selector ) {
++                              handlers.splice( handlers.delegateCount++, 0, handleObj );
++                      } else {
++                              handlers.push( handleObj );
++                      }
++
++                      // Keep track of which events have ever been used, for event optimization
++                      jQuery.event.global[ type ] = true;
++              }
++
++      },
++
++      // Detach an event or set of events from an element
++      remove: function( elem, types, handler, selector, mappedTypes ) {
++
++              var j, origCount, tmp,
++                      events, t, handleObj,
++                      special, handlers, type, namespaces, origType,
++                      elemData = dataPriv.hasData( elem ) && dataPriv.get( elem );
++
++              if ( !elemData || !( events = elemData.events ) ) {
++                      return;
++              }
++
++              // 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();
++
++                      // 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;
++                      }
++
++                      special = jQuery.event.special[ type ] || {};
++                      type = ( selector ? special.delegateType : special.bindType ) || type;
++                      handlers = events[ type ] || [];
++                      tmp = tmp[ 2 ] &&
++                              new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" );
++
++                      // Remove matching events
++                      origCount = j = handlers.length;
++                      while ( j-- ) {
++                              handleObj = handlers[ j ];
++
++                              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 ( handleObj.selector ) {
++                                              handlers.delegateCount--;
++                                      }
++                                      if ( special.remove ) {
++                                              special.remove.call( elem, handleObj );
++                                      }
++                              }
++                      }
++
++                      // 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 ];
++                      }
++              }
++
++              // Remove data and the expando if it's no longer used
++              if ( jQuery.isEmptyObject( events ) ) {
++                      dataPriv.remove( elem, "handle events" );
++              }
++      },
++
++      dispatch: function( nativeEvent ) {
++
++              // Make a writable jQuery.Event from the native event object
++              var event = jQuery.event.fix( nativeEvent );
++
++              var i, j, ret, matched, handleObj, handlerQueue,
++                      args = new Array( arguments.length ),
++                      handlers = ( dataPriv.get( this, "events" ) || {} )[ event.type ] || [],
++                      special = jQuery.event.special[ event.type ] || {};
++
++              // Use the fix-ed jQuery.Event rather than the (read-only) native event
++              args[ 0 ] = event;
++
++              for ( i = 1; i < arguments.length; i++ ) {
++                      args[ i ] = arguments[ i ];
++              }
++
++              event.delegateTarget = this;
++
++              // Call the preDispatch hook for the mapped type, and let it bail if desired
++              if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
++                      return;
++              }
++
++              // 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() ) {
++
++                              // 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.rnamespace || event.rnamespace.test( handleObj.namespace ) ) {
++
++                                      event.handleObj = handleObj;
++                                      event.data = handleObj.data;
++
++                                      ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle ||
++                                              handleObj.handler ).apply( matched.elem, args );
++
++                                      if ( ret !== undefined ) {
++                                              if ( ( event.result = ret ) === false ) {
++                                                      event.preventDefault();
++                                                      event.stopPropagation();
++                                              }
++                                      }
++                              }
++                      }
++              }
++
++              // Call the postDispatch hook for the mapped type
++              if ( special.postDispatch ) {
++                      special.postDispatch.call( this, event );
++              }
++
++              return event.result;
++      },
++
++      handlers: function( event, handlers ) {
++              var i, handleObj, sel, matchedHandlers, matchedSelectors,
++                      handlerQueue = [],
++                      delegateCount = handlers.delegateCount,
++                      cur = event.target;
++
++              // Find delegate handlers
++              if ( delegateCount &&
++
++                      // Support: IE <=9
++                      // Black-hole SVG <use> instance trees (trac-13180)
++                      cur.nodeType &&
++
++                      // 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 ) ) {
++
++                      for ( ; cur !== this; cur = cur.parentNode || this ) {
++
++                              // 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 ];
++
++                                              // Don't conflict with Object.prototype properties (#13203)
++                                              sel = handleObj.selector + " ";
++
++                                              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 } );
++                                      }
++                              }
++                      }
++              }
++
++              // Add the remaining (directly-bound) handlers
++              cur = this;
++              if ( delegateCount < handlers.length ) {
++                      handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } );
++              }
++
++              return handlerQueue;
++      },
++
++      addProp: function( name, hook ) {
++              Object.defineProperty( jQuery.Event.prototype, name, {
++                      enumerable: true,
++                      configurable: true,
++
++                      get: isFunction( hook ) ?
++                              function() {
++                                      if ( this.originalEvent ) {
++                                                      return hook( this.originalEvent );
++                                      }
++                              } :
++                              function() {
++                                      if ( this.originalEvent ) {
++                                                      return this.originalEvent[ name ];
++                                      }
++                              },
++
++                      set: function( value ) {
++                              Object.defineProperty( this, name, {
++                                      enumerable: true,
++                                      configurable: true,
++                                      writable: true,
++                                      value: value
++                              } );
++                      }
++              } );
++      },
++
++      fix: function( originalEvent ) {
++              return originalEvent[ jQuery.expando ] ?
++                      originalEvent :
++                      new jQuery.Event( originalEvent );
++      },
++
++      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 ) {
++                                      this.focus();
++                                      return false;
++                              }
++                      },
++                      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 ( this.type === "checkbox" && this.click && nodeName( this, "input" ) ) {
++                                      this.click();
++                                      return false;
++                              }
++                      },
++
++                      // For cross-browser consistency, don't fire native .click() on links
++                      _default: function( event ) {
++                              return nodeName( event.target, "a" );
++                      }
++              },
++
++              beforeunload: {
++                      postDispatch: function( event ) {
++
++                              // 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;
++                              }
++                      }
++              }
++      }
++};
++
++jQuery.removeEvent = function( elem, type, handle ) {
++
++      // This "if" is needed for plain objects
++      if ( elem.removeEventListener ) {
++              elem.removeEventListener( type, handle );
++      }
++};
++
++jQuery.Event = function( src, props ) {
++
++      // Allow instantiation without the 'new' keyword
++      if ( !( this instanceof jQuery.Event ) ) {
++              return new jQuery.Event( src, props );
++      }
++
++      // 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.defaultPrevented === undefined &&
++
++                              // Support: Android <=2.3 only
++                              src.returnValue === false ?
++                      returnTrue :
++                      returnFalse;
++
++              // 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;
++
++              this.currentTarget = src.currentTarget;
++              this.relatedTarget = src.relatedTarget;
++
++      // Event type
++      } else {
++              this.type = src;
++      }
++
++      // Put explicitly provided properties onto the event object
++      if ( props ) {
++              jQuery.extend( this, props );
++      }
++
++      // Create a timestamp if incoming event doesn't have one
++      this.timeStamp = src && src.timeStamp || Date.now();
++
++      // Mark it as fixed
++      this[ jQuery.expando ] = true;
++};
++
++// 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,
++
++      preventDefault: function() {
++              var e = this.originalEvent;
++
++              this.isDefaultPrevented = returnTrue;
++
++              if ( e && !this.isSimulated ) {
++                      e.preventDefault();
++              }
++      },
++      stopPropagation: function() {
++              var e = this.originalEvent;
++
++              this.isPropagationStopped = returnTrue;
++
++              if ( e && !this.isSimulated ) {
++                      e.stopPropagation();
++              }
++      },
++      stopImmediatePropagation: function() {
++              var e = this.originalEvent;
++
++              this.isImmediatePropagationStopped = returnTrue;
++
++              if ( e && !this.isSimulated ) {
++                      e.stopImmediatePropagation();
++              }
++
++              this.stopPropagation();
++      }
++};
++
++// 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,
++      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;
++                      }
++
++                      return 0;
++              }
++
++              return event.which;
++      }
++}, jQuery.event.addProp );
++
++// 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,
++
++              handle: function( event ) {
++                      var ret,
++                              target = this,
++                              related = event.relatedTarget,
++                              handleObj = event.handleObj;
++
++                      // 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;
++              }
++      };
++} );
++
++jQuery.fn.extend( {
++
++      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 ) {
++
++                      // ( 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
++
++      /* eslint-disable max-len */
++
++      // See https://github.com/eslint/eslint/issues/3229
++      rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi,
++
++      /* eslint-enable */
++
++      // Support: IE <=10 - 11, Edge 12 - 13 only
++      // In IE/Edge using regex groups here causes severe slowdowns.
++      // See https://connect.microsoft.com/IE/feedback/details/1736512/
++      rnoInnerhtml = /<script|<style|<link/i,
++
++      // checked="checked" or checked
++      rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
++      rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;
++
++// 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" ) ) {
++
++              return jQuery( elem ).children( "tbody" )[ 0 ] || elem;
++      }
++
++      return elem;
++}
++
++// 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 elem;
++}
++
++function cloneCopyEvent( src, dest ) {
++      var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events;
++
++      if ( dest.nodeType !== 1 ) {
++              return;
++      }
++
++      // 1. Copy private data: events, handlers, etc.
++      if ( dataPriv.hasData( src ) ) {
++              pdataOld = dataPriv.access( src );
++              pdataCur = dataPriv.set( dest, pdataOld );
++              events = pdataOld.events;
++
++              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 ] );
++                              }
++                      }
++              }
++      }
++
++      // 2. Copy user data
++      if ( dataUser.hasData( src ) ) {
++              udataOld = dataUser.access( src );
++              udataCur = jQuery.extend( {}, udataOld );
++
++              dataUser.set( dest, udataCur );
++      }
++}
++
++// 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;
++      }
++}
++
++function domManip( collection, args, callback, ignored ) {
++
++      // Flatten any nested arrays
++      args = concat.apply( [], args );
++
++      var fragment, first, scripts, hasScripts, node, doc,
++              i = 0,
++              l = collection.length,
++              iNoClone = l - 1,
++              value = args[ 0 ],
++              valueIsFunction = isFunction( value );
++
++      // 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 );
++              } );
++      }
++
++      if ( l ) {
++              fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored );
++              first = fragment.firstChild;
++
++              if ( fragment.childNodes.length === 1 ) {
++                      fragment = first;
++              }
++
++              // 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;
++
++                      // 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;
++
++                              if ( i !== iNoClone ) {
++                                      node = jQuery.clone( node, true, true );
++
++                                      // Keep references to cloned scripts for later restoration
++                                      if ( hasScripts ) {
++
++                                              // Support: Android <=4.0 only, PhantomJS 1 only
++                                              // push.apply(_, arraylike) throws on ancient WebKit
++                                              jQuery.merge( scripts, getAll( node, "script" ) );
++                                      }
++                              }
++
++                              callback.call( collection[ i ], node, i );
++                      }
++
++                      if ( hasScripts ) {
++                              doc = scripts[ scripts.length - 1 ].ownerDocument;
++
++                              // Reenable scripts
++                              jQuery.map( scripts, restoreScript );
++
++                              // 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 ) {
++                                                              jQuery._evalUrl( node.src );
++                                                      }
++                                              } else {
++                                                      DOMEval( node.textContent.replace( rcleanScript, "" ), doc, node );
++                                              }
++                                      }
++                              }
++                      }
++              }
++      }
++
++      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 ( node.parentNode ) {
++                      if ( keepData && jQuery.contains( node.ownerDocument, node ) ) {
++                              setGlobalEval( getAll( node, "script" ) );
++                      }
++                      node.parentNode.removeChild( node );
++              }
++      }
++
++      return elem;
++}
++
++jQuery.extend( {
++      htmlPrefilter: function( html ) {
++              return html.replace( rxhtmlTag, "<$1></$2>" );
++      },
++
++      clone: function( elem, dataAndEvents, deepDataAndEvents ) {
++              var i, l, srcElements, destElements,
++                      clone = elem.cloneNode( true ),
++                      inPage = jQuery.contains( elem.ownerDocument, elem );
++
++              // Fix IE cloning issues
++              if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) &&
++                              !jQuery.isXMLDoc( elem ) ) {
++
++                      // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2
++                      destElements = getAll( clone );
++                      srcElements = getAll( elem );
++
++                      for ( i = 0, l = srcElements.length; i < l; i++ ) {
++                              fixInput( srcElements[ i ], destElements[ i ] );
++                      }
++              }
++
++              // Copy the events from the original to the clone
++              if ( dataAndEvents ) {
++                      if ( deepDataAndEvents ) {
++                              srcElements = srcElements || getAll( elem );
++                              destElements = destElements || getAll( clone );
++
++                              for ( i = 0, l = srcElements.length; i < l; i++ ) {
++                                      cloneCopyEvent( srcElements[ i ], destElements[ i ] );
++                              }
++                      } else {
++                              cloneCopyEvent( elem, clone );
++                      }
++              }
++
++              // Preserve script evaluation history
++              destElements = getAll( clone, "script" );
++              if ( destElements.length > 0 ) {
++                      setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
++              }
++
++              // Return the cloned set
++              return clone;
++      },
++
++      cleanData: function( elems ) {
++              var data, elem, type,
++                      special = jQuery.event.special,
++                      i = 0;
++
++              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 );
++
++                                                      // This is a shortcut to avoid jQuery.event.remove's overhead
++                                                      } else {
++                                                              jQuery.removeEvent( elem, type, data.handle );
++                                                      }
++                                              }
++                                      }
++
++                                      // Support: Chrome <=35 - 45+
++                                      // Assign undefined instead of using delete, see Data#remove
++                                      elem[ dataPriv.expando ] = undefined;
++                              }
++                              if ( elem[ dataUser.expando ] ) {
++
++                                      // Support: Chrome <=35 - 45+
++                                      // Assign undefined instead of using delete, see Data#remove
++                                      elem[ dataUser.expando ] = undefined;
++                              }
++                      }
++              }
++      }
++} );
++
++jQuery.fn.extend( {
++      detach: function( selector ) {
++              return remove( this, selector, true );
++      },
++
++      remove: function( selector ) {
++              return remove( this, selector );
++      },
++
++      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 );
++      },
++
++      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 );
++                      }
++              } );
++      },
++
++      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 );
++                      }
++              } );
++      },
++
++      before: function() {
++              return domManip( this, arguments, function( elem ) {
++                      if ( this.parentNode ) {
++                              this.parentNode.insertBefore( elem, this );
++                      }
++              } );
++      },
++
++      after: function() {
++              return domManip( this, arguments, function( elem ) {
++                      if ( this.parentNode ) {
++                              this.parentNode.insertBefore( elem, this.nextSibling );
++                      }
++              } );
++      },
++
++      empty: function() {
++              var elem,
++                      i = 0;
++
++              for ( ; ( elem = this[ i ] ) != null; i++ ) {
++                      if ( elem.nodeType === 1 ) {
++
++                              // Prevent memory leaks
++                              jQuery.cleanData( getAll( elem, false ) );
++
++                              // Remove any remaining nodes
++                              elem.textContent = "";
++                      }
++              }
++
++              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 );
++              } );
++      },
++
++      html: function( value ) {
++              return access( this, function( value ) {
++                      var elem = this[ 0 ] || {},
++                              i = 0,
++                              l = this.length;
++
++                      if ( value === undefined && elem.nodeType === 1 ) {
++                              return elem.innerHTML;
++                      }
++
++                      // See if we can take a shortcut and just use innerHTML
++                      if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
++                              !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) {
++
++                              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 using innerHTML throws an exception, use the fallback method
++                              } catch ( e ) {}
++                      }
++
++                      if ( elem ) {
++                              this.empty().append( value );
++                      }
++              }, null, value, arguments.length );
++      },
++
++      replaceWith: function() {
++              var ignored = [];
++
++              // Make the changes, replacing each non-ignored context element with the new content
++              return domManip( this, arguments, function( elem ) {
++                      var parent = this.parentNode;
++
++                      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 this.pushStack( ret );
++      };
++} );
++var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" );
++
++var getStyles = function( elem ) {
++
++              // 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;
++              }
++
++              return view.getComputedStyle( elem );
++      };
++
++var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" );
++
++
++
++( function() {
++
++      // 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;
++              }
++
++              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 );
++
++              var divStyle = window.getComputedStyle( div );
++              pixelPositionVal = divStyle.top !== "1%";
++
++              // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44
++              reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12;
++
++              // 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;
++
++              // Support: IE 9 - 11 only
++              // Detect misreporting of content dimensions for box-sizing:border-box elements
++              boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36;
++
++              // Support: IE 9 only
++              // Detect overflow:scroll screwiness (gh-3699)
++              div.style.position = "absolute";
++              scrollboxSizeVal = div.offsetWidth === 36 || "absolute";
++
++              documentElement.removeChild( container );
++
++              // 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;
++      }
++
++      function roundPixelMeasures( measure ) {
++              return Math.round( parseFloat( measure ) );
++      }
++
++      var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal,
++              reliableMarginLeftVal,
++              container = document.createElement( "div" ),
++              div = document.createElement( "div" );
++
++      // Finish early in limited (non-browser) environments
++      if ( !div.style ) {
++              return;
++      }
++
++      // 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";
++
++      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;
++              }
++      } );
++} )();
++
++
++function curCSS( elem, name, computed ) {
++      var width, minWidth, maxWidth, ret,
++
++              // Support: Firefox 51+
++              // Retrieving style before computed somehow
++              // fixes an issue with getting wrong values
++              // on detached elements
++              style = elem.style;
++
++      computed = computed || getStyles( elem );
++
++      // getPropertyValue is needed for:
++      //   .css('filter') (IE 9 only, #12537)
++      //   .css('--customProperty) (#3144)
++      if ( computed ) {
++              ret = computed.getPropertyValue( name ) || computed[ name ];
++
++              if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {
++                      ret = jQuery.style( elem, name );
++              }
++
++              // A tribute to the "awesome hack by Dean Edwards"
++              // Android Browser returns percentage for some values,
++              // but width seems to be reliably pixels.
++              // This is against the CSSOM draft spec:
++              // https://drafts.csswg.org/cssom/#resolved-values
++              if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) {
++
++                      // Remember the original values
++                      width = style.width;
++                      minWidth = style.minWidth;
++                      maxWidth = style.maxWidth;
++
++                      // Put in the new values to get a computed value out
++                      style.minWidth = style.maxWidth = style.width = ret;
++                      ret = computed.width;
++
++                      // Revert the changed values
++                      style.width = width;
++                      style.minWidth = minWidth;
++                      style.maxWidth = maxWidth;
++              }
++      }
++
++      return ret !== undefined ?
++
++              // Support: IE <=9 - 11 only
++              // IE returns zIndex value as an integer.
++              ret + "" :
++              ret;
++}
++
++
++function addGetHookIf( conditionFn, hookFn ) {
++
++      // Define the hook, we'll check on the first run if it's really needed.
++      return {
++              get: function() {
++                      if ( conditionFn() ) {
++
++                              // Hook not needed (or it's not possible to use it due
++                              // to missing dependency), remove it.
++                              delete this.get;
++                              return;
++                      }
++
++                      // Hook needed; redefine it so that the support test is not executed again.
++                      return ( this.get = hookFn ).apply( this, arguments );
++              }
++      };
++}
++
++
++var
++
++      // Swappable if display is none or starts with table
++      // except "table", "table-cell", or "table-caption"
++      // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
++      rdisplayswap = /^(none|table(?!-c[ea]).+)/,
++      rcustomProp = /^--/,
++      cssShow = { position: "absolute", visibility: "hidden", display: "block" },
++      cssNormalTransform = {
++              letterSpacing: "0",
++              fontWeight: "400"
++      },
++
++      cssPrefixes = [ "Webkit", "Moz", "ms" ],
++      emptyStyle = document.createElement( "div" ).style;
++
++// Return a css property mapped to a potentially vendor prefixed property
++function vendorPropName( name ) {
++
++      // Shortcut for names that are not vendor prefixed
++      if ( name in emptyStyle ) {
++              return name;
++      }
++
++      // Check for vendor prefixed names
++      var capName = name[ 0 ].toUpperCase() + name.slice( 1 ),
++              i = cssPrefixes.length;
++
++      while ( i-- ) {
++              name = cssPrefixes[ i ] + capName;
++              if ( name in emptyStyle ) {
++                      return name;
++              }
++      }
++}
++
++// Return a property mapped along what jQuery.cssProps suggests or to
++// a vendor prefixed property.
++function finalPropName( name ) {
++      var ret = jQuery.cssProps[ name ];
++      if ( !ret ) {
++              ret = jQuery.cssProps[ name ] = vendorPropName( name ) || name;
++      }
++      return ret;
++}
++
++function setPositiveNumber( elem, value, subtract ) {
++
++      // Any relative (+/-) values have already been
++      // normalized at this point
++      var matches = rcssNum.exec( value );
++      return matches ?
++
++              // Guard against undefined "subtract", e.g., when used as in cssHooks
++              Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) :
++              value;
++}
++
++function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) {
++      var i = dimension === "width" ? 1 : 0,
++              extra = 0,
++              delta = 0;
++
++      // Adjustment may not be necessary
++      if ( box === ( isBorderBox ? "border" : "content" ) ) {
++              return 0;
++      }
++
++      for ( ; i < 4; i += 2 ) {
++
++              // Both box models exclude margin
++              if ( box === "margin" ) {
++                      delta += jQuery.css( elem, box + cssExpand[ i ], true, styles );
++              }
++
++              // If we get here with a content-box, we're seeking "padding" or "border" or "margin"
++              if ( !isBorderBox ) {
++
++                      // Add padding
++                      delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
++
++                      // For "border" or "margin", add border
++                      if ( box !== "padding" ) {
++                              delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
++
++                      // But still keep track of it otherwise
++                      } else {
++                              extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
++                      }
++
++              // If we get here with a border-box (content + padding + border), we're seeking "content" or
++              // "padding" or "margin"
++              } else {
++
++                      // For "content", subtract padding
++                      if ( box === "content" ) {
++                              delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
++                      }
++
++                      // For "content" or "padding", subtract border
++                      if ( box !== "margin" ) {
++                              delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
++                      }
++              }
++      }
++
++      // Account for positive content-box scroll gutter when requested by providing computedVal
++      if ( !isBorderBox && computedVal >= 0 ) {
++
++              // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border
++              // Assuming integer scroll gutter, subtract the rest and round down
++              delta += Math.max( 0, Math.ceil(
++                      elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] -
++                      computedVal -
++                      delta -
++                      extra -
++                      0.5
++              ) );
++      }
++
++      return delta;
++}
++
++function getWidthOrHeight( elem, dimension, extra ) {
++
++      // Start with computed style
++      var styles = getStyles( elem ),
++              val = curCSS( elem, dimension, styles ),
++              isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
++              valueIsBorderBox = isBorderBox;
++
++      // Support: Firefox <=54
++      // Return a confounding non-pixel value or feign ignorance, as appropriate.
++      if ( rnumnonpx.test( val ) ) {
++              if ( !extra ) {
++                      return val;
++              }
++              val = "auto";
++      }
++
++      // Check for style in case a browser which returns unreliable values
++      // for getComputedStyle silently falls back to the reliable elem.style
++      valueIsBorderBox = valueIsBorderBox &&
++              ( support.boxSizingReliable() || val === elem.style[ dimension ] );
++
++      // Fall back to offsetWidth/offsetHeight when value is "auto"
++      // This happens for inline elements with no explicit setting (gh-3571)
++      // Support: Android <=4.1 - 4.3 only
++      // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602)
++      if ( val === "auto" ||
++              !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) {
++
++              val = elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ];
++
++              // offsetWidth/offsetHeight provide border-box values
++              valueIsBorderBox = true;
++      }
++
++      // Normalize "" and auto
++      val = parseFloat( val ) || 0;
++
++      // Adjust for the element's box model
++      return ( val +
++              boxModelAdjustment(
++                      elem,
++                      dimension,
++                      extra || ( isBorderBox ? "border" : "content" ),
++                      valueIsBorderBox,
++                      styles,
++
++                      // Provide the current computed size to request scroll gutter calculation (gh-3589)
++                      val
++              )
++      ) + "px";
++}
++
++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;
++                              }
++                      }
++              }
++      },
++
++      // Don't automatically add "px" to these possibly-unitless properties
++      cssNumber: {
++              "animationIterationCount": true,
++              "columnCount": true,
++              "fillOpacity": true,
++              "flexGrow": true,
++              "flexShrink": true,
++              "fontWeight": true,
++              "lineHeight": true,
++              "opacity": true,
++              "order": true,
++              "orphans": true,
++              "widows": true,
++              "zIndex": true,
++              "zoom": true
++      },
++
++      // Add in properties whose names you wish to fix before
++      // setting or getting the value
++      cssProps: {},
++
++      // 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 = camelCase( name ),
++                      isCustomProp = rcustomProp.test( name ),
++                      style = elem.style;
++
++              // Make sure that we're working with the right name. We don't
++              // want to query the value if it is a CSS custom property
++              // since they are user-defined.
++              if ( !isCustomProp ) {
++                      name = finalPropName( origName );
++              }
++
++              // Gets hook for the prefixed version, then unprefixed version
++              hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
++
++              // Check if we're setting a value
++              if ( value !== undefined ) {
++                      type = typeof value;
++
++                      // Convert "+=" or "-=" to relative numbers (#7345)
++                      if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) {
++                              value = adjustCSS( elem, name, ret );
++
++                              // Fixes bug #9237
++                              type = "number";
++                      }
++
++                      // Make sure that null and NaN values aren't set (#7116)
++                      if ( value == null || value !== value ) {
++                              return;
++                      }
++
++                      // If a number was passed in, add the unit (except for certain CSS properties)
++                      if ( type === "number" ) {
++                              value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" );
++                      }
++
++                      // background-* props affect original clone's values
++                      if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) {
++                              style[ name ] = "inherit";
++                      }
++
++                      // 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 ) {
++
++                              if ( isCustomProp ) {
++                                      style.setProperty( name, value );
++                              } else {
++                                      style[ name ] = value;
++                              }
++                      }
++
++              } 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;
++                      }
++
++                      // Otherwise just get the value from the style object
++                      return style[ name ];
++              }
++      },
++
++      css: function( elem, name, extra, styles ) {
++              var val, num, hooks,
++                      origName = camelCase( name ),
++                      isCustomProp = rcustomProp.test( name );
++
++              // Make sure that we're working with the right name. We don't
++              // want to modify the value if it is a CSS custom property
++              // since they are user-defined.
++              if ( !isCustomProp ) {
++                      name = finalPropName( origName );
++              }
++
++              // Try prefixed name followed by the unprefixed name
++              hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
++
++              // If a hook was provided get the computed value from there
++              if ( hooks && "get" in hooks ) {
++                      val = hooks.get( elem, true, extra );
++              }
++
++              // Otherwise, if a way to get the computed value exists, use that
++              if ( val === undefined ) {
++                      val = curCSS( elem, name, styles );
++              }
++
++              // Convert "normal" to computed value
++              if ( val === "normal" && name in cssNormalTransform ) {
++                      val = cssNormalTransform[ name ];
++              }
++
++              // Make numeric if forced or a qualifier was provided and val looks numeric
++              if ( extra === "" || extra ) {
++                      num = parseFloat( val );
++                      return extra === true || isFinite( num ) ? num || 0 : val;
++              }
++
++              return val;
++      }
++} );
++
++jQuery.each( [ "height", "width" ], function( i, dimension ) {
++      jQuery.cssHooks[ dimension ] = {
++              get: function( elem, computed, extra ) {
++                      if ( computed ) {
++
++                              // Certain elements can have dimension info if we invisibly show them
++                              // but it must have a current display style that would benefit
++                              return rdisplayswap.test( jQuery.css( elem, "display" ) ) &&
++
++                                      // Support: Safari 8+
++                                      // Table columns in Safari have non-zero offsetWidth & zero
++                                      // getBoundingClientRect().width unless display is changed.
++                                      // Support: IE <=11 only
++                                      // Running getBoundingClientRect on a disconnected node
++                                      // in IE throws an error.
++                                      ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ?
++                                              swap( elem, cssShow, function() {
++                                                      return getWidthOrHeight( elem, dimension, extra );
++                                              } ) :
++                                              getWidthOrHeight( elem, dimension, extra );
++                      }
++              },
++
++              set: function( elem, value, extra ) {
++                      var matches,
++                              styles = getStyles( elem ),
++                              isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
++                              subtract = extra && boxModelAdjustment(
++                                      elem,
++                                      dimension,
++                                      extra,
++                                      isBorderBox,
++                                      styles
++                              );
++
++                      // Account for unreliable border-box dimensions by comparing offset* to computed and
++                      // faking a content-box to get border and padding (gh-3699)
++                      if ( isBorderBox && support.scrollboxSize() === styles.position ) {
++                              subtract -= Math.ceil(
++                                      elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] -
++                                      parseFloat( styles[ dimension ] ) -
++                                      boxModelAdjustment( elem, dimension, "border", false, styles ) -
++                                      0.5
++                              );
++                      }
++
++                      // Convert to pixels if value adjustment is needed
++                      if ( subtract && ( matches = rcssNum.exec( value ) ) &&
++                              ( matches[ 3 ] || "px" ) !== "px" ) {
++
++                              elem.style[ dimension ] = value;
++                              value = jQuery.css( elem, dimension );
++                      }
++
++                      return setPositiveNumber( elem, value, subtract );
++              }
++      };
++} );
++
++jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft,
++      function( elem, computed ) {
++              if ( computed ) {
++                      return ( parseFloat( curCSS( elem, "marginLeft" ) ) ||
++                              elem.getBoundingClientRect().left -
++                                      swap( elem, { marginLeft: 0 }, function() {
++                                              return elem.getBoundingClientRect().left;
++                                      } )
++                              ) + "px";
++              }
++      }
++);
++
++// These hooks are used by animate to expand properties
++jQuery.each( {
++      margin: "",
++      padding: "",
++      border: "Width"
++}, function( prefix, suffix ) {
++      jQuery.cssHooks[ prefix + suffix ] = {
++              expand: function( value ) {
++                      var i = 0,
++                              expanded = {},
++
++                              // Assumes a single number if not a string
++                              parts = typeof value === "string" ? value.split( " " ) : [ value ];
++
++                      for ( ; i < 4; i++ ) {
++                              expanded[ prefix + cssExpand[ i ] + suffix ] =
++                                      parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
++                      }
++
++                      return expanded;
++              }
++      };
++
++      if ( prefix !== "margin" ) {
++              jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
++      }
++} );
++
++jQuery.fn.extend( {
++      css: function( name, value ) {
++              return access( this, function( elem, name, value ) {
++                      var styles, len,
++                              map = {},
++                              i = 0;
++
++                      if ( Array.isArray( name ) ) {
++                              styles = getStyles( elem );
++                              len = name.length;
++
++                              for ( ; i < len; i++ ) {
++                                      map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
++                              }
++
++                              return map;
++                      }
++
++                      return value !== undefined ?
++                              jQuery.style( elem, name, value ) :
++                              jQuery.css( elem, name );
++              }, name, value, arguments.length > 1 );
++      }
++} );
++
++
++function Tween( elem, options, prop, end, easing ) {
++      return new Tween.prototype.init( elem, options, prop, end, easing );
++}
++jQuery.Tween = Tween;
++
++Tween.prototype = {
++      constructor: Tween,
++      init: function( elem, options, prop, end, easing, unit ) {
++              this.elem = elem;
++              this.prop = prop;
++              this.easing = easing || jQuery.easing._default;
++              this.options = options;
++              this.start = this.now = this.cur();
++              this.end = end;
++              this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
++      },
++      cur: function() {
++              var hooks = Tween.propHooks[ this.prop ];
++
++              return hooks && hooks.get ?
++                      hooks.get( this ) :
++                      Tween.propHooks._default.get( this );
++      },
++      run: function( percent ) {
++              var eased,
++                      hooks = Tween.propHooks[ this.prop ];
++
++              if ( this.options.duration ) {
++                      this.pos = eased = jQuery.easing[ this.easing ](
++                              percent, this.options.duration * percent, 0, 1, this.options.duration
++                      );
++              } else {
++                      this.pos = eased = percent;
++              }
++              this.now = ( this.end - this.start ) * eased + this.start;
++
++              if ( this.options.step ) {
++                      this.options.step.call( this.elem, this.now, this );
++              }
++
++              if ( hooks && hooks.set ) {
++                      hooks.set( this );
++              } else {
++                      Tween.propHooks._default.set( this );
++              }
++              return this;
++      }
++};
++
++Tween.prototype.init.prototype = Tween.prototype;
++
++Tween.propHooks = {
++      _default: {
++              get: function( tween ) {
++                      var result;
++
++                      // Use a property on the element directly when it is not a DOM element,
++                      // or when there is no matching style property that exists.
++                      if ( tween.elem.nodeType !== 1 ||
++                              tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) {
++                              return tween.elem[ tween.prop ];
++                      }
++
++                      // Passing an empty string as a 3rd parameter to .css will automatically
++                      // attempt a parseFloat and fallback to a string if the parse fails.
++                      // Simple values such as "10px" are parsed to Float;
++                      // complex values such as "rotate(1rad)" are returned as-is.
++                      result = jQuery.css( tween.elem, tween.prop, "" );
++
++                      // Empty strings, null, undefined and "auto" are converted to 0.
++                      return !result || result === "auto" ? 0 : result;
++              },
++              set: function( tween ) {
++
++                      // Use step hook for back compat.
++                      // Use cssHook if its there.
++                      // Use .style if available and use plain properties where available.
++                      if ( jQuery.fx.step[ tween.prop ] ) {
++                              jQuery.fx.step[ tween.prop ]( tween );
++                      } else if ( tween.elem.nodeType === 1 &&
++                              ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null ||
++                                      jQuery.cssHooks[ tween.prop ] ) ) {
++                              jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
++                      } else {
++                              tween.elem[ tween.prop ] = tween.now;
++                      }
++              }
++      }
++};
++
++// Support: IE <=9 only
++// Panic based approach to setting things on disconnected nodes
++Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
++      set: function( tween ) {
++              if ( tween.elem.nodeType && tween.elem.parentNode ) {
++                      tween.elem[ tween.prop ] = tween.now;
++              }
++      }
++};
++
++jQuery.easing = {
++      linear: function( p ) {
++              return p;
++      },
++      swing: function( p ) {
++              return 0.5 - Math.cos( p * Math.PI ) / 2;
++      },
++      _default: "swing"
++};
++
++jQuery.fx = Tween.prototype.init;
++
++// Back compat <1.8 extension point
++jQuery.fx.step = {};
++
++
++
++
++var
++      fxNow, inProgress,
++      rfxtypes = /^(?:toggle|show|hide)$/,
++      rrun = /queueHooks$/;
++
++function schedule() {
++      if ( inProgress ) {
++              if ( document.hidden === false && window.requestAnimationFrame ) {
++                      window.requestAnimationFrame( schedule );
++              } else {
++                      window.setTimeout( schedule, jQuery.fx.interval );
++              }
++
++              jQuery.fx.tick();
++      }
++}
++
++// Animations created synchronously will run synchronously
++function createFxNow() {
++      window.setTimeout( function() {
++              fxNow = undefined;
++      } );
++      return ( fxNow = Date.now() );
++}
++
++// Generate parameters to create a standard animation
++function genFx( type, includeWidth ) {
++      var which,
++              i = 0,
++              attrs = { height: type };
++
++      // If we include width, step value is 1 to do all cssExpand values,
++      // otherwise step value is 2 to skip over Left and Right
++      includeWidth = includeWidth ? 1 : 0;
++      for ( ; i < 4; i += 2 - includeWidth ) {
++              which = cssExpand[ i ];
++              attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
++      }
++
++      if ( includeWidth ) {
++              attrs.opacity = attrs.width = type;
++      }
++
++      return attrs;
++}
++
++function createTween( value, prop, animation ) {
++      var tween,
++              collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ),
++              index = 0,
++              length = collection.length;
++      for ( ; index < length; index++ ) {
++              if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) {
++
++                      // We're done with this property
++                      return tween;
++              }
++      }
++}
++
++function defaultPrefilter( elem, props, opts ) {
++      var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display,
++              isBox = "width" in props || "height" in props,
++              anim = this,
++              orig = {},
++              style = elem.style,
++              hidden = elem.nodeType && isHiddenWithinTree( elem ),
++              dataShow = dataPriv.get( elem, "fxshow" );
++
++      // Queue-skipping animations hijack the fx hooks
++      if ( !opts.queue ) {
++              hooks = jQuery._queueHooks( elem, "fx" );
++              if ( hooks.unqueued == null ) {
++                      hooks.unqueued = 0;
++                      oldfire = hooks.empty.fire;
++                      hooks.empty.fire = function() {
++                              if ( !hooks.unqueued ) {
++                                      oldfire();
++                              }
++                      };
++              }
++              hooks.unqueued++;
++
++              anim.always( function() {
++
++                      // Ensure the complete handler is called before this completes
++                      anim.always( function() {
++                              hooks.unqueued--;
++                              if ( !jQuery.queue( elem, "fx" ).length ) {
++                                      hooks.empty.fire();
++                              }
++                      } );
++              } );
++      }
++
++      // Detect show/hide animations
++      for ( prop in props ) {
++              value = props[ prop ];
++              if ( rfxtypes.test( value ) ) {
++                      delete props[ prop ];
++                      toggle = toggle || value === "toggle";
++                      if ( value === ( hidden ? "hide" : "show" ) ) {
++
++                              // Pretend to be hidden if this is a "show" and
++                              // there is still data from a stopped show/hide
++                              if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) {
++                                      hidden = true;
++
++                              // Ignore all other no-op show/hide data
++                              } else {
++                                      continue;
++                              }
++                      }
++                      orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );
++              }
++      }
++
++      // Bail out if this is a no-op like .hide().hide()
++      propTween = !jQuery.isEmptyObject( props );
++      if ( !propTween && jQuery.isEmptyObject( orig ) ) {
++              return;
++      }
++
++      // Restrict "overflow" and "display" styles during box animations
++      if ( isBox && elem.nodeType === 1 ) {
++
++              // Support: IE <=9 - 11, Edge 12 - 15
++              // Record all 3 overflow attributes because IE does not infer the shorthand
++              // from identically-valued overflowX and overflowY and Edge just mirrors
++              // the overflowX value there.
++              opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
++
++              // Identify a display type, preferring old show/hide data over the CSS cascade
++              restoreDisplay = dataShow && dataShow.display;
++              if ( restoreDisplay == null ) {
++                      restoreDisplay = dataPriv.get( elem, "display" );
++              }
++              display = jQuery.css( elem, "display" );
++              if ( display === "none" ) {
++                      if ( restoreDisplay ) {
++                              display = restoreDisplay;
++                      } else {
++
++                              // Get nonempty value(s) by temporarily forcing visibility
++                              showHide( [ elem ], true );
++                              restoreDisplay = elem.style.display || restoreDisplay;
++                              display = jQuery.css( elem, "display" );
++                              showHide( [ elem ] );
++                      }
++              }
++
++              // Animate inline elements as inline-block
++              if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) {
++                      if ( jQuery.css( elem, "float" ) === "none" ) {
++
++                              // Restore the original display value at the end of pure show/hide animations
++                              if ( !propTween ) {
++                                      anim.done( function() {
++                                              style.display = restoreDisplay;
++                                      } );
++                                      if ( restoreDisplay == null ) {
++                                              display = style.display;
++                                              restoreDisplay = display === "none" ? "" : display;
++                                      }
++                              }
++                              style.display = "inline-block";
++                      }
++              }
++      }
++
++      if ( opts.overflow ) {
++              style.overflow = "hidden";
++              anim.always( function() {
++                      style.overflow = opts.overflow[ 0 ];
++                      style.overflowX = opts.overflow[ 1 ];
++                      style.overflowY = opts.overflow[ 2 ];
++              } );
++      }
++
++      // Implement show/hide animations
++      propTween = false;
++      for ( prop in orig ) {
++
++              // General show/hide setup for this element animation
++              if ( !propTween ) {
++                      if ( dataShow ) {
++                              if ( "hidden" in dataShow ) {
++                                      hidden = dataShow.hidden;
++                              }
++                      } else {
++                              dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } );
++                      }
++
++                      // Store hidden/visible for toggle so `.stop().toggle()` "reverses"
++                      if ( toggle ) {
++                              dataShow.hidden = !hidden;
++                      }
++
++                      // Show elements before animating them
++                      if ( hidden ) {
++                              showHide( [ elem ], true );
++                      }
++
++                      /* eslint-disable no-loop-func */
++
++                      anim.done( function() {
++
++                      /* eslint-enable no-loop-func */
++
++                              // The final step of a "hide" animation is actually hiding the element
++                              if ( !hidden ) {
++                                      showHide( [ elem ] );
++                              }
++                              dataPriv.remove( elem, "fxshow" );
++                              for ( prop in orig ) {
++                                      jQuery.style( elem, prop, orig[ prop ] );
++                              }
++                      } );
++              }
++
++              // Per-property setup
++              propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );
++              if ( !( prop in dataShow ) ) {
++                      dataShow[ prop ] = propTween.start;
++                      if ( hidden ) {
++                              propTween.end = propTween.start;
++                              propTween.start = 0;
++                      }
++              }
++      }
++}
++
++function propFilter( props, specialEasing ) {
++      var index, name, easing, value, hooks;
++
++      // camelCase, specialEasing and expand cssHook pass
++      for ( index in props ) {
++              name = camelCase( index );
++              easing = specialEasing[ name ];
++              value = props[ index ];
++              if ( Array.isArray( value ) ) {
++                      easing = value[ 1 ];
++                      value = props[ index ] = value[ 0 ];
++              }
++
++              if ( index !== name ) {
++                      props[ name ] = value;
++                      delete props[ index ];
++              }
++
++              hooks = jQuery.cssHooks[ name ];
++              if ( hooks && "expand" in hooks ) {
++                      value = hooks.expand( value );
++                      delete props[ name ];
++
++                      // Not quite $.extend, this won't overwrite existing keys.
++                      // Reusing 'index' because we have the correct "name"
++                      for ( index in value ) {
++                              if ( !( index in props ) ) {
++                                      props[ index ] = value[ index ];
++                                      specialEasing[ index ] = easing;
++                              }
++                      }
++              } else {
++                      specialEasing[ name ] = easing;
++              }
++      }
++}
++
++function Animation( elem, properties, options ) {
++      var result,
++              stopped,
++              index = 0,
++              length = Animation.prefilters.length,
++              deferred = jQuery.Deferred().always( function() {
++
++                      // Don't match elem in the :animated selector
++                      delete tick.elem;
++              } ),
++              tick = function() {
++                      if ( stopped ) {
++                              return false;
++                      }
++                      var currentTime = fxNow || createFxNow(),
++                              remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
++
++                              // Support: Android 2.3 only
++                              // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497)
++                              temp = remaining / animation.duration || 0,
++                              percent = 1 - temp,
++                              index = 0,
++                              length = animation.tweens.length;
++
++                      for ( ; index < length; index++ ) {
++                              animation.tweens[ index ].run( percent );
++                      }
++
++                      deferred.notifyWith( elem, [ animation, percent, remaining ] );
++
++                      // If there's more to do, yield
++                      if ( percent < 1 && length ) {
++                              return remaining;
++                      }
++
++                      // If this was an empty animation, synthesize a final progress notification
++                      if ( !length ) {
++                              deferred.notifyWith( elem, [ animation, 1, 0 ] );
++                      }
++
++                      // Resolve the animation and report its conclusion
++                      deferred.resolveWith( elem, [ animation ] );
++                      return false;
++              },
++              animation = deferred.promise( {
++                      elem: elem,
++                      props: jQuery.extend( {}, properties ),
++                      opts: jQuery.extend( true, {
++                              specialEasing: {},
++                              easing: jQuery.easing._default
++                      }, options ),
++                      originalProperties: properties,
++                      originalOptions: options,
++                      startTime: fxNow || createFxNow(),
++                      duration: options.duration,
++                      tweens: [],
++                      createTween: function( prop, end ) {
++                              var tween = jQuery.Tween( elem, animation.opts, prop, end,
++                                              animation.opts.specialEasing[ prop ] || animation.opts.easing );
++                              animation.tweens.push( tween );
++                              return tween;
++                      },
++                      stop: function( gotoEnd ) {
++                              var index = 0,
++
++                                      // If we are going to the end, we want to run all the tweens
++                                      // otherwise we skip this part
++                                      length = gotoEnd ? animation.tweens.length : 0;
++                              if ( stopped ) {
++                                      return this;
++                              }
++                              stopped = true;
++                              for ( ; index < length; index++ ) {
++                                      animation.tweens[ index ].run( 1 );
++                              }
++
++                              // Resolve when we played the last frame; otherwise, reject
++                              if ( gotoEnd ) {
++                                      deferred.notifyWith( elem, [ animation, 1, 0 ] );
++                                      deferred.resolveWith( elem, [ animation, gotoEnd ] );
++                              } else {
++                                      deferred.rejectWith( elem, [ animation, gotoEnd ] );
++                              }
++                              return this;
++                      }
++              } ),
++              props = animation.props;
++
++      propFilter( props, animation.opts.specialEasing );
++
++      for ( ; index < length; index++ ) {
++              result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts );
++              if ( result ) {
++                      if ( isFunction( result.stop ) ) {
++                              jQuery._queueHooks( animation.elem, animation.opts.queue ).stop =
++                                      result.stop.bind( result );
++                      }
++                      return result;
++              }
++      }
++
++      jQuery.map( props, createTween, animation );
++
++      if ( isFunction( animation.opts.start ) ) {
++              animation.opts.start.call( elem, animation );
++      }
++
++      // Attach callbacks from options
++      animation
++              .progress( animation.opts.progress )
++              .done( animation.opts.done, animation.opts.complete )
++              .fail( animation.opts.fail )
++              .always( animation.opts.always );
++
++      jQuery.fx.timer(
++              jQuery.extend( tick, {
++                      elem: elem,
++                      anim: animation,
++                      queue: animation.opts.queue
++              } )
++      );
++
++      return animation;
++}
++
++jQuery.Animation = jQuery.extend( Animation, {
++
++      tweeners: {
++              "*": [ function( prop, value ) {
++                      var tween = this.createTween( prop, value );
++                      adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween );
++                      return tween;
++              } ]
++      },
++
++      tweener: function( props, callback ) {
++              if ( isFunction( props ) ) {
++                      callback = props;
++                      props = [ "*" ];
++              } else {
++                      props = props.match( rnothtmlwhite );
++              }
++
++              var prop,
++                      index = 0,
++                      length = props.length;
++
++              for ( ; index < length; index++ ) {
++                      prop = props[ index ];
++                      Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || [];
++                      Animation.tweeners[ prop ].unshift( callback );
++              }
++      },
++
++      prefilters: [ defaultPrefilter ],
++
++      prefilter: function( callback, prepend ) {
++              if ( prepend ) {
++                      Animation.prefilters.unshift( callback );
++              } else {
++                      Animation.prefilters.push( callback );
++              }
++      }
++} );
++
++jQuery.speed = function( speed, easing, fn ) {
++      var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
++              complete: fn || !fn && easing ||
++                      isFunction( speed ) && speed,
++              duration: speed,
++              easing: fn && easing || easing && !isFunction( easing ) && easing
++      };
++
++      // Go to the end state if fx are off
++      if ( jQuery.fx.off ) {
++              opt.duration = 0;
++
++      } else {
++              if ( typeof opt.duration !== "number" ) {
++                      if ( opt.duration in jQuery.fx.speeds ) {
++                              opt.duration = jQuery.fx.speeds[ opt.duration ];
++
++                      } else {
++                              opt.duration = jQuery.fx.speeds._default;
++                      }
++              }
++      }
++
++      // Normalize opt.queue - true/undefined/null -> "fx"
++      if ( opt.queue == null || opt.queue === true ) {
++              opt.queue = "fx";
++      }
++
++      // Queueing
++      opt.old = opt.complete;
++
++      opt.complete = function() {
++              if ( isFunction( opt.old ) ) {
++                      opt.old.call( this );
++              }
++
++              if ( opt.queue ) {
++                      jQuery.dequeue( this, opt.queue );
++              }
++      };
++
++      return opt;
++};
++
++jQuery.fn.extend( {
++      fadeTo: function( speed, to, easing, callback ) {
++
++              // Show any hidden elements after setting opacity to 0
++              return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show()
++
++                      // Animate to the value specified
++                      .end().animate( { opacity: to }, speed, easing, callback );
++      },
++      animate: function( prop, speed, easing, callback ) {
++              var empty = jQuery.isEmptyObject( prop ),
++                      optall = jQuery.speed( speed, easing, callback ),
++                      doAnimation = function() {
++
++                              // Operate on a copy of prop so per-property easing won't be lost
++                              var anim = Animation( this, jQuery.extend( {}, prop ), optall );
++
++                              // Empty animations, or finishing resolves immediately
++                              if ( empty || dataPriv.get( this, "finish" ) ) {
++                                      anim.stop( true );
++                              }
++                      };
++                      doAnimation.finish = doAnimation;
++
++              return empty || optall.queue === false ?
++                      this.each( doAnimation ) :
++                      this.queue( optall.queue, doAnimation );
++      },
++      stop: function( type, clearQueue, gotoEnd ) {
++              var stopQueue = function( hooks ) {
++                      var stop = hooks.stop;
++                      delete hooks.stop;
++                      stop( gotoEnd );
++              };
++
++              if ( typeof type !== "string" ) {
++                      gotoEnd = clearQueue;
++                      clearQueue = type;
++                      type = undefined;
++              }
++              if ( clearQueue && type !== false ) {
++                      this.queue( type || "fx", [] );
++              }
++
++              return this.each( function() {
++                      var dequeue = true,
++                              index = type != null && type + "queueHooks",
++                              timers = jQuery.timers,
++                              data = dataPriv.get( this );
++
++                      if ( index ) {
++                              if ( data[ index ] && data[ index ].stop ) {
++                                      stopQueue( data[ index ] );
++                              }
++                      } else {
++                              for ( index in data ) {
++                                      if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
++                                              stopQueue( data[ index ] );
++                                      }
++                              }
++                      }
++
++                      for ( index = timers.length; index--; ) {
++                              if ( timers[ index ].elem === this &&
++                                      ( type == null || timers[ index ].queue === type ) ) {
++
++                                      timers[ index ].anim.stop( gotoEnd );
++                                      dequeue = false;
++                                      timers.splice( index, 1 );
++                              }
++                      }
++
++                      // Start the next in the queue if the last step wasn't forced.
++                      // Timers currently will call their complete callbacks, which
++                      // will dequeue but only if they were gotoEnd.
++                      if ( dequeue || !gotoEnd ) {
++                              jQuery.dequeue( this, type );
++                      }
++              } );
++      },
++      finish: function( type ) {
++              if ( type !== false ) {
++                      type = type || "fx";
++              }
++              return this.each( function() {
++                      var index,
++                              data = dataPriv.get( this ),
++                              queue = data[ type + "queue" ],
++                              hooks = data[ type + "queueHooks" ],
++                              timers = jQuery.timers,
++                              length = queue ? queue.length : 0;
++
++                      // Enable finishing flag on private data
++                      data.finish = true;
++
++                      // Empty the queue first
++                      jQuery.queue( this, type, [] );
++
++                      if ( hooks && hooks.stop ) {
++                              hooks.stop.call( this, true );
++                      }
++
++                      // Look for any active animations, and finish them
++                      for ( index = timers.length; index--; ) {
++                              if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
++                                      timers[ index ].anim.stop( true );
++                                      timers.splice( index, 1 );
++                              }
++                      }
++
++                      // Look for any animations in the old queue and finish them
++                      for ( index = 0; index < length; index++ ) {
++                              if ( queue[ index ] && queue[ index ].finish ) {
++                                      queue[ index ].finish.call( this );
++                              }
++                      }
++
++                      // Turn off finishing flag
++                      delete data.finish;
++              } );
++      }
++} );
++
++jQuery.each( [ "toggle", "show", "hide" ], function( i, name ) {
++      var cssFn = jQuery.fn[ name ];
++      jQuery.fn[ name ] = function( speed, easing, callback ) {
++              return speed == null || typeof speed === "boolean" ?
++                      cssFn.apply( this, arguments ) :
++                      this.animate( genFx( name, true ), speed, easing, callback );
++      };
++} );
++
++// Generate shortcuts for custom animations
++jQuery.each( {
++      slideDown: genFx( "show" ),
++      slideUp: genFx( "hide" ),
++      slideToggle: genFx( "toggle" ),
++      fadeIn: { opacity: "show" },
++      fadeOut: { opacity: "hide" },
++      fadeToggle: { opacity: "toggle" }
++}, function( name, props ) {
++      jQuery.fn[ name ] = function( speed, easing, callback ) {
++              return this.animate( props, speed, easing, callback );
++      };
++} );
++
++jQuery.timers = [];
++jQuery.fx.tick = function() {
++      var timer,
++              i = 0,
++              timers = jQuery.timers;
++
++      fxNow = Date.now();
++
++      for ( ; i < timers.length; i++ ) {
++              timer = timers[ i ];
++
++              // Run the timer and safely remove it when done (allowing for external removal)
++              if ( !timer() && timers[ i ] === timer ) {
++                      timers.splice( i--, 1 );
++              }
++      }
++
++      if ( !timers.length ) {
++              jQuery.fx.stop();
++      }
++      fxNow = undefined;
++};
++
++jQuery.fx.timer = function( timer ) {
++      jQuery.timers.push( timer );
++      jQuery.fx.start();
++};
++
++jQuery.fx.interval = 13;
++jQuery.fx.start = function() {
++      if ( inProgress ) {
++              return;
++      }
++
++      inProgress = true;
++      schedule();
++};
++
++jQuery.fx.stop = function() {
++      inProgress = null;
++};
++
++jQuery.fx.speeds = {
++      slow: 600,
++      fast: 200,
++
++      // Default speed
++      _default: 400
++};
++
++
++// Based off of the plugin by Clint Helfers, with permission.
++// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/
++jQuery.fn.delay = function( time, type ) {
++      time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
++      type = type || "fx";
++
++      return this.queue( type, function( next, hooks ) {
++              var timeout = window.setTimeout( next, time );
++              hooks.stop = function() {
++                      window.clearTimeout( timeout );
++              };
++      } );
++};
++
++
++( function() {
++      var input = document.createElement( "input" ),
++              select = document.createElement( "select" ),
++              opt = select.appendChild( document.createElement( "option" ) );
++
++      input.type = "checkbox";
++
++      // Support: Android <=4.3 only
++      // Default value for a checkbox should be "on"
++      support.checkOn = input.value !== "";
++
++      // Support: IE <=11 only
++      // Must access selectedIndex to make default options select
++      support.optSelected = opt.selected;
++
++      // Support: IE <=11 only
++      // An input loses its value after becoming a radio
++      input = document.createElement( "input" );
++      input.value = "t";
++      input.type = "radio";
++      support.radioValue = input.value === "t";
++} )();
++
++
++var boolHook,
++      attrHandle = jQuery.expr.attrHandle;
++
++jQuery.fn.extend( {
++      attr: function( name, value ) {
++              return access( this, jQuery.attr, name, value, arguments.length > 1 );
++      },
++
++      removeAttr: function( name ) {
++              return this.each( function() {
++                      jQuery.removeAttr( this, name );
++              } );
++      }
++} );
++
++jQuery.extend( {
++      attr: function( elem, name, value ) {
++              var ret, hooks,
++                      nType = elem.nodeType;
++
++              // Don't get/set attributes on text, comment and attribute nodes
++              if ( nType === 3 || nType === 8 || nType === 2 ) {
++                      return;
++              }
++
++              // Fallback to prop when attributes are not supported
++              if ( typeof elem.getAttribute === "undefined" ) {
++                      return jQuery.prop( elem, name, value );
++              }
++
++              // Attribute hooks are determined by the lowercase version
++              // Grab necessary hook if one is defined
++              if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
++                      hooks = jQuery.attrHooks[ name.toLowerCase() ] ||
++                              ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined );
++              }
++
++              if ( value !== undefined ) {
++                      if ( value === null ) {
++                              jQuery.removeAttr( elem, name );
++                              return;
++                      }
++
++                      if ( hooks && "set" in hooks &&
++                              ( ret = hooks.set( elem, value, name ) ) !== undefined ) {
++                              return ret;
++                      }
++
++                      elem.setAttribute( name, value + "" );
++                      return value;
++              }
++
++              if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {
++                      return ret;
++              }
++
++              ret = jQuery.find.attr( elem, name );
++
++              // Non-existent attributes return null, we normalize to undefined
++              return ret == null ? undefined : ret;
++      },
++
++      attrHooks: {
++              type: {
++                      set: function( elem, value ) {
++                              if ( !support.radioValue && value === "radio" &&
++                                      nodeName( elem, "input" ) ) {
++                                      var val = elem.value;
++                                      elem.setAttribute( "type", value );
++                                      if ( val ) {
++                                              elem.value = val;
++                                      }
++                                      return value;
++                              }
++                      }
++              }
++      },
++
++      removeAttr: function( elem, value ) {
++              var name,
++                      i = 0,
++
++                      // Attribute names can contain non-HTML whitespace characters
++                      // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
++                      attrNames = value && value.match( rnothtmlwhite );
++
++              if ( attrNames && elem.nodeType === 1 ) {
++                      while ( ( name = attrNames[ i++ ] ) ) {
++                              elem.removeAttribute( name );
++                      }
++              }
++      }
++} );
++
++// Hooks for boolean attributes
++boolHook = {
++      set: function( elem, value, name ) {
++              if ( value === false ) {
++
++                      // Remove boolean attributes when set to false
++                      jQuery.removeAttr( elem, name );
++              } else {
++                      elem.setAttribute( name, name );
++              }
++              return name;
++      }
++};
++
++jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) {
++      var getter = attrHandle[ name ] || jQuery.find.attr;
++
++      attrHandle[ name ] = function( elem, name, isXML ) {
++              var ret, handle,
++                      lowercaseName = name.toLowerCase();
++
++              if ( !isXML ) {
++
++                      // Avoid an infinite loop by temporarily removing this function from the getter
++                      handle = attrHandle[ lowercaseName ];
++                      attrHandle[ lowercaseName ] = ret;
++                      ret = getter( elem, name, isXML ) != null ?
++                              lowercaseName :
++                              null;
++                      attrHandle[ lowercaseName ] = handle;
++              }
++              return ret;
++      };
++} );
++
++
++
++
++var rfocusable = /^(?:input|select|textarea|button)$/i,
++      rclickable = /^(?:a|area)$/i;
++
++jQuery.fn.extend( {
++      prop: function( name, value ) {
++              return access( this, jQuery.prop, name, value, arguments.length > 1 );
++      },
++
++      removeProp: function( name ) {
++              return this.each( function() {
++                      delete this[ jQuery.propFix[ name ] || name ];
++              } );
++      }
++} );
++
++jQuery.extend( {
++      prop: function( elem, name, value ) {
++              var ret, hooks,
++                      nType = elem.nodeType;
++
++              // Don't get/set properties on text, comment and attribute nodes
++              if ( nType === 3 || nType === 8 || nType === 2 ) {
++                      return;
++              }
++
++              if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
++
++                      // Fix name and attach hooks
++                      name = jQuery.propFix[ name ] || name;
++                      hooks = jQuery.propHooks[ name ];
++              }
++
++              if ( value !== undefined ) {
++                      if ( hooks && "set" in hooks &&
++                              ( ret = hooks.set( elem, value, name ) ) !== undefined ) {
++                              return ret;
++                      }
++
++                      return ( elem[ name ] = value );
++              }
++
++              if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {
++                      return ret;
++              }
++
++              return elem[ name ];
++      },
++
++      propHooks: {
++              tabIndex: {
++                      get: function( elem ) {
++
++                              // Support: IE <=9 - 11 only
++                              // elem.tabIndex doesn't always return the
++                              // correct value when it hasn't been explicitly set
++                              // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
++                              // Use proper attribute retrieval(#12072)
++                              var tabindex = jQuery.find.attr( elem, "tabindex" );
++
++                              if ( tabindex ) {
++                                      return parseInt( tabindex, 10 );
++                              }
++
++                              if (
++                                      rfocusable.test( elem.nodeName ) ||
++                                      rclickable.test( elem.nodeName ) &&
++                                      elem.href
++                              ) {
++                                      return 0;
++                              }
++
++                              return -1;
++                      }
++              }
++      },
++
++      propFix: {
++              "for": "htmlFor",
++              "class": "className"
++      }
++} );
++
++// Support: IE <=11 only
++// Accessing the selectedIndex property
++// forces the browser to respect setting selected
++// on the option
++// The getter ensures a default option is selected
++// when in an optgroup
++// eslint rule "no-unused-expressions" is disabled for this code
++// since it considers such accessions noop
++if ( !support.optSelected ) {
++      jQuery.propHooks.selected = {
++              get: function( elem ) {
++
++                      /* eslint no-unused-expressions: "off" */
++
++                      var parent = elem.parentNode;
++                      if ( parent && parent.parentNode ) {
++                              parent.parentNode.selectedIndex;
++                      }
++                      return null;
++              },
++              set: function( elem ) {
++
++                      /* eslint no-unused-expressions: "off" */
++
++                      var parent = elem.parentNode;
++                      if ( parent ) {
++                              parent.selectedIndex;
++
++                              if ( parent.parentNode ) {
++                                      parent.parentNode.selectedIndex;
++                              }
++                      }
++              }
++      };
++}
++
++jQuery.each( [
++      "tabIndex",
++      "readOnly",
++      "maxLength",
++      "cellSpacing",
++      "cellPadding",
++      "rowSpan",
++      "colSpan",
++      "useMap",
++      "frameBorder",
++      "contentEditable"
++], function() {
++      jQuery.propFix[ this.toLowerCase() ] = this;
++} );
++
++
++
++
++      // Strip and collapse whitespace according to HTML spec
++      // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace
++      function stripAndCollapse( value ) {
++              var tokens = value.match( rnothtmlwhite ) || [];
++              return tokens.join( " " );
++      }
++
++
++function getClass( elem ) {
++      return elem.getAttribute && elem.getAttribute( "class" ) || "";
++}
++
++function classesToArray( value ) {
++      if ( Array.isArray( value ) ) {
++              return value;
++      }
++      if ( typeof value === "string" ) {
++              return value.match( rnothtmlwhite ) || [];
++      }
++      return [];
++}
++
++jQuery.fn.extend( {
++      addClass: function( value ) {
++              var classes, elem, cur, curValue, clazz, j, finalValue,
++                      i = 0;
++
++              if ( isFunction( value ) ) {
++                      return this.each( function( j ) {
++                              jQuery( this ).addClass( value.call( this, j, getClass( this ) ) );
++                      } );
++              }
++
++              classes = classesToArray( value );
++
++              if ( classes.length ) {
++                      while ( ( elem = this[ i++ ] ) ) {
++                              curValue = getClass( elem );
++                              cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );
++
++                              if ( cur ) {
++                                      j = 0;
++                                      while ( ( clazz = classes[ j++ ] ) ) {
++                                              if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
++                                                      cur += clazz + " ";
++                                              }
++                                      }
++
++                                      // Only assign if different to avoid unneeded rendering.
++                                      finalValue = stripAndCollapse( cur );
++                                      if ( curValue !== finalValue ) {
++                                              elem.setAttribute( "class", finalValue );
++                                      }
++                              }
++                      }
++              }
++
++              return this;
++      },
++
++      removeClass: function( value ) {
++              var classes, elem, cur, curValue, clazz, j, finalValue,
++                      i = 0;
++
++              if ( isFunction( value ) ) {
++                      return this.each( function( j ) {
++                              jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) );
++                      } );
++              }
++
++              if ( !arguments.length ) {
++                      return this.attr( "class", "" );
++              }
++
++              classes = classesToArray( value );
++
++              if ( classes.length ) {
++                      while ( ( elem = this[ i++ ] ) ) {
++                              curValue = getClass( elem );
++
++                              // This expression is here for better compressibility (see addClass)
++                              cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );
++
++                              if ( cur ) {
++                                      j = 0;
++                                      while ( ( clazz = classes[ j++ ] ) ) {
++
++                                              // Remove *all* instances
++                                              while ( cur.indexOf( " " + clazz + " " ) > -1 ) {
++                                                      cur = cur.replace( " " + clazz + " ", " " );
++                                              }
++                                      }
++
++                                      // Only assign if different to avoid unneeded rendering.
++                                      finalValue = stripAndCollapse( cur );
++                                      if ( curValue !== finalValue ) {
++                                              elem.setAttribute( "class", finalValue );
++                                      }
++                              }
++                      }
++              }
++
++              return this;
++      },
++
++      toggleClass: function( value, stateVal ) {
++              var type = typeof value,
++                      isValidValue = type === "string" || Array.isArray( value );
++
++              if ( typeof stateVal === "boolean" && isValidValue ) {
++                      return stateVal ? this.addClass( value ) : this.removeClass( value );
++              }
++
++              if ( isFunction( value ) ) {
++                      return this.each( function( i ) {
++                              jQuery( this ).toggleClass(
++                                      value.call( this, i, getClass( this ), stateVal ),
++                                      stateVal
++                              );
++                      } );
++              }
++
++              return this.each( function() {
++                      var className, i, self, classNames;
++
++                      if ( isValidValue ) {
++
++                              // Toggle individual class names
++                              i = 0;
++                              self = jQuery( this );
++                              classNames = classesToArray( value );
++
++                              while ( ( className = classNames[ i++ ] ) ) {
++
++                                      // Check each className given, space separated list
++                                      if ( self.hasClass( className ) ) {
++                                              self.removeClass( className );
++                                      } else {
++                                              self.addClass( className );
++                                      }
++                              }
++
++                      // Toggle whole class name
++                      } else if ( value === undefined || type === "boolean" ) {
++                              className = getClass( this );
++                              if ( className ) {
++
++                                      // Store className if set
++                                      dataPriv.set( this, "__className__", className );
++                              }
++
++                              // If the element has a class name or if we're passed `false`,
++                              // then remove the whole classname (if there was one, the above saved it).
++                              // Otherwise bring back whatever was previously saved (if anything),
++                              // falling back to the empty string if nothing was stored.
++                              if ( this.setAttribute ) {
++                                      this.setAttribute( "class",
++                                              className || value === false ?
++                                              "" :
++                                              dataPriv.get( this, "__className__" ) || ""
++                                      );
++                              }
++                      }
++              } );
++      },
++
++      hasClass: function( selector ) {
++              var className, elem,
++                      i = 0;
++
++              className = " " + selector + " ";
++              while ( ( elem = this[ i++ ] ) ) {
++                      if ( elem.nodeType === 1 &&
++                              ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) {
++                                      return true;
++                      }
++              }
++
++              return false;
++      }
++} );
++
++
++
++
++var rreturn = /\r/g;
++
++jQuery.fn.extend( {
++      val: function( value ) {
++              var hooks, ret, valueIsFunction,
++                      elem = this[ 0 ];
++
++              if ( !arguments.length ) {
++                      if ( elem ) {
++                              hooks = jQuery.valHooks[ elem.type ] ||
++                                      jQuery.valHooks[ elem.nodeName.toLowerCase() ];
++
++                              if ( hooks &&
++                                      "get" in hooks &&
++                                      ( ret = hooks.get( elem, "value" ) ) !== undefined
++                              ) {
++                                      return ret;
++                              }
++
++                              ret = elem.value;
++
++                              // Handle most common string cases
++                              if ( typeof ret === "string" ) {
++                                      return ret.replace( rreturn, "" );
++                              }
++
++                              // Handle cases where value is null/undef or number
++                              return ret == null ? "" : ret;
++                      }
++
++                      return;
++              }
++
++              valueIsFunction = isFunction( value );
++
++              return this.each( function( i ) {
++                      var val;
++
++                      if ( this.nodeType !== 1 ) {
++                              return;
++                      }
++
++                      if ( valueIsFunction ) {
++                              val = value.call( this, i, jQuery( this ).val() );
++                      } else {
++                              val = value;
++                      }
++
++                      // Treat null/undefined as ""; convert numbers to string
++                      if ( val == null ) {
++                              val = "";
++
++                      } else if ( typeof val === "number" ) {
++                              val += "";
++
++                      } else if ( Array.isArray( val ) ) {
++                              val = jQuery.map( val, function( value ) {
++                                      return value == null ? "" : value + "";
++                              } );
++                      }
++
++                      hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
++
++                      // If set returns undefined, fall back to normal setting
++                      if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) {
++                              this.value = val;
++                      }
++              } );
++      }
++} );
++
++jQuery.extend( {
++      valHooks: {
++              option: {
++                      get: function( elem ) {
++
++                              var val = jQuery.find.attr( elem, "value" );
++                              return val != null ?
++                                      val :
++
++                                      // Support: IE <=10 - 11 only
++                                      // option.text throws exceptions (#14686, #14858)
++                                      // Strip and collapse whitespace
++                                      // https://html.spec.whatwg.org/#strip-and-collapse-whitespace
++                                      stripAndCollapse( jQuery.text( elem ) );
++                      }
++              },
++              select: {
++                      get: function( elem ) {
++                              var value, option, i,
++                                      options = elem.options,
++                                      index = elem.selectedIndex,
++                                      one = elem.type === "select-one",
++                                      values = one ? null : [],
++                                      max = one ? index + 1 : options.length;
++
++                              if ( index < 0 ) {
++                                      i = max;
++
++                              } else {
++                                      i = one ? index : 0;
++                              }
++
++                              // Loop through all the selected options
++                              for ( ; i < max; i++ ) {
++                                      option = options[ i ];
++
++                                      // Support: IE <=9 only
++                                      // IE8-9 doesn't update selected after form reset (#2551)
++                                      if ( ( option.selected || i === index ) &&
++
++                                                      // Don't return options that are disabled or in a disabled optgroup
++                                                      !option.disabled &&
++                                                      ( !option.parentNode.disabled ||
++                                                              !nodeName( option.parentNode, "optgroup" ) ) ) {
++
++                                              // Get the specific value for the option
++                                              value = jQuery( option ).val();
++
++                                              // We don't need an array for one selects
++                                              if ( one ) {
++                                                      return value;
++                                              }
++
++                                              // Multi-Selects return an array
++                                              values.push( value );
++                                      }
++                              }
++
++                              return values;
++                      },
++
++                      set: function( elem, value ) {
++                              var optionSet, option,
++                                      options = elem.options,
++                                      values = jQuery.makeArray( value ),
++                                      i = options.length;
++
++                              while ( i-- ) {
++                                      option = options[ i ];
++
++                                      /* eslint-disable no-cond-assign */
++
++                                      if ( option.selected =
++                                              jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1
++                                      ) {
++                                              optionSet = true;
++                                      }
++
++                                      /* eslint-enable no-cond-assign */
++                              }
++
++                              // Force browsers to behave consistently when non-matching value is set
++                              if ( !optionSet ) {
++                                      elem.selectedIndex = -1;
++                              }
++                              return values;
++                      }
++              }
++      }
++} );
++
++// Radios and checkboxes getter/setter
++jQuery.each( [ "radio", "checkbox" ], function() {
++      jQuery.valHooks[ this ] = {
++              set: function( elem, value ) {
++                      if ( Array.isArray( value ) ) {
++                              return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 );
++                      }
++              }
++      };
++      if ( !support.checkOn ) {
++              jQuery.valHooks[ this ].get = function( elem ) {
++                      return elem.getAttribute( "value" ) === null ? "on" : elem.value;
++              };
++      }
++} );
++
++
++
++
++// Return jQuery for attributes-only inclusion
++
++
++support.focusin = "onfocusin" in window;
++
++
++var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
++      stopPropagationCallback = function( e ) {
++              e.stopPropagation();
++      };
++
++jQuery.extend( jQuery.event, {
++
++      trigger: function( event, data, elem, onlyHandlers ) {
++
++              var i, cur, tmp, bubbleType, ontype, handle, special, lastElement,
++                      eventPath = [ elem || document ],
++                      type = hasOwn.call( event, "type" ) ? event.type : event,
++                      namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : [];
++
++              cur = lastElement = tmp = elem = elem || document;
++
++              // Don't do events on text and comment nodes
++              if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
++                      return;
++              }
++
++              // focus/blur morphs to focusin/out; ensure we're not firing them right now
++              if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
++                      return;
++              }
++
++              if ( type.indexOf( "." ) > -1 ) {
++
++                      // 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;
++
++              // 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 );
++
++              // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
++              event.isTrigger = onlyHandlers ? 2 : 3;
++              event.namespace = namespaces.join( "." );
++              event.rnamespace = event.namespace ?
++                      new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) :
++                      null;
++
++              // Clean up the event in case it is being reused
++              event.result = undefined;
++              if ( !event.target ) {
++                      event.target = elem;
++              }
++
++              // Clone any incoming data and prepend the event, creating the handler arg list
++              data = data == null ?
++                      [ event ] :
++                      jQuery.makeArray( data, [ event ] );
++
++              // 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 && !isWindow( elem ) ) {
++
++                      bubbleType = special.delegateType || type;
++                      if ( !rfocusMorph.test( bubbleType + type ) ) {
++                              cur = cur.parentNode;
++                      }
++                      for ( ; cur; cur = cur.parentNode ) {
++                              eventPath.push( cur );
++                              tmp = cur;
++                      }
++
++                      // 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 );
++                      }
++              }
++
++              // Fire handlers on the event path
++              i = 0;
++              while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) {
++                      lastElement = cur;
++                      event.type = i > 1 ?
++                              bubbleType :
++                              special.bindType || type;
++
++                      // jQuery handler
++                      handle = ( dataPriv.get( cur, "events" ) || {} )[ event.type ] &&
++                              dataPriv.get( cur, "handle" );
++                      if ( handle ) {
++                              handle.apply( cur, data );
++                      }
++
++                      // Native handler
++                      handle = ontype && cur[ ontype ];
++                      if ( handle && handle.apply && acceptData( cur ) ) {
++                              event.result = handle.apply( cur, data );
++                              if ( event.result === false ) {
++                                      event.preventDefault();
++                              }
++                      }
++              }
++              event.type = type;
++
++              // If nobody prevented the default action, do it now
++              if ( !onlyHandlers && !event.isDefaultPrevented() ) {
++
++                      if ( ( !special._default ||
++                              special._default.apply( eventPath.pop(), data ) === false ) &&
++                              acceptData( elem ) ) {
++
++                              // Call a native DOM method on the target with the same name as the event.
++                              // Don't do default actions on window, that's where global variables be (#6170)
++                              if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) {
++
++                                      // 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;
++
++                                      if ( event.isPropagationStopped() ) {
++                                              lastElement.addEventListener( type, stopPropagationCallback );
++                                      }
++
++                                      elem[ type ]();
++
++                                      if ( event.isPropagationStopped() ) {
++                                              lastElement.removeEventListener( type, stopPropagationCallback );
++                                      }
++
++                                      jQuery.event.triggered = undefined;
++
++                                      if ( tmp ) {
++                                              elem[ ontype ] = tmp;
++                                      }
++                              }
++                      }
++              }
++
++              return event.result;
++      },
++
++      // Piggyback on a donor event to simulate a different one
++      // Used only for `focus(in | out)` events
++      simulate: function( type, elem, event ) {
++              var e = jQuery.extend(
++                      new jQuery.Event(),
++                      event,
++                      {
++                              type: type,
++                              isSimulated: true
++                      }
++              );
++
++              jQuery.event.trigger( e, null, elem );
++      }
++
++} );
++
++jQuery.fn.extend( {
++
++      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 );
++              }
++      }
++} );
++
++
++// Support: Firefox <=44
++// Firefox doesn't have focus(in | out) events
++// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787
++//
++// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1
++// focus(in | out) events fire after focus & blur events,
++// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order
++// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857
++if ( !support.focusin ) {
++      jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) {
++
++              // Attach a single capturing handler on the document while someone wants focusin/focusout
++              var handler = function( event ) {
++                      jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) );
++              };
++
++              jQuery.event.special[ fix ] = {
++                      setup: function() {
++                              var doc = this.ownerDocument || this,
++                                      attaches = dataPriv.access( doc, fix );
++
++                              if ( !attaches ) {
++                                      doc.addEventListener( orig, handler, true );
++                              }
++                              dataPriv.access( doc, fix, ( attaches || 0 ) + 1 );
++                      },
++                      teardown: function() {
++                              var doc = this.ownerDocument || this,
++                                      attaches = dataPriv.access( doc, fix ) - 1;
++
++                              if ( !attaches ) {
++                                      doc.removeEventListener( orig, handler, true );
++                                      dataPriv.remove( doc, fix );
++
++                              } else {
++                                      dataPriv.access( doc, fix, attaches );
++                              }
++                      }
++              };
++      } );
++}
++var location = window.location;
++
++var nonce = Date.now();
++
++var rquery = ( /\?/ );
++
++
++
++// Cross-browser xml parsing
++jQuery.parseXML = function( data ) {
++      var xml;
++      if ( !data || typeof data !== "string" ) {
++              return null;
++      }
++
++      // Support: IE 9 - 11 only
++      // IE throws on parseFromString with invalid input.
++      try {
++              xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" );
++      } catch ( e ) {
++              xml = undefined;
++      }
++
++      if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) {
++              jQuery.error( "Invalid XML: " + data );
++      }
++      return xml;
++};
++
++
++var
++      rbracket = /\[\]$/,
++      rCRLF = /\r?\n/g,
++      rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
++      rsubmittable = /^(?:input|select|textarea|keygen)/i;
++
++function buildParams( prefix, obj, traditional, add ) {
++      var name;
++
++      if ( Array.isArray( obj ) ) {
++
++              // Serialize array item.
++              jQuery.each( obj, function( i, v ) {
++                      if ( traditional || rbracket.test( prefix ) ) {
++
++                              // Treat each array item as a scalar.
++                              add( prefix, v );
++
++                      } else {
++
++                              // Item is non-scalar (array or object), encode its numeric index.
++                              buildParams(
++                                      prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]",
++                                      v,
++                                      traditional,
++                                      add
++                              );
++                      }
++              } );
++
++      } else if ( !traditional && toType( obj ) === "object" ) {
++
++              // Serialize object item.
++              for ( name in obj ) {
++                      buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
++              }
++
++      } else {
++
++              // Serialize scalar item.
++              add( prefix, obj );
++      }
++}
++
++// Serialize an array of form elements or a set of
++// key/values into a query string
++jQuery.param = function( a, traditional ) {
++      var prefix,
++              s = [],
++              add = function( key, valueOrFunction ) {
++
++                      // If value is a function, invoke it and use its return value
++                      var value = isFunction( valueOrFunction ) ?
++                              valueOrFunction() :
++                              valueOrFunction;
++
++                      s[ s.length ] = encodeURIComponent( key ) + "=" +
++                              encodeURIComponent( value == null ? "" : value );
++              };
++
++      // If an array was passed in, assume that it is an array of form elements.
++      if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
++
++              // Serialize the form elements
++              jQuery.each( a, function() {
++                      add( this.name, this.value );
++              } );
++
++      } else {
++
++              // If traditional, encode the "old" way (the way 1.3.2 or older
++              // did it), otherwise encode params recursively.
++              for ( prefix in a ) {
++                      buildParams( prefix, a[ prefix ], traditional, add );
++              }
++      }
++
++      // Return the resulting serialization
++      return s.join( "&" );
++};
++
++jQuery.fn.extend( {
++      serialize: function() {
++              return jQuery.param( this.serializeArray() );
++      },
++      serializeArray: function() {
++              return this.map( function() {
++
++                      // Can add propHook for "elements" to filter or add form elements
++                      var elements = jQuery.prop( this, "elements" );
++                      return elements ? jQuery.makeArray( elements ) : this;
++              } )
++              .filter( function() {
++                      var type = this.type;
++
++                      // Use .is( ":disabled" ) so that fieldset[disabled] works
++                      return this.name && !jQuery( this ).is( ":disabled" ) &&
++                              rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
++                              ( this.checked || !rcheckableType.test( type ) );
++              } )
++              .map( function( i, elem ) {
++                      var val = jQuery( this ).val();
++
++                      if ( val == null ) {
++                              return null;
++                      }
++
++                      if ( Array.isArray( val ) ) {
++                              return jQuery.map( val, function( val ) {
++                                      return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
++                              } );
++                      }
++
++                      return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
++              } ).get();
++      }
++} );
++
++
++var
++      r20 = /%20/g,
++      rhash = /#.*$/,
++      rantiCache = /([?&])_=[^&]*/,
++      rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg,
++
++      // #7653, #8125, #8152: local protocol detection
++      rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
++      rnoContent = /^(?:GET|HEAD)$/,
++      rprotocol = /^\/\//,
++
++      /* Prefilters
++       * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
++       * 2) These are called:
++       *    - BEFORE asking for a transport
++       *    - AFTER param serialization (s.data is a string if s.processData is true)
++       * 3) key is the dataType
++       * 4) the catchall symbol "*" can be used
++       * 5) execution will start with transport dataType and THEN continue down to "*" if needed
++       */
++      prefilters = {},
++
++      /* Transports bindings
++       * 1) key is the dataType
++       * 2) the catchall symbol "*" can be used
++       * 3) selection will start with transport dataType and THEN go to "*" if needed
++       */
++      transports = {},
++
++      // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
++      allTypes = "*/".concat( "*" ),
++
++      // Anchor tag for parsing the document origin
++      originAnchor = document.createElement( "a" );
++      originAnchor.href = location.href;
++
++// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
++function addToPrefiltersOrTransports( structure ) {
++
++      // dataTypeExpression is optional and defaults to "*"
++      return function( dataTypeExpression, func ) {
++
++              if ( typeof dataTypeExpression !== "string" ) {
++                      func = dataTypeExpression;
++                      dataTypeExpression = "*";
++              }
++
++              var dataType,
++                      i = 0,
++                      dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || [];
++
++              if ( isFunction( func ) ) {
++
++                      // For each dataType in the dataTypeExpression
++                      while ( ( dataType = dataTypes[ i++ ] ) ) {
++
++                              // Prepend if requested
++                              if ( dataType[ 0 ] === "+" ) {
++                                      dataType = dataType.slice( 1 ) || "*";
++                                      ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func );
++
++                              // Otherwise append
++                              } else {
++                                      ( structure[ dataType ] = structure[ dataType ] || [] ).push( func );
++                              }
++                      }
++              }
++      };
++}
++
++// Base inspection function for prefilters and transports
++function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {
++
++      var inspected = {},
++              seekingTransport = ( structure === transports );
++
++      function inspect( dataType ) {
++              var selected;
++              inspected[ dataType ] = true;
++              jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
++                      var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
++                      if ( typeof dataTypeOrTransport === "string" &&
++                              !seekingTransport && !inspected[ dataTypeOrTransport ] ) {
++
++                              options.dataTypes.unshift( dataTypeOrTransport );
++                              inspect( dataTypeOrTransport );
++                              return false;
++                      } else if ( seekingTransport ) {
++                              return !( selected = dataTypeOrTransport );
++                      }
++              } );
++              return selected;
++      }
++
++      return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
++}
++
++// A special extend for ajax options
++// that takes "flat" options (not to be deep extended)
++// Fixes #9887
++function ajaxExtend( target, src ) {
++      var key, deep,
++              flatOptions = jQuery.ajaxSettings.flatOptions || {};
++
++      for ( key in src ) {
++              if ( src[ key ] !== undefined ) {
++                      ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ];
++              }
++      }
++      if ( deep ) {
++              jQuery.extend( true, target, deep );
++      }
++
++      return target;
++}
++
++/* Handles responses to an ajax request:
++ * - finds the right dataType (mediates between content-type and expected dataType)
++ * - returns the corresponding response
++ */
++function ajaxHandleResponses( s, jqXHR, responses ) {
++
++      var ct, type, finalDataType, firstDataType,
++              contents = s.contents,
++              dataTypes = s.dataTypes;
++
++      // Remove auto dataType and get content-type in the process
++      while ( dataTypes[ 0 ] === "*" ) {
++              dataTypes.shift();
++              if ( ct === undefined ) {
++                      ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" );
++              }
++      }
++
++      // Check if we're dealing with a known content-type
++      if ( ct ) {
++              for ( type in contents ) {
++                      if ( contents[ type ] && contents[ type ].test( ct ) ) {
++                              dataTypes.unshift( type );
++                              break;
++                      }
++              }
++      }
++
++      // Check to see if we have a response for the expected dataType
++      if ( dataTypes[ 0 ] in responses ) {
++              finalDataType = dataTypes[ 0 ];
++      } else {
++
++              // Try convertible dataTypes
++              for ( type in responses ) {
++                      if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) {
++                              finalDataType = type;
++                              break;
++                      }
++                      if ( !firstDataType ) {
++                              firstDataType = type;
++                      }
++              }
++
++              // Or just use first one
++              finalDataType = finalDataType || firstDataType;
++      }
++
++      // If we found a dataType
++      // We add the dataType to the list if needed
++      // and return the corresponding response
++      if ( finalDataType ) {
++              if ( finalDataType !== dataTypes[ 0 ] ) {
++                      dataTypes.unshift( finalDataType );
++              }
++              return responses[ finalDataType ];
++      }
++}
++
++/* Chain conversions given the request and the original response
++ * Also sets the responseXXX fields on the jqXHR instance
++ */
++function ajaxConvert( s, response, jqXHR, isSuccess ) {
++      var conv2, current, conv, tmp, prev,
++              converters = {},
++
++              // Work with a copy of dataTypes in case we need to modify it for conversion
++              dataTypes = s.dataTypes.slice();
++
++      // Create converters map with lowercased keys
++      if ( dataTypes[ 1 ] ) {
++              for ( conv in s.converters ) {
++                      converters[ conv.toLowerCase() ] = s.converters[ conv ];
++              }
++      }
++
++      current = dataTypes.shift();
++
++      // Convert to each sequential dataType
++      while ( current ) {
++
++              if ( s.responseFields[ current ] ) {
++                      jqXHR[ s.responseFields[ current ] ] = response;
++              }
++
++              // Apply the dataFilter if provided
++              if ( !prev && isSuccess && s.dataFilter ) {
++                      response = s.dataFilter( response, s.dataType );
++              }
++
++              prev = current;
++              current = dataTypes.shift();
++
++              if ( current ) {
++
++                      // There's only work to do if current dataType is non-auto
++                      if ( current === "*" ) {
++
++                              current = prev;
++
++                      // Convert response if prev dataType is non-auto and differs from current
++                      } else if ( prev !== "*" && prev !== current ) {
++
++                              // Seek a direct converter
++                              conv = converters[ prev + " " + current ] || converters[ "* " + current ];
++
++                              // If none found, seek a pair
++                              if ( !conv ) {
++                                      for ( conv2 in converters ) {
++
++                                              // If conv2 outputs current
++                                              tmp = conv2.split( " " );
++                                              if ( tmp[ 1 ] === current ) {
++
++                                                      // If prev can be converted to accepted input
++                                                      conv = converters[ prev + " " + tmp[ 0 ] ] ||
++                                                              converters[ "* " + tmp[ 0 ] ];
++                                                      if ( conv ) {
++
++                                                              // Condense equivalence converters
++                                                              if ( conv === true ) {
++                                                                      conv = converters[ conv2 ];
++
++                                                              // Otherwise, insert the intermediate dataType
++                                                              } else if ( converters[ conv2 ] !== true ) {
++                                                                      current = tmp[ 0 ];
++                                                                      dataTypes.unshift( tmp[ 1 ] );
++                                                              }
++                                                              break;
++                                                      }
++                                              }
++                                      }
++                              }
++
++                              // Apply converter (if not an equivalence)
++                              if ( conv !== true ) {
++
++                                      // Unless errors are allowed to bubble, catch and return them
++                                      if ( conv && s.throws ) {
++                                              response = conv( response );
++                                      } else {
++                                              try {
++                                                      response = conv( response );
++                                              } catch ( e ) {
++                                                      return {
++                                                              state: "parsererror",
++                                                              error: conv ? e : "No conversion from " + prev + " to " + current
++                                                      };
++                                              }
++                                      }
++                              }
++                      }
++              }
++      }
++
++      return { state: "success", data: response };
++}
++
++jQuery.extend( {
++
++      // Counter for holding the number of active queries
++      active: 0,
++
++      // Last-Modified header cache for next request
++      lastModified: {},
++      etag: {},
++
++      ajaxSettings: {
++              url: location.href,
++              type: "GET",
++              isLocal: rlocalProtocol.test( location.protocol ),
++              global: true,
++              processData: true,
++              async: true,
++              contentType: "application/x-www-form-urlencoded; charset=UTF-8",
++
++              /*
++              timeout: 0,
++              data: null,
++              dataType: null,
++              username: null,
++              password: null,
++              cache: null,
++              throws: false,
++              traditional: false,
++              headers: {},
++              */
++
++              accepts: {
++                      "*": allTypes,
++                      text: "text/plain",
++                      html: "text/html",
++                      xml: "application/xml, text/xml",
++                      json: "application/json, text/javascript"
++              },
++
++              contents: {
++                      xml: /\bxml\b/,
++                      html: /\bhtml/,
++                      json: /\bjson\b/
++              },
++
++              responseFields: {
++                      xml: "responseXML",
++                      text: "responseText",
++                      json: "responseJSON"
++              },
++
++              // Data converters
++              // Keys separate source (or catchall "*") and destination types with a single space
++              converters: {
++
++                      // Convert anything to text
++                      "* text": String,
++
++                      // Text to html (true = no transformation)
++                      "text html": true,
++
++                      // Evaluate text as a json expression
++                      "text json": JSON.parse,
++
++                      // Parse text as xml
++                      "text xml": jQuery.parseXML
++              },
++
++              // For options that shouldn't be deep extended:
++              // you can add your own custom options here if
++              // and when you create one that shouldn't be
++              // deep extended (see ajaxExtend)
++              flatOptions: {
++                      url: true,
++                      context: true
++              }
++      },
++
++      // Creates a full fledged settings object into target
++      // with both ajaxSettings and settings fields.
++      // If target is omitted, writes into ajaxSettings.
++      ajaxSetup: function( target, settings ) {
++              return settings ?
++
++                      // Building a settings object
++                      ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :
++
++                      // Extending ajaxSettings
++                      ajaxExtend( jQuery.ajaxSettings, target );
++      },
++
++      ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
++      ajaxTransport: addToPrefiltersOrTransports( transports ),
++
++      // Main method
++      ajax: function( url, options ) {
++
++              // If url is an object, simulate pre-1.5 signature
++              if ( typeof url === "object" ) {
++                      options = url;
++                      url = undefined;
++              }
++
++              // Force options to be an object
++              options = options || {};
++
++              var transport,
++
++                      // URL without anti-cache param
++                      cacheURL,
++
++                      // Response headers
++                      responseHeadersString,
++                      responseHeaders,
++
++                      // timeout handle
++                      timeoutTimer,
++
++                      // Url cleanup var
++                      urlAnchor,
++
++                      // Request state (becomes false upon send and true upon completion)
++                      completed,
++
++                      // To know if global events are to be dispatched
++                      fireGlobals,
++
++                      // Loop variable
++                      i,
++
++                      // uncached part of the url
++                      uncached,
++
++                      // Create the final options object
++                      s = jQuery.ajaxSetup( {}, options ),
++
++                      // Callbacks context
++                      callbackContext = s.context || s,
++
++                      // Context for global events is callbackContext if it is a DOM node or jQuery collection
++                      globalEventContext = s.context &&
++                              ( callbackContext.nodeType || callbackContext.jquery ) ?
++                                      jQuery( callbackContext ) :
++                                      jQuery.event,
++
++                      // Deferreds
++                      deferred = jQuery.Deferred(),
++                      completeDeferred = jQuery.Callbacks( "once memory" ),
++
++                      // Status-dependent callbacks
++                      statusCode = s.statusCode || {},
++
++                      // Headers (they are sent all at once)
++                      requestHeaders = {},
++                      requestHeadersNames = {},
++
++                      // Default abort message
++                      strAbort = "canceled",
++
++                      // Fake xhr
++                      jqXHR = {
++                              readyState: 0,
++
++                              // Builds headers hashtable if needed
++                              getResponseHeader: function( key ) {
++                                      var match;
++                                      if ( completed ) {
++                                              if ( !responseHeaders ) {
++                                                      responseHeaders = {};
++                                                      while ( ( match = rheaders.exec( responseHeadersString ) ) ) {
++                                                              responseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ];
++                                                      }
++                                              }
++                                              match = responseHeaders[ key.toLowerCase() ];
++                                      }
++                                      return match == null ? null : match;
++                              },
++
++                              // Raw string
++                              getAllResponseHeaders: function() {
++                                      return completed ? responseHeadersString : null;
++                              },
++
++                              // Caches the header
++                              setRequestHeader: function( name, value ) {
++                                      if ( completed == null ) {
++                                              name = requestHeadersNames[ name.toLowerCase() ] =
++                                                      requestHeadersNames[ name.toLowerCase() ] || name;
++                                              requestHeaders[ name ] = value;
++                                      }
++                                      return this;
++                              },
++
++                              // Overrides response content-type header
++                              overrideMimeType: function( type ) {
++                                      if ( completed == null ) {
++                                              s.mimeType = type;
++                                      }
++                                      return this;
++                              },
++
++                              // Status-dependent callbacks
++                              statusCode: function( map ) {
++                                      var code;
++                                      if ( map ) {
++                                              if ( completed ) {
++
++                                                      // Execute the appropriate callbacks
++                                                      jqXHR.always( map[ jqXHR.status ] );
++                                              } else {
++
++                                                      // Lazy-add the new callbacks in a way that preserves old ones
++                                                      for ( code in map ) {
++                                                              statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
++                                                      }
++                                              }
++                                      }
++                                      return this;
++                              },
++
++                              // Cancel the request
++                              abort: function( statusText ) {
++                                      var finalText = statusText || strAbort;
++                                      if ( transport ) {
++                                              transport.abort( finalText );
++                                      }
++                                      done( 0, finalText );
++                                      return this;
++                              }
++                      };
++
++              // Attach deferreds
++              deferred.promise( jqXHR );
++
++              // Add protocol if not provided (prefilters might expect it)
++              // Handle falsy url in the settings object (#10093: consistency with old signature)
++              // We also use the url parameter if available
++              s.url = ( ( url || s.url || location.href ) + "" )
++                      .replace( rprotocol, location.protocol + "//" );
++
++              // Alias method option to type as per ticket #12004
++              s.type = options.method || options.type || s.method || s.type;
++
++              // Extract dataTypes list
++              s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ];
++
++              // A cross-domain request is in order when the origin doesn't match the current origin.
++              if ( s.crossDomain == null ) {
++                      urlAnchor = document.createElement( "a" );
++
++                      // Support: IE <=8 - 11, Edge 12 - 15
++                      // IE throws exception on accessing the href property if url is malformed,
++                      // e.g. http://example.com:80x/
++                      try {
++                              urlAnchor.href = s.url;
++
++                              // Support: IE <=8 - 11 only
++                              // Anchor's host property isn't correctly set when s.url is relative
++                              urlAnchor.href = urlAnchor.href;
++                              s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !==
++                                      urlAnchor.protocol + "//" + urlAnchor.host;
++                      } catch ( e ) {
++
++                              // If there is an error parsing the URL, assume it is crossDomain,
++                              // it can be rejected by the transport if it is invalid
++                              s.crossDomain = true;
++                      }
++              }
++
++              // Convert data if not already a string
++              if ( s.data && s.processData && typeof s.data !== "string" ) {
++                      s.data = jQuery.param( s.data, s.traditional );
++              }
++
++              // Apply prefilters
++              inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
++
++              // If request was aborted inside a prefilter, stop there
++              if ( completed ) {
++                      return jqXHR;
++              }
++
++              // We can fire global events as of now if asked to
++              // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118)
++              fireGlobals = jQuery.event && s.global;
++
++              // Watch for a new set of requests
++              if ( fireGlobals && jQuery.active++ === 0 ) {
++                      jQuery.event.trigger( "ajaxStart" );
++              }
++
++              // Uppercase the type
++              s.type = s.type.toUpperCase();
++
++              // Determine if request has content
++              s.hasContent = !rnoContent.test( s.type );
++
++              // Save the URL in case we're toying with the If-Modified-Since
++              // and/or If-None-Match header later on
++              // Remove hash to simplify url manipulation
++              cacheURL = s.url.replace( rhash, "" );
++
++              // More options handling for requests with no content
++              if ( !s.hasContent ) {
++
++                      // Remember the hash so we can put it back
++                      uncached = s.url.slice( cacheURL.length );
++
++                      // If data is available and should be processed, append data to url
++                      if ( s.data && ( s.processData || typeof s.data === "string" ) ) {
++                              cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data;
++
++                              // #9682: remove data so that it's not used in an eventual retry
++                              delete s.data;
++                      }
++
++                      // Add or update anti-cache param if needed
++                      if ( s.cache === false ) {
++                              cacheURL = cacheURL.replace( rantiCache, "$1" );
++                              uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce++ ) + uncached;
++                      }
++
++                      // Put hash and anti-cache on the URL that will be requested (gh-1732)
++                      s.url = cacheURL + uncached;
++
++              // Change '%20' to '+' if this is encoded form body content (gh-2658)
++              } else if ( s.data && s.processData &&
++                      ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) {
++                      s.data = s.data.replace( r20, "+" );
++              }
++
++              // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
++              if ( s.ifModified ) {
++                      if ( jQuery.lastModified[ cacheURL ] ) {
++                              jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
++                      }
++                      if ( jQuery.etag[ cacheURL ] ) {
++                              jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
++                      }
++              }
++
++              // Set the correct header, if data is being sent
++              if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
++                      jqXHR.setRequestHeader( "Content-Type", s.contentType );
++              }
++
++              // Set the Accepts header for the server, depending on the dataType
++              jqXHR.setRequestHeader(
++                      "Accept",
++                      s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ?
++                              s.accepts[ s.dataTypes[ 0 ] ] +
++                                      ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
++                              s.accepts[ "*" ]
++              );
++
++              // Check for headers option
++              for ( i in s.headers ) {
++                      jqXHR.setRequestHeader( i, s.headers[ i ] );
++              }
++
++              // Allow custom headers/mimetypes and early abort
++              if ( s.beforeSend &&
++                      ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) {
++
++                      // Abort if not done already and return
++                      return jqXHR.abort();
++              }
++
++              // Aborting is no longer a cancellation
++              strAbort = "abort";
++
++              // Install callbacks on deferreds
++              completeDeferred.add( s.complete );
++              jqXHR.done( s.success );
++              jqXHR.fail( s.error );
++
++              // Get transport
++              transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
++
++              // If no transport, we auto-abort
++              if ( !transport ) {
++                      done( -1, "No Transport" );
++              } else {
++                      jqXHR.readyState = 1;
++
++                      // Send global event
++                      if ( fireGlobals ) {
++                              globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
++                      }
++
++                      // If request was aborted inside ajaxSend, stop there
++                      if ( completed ) {
++                              return jqXHR;
++                      }
++
++                      // Timeout
++                      if ( s.async && s.timeout > 0 ) {
++                              timeoutTimer = window.setTimeout( function() {
++                                      jqXHR.abort( "timeout" );
++                              }, s.timeout );
++                      }
++
++                      try {
++                              completed = false;
++                              transport.send( requestHeaders, done );
++                      } catch ( e ) {
++
++                              // Rethrow post-completion exceptions
++                              if ( completed ) {
++                                      throw e;
++                              }
++
++                              // Propagate others as results
++                              done( -1, e );
++                      }
++              }
++
++              // Callback for when everything is done
++              function done( status, nativeStatusText, responses, headers ) {
++                      var isSuccess, success, error, response, modified,
++                              statusText = nativeStatusText;
++
++                      // Ignore repeat invocations
++                      if ( completed ) {
++                              return;
++                      }
++
++                      completed = true;
++
++                      // Clear timeout if it exists
++                      if ( timeoutTimer ) {
++                              window.clearTimeout( timeoutTimer );
++                      }
++
++                      // Dereference transport for early garbage collection
++                      // (no matter how long the jqXHR object will be used)
++                      transport = undefined;
++
++                      // Cache response headers
++                      responseHeadersString = headers || "";
++
++                      // Set readyState
++                      jqXHR.readyState = status > 0 ? 4 : 0;
++
++                      // Determine if successful
++                      isSuccess = status >= 200 && status < 300 || status === 304;
++
++                      // Get response data
++                      if ( responses ) {
++                              response = ajaxHandleResponses( s, jqXHR, responses );
++                      }
++
++                      // Convert no matter what (that way responseXXX fields are always set)
++                      response = ajaxConvert( s, response, jqXHR, isSuccess );
++
++                      // If successful, handle type chaining
++                      if ( isSuccess ) {
++
++                              // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
++                              if ( s.ifModified ) {
++                                      modified = jqXHR.getResponseHeader( "Last-Modified" );
++                                      if ( modified ) {
++                                              jQuery.lastModified[ cacheURL ] = modified;
++                                      }
++                                      modified = jqXHR.getResponseHeader( "etag" );
++                                      if ( modified ) {
++                                              jQuery.etag[ cacheURL ] = modified;
++                                      }
++                              }
++
++                              // if no content
++                              if ( status === 204 || s.type === "HEAD" ) {
++                                      statusText = "nocontent";
++
++                              // if not modified
++                              } else if ( status === 304 ) {
++                                      statusText = "notmodified";
++
++                              // If we have data, let's convert it
++                              } else {
++                                      statusText = response.state;
++                                      success = response.data;
++                                      error = response.error;
++                                      isSuccess = !error;
++                              }
++                      } else {
++
++                              // Extract error from statusText and normalize for non-aborts
++                              error = statusText;
++                              if ( status || !statusText ) {
++                                      statusText = "error";
++                                      if ( status < 0 ) {
++                                              status = 0;
++                                      }
++                              }
++                      }
++
++                      // Set data for the fake xhr object
++                      jqXHR.status = status;
++                      jqXHR.statusText = ( nativeStatusText || statusText ) + "";
++
++                      // Success/Error
++                      if ( isSuccess ) {
++                              deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
++                      } else {
++                              deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
++                      }
++
++                      // Status-dependent callbacks
++                      jqXHR.statusCode( statusCode );
++                      statusCode = undefined;
++
++                      if ( fireGlobals ) {
++                              globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
++                                      [ jqXHR, s, isSuccess ? success : error ] );
++                      }
++
++                      // Complete
++                      completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
++
++                      if ( fireGlobals ) {
++                              globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
++
++                              // Handle the global AJAX counter
++                              if ( !( --jQuery.active ) ) {
++                                      jQuery.event.trigger( "ajaxStop" );
++                              }
++                      }
++              }
++
++              return jqXHR;
++      },
++
++      getJSON: function( url, data, callback ) {
++              return jQuery.get( url, data, callback, "json" );
++      },
++
++      getScript: function( url, callback ) {
++              return jQuery.get( url, undefined, callback, "script" );
++      }
++} );
++
++jQuery.each( [ "get", "post" ], function( i, method ) {
++      jQuery[ method ] = function( url, data, callback, type ) {
++
++              // Shift arguments if data argument was omitted
++              if ( isFunction( data ) ) {
++                      type = type || callback;
++                      callback = data;
++                      data = undefined;
++              }
++
++              // The url can be an options object (which then must have .url)
++              return jQuery.ajax( jQuery.extend( {
++                      url: url,
++                      type: method,
++                      dataType: type,
++                      data: data,
++                      success: callback
++              }, jQuery.isPlainObject( url ) && url ) );
++      };
++} );
++
++
++jQuery._evalUrl = function( url ) {
++      return jQuery.ajax( {
++              url: url,
++
++              // Make this explicit, since user can override this through ajaxSetup (#11264)
++              type: "GET",
++              dataType: "script",
++              cache: true,
++              async: false,
++              global: false,
++              "throws": true
++      } );
++};
++
++
++jQuery.fn.extend( {
++      wrapAll: function( html ) {
++              var wrap;
++
++              if ( this[ 0 ] ) {
++                      if ( isFunction( html ) ) {
++                              html = html.call( this[ 0 ] );
++                      }
++
++                      // The elements to wrap the target around
++                      wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true );
++
++                      if ( this[ 0 ].parentNode ) {
++                              wrap.insertBefore( this[ 0 ] );
++                      }
++
++                      wrap.map( function() {
++                              var elem = this;
++
++                              while ( elem.firstElementChild ) {
++                                      elem = elem.firstElementChild;
++                              }
++
++                              return elem;
++                      } ).append( this );
++              }
++
++              return this;
++      },
++
++      wrapInner: function( html ) {
++              if ( isFunction( html ) ) {
++                      return this.each( function( i ) {
++                              jQuery( this ).wrapInner( html.call( this, i ) );
++                      } );
++              }
++
++              return this.each( function() {
++                      var self = jQuery( this ),
++                              contents = self.contents();
++
++                      if ( contents.length ) {
++                              contents.wrapAll( html );
++
++                      } else {
++                              self.append( html );
++                      }
++              } );
++      },
++
++      wrap: function( html ) {
++              var htmlIsFunction = isFunction( html );
++
++              return this.each( function( i ) {
++                      jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html );
++              } );
++      },
++
++      unwrap: function( selector ) {
++              this.parent( selector ).not( "body" ).each( function() {
++                      jQuery( this ).replaceWith( this.childNodes );
++              } );
++              return this;
++      }
++} );
++
++
++jQuery.expr.pseudos.hidden = function( elem ) {
++      return !jQuery.expr.pseudos.visible( elem );
++};
++jQuery.expr.pseudos.visible = function( elem ) {
++      return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );
++};
++
++
++
++
++jQuery.ajaxSettings.xhr = function() {
++      try {
++              return new window.XMLHttpRequest();
++      } catch ( e ) {}
++};
++
++var xhrSuccessStatus = {
++
++              // File protocol always yields status code 0, assume 200
++              0: 200,
++
++              // Support: IE <=9 only
++              // #1450: sometimes IE returns 1223 when it should be 204
++              1223: 204
++      },
++      xhrSupported = jQuery.ajaxSettings.xhr();
++
++support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
++support.ajax = xhrSupported = !!xhrSupported;
++
++jQuery.ajaxTransport( function( options ) {
++      var callback, errorCallback;
++
++      // Cross domain only allowed if supported through XMLHttpRequest
++      if ( support.cors || xhrSupported && !options.crossDomain ) {
++              return {
++                      send: function( headers, complete ) {
++                              var i,
++                                      xhr = options.xhr();
++
++                              xhr.open(
++                                      options.type,
++                                      options.url,
++                                      options.async,
++                                      options.username,
++                                      options.password
++                              );
++
++                              // Apply custom fields if provided
++                              if ( options.xhrFields ) {
++                                      for ( i in options.xhrFields ) {
++                                              xhr[ i ] = options.xhrFields[ i ];
++                                      }
++                              }
++
++                              // Override mime type if needed
++                              if ( options.mimeType && xhr.overrideMimeType ) {
++                                      xhr.overrideMimeType( options.mimeType );
++                              }
++
++                              // X-Requested-With header
++                              // For cross-domain requests, seeing as conditions for a preflight are
++                              // akin to a jigsaw puzzle, we simply never set it to be sure.
++                              // (it can always be set on a per-request basis or even using ajaxSetup)
++                              // For same-domain requests, won't change header if already provided.
++                              if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) {
++                                      headers[ "X-Requested-With" ] = "XMLHttpRequest";
++                              }
++
++                              // Set headers
++                              for ( i in headers ) {
++                                      xhr.setRequestHeader( i, headers[ i ] );
++                              }
++
++                              // Callback
++                              callback = function( type ) {
++                                      return function() {
++                                              if ( callback ) {
++                                                      callback = errorCallback = xhr.onload =
++                                                              xhr.onerror = xhr.onabort = xhr.ontimeout =
++                                                                      xhr.onreadystatechange = null;
++
++                                                      if ( type === "abort" ) {
++                                                              xhr.abort();
++                                                      } else if ( type === "error" ) {
++
++                                                              // Support: IE <=9 only
++                                                              // On a manual native abort, IE9 throws
++                                                              // errors on any property access that is not readyState
++                                                              if ( typeof xhr.status !== "number" ) {
++                                                                      complete( 0, "error" );
++                                                              } else {
++                                                                      complete(
++
++                                                                              // File: protocol always yields status 0; see #8605, #14207
++                                                                              xhr.status,
++                                                                              xhr.statusText
++                                                                      );
++                                                              }
++                                                      } else {
++                                                              complete(
++                                                                      xhrSuccessStatus[ xhr.status ] || xhr.status,
++                                                                      xhr.statusText,
++
++                                                                      // Support: IE <=9 only
++                                                                      // IE9 has no XHR2 but throws on binary (trac-11426)
++                                                                      // For XHR2 non-text, let the caller handle it (gh-2498)
++                                                                      ( xhr.responseType || "text" ) !== "text"  ||
++                                                                      typeof xhr.responseText !== "string" ?
++                                                                              { binary: xhr.response } :
++                                                                              { text: xhr.responseText },
++                                                                      xhr.getAllResponseHeaders()
++                                                              );
++                                                      }
++                                              }
++                                      };
++                              };
++
++                              // Listen to events
++                              xhr.onload = callback();
++                              errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" );
++
++                              // Support: IE 9 only
++                              // Use onreadystatechange to replace onabort
++                              // to handle uncaught aborts
++                              if ( xhr.onabort !== undefined ) {
++                                      xhr.onabort = errorCallback;
++                              } else {
++                                      xhr.onreadystatechange = function() {
++
++                                              // Check readyState before timeout as it changes
++                                              if ( xhr.readyState === 4 ) {
++
++                                                      // Allow onerror to be called first,
++                                                      // but that will not handle a native abort
++                                                      // Also, save errorCallback to a variable
++                                                      // as xhr.onerror cannot be accessed
++                                                      window.setTimeout( function() {
++                                                              if ( callback ) {
++                                                                      errorCallback();
++                                                              }
++                                                      } );
++                                              }
++                                      };
++                              }
++
++                              // Create the abort callback
++                              callback = callback( "abort" );
++
++                              try {
++
++                                      // Do send the request (this may raise an exception)
++                                      xhr.send( options.hasContent && options.data || null );
++                              } catch ( e ) {
++
++                                      // #14683: Only rethrow if this hasn't been notified as an error yet
++                                      if ( callback ) {
++                                              throw e;
++                                      }
++                              }
++                      },
++
++                      abort: function() {
++                              if ( callback ) {
++                                      callback();
++                              }
++                      }
++              };
++      }
++} );
++
++
++
++
++// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432)
++jQuery.ajaxPrefilter( function( s ) {
++      if ( s.crossDomain ) {
++              s.contents.script = false;
++      }
++} );
++
++// Install script dataType
++jQuery.ajaxSetup( {
++      accepts: {
++              script: "text/javascript, application/javascript, " +
++                      "application/ecmascript, application/x-ecmascript"
++      },
++      contents: {
++              script: /\b(?:java|ecma)script\b/
++      },
++      converters: {
++              "text script": function( text ) {
++                      jQuery.globalEval( text );
++                      return text;
++              }
++      }
++} );
++
++// Handle cache's special case and crossDomain
++jQuery.ajaxPrefilter( "script", function( s ) {
++      if ( s.cache === undefined ) {
++              s.cache = false;
++      }
++      if ( s.crossDomain ) {
++              s.type = "GET";
++      }
++} );
++
++// Bind script tag hack transport
++jQuery.ajaxTransport( "script", function( s ) {
++
++      // This transport only deals with cross domain requests
++      if ( s.crossDomain ) {
++              var script, callback;
++              return {
++                      send: function( _, complete ) {
++                              script = jQuery( "<script>" ).prop( {
++                                      charset: s.scriptCharset,
++                                      src: s.url
++                              } ).on(
++                                      "load error",
++                                      callback = function( evt ) {
++                                              script.remove();
++                                              callback = null;
++                                              if ( evt ) {
++                                                      complete( evt.type === "error" ? 404 : 200, evt.type );
++                                              }
++                                      }
++                              );
++
++                              // Use native DOM manipulation to avoid our domManip AJAX trickery
++                              document.head.appendChild( script[ 0 ] );
++                      },
++                      abort: function() {
++                              if ( callback ) {
++                                      callback();
++                              }
++                      }
++              };
++      }
++} );
++
++
++
++
++var oldCallbacks = [],
++      rjsonp = /(=)\?(?=&|$)|\?\?/;
++
++// Default jsonp settings
++jQuery.ajaxSetup( {
++      jsonp: "callback",
++      jsonpCallback: function() {
++              var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) );
++              this[ callback ] = true;
++              return callback;
++      }
++} );
++
++// Detect, normalize options and install callbacks for jsonp requests
++jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
++
++      var callbackName, overwritten, responseContainer,
++              jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?
++                      "url" :
++                      typeof s.data === "string" &&
++                              ( s.contentType || "" )
++                                      .indexOf( "application/x-www-form-urlencoded" ) === 0 &&
++                              rjsonp.test( s.data ) && "data"
++              );
++
++      // Handle iff the expected data type is "jsonp" or we have a parameter to set
++      if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) {
++
++              // Get callback name, remembering preexisting value associated with it
++              callbackName = s.jsonpCallback = isFunction( s.jsonpCallback ) ?
++                      s.jsonpCallback() :
++                      s.jsonpCallback;
++
++              // Insert callback into url or form data
++              if ( jsonProp ) {
++                      s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName );
++              } else if ( s.jsonp !== false ) {
++                      s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
++              }
++
++              // Use data converter to retrieve json after script execution
++              s.converters[ "script json" ] = function() {
++                      if ( !responseContainer ) {
++                              jQuery.error( callbackName + " was not called" );
++                      }
++                      return responseContainer[ 0 ];
++              };
++
++              // Force json dataType
++              s.dataTypes[ 0 ] = "json";
++
++              // Install callback
++              overwritten = window[ callbackName ];
++              window[ callbackName ] = function() {
++                      responseContainer = arguments;
++              };
++
++              // Clean-up function (fires after converters)
++              jqXHR.always( function() {
++
++                      // If previous value didn't exist - remove it
++                      if ( overwritten === undefined ) {
++                              jQuery( window ).removeProp( callbackName );
++
++                      // Otherwise restore preexisting value
++                      } else {
++                              window[ callbackName ] = overwritten;
++                      }
++
++                      // Save back as free
++                      if ( s[ callbackName ] ) {
++
++                              // Make sure that re-using the options doesn't screw things around
++                              s.jsonpCallback = originalSettings.jsonpCallback;
++
++                              // Save the callback name for future use
++                              oldCallbacks.push( callbackName );
++                      }
++
++                      // Call if it was a function and we have a response
++                      if ( responseContainer && isFunction( overwritten ) ) {
++                              overwritten( responseContainer[ 0 ] );
++                      }
++
++                      responseContainer = overwritten = undefined;
++              } );
++
++              // Delegate to script
++              return "script";
++      }
++} );
++
++
++
++
++// Support: Safari 8 only
++// In Safari 8 documents created via document.implementation.createHTMLDocument
++// collapse sibling forms: the second one becomes a child of the first one.
++// Because of that, this security measure has to be disabled in Safari 8.
++// https://bugs.webkit.org/show_bug.cgi?id=137337
++support.createHTMLDocument = ( function() {
++      var body = document.implementation.createHTMLDocument( "" ).body;
++      body.innerHTML = "<form></form><form></form>";
++      return body.childNodes.length === 2;
++} )();
++
++
++// Argument "data" should be string of html
++// context (optional): If specified, the fragment will be created in this context,
++// defaults to document
++// keepScripts (optional): If true, will include scripts passed in the html string
++jQuery.parseHTML = function( data, context, keepScripts ) {
++      if ( typeof data !== "string" ) {
++              return [];
++      }
++      if ( typeof context === "boolean" ) {
++              keepScripts = context;
++              context = false;
++      }
++
++      var base, parsed, scripts;
++
++      if ( !context ) {
++
++              // Stop scripts or inline event handlers from being executed immediately
++              // by using document.implementation
++              if ( support.createHTMLDocument ) {
++                      context = document.implementation.createHTMLDocument( "" );
++
++                      // Set the base href for the created document
++                      // so any parsed elements with URLs
++                      // are based on the document's URL (gh-2965)
++                      base = context.createElement( "base" );
++                      base.href = document.location.href;
++                      context.head.appendChild( base );
++              } else {
++                      context = document;
++              }
++      }
++
++      parsed = rsingleTag.exec( data );
++      scripts = !keepScripts && [];
++
++      // Single tag
++      if ( parsed ) {
++              return [ context.createElement( parsed[ 1 ] ) ];
++      }
++
++      parsed = buildFragment( [ data ], context, scripts );
++
++      if ( scripts && scripts.length ) {
++              jQuery( scripts ).remove();
++      }
++
++      return jQuery.merge( [], parsed.childNodes );
++};
++
++
++/**
++ * Load a url into a page
++ */
++jQuery.fn.load = function( url, params, callback ) {
++      var selector, type, response,
++              self = this,
++              off = url.indexOf( " " );
++
++      if ( off > -1 ) {
++              selector = stripAndCollapse( url.slice( off ) );
++              url = url.slice( 0, off );
++      }
++
++      // If it's a function
++      if ( isFunction( params ) ) {
++
++              // We assume that it's the callback
++              callback = params;
++              params = undefined;
++
++      // Otherwise, build a param string
++      } else if ( params && typeof params === "object" ) {
++              type = "POST";
++      }
++
++      // If we have elements to modify, make the request
++      if ( self.length > 0 ) {
++              jQuery.ajax( {
++                      url: url,
++
++                      // If "type" variable is undefined, then "GET" method will be used.
++                      // Make value of this field explicit since
++                      // user can override it through ajaxSetup method
++                      type: type || "GET",
++                      dataType: "html",
++                      data: params
++              } ).done( function( responseText ) {
++
++                      // Save response for use in complete callback
++                      response = arguments;
++
++                      self.html( selector ?
++
++                              // If a selector was specified, locate the right elements in a dummy div
++                              // Exclude scripts to avoid IE 'Permission Denied' errors
++                              jQuery( "<div>" ).append( jQuery.parseHTML( responseText ) ).find( selector ) :
++
++                              // Otherwise use the full result
++                              responseText );
++
++              // If the request succeeds, this function gets "data", "status", "jqXHR"
++              // but they are ignored because response was set above.
++              // If it fails, this function gets "jqXHR", "status", "error"
++              } ).always( callback && function( jqXHR, status ) {
++                      self.each( function() {
++                              callback.apply( this, response || [ jqXHR.responseText, status, jqXHR ] );
++                      } );
++              } );
++      }
++
++      return this;
++};
++
++
++
++
++// Attach a bunch of functions for handling common AJAX events
++jQuery.each( [
++      "ajaxStart",
++      "ajaxStop",
++      "ajaxComplete",
++      "ajaxError",
++      "ajaxSuccess",
++      "ajaxSend"
++], function( i, type ) {
++      jQuery.fn[ type ] = function( fn ) {
++              return this.on( type, fn );
++      };
++} );
++
++
++
++
++jQuery.expr.pseudos.animated = function( elem ) {
++      return jQuery.grep( jQuery.timers, function( fn ) {
++              return elem === fn.elem;
++      } ).length;
++};
++
++
++
++
++jQuery.offset = {
++      setOffset: function( elem, options, i ) {
++              var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
++                      position = jQuery.css( elem, "position" ),
++                      curElem = jQuery( elem ),
++                      props = {};
++
++              // Set position first, in-case top/left are set even on static elem
++              if ( position === "static" ) {
++                      elem.style.position = "relative";
++              }
++
++              curOffset = curElem.offset();
++              curCSSTop = jQuery.css( elem, "top" );
++              curCSSLeft = jQuery.css( elem, "left" );
++              calculatePosition = ( position === "absolute" || position === "fixed" ) &&
++                      ( curCSSTop + curCSSLeft ).indexOf( "auto" ) > -1;
++
++              // Need to be able to calculate position if either
++              // top or left is auto and position is either absolute or fixed
++              if ( calculatePosition ) {
++                      curPosition = curElem.position();
++                      curTop = curPosition.top;
++                      curLeft = curPosition.left;
++
++              } else {
++                      curTop = parseFloat( curCSSTop ) || 0;
++                      curLeft = parseFloat( curCSSLeft ) || 0;
++              }
++
++              if ( isFunction( options ) ) {
++
++                      // Use jQuery.extend here to allow modification of coordinates argument (gh-1848)
++                      options = options.call( elem, i, jQuery.extend( {}, curOffset ) );
++              }
++
++              if ( options.top != null ) {
++                      props.top = ( options.top - curOffset.top ) + curTop;
++              }
++              if ( options.left != null ) {
++                      props.left = ( options.left - curOffset.left ) + curLeft;
++              }
++
++              if ( "using" in options ) {
++                      options.using.call( elem, props );
++
++              } else {
++                      curElem.css( props );
++              }
++      }
++};
++
++jQuery.fn.extend( {
++
++      // offset() relates an element's border box to the document origin
++      offset: function( options ) {
++
++              // Preserve chaining for setter
++              if ( arguments.length ) {
++                      return options === undefined ?
++                              this :
++                              this.each( function( i ) {
++                                      jQuery.offset.setOffset( this, options, i );
++                              } );
++              }
++
++              var rect, win,
++                      elem = this[ 0 ];
++
++              if ( !elem ) {
++                      return;
++              }
++
++              // Return zeros for disconnected and hidden (display: none) elements (gh-2310)
++              // Support: IE <=11 only
++              // Running getBoundingClientRect on a
++              // disconnected node in IE throws an error
++              if ( !elem.getClientRects().length ) {
++                      return { top: 0, left: 0 };
++              }
++
++              // Get document-relative position by adding viewport scroll to viewport-relative gBCR
++              rect = elem.getBoundingClientRect();
++              win = elem.ownerDocument.defaultView;
++              return {
++                      top: rect.top + win.pageYOffset,
++                      left: rect.left + win.pageXOffset
++              };
++      },
++
++      // position() relates an element's margin box to its offset parent's padding box
++      // This corresponds to the behavior of CSS absolute positioning
++      position: function() {
++              if ( !this[ 0 ] ) {
++                      return;
++              }
++
++              var offsetParent, offset, doc,
++                      elem = this[ 0 ],
++                      parentOffset = { top: 0, left: 0 };
++
++              // position:fixed elements are offset from the viewport, which itself always has zero offset
++              if ( jQuery.css( elem, "position" ) === "fixed" ) {
++
++                      // Assume position:fixed implies availability of getBoundingClientRect
++                      offset = elem.getBoundingClientRect();
++
++              } else {
++                      offset = this.offset();
++
++                      // Account for the *real* offset parent, which can be the document or its root element
++                      // when a statically positioned element is identified
++                      doc = elem.ownerDocument;
++                      offsetParent = elem.offsetParent || doc.documentElement;
++                      while ( offsetParent &&
++                              ( offsetParent === doc.body || offsetParent === doc.documentElement ) &&
++                              jQuery.css( offsetParent, "position" ) === "static" ) {
++
++                              offsetParent = offsetParent.parentNode;
++                      }
++                      if ( offsetParent && offsetParent !== elem && offsetParent.nodeType === 1 ) {
++
++                              // Incorporate borders into its offset, since they are outside its content origin
++                              parentOffset = jQuery( offsetParent ).offset();
++                              parentOffset.top += jQuery.css( offsetParent, "borderTopWidth", true );
++                              parentOffset.left += jQuery.css( offsetParent, "borderLeftWidth", true );
++                      }
++              }
++
++              // Subtract parent offsets and element margins
++              return {
++                      top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
++                      left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true )
++              };
++      },
++
++      // This method will return documentElement in the following cases:
++      // 1) For the element inside the iframe without offsetParent, this method will return
++      //    documentElement of the parent window
++      // 2) For the hidden or detached element
++      // 3) For body or html element, i.e. in case of the html node - it will return itself
++      //
++      // but those exceptions were never presented as a real life use-cases
++      // and might be considered as more preferable results.
++      //
++      // This logic, however, is not guaranteed and can change at any point in the future
++      offsetParent: function() {
++              return this.map( function() {
++                      var offsetParent = this.offsetParent;
++
++                      while ( offsetParent && jQuery.css( offsetParent, "position" ) === "static" ) {
++                              offsetParent = offsetParent.offsetParent;
++                      }
++
++                      return offsetParent || documentElement;
++              } );
++      }
++} );
++
++// Create scrollLeft and scrollTop methods
++jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) {
++      var top = "pageYOffset" === prop;
++
++      jQuery.fn[ method ] = function( val ) {
++              return access( this, function( elem, method, val ) {
++
++                      // Coalesce documents and windows
++                      var win;
++                      if ( isWindow( elem ) ) {
++                              win = elem;
++                      } else if ( elem.nodeType === 9 ) {
++                              win = elem.defaultView;
++                      }
++
++                      if ( val === undefined ) {
++                              return win ? win[ prop ] : elem[ method ];
++                      }
++
++                      if ( win ) {
++                              win.scrollTo(
++                                      !top ? val : win.pageXOffset,
++                                      top ? val : win.pageYOffset
++                              );
++
++                      } else {
++                              elem[ method ] = val;
++                      }
++              }, method, val, arguments.length );
++      };
++} );
++
++// Support: Safari <=7 - 9.1, Chrome <=37 - 49
++// Add the top/left cssHooks using jQuery.fn.position
++// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
++// Blink bug: https://bugs.chromium.org/p/chromium/issues/detail?id=589347
++// getComputedStyle returns percent when specified for top/left/bottom/right;
++// rather than make the css module depend on the offset module, just check for it here
++jQuery.each( [ "top", "left" ], function( i, prop ) {
++      jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition,
++              function( elem, computed ) {
++                      if ( computed ) {
++                              computed = curCSS( elem, prop );
++
++                              // If curCSS returns percentage, fallback to offset
++                              return rnumnonpx.test( computed ) ?
++                                      jQuery( elem ).position()[ prop ] + "px" :
++                                      computed;
++                      }
++              }
++      );
++} );
++
++
++// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
++jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
++      jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name },
++              function( defaultExtra, funcName ) {
++
++              // Margin is only for outerHeight, outerWidth
++              jQuery.fn[ funcName ] = function( margin, value ) {
++                      var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
++                              extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
++
++                      return access( this, function( elem, type, value ) {
++                              var doc;
++
++                              if ( isWindow( elem ) ) {
++
++                                      // $( window ).outerWidth/Height return w/h including scrollbars (gh-1729)
++                                      return funcName.indexOf( "outer" ) === 0 ?
++                                              elem[ "inner" + name ] :
++                                              elem.document.documentElement[ "client" + name ];
++                              }
++
++                              // Get document width or height
++                              if ( elem.nodeType === 9 ) {
++                                      doc = elem.documentElement;
++
++                                      // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height],
++                                      // whichever is greatest
++                                      return Math.max(
++                                              elem.body[ "scroll" + name ], doc[ "scroll" + name ],
++                                              elem.body[ "offset" + name ], doc[ "offset" + name ],
++                                              doc[ "client" + name ]
++                                      );
++                              }
++
++                              return value === undefined ?
++
++                                      // Get width or height on the element, requesting but not forcing parseFloat
++                                      jQuery.css( elem, type, extra ) :
++
++                                      // Set width or height on the element
++                                      jQuery.style( elem, type, value, extra );
++                      }, type, chainable ? margin : undefined, chainable );
++              };
++      } );
++} );
++
++
++jQuery.each( ( "blur focus focusin focusout resize scroll click dblclick " +
++      "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
++      "change select submit keydown keypress keyup contextmenu" ).split( " " ),
++      function( i, name ) {
++
++      // Handle event binding
++      jQuery.fn[ name ] = function( data, fn ) {
++              return arguments.length > 0 ?
++                      this.on( name, null, data, fn ) :
++                      this.trigger( name );
++      };
++} );
++
++jQuery.fn.extend( {
++      hover: function( fnOver, fnOut ) {
++              return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
++      }
++} );
++
++
++
++
++jQuery.fn.extend( {
++
++      bind: function( types, data, fn ) {
++              return this.on( types, null, data, fn );
++      },
++      unbind: function( types, fn ) {
++              return this.off( types, null, fn );
++      },
++
++      delegate: function( selector, types, data, fn ) {
++              return this.on( types, selector, data, fn );
++      },
++      undelegate: function( selector, types, fn ) {
++
++              // ( namespace ) or ( selector, types [, fn] )
++              return arguments.length === 1 ?
++                      this.off( selector, "**" ) :
++                      this.off( types, selector || "**", fn );
++      }
++} );
++
++// Bind a function to a context, optionally partially applying any
++// arguments.
++// jQuery.proxy is deprecated to promote standards (specifically Function#bind)
++// However, it is not slated for removal any time soon
++jQuery.proxy = function( fn, context ) {
++      var tmp, args, proxy;
++
++      if ( typeof context === "string" ) {
++              tmp = fn[ context ];
++              context = fn;
++              fn = tmp;
++      }
++
++      // Quick check to determine if target is callable, in the spec
++      // this throws a TypeError, but we will just return undefined.
++      if ( !isFunction( fn ) ) {
++              return undefined;
++      }
++
++      // Simulated bind
++      args = slice.call( arguments, 2 );
++      proxy = function() {
++              return fn.apply( context || this, args.concat( slice.call( arguments ) ) );
++      };
++
++      // Set the guid of unique handler to the same of original handler, so it can be removed
++      proxy.guid = fn.guid = fn.guid || jQuery.guid++;
++
++      return proxy;
++};
++
++jQuery.holdReady = function( hold ) {
++      if ( hold ) {
++              jQuery.readyWait++;
++      } else {
++              jQuery.ready( true );
++      }
++};
++jQuery.isArray = Array.isArray;
++jQuery.parseJSON = JSON.parse;
++jQuery.nodeName = nodeName;
++jQuery.isFunction = isFunction;
++jQuery.isWindow = isWindow;
++jQuery.camelCase = camelCase;
++jQuery.type = toType;
++
++jQuery.now = Date.now;
++
++jQuery.isNumeric = function( obj ) {
++
++      // As of jQuery 3.0, isNumeric is limited to
++      // strings and numbers (primitives or objects)
++      // that can be coerced to finite numbers (gh-2662)
++      var type = jQuery.type( obj );
++      return ( type === "number" || type === "string" ) &&
++
++              // parseFloat NaNs numeric-cast false positives ("")
++              // ...but misinterprets leading-number strings, particularly hex literals ("0x...")
++              // subtraction forces infinities to NaN
++              !isNaN( obj - parseFloat( obj ) );
++};
++
++
++
++
++// Register as a named AMD module, since jQuery can be concatenated with other
++// files that may use define, but not via a proper concatenation script that
++// understands anonymous AMD modules. A named AMD is safest and most robust
++// way to register. Lowercase jquery is used because AMD module names are
++// derived from file names, and jQuery is normally delivered in a lowercase
++// file name. Do this after creating the global so that if an AMD module wants
++// to call noConflict to hide this version of jQuery, it will work.
++
++// Note that for maximum portability, libraries that are not jQuery should
++// declare themselves as anonymous modules, and avoid setting a global if an
++// AMD loader is present. jQuery is a special case. For more information, see
++// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon
++
++if ( typeof define === "function" && define.amd ) {
++      define( "jquery", [], function() {
++              return jQuery;
++      } );
++}
++
++
++
++
++var
++
++      // Map over jQuery in case of overwrite
++      _jQuery = window.jQuery,
++
++      // Map over the $ in case of overwrite
++      _$ = window.$;
++
++jQuery.noConflict = function( deep ) {
++      if ( window.$ === jQuery ) {
++              window.$ = _$;
++      }
++
++      if ( deep && window.jQuery === jQuery ) {
++              window.jQuery = _jQuery;
++      }
++
++      return jQuery;
++};
++
++// Expose jQuery and $ identifiers, even in AMD
++// (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
++// and CommonJS for browser emulators (#13566)
++if ( !noGlobal ) {
++      window.jQuery = window.$ = jQuery;
++}
++
++
++
++
++return jQuery;
++} );
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..31ee9cd8116b6b65683351c782446eb7ee48cf15
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,16617 @@@
++/*! jQuery UI - v1.11.4 - 2015-03-11
++* http://jqueryui.com
++* Includes: core.js, widget.js, mouse.js, position.js, accordion.js, autocomplete.js, button.js, datepicker.js, dialog.js, draggable.js, droppable.js, effect.js, effect-blind.js, effect-bounce.js, effect-clip.js, effect-drop.js, effect-explode.js, effect-fade.js, effect-fold.js, effect-highlight.js, effect-puff.js, effect-pulsate.js, effect-scale.js, effect-shake.js, effect-size.js, effect-slide.js, effect-transfer.js, menu.js, progressbar.js, resizable.js, selectable.js, selectmenu.js, slider.js, sortable.js, spinner.js, tabs.js, tooltip.js
++* Copyright 2015 jQuery Foundation and other contributors; Licensed MIT */
++
++(function( factory ) {
++      if ( typeof define === "function" && define.amd ) {
++
++              // AMD. Register as an anonymous module.
++              define([ "jquery" ], factory );
++      } else {
++
++              // Browser globals
++              factory( jQuery );
++      }
++}(function( $ ) {
++/*!
++ * jQuery UI Core 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/category/ui-core/
++ */
++
++
++// $.ui might exist from components with no dependencies, e.g., $.ui.position
++$.ui = $.ui || {};
++
++$.extend( $.ui, {
++      version: "1.11.4",
++
++      keyCode: {
++              BACKSPACE: 8,
++              COMMA: 188,
++              DELETE: 46,
++              DOWN: 40,
++              END: 35,
++              ENTER: 13,
++              ESCAPE: 27,
++              HOME: 36,
++              LEFT: 37,
++              PAGE_DOWN: 34,
++              PAGE_UP: 33,
++              PERIOD: 190,
++              RIGHT: 39,
++              SPACE: 32,
++              TAB: 9,
++              UP: 38
++      }
++});
++
++// plugins
++$.fn.extend({
++      scrollParent: function( includeHidden ) {
++              var position = this.css( "position" ),
++                      excludeStaticParent = position === "absolute",
++                      overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/,
++                      scrollParent = this.parents().filter( function() {
++                              var parent = $( this );
++                              if ( excludeStaticParent && parent.css( "position" ) === "static" ) {
++                                      return false;
++                              }
++                              return overflowRegex.test( parent.css( "overflow" ) + parent.css( "overflow-y" ) + parent.css( "overflow-x" ) );
++                      }).eq( 0 );
++
++              return position === "fixed" || !scrollParent.length ? $( this[ 0 ].ownerDocument || document ) : scrollParent;
++      },
++
++      uniqueId: (function() {
++              var uuid = 0;
++
++              return function() {
++                      return this.each(function() {
++                              if ( !this.id ) {
++                                      this.id = "ui-id-" + ( ++uuid );
++                              }
++                      });
++              };
++      })(),
++
++      removeUniqueId: function() {
++              return this.each(function() {
++                      if ( /^ui-id-\d+$/.test( this.id ) ) {
++                              $( this ).removeAttr( "id" );
++                      }
++              });
++      }
++});
++
++// selectors
++function focusable( element, isTabIndexNotNaN ) {
++      var map, mapName, img,
++              nodeName = element.nodeName.toLowerCase();
++      if ( "area" === nodeName ) {
++              map = element.parentNode;
++              mapName = map.name;
++              if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
++                      return false;
++              }
++              img = $( "img[usemap='#" + mapName + "']" )[ 0 ];
++              return !!img && visible( img );
++      }
++      return ( /^(input|select|textarea|button|object)$/.test( nodeName ) ?
++              !element.disabled :
++              "a" === nodeName ?
++                      element.href || isTabIndexNotNaN :
++                      isTabIndexNotNaN) &&
++              // the element and all of its ancestors must be visible
++              visible( element );
++}
++
++function visible( element ) {
++      return $.expr.filters.visible( element ) &&
++              !$( element ).parents().addBack().filter(function() {
++                      return $.css( this, "visibility" ) === "hidden";
++              }).length;
++}
++
++$.extend( $.expr[ ":" ], {
++      data: $.expr.createPseudo ?
++              $.expr.createPseudo(function( dataName ) {
++                      return function( elem ) {
++                              return !!$.data( elem, dataName );
++                      };
++              }) :
++              // support: jQuery <1.8
++              function( elem, i, match ) {
++                      return !!$.data( elem, match[ 3 ] );
++              },
++
++      focusable: function( element ) {
++              return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) );
++      },
++
++      tabbable: function( element ) {
++              var tabIndex = $.attr( element, "tabindex" ),
++                      isTabIndexNaN = isNaN( tabIndex );
++              return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN );
++      }
++});
++
++// support: jQuery <1.8
++if ( !$( "<a>" ).outerWidth( 1 ).jquery ) {
++      $.each( [ "Width", "Height" ], function( i, name ) {
++              var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
++                      type = name.toLowerCase(),
++                      orig = {
++                              innerWidth: $.fn.innerWidth,
++                              innerHeight: $.fn.innerHeight,
++                              outerWidth: $.fn.outerWidth,
++                              outerHeight: $.fn.outerHeight
++                      };
++
++              function reduce( elem, size, border, margin ) {
++                      $.each( side, function() {
++                              size -= parseFloat( $.css( elem, "padding" + this ) ) || 0;
++                              if ( border ) {
++                                      size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0;
++                              }
++                              if ( margin ) {
++                                      size -= parseFloat( $.css( elem, "margin" + this ) ) || 0;
++                              }
++                      });
++                      return size;
++              }
++
++              $.fn[ "inner" + name ] = function( size ) {
++                      if ( size === undefined ) {
++                              return orig[ "inner" + name ].call( this );
++                      }
++
++                      return this.each(function() {
++                              $( this ).css( type, reduce( this, size ) + "px" );
++                      });
++              };
++
++              $.fn[ "outer" + name] = function( size, margin ) {
++                      if ( typeof size !== "number" ) {
++                              return orig[ "outer" + name ].call( this, size );
++                      }
++
++                      return this.each(function() {
++                              $( this).css( type, reduce( this, size, true, margin ) + "px" );
++                      });
++              };
++      });
++}
++
++// support: jQuery <1.8
++if ( !$.fn.addBack ) {
++      $.fn.addBack = function( selector ) {
++              return this.add( selector == null ?
++                      this.prevObject : this.prevObject.filter( selector )
++              );
++      };
++}
++
++// support: jQuery 1.6.1, 1.6.2 (http://bugs.jquery.com/ticket/9413)
++if ( $( "<a>" ).data( "a-b", "a" ).removeData( "a-b" ).data( "a-b" ) ) {
++      $.fn.removeData = (function( removeData ) {
++              return function( key ) {
++                      if ( arguments.length ) {
++                              return removeData.call( this, $.camelCase( key ) );
++                      } else {
++                              return removeData.call( this );
++                      }
++              };
++      })( $.fn.removeData );
++}
++
++// deprecated
++$.ui.ie = !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() );
++
++$.fn.extend({
++      focus: (function( orig ) {
++              return function( delay, fn ) {
++                      return typeof delay === "number" ?
++                              this.each(function() {
++                                      var elem = this;
++                                      setTimeout(function() {
++                                              $( elem ).focus();
++                                              if ( fn ) {
++                                                      fn.call( elem );
++                                              }
++                                      }, delay );
++                              }) :
++                              orig.apply( this, arguments );
++              };
++      })( $.fn.focus ),
++
++      disableSelection: (function() {
++              var eventType = "onselectstart" in document.createElement( "div" ) ?
++                      "selectstart" :
++                      "mousedown";
++
++              return function() {
++                      return this.bind( eventType + ".ui-disableSelection", function( event ) {
++                              event.preventDefault();
++                      });
++              };
++      })(),
++
++      enableSelection: function() {
++              return this.unbind( ".ui-disableSelection" );
++      },
++
++      zIndex: function( zIndex ) {
++              if ( zIndex !== undefined ) {
++                      return this.css( "zIndex", zIndex );
++              }
++
++              if ( this.length ) {
++                      var elem = $( this[ 0 ] ), position, value;
++                      while ( elem.length && elem[ 0 ] !== document ) {
++                              // Ignore z-index if position is set to a value where z-index is ignored by the browser
++                              // This makes behavior of this function consistent across browsers
++                              // WebKit always returns auto if the element is positioned
++                              position = elem.css( "position" );
++                              if ( position === "absolute" || position === "relative" || position === "fixed" ) {
++                                      // IE returns 0 when zIndex is not specified
++                                      // other browsers return a string
++                                      // we ignore the case of nested elements with an explicit value of 0
++                                      // <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
++                                      value = parseInt( elem.css( "zIndex" ), 10 );
++                                      if ( !isNaN( value ) && value !== 0 ) {
++                                              return value;
++                                      }
++                              }
++                              elem = elem.parent();
++                      }
++              }
++
++              return 0;
++      }
++});
++
++// $.ui.plugin is deprecated. Use $.widget() extensions instead.
++$.ui.plugin = {
++      add: function( module, option, set ) {
++              var i,
++                      proto = $.ui[ module ].prototype;
++              for ( i in set ) {
++                      proto.plugins[ i ] = proto.plugins[ i ] || [];
++                      proto.plugins[ i ].push( [ option, set[ i ] ] );
++              }
++      },
++      call: function( instance, name, args, allowDisconnected ) {
++              var i,
++                      set = instance.plugins[ name ];
++
++              if ( !set ) {
++                      return;
++              }
++
++              if ( !allowDisconnected && ( !instance.element[ 0 ].parentNode || instance.element[ 0 ].parentNode.nodeType === 11 ) ) {
++                      return;
++              }
++
++              for ( i = 0; i < set.length; i++ ) {
++                      if ( instance.options[ set[ i ][ 0 ] ] ) {
++                              set[ i ][ 1 ].apply( instance.element, args );
++                      }
++              }
++      }
++};
++
++
++/*!
++ * jQuery UI Widget 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/jQuery.widget/
++ */
++
++
++var widget_uuid = 0,
++      widget_slice = Array.prototype.slice;
++
++$.cleanData = (function( orig ) {
++      return function( elems ) {
++              var events, elem, i;
++              for ( i = 0; (elem = elems[i]) != null; i++ ) {
++                      try {
++
++                              // Only trigger remove when necessary to save time
++                              events = $._data( elem, "events" );
++                              if ( events && events.remove ) {
++                                      $( elem ).triggerHandler( "remove" );
++                              }
++
++                      // http://bugs.jquery.com/ticket/8235
++                      } catch ( e ) {}
++              }
++              orig( elems );
++      };
++})( $.cleanData );
++
++$.widget = function( name, base, prototype ) {
++      var fullName, existingConstructor, constructor, basePrototype,
++              // proxiedPrototype allows the provided prototype to remain unmodified
++              // so that it can be used as a mixin for multiple widgets (#8876)
++              proxiedPrototype = {},
++              namespace = name.split( "." )[ 0 ];
++
++      name = name.split( "." )[ 1 ];
++      fullName = namespace + "-" + name;
++
++      if ( !prototype ) {
++              prototype = base;
++              base = $.Widget;
++      }
++
++      // create selector for plugin
++      $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) {
++              return !!$.data( elem, fullName );
++      };
++
++      $[ namespace ] = $[ namespace ] || {};
++      existingConstructor = $[ namespace ][ name ];
++      constructor = $[ namespace ][ name ] = function( options, element ) {
++              // allow instantiation without "new" keyword
++              if ( !this._createWidget ) {
++                      return new constructor( options, element );
++              }
++
++              // allow instantiation without initializing for simple inheritance
++              // must use "new" keyword (the code above always passes args)
++              if ( arguments.length ) {
++                      this._createWidget( options, element );
++              }
++      };
++      // extend with the existing constructor to carry over any static properties
++      $.extend( constructor, existingConstructor, {
++              version: prototype.version,
++              // copy the object used to create the prototype in case we need to
++              // redefine the widget later
++              _proto: $.extend( {}, prototype ),
++              // track widgets that inherit from this widget in case this widget is
++              // redefined after a widget inherits from it
++              _childConstructors: []
++      });
++
++      basePrototype = new base();
++      // we need to make the options hash a property directly on the new instance
++      // otherwise we'll modify the options hash on the prototype that we're
++      // inheriting from
++      basePrototype.options = $.widget.extend( {}, basePrototype.options );
++      $.each( prototype, function( prop, value ) {
++              if ( !$.isFunction( value ) ) {
++                      proxiedPrototype[ prop ] = value;
++                      return;
++              }
++              proxiedPrototype[ prop ] = (function() {
++                      var _super = function() {
++                                      return base.prototype[ prop ].apply( this, arguments );
++                              },
++                              _superApply = function( args ) {
++                                      return base.prototype[ prop ].apply( this, args );
++                              };
++                      return function() {
++                              var __super = this._super,
++                                      __superApply = this._superApply,
++                                      returnValue;
++
++                              this._super = _super;
++                              this._superApply = _superApply;
++
++                              returnValue = value.apply( this, arguments );
++
++                              this._super = __super;
++                              this._superApply = __superApply;
++
++                              return returnValue;
++                      };
++              })();
++      });
++      constructor.prototype = $.widget.extend( basePrototype, {
++              // TODO: remove support for widgetEventPrefix
++              // always use the name + a colon as the prefix, e.g., draggable:start
++              // don't prefix for widgets that aren't DOM-based
++              widgetEventPrefix: existingConstructor ? (basePrototype.widgetEventPrefix || name) : name
++      }, proxiedPrototype, {
++              constructor: constructor,
++              namespace: namespace,
++              widgetName: name,
++              widgetFullName: fullName
++      });
++
++      // If this widget is being redefined then we need to find all widgets that
++      // are inheriting from it and redefine all of them so that they inherit from
++      // the new version of this widget. We're essentially trying to replace one
++      // level in the prototype chain.
++      if ( existingConstructor ) {
++              $.each( existingConstructor._childConstructors, function( i, child ) {
++                      var childPrototype = child.prototype;
++
++                      // redefine the child widget using the same prototype that was
++                      // originally used, but inherit from the new version of the base
++                      $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto );
++              });
++              // remove the list of existing child constructors from the old constructor
++              // so the old child constructors can be garbage collected
++              delete existingConstructor._childConstructors;
++      } else {
++              base._childConstructors.push( constructor );
++      }
++
++      $.widget.bridge( name, constructor );
++
++      return constructor;
++};
++
++$.widget.extend = function( target ) {
++      var input = widget_slice.call( arguments, 1 ),
++              inputIndex = 0,
++              inputLength = input.length,
++              key,
++              value;
++      for ( ; inputIndex < inputLength; inputIndex++ ) {
++              for ( key in input[ inputIndex ] ) {
++                      value = input[ inputIndex ][ key ];
++                      if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
++                              // Clone objects
++                              if ( $.isPlainObject( value ) ) {
++                                      target[ key ] = $.isPlainObject( target[ key ] ) ?
++                                              $.widget.extend( {}, target[ key ], value ) :
++                                              // Don't extend strings, arrays, etc. with objects
++                                              $.widget.extend( {}, value );
++                              // Copy everything else by reference
++                              } else {
++                                      target[ key ] = value;
++                              }
++                      }
++              }
++      }
++      return target;
++};
++
++$.widget.bridge = function( name, object ) {
++      var fullName = object.prototype.widgetFullName || name;
++      $.fn[ name ] = function( options ) {
++              var isMethodCall = typeof options === "string",
++                      args = widget_slice.call( arguments, 1 ),
++                      returnValue = this;
++
++              if ( isMethodCall ) {
++                      this.each(function() {
++                              var methodValue,
++                                      instance = $.data( this, fullName );
++                              if ( options === "instance" ) {
++                                      returnValue = instance;
++                                      return false;
++                              }
++                              if ( !instance ) {
++                                      return $.error( "cannot call methods on " + name + " prior to initialization; " +
++                                              "attempted to call method '" + options + "'" );
++                              }
++                              if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) {
++                                      return $.error( "no such method '" + options + "' for " + name + " widget instance" );
++                              }
++                              methodValue = instance[ options ].apply( instance, args );
++                              if ( methodValue !== instance && methodValue !== undefined ) {
++                                      returnValue = methodValue && methodValue.jquery ?
++                                              returnValue.pushStack( methodValue.get() ) :
++                                              methodValue;
++                                      return false;
++                              }
++                      });
++              } else {
++
++                      // Allow multiple hashes to be passed on init
++                      if ( args.length ) {
++                              options = $.widget.extend.apply( null, [ options ].concat(args) );
++                      }
++
++                      this.each(function() {
++                              var instance = $.data( this, fullName );
++                              if ( instance ) {
++                                      instance.option( options || {} );
++                                      if ( instance._init ) {
++                                              instance._init();
++                                      }
++                              } else {
++                                      $.data( this, fullName, new object( options, this ) );
++                              }
++                      });
++              }
++
++              return returnValue;
++      };
++};
++
++$.Widget = function( /* options, element */ ) {};
++$.Widget._childConstructors = [];
++
++$.Widget.prototype = {
++      widgetName: "widget",
++      widgetEventPrefix: "",
++      defaultElement: "<div>",
++      options: {
++              disabled: false,
++
++              // callbacks
++              create: null
++      },
++      _createWidget: function( options, element ) {
++              element = $( element || this.defaultElement || this )[ 0 ];
++              this.element = $( element );
++              this.uuid = widget_uuid++;
++              this.eventNamespace = "." + this.widgetName + this.uuid;
++
++              this.bindings = $();
++              this.hoverable = $();
++              this.focusable = $();
++
++              if ( element !== this ) {
++                      $.data( element, this.widgetFullName, this );
++                      this._on( true, this.element, {
++                              remove: function( event ) {
++                                      if ( event.target === element ) {
++                                              this.destroy();
++                                      }
++                              }
++                      });
++                      this.document = $( element.style ?
++                              // element within the document
++                              element.ownerDocument :
++                              // element is window or document
++                              element.document || element );
++                      this.window = $( this.document[0].defaultView || this.document[0].parentWindow );
++              }
++
++              this.options = $.widget.extend( {},
++                      this.options,
++                      this._getCreateOptions(),
++                      options );
++
++              this._create();
++              this._trigger( "create", null, this._getCreateEventData() );
++              this._init();
++      },
++      _getCreateOptions: $.noop,
++      _getCreateEventData: $.noop,
++      _create: $.noop,
++      _init: $.noop,
++
++      destroy: function() {
++              this._destroy();
++              // we can probably remove the unbind calls in 2.0
++              // all event bindings should go through this._on()
++              this.element
++                      .unbind( this.eventNamespace )
++                      .removeData( this.widgetFullName )
++                      // support: jquery <1.6.3
++                      // http://bugs.jquery.com/ticket/9413
++                      .removeData( $.camelCase( this.widgetFullName ) );
++              this.widget()
++                      .unbind( this.eventNamespace )
++                      .removeAttr( "aria-disabled" )
++                      .removeClass(
++                              this.widgetFullName + "-disabled " +
++                              "ui-state-disabled" );
++
++              // clean up events and states
++              this.bindings.unbind( this.eventNamespace );
++              this.hoverable.removeClass( "ui-state-hover" );
++              this.focusable.removeClass( "ui-state-focus" );
++      },
++      _destroy: $.noop,
++
++      widget: function() {
++              return this.element;
++      },
++
++      option: function( key, value ) {
++              var options = key,
++                      parts,
++                      curOption,
++                      i;
++
++              if ( arguments.length === 0 ) {
++                      // don't return a reference to the internal hash
++                      return $.widget.extend( {}, this.options );
++              }
++
++              if ( typeof key === "string" ) {
++                      // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
++                      options = {};
++                      parts = key.split( "." );
++                      key = parts.shift();
++                      if ( parts.length ) {
++                              curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
++                              for ( i = 0; i < parts.length - 1; i++ ) {
++                                      curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
++                                      curOption = curOption[ parts[ i ] ];
++                              }
++                              key = parts.pop();
++                              if ( arguments.length === 1 ) {
++                                      return curOption[ key ] === undefined ? null : curOption[ key ];
++                              }
++                              curOption[ key ] = value;
++                      } else {
++                              if ( arguments.length === 1 ) {
++                                      return this.options[ key ] === undefined ? null : this.options[ key ];
++                              }
++                              options[ key ] = value;
++                      }
++              }
++
++              this._setOptions( options );
++
++              return this;
++      },
++      _setOptions: function( options ) {
++              var key;
++
++              for ( key in options ) {
++                      this._setOption( key, options[ key ] );
++              }
++
++              return this;
++      },
++      _setOption: function( key, value ) {
++              this.options[ key ] = value;
++
++              if ( key === "disabled" ) {
++                      this.widget()
++                              .toggleClass( this.widgetFullName + "-disabled", !!value );
++
++                      // If the widget is becoming disabled, then nothing is interactive
++                      if ( value ) {
++                              this.hoverable.removeClass( "ui-state-hover" );
++                              this.focusable.removeClass( "ui-state-focus" );
++                      }
++              }
++
++              return this;
++      },
++
++      enable: function() {
++              return this._setOptions({ disabled: false });
++      },
++      disable: function() {
++              return this._setOptions({ disabled: true });
++      },
++
++      _on: function( suppressDisabledCheck, element, handlers ) {
++              var delegateElement,
++                      instance = this;
++
++              // no suppressDisabledCheck flag, shuffle arguments
++              if ( typeof suppressDisabledCheck !== "boolean" ) {
++                      handlers = element;
++                      element = suppressDisabledCheck;
++                      suppressDisabledCheck = false;
++              }
++
++              // no element argument, shuffle and use this.element
++              if ( !handlers ) {
++                      handlers = element;
++                      element = this.element;
++                      delegateElement = this.widget();
++              } else {
++                      element = delegateElement = $( element );
++                      this.bindings = this.bindings.add( element );
++              }
++
++              $.each( handlers, function( event, handler ) {
++                      function handlerProxy() {
++                              // allow widgets to customize the disabled handling
++                              // - disabled as an array instead of boolean
++                              // - disabled class as method for disabling individual parts
++                              if ( !suppressDisabledCheck &&
++                                              ( instance.options.disabled === true ||
++                                                      $( this ).hasClass( "ui-state-disabled" ) ) ) {
++                                      return;
++                              }
++                              return ( typeof handler === "string" ? instance[ handler ] : handler )
++                                      .apply( instance, arguments );
++                      }
++
++                      // copy the guid so direct unbinding works
++                      if ( typeof handler !== "string" ) {
++                              handlerProxy.guid = handler.guid =
++                                      handler.guid || handlerProxy.guid || $.guid++;
++                      }
++
++                      var match = event.match( /^([\w:-]*)\s*(.*)$/ ),
++                              eventName = match[1] + instance.eventNamespace,
++                              selector = match[2];
++                      if ( selector ) {
++                              delegateElement.delegate( selector, eventName, handlerProxy );
++                      } else {
++                              element.bind( eventName, handlerProxy );
++                      }
++              });
++      },
++
++      _off: function( element, eventName ) {
++              eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) +
++                      this.eventNamespace;
++              element.unbind( eventName ).undelegate( eventName );
++
++              // Clear the stack to avoid memory leaks (#10056)
++              this.bindings = $( this.bindings.not( element ).get() );
++              this.focusable = $( this.focusable.not( element ).get() );
++              this.hoverable = $( this.hoverable.not( element ).get() );
++      },
++
++      _delay: function( handler, delay ) {
++              function handlerProxy() {
++                      return ( typeof handler === "string" ? instance[ handler ] : handler )
++                              .apply( instance, arguments );
++              }
++              var instance = this;
++              return setTimeout( handlerProxy, delay || 0 );
++      },
++
++      _hoverable: function( element ) {
++              this.hoverable = this.hoverable.add( element );
++              this._on( element, {
++                      mouseenter: function( event ) {
++                              $( event.currentTarget ).addClass( "ui-state-hover" );
++                      },
++                      mouseleave: function( event ) {
++                              $( event.currentTarget ).removeClass( "ui-state-hover" );
++                      }
++              });
++      },
++
++      _focusable: function( element ) {
++              this.focusable = this.focusable.add( element );
++              this._on( element, {
++                      focusin: function( event ) {
++                              $( event.currentTarget ).addClass( "ui-state-focus" );
++                      },
++                      focusout: function( event ) {
++                              $( event.currentTarget ).removeClass( "ui-state-focus" );
++                      }
++              });
++      },
++
++      _trigger: function( type, event, data ) {
++              var prop, orig,
++                      callback = this.options[ type ];
++
++              data = data || {};
++              event = $.Event( event );
++              event.type = ( type === this.widgetEventPrefix ?
++                      type :
++                      this.widgetEventPrefix + type ).toLowerCase();
++              // the original event may come from any element
++              // so we need to reset the target on the new event
++              event.target = this.element[ 0 ];
++
++              // copy original event properties over to the new event
++              orig = event.originalEvent;
++              if ( orig ) {
++                      for ( prop in orig ) {
++                              if ( !( prop in event ) ) {
++                                      event[ prop ] = orig[ prop ];
++                              }
++                      }
++              }
++
++              this.element.trigger( event, data );
++              return !( $.isFunction( callback ) &&
++                      callback.apply( this.element[0], [ event ].concat( data ) ) === false ||
++                      event.isDefaultPrevented() );
++      }
++};
++
++$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
++      $.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
++              if ( typeof options === "string" ) {
++                      options = { effect: options };
++              }
++              var hasOptions,
++                      effectName = !options ?
++                              method :
++                              options === true || typeof options === "number" ?
++                                      defaultEffect :
++                                      options.effect || defaultEffect;
++              options = options || {};
++              if ( typeof options === "number" ) {
++                      options = { duration: options };
++              }
++              hasOptions = !$.isEmptyObject( options );
++              options.complete = callback;
++              if ( options.delay ) {
++                      element.delay( options.delay );
++              }
++              if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
++                      element[ method ]( options );
++              } else if ( effectName !== method && element[ effectName ] ) {
++                      element[ effectName ]( options.duration, options.easing, callback );
++              } else {
++                      element.queue(function( next ) {
++                              $( this )[ method ]();
++                              if ( callback ) {
++                                      callback.call( element[ 0 ] );
++                              }
++                              next();
++                      });
++              }
++      };
++});
++
++var widget = $.widget;
++
++
++/*!
++ * jQuery UI Mouse 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/mouse/
++ */
++
++
++var mouseHandled = false;
++$( document ).mouseup( function() {
++      mouseHandled = false;
++});
++
++var mouse = $.widget("ui.mouse", {
++      version: "1.11.4",
++      options: {
++              cancel: "input,textarea,button,select,option",
++              distance: 1,
++              delay: 0
++      },
++      _mouseInit: function() {
++              var that = this;
++
++              this.element
++                      .bind("mousedown." + this.widgetName, function(event) {
++                              return that._mouseDown(event);
++                      })
++                      .bind("click." + this.widgetName, function(event) {
++                              if (true === $.data(event.target, that.widgetName + ".preventClickEvent")) {
++                                      $.removeData(event.target, that.widgetName + ".preventClickEvent");
++                                      event.stopImmediatePropagation();
++                                      return false;
++                              }
++                      });
++
++              this.started = false;
++      },
++
++      // TODO: make sure destroying one instance of mouse doesn't mess with
++      // other instances of mouse
++      _mouseDestroy: function() {
++              this.element.unbind("." + this.widgetName);
++              if ( this._mouseMoveDelegate ) {
++                      this.document
++                              .unbind("mousemove." + this.widgetName, this._mouseMoveDelegate)
++                              .unbind("mouseup." + this.widgetName, this._mouseUpDelegate);
++              }
++      },
++
++      _mouseDown: function(event) {
++              // don't let more than one widget handle mouseStart
++              if ( mouseHandled ) {
++                      return;
++              }
++
++              this._mouseMoved = false;
++
++              // we may have missed mouseup (out of window)
++              (this._mouseStarted && this._mouseUp(event));
++
++              this._mouseDownEvent = event;
++
++              var that = this,
++                      btnIsLeft = (event.which === 1),
++                      // event.target.nodeName works around a bug in IE 8 with
++                      // disabled inputs (#7620)
++                      elIsCancel = (typeof this.options.cancel === "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false);
++              if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
++                      return true;
++              }
++
++              this.mouseDelayMet = !this.options.delay;
++              if (!this.mouseDelayMet) {
++                      this._mouseDelayTimer = setTimeout(function() {
++                              that.mouseDelayMet = true;
++                      }, this.options.delay);
++              }
++
++              if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
++                      this._mouseStarted = (this._mouseStart(event) !== false);
++                      if (!this._mouseStarted) {
++                              event.preventDefault();
++                              return true;
++                      }
++              }
++
++              // Click event may never have fired (Gecko & Opera)
++              if (true === $.data(event.target, this.widgetName + ".preventClickEvent")) {
++                      $.removeData(event.target, this.widgetName + ".preventClickEvent");
++              }
++
++              // these delegates are required to keep context
++              this._mouseMoveDelegate = function(event) {
++                      return that._mouseMove(event);
++              };
++              this._mouseUpDelegate = function(event) {
++                      return that._mouseUp(event);
++              };
++
++              this.document
++                      .bind( "mousemove." + this.widgetName, this._mouseMoveDelegate )
++                      .bind( "mouseup." + this.widgetName, this._mouseUpDelegate );
++
++              event.preventDefault();
++
++              mouseHandled = true;
++              return true;
++      },
++
++      _mouseMove: function(event) {
++              // Only check for mouseups outside the document if you've moved inside the document
++              // at least once. This prevents the firing of mouseup in the case of IE<9, which will
++              // fire a mousemove event if content is placed under the cursor. See #7778
++              // Support: IE <9
++              if ( this._mouseMoved ) {
++                      // IE mouseup check - mouseup happened when mouse was out of window
++                      if ($.ui.ie && ( !document.documentMode || document.documentMode < 9 ) && !event.button) {
++                              return this._mouseUp(event);
++
++                      // Iframe mouseup check - mouseup occurred in another document
++                      } else if ( !event.which ) {
++                              return this._mouseUp( event );
++                      }
++              }
++
++              if ( event.which || event.button ) {
++                      this._mouseMoved = true;
++              }
++
++              if (this._mouseStarted) {
++                      this._mouseDrag(event);
++                      return event.preventDefault();
++              }
++
++              if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
++                      this._mouseStarted =
++                              (this._mouseStart(this._mouseDownEvent, event) !== false);
++                      (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
++              }
++
++              return !this._mouseStarted;
++      },
++
++      _mouseUp: function(event) {
++              this.document
++                      .unbind( "mousemove." + this.widgetName, this._mouseMoveDelegate )
++                      .unbind( "mouseup." + this.widgetName, this._mouseUpDelegate );
++
++              if (this._mouseStarted) {
++                      this._mouseStarted = false;
++
++                      if (event.target === this._mouseDownEvent.target) {
++                              $.data(event.target, this.widgetName + ".preventClickEvent", true);
++                      }
++
++                      this._mouseStop(event);
++              }
++
++              mouseHandled = false;
++              return false;
++      },
++
++      _mouseDistanceMet: function(event) {
++              return (Math.max(
++                              Math.abs(this._mouseDownEvent.pageX - event.pageX),
++                              Math.abs(this._mouseDownEvent.pageY - event.pageY)
++                      ) >= this.options.distance
++              );
++      },
++
++      _mouseDelayMet: function(/* event */) {
++              return this.mouseDelayMet;
++      },
++
++      // These are placeholder methods, to be overriden by extending plugin
++      _mouseStart: function(/* event */) {},
++      _mouseDrag: function(/* event */) {},
++      _mouseStop: function(/* event */) {},
++      _mouseCapture: function(/* event */) { return true; }
++});
++
++
++/*!
++ * jQuery UI Position 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/position/
++ */
++
++(function() {
++
++$.ui = $.ui || {};
++
++var cachedScrollbarWidth, supportsOffsetFractions,
++      max = Math.max,
++      abs = Math.abs,
++      round = Math.round,
++      rhorizontal = /left|center|right/,
++      rvertical = /top|center|bottom/,
++      roffset = /[\+\-]\d+(\.[\d]+)?%?/,
++      rposition = /^\w+/,
++      rpercent = /%$/,
++      _position = $.fn.position;
++
++function getOffsets( offsets, width, height ) {
++      return [
++              parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ),
++              parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 )
++      ];
++}
++
++function parseCss( element, property ) {
++      return parseInt( $.css( element, property ), 10 ) || 0;
++}
++
++function getDimensions( elem ) {
++      var raw = elem[0];
++      if ( raw.nodeType === 9 ) {
++              return {
++                      width: elem.width(),
++                      height: elem.height(),
++                      offset: { top: 0, left: 0 }
++              };
++      }
++      if ( $.isWindow( raw ) ) {
++              return {
++                      width: elem.width(),
++                      height: elem.height(),
++                      offset: { top: elem.scrollTop(), left: elem.scrollLeft() }
++              };
++      }
++      if ( raw.preventDefault ) {
++              return {
++                      width: 0,
++                      height: 0,
++                      offset: { top: raw.pageY, left: raw.pageX }
++              };
++      }
++      return {
++              width: elem.outerWidth(),
++              height: elem.outerHeight(),
++              offset: elem.offset()
++      };
++}
++
++$.position = {
++      scrollbarWidth: function() {
++              if ( cachedScrollbarWidth !== undefined ) {
++                      return cachedScrollbarWidth;
++              }
++              var w1, w2,
++                      div = $( "<div style='display:block;position:absolute;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>" ),
++                      innerDiv = div.children()[0];
++
++              $( "body" ).append( div );
++              w1 = innerDiv.offsetWidth;
++              div.css( "overflow", "scroll" );
++
++              w2 = innerDiv.offsetWidth;
++
++              if ( w1 === w2 ) {
++                      w2 = div[0].clientWidth;
++              }
++
++              div.remove();
++
++              return (cachedScrollbarWidth = w1 - w2);
++      },
++      getScrollInfo: function( within ) {
++              var overflowX = within.isWindow || within.isDocument ? "" :
++                              within.element.css( "overflow-x" ),
++                      overflowY = within.isWindow || within.isDocument ? "" :
++                              within.element.css( "overflow-y" ),
++                      hasOverflowX = overflowX === "scroll" ||
++                              ( overflowX === "auto" && within.width < within.element[0].scrollWidth ),
++                      hasOverflowY = overflowY === "scroll" ||
++                              ( overflowY === "auto" && within.height < within.element[0].scrollHeight );
++              return {
++                      width: hasOverflowY ? $.position.scrollbarWidth() : 0,
++                      height: hasOverflowX ? $.position.scrollbarWidth() : 0
++              };
++      },
++      getWithinInfo: function( element ) {
++              var withinElement = $( element || window ),
++                      isWindow = $.isWindow( withinElement[0] ),
++                      isDocument = !!withinElement[ 0 ] && withinElement[ 0 ].nodeType === 9;
++              return {
++                      element: withinElement,
++                      isWindow: isWindow,
++                      isDocument: isDocument,
++                      offset: withinElement.offset() || { left: 0, top: 0 },
++                      scrollLeft: withinElement.scrollLeft(),
++                      scrollTop: withinElement.scrollTop(),
++
++                      // support: jQuery 1.6.x
++                      // jQuery 1.6 doesn't support .outerWidth/Height() on documents or windows
++                      width: isWindow || isDocument ? withinElement.width() : withinElement.outerWidth(),
++                      height: isWindow || isDocument ? withinElement.height() : withinElement.outerHeight()
++              };
++      }
++};
++
++$.fn.position = function( options ) {
++      if ( !options || !options.of ) {
++              return _position.apply( this, arguments );
++      }
++
++      // make a copy, we don't want to modify arguments
++      options = $.extend( {}, options );
++
++      var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions,
++              target = $( options.of ),
++              within = $.position.getWithinInfo( options.within ),
++              scrollInfo = $.position.getScrollInfo( within ),
++              collision = ( options.collision || "flip" ).split( " " ),
++              offsets = {};
++
++      dimensions = getDimensions( target );
++      if ( target[0].preventDefault ) {
++              // force left top to allow flipping
++              options.at = "left top";
++      }
++      targetWidth = dimensions.width;
++      targetHeight = dimensions.height;
++      targetOffset = dimensions.offset;
++      // clone to reuse original targetOffset later
++      basePosition = $.extend( {}, targetOffset );
++
++      // force my and at to have valid horizontal and vertical positions
++      // if a value is missing or invalid, it will be converted to center
++      $.each( [ "my", "at" ], function() {
++              var pos = ( options[ this ] || "" ).split( " " ),
++                      horizontalOffset,
++                      verticalOffset;
++
++              if ( pos.length === 1) {
++                      pos = rhorizontal.test( pos[ 0 ] ) ?
++                              pos.concat( [ "center" ] ) :
++                              rvertical.test( pos[ 0 ] ) ?
++                                      [ "center" ].concat( pos ) :
++                                      [ "center", "center" ];
++              }
++              pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center";
++              pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center";
++
++              // calculate offsets
++              horizontalOffset = roffset.exec( pos[ 0 ] );
++              verticalOffset = roffset.exec( pos[ 1 ] );
++              offsets[ this ] = [
++                      horizontalOffset ? horizontalOffset[ 0 ] : 0,
++                      verticalOffset ? verticalOffset[ 0 ] : 0
++              ];
++
++              // reduce to just the positions without the offsets
++              options[ this ] = [
++                      rposition.exec( pos[ 0 ] )[ 0 ],
++                      rposition.exec( pos[ 1 ] )[ 0 ]
++              ];
++      });
++
++      // normalize collision option
++      if ( collision.length === 1 ) {
++              collision[ 1 ] = collision[ 0 ];
++      }
++
++      if ( options.at[ 0 ] === "right" ) {
++              basePosition.left += targetWidth;
++      } else if ( options.at[ 0 ] === "center" ) {
++              basePosition.left += targetWidth / 2;
++      }
++
++      if ( options.at[ 1 ] === "bottom" ) {
++              basePosition.top += targetHeight;
++      } else if ( options.at[ 1 ] === "center" ) {
++              basePosition.top += targetHeight / 2;
++      }
++
++      atOffset = getOffsets( offsets.at, targetWidth, targetHeight );
++      basePosition.left += atOffset[ 0 ];
++      basePosition.top += atOffset[ 1 ];
++
++      return this.each(function() {
++              var collisionPosition, using,
++                      elem = $( this ),
++                      elemWidth = elem.outerWidth(),
++                      elemHeight = elem.outerHeight(),
++                      marginLeft = parseCss( this, "marginLeft" ),
++                      marginTop = parseCss( this, "marginTop" ),
++                      collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) + scrollInfo.width,
++                      collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) + scrollInfo.height,
++                      position = $.extend( {}, basePosition ),
++                      myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() );
++
++              if ( options.my[ 0 ] === "right" ) {
++                      position.left -= elemWidth;
++              } else if ( options.my[ 0 ] === "center" ) {
++                      position.left -= elemWidth / 2;
++              }
++
++              if ( options.my[ 1 ] === "bottom" ) {
++                      position.top -= elemHeight;
++              } else if ( options.my[ 1 ] === "center" ) {
++                      position.top -= elemHeight / 2;
++              }
++
++              position.left += myOffset[ 0 ];
++              position.top += myOffset[ 1 ];
++
++              // if the browser doesn't support fractions, then round for consistent results
++              if ( !supportsOffsetFractions ) {
++                      position.left = round( position.left );
++                      position.top = round( position.top );
++              }
++
++              collisionPosition = {
++                      marginLeft: marginLeft,
++                      marginTop: marginTop
++              };
++
++              $.each( [ "left", "top" ], function( i, dir ) {
++                      if ( $.ui.position[ collision[ i ] ] ) {
++                              $.ui.position[ collision[ i ] ][ dir ]( position, {
++                                      targetWidth: targetWidth,
++                                      targetHeight: targetHeight,
++                                      elemWidth: elemWidth,
++                                      elemHeight: elemHeight,
++                                      collisionPosition: collisionPosition,
++                                      collisionWidth: collisionWidth,
++                                      collisionHeight: collisionHeight,
++                                      offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ],
++                                      my: options.my,
++                                      at: options.at,
++                                      within: within,
++                                      elem: elem
++                              });
++                      }
++              });
++
++              if ( options.using ) {
++                      // adds feedback as second argument to using callback, if present
++                      using = function( props ) {
++                              var left = targetOffset.left - position.left,
++                                      right = left + targetWidth - elemWidth,
++                                      top = targetOffset.top - position.top,
++                                      bottom = top + targetHeight - elemHeight,
++                                      feedback = {
++                                              target: {
++                                                      element: target,
++                                                      left: targetOffset.left,
++                                                      top: targetOffset.top,
++                                                      width: targetWidth,
++                                                      height: targetHeight
++                                              },
++                                              element: {
++                                                      element: elem,
++                                                      left: position.left,
++                                                      top: position.top,
++                                                      width: elemWidth,
++                                                      height: elemHeight
++                                              },
++                                              horizontal: right < 0 ? "left" : left > 0 ? "right" : "center",
++                                              vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle"
++                                      };
++                              if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) {
++                                      feedback.horizontal = "center";
++                              }
++                              if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) {
++                                      feedback.vertical = "middle";
++                              }
++                              if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) {
++                                      feedback.important = "horizontal";
++                              } else {
++                                      feedback.important = "vertical";
++                              }
++                              options.using.call( this, props, feedback );
++                      };
++              }
++
++              elem.offset( $.extend( position, { using: using } ) );
++      });
++};
++
++$.ui.position = {
++      fit: {
++              left: function( position, data ) {
++                      var within = data.within,
++                              withinOffset = within.isWindow ? within.scrollLeft : within.offset.left,
++                              outerWidth = within.width,
++                              collisionPosLeft = position.left - data.collisionPosition.marginLeft,
++                              overLeft = withinOffset - collisionPosLeft,
++                              overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset,
++                              newOverRight;
++
++                      // element is wider than within
++                      if ( data.collisionWidth > outerWidth ) {
++                              // element is initially over the left side of within
++                              if ( overLeft > 0 && overRight <= 0 ) {
++                                      newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - withinOffset;
++                                      position.left += overLeft - newOverRight;
++                              // element is initially over right side of within
++                              } else if ( overRight > 0 && overLeft <= 0 ) {
++                                      position.left = withinOffset;
++                              // element is initially over both left and right sides of within
++                              } else {
++                                      if ( overLeft > overRight ) {
++                                              position.left = withinOffset + outerWidth - data.collisionWidth;
++                                      } else {
++                                              position.left = withinOffset;
++                                      }
++                              }
++                      // too far left -> align with left edge
++                      } else if ( overLeft > 0 ) {
++                              position.left += overLeft;
++                      // too far right -> align with right edge
++                      } else if ( overRight > 0 ) {
++                              position.left -= overRight;
++                      // adjust based on position and margin
++                      } else {
++                              position.left = max( position.left - collisionPosLeft, position.left );
++                      }
++              },
++              top: function( position, data ) {
++                      var within = data.within,
++                              withinOffset = within.isWindow ? within.scrollTop : within.offset.top,
++                              outerHeight = data.within.height,
++                              collisionPosTop = position.top - data.collisionPosition.marginTop,
++                              overTop = withinOffset - collisionPosTop,
++                              overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset,
++                              newOverBottom;
++
++                      // element is taller than within
++                      if ( data.collisionHeight > outerHeight ) {
++                              // element is initially over the top of within
++                              if ( overTop > 0 && overBottom <= 0 ) {
++                                      newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - withinOffset;
++                                      position.top += overTop - newOverBottom;
++                              // element is initially over bottom of within
++                              } else if ( overBottom > 0 && overTop <= 0 ) {
++                                      position.top = withinOffset;
++                              // element is initially over both top and bottom of within
++                              } else {
++                                      if ( overTop > overBottom ) {
++                                              position.top = withinOffset + outerHeight - data.collisionHeight;
++                                      } else {
++                                              position.top = withinOffset;
++                                      }
++                              }
++                      // too far up -> align with top
++                      } else if ( overTop > 0 ) {
++                              position.top += overTop;
++                      // too far down -> align with bottom edge
++                      } else if ( overBottom > 0 ) {
++                              position.top -= overBottom;
++                      // adjust based on position and margin
++                      } else {
++                              position.top = max( position.top - collisionPosTop, position.top );
++                      }
++              }
++      },
++      flip: {
++              left: function( position, data ) {
++                      var within = data.within,
++                              withinOffset = within.offset.left + within.scrollLeft,
++                              outerWidth = within.width,
++                              offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left,
++                              collisionPosLeft = position.left - data.collisionPosition.marginLeft,
++                              overLeft = collisionPosLeft - offsetLeft,
++                              overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft,
++                              myOffset = data.my[ 0 ] === "left" ?
++                                      -data.elemWidth :
++                                      data.my[ 0 ] === "right" ?
++                                              data.elemWidth :
++                                              0,
++                              atOffset = data.at[ 0 ] === "left" ?
++                                      data.targetWidth :
++                                      data.at[ 0 ] === "right" ?
++                                              -data.targetWidth :
++                                              0,
++                              offset = -2 * data.offset[ 0 ],
++                              newOverRight,
++                              newOverLeft;
++
++                      if ( overLeft < 0 ) {
++                              newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - outerWidth - withinOffset;
++                              if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) {
++                                      position.left += myOffset + atOffset + offset;
++                              }
++                      } else if ( overRight > 0 ) {
++                              newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + atOffset + offset - offsetLeft;
++                              if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) {
++                                      position.left += myOffset + atOffset + offset;
++                              }
++                      }
++              },
++              top: function( position, data ) {
++                      var within = data.within,
++                              withinOffset = within.offset.top + within.scrollTop,
++                              outerHeight = within.height,
++                              offsetTop = within.isWindow ? within.scrollTop : within.offset.top,
++                              collisionPosTop = position.top - data.collisionPosition.marginTop,
++                              overTop = collisionPosTop - offsetTop,
++                              overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop,
++                              top = data.my[ 1 ] === "top",
++                              myOffset = top ?
++                                      -data.elemHeight :
++                                      data.my[ 1 ] === "bottom" ?
++                                              data.elemHeight :
++                                              0,
++                              atOffset = data.at[ 1 ] === "top" ?
++                                      data.targetHeight :
++                                      data.at[ 1 ] === "bottom" ?
++                                              -data.targetHeight :
++                                              0,
++                              offset = -2 * data.offset[ 1 ],
++                              newOverTop,
++                              newOverBottom;
++                      if ( overTop < 0 ) {
++                              newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - outerHeight - withinOffset;
++                              if ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) {
++                                      position.top += myOffset + atOffset + offset;
++                              }
++                      } else if ( overBottom > 0 ) {
++                              newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + offset - offsetTop;
++                              if ( newOverTop > 0 || abs( newOverTop ) < overBottom ) {
++                                      position.top += myOffset + atOffset + offset;
++                              }
++                      }
++              }
++      },
++      flipfit: {
++              left: function() {
++                      $.ui.position.flip.left.apply( this, arguments );
++                      $.ui.position.fit.left.apply( this, arguments );
++              },
++              top: function() {
++                      $.ui.position.flip.top.apply( this, arguments );
++                      $.ui.position.fit.top.apply( this, arguments );
++              }
++      }
++};
++
++// fraction support test
++(function() {
++      var testElement, testElementParent, testElementStyle, offsetLeft, i,
++              body = document.getElementsByTagName( "body" )[ 0 ],
++              div = document.createElement( "div" );
++
++      //Create a "fake body" for testing based on method used in jQuery.support
++      testElement = document.createElement( body ? "div" : "body" );
++      testElementStyle = {
++              visibility: "hidden",
++              width: 0,
++              height: 0,
++              border: 0,
++              margin: 0,
++              background: "none"
++      };
++      if ( body ) {
++              $.extend( testElementStyle, {
++                      position: "absolute",
++                      left: "-1000px",
++                      top: "-1000px"
++              });
++      }
++      for ( i in testElementStyle ) {
++              testElement.style[ i ] = testElementStyle[ i ];
++      }
++      testElement.appendChild( div );
++      testElementParent = body || document.documentElement;
++      testElementParent.insertBefore( testElement, testElementParent.firstChild );
++
++      div.style.cssText = "position: absolute; left: 10.7432222px;";
++
++      offsetLeft = $( div ).offset().left;
++      supportsOffsetFractions = offsetLeft > 10 && offsetLeft < 11;
++
++      testElement.innerHTML = "";
++      testElementParent.removeChild( testElement );
++})();
++
++})();
++
++var position = $.ui.position;
++
++
++/*!
++ * jQuery UI Accordion 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/accordion/
++ */
++
++
++var accordion = $.widget( "ui.accordion", {
++      version: "1.11.4",
++      options: {
++              active: 0,
++              animate: {},
++              collapsible: false,
++              event: "click",
++              header: "> li > :first-child,> :not(li):even",
++              heightStyle: "auto",
++              icons: {
++                      activeHeader: "ui-icon-triangle-1-s",
++                      header: "ui-icon-triangle-1-e"
++              },
++
++              // callbacks
++              activate: null,
++              beforeActivate: null
++      },
++
++      hideProps: {
++              borderTopWidth: "hide",
++              borderBottomWidth: "hide",
++              paddingTop: "hide",
++              paddingBottom: "hide",
++              height: "hide"
++      },
++
++      showProps: {
++              borderTopWidth: "show",
++              borderBottomWidth: "show",
++              paddingTop: "show",
++              paddingBottom: "show",
++              height: "show"
++      },
++
++      _create: function() {
++              var options = this.options;
++              this.prevShow = this.prevHide = $();
++              this.element.addClass( "ui-accordion ui-widget ui-helper-reset" )
++                      // ARIA
++                      .attr( "role", "tablist" );
++
++              // don't allow collapsible: false and active: false / null
++              if ( !options.collapsible && (options.active === false || options.active == null) ) {
++                      options.active = 0;
++              }
++
++              this._processPanels();
++              // handle negative values
++              if ( options.active < 0 ) {
++                      options.active += this.headers.length;
++              }
++              this._refresh();
++      },
++
++      _getCreateEventData: function() {
++              return {
++                      header: this.active,
++                      panel: !this.active.length ? $() : this.active.next()
++              };
++      },
++
++      _createIcons: function() {
++              var icons = this.options.icons;
++              if ( icons ) {
++                      $( "<span>" )
++                              .addClass( "ui-accordion-header-icon ui-icon " + icons.header )
++                              .prependTo( this.headers );
++                      this.active.children( ".ui-accordion-header-icon" )
++                              .removeClass( icons.header )
++                              .addClass( icons.activeHeader );
++                      this.headers.addClass( "ui-accordion-icons" );
++              }
++      },
++
++      _destroyIcons: function() {
++              this.headers
++                      .removeClass( "ui-accordion-icons" )
++                      .children( ".ui-accordion-header-icon" )
++                              .remove();
++      },
++
++      _destroy: function() {
++              var contents;
++
++              // clean up main element
++              this.element
++                      .removeClass( "ui-accordion ui-widget ui-helper-reset" )
++                      .removeAttr( "role" );
++
++              // clean up headers
++              this.headers
++                      .removeClass( "ui-accordion-header ui-accordion-header-active ui-state-default " +
++                              "ui-corner-all ui-state-active ui-state-disabled ui-corner-top" )
++                      .removeAttr( "role" )
++                      .removeAttr( "aria-expanded" )
++                      .removeAttr( "aria-selected" )
++                      .removeAttr( "aria-controls" )
++                      .removeAttr( "tabIndex" )
++                      .removeUniqueId();
++
++              this._destroyIcons();
++
++              // clean up content panels
++              contents = this.headers.next()
++                      .removeClass( "ui-helper-reset ui-widget-content ui-corner-bottom " +
++                              "ui-accordion-content ui-accordion-content-active ui-state-disabled" )
++                      .css( "display", "" )
++                      .removeAttr( "role" )
++                      .removeAttr( "aria-hidden" )
++                      .removeAttr( "aria-labelledby" )
++                      .removeUniqueId();
++
++              if ( this.options.heightStyle !== "content" ) {
++                      contents.css( "height", "" );
++              }
++      },
++
++      _setOption: function( key, value ) {
++              if ( key === "active" ) {
++                      // _activate() will handle invalid values and update this.options
++                      this._activate( value );
++                      return;
++              }
++
++              if ( key === "event" ) {
++                      if ( this.options.event ) {
++                              this._off( this.headers, this.options.event );
++                      }
++                      this._setupEvents( value );
++              }
++
++              this._super( key, value );
++
++              // setting collapsible: false while collapsed; open first panel
++              if ( key === "collapsible" && !value && this.options.active === false ) {
++                      this._activate( 0 );
++              }
++
++              if ( key === "icons" ) {
++                      this._destroyIcons();
++                      if ( value ) {
++                              this._createIcons();
++                      }
++              }
++
++              // #5332 - opacity doesn't cascade to positioned elements in IE
++              // so we need to add the disabled class to the headers and panels
++              if ( key === "disabled" ) {
++                      this.element
++                              .toggleClass( "ui-state-disabled", !!value )
++                              .attr( "aria-disabled", value );
++                      this.headers.add( this.headers.next() )
++                              .toggleClass( "ui-state-disabled", !!value );
++              }
++      },
++
++      _keydown: function( event ) {
++              if ( event.altKey || event.ctrlKey ) {
++                      return;
++              }
++
++              var keyCode = $.ui.keyCode,
++                      length = this.headers.length,
++                      currentIndex = this.headers.index( event.target ),
++                      toFocus = false;
++
++              switch ( event.keyCode ) {
++                      case keyCode.RIGHT:
++                      case keyCode.DOWN:
++                              toFocus = this.headers[ ( currentIndex + 1 ) % length ];
++                              break;
++                      case keyCode.LEFT:
++                      case keyCode.UP:
++                              toFocus = this.headers[ ( currentIndex - 1 + length ) % length ];
++                              break;
++                      case keyCode.SPACE:
++                      case keyCode.ENTER:
++                              this._eventHandler( event );
++                              break;
++                      case keyCode.HOME:
++                              toFocus = this.headers[ 0 ];
++                              break;
++                      case keyCode.END:
++                              toFocus = this.headers[ length - 1 ];
++                              break;
++              }
++
++              if ( toFocus ) {
++                      $( event.target ).attr( "tabIndex", -1 );
++                      $( toFocus ).attr( "tabIndex", 0 );
++                      toFocus.focus();
++                      event.preventDefault();
++              }
++      },
++
++      _panelKeyDown: function( event ) {
++              if ( event.keyCode === $.ui.keyCode.UP && event.ctrlKey ) {
++                      $( event.currentTarget ).prev().focus();
++              }
++      },
++
++      refresh: function() {
++              var options = this.options;
++              this._processPanels();
++
++              // was collapsed or no panel
++              if ( ( options.active === false && options.collapsible === true ) || !this.headers.length ) {
++                      options.active = false;
++                      this.active = $();
++              // active false only when collapsible is true
++              } else if ( options.active === false ) {
++                      this._activate( 0 );
++              // was active, but active panel is gone
++              } else if ( this.active.length && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
++                      // all remaining panel are disabled
++                      if ( this.headers.length === this.headers.find(".ui-state-disabled").length ) {
++                              options.active = false;
++                              this.active = $();
++                      // activate previous panel
++                      } else {
++                              this._activate( Math.max( 0, options.active - 1 ) );
++                      }
++              // was active, active panel still exists
++              } else {
++                      // make sure active index is correct
++                      options.active = this.headers.index( this.active );
++              }
++
++              this._destroyIcons();
++
++              this._refresh();
++      },
++
++      _processPanels: function() {
++              var prevHeaders = this.headers,
++                      prevPanels = this.panels;
++
++              this.headers = this.element.find( this.options.header )
++                      .addClass( "ui-accordion-header ui-state-default ui-corner-all" );
++
++              this.panels = this.headers.next()
++                      .addClass( "ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom" )
++                      .filter( ":not(.ui-accordion-content-active)" )
++                      .hide();
++
++              // Avoid memory leaks (#10056)
++              if ( prevPanels ) {
++                      this._off( prevHeaders.not( this.headers ) );
++                      this._off( prevPanels.not( this.panels ) );
++              }
++      },
++
++      _refresh: function() {
++              var maxHeight,
++                      options = this.options,
++                      heightStyle = options.heightStyle,
++                      parent = this.element.parent();
++
++              this.active = this._findActive( options.active )
++                      .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" )
++                      .removeClass( "ui-corner-all" );
++              this.active.next()
++                      .addClass( "ui-accordion-content-active" )
++                      .show();
++
++              this.headers
++                      .attr( "role", "tab" )
++                      .each(function() {
++                              var header = $( this ),
++                                      headerId = header.uniqueId().attr( "id" ),
++                                      panel = header.next(),
++                                      panelId = panel.uniqueId().attr( "id" );
++                              header.attr( "aria-controls", panelId );
++                              panel.attr( "aria-labelledby", headerId );
++                      })
++                      .next()
++                              .attr( "role", "tabpanel" );
++
++              this.headers
++                      .not( this.active )
++                      .attr({
++                              "aria-selected": "false",
++                              "aria-expanded": "false",
++                              tabIndex: -1
++                      })
++                      .next()
++                              .attr({
++                                      "aria-hidden": "true"
++                              })
++                              .hide();
++
++              // make sure at least one header is in the tab order
++              if ( !this.active.length ) {
++                      this.headers.eq( 0 ).attr( "tabIndex", 0 );
++              } else {
++                      this.active.attr({
++                              "aria-selected": "true",
++                              "aria-expanded": "true",
++                              tabIndex: 0
++                      })
++                      .next()
++                              .attr({
++                                      "aria-hidden": "false"
++                              });
++              }
++
++              this._createIcons();
++
++              this._setupEvents( options.event );
++
++              if ( heightStyle === "fill" ) {
++                      maxHeight = parent.height();
++                      this.element.siblings( ":visible" ).each(function() {
++                              var elem = $( this ),
++                                      position = elem.css( "position" );
++
++                              if ( position === "absolute" || position === "fixed" ) {
++                                      return;
++                              }
++                              maxHeight -= elem.outerHeight( true );
++                      });
++
++                      this.headers.each(function() {
++                              maxHeight -= $( this ).outerHeight( true );
++                      });
++
++                      this.headers.next()
++                              .each(function() {
++                                      $( this ).height( Math.max( 0, maxHeight -
++                                              $( this ).innerHeight() + $( this ).height() ) );
++                              })
++                              .css( "overflow", "auto" );
++              } else if ( heightStyle === "auto" ) {
++                      maxHeight = 0;
++                      this.headers.next()
++                              .each(function() {
++                                      maxHeight = Math.max( maxHeight, $( this ).css( "height", "" ).height() );
++                              })
++                              .height( maxHeight );
++              }
++      },
++
++      _activate: function( index ) {
++              var active = this._findActive( index )[ 0 ];
++
++              // trying to activate the already active panel
++              if ( active === this.active[ 0 ] ) {
++                      return;
++              }
++
++              // trying to collapse, simulate a click on the currently active header
++              active = active || this.active[ 0 ];
++
++              this._eventHandler({
++                      target: active,
++                      currentTarget: active,
++                      preventDefault: $.noop
++              });
++      },
++
++      _findActive: function( selector ) {
++              return typeof selector === "number" ? this.headers.eq( selector ) : $();
++      },
++
++      _setupEvents: function( event ) {
++              var events = {
++                      keydown: "_keydown"
++              };
++              if ( event ) {
++                      $.each( event.split( " " ), function( index, eventName ) {
++                              events[ eventName ] = "_eventHandler";
++                      });
++              }
++
++              this._off( this.headers.add( this.headers.next() ) );
++              this._on( this.headers, events );
++              this._on( this.headers.next(), { keydown: "_panelKeyDown" });
++              this._hoverable( this.headers );
++              this._focusable( this.headers );
++      },
++
++      _eventHandler: function( event ) {
++              var options = this.options,
++                      active = this.active,
++                      clicked = $( event.currentTarget ),
++                      clickedIsActive = clicked[ 0 ] === active[ 0 ],
++                      collapsing = clickedIsActive && options.collapsible,
++                      toShow = collapsing ? $() : clicked.next(),
++                      toHide = active.next(),
++                      eventData = {
++                              oldHeader: active,
++                              oldPanel: toHide,
++                              newHeader: collapsing ? $() : clicked,
++                              newPanel: toShow
++                      };
++
++              event.preventDefault();
++
++              if (
++                              // click on active header, but not collapsible
++                              ( clickedIsActive && !options.collapsible ) ||
++                              // allow canceling activation
++                              ( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
++                      return;
++              }
++
++              options.active = collapsing ? false : this.headers.index( clicked );
++
++              // when the call to ._toggle() comes after the class changes
++              // it causes a very odd bug in IE 8 (see #6720)
++              this.active = clickedIsActive ? $() : clicked;
++              this._toggle( eventData );
++
++              // switch classes
++              // corner classes on the previously active header stay after the animation
++              active.removeClass( "ui-accordion-header-active ui-state-active" );
++              if ( options.icons ) {
++                      active.children( ".ui-accordion-header-icon" )
++                              .removeClass( options.icons.activeHeader )
++                              .addClass( options.icons.header );
++              }
++
++              if ( !clickedIsActive ) {
++                      clicked
++                              .removeClass( "ui-corner-all" )
++                              .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" );
++                      if ( options.icons ) {
++                              clicked.children( ".ui-accordion-header-icon" )
++                                      .removeClass( options.icons.header )
++                                      .addClass( options.icons.activeHeader );
++                      }
++
++                      clicked
++                              .next()
++                              .addClass( "ui-accordion-content-active" );
++              }
++      },
++
++      _toggle: function( data ) {
++              var toShow = data.newPanel,
++                      toHide = this.prevShow.length ? this.prevShow : data.oldPanel;
++
++              // handle activating a panel during the animation for another activation
++              this.prevShow.add( this.prevHide ).stop( true, true );
++              this.prevShow = toShow;
++              this.prevHide = toHide;
++
++              if ( this.options.animate ) {
++                      this._animate( toShow, toHide, data );
++              } else {
++                      toHide.hide();
++                      toShow.show();
++                      this._toggleComplete( data );
++              }
++
++              toHide.attr({
++                      "aria-hidden": "true"
++              });
++              toHide.prev().attr({
++                      "aria-selected": "false",
++                      "aria-expanded": "false"
++              });
++              // if we're switching panels, remove the old header from the tab order
++              // if we're opening from collapsed state, remove the previous header from the tab order
++              // if we're collapsing, then keep the collapsing header in the tab order
++              if ( toShow.length && toHide.length ) {
++                      toHide.prev().attr({
++                              "tabIndex": -1,
++                              "aria-expanded": "false"
++                      });
++              } else if ( toShow.length ) {
++                      this.headers.filter(function() {
++                              return parseInt( $( this ).attr( "tabIndex" ), 10 ) === 0;
++                      })
++                      .attr( "tabIndex", -1 );
++              }
++
++              toShow
++                      .attr( "aria-hidden", "false" )
++                      .prev()
++                              .attr({
++                                      "aria-selected": "true",
++                                      "aria-expanded": "true",
++                                      tabIndex: 0
++                              });
++      },
++
++      _animate: function( toShow, toHide, data ) {
++              var total, easing, duration,
++                      that = this,
++                      adjust = 0,
++                      boxSizing = toShow.css( "box-sizing" ),
++                      down = toShow.length &&
++                              ( !toHide.length || ( toShow.index() < toHide.index() ) ),
++                      animate = this.options.animate || {},
++                      options = down && animate.down || animate,
++                      complete = function() {
++                              that._toggleComplete( data );
++                      };
++
++              if ( typeof options === "number" ) {
++                      duration = options;
++              }
++              if ( typeof options === "string" ) {
++                      easing = options;
++              }
++              // fall back from options to animation in case of partial down settings
++              easing = easing || options.easing || animate.easing;
++              duration = duration || options.duration || animate.duration;
++
++              if ( !toHide.length ) {
++                      return toShow.animate( this.showProps, duration, easing, complete );
++              }
++              if ( !toShow.length ) {
++                      return toHide.animate( this.hideProps, duration, easing, complete );
++              }
++
++              total = toShow.show().outerHeight();
++              toHide.animate( this.hideProps, {
++                      duration: duration,
++                      easing: easing,
++                      step: function( now, fx ) {
++                              fx.now = Math.round( now );
++                      }
++              });
++              toShow
++                      .hide()
++                      .animate( this.showProps, {
++                              duration: duration,
++                              easing: easing,
++                              complete: complete,
++                              step: function( now, fx ) {
++                                      fx.now = Math.round( now );
++                                      if ( fx.prop !== "height" ) {
++                                              if ( boxSizing === "content-box" ) {
++                                                      adjust += fx.now;
++                                              }
++                                      } else if ( that.options.heightStyle !== "content" ) {
++                                              fx.now = Math.round( total - toHide.outerHeight() - adjust );
++                                              adjust = 0;
++                                      }
++                              }
++                      });
++      },
++
++      _toggleComplete: function( data ) {
++              var toHide = data.oldPanel;
++
++              toHide
++                      .removeClass( "ui-accordion-content-active" )
++                      .prev()
++                              .removeClass( "ui-corner-top" )
++                              .addClass( "ui-corner-all" );
++
++              // Work around for rendering bug in IE (#5421)
++              if ( toHide.length ) {
++                      toHide.parent()[ 0 ].className = toHide.parent()[ 0 ].className;
++              }
++              this._trigger( "activate", null, data );
++      }
++});
++
++
++/*!
++ * jQuery UI Menu 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/menu/
++ */
++
++
++var menu = $.widget( "ui.menu", {
++      version: "1.11.4",
++      defaultElement: "<ul>",
++      delay: 300,
++      options: {
++              icons: {
++                      submenu: "ui-icon-carat-1-e"
++              },
++              items: "> *",
++              menus: "ul",
++              position: {
++                      my: "left-1 top",
++                      at: "right top"
++              },
++              role: "menu",
++
++              // callbacks
++              blur: null,
++              focus: null,
++              select: null
++      },
++
++      _create: function() {
++              this.activeMenu = this.element;
++
++              // Flag used to prevent firing of the click handler
++              // as the event bubbles up through nested menus
++              this.mouseHandled = false;
++              this.element
++                      .uniqueId()
++                      .addClass( "ui-menu ui-widget ui-widget-content" )
++                      .toggleClass( "ui-menu-icons", !!this.element.find( ".ui-icon" ).length )
++                      .attr({
++                              role: this.options.role,
++                              tabIndex: 0
++                      });
++
++              if ( this.options.disabled ) {
++                      this.element
++                              .addClass( "ui-state-disabled" )
++                              .attr( "aria-disabled", "true" );
++              }
++
++              this._on({
++                      // Prevent focus from sticking to links inside menu after clicking
++                      // them (focus should always stay on UL during navigation).
++                      "mousedown .ui-menu-item": function( event ) {
++                              event.preventDefault();
++                      },
++                      "click .ui-menu-item": function( event ) {
++                              var target = $( event.target );
++                              if ( !this.mouseHandled && target.not( ".ui-state-disabled" ).length ) {
++                                      this.select( event );
++
++                                      // Only set the mouseHandled flag if the event will bubble, see #9469.
++                                      if ( !event.isPropagationStopped() ) {
++                                              this.mouseHandled = true;
++                                      }
++
++                                      // Open submenu on click
++                                      if ( target.has( ".ui-menu" ).length ) {
++                                              this.expand( event );
++                                      } else if ( !this.element.is( ":focus" ) && $( this.document[ 0 ].activeElement ).closest( ".ui-menu" ).length ) {
++
++                                              // Redirect focus to the menu
++                                              this.element.trigger( "focus", [ true ] );
++
++                                              // If the active item is on the top level, let it stay active.
++                                              // Otherwise, blur the active item since it is no longer visible.
++                                              if ( this.active && this.active.parents( ".ui-menu" ).length === 1 ) {
++                                                      clearTimeout( this.timer );
++                                              }
++                                      }
++                              }
++                      },
++                      "mouseenter .ui-menu-item": function( event ) {
++                              // Ignore mouse events while typeahead is active, see #10458.
++                              // Prevents focusing the wrong item when typeahead causes a scroll while the mouse
++                              // is over an item in the menu
++                              if ( this.previousFilter ) {
++                                      return;
++                              }
++                              var target = $( event.currentTarget );
++                              // Remove ui-state-active class from siblings of the newly focused menu item
++                              // to avoid a jump caused by adjacent elements both having a class with a border
++                              target.siblings( ".ui-state-active" ).removeClass( "ui-state-active" );
++                              this.focus( event, target );
++                      },
++                      mouseleave: "collapseAll",
++                      "mouseleave .ui-menu": "collapseAll",
++                      focus: function( event, keepActiveItem ) {
++                              // If there's already an active item, keep it active
++                              // If not, activate the first item
++                              var item = this.active || this.element.find( this.options.items ).eq( 0 );
++
++                              if ( !keepActiveItem ) {
++                                      this.focus( event, item );
++                              }
++                      },
++                      blur: function( event ) {
++                              this._delay(function() {
++                                      if ( !$.contains( this.element[0], this.document[0].activeElement ) ) {
++                                              this.collapseAll( event );
++                                      }
++                              });
++                      },
++                      keydown: "_keydown"
++              });
++
++              this.refresh();
++
++              // Clicks outside of a menu collapse any open menus
++              this._on( this.document, {
++                      click: function( event ) {
++                              if ( this._closeOnDocumentClick( event ) ) {
++                                      this.collapseAll( event );
++                              }
++
++                              // Reset the mouseHandled flag
++                              this.mouseHandled = false;
++                      }
++              });
++      },
++
++      _destroy: function() {
++              // Destroy (sub)menus
++              this.element
++                      .removeAttr( "aria-activedescendant" )
++                      .find( ".ui-menu" ).addBack()
++                              .removeClass( "ui-menu ui-widget ui-widget-content ui-menu-icons ui-front" )
++                              .removeAttr( "role" )
++                              .removeAttr( "tabIndex" )
++                              .removeAttr( "aria-labelledby" )
++                              .removeAttr( "aria-expanded" )
++                              .removeAttr( "aria-hidden" )
++                              .removeAttr( "aria-disabled" )
++                              .removeUniqueId()
++                              .show();
++
++              // Destroy menu items
++              this.element.find( ".ui-menu-item" )
++                      .removeClass( "ui-menu-item" )
++                      .removeAttr( "role" )
++                      .removeAttr( "aria-disabled" )
++                      .removeUniqueId()
++                      .removeClass( "ui-state-hover" )
++                      .removeAttr( "tabIndex" )
++                      .removeAttr( "role" )
++                      .removeAttr( "aria-haspopup" )
++                      .children().each( function() {
++                              var elem = $( this );
++                              if ( elem.data( "ui-menu-submenu-carat" ) ) {
++                                      elem.remove();
++                              }
++                      });
++
++              // Destroy menu dividers
++              this.element.find( ".ui-menu-divider" ).removeClass( "ui-menu-divider ui-widget-content" );
++      },
++
++      _keydown: function( event ) {
++              var match, prev, character, skip,
++                      preventDefault = true;
++
++              switch ( event.keyCode ) {
++              case $.ui.keyCode.PAGE_UP:
++                      this.previousPage( event );
++                      break;
++              case $.ui.keyCode.PAGE_DOWN:
++                      this.nextPage( event );
++                      break;
++              case $.ui.keyCode.HOME:
++                      this._move( "first", "first", event );
++                      break;
++              case $.ui.keyCode.END:
++                      this._move( "last", "last", event );
++                      break;
++              case $.ui.keyCode.UP:
++                      this.previous( event );
++                      break;
++              case $.ui.keyCode.DOWN:
++                      this.next( event );
++                      break;
++              case $.ui.keyCode.LEFT:
++                      this.collapse( event );
++                      break;
++              case $.ui.keyCode.RIGHT:
++                      if ( this.active && !this.active.is( ".ui-state-disabled" ) ) {
++                              this.expand( event );
++                      }
++                      break;
++              case $.ui.keyCode.ENTER:
++              case $.ui.keyCode.SPACE:
++                      this._activate( event );
++                      break;
++              case $.ui.keyCode.ESCAPE:
++                      this.collapse( event );
++                      break;
++              default:
++                      preventDefault = false;
++                      prev = this.previousFilter || "";
++                      character = String.fromCharCode( event.keyCode );
++                      skip = false;
++
++                      clearTimeout( this.filterTimer );
++
++                      if ( character === prev ) {
++                              skip = true;
++                      } else {
++                              character = prev + character;
++                      }
++
++                      match = this._filterMenuItems( character );
++                      match = skip && match.index( this.active.next() ) !== -1 ?
++                              this.active.nextAll( ".ui-menu-item" ) :
++                              match;
++
++                      // If no matches on the current filter, reset to the last character pressed
++                      // to move down the menu to the first item that starts with that character
++                      if ( !match.length ) {
++                              character = String.fromCharCode( event.keyCode );
++                              match = this._filterMenuItems( character );
++                      }
++
++                      if ( match.length ) {
++                              this.focus( event, match );
++                              this.previousFilter = character;
++                              this.filterTimer = this._delay(function() {
++                                      delete this.previousFilter;
++                              }, 1000 );
++                      } else {
++                              delete this.previousFilter;
++                      }
++              }
++
++              if ( preventDefault ) {
++                      event.preventDefault();
++              }
++      },
++
++      _activate: function( event ) {
++              if ( !this.active.is( ".ui-state-disabled" ) ) {
++                      if ( this.active.is( "[aria-haspopup='true']" ) ) {
++                              this.expand( event );
++                      } else {
++                              this.select( event );
++                      }
++              }
++      },
++
++      refresh: function() {
++              var menus, items,
++                      that = this,
++                      icon = this.options.icons.submenu,
++                      submenus = this.element.find( this.options.menus );
++
++              this.element.toggleClass( "ui-menu-icons", !!this.element.find( ".ui-icon" ).length );
++
++              // Initialize nested menus
++              submenus.filter( ":not(.ui-menu)" )
++                      .addClass( "ui-menu ui-widget ui-widget-content ui-front" )
++                      .hide()
++                      .attr({
++                              role: this.options.role,
++                              "aria-hidden": "true",
++                              "aria-expanded": "false"
++                      })
++                      .each(function() {
++                              var menu = $( this ),
++                                      item = menu.parent(),
++                                      submenuCarat = $( "<span>" )
++                                              .addClass( "ui-menu-icon ui-icon " + icon )
++                                              .data( "ui-menu-submenu-carat", true );
++
++                              item
++                                      .attr( "aria-haspopup", "true" )
++                                      .prepend( submenuCarat );
++                              menu.attr( "aria-labelledby", item.attr( "id" ) );
++                      });
++
++              menus = submenus.add( this.element );
++              items = menus.find( this.options.items );
++
++              // Initialize menu-items containing spaces and/or dashes only as dividers
++              items.not( ".ui-menu-item" ).each(function() {
++                      var item = $( this );
++                      if ( that._isDivider( item ) ) {
++                              item.addClass( "ui-widget-content ui-menu-divider" );
++                      }
++              });
++
++              // Don't refresh list items that are already adapted
++              items.not( ".ui-menu-item, .ui-menu-divider" )
++                      .addClass( "ui-menu-item" )
++                      .uniqueId()
++                      .attr({
++                              tabIndex: -1,
++                              role: this._itemRole()
++                      });
++
++              // Add aria-disabled attribute to any disabled menu item
++              items.filter( ".ui-state-disabled" ).attr( "aria-disabled", "true" );
++
++              // If the active item has been removed, blur the menu
++              if ( this.active && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
++                      this.blur();
++              }
++      },
++
++      _itemRole: function() {
++              return {
++                      menu: "menuitem",
++                      listbox: "option"
++              }[ this.options.role ];
++      },
++
++      _setOption: function( key, value ) {
++              if ( key === "icons" ) {
++                      this.element.find( ".ui-menu-icon" )
++                              .removeClass( this.options.icons.submenu )
++                              .addClass( value.submenu );
++              }
++              if ( key === "disabled" ) {
++                      this.element
++                              .toggleClass( "ui-state-disabled", !!value )
++                              .attr( "aria-disabled", value );
++              }
++              this._super( key, value );
++      },
++
++      focus: function( event, item ) {
++              var nested, focused;
++              this.blur( event, event && event.type === "focus" );
++
++              this._scrollIntoView( item );
++
++              this.active = item.first();
++              focused = this.active.addClass( "ui-state-focus" ).removeClass( "ui-state-active" );
++              // Only update aria-activedescendant if there's a role
++              // otherwise we assume focus is managed elsewhere
++              if ( this.options.role ) {
++                      this.element.attr( "aria-activedescendant", focused.attr( "id" ) );
++              }
++
++              // Highlight active parent menu item, if any
++              this.active
++                      .parent()
++                      .closest( ".ui-menu-item" )
++                      .addClass( "ui-state-active" );
++
++              if ( event && event.type === "keydown" ) {
++                      this._close();
++              } else {
++                      this.timer = this._delay(function() {
++                              this._close();
++                      }, this.delay );
++              }
++
++              nested = item.children( ".ui-menu" );
++              if ( nested.length && event && ( /^mouse/.test( event.type ) ) ) {
++                      this._startOpening(nested);
++              }
++              this.activeMenu = item.parent();
++
++              this._trigger( "focus", event, { item: item } );
++      },
++
++      _scrollIntoView: function( item ) {
++              var borderTop, paddingTop, offset, scroll, elementHeight, itemHeight;
++              if ( this._hasScroll() ) {
++                      borderTop = parseFloat( $.css( this.activeMenu[0], "borderTopWidth" ) ) || 0;
++                      paddingTop = parseFloat( $.css( this.activeMenu[0], "paddingTop" ) ) || 0;
++                      offset = item.offset().top - this.activeMenu.offset().top - borderTop - paddingTop;
++                      scroll = this.activeMenu.scrollTop();
++                      elementHeight = this.activeMenu.height();
++                      itemHeight = item.outerHeight();
++
++                      if ( offset < 0 ) {
++                              this.activeMenu.scrollTop( scroll + offset );
++                      } else if ( offset + itemHeight > elementHeight ) {
++                              this.activeMenu.scrollTop( scroll + offset - elementHeight + itemHeight );
++                      }
++              }
++      },
++
++      blur: function( event, fromFocus ) {
++              if ( !fromFocus ) {
++                      clearTimeout( this.timer );
++              }
++
++              if ( !this.active ) {
++                      return;
++              }
++
++              this.active.removeClass( "ui-state-focus" );
++              this.active = null;
++
++              this._trigger( "blur", event, { item: this.active } );
++      },
++
++      _startOpening: function( submenu ) {
++              clearTimeout( this.timer );
++
++              // Don't open if already open fixes a Firefox bug that caused a .5 pixel
++              // shift in the submenu position when mousing over the carat icon
++              if ( submenu.attr( "aria-hidden" ) !== "true" ) {
++                      return;
++              }
++
++              this.timer = this._delay(function() {
++                      this._close();
++                      this._open( submenu );
++              }, this.delay );
++      },
++
++      _open: function( submenu ) {
++              var position = $.extend({
++                      of: this.active
++              }, this.options.position );
++
++              clearTimeout( this.timer );
++              this.element.find( ".ui-menu" ).not( submenu.parents( ".ui-menu" ) )
++                      .hide()
++                      .attr( "aria-hidden", "true" );
++
++              submenu
++                      .show()
++                      .removeAttr( "aria-hidden" )
++                      .attr( "aria-expanded", "true" )
++                      .position( position );
++      },
++
++      collapseAll: function( event, all ) {
++              clearTimeout( this.timer );
++              this.timer = this._delay(function() {
++                      // If we were passed an event, look for the submenu that contains the event
++                      var currentMenu = all ? this.element :
++                              $( event && event.target ).closest( this.element.find( ".ui-menu" ) );
++
++                      // If we found no valid submenu ancestor, use the main menu to close all sub menus anyway
++                      if ( !currentMenu.length ) {
++                              currentMenu = this.element;
++                      }
++
++                      this._close( currentMenu );
++
++                      this.blur( event );
++                      this.activeMenu = currentMenu;
++              }, this.delay );
++      },
++
++      // With no arguments, closes the currently active menu - if nothing is active
++      // it closes all menus.  If passed an argument, it will search for menus BELOW
++      _close: function( startMenu ) {
++              if ( !startMenu ) {
++                      startMenu = this.active ? this.active.parent() : this.element;
++              }
++
++              startMenu
++                      .find( ".ui-menu" )
++                              .hide()
++                              .attr( "aria-hidden", "true" )
++                              .attr( "aria-expanded", "false" )
++                      .end()
++                      .find( ".ui-state-active" ).not( ".ui-state-focus" )
++                              .removeClass( "ui-state-active" );
++      },
++
++      _closeOnDocumentClick: function( event ) {
++              return !$( event.target ).closest( ".ui-menu" ).length;
++      },
++
++      _isDivider: function( item ) {
++
++              // Match hyphen, em dash, en dash
++              return !/[^\-\u2014\u2013\s]/.test( item.text() );
++      },
++
++      collapse: function( event ) {
++              var newItem = this.active &&
++                      this.active.parent().closest( ".ui-menu-item", this.element );
++              if ( newItem && newItem.length ) {
++                      this._close();
++                      this.focus( event, newItem );
++              }
++      },
++
++      expand: function( event ) {
++              var newItem = this.active &&
++                      this.active
++                              .children( ".ui-menu " )
++                              .find( this.options.items )
++                              .first();
++
++              if ( newItem && newItem.length ) {
++                      this._open( newItem.parent() );
++
++                      // Delay so Firefox will not hide activedescendant change in expanding submenu from AT
++                      this._delay(function() {
++                              this.focus( event, newItem );
++                      });
++              }
++      },
++
++      next: function( event ) {
++              this._move( "next", "first", event );
++      },
++
++      previous: function( event ) {
++              this._move( "prev", "last", event );
++      },
++
++      isFirstItem: function() {
++              return this.active && !this.active.prevAll( ".ui-menu-item" ).length;
++      },
++
++      isLastItem: function() {
++              return this.active && !this.active.nextAll( ".ui-menu-item" ).length;
++      },
++
++      _move: function( direction, filter, event ) {
++              var next;
++              if ( this.active ) {
++                      if ( direction === "first" || direction === "last" ) {
++                              next = this.active
++                                      [ direction === "first" ? "prevAll" : "nextAll" ]( ".ui-menu-item" )
++                                      .eq( -1 );
++                      } else {
++                              next = this.active
++                                      [ direction + "All" ]( ".ui-menu-item" )
++                                      .eq( 0 );
++                      }
++              }
++              if ( !next || !next.length || !this.active ) {
++                      next = this.activeMenu.find( this.options.items )[ filter ]();
++              }
++
++              this.focus( event, next );
++      },
++
++      nextPage: function( event ) {
++              var item, base, height;
++
++              if ( !this.active ) {
++                      this.next( event );
++                      return;
++              }
++              if ( this.isLastItem() ) {
++                      return;
++              }
++              if ( this._hasScroll() ) {
++                      base = this.active.offset().top;
++                      height = this.element.height();
++                      this.active.nextAll( ".ui-menu-item" ).each(function() {
++                              item = $( this );
++                              return item.offset().top - base - height < 0;
++                      });
++
++                      this.focus( event, item );
++              } else {
++                      this.focus( event, this.activeMenu.find( this.options.items )
++                              [ !this.active ? "first" : "last" ]() );
++              }
++      },
++
++      previousPage: function( event ) {
++              var item, base, height;
++              if ( !this.active ) {
++                      this.next( event );
++                      return;
++              }
++              if ( this.isFirstItem() ) {
++                      return;
++              }
++              if ( this._hasScroll() ) {
++                      base = this.active.offset().top;
++                      height = this.element.height();
++                      this.active.prevAll( ".ui-menu-item" ).each(function() {
++                              item = $( this );
++                              return item.offset().top - base + height > 0;
++                      });
++
++                      this.focus( event, item );
++              } else {
++                      this.focus( event, this.activeMenu.find( this.options.items ).first() );
++              }
++      },
++
++      _hasScroll: function() {
++              return this.element.outerHeight() < this.element.prop( "scrollHeight" );
++      },
++
++      select: function( event ) {
++              // TODO: It should never be possible to not have an active item at this
++              // point, but the tests don't trigger mouseenter before click.
++              this.active = this.active || $( event.target ).closest( ".ui-menu-item" );
++              var ui = { item: this.active };
++              if ( !this.active.has( ".ui-menu" ).length ) {
++                      this.collapseAll( event, true );
++              }
++              this._trigger( "select", event, ui );
++      },
++
++      _filterMenuItems: function(character) {
++              var escapedCharacter = character.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" ),
++                      regex = new RegExp( "^" + escapedCharacter, "i" );
++
++              return this.activeMenu
++                      .find( this.options.items )
++
++                      // Only match on items, not dividers or other content (#10571)
++                      .filter( ".ui-menu-item" )
++                      .filter(function() {
++                              return regex.test( $.trim( $( this ).text() ) );
++                      });
++      }
++});
++
++
++/*!
++ * jQuery UI Autocomplete 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/autocomplete/
++ */
++
++
++$.widget( "ui.autocomplete", {
++      version: "1.11.4",
++      defaultElement: "<input>",
++      options: {
++              appendTo: null,
++              autoFocus: false,
++              delay: 300,
++              minLength: 1,
++              position: {
++                      my: "left top",
++                      at: "left bottom",
++                      collision: "none"
++              },
++              source: null,
++
++              // callbacks
++              change: null,
++              close: null,
++              focus: null,
++              open: null,
++              response: null,
++              search: null,
++              select: null
++      },
++
++      requestIndex: 0,
++      pending: 0,
++
++      _create: function() {
++              // Some browsers only repeat keydown events, not keypress events,
++              // so we use the suppressKeyPress flag to determine if we've already
++              // handled the keydown event. #7269
++              // Unfortunately the code for & in keypress is the same as the up arrow,
++              // so we use the suppressKeyPressRepeat flag to avoid handling keypress
++              // events when we know the keydown event was used to modify the
++              // search term. #7799
++              var suppressKeyPress, suppressKeyPressRepeat, suppressInput,
++                      nodeName = this.element[ 0 ].nodeName.toLowerCase(),
++                      isTextarea = nodeName === "textarea",
++                      isInput = nodeName === "input";
++
++              this.isMultiLine =
++                      // Textareas are always multi-line
++                      isTextarea ? true :
++                      // Inputs are always single-line, even if inside a contentEditable element
++                      // IE also treats inputs as contentEditable
++                      isInput ? false :
++                      // All other element types are determined by whether or not they're contentEditable
++                      this.element.prop( "isContentEditable" );
++
++              this.valueMethod = this.element[ isTextarea || isInput ? "val" : "text" ];
++              this.isNewMenu = true;
++
++              this.element
++                      .addClass( "ui-autocomplete-input" )
++                      .attr( "autocomplete", "off" );
++
++              this._on( this.element, {
++                      keydown: function( event ) {
++                              if ( this.element.prop( "readOnly" ) ) {
++                                      suppressKeyPress = true;
++                                      suppressInput = true;
++                                      suppressKeyPressRepeat = true;
++                                      return;
++                              }
++
++                              suppressKeyPress = false;
++                              suppressInput = false;
++                              suppressKeyPressRepeat = false;
++                              var keyCode = $.ui.keyCode;
++                              switch ( event.keyCode ) {
++                              case keyCode.PAGE_UP:
++                                      suppressKeyPress = true;
++                                      this._move( "previousPage", event );
++                                      break;
++                              case keyCode.PAGE_DOWN:
++                                      suppressKeyPress = true;
++                                      this._move( "nextPage", event );
++                                      break;
++                              case keyCode.UP:
++                                      suppressKeyPress = true;
++                                      this._keyEvent( "previous", event );
++                                      break;
++                              case keyCode.DOWN:
++                                      suppressKeyPress = true;
++                                      this._keyEvent( "next", event );
++                                      break;
++                              case keyCode.ENTER:
++                                      // when menu is open and has focus
++                                      if ( this.menu.active ) {
++                                              // #6055 - Opera still allows the keypress to occur
++                                              // which causes forms to submit
++                                              suppressKeyPress = true;
++                                              event.preventDefault();
++                                              this.menu.select( event );
++                                      }
++                                      break;
++                              case keyCode.TAB:
++                                      if ( this.menu.active ) {
++                                              this.menu.select( event );
++                                      }
++                                      break;
++                              case keyCode.ESCAPE:
++                                      if ( this.menu.element.is( ":visible" ) ) {
++                                              if ( !this.isMultiLine ) {
++                                                      this._value( this.term );
++                                              }
++                                              this.close( event );
++                                              // Different browsers have different default behavior for escape
++                                              // Single press can mean undo or clear
++                                              // Double press in IE means clear the whole form
++                                              event.preventDefault();
++                                      }
++                                      break;
++                              default:
++                                      suppressKeyPressRepeat = true;
++                                      // search timeout should be triggered before the input value is changed
++                                      this._searchTimeout( event );
++                                      break;
++                              }
++                      },
++                      keypress: function( event ) {
++                              if ( suppressKeyPress ) {
++                                      suppressKeyPress = false;
++                                      if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
++                                              event.preventDefault();
++                                      }
++                                      return;
++                              }
++                              if ( suppressKeyPressRepeat ) {
++                                      return;
++                              }
++
++                              // replicate some key handlers to allow them to repeat in Firefox and Opera
++                              var keyCode = $.ui.keyCode;
++                              switch ( event.keyCode ) {
++                              case keyCode.PAGE_UP:
++                                      this._move( "previousPage", event );
++                                      break;
++                              case keyCode.PAGE_DOWN:
++                                      this._move( "nextPage", event );
++                                      break;
++                              case keyCode.UP:
++                                      this._keyEvent( "previous", event );
++                                      break;
++                              case keyCode.DOWN:
++                                      this._keyEvent( "next", event );
++                                      break;
++                              }
++                      },
++                      input: function( event ) {
++                              if ( suppressInput ) {
++                                      suppressInput = false;
++                                      event.preventDefault();
++                                      return;
++                              }
++                              this._searchTimeout( event );
++                      },
++                      focus: function() {
++                              this.selectedItem = null;
++                              this.previous = this._value();
++                      },
++                      blur: function( event ) {
++                              if ( this.cancelBlur ) {
++                                      delete this.cancelBlur;
++                                      return;
++                              }
++
++                              clearTimeout( this.searching );
++                              this.close( event );
++                              this._change( event );
++                      }
++              });
++
++              this._initSource();
++              this.menu = $( "<ul>" )
++                      .addClass( "ui-autocomplete ui-front" )
++                      .appendTo( this._appendTo() )
++                      .menu({
++                              // disable ARIA support, the live region takes care of that
++                              role: null
++                      })
++                      .hide()
++                      .menu( "instance" );
++
++              this._on( this.menu.element, {
++                      mousedown: function( event ) {
++                              // prevent moving focus out of the text field
++                              event.preventDefault();
++
++                              // IE doesn't prevent moving focus even with event.preventDefault()
++                              // so we set a flag to know when we should ignore the blur event
++                              this.cancelBlur = true;
++                              this._delay(function() {
++                                      delete this.cancelBlur;
++                              });
++
++                              // clicking on the scrollbar causes focus to shift to the body
++                              // but we can't detect a mouseup or a click immediately afterward
++                              // so we have to track the next mousedown and close the menu if
++                              // the user clicks somewhere outside of the autocomplete
++                              var menuElement = this.menu.element[ 0 ];
++                              if ( !$( event.target ).closest( ".ui-menu-item" ).length ) {
++                                      this._delay(function() {
++                                              var that = this;
++                                              this.document.one( "mousedown", function( event ) {
++                                                      if ( event.target !== that.element[ 0 ] &&
++                                                                      event.target !== menuElement &&
++                                                                      !$.contains( menuElement, event.target ) ) {
++                                                              that.close();
++                                                      }
++                                              });
++                                      });
++                              }
++                      },
++                      menufocus: function( event, ui ) {
++                              var label, item;
++                              // support: Firefox
++                              // Prevent accidental activation of menu items in Firefox (#7024 #9118)
++                              if ( this.isNewMenu ) {
++                                      this.isNewMenu = false;
++                                      if ( event.originalEvent && /^mouse/.test( event.originalEvent.type ) ) {
++                                              this.menu.blur();
++
++                                              this.document.one( "mousemove", function() {
++                                                      $( event.target ).trigger( event.originalEvent );
++                                              });
++
++                                              return;
++                                      }
++                              }
++
++                              item = ui.item.data( "ui-autocomplete-item" );
++                              if ( false !== this._trigger( "focus", event, { item: item } ) ) {
++                                      // use value to match what will end up in the input, if it was a key event
++                                      if ( event.originalEvent && /^key/.test( event.originalEvent.type ) ) {
++                                              this._value( item.value );
++                                      }
++                              }
++
++                              // Announce the value in the liveRegion
++                              label = ui.item.attr( "aria-label" ) || item.value;
++                              if ( label && $.trim( label ).length ) {
++                                      this.liveRegion.children().hide();
++                                      $( "<div>" ).text( label ).appendTo( this.liveRegion );
++                              }
++                      },
++                      menuselect: function( event, ui ) {
++                              var item = ui.item.data( "ui-autocomplete-item" ),
++                                      previous = this.previous;
++
++                              // only trigger when focus was lost (click on menu)
++                              if ( this.element[ 0 ] !== this.document[ 0 ].activeElement ) {
++                                      this.element.focus();
++                                      this.previous = previous;
++                                      // #6109 - IE triggers two focus events and the second
++                                      // is asynchronous, so we need to reset the previous
++                                      // term synchronously and asynchronously :-(
++                                      this._delay(function() {
++                                              this.previous = previous;
++                                              this.selectedItem = item;
++                                      });
++                              }
++
++                              if ( false !== this._trigger( "select", event, { item: item } ) ) {
++                                      this._value( item.value );
++                              }
++                              // reset the term after the select event
++                              // this allows custom select handling to work properly
++                              this.term = this._value();
++
++                              this.close( event );
++                              this.selectedItem = item;
++                      }
++              });
++
++              this.liveRegion = $( "<span>", {
++                              role: "status",
++                              "aria-live": "assertive",
++                              "aria-relevant": "additions"
++                      })
++                      .addClass( "ui-helper-hidden-accessible" )
++                      .appendTo( this.document[ 0 ].body );
++
++              // turning off autocomplete prevents the browser from remembering the
++              // value when navigating through history, so we re-enable autocomplete
++              // if the page is unloaded before the widget is destroyed. #7790
++              this._on( this.window, {
++                      beforeunload: function() {
++                              this.element.removeAttr( "autocomplete" );
++                      }
++              });
++      },
++
++      _destroy: function() {
++              clearTimeout( this.searching );
++              this.element
++                      .removeClass( "ui-autocomplete-input" )
++                      .removeAttr( "autocomplete" );
++              this.menu.element.remove();
++              this.liveRegion.remove();
++      },
++
++      _setOption: function( key, value ) {
++              this._super( key, value );
++              if ( key === "source" ) {
++                      this._initSource();
++              }
++              if ( key === "appendTo" ) {
++                      this.menu.element.appendTo( this._appendTo() );
++              }
++              if ( key === "disabled" && value && this.xhr ) {
++                      this.xhr.abort();
++              }
++      },
++
++      _appendTo: function() {
++              var element = this.options.appendTo;
++
++              if ( element ) {
++                      element = element.jquery || element.nodeType ?
++                              $( element ) :
++                              this.document.find( element ).eq( 0 );
++              }
++
++              if ( !element || !element[ 0 ] ) {
++                      element = this.element.closest( ".ui-front" );
++              }
++
++              if ( !element.length ) {
++                      element = this.document[ 0 ].body;
++              }
++
++              return element;
++      },
++
++      _initSource: function() {
++              var array, url,
++                      that = this;
++              if ( $.isArray( this.options.source ) ) {
++                      array = this.options.source;
++                      this.source = function( request, response ) {
++                              response( $.ui.autocomplete.filter( array, request.term ) );
++                      };
++              } else if ( typeof this.options.source === "string" ) {
++                      url = this.options.source;
++                      this.source = function( request, response ) {
++                              if ( that.xhr ) {
++                                      that.xhr.abort();
++                              }
++                              that.xhr = $.ajax({
++                                      url: url,
++                                      data: request,
++                                      dataType: "json",
++                                      success: function( data ) {
++                                              response( data );
++                                      },
++                                      error: function() {
++                                              response([]);
++                                      }
++                              });
++                      };
++              } else {
++                      this.source = this.options.source;
++              }
++      },
++
++      _searchTimeout: function( event ) {
++              clearTimeout( this.searching );
++              this.searching = this._delay(function() {
++
++                      // Search if the value has changed, or if the user retypes the same value (see #7434)
++                      var equalValues = this.term === this._value(),
++                              menuVisible = this.menu.element.is( ":visible" ),
++                              modifierKey = event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
++
++                      if ( !equalValues || ( equalValues && !menuVisible && !modifierKey ) ) {
++                              this.selectedItem = null;
++                              this.search( null, event );
++                      }
++              }, this.options.delay );
++      },
++
++      search: function( value, event ) {
++              value = value != null ? value : this._value();
++
++              // always save the actual value, not the one passed as an argument
++              this.term = this._value();
++
++              if ( value.length < this.options.minLength ) {
++                      return this.close( event );
++              }
++
++              if ( this._trigger( "search", event ) === false ) {
++                      return;
++              }
++
++              return this._search( value );
++      },
++
++      _search: function( value ) {
++              this.pending++;
++              this.element.addClass( "ui-autocomplete-loading" );
++              this.cancelSearch = false;
++
++              this.source( { term: value }, this._response() );
++      },
++
++      _response: function() {
++              var index = ++this.requestIndex;
++
++              return $.proxy(function( content ) {
++                      if ( index === this.requestIndex ) {
++                              this.__response( content );
++                      }
++
++                      this.pending--;
++                      if ( !this.pending ) {
++                              this.element.removeClass( "ui-autocomplete-loading" );
++                      }
++              }, this );
++      },
++
++      __response: function( content ) {
++              if ( content ) {
++                      content = this._normalize( content );
++              }
++              this._trigger( "response", null, { content: content } );
++              if ( !this.options.disabled && content && content.length && !this.cancelSearch ) {
++                      this._suggest( content );
++                      this._trigger( "open" );
++              } else {
++                      // use ._close() instead of .close() so we don't cancel future searches
++                      this._close();
++              }
++      },
++
++      close: function( event ) {
++              this.cancelSearch = true;
++              this._close( event );
++      },
++
++      _close: function( event ) {
++              if ( this.menu.element.is( ":visible" ) ) {
++                      this.menu.element.hide();
++                      this.menu.blur();
++                      this.isNewMenu = true;
++                      this._trigger( "close", event );
++              }
++      },
++
++      _change: function( event ) {
++              if ( this.previous !== this._value() ) {
++                      this._trigger( "change", event, { item: this.selectedItem } );
++              }
++      },
++
++      _normalize: function( items ) {
++              // assume all items have the right format when the first item is complete
++              if ( items.length && items[ 0 ].label && items[ 0 ].value ) {
++                      return items;
++              }
++              return $.map( items, function( item ) {
++                      if ( typeof item === "string" ) {
++                              return {
++                                      label: item,
++                                      value: item
++                              };
++                      }
++                      return $.extend( {}, item, {
++                              label: item.label || item.value,
++                              value: item.value || item.label
++                      });
++              });
++      },
++
++      _suggest: function( items ) {
++              var ul = this.menu.element.empty();
++              this._renderMenu( ul, items );
++              this.isNewMenu = true;
++              this.menu.refresh();
++
++              // size and position menu
++              ul.show();
++              this._resizeMenu();
++              ul.position( $.extend({
++                      of: this.element
++              }, this.options.position ) );
++
++              if ( this.options.autoFocus ) {
++                      this.menu.next();
++              }
++      },
++
++      _resizeMenu: function() {
++              var ul = this.menu.element;
++              ul.outerWidth( Math.max(
++                      // Firefox wraps long text (possibly a rounding bug)
++                      // so we add 1px to avoid the wrapping (#7513)
++                      ul.width( "" ).outerWidth() + 1,
++                      this.element.outerWidth()
++              ) );
++      },
++
++      _renderMenu: function( ul, items ) {
++              var that = this;
++              $.each( items, function( index, item ) {
++                      that._renderItemData( ul, item );
++              });
++      },
++
++      _renderItemData: function( ul, item ) {
++              return this._renderItem( ul, item ).data( "ui-autocomplete-item", item );
++      },
++
++      _renderItem: function( ul, item ) {
++              return $( "<li>" ).text( item.label ).appendTo( ul );
++      },
++
++      _move: function( direction, event ) {
++              if ( !this.menu.element.is( ":visible" ) ) {
++                      this.search( null, event );
++                      return;
++              }
++              if ( this.menu.isFirstItem() && /^previous/.test( direction ) ||
++                              this.menu.isLastItem() && /^next/.test( direction ) ) {
++
++                      if ( !this.isMultiLine ) {
++                              this._value( this.term );
++                      }
++
++                      this.menu.blur();
++                      return;
++              }
++              this.menu[ direction ]( event );
++      },
++
++      widget: function() {
++              return this.menu.element;
++      },
++
++      _value: function() {
++              return this.valueMethod.apply( this.element, arguments );
++      },
++
++      _keyEvent: function( keyEvent, event ) {
++              if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
++                      this._move( keyEvent, event );
++
++                      // prevents moving cursor to beginning/end of the text field in some browsers
++                      event.preventDefault();
++              }
++      }
++});
++
++$.extend( $.ui.autocomplete, {
++      escapeRegex: function( value ) {
++              return value.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" );
++      },
++      filter: function( array, term ) {
++              var matcher = new RegExp( $.ui.autocomplete.escapeRegex( term ), "i" );
++              return $.grep( array, function( value ) {
++                      return matcher.test( value.label || value.value || value );
++              });
++      }
++});
++
++// live region extension, adding a `messages` option
++// NOTE: This is an experimental API. We are still investigating
++// a full solution for string manipulation and internationalization.
++$.widget( "ui.autocomplete", $.ui.autocomplete, {
++      options: {
++              messages: {
++                      noResults: "No search results.",
++                      results: function( amount ) {
++                              return amount + ( amount > 1 ? " results are" : " result is" ) +
++                                      " available, use up and down arrow keys to navigate.";
++                      }
++              }
++      },
++
++      __response: function( content ) {
++              var message;
++              this._superApply( arguments );
++              if ( this.options.disabled || this.cancelSearch ) {
++                      return;
++              }
++              if ( content && content.length ) {
++                      message = this.options.messages.results( content.length );
++              } else {
++                      message = this.options.messages.noResults;
++              }
++              this.liveRegion.children().hide();
++              $( "<div>" ).text( message ).appendTo( this.liveRegion );
++      }
++});
++
++var autocomplete = $.ui.autocomplete;
++
++
++/*!
++ * jQuery UI Button 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/button/
++ */
++
++
++var lastActive,
++      baseClasses = "ui-button ui-widget ui-state-default ui-corner-all",
++      typeClasses = "ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only",
++      formResetHandler = function() {
++              var form = $( this );
++              setTimeout(function() {
++                      form.find( ":ui-button" ).button( "refresh" );
++              }, 1 );
++      },
++      radioGroup = function( radio ) {
++              var name = radio.name,
++                      form = radio.form,
++                      radios = $( [] );
++              if ( name ) {
++                      name = name.replace( /'/g, "\\'" );
++                      if ( form ) {
++                              radios = $( form ).find( "[name='" + name + "'][type=radio]" );
++                      } else {
++                              radios = $( "[name='" + name + "'][type=radio]", radio.ownerDocument )
++                                      .filter(function() {
++                                              return !this.form;
++                                      });
++                      }
++              }
++              return radios;
++      };
++
++$.widget( "ui.button", {
++      version: "1.11.4",
++      defaultElement: "<button>",
++      options: {
++              disabled: null,
++              text: true,
++              label: null,
++              icons: {
++                      primary: null,
++                      secondary: null
++              }
++      },
++      _create: function() {
++              this.element.closest( "form" )
++                      .unbind( "reset" + this.eventNamespace )
++                      .bind( "reset" + this.eventNamespace, formResetHandler );
++
++              if ( typeof this.options.disabled !== "boolean" ) {
++                      this.options.disabled = !!this.element.prop( "disabled" );
++              } else {
++                      this.element.prop( "disabled", this.options.disabled );
++              }
++
++              this._determineButtonType();
++              this.hasTitle = !!this.buttonElement.attr( "title" );
++
++              var that = this,
++                      options = this.options,
++                      toggleButton = this.type === "checkbox" || this.type === "radio",
++                      activeClass = !toggleButton ? "ui-state-active" : "";
++
++              if ( options.label === null ) {
++                      options.label = (this.type === "input" ? this.buttonElement.val() : this.buttonElement.html());
++              }
++
++              this._hoverable( this.buttonElement );
++
++              this.buttonElement
++                      .addClass( baseClasses )
++                      .attr( "role", "button" )
++                      .bind( "mouseenter" + this.eventNamespace, function() {
++                              if ( options.disabled ) {
++                                      return;
++                              }
++                              if ( this === lastActive ) {
++                                      $( this ).addClass( "ui-state-active" );
++                              }
++                      })
++                      .bind( "mouseleave" + this.eventNamespace, function() {
++                              if ( options.disabled ) {
++                                      return;
++                              }
++                              $( this ).removeClass( activeClass );
++                      })
++                      .bind( "click" + this.eventNamespace, function( event ) {
++                              if ( options.disabled ) {
++                                      event.preventDefault();
++                                      event.stopImmediatePropagation();
++                              }
++                      });
++
++              // Can't use _focusable() because the element that receives focus
++              // and the element that gets the ui-state-focus class are different
++              this._on({
++                      focus: function() {
++                              this.buttonElement.addClass( "ui-state-focus" );
++                      },
++                      blur: function() {
++                              this.buttonElement.removeClass( "ui-state-focus" );
++                      }
++              });
++
++              if ( toggleButton ) {
++                      this.element.bind( "change" + this.eventNamespace, function() {
++                              that.refresh();
++                      });
++              }
++
++              if ( this.type === "checkbox" ) {
++                      this.buttonElement.bind( "click" + this.eventNamespace, function() {
++                              if ( options.disabled ) {
++                                      return false;
++                              }
++                      });
++              } else if ( this.type === "radio" ) {
++                      this.buttonElement.bind( "click" + this.eventNamespace, function() {
++                              if ( options.disabled ) {
++                                      return false;
++                              }
++                              $( this ).addClass( "ui-state-active" );
++                              that.buttonElement.attr( "aria-pressed", "true" );
++
++                              var radio = that.element[ 0 ];
++                              radioGroup( radio )
++                                      .not( radio )
++                                      .map(function() {
++                                              return $( this ).button( "widget" )[ 0 ];
++                                      })
++                                      .removeClass( "ui-state-active" )
++                                      .attr( "aria-pressed", "false" );
++                      });
++              } else {
++                      this.buttonElement
++                              .bind( "mousedown" + this.eventNamespace, function() {
++                                      if ( options.disabled ) {
++                                              return false;
++                                      }
++                                      $( this ).addClass( "ui-state-active" );
++                                      lastActive = this;
++                                      that.document.one( "mouseup", function() {
++                                              lastActive = null;
++                                      });
++                              })
++                              .bind( "mouseup" + this.eventNamespace, function() {
++                                      if ( options.disabled ) {
++                                              return false;
++                                      }
++                                      $( this ).removeClass( "ui-state-active" );
++                              })
++                              .bind( "keydown" + this.eventNamespace, function(event) {
++                                      if ( options.disabled ) {
++                                              return false;
++                                      }
++                                      if ( event.keyCode === $.ui.keyCode.SPACE || event.keyCode === $.ui.keyCode.ENTER ) {
++                                              $( this ).addClass( "ui-state-active" );
++                                      }
++                              })
++                              // see #8559, we bind to blur here in case the button element loses
++                              // focus between keydown and keyup, it would be left in an "active" state
++                              .bind( "keyup" + this.eventNamespace + " blur" + this.eventNamespace, function() {
++                                      $( this ).removeClass( "ui-state-active" );
++                              });
++
++                      if ( this.buttonElement.is("a") ) {
++                              this.buttonElement.keyup(function(event) {
++                                      if ( event.keyCode === $.ui.keyCode.SPACE ) {
++                                              // TODO pass through original event correctly (just as 2nd argument doesn't work)
++                                              $( this ).click();
++                                      }
++                              });
++                      }
++              }
++
++              this._setOption( "disabled", options.disabled );
++              this._resetButton();
++      },
++
++      _determineButtonType: function() {
++              var ancestor, labelSelector, checked;
++
++              if ( this.element.is("[type=checkbox]") ) {
++                      this.type = "checkbox";
++              } else if ( this.element.is("[type=radio]") ) {
++                      this.type = "radio";
++              } else if ( this.element.is("input") ) {
++                      this.type = "input";
++              } else {
++                      this.type = "button";
++              }
++
++              if ( this.type === "checkbox" || this.type === "radio" ) {
++                      // we don't search against the document in case the element
++                      // is disconnected from the DOM
++                      ancestor = this.element.parents().last();
++                      labelSelector = "label[for='" + this.element.attr("id") + "']";
++                      this.buttonElement = ancestor.find( labelSelector );
++                      if ( !this.buttonElement.length ) {
++                              ancestor = ancestor.length ? ancestor.siblings() : this.element.siblings();
++                              this.buttonElement = ancestor.filter( labelSelector );
++                              if ( !this.buttonElement.length ) {
++                                      this.buttonElement = ancestor.find( labelSelector );
++                              }
++                      }
++                      this.element.addClass( "ui-helper-hidden-accessible" );
++
++                      checked = this.element.is( ":checked" );
++                      if ( checked ) {
++                              this.buttonElement.addClass( "ui-state-active" );
++                      }
++                      this.buttonElement.prop( "aria-pressed", checked );
++              } else {
++                      this.buttonElement = this.element;
++              }
++      },
++
++      widget: function() {
++              return this.buttonElement;
++      },
++
++      _destroy: function() {
++              this.element
++                      .removeClass( "ui-helper-hidden-accessible" );
++              this.buttonElement
++                      .removeClass( baseClasses + " ui-state-active " + typeClasses )
++                      .removeAttr( "role" )
++                      .removeAttr( "aria-pressed" )
++                      .html( this.buttonElement.find(".ui-button-text").html() );
++
++              if ( !this.hasTitle ) {
++                      this.buttonElement.removeAttr( "title" );
++              }
++      },
++
++      _setOption: function( key, value ) {
++              this._super( key, value );
++              if ( key === "disabled" ) {
++                      this.widget().toggleClass( "ui-state-disabled", !!value );
++                      this.element.prop( "disabled", !!value );
++                      if ( value ) {
++                              if ( this.type === "checkbox" || this.type === "radio" ) {
++                                      this.buttonElement.removeClass( "ui-state-focus" );
++                              } else {
++                                      this.buttonElement.removeClass( "ui-state-focus ui-state-active" );
++                              }
++                      }
++                      return;
++              }
++              this._resetButton();
++      },
++
++      refresh: function() {
++              //See #8237 & #8828
++              var isDisabled = this.element.is( "input, button" ) ? this.element.is( ":disabled" ) : this.element.hasClass( "ui-button-disabled" );
++
++              if ( isDisabled !== this.options.disabled ) {
++                      this._setOption( "disabled", isDisabled );
++              }
++              if ( this.type === "radio" ) {
++                      radioGroup( this.element[0] ).each(function() {
++                              if ( $( this ).is( ":checked" ) ) {
++                                      $( this ).button( "widget" )
++                                              .addClass( "ui-state-active" )
++                                              .attr( "aria-pressed", "true" );
++                              } else {
++                                      $( this ).button( "widget" )
++                                              .removeClass( "ui-state-active" )
++                                              .attr( "aria-pressed", "false" );
++                              }
++                      });
++              } else if ( this.type === "checkbox" ) {
++                      if ( this.element.is( ":checked" ) ) {
++                              this.buttonElement
++                                      .addClass( "ui-state-active" )
++                                      .attr( "aria-pressed", "true" );
++                      } else {
++                              this.buttonElement
++                                      .removeClass( "ui-state-active" )
++                                      .attr( "aria-pressed", "false" );
++                      }
++              }
++      },
++
++      _resetButton: function() {
++              if ( this.type === "input" ) {
++                      if ( this.options.label ) {
++                              this.element.val( this.options.label );
++                      }
++                      return;
++              }
++              var buttonElement = this.buttonElement.removeClass( typeClasses ),
++                      buttonText = $( "<span></span>", this.document[0] )
++                              .addClass( "ui-button-text" )
++                              .html( this.options.label )
++                              .appendTo( buttonElement.empty() )
++                              .text(),
++                      icons = this.options.icons,
++                      multipleIcons = icons.primary && icons.secondary,
++                      buttonClasses = [];
++
++              if ( icons.primary || icons.secondary ) {
++                      if ( this.options.text ) {
++                              buttonClasses.push( "ui-button-text-icon" + ( multipleIcons ? "s" : ( icons.primary ? "-primary" : "-secondary" ) ) );
++                      }
++
++                      if ( icons.primary ) {
++                              buttonElement.prepend( "<span class='ui-button-icon-primary ui-icon " + icons.primary + "'></span>" );
++                      }
++
++                      if ( icons.secondary ) {
++                              buttonElement.append( "<span class='ui-button-icon-secondary ui-icon " + icons.secondary + "'></span>" );
++                      }
++
++                      if ( !this.options.text ) {
++                              buttonClasses.push( multipleIcons ? "ui-button-icons-only" : "ui-button-icon-only" );
++
++                              if ( !this.hasTitle ) {
++                                      buttonElement.attr( "title", $.trim( buttonText ) );
++                              }
++                      }
++              } else {
++                      buttonClasses.push( "ui-button-text-only" );
++              }
++              buttonElement.addClass( buttonClasses.join( " " ) );
++      }
++});
++
++$.widget( "ui.buttonset", {
++      version: "1.11.4",
++      options: {
++              items: "button, input[type=button], input[type=submit], input[type=reset], input[type=checkbox], input[type=radio], a, :data(ui-button)"
++      },
++
++      _create: function() {
++              this.element.addClass( "ui-buttonset" );
++      },
++
++      _init: function() {
++              this.refresh();
++      },
++
++      _setOption: function( key, value ) {
++              if ( key === "disabled" ) {
++                      this.buttons.button( "option", key, value );
++              }
++
++              this._super( key, value );
++      },
++
++      refresh: function() {
++              var rtl = this.element.css( "direction" ) === "rtl",
++                      allButtons = this.element.find( this.options.items ),
++                      existingButtons = allButtons.filter( ":ui-button" );
++
++              // Initialize new buttons
++              allButtons.not( ":ui-button" ).button();
++
++              // Refresh existing buttons
++              existingButtons.button( "refresh" );
++
++              this.buttons = allButtons
++                      .map(function() {
++                              return $( this ).button( "widget" )[ 0 ];
++                      })
++                              .removeClass( "ui-corner-all ui-corner-left ui-corner-right" )
++                              .filter( ":first" )
++                                      .addClass( rtl ? "ui-corner-right" : "ui-corner-left" )
++                              .end()
++                              .filter( ":last" )
++                                      .addClass( rtl ? "ui-corner-left" : "ui-corner-right" )
++                              .end()
++                      .end();
++      },
++
++      _destroy: function() {
++              this.element.removeClass( "ui-buttonset" );
++              this.buttons
++                      .map(function() {
++                              return $( this ).button( "widget" )[ 0 ];
++                      })
++                              .removeClass( "ui-corner-left ui-corner-right" )
++                      .end()
++                      .button( "destroy" );
++      }
++});
++
++var button = $.ui.button;
++
++
++/*!
++ * jQuery UI Datepicker 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/datepicker/
++ */
++
++
++$.extend($.ui, { datepicker: { version: "1.11.4" } });
++
++var datepicker_instActive;
++
++function datepicker_getZindex( elem ) {
++      var position, value;
++      while ( elem.length && elem[ 0 ] !== document ) {
++              // Ignore z-index if position is set to a value where z-index is ignored by the browser
++              // This makes behavior of this function consistent across browsers
++              // WebKit always returns auto if the element is positioned
++              position = elem.css( "position" );
++              if ( position === "absolute" || position === "relative" || position === "fixed" ) {
++                      // IE returns 0 when zIndex is not specified
++                      // other browsers return a string
++                      // we ignore the case of nested elements with an explicit value of 0
++                      // <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
++                      value = parseInt( elem.css( "zIndex" ), 10 );
++                      if ( !isNaN( value ) && value !== 0 ) {
++                              return value;
++                      }
++              }
++              elem = elem.parent();
++      }
++
++      return 0;
++}
++/* Date picker manager.
++   Use the singleton instance of this class, $.datepicker, to interact with the date picker.
++   Settings for (groups of) date pickers are maintained in an instance object,
++   allowing multiple different settings on the same page. */
++
++function Datepicker() {
++      this._curInst = null; // The current instance in use
++      this._keyEvent = false; // If the last event was a key event
++      this._disabledInputs = []; // List of date picker inputs that have been disabled
++      this._datepickerShowing = false; // True if the popup picker is showing , false if not
++      this._inDialog = false; // True if showing within a "dialog", false if not
++      this._mainDivId = "ui-datepicker-div"; // The ID of the main datepicker division
++      this._inlineClass = "ui-datepicker-inline"; // The name of the inline marker class
++      this._appendClass = "ui-datepicker-append"; // The name of the append marker class
++      this._triggerClass = "ui-datepicker-trigger"; // The name of the trigger marker class
++      this._dialogClass = "ui-datepicker-dialog"; // The name of the dialog marker class
++      this._disableClass = "ui-datepicker-disabled"; // The name of the disabled covering marker class
++      this._unselectableClass = "ui-datepicker-unselectable"; // The name of the unselectable cell marker class
++      this._currentClass = "ui-datepicker-current-day"; // The name of the current day marker class
++      this._dayOverClass = "ui-datepicker-days-cell-over"; // The name of the day hover marker class
++      this.regional = []; // Available regional settings, indexed by language code
++      this.regional[""] = { // Default regional settings
++              closeText: "Done", // Display text for close link
++              prevText: "Prev", // Display text for previous month link
++              nextText: "Next", // Display text for next month link
++              currentText: "Today", // Display text for current month link
++              monthNames: ["January","February","March","April","May","June",
++                      "July","August","September","October","November","December"], // Names of months for drop-down and formatting
++              monthNamesShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], // For formatting
++              dayNames: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], // For formatting
++              dayNamesShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], // For formatting
++              dayNamesMin: ["Su","Mo","Tu","We","Th","Fr","Sa"], // Column headings for days starting at Sunday
++              weekHeader: "Wk", // Column header for week of the year
++              dateFormat: "mm/dd/yy", // See format options on parseDate
++              firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
++              isRTL: false, // True if right-to-left language, false if left-to-right
++              showMonthAfterYear: false, // True if the year select precedes month, false for month then year
++              yearSuffix: "" // Additional text to append to the year in the month headers
++      };
++      this._defaults = { // Global defaults for all the date picker instances
++              showOn: "focus", // "focus" for popup on focus,
++                      // "button" for trigger button, or "both" for either
++              showAnim: "fadeIn", // Name of jQuery animation for popup
++              showOptions: {}, // Options for enhanced animations
++              defaultDate: null, // Used when field is blank: actual date,
++                      // +/-number for offset from today, null for today
++              appendText: "", // Display text following the input box, e.g. showing the format
++              buttonText: "...", // Text for trigger button
++              buttonImage: "", // URL for trigger button image
++              buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
++              hideIfNoPrevNext: false, // True to hide next/previous month links
++                      // if not applicable, false to just disable them
++              navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links
++              gotoCurrent: false, // True if today link goes back to current selection instead
++              changeMonth: false, // True if month can be selected directly, false if only prev/next
++              changeYear: false, // True if year can be selected directly, false if only prev/next
++              yearRange: "c-10:c+10", // Range of years to display in drop-down,
++                      // either relative to today's year (-nn:+nn), relative to currently displayed year
++                      // (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n)
++              showOtherMonths: false, // True to show dates in other months, false to leave blank
++              selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable
++              showWeek: false, // True to show week of the year, false to not show it
++              calculateWeek: this.iso8601Week, // How to calculate the week of the year,
++                      // takes a Date and returns the number of the week for it
++              shortYearCutoff: "+10", // Short year values < this are in the current century,
++                      // > this are in the previous century,
++                      // string value starting with "+" for current year + value
++              minDate: null, // The earliest selectable date, or null for no limit
++              maxDate: null, // The latest selectable date, or null for no limit
++              duration: "fast", // Duration of display/closure
++              beforeShowDay: null, // Function that takes a date and returns an array with
++                      // [0] = true if selectable, false if not, [1] = custom CSS class name(s) or "",
++                      // [2] = cell title (optional), e.g. $.datepicker.noWeekends
++              beforeShow: null, // Function that takes an input field and
++                      // returns a set of custom settings for the date picker
++              onSelect: null, // Define a callback function when a date is selected
++              onChangeMonthYear: null, // Define a callback function when the month or year is changed
++              onClose: null, // Define a callback function when the datepicker is closed
++              numberOfMonths: 1, // Number of months to show at a time
++              showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0)
++              stepMonths: 1, // Number of months to step back/forward
++              stepBigMonths: 12, // Number of months to step back/forward for the big links
++              altField: "", // Selector for an alternate field to store selected dates into
++              altFormat: "", // The date format to use for the alternate field
++              constrainInput: true, // The input is constrained by the current date format
++              showButtonPanel: false, // True to show button panel, false to not show it
++              autoSize: false, // True to size the input for the date format, false to leave as is
++              disabled: false // The initial disabled state
++      };
++      $.extend(this._defaults, this.regional[""]);
++      this.regional.en = $.extend( true, {}, this.regional[ "" ]);
++      this.regional[ "en-US" ] = $.extend( true, {}, this.regional.en );
++      this.dpDiv = datepicker_bindHover($("<div id='" + this._mainDivId + "' class='ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>"));
++}
++
++$.extend(Datepicker.prototype, {
++      /* Class name added to elements to indicate already configured with a date picker. */
++      markerClassName: "hasDatepicker",
++
++      //Keep track of the maximum number of rows displayed (see #7043)
++      maxRows: 4,
++
++      // TODO rename to "widget" when switching to widget factory
++      _widgetDatepicker: function() {
++              return this.dpDiv;
++      },
++
++      /* Override the default settings for all instances of the date picker.
++       * @param  settings  object - the new settings to use as defaults (anonymous object)
++       * @return the manager object
++       */
++      setDefaults: function(settings) {
++              datepicker_extendRemove(this._defaults, settings || {});
++              return this;
++      },
++
++      /* Attach the date picker to a jQuery selection.
++       * @param  target       element - the target input field or division or span
++       * @param  settings  object - the new settings to use for this date picker instance (anonymous)
++       */
++      _attachDatepicker: function(target, settings) {
++              var nodeName, inline, inst;
++              nodeName = target.nodeName.toLowerCase();
++              inline = (nodeName === "div" || nodeName === "span");
++              if (!target.id) {
++                      this.uuid += 1;
++                      target.id = "dp" + this.uuid;
++              }
++              inst = this._newInst($(target), inline);
++              inst.settings = $.extend({}, settings || {});
++              if (nodeName === "input") {
++                      this._connectDatepicker(target, inst);
++              } else if (inline) {
++                      this._inlineDatepicker(target, inst);
++              }
++      },
++
++      /* Create a new instance object. */
++      _newInst: function(target, inline) {
++              var id = target[0].id.replace(/([^A-Za-z0-9_\-])/g, "\\\\$1"); // escape jQuery meta chars
++              return {id: id, input: target, // associated target
++                      selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection
++                      drawMonth: 0, drawYear: 0, // month being drawn
++                      inline: inline, // is datepicker inline or not
++                      dpDiv: (!inline ? this.dpDiv : // presentation div
++                      datepicker_bindHover($("<div class='" + this._inlineClass + " ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>")))};
++      },
++
++      /* Attach the date picker to an input field. */
++      _connectDatepicker: function(target, inst) {
++              var input = $(target);
++              inst.append = $([]);
++              inst.trigger = $([]);
++              if (input.hasClass(this.markerClassName)) {
++                      return;
++              }
++              this._attachments(input, inst);
++              input.addClass(this.markerClassName).keydown(this._doKeyDown).
++                      keypress(this._doKeyPress).keyup(this._doKeyUp);
++              this._autoSize(inst);
++              $.data(target, "datepicker", inst);
++              //If disabled option is true, disable the datepicker once it has been attached to the input (see ticket #5665)
++              if( inst.settings.disabled ) {
++                      this._disableDatepicker( target );
++              }
++      },
++
++      /* Make attachments based on settings. */
++      _attachments: function(input, inst) {
++              var showOn, buttonText, buttonImage,
++                      appendText = this._get(inst, "appendText"),
++                      isRTL = this._get(inst, "isRTL");
++
++              if (inst.append) {
++                      inst.append.remove();
++              }
++              if (appendText) {
++                      inst.append = $("<span class='" + this._appendClass + "'>" + appendText + "</span>");
++                      input[isRTL ? "before" : "after"](inst.append);
++              }
++
++              input.unbind("focus", this._showDatepicker);
++
++              if (inst.trigger) {
++                      inst.trigger.remove();
++              }
++
++              showOn = this._get(inst, "showOn");
++              if (showOn === "focus" || showOn === "both") { // pop-up date picker when in the marked field
++                      input.focus(this._showDatepicker);
++              }
++              if (showOn === "button" || showOn === "both") { // pop-up date picker when button clicked
++                      buttonText = this._get(inst, "buttonText");
++                      buttonImage = this._get(inst, "buttonImage");
++                      inst.trigger = $(this._get(inst, "buttonImageOnly") ?
++                              $("<img/>").addClass(this._triggerClass).
++                                      attr({ src: buttonImage, alt: buttonText, title: buttonText }) :
++                              $("<button type='button'></button>").addClass(this._triggerClass).
++                                      html(!buttonImage ? buttonText : $("<img/>").attr(
++                                      { src:buttonImage, alt:buttonText, title:buttonText })));
++                      input[isRTL ? "before" : "after"](inst.trigger);
++                      inst.trigger.click(function() {
++                              if ($.datepicker._datepickerShowing && $.datepicker._lastInput === input[0]) {
++                                      $.datepicker._hideDatepicker();
++                              } else if ($.datepicker._datepickerShowing && $.datepicker._lastInput !== input[0]) {
++                                      $.datepicker._hideDatepicker();
++                                      $.datepicker._showDatepicker(input[0]);
++                              } else {
++                                      $.datepicker._showDatepicker(input[0]);
++                              }
++                              return false;
++                      });
++              }
++      },
++
++      /* Apply the maximum length for the date format. */
++      _autoSize: function(inst) {
++              if (this._get(inst, "autoSize") && !inst.inline) {
++                      var findMax, max, maxI, i,
++                              date = new Date(2009, 12 - 1, 20), // Ensure double digits
++                              dateFormat = this._get(inst, "dateFormat");
++
++                      if (dateFormat.match(/[DM]/)) {
++                              findMax = function(names) {
++                                      max = 0;
++                                      maxI = 0;
++                                      for (i = 0; i < names.length; i++) {
++                                              if (names[i].length > max) {
++                                                      max = names[i].length;
++                                                      maxI = i;
++                                              }
++                                      }
++                                      return maxI;
++                              };
++                              date.setMonth(findMax(this._get(inst, (dateFormat.match(/MM/) ?
++                                      "monthNames" : "monthNamesShort"))));
++                              date.setDate(findMax(this._get(inst, (dateFormat.match(/DD/) ?
++                                      "dayNames" : "dayNamesShort"))) + 20 - date.getDay());
++                      }
++                      inst.input.attr("size", this._formatDate(inst, date).length);
++              }
++      },
++
++      /* Attach an inline date picker to a div. */
++      _inlineDatepicker: function(target, inst) {
++              var divSpan = $(target);
++              if (divSpan.hasClass(this.markerClassName)) {
++                      return;
++              }
++              divSpan.addClass(this.markerClassName).append(inst.dpDiv);
++              $.data(target, "datepicker", inst);
++              this._setDate(inst, this._getDefaultDate(inst), true);
++              this._updateDatepicker(inst);
++              this._updateAlternate(inst);
++              //If disabled option is true, disable the datepicker before showing it (see ticket #5665)
++              if( inst.settings.disabled ) {
++                      this._disableDatepicker( target );
++              }
++              // Set display:block in place of inst.dpDiv.show() which won't work on disconnected elements
++              // http://bugs.jqueryui.com/ticket/7552 - A Datepicker created on a detached div has zero height
++              inst.dpDiv.css( "display", "block" );
++      },
++
++      /* Pop-up the date picker in a "dialog" box.
++       * @param  input element - ignored
++       * @param  date string or Date - the initial date to display
++       * @param  onSelect  function - the function to call when a date is selected
++       * @param  settings  object - update the dialog date picker instance's settings (anonymous object)
++       * @param  pos int[2] - coordinates for the dialog's position within the screen or
++       *                                      event - with x/y coordinates or
++       *                                      leave empty for default (screen centre)
++       * @return the manager object
++       */
++      _dialogDatepicker: function(input, date, onSelect, settings, pos) {
++              var id, browserWidth, browserHeight, scrollX, scrollY,
++                      inst = this._dialogInst; // internal instance
++
++              if (!inst) {
++                      this.uuid += 1;
++                      id = "dp" + this.uuid;
++                      this._dialogInput = $("<input type='text' id='" + id +
++                              "' style='position: absolute; top: -100px; width: 0px;'/>");
++                      this._dialogInput.keydown(this._doKeyDown);
++                      $("body").append(this._dialogInput);
++                      inst = this._dialogInst = this._newInst(this._dialogInput, false);
++                      inst.settings = {};
++                      $.data(this._dialogInput[0], "datepicker", inst);
++              }
++              datepicker_extendRemove(inst.settings, settings || {});
++              date = (date && date.constructor === Date ? this._formatDate(inst, date) : date);
++              this._dialogInput.val(date);
++
++              this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null);
++              if (!this._pos) {
++                      browserWidth = document.documentElement.clientWidth;
++                      browserHeight = document.documentElement.clientHeight;
++                      scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
++                      scrollY = document.documentElement.scrollTop || document.body.scrollTop;
++                      this._pos = // should use actual width/height below
++                              [(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY];
++              }
++
++              // move input on screen for focus, but hidden behind dialog
++              this._dialogInput.css("left", (this._pos[0] + 20) + "px").css("top", this._pos[1] + "px");
++              inst.settings.onSelect = onSelect;
++              this._inDialog = true;
++              this.dpDiv.addClass(this._dialogClass);
++              this._showDatepicker(this._dialogInput[0]);
++              if ($.blockUI) {
++                      $.blockUI(this.dpDiv);
++              }
++              $.data(this._dialogInput[0], "datepicker", inst);
++              return this;
++      },
++
++      /* Detach a datepicker from its control.
++       * @param  target       element - the target input field or division or span
++       */
++      _destroyDatepicker: function(target) {
++              var nodeName,
++                      $target = $(target),
++                      inst = $.data(target, "datepicker");
++
++              if (!$target.hasClass(this.markerClassName)) {
++                      return;
++              }
++
++              nodeName = target.nodeName.toLowerCase();
++              $.removeData(target, "datepicker");
++              if (nodeName === "input") {
++                      inst.append.remove();
++                      inst.trigger.remove();
++                      $target.removeClass(this.markerClassName).
++                              unbind("focus", this._showDatepicker).
++                              unbind("keydown", this._doKeyDown).
++                              unbind("keypress", this._doKeyPress).
++                              unbind("keyup", this._doKeyUp);
++              } else if (nodeName === "div" || nodeName === "span") {
++                      $target.removeClass(this.markerClassName).empty();
++              }
++
++              if ( datepicker_instActive === inst ) {
++                      datepicker_instActive = null;
++              }
++      },
++
++      /* Enable the date picker to a jQuery selection.
++       * @param  target       element - the target input field or division or span
++       */
++      _enableDatepicker: function(target) {
++              var nodeName, inline,
++                      $target = $(target),
++                      inst = $.data(target, "datepicker");
++
++              if (!$target.hasClass(this.markerClassName)) {
++                      return;
++              }
++
++              nodeName = target.nodeName.toLowerCase();
++              if (nodeName === "input") {
++                      target.disabled = false;
++                      inst.trigger.filter("button").
++                              each(function() { this.disabled = false; }).end().
++                              filter("img").css({opacity: "1.0", cursor: ""});
++              } else if (nodeName === "div" || nodeName === "span") {
++                      inline = $target.children("." + this._inlineClass);
++                      inline.children().removeClass("ui-state-disabled");
++                      inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
++                              prop("disabled", false);
++              }
++              this._disabledInputs = $.map(this._disabledInputs,
++                      function(value) { return (value === target ? null : value); }); // delete entry
++      },
++
++      /* Disable the date picker to a jQuery selection.
++       * @param  target       element - the target input field or division or span
++       */
++      _disableDatepicker: function(target) {
++              var nodeName, inline,
++                      $target = $(target),
++                      inst = $.data(target, "datepicker");
++
++              if (!$target.hasClass(this.markerClassName)) {
++                      return;
++              }
++
++              nodeName = target.nodeName.toLowerCase();
++              if (nodeName === "input") {
++                      target.disabled = true;
++                      inst.trigger.filter("button").
++                              each(function() { this.disabled = true; }).end().
++                              filter("img").css({opacity: "0.5", cursor: "default"});
++              } else if (nodeName === "div" || nodeName === "span") {
++                      inline = $target.children("." + this._inlineClass);
++                      inline.children().addClass("ui-state-disabled");
++                      inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
++                              prop("disabled", true);
++              }
++              this._disabledInputs = $.map(this._disabledInputs,
++                      function(value) { return (value === target ? null : value); }); // delete entry
++              this._disabledInputs[this._disabledInputs.length] = target;
++      },
++
++      /* Is the first field in a jQuery collection disabled as a datepicker?
++       * @param  target       element - the target input field or division or span
++       * @return boolean - true if disabled, false if enabled
++       */
++      _isDisabledDatepicker: function(target) {
++              if (!target) {
++                      return false;
++              }
++              for (var i = 0; i < this._disabledInputs.length; i++) {
++                      if (this._disabledInputs[i] === target) {
++                              return true;
++                      }
++              }
++              return false;
++      },
++
++      /* Retrieve the instance data for the target control.
++       * @param  target  element - the target input field or division or span
++       * @return  object - the associated instance data
++       * @throws  error if a jQuery problem getting data
++       */
++      _getInst: function(target) {
++              try {
++                      return $.data(target, "datepicker");
++              }
++              catch (err) {
++                      throw "Missing instance data for this datepicker";
++              }
++      },
++
++      /* Update or retrieve the settings for a date picker attached to an input field or division.
++       * @param  target  element - the target input field or division or span
++       * @param  name object - the new settings to update or
++       *                              string - the name of the setting to change or retrieve,
++       *                              when retrieving also "all" for all instance settings or
++       *                              "defaults" for all global defaults
++       * @param  value   any - the new value for the setting
++       *                              (omit if above is an object or to retrieve a value)
++       */
++      _optionDatepicker: function(target, name, value) {
++              var settings, date, minDate, maxDate,
++                      inst = this._getInst(target);
++
++              if (arguments.length === 2 && typeof name === "string") {
++                      return (name === "defaults" ? $.extend({}, $.datepicker._defaults) :
++                              (inst ? (name === "all" ? $.extend({}, inst.settings) :
++                              this._get(inst, name)) : null));
++              }
++
++              settings = name || {};
++              if (typeof name === "string") {
++                      settings = {};
++                      settings[name] = value;
++              }
++
++              if (inst) {
++                      if (this._curInst === inst) {
++                              this._hideDatepicker();
++                      }
++
++                      date = this._getDateDatepicker(target, true);
++                      minDate = this._getMinMaxDate(inst, "min");
++                      maxDate = this._getMinMaxDate(inst, "max");
++                      datepicker_extendRemove(inst.settings, settings);
++                      // reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided
++                      if (minDate !== null && settings.dateFormat !== undefined && settings.minDate === undefined) {
++                              inst.settings.minDate = this._formatDate(inst, minDate);
++                      }
++                      if (maxDate !== null && settings.dateFormat !== undefined && settings.maxDate === undefined) {
++                              inst.settings.maxDate = this._formatDate(inst, maxDate);
++                      }
++                      if ( "disabled" in settings ) {
++                              if ( settings.disabled ) {
++                                      this._disableDatepicker(target);
++                              } else {
++                                      this._enableDatepicker(target);
++                              }
++                      }
++                      this._attachments($(target), inst);
++                      this._autoSize(inst);
++                      this._setDate(inst, date);
++                      this._updateAlternate(inst);
++                      this._updateDatepicker(inst);
++              }
++      },
++
++      // change method deprecated
++      _changeDatepicker: function(target, name, value) {
++              this._optionDatepicker(target, name, value);
++      },
++
++      /* Redraw the date picker attached to an input field or division.
++       * @param  target  element - the target input field or division or span
++       */
++      _refreshDatepicker: function(target) {
++              var inst = this._getInst(target);
++              if (inst) {
++                      this._updateDatepicker(inst);
++              }
++      },
++
++      /* Set the dates for a jQuery selection.
++       * @param  target element - the target input field or division or span
++       * @param  date Date - the new date
++       */
++      _setDateDatepicker: function(target, date) {
++              var inst = this._getInst(target);
++              if (inst) {
++                      this._setDate(inst, date);
++                      this._updateDatepicker(inst);
++                      this._updateAlternate(inst);
++              }
++      },
++
++      /* Get the date(s) for the first entry in a jQuery selection.
++       * @param  target element - the target input field or division or span
++       * @param  noDefault boolean - true if no default date is to be used
++       * @return Date - the current date
++       */
++      _getDateDatepicker: function(target, noDefault) {
++              var inst = this._getInst(target);
++              if (inst && !inst.inline) {
++                      this._setDateFromField(inst, noDefault);
++              }
++              return (inst ? this._getDate(inst) : null);
++      },
++
++      /* Handle keystrokes. */
++      _doKeyDown: function(event) {
++              var onSelect, dateStr, sel,
++                      inst = $.datepicker._getInst(event.target),
++                      handled = true,
++                      isRTL = inst.dpDiv.is(".ui-datepicker-rtl");
++
++              inst._keyEvent = true;
++              if ($.datepicker._datepickerShowing) {
++                      switch (event.keyCode) {
++                              case 9: $.datepicker._hideDatepicker();
++                                              handled = false;
++                                              break; // hide on tab out
++                              case 13: sel = $("td." + $.datepicker._dayOverClass + ":not(." +
++                                                                      $.datepicker._currentClass + ")", inst.dpDiv);
++                                              if (sel[0]) {
++                                                      $.datepicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, sel[0]);
++                                              }
++
++                                              onSelect = $.datepicker._get(inst, "onSelect");
++                                              if (onSelect) {
++                                                      dateStr = $.datepicker._formatDate(inst);
++
++                                                      // trigger custom callback
++                                                      onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]);
++                                              } else {
++                                                      $.datepicker._hideDatepicker();
++                                              }
++
++                                              return false; // don't submit the form
++                              case 27: $.datepicker._hideDatepicker();
++                                              break; // hide on escape
++                              case 33: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
++                                                      -$.datepicker._get(inst, "stepBigMonths") :
++                                                      -$.datepicker._get(inst, "stepMonths")), "M");
++                                              break; // previous month/year on page up/+ ctrl
++                              case 34: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
++                                                      +$.datepicker._get(inst, "stepBigMonths") :
++                                                      +$.datepicker._get(inst, "stepMonths")), "M");
++                                              break; // next month/year on page down/+ ctrl
++                              case 35: if (event.ctrlKey || event.metaKey) {
++                                                      $.datepicker._clearDate(event.target);
++                                              }
++                                              handled = event.ctrlKey || event.metaKey;
++                                              break; // clear on ctrl or command +end
++                              case 36: if (event.ctrlKey || event.metaKey) {
++                                                      $.datepicker._gotoToday(event.target);
++                                              }
++                                              handled = event.ctrlKey || event.metaKey;
++                                              break; // current on ctrl or command +home
++                              case 37: if (event.ctrlKey || event.metaKey) {
++                                                      $.datepicker._adjustDate(event.target, (isRTL ? +1 : -1), "D");
++                                              }
++                                              handled = event.ctrlKey || event.metaKey;
++                                              // -1 day on ctrl or command +left
++                                              if (event.originalEvent.altKey) {
++                                                      $.datepicker._adjustDate(event.target, (event.ctrlKey ?
++                                                              -$.datepicker._get(inst, "stepBigMonths") :
++                                                              -$.datepicker._get(inst, "stepMonths")), "M");
++                                              }
++                                              // next month/year on alt +left on Mac
++                                              break;
++                              case 38: if (event.ctrlKey || event.metaKey) {
++                                                      $.datepicker._adjustDate(event.target, -7, "D");
++                                              }
++                                              handled = event.ctrlKey || event.metaKey;
++                                              break; // -1 week on ctrl or command +up
++                              case 39: if (event.ctrlKey || event.metaKey) {
++                                                      $.datepicker._adjustDate(event.target, (isRTL ? -1 : +1), "D");
++                                              }
++                                              handled = event.ctrlKey || event.metaKey;
++                                              // +1 day on ctrl or command +right
++                                              if (event.originalEvent.altKey) {
++                                                      $.datepicker._adjustDate(event.target, (event.ctrlKey ?
++                                                              +$.datepicker._get(inst, "stepBigMonths") :
++                                                              +$.datepicker._get(inst, "stepMonths")), "M");
++                                              }
++                                              // next month/year on alt +right
++                                              break;
++                              case 40: if (event.ctrlKey || event.metaKey) {
++                                                      $.datepicker._adjustDate(event.target, +7, "D");
++                                              }
++                                              handled = event.ctrlKey || event.metaKey;
++                                              break; // +1 week on ctrl or command +down
++                              default: handled = false;
++                      }
++              } else if (event.keyCode === 36 && event.ctrlKey) { // display the date picker on ctrl+home
++                      $.datepicker._showDatepicker(this);
++              } else {
++                      handled = false;
++              }
++
++              if (handled) {
++                      event.preventDefault();
++                      event.stopPropagation();
++              }
++      },
++
++      /* Filter entered characters - based on date format. */
++      _doKeyPress: function(event) {
++              var chars, chr,
++                      inst = $.datepicker._getInst(event.target);
++
++              if ($.datepicker._get(inst, "constrainInput")) {
++                      chars = $.datepicker._possibleChars($.datepicker._get(inst, "dateFormat"));
++                      chr = String.fromCharCode(event.charCode == null ? event.keyCode : event.charCode);
++                      return event.ctrlKey || event.metaKey || (chr < " " || !chars || chars.indexOf(chr) > -1);
++              }
++      },
++
++      /* Synchronise manual entry and field/alternate field. */
++      _doKeyUp: function(event) {
++              var date,
++                      inst = $.datepicker._getInst(event.target);
++
++              if (inst.input.val() !== inst.lastVal) {
++                      try {
++                              date = $.datepicker.parseDate($.datepicker._get(inst, "dateFormat"),
++                                      (inst.input ? inst.input.val() : null),
++                                      $.datepicker._getFormatConfig(inst));
++
++                              if (date) { // only if valid
++                                      $.datepicker._setDateFromField(inst);
++                                      $.datepicker._updateAlternate(inst);
++                                      $.datepicker._updateDatepicker(inst);
++                              }
++                      }
++                      catch (err) {
++                      }
++              }
++              return true;
++      },
++
++      /* Pop-up the date picker for a given input field.
++       * If false returned from beforeShow event handler do not show.
++       * @param  input  element - the input field attached to the date picker or
++       *                                      event - if triggered by focus
++       */
++      _showDatepicker: function(input) {
++              input = input.target || input;
++              if (input.nodeName.toLowerCase() !== "input") { // find from button/image trigger
++                      input = $("input", input.parentNode)[0];
++              }
++
++              if ($.datepicker._isDisabledDatepicker(input) || $.datepicker._lastInput === input) { // already here
++                      return;
++              }
++
++              var inst, beforeShow, beforeShowSettings, isFixed,
++                      offset, showAnim, duration;
++
++              inst = $.datepicker._getInst(input);
++              if ($.datepicker._curInst && $.datepicker._curInst !== inst) {
++                      $.datepicker._curInst.dpDiv.stop(true, true);
++                      if ( inst && $.datepicker._datepickerShowing ) {
++                              $.datepicker._hideDatepicker( $.datepicker._curInst.input[0] );
++                      }
++              }
++
++              beforeShow = $.datepicker._get(inst, "beforeShow");
++              beforeShowSettings = beforeShow ? beforeShow.apply(input, [input, inst]) : {};
++              if(beforeShowSettings === false){
++                      return;
++              }
++              datepicker_extendRemove(inst.settings, beforeShowSettings);
++
++              inst.lastVal = null;
++              $.datepicker._lastInput = input;
++              $.datepicker._setDateFromField(inst);
++
++              if ($.datepicker._inDialog) { // hide cursor
++                      input.value = "";
++              }
++              if (!$.datepicker._pos) { // position below input
++                      $.datepicker._pos = $.datepicker._findPos(input);
++                      $.datepicker._pos[1] += input.offsetHeight; // add the height
++              }
++
++              isFixed = false;
++              $(input).parents().each(function() {
++                      isFixed |= $(this).css("position") === "fixed";
++                      return !isFixed;
++              });
++
++              offset = {left: $.datepicker._pos[0], top: $.datepicker._pos[1]};
++              $.datepicker._pos = null;
++              //to avoid flashes on Firefox
++              inst.dpDiv.empty();
++              // determine sizing offscreen
++              inst.dpDiv.css({position: "absolute", display: "block", top: "-1000px"});
++              $.datepicker._updateDatepicker(inst);
++              // fix width for dynamic number of date pickers
++              // and adjust position before showing
++              offset = $.datepicker._checkOffset(inst, offset, isFixed);
++              inst.dpDiv.css({position: ($.datepicker._inDialog && $.blockUI ?
++                      "static" : (isFixed ? "fixed" : "absolute")), display: "none",
++                      left: offset.left + "px", top: offset.top + "px"});
++
++              if (!inst.inline) {
++                      showAnim = $.datepicker._get(inst, "showAnim");
++                      duration = $.datepicker._get(inst, "duration");
++                      inst.dpDiv.css( "z-index", datepicker_getZindex( $( input ) ) + 1 );
++                      $.datepicker._datepickerShowing = true;
++
++                      if ( $.effects && $.effects.effect[ showAnim ] ) {
++                              inst.dpDiv.show(showAnim, $.datepicker._get(inst, "showOptions"), duration);
++                      } else {
++                              inst.dpDiv[showAnim || "show"](showAnim ? duration : null);
++                      }
++
++                      if ( $.datepicker._shouldFocusInput( inst ) ) {
++                              inst.input.focus();
++                      }
++
++                      $.datepicker._curInst = inst;
++              }
++      },
++
++      /* Generate the date picker content. */
++      _updateDatepicker: function(inst) {
++              this.maxRows = 4; //Reset the max number of rows being displayed (see #7043)
++              datepicker_instActive = inst; // for delegate hover events
++              inst.dpDiv.empty().append(this._generateHTML(inst));
++              this._attachHandlers(inst);
++
++              var origyearshtml,
++                      numMonths = this._getNumberOfMonths(inst),
++                      cols = numMonths[1],
++                      width = 17,
++                      activeCell = inst.dpDiv.find( "." + this._dayOverClass + " a" );
++
++              if ( activeCell.length > 0 ) {
++                      datepicker_handleMouseover.apply( activeCell.get( 0 ) );
++              }
++
++              inst.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width("");
++              if (cols > 1) {
++                      inst.dpDiv.addClass("ui-datepicker-multi-" + cols).css("width", (width * cols) + "em");
++              }
++              inst.dpDiv[(numMonths[0] !== 1 || numMonths[1] !== 1 ? "add" : "remove") +
++                      "Class"]("ui-datepicker-multi");
++              inst.dpDiv[(this._get(inst, "isRTL") ? "add" : "remove") +
++                      "Class"]("ui-datepicker-rtl");
++
++              if (inst === $.datepicker._curInst && $.datepicker._datepickerShowing && $.datepicker._shouldFocusInput( inst ) ) {
++                      inst.input.focus();
++              }
++
++              // deffered render of the years select (to avoid flashes on Firefox)
++              if( inst.yearshtml ){
++                      origyearshtml = inst.yearshtml;
++                      setTimeout(function(){
++                              //assure that inst.yearshtml didn't change.
++                              if( origyearshtml === inst.yearshtml && inst.yearshtml ){
++                                      inst.dpDiv.find("select.ui-datepicker-year:first").replaceWith(inst.yearshtml);
++                              }
++                              origyearshtml = inst.yearshtml = null;
++                      }, 0);
++              }
++      },
++
++      // #6694 - don't focus the input if it's already focused
++      // this breaks the change event in IE
++      // Support: IE and jQuery <1.9
++      _shouldFocusInput: function( inst ) {
++              return inst.input && inst.input.is( ":visible" ) && !inst.input.is( ":disabled" ) && !inst.input.is( ":focus" );
++      },
++
++      /* Check positioning to remain on screen. */
++      _checkOffset: function(inst, offset, isFixed) {
++              var dpWidth = inst.dpDiv.outerWidth(),
++                      dpHeight = inst.dpDiv.outerHeight(),
++                      inputWidth = inst.input ? inst.input.outerWidth() : 0,
++                      inputHeight = inst.input ? inst.input.outerHeight() : 0,
++                      viewWidth = document.documentElement.clientWidth + (isFixed ? 0 : $(document).scrollLeft()),
++                      viewHeight = document.documentElement.clientHeight + (isFixed ? 0 : $(document).scrollTop());
++
++              offset.left -= (this._get(inst, "isRTL") ? (dpWidth - inputWidth) : 0);
++              offset.left -= (isFixed && offset.left === inst.input.offset().left) ? $(document).scrollLeft() : 0;
++              offset.top -= (isFixed && offset.top === (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0;
++
++              // now check if datepicker is showing outside window viewport - move to a better place if so.
++              offset.left -= Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ?
++                      Math.abs(offset.left + dpWidth - viewWidth) : 0);
++              offset.top -= Math.min(offset.top, (offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ?
++                      Math.abs(dpHeight + inputHeight) : 0);
++
++              return offset;
++      },
++
++      /* Find an object's position on the screen. */
++      _findPos: function(obj) {
++              var position,
++                      inst = this._getInst(obj),
++                      isRTL = this._get(inst, "isRTL");
++
++              while (obj && (obj.type === "hidden" || obj.nodeType !== 1 || $.expr.filters.hidden(obj))) {
++                      obj = obj[isRTL ? "previousSibling" : "nextSibling"];
++              }
++
++              position = $(obj).offset();
++              return [position.left, position.top];
++      },
++
++      /* Hide the date picker from view.
++       * @param  input  element - the input field attached to the date picker
++       */
++      _hideDatepicker: function(input) {
++              var showAnim, duration, postProcess, onClose,
++                      inst = this._curInst;
++
++              if (!inst || (input && inst !== $.data(input, "datepicker"))) {
++                      return;
++              }
++
++              if (this._datepickerShowing) {
++                      showAnim = this._get(inst, "showAnim");
++                      duration = this._get(inst, "duration");
++                      postProcess = function() {
++                              $.datepicker._tidyDialog(inst);
++                      };
++
++                      // DEPRECATED: after BC for 1.8.x $.effects[ showAnim ] is not needed
++                      if ( $.effects && ( $.effects.effect[ showAnim ] || $.effects[ showAnim ] ) ) {
++                              inst.dpDiv.hide(showAnim, $.datepicker._get(inst, "showOptions"), duration, postProcess);
++                      } else {
++                              inst.dpDiv[(showAnim === "slideDown" ? "slideUp" :
++                                      (showAnim === "fadeIn" ? "fadeOut" : "hide"))]((showAnim ? duration : null), postProcess);
++                      }
++
++                      if (!showAnim) {
++                              postProcess();
++                      }
++                      this._datepickerShowing = false;
++
++                      onClose = this._get(inst, "onClose");
++                      if (onClose) {
++                              onClose.apply((inst.input ? inst.input[0] : null), [(inst.input ? inst.input.val() : ""), inst]);
++                      }
++
++                      this._lastInput = null;
++                      if (this._inDialog) {
++                              this._dialogInput.css({ position: "absolute", left: "0", top: "-100px" });
++                              if ($.blockUI) {
++                                      $.unblockUI();
++                                      $("body").append(this.dpDiv);
++                              }
++                      }
++                      this._inDialog = false;
++              }
++      },
++
++      /* Tidy up after a dialog display. */
++      _tidyDialog: function(inst) {
++              inst.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar");
++      },
++
++      /* Close date picker if clicked elsewhere. */
++      _checkExternalClick: function(event) {
++              if (!$.datepicker._curInst) {
++                      return;
++              }
++
++              var $target = $(event.target),
++                      inst = $.datepicker._getInst($target[0]);
++
++              if ( ( ( $target[0].id !== $.datepicker._mainDivId &&
++                              $target.parents("#" + $.datepicker._mainDivId).length === 0 &&
++                              !$target.hasClass($.datepicker.markerClassName) &&
++                              !$target.closest("." + $.datepicker._triggerClass).length &&
++                              $.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI) ) ) ||
++                      ( $target.hasClass($.datepicker.markerClassName) && $.datepicker._curInst !== inst ) ) {
++                              $.datepicker._hideDatepicker();
++              }
++      },
++
++      /* Adjust one of the date sub-fields. */
++      _adjustDate: function(id, offset, period) {
++              var target = $(id),
++                      inst = this._getInst(target[0]);
++
++              if (this._isDisabledDatepicker(target[0])) {
++                      return;
++              }
++              this._adjustInstDate(inst, offset +
++                      (period === "M" ? this._get(inst, "showCurrentAtPos") : 0), // undo positioning
++                      period);
++              this._updateDatepicker(inst);
++      },
++
++      /* Action for current link. */
++      _gotoToday: function(id) {
++              var date,
++                      target = $(id),
++                      inst = this._getInst(target[0]);
++
++              if (this._get(inst, "gotoCurrent") && inst.currentDay) {
++                      inst.selectedDay = inst.currentDay;
++                      inst.drawMonth = inst.selectedMonth = inst.currentMonth;
++                      inst.drawYear = inst.selectedYear = inst.currentYear;
++              } else {
++                      date = new Date();
++                      inst.selectedDay = date.getDate();
++                      inst.drawMonth = inst.selectedMonth = date.getMonth();
++                      inst.drawYear = inst.selectedYear = date.getFullYear();
++              }
++              this._notifyChange(inst);
++              this._adjustDate(target);
++      },
++
++      /* Action for selecting a new month/year. */
++      _selectMonthYear: function(id, select, period) {
++              var target = $(id),
++                      inst = this._getInst(target[0]);
++
++              inst["selected" + (period === "M" ? "Month" : "Year")] =
++              inst["draw" + (period === "M" ? "Month" : "Year")] =
++                      parseInt(select.options[select.selectedIndex].value,10);
++
++              this._notifyChange(inst);
++              this._adjustDate(target);
++      },
++
++      /* Action for selecting a day. */
++      _selectDay: function(id, month, year, td) {
++              var inst,
++                      target = $(id);
++
++              if ($(td).hasClass(this._unselectableClass) || this._isDisabledDatepicker(target[0])) {
++                      return;
++              }
++
++              inst = this._getInst(target[0]);
++              inst.selectedDay = inst.currentDay = $("a", td).html();
++              inst.selectedMonth = inst.currentMonth = month;
++              inst.selectedYear = inst.currentYear = year;
++              this._selectDate(id, this._formatDate(inst,
++                      inst.currentDay, inst.currentMonth, inst.currentYear));
++      },
++
++      /* Erase the input field and hide the date picker. */
++      _clearDate: function(id) {
++              var target = $(id);
++              this._selectDate(target, "");
++      },
++
++      /* Update the input field with the selected date. */
++      _selectDate: function(id, dateStr) {
++              var onSelect,
++                      target = $(id),
++                      inst = this._getInst(target[0]);
++
++              dateStr = (dateStr != null ? dateStr : this._formatDate(inst));
++              if (inst.input) {
++                      inst.input.val(dateStr);
++              }
++              this._updateAlternate(inst);
++
++              onSelect = this._get(inst, "onSelect");
++              if (onSelect) {
++                      onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]);  // trigger custom callback
++              } else if (inst.input) {
++                      inst.input.trigger("change"); // fire the change event
++              }
++
++              if (inst.inline){
++                      this._updateDatepicker(inst);
++              } else {
++                      this._hideDatepicker();
++                      this._lastInput = inst.input[0];
++                      if (typeof(inst.input[0]) !== "object") {
++                              inst.input.focus(); // restore focus
++                      }
++                      this._lastInput = null;
++              }
++      },
++
++      /* Update any alternate field to synchronise with the main field. */
++      _updateAlternate: function(inst) {
++              var altFormat, date, dateStr,
++                      altField = this._get(inst, "altField");
++
++              if (altField) { // update alternate field too
++                      altFormat = this._get(inst, "altFormat") || this._get(inst, "dateFormat");
++                      date = this._getDate(inst);
++                      dateStr = this.formatDate(altFormat, date, this._getFormatConfig(inst));
++                      $(altField).each(function() { $(this).val(dateStr); });
++              }
++      },
++
++      /* Set as beforeShowDay function to prevent selection of weekends.
++       * @param  date  Date - the date to customise
++       * @return [boolean, string] - is this date selectable?, what is its CSS class?
++       */
++      noWeekends: function(date) {
++              var day = date.getDay();
++              return [(day > 0 && day < 6), ""];
++      },
++
++      /* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
++       * @param  date  Date - the date to get the week for
++       * @return  number - the number of the week within the year that contains this date
++       */
++      iso8601Week: function(date) {
++              var time,
++                      checkDate = new Date(date.getTime());
++
++              // Find Thursday of this week starting on Monday
++              checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7));
++
++              time = checkDate.getTime();
++              checkDate.setMonth(0); // Compare with Jan 1
++              checkDate.setDate(1);
++              return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
++      },
++
++      /* Parse a string value into a date object.
++       * See formatDate below for the possible formats.
++       *
++       * @param  format string - the expected format of the date
++       * @param  value string - the date in the above format
++       * @param  settings Object - attributes include:
++       *                                      shortYearCutoff  number - the cutoff year for determining the century (optional)
++       *                                      dayNamesShort   string[7] - abbreviated names of the days from Sunday (optional)
++       *                                      dayNames                string[7] - names of the days from Sunday (optional)
++       *                                      monthNamesShort string[12] - abbreviated names of the months (optional)
++       *                                      monthNames              string[12] - names of the months (optional)
++       * @return  Date - the extracted date value or null if value is blank
++       */
++      parseDate: function (format, value, settings) {
++              if (format == null || value == null) {
++                      throw "Invalid arguments";
++              }
++
++              value = (typeof value === "object" ? value.toString() : value + "");
++              if (value === "") {
++                      return null;
++              }
++
++              var iFormat, dim, extra,
++                      iValue = 0,
++                      shortYearCutoffTemp = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff,
++                      shortYearCutoff = (typeof shortYearCutoffTemp !== "string" ? shortYearCutoffTemp :
++                              new Date().getFullYear() % 100 + parseInt(shortYearCutoffTemp, 10)),
++                      dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort,
++                      dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames,
++                      monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort,
++                      monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames,
++                      year = -1,
++                      month = -1,
++                      day = -1,
++                      doy = -1,
++                      literal = false,
++                      date,
++                      // Check whether a format character is doubled
++                      lookAhead = function(match) {
++                              var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
++                              if (matches) {
++                                      iFormat++;
++                              }
++                              return matches;
++                      },
++                      // Extract a number from the string value
++                      getNumber = function(match) {
++                              var isDoubled = lookAhead(match),
++                                      size = (match === "@" ? 14 : (match === "!" ? 20 :
++                                      (match === "y" && isDoubled ? 4 : (match === "o" ? 3 : 2)))),
++                                      minSize = (match === "y" ? size : 1),
++                                      digits = new RegExp("^\\d{" + minSize + "," + size + "}"),
++                                      num = value.substring(iValue).match(digits);
++                              if (!num) {
++                                      throw "Missing number at position " + iValue;
++                              }
++                              iValue += num[0].length;
++                              return parseInt(num[0], 10);
++                      },
++                      // Extract a name from the string value and convert to an index
++                      getName = function(match, shortNames, longNames) {
++                              var index = -1,
++                                      names = $.map(lookAhead(match) ? longNames : shortNames, function (v, k) {
++                                              return [ [k, v] ];
++                                      }).sort(function (a, b) {
++                                              return -(a[1].length - b[1].length);
++                                      });
++
++                              $.each(names, function (i, pair) {
++                                      var name = pair[1];
++                                      if (value.substr(iValue, name.length).toLowerCase() === name.toLowerCase()) {
++                                              index = pair[0];
++                                              iValue += name.length;
++                                              return false;
++                                      }
++                              });
++                              if (index !== -1) {
++                                      return index + 1;
++                              } else {
++                                      throw "Unknown name at position " + iValue;
++                              }
++                      },
++                      // Confirm that a literal character matches the string value
++                      checkLiteral = function() {
++                              if (value.charAt(iValue) !== format.charAt(iFormat)) {
++                                      throw "Unexpected literal at position " + iValue;
++                              }
++                              iValue++;
++                      };
++
++              for (iFormat = 0; iFormat < format.length; iFormat++) {
++                      if (literal) {
++                              if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
++                                      literal = false;
++                              } else {
++                                      checkLiteral();
++                              }
++                      } else {
++                              switch (format.charAt(iFormat)) {
++                                      case "d":
++                                              day = getNumber("d");
++                                              break;
++                                      case "D":
++                                              getName("D", dayNamesShort, dayNames);
++                                              break;
++                                      case "o":
++                                              doy = getNumber("o");
++                                              break;
++                                      case "m":
++                                              month = getNumber("m");
++                                              break;
++                                      case "M":
++                                              month = getName("M", monthNamesShort, monthNames);
++                                              break;
++                                      case "y":
++                                              year = getNumber("y");
++                                              break;
++                                      case "@":
++                                              date = new Date(getNumber("@"));
++                                              year = date.getFullYear();
++                                              month = date.getMonth() + 1;
++                                              day = date.getDate();
++                                              break;
++                                      case "!":
++                                              date = new Date((getNumber("!") - this._ticksTo1970) / 10000);
++                                              year = date.getFullYear();
++                                              month = date.getMonth() + 1;
++                                              day = date.getDate();
++                                              break;
++                                      case "'":
++                                              if (lookAhead("'")){
++                                                      checkLiteral();
++                                              } else {
++                                                      literal = true;
++                                              }
++                                              break;
++                                      default:
++                                              checkLiteral();
++                              }
++                      }
++              }
++
++              if (iValue < value.length){
++                      extra = value.substr(iValue);
++                      if (!/^\s+/.test(extra)) {
++                              throw "Extra/unparsed characters found in date: " + extra;
++                      }
++              }
++
++              if (year === -1) {
++                      year = new Date().getFullYear();
++              } else if (year < 100) {
++                      year += new Date().getFullYear() - new Date().getFullYear() % 100 +
++                              (year <= shortYearCutoff ? 0 : -100);
++              }
++
++              if (doy > -1) {
++                      month = 1;
++                      day = doy;
++                      do {
++                              dim = this._getDaysInMonth(year, month - 1);
++                              if (day <= dim) {
++                                      break;
++                              }
++                              month++;
++                              day -= dim;
++                      } while (true);
++              }
++
++              date = this._daylightSavingAdjust(new Date(year, month - 1, day));
++              if (date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day) {
++                      throw "Invalid date"; // E.g. 31/02/00
++              }
++              return date;
++      },
++
++      /* Standard date formats. */
++      ATOM: "yy-mm-dd", // RFC 3339 (ISO 8601)
++      COOKIE: "D, dd M yy",
++      ISO_8601: "yy-mm-dd",
++      RFC_822: "D, d M y",
++      RFC_850: "DD, dd-M-y",
++      RFC_1036: "D, d M y",
++      RFC_1123: "D, d M yy",
++      RFC_2822: "D, d M yy",
++      RSS: "D, d M y", // RFC 822
++      TICKS: "!",
++      TIMESTAMP: "@",
++      W3C: "yy-mm-dd", // ISO 8601
++
++      _ticksTo1970: (((1970 - 1) * 365 + Math.floor(1970 / 4) - Math.floor(1970 / 100) +
++              Math.floor(1970 / 400)) * 24 * 60 * 60 * 10000000),
++
++      /* Format a date object into a string value.
++       * The format can be combinations of the following:
++       * d  - day of month (no leading zero)
++       * dd - day of month (two digit)
++       * o  - day of year (no leading zeros)
++       * oo - day of year (three digit)
++       * D  - day name short
++       * DD - day name long
++       * m  - month of year (no leading zero)
++       * mm - month of year (two digit)
++       * M  - month name short
++       * MM - month name long
++       * y  - year (two digit)
++       * yy - year (four digit)
++       * @ - Unix timestamp (ms since 01/01/1970)
++       * ! - Windows ticks (100ns since 01/01/0001)
++       * "..." - literal text
++       * '' - single quote
++       *
++       * @param  format string - the desired format of the date
++       * @param  date Date - the date value to format
++       * @param  settings Object - attributes include:
++       *                                      dayNamesShort   string[7] - abbreviated names of the days from Sunday (optional)
++       *                                      dayNames                string[7] - names of the days from Sunday (optional)
++       *                                      monthNamesShort string[12] - abbreviated names of the months (optional)
++       *                                      monthNames              string[12] - names of the months (optional)
++       * @return  string - the date in the above format
++       */
++      formatDate: function (format, date, settings) {
++              if (!date) {
++                      return "";
++              }
++
++              var iFormat,
++                      dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort,
++                      dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames,
++                      monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort,
++                      monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames,
++                      // Check whether a format character is doubled
++                      lookAhead = function(match) {
++                              var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
++                              if (matches) {
++                                      iFormat++;
++                              }
++                              return matches;
++                      },
++                      // Format a number, with leading zero if necessary
++                      formatNumber = function(match, value, len) {
++                              var num = "" + value;
++                              if (lookAhead(match)) {
++                                      while (num.length < len) {
++                                              num = "0" + num;
++                                      }
++                              }
++                              return num;
++                      },
++                      // Format a name, short or long as requested
++                      formatName = function(match, value, shortNames, longNames) {
++                              return (lookAhead(match) ? longNames[value] : shortNames[value]);
++                      },
++                      output = "",
++                      literal = false;
++
++              if (date) {
++                      for (iFormat = 0; iFormat < format.length; iFormat++) {
++                              if (literal) {
++                                      if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
++                                              literal = false;
++                                      } else {
++                                              output += format.charAt(iFormat);
++                                      }
++                              } else {
++                                      switch (format.charAt(iFormat)) {
++                                              case "d":
++                                                      output += formatNumber("d", date.getDate(), 2);
++                                                      break;
++                                              case "D":
++                                                      output += formatName("D", date.getDay(), dayNamesShort, dayNames);
++                                                      break;
++                                              case "o":
++                                                      output += formatNumber("o",
++                                                              Math.round((new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000), 3);
++                                                      break;
++                                              case "m":
++                                                      output += formatNumber("m", date.getMonth() + 1, 2);
++                                                      break;
++                                              case "M":
++                                                      output += formatName("M", date.getMonth(), monthNamesShort, monthNames);
++                                                      break;
++                                              case "y":
++                                                      output += (lookAhead("y") ? date.getFullYear() :
++                                                              (date.getYear() % 100 < 10 ? "0" : "") + date.getYear() % 100);
++                                                      break;
++                                              case "@":
++                                                      output += date.getTime();
++                                                      break;
++                                              case "!":
++                                                      output += date.getTime() * 10000 + this._ticksTo1970;
++                                                      break;
++                                              case "'":
++                                                      if (lookAhead("'")) {
++                                                              output += "'";
++                                                      } else {
++                                                              literal = true;
++                                                      }
++                                                      break;
++                                              default:
++                                                      output += format.charAt(iFormat);
++                                      }
++                              }
++                      }
++              }
++              return output;
++      },
++
++      /* Extract all possible characters from the date format. */
++      _possibleChars: function (format) {
++              var iFormat,
++                      chars = "",
++                      literal = false,
++                      // Check whether a format character is doubled
++                      lookAhead = function(match) {
++                              var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
++                              if (matches) {
++                                      iFormat++;
++                              }
++                              return matches;
++                      };
++
++              for (iFormat = 0; iFormat < format.length; iFormat++) {
++                      if (literal) {
++                              if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
++                                      literal = false;
++                              } else {
++                                      chars += format.charAt(iFormat);
++                              }
++                      } else {
++                              switch (format.charAt(iFormat)) {
++                                      case "d": case "m": case "y": case "@":
++                                              chars += "0123456789";
++                                              break;
++                                      case "D": case "M":
++                                              return null; // Accept anything
++                                      case "'":
++                                              if (lookAhead("'")) {
++                                                      chars += "'";
++                                              } else {
++                                                      literal = true;
++                                              }
++                                              break;
++                                      default:
++                                              chars += format.charAt(iFormat);
++                              }
++                      }
++              }
++              return chars;
++      },
++
++      /* Get a setting value, defaulting if necessary. */
++      _get: function(inst, name) {
++              return inst.settings[name] !== undefined ?
++                      inst.settings[name] : this._defaults[name];
++      },
++
++      /* Parse existing date and initialise date picker. */
++      _setDateFromField: function(inst, noDefault) {
++              if (inst.input.val() === inst.lastVal) {
++                      return;
++              }
++
++              var dateFormat = this._get(inst, "dateFormat"),
++                      dates = inst.lastVal = inst.input ? inst.input.val() : null,
++                      defaultDate = this._getDefaultDate(inst),
++                      date = defaultDate,
++                      settings = this._getFormatConfig(inst);
++
++              try {
++                      date = this.parseDate(dateFormat, dates, settings) || defaultDate;
++              } catch (event) {
++                      dates = (noDefault ? "" : dates);
++              }
++              inst.selectedDay = date.getDate();
++              inst.drawMonth = inst.selectedMonth = date.getMonth();
++              inst.drawYear = inst.selectedYear = date.getFullYear();
++              inst.currentDay = (dates ? date.getDate() : 0);
++              inst.currentMonth = (dates ? date.getMonth() : 0);
++              inst.currentYear = (dates ? date.getFullYear() : 0);
++              this._adjustInstDate(inst);
++      },
++
++      /* Retrieve the default date shown on opening. */
++      _getDefaultDate: function(inst) {
++              return this._restrictMinMax(inst,
++                      this._determineDate(inst, this._get(inst, "defaultDate"), new Date()));
++      },
++
++      /* A date may be specified as an exact value or a relative one. */
++      _determineDate: function(inst, date, defaultDate) {
++              var offsetNumeric = function(offset) {
++                              var date = new Date();
++                              date.setDate(date.getDate() + offset);
++                              return date;
++                      },
++                      offsetString = function(offset) {
++                              try {
++                                      return $.datepicker.parseDate($.datepicker._get(inst, "dateFormat"),
++                                              offset, $.datepicker._getFormatConfig(inst));
++                              }
++                              catch (e) {
++                                      // Ignore
++                              }
++
++                              var date = (offset.toLowerCase().match(/^c/) ?
++                                      $.datepicker._getDate(inst) : null) || new Date(),
++                                      year = date.getFullYear(),
++                                      month = date.getMonth(),
++                                      day = date.getDate(),
++                                      pattern = /([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,
++                                      matches = pattern.exec(offset);
++
++                              while (matches) {
++                                      switch (matches[2] || "d") {
++                                              case "d" : case "D" :
++                                                      day += parseInt(matches[1],10); break;
++                                              case "w" : case "W" :
++                                                      day += parseInt(matches[1],10) * 7; break;
++                                              case "m" : case "M" :
++                                                      month += parseInt(matches[1],10);
++                                                      day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
++                                                      break;
++                                              case "y": case "Y" :
++                                                      year += parseInt(matches[1],10);
++                                                      day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
++                                                      break;
++                                      }
++                                      matches = pattern.exec(offset);
++                              }
++                              return new Date(year, month, day);
++                      },
++                      newDate = (date == null || date === "" ? defaultDate : (typeof date === "string" ? offsetString(date) :
++                              (typeof date === "number" ? (isNaN(date) ? defaultDate : offsetNumeric(date)) : new Date(date.getTime()))));
++
++              newDate = (newDate && newDate.toString() === "Invalid Date" ? defaultDate : newDate);
++              if (newDate) {
++                      newDate.setHours(0);
++                      newDate.setMinutes(0);
++                      newDate.setSeconds(0);
++                      newDate.setMilliseconds(0);
++              }
++              return this._daylightSavingAdjust(newDate);
++      },
++
++      /* Handle switch to/from daylight saving.
++       * Hours may be non-zero on daylight saving cut-over:
++       * > 12 when midnight changeover, but then cannot generate
++       * midnight datetime, so jump to 1AM, otherwise reset.
++       * @param  date  (Date) the date to check
++       * @return  (Date) the corrected date
++       */
++      _daylightSavingAdjust: function(date) {
++              if (!date) {
++                      return null;
++              }
++              date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0);
++              return date;
++      },
++
++      /* Set the date(s) directly. */
++      _setDate: function(inst, date, noChange) {
++              var clear = !date,
++                      origMonth = inst.selectedMonth,
++                      origYear = inst.selectedYear,
++                      newDate = this._restrictMinMax(inst, this._determineDate(inst, date, new Date()));
++
++              inst.selectedDay = inst.currentDay = newDate.getDate();
++              inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth();
++              inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear();
++              if ((origMonth !== inst.selectedMonth || origYear !== inst.selectedYear) && !noChange) {
++                      this._notifyChange(inst);
++              }
++              this._adjustInstDate(inst);
++              if (inst.input) {
++                      inst.input.val(clear ? "" : this._formatDate(inst));
++              }
++      },
++
++      /* Retrieve the date(s) directly. */
++      _getDate: function(inst) {
++              var startDate = (!inst.currentYear || (inst.input && inst.input.val() === "") ? null :
++                      this._daylightSavingAdjust(new Date(
++                      inst.currentYear, inst.currentMonth, inst.currentDay)));
++                      return startDate;
++      },
++
++      /* Attach the onxxx handlers.  These are declared statically so
++       * they work with static code transformers like Caja.
++       */
++      _attachHandlers: function(inst) {
++              var stepMonths = this._get(inst, "stepMonths"),
++                      id = "#" + inst.id.replace( /\\\\/g, "\\" );
++              inst.dpDiv.find("[data-handler]").map(function () {
++                      var handler = {
++                              prev: function () {
++                                      $.datepicker._adjustDate(id, -stepMonths, "M");
++                              },
++                              next: function () {
++                                      $.datepicker._adjustDate(id, +stepMonths, "M");
++                              },
++                              hide: function () {
++                                      $.datepicker._hideDatepicker();
++                              },
++                              today: function () {
++                                      $.datepicker._gotoToday(id);
++                              },
++                              selectDay: function () {
++                                      $.datepicker._selectDay(id, +this.getAttribute("data-month"), +this.getAttribute("data-year"), this);
++                                      return false;
++                              },
++                              selectMonth: function () {
++                                      $.datepicker._selectMonthYear(id, this, "M");
++                                      return false;
++                              },
++                              selectYear: function () {
++                                      $.datepicker._selectMonthYear(id, this, "Y");
++                                      return false;
++                              }
++                      };
++                      $(this).bind(this.getAttribute("data-event"), handler[this.getAttribute("data-handler")]);
++              });
++      },
++
++      /* Generate the HTML for the current state of the date picker. */
++      _generateHTML: function(inst) {
++              var maxDraw, prevText, prev, nextText, next, currentText, gotoDate,
++                      controls, buttonPanel, firstDay, showWeek, dayNames, dayNamesMin,
++                      monthNames, monthNamesShort, beforeShowDay, showOtherMonths,
++                      selectOtherMonths, defaultDate, html, dow, row, group, col, selectedDate,
++                      cornerClass, calender, thead, day, daysInMonth, leadDays, curRows, numRows,
++                      printDate, dRow, tbody, daySettings, otherMonth, unselectable,
++                      tempDate = new Date(),
++                      today = this._daylightSavingAdjust(
++                              new Date(tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate())), // clear time
++                      isRTL = this._get(inst, "isRTL"),
++                      showButtonPanel = this._get(inst, "showButtonPanel"),
++                      hideIfNoPrevNext = this._get(inst, "hideIfNoPrevNext"),
++                      navigationAsDateFormat = this._get(inst, "navigationAsDateFormat"),
++                      numMonths = this._getNumberOfMonths(inst),
++                      showCurrentAtPos = this._get(inst, "showCurrentAtPos"),
++                      stepMonths = this._get(inst, "stepMonths"),
++                      isMultiMonth = (numMonths[0] !== 1 || numMonths[1] !== 1),
++                      currentDate = this._daylightSavingAdjust((!inst.currentDay ? new Date(9999, 9, 9) :
++                              new Date(inst.currentYear, inst.currentMonth, inst.currentDay))),
++                      minDate = this._getMinMaxDate(inst, "min"),
++                      maxDate = this._getMinMaxDate(inst, "max"),
++                      drawMonth = inst.drawMonth - showCurrentAtPos,
++                      drawYear = inst.drawYear;
++
++              if (drawMonth < 0) {
++                      drawMonth += 12;
++                      drawYear--;
++              }
++              if (maxDate) {
++                      maxDraw = this._daylightSavingAdjust(new Date(maxDate.getFullYear(),
++                              maxDate.getMonth() - (numMonths[0] * numMonths[1]) + 1, maxDate.getDate()));
++                      maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw);
++                      while (this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1)) > maxDraw) {
++                              drawMonth--;
++                              if (drawMonth < 0) {
++                                      drawMonth = 11;
++                                      drawYear--;
++                              }
++                      }
++              }
++              inst.drawMonth = drawMonth;
++              inst.drawYear = drawYear;
++
++              prevText = this._get(inst, "prevText");
++              prevText = (!navigationAsDateFormat ? prevText : this.formatDate(prevText,
++                      this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepMonths, 1)),
++                      this._getFormatConfig(inst)));
++
++              prev = (this._canAdjustMonth(inst, -1, drawYear, drawMonth) ?
++                      "<a class='ui-datepicker-prev ui-corner-all' data-handler='prev' data-event='click'" +
++                      " title='" + prevText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "e" : "w") + "'>" + prevText + "</span></a>" :
++                      (hideIfNoPrevNext ? "" : "<a class='ui-datepicker-prev ui-corner-all ui-state-disabled' title='"+ prevText +"'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "e" : "w") + "'>" + prevText + "</span></a>"));
++
++              nextText = this._get(inst, "nextText");
++              nextText = (!navigationAsDateFormat ? nextText : this.formatDate(nextText,
++                      this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepMonths, 1)),
++                      this._getFormatConfig(inst)));
++
++              next = (this._canAdjustMonth(inst, +1, drawYear, drawMonth) ?
++                      "<a class='ui-datepicker-next ui-corner-all' data-handler='next' data-event='click'" +
++                      " title='" + nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "w" : "e") + "'>" + nextText + "</span></a>" :
++                      (hideIfNoPrevNext ? "" : "<a class='ui-datepicker-next ui-corner-all ui-state-disabled' title='"+ nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "w" : "e") + "'>" + nextText + "</span></a>"));
++
++              currentText = this._get(inst, "currentText");
++              gotoDate = (this._get(inst, "gotoCurrent") && inst.currentDay ? currentDate : today);
++              currentText = (!navigationAsDateFormat ? currentText :
++                      this.formatDate(currentText, gotoDate, this._getFormatConfig(inst)));
++
++              controls = (!inst.inline ? "<button type='button' class='ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all' data-handler='hide' data-event='click'>" +
++                      this._get(inst, "closeText") + "</button>" : "");
++
++              buttonPanel = (showButtonPanel) ? "<div class='ui-datepicker-buttonpane ui-widget-content'>" + (isRTL ? controls : "") +
++                      (this._isInRange(inst, gotoDate) ? "<button type='button' class='ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all' data-handler='today' data-event='click'" +
++                      ">" + currentText + "</button>" : "") + (isRTL ? "" : controls) + "</div>" : "";
++
++              firstDay = parseInt(this._get(inst, "firstDay"),10);
++              firstDay = (isNaN(firstDay) ? 0 : firstDay);
++
++              showWeek = this._get(inst, "showWeek");
++              dayNames = this._get(inst, "dayNames");
++              dayNamesMin = this._get(inst, "dayNamesMin");
++              monthNames = this._get(inst, "monthNames");
++              monthNamesShort = this._get(inst, "monthNamesShort");
++              beforeShowDay = this._get(inst, "beforeShowDay");
++              showOtherMonths = this._get(inst, "showOtherMonths");
++              selectOtherMonths = this._get(inst, "selectOtherMonths");
++              defaultDate = this._getDefaultDate(inst);
++              html = "";
++              dow;
++              for (row = 0; row < numMonths[0]; row++) {
++                      group = "";
++                      this.maxRows = 4;
++                      for (col = 0; col < numMonths[1]; col++) {
++                              selectedDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.selectedDay));
++                              cornerClass = " ui-corner-all";
++                              calender = "";
++                              if (isMultiMonth) {
++                                      calender += "<div class='ui-datepicker-group";
++                                      if (numMonths[1] > 1) {
++                                              switch (col) {
++                                                      case 0: calender += " ui-datepicker-group-first";
++                                                              cornerClass = " ui-corner-" + (isRTL ? "right" : "left"); break;
++                                                      case numMonths[1]-1: calender += " ui-datepicker-group-last";
++                                                              cornerClass = " ui-corner-" + (isRTL ? "left" : "right"); break;
++                                                      default: calender += " ui-datepicker-group-middle"; cornerClass = ""; break;
++                                              }
++                                      }
++                                      calender += "'>";
++                              }
++                              calender += "<div class='ui-datepicker-header ui-widget-header ui-helper-clearfix" + cornerClass + "'>" +
++                                      (/all|left/.test(cornerClass) && row === 0 ? (isRTL ? next : prev) : "") +
++                                      (/all|right/.test(cornerClass) && row === 0 ? (isRTL ? prev : next) : "") +
++                                      this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate,
++                                      row > 0 || col > 0, monthNames, monthNamesShort) + // draw month headers
++                                      "</div><table class='ui-datepicker-calendar'><thead>" +
++                                      "<tr>";
++                              thead = (showWeek ? "<th class='ui-datepicker-week-col'>" + this._get(inst, "weekHeader") + "</th>" : "");
++                              for (dow = 0; dow < 7; dow++) { // days of the week
++                                      day = (dow + firstDay) % 7;
++                                      thead += "<th scope='col'" + ((dow + firstDay + 6) % 7 >= 5 ? " class='ui-datepicker-week-end'" : "") + ">" +
++                                              "<span title='" + dayNames[day] + "'>" + dayNamesMin[day] + "</span></th>";
++                              }
++                              calender += thead + "</tr></thead><tbody>";
++                              daysInMonth = this._getDaysInMonth(drawYear, drawMonth);
++                              if (drawYear === inst.selectedYear && drawMonth === inst.selectedMonth) {
++                                      inst.selectedDay = Math.min(inst.selectedDay, daysInMonth);
++                              }
++                              leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7;
++                              curRows = Math.ceil((leadDays + daysInMonth) / 7); // calculate the number of rows to generate
++                              numRows = (isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows); //If multiple months, use the higher number of rows (see #7043)
++                              this.maxRows = numRows;
++                              printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays));
++                              for (dRow = 0; dRow < numRows; dRow++) { // create date picker rows
++                                      calender += "<tr>";
++                                      tbody = (!showWeek ? "" : "<td class='ui-datepicker-week-col'>" +
++                                              this._get(inst, "calculateWeek")(printDate) + "</td>");
++                                      for (dow = 0; dow < 7; dow++) { // create date picker days
++                                              daySettings = (beforeShowDay ?
++                                                      beforeShowDay.apply((inst.input ? inst.input[0] : null), [printDate]) : [true, ""]);
++                                              otherMonth = (printDate.getMonth() !== drawMonth);
++                                              unselectable = (otherMonth && !selectOtherMonths) || !daySettings[0] ||
++                                                      (minDate && printDate < minDate) || (maxDate && printDate > maxDate);
++                                              tbody += "<td class='" +
++                                                      ((dow + firstDay + 6) % 7 >= 5 ? " ui-datepicker-week-end" : "") + // highlight weekends
++                                                      (otherMonth ? " ui-datepicker-other-month" : "") + // highlight days from other months
++                                                      ((printDate.getTime() === selectedDate.getTime() && drawMonth === inst.selectedMonth && inst._keyEvent) || // user pressed key
++                                                      (defaultDate.getTime() === printDate.getTime() && defaultDate.getTime() === selectedDate.getTime()) ?
++                                                      // or defaultDate is current printedDate and defaultDate is selectedDate
++                                                      " " + this._dayOverClass : "") + // highlight selected day
++                                                      (unselectable ? " " + this._unselectableClass + " ui-state-disabled": "") +  // highlight unselectable days
++                                                      (otherMonth && !showOtherMonths ? "" : " " + daySettings[1] + // highlight custom dates
++                                                      (printDate.getTime() === currentDate.getTime() ? " " + this._currentClass : "") + // highlight selected day
++                                                      (printDate.getTime() === today.getTime() ? " ui-datepicker-today" : "")) + "'" + // highlight today (if different)
++                                                      ((!otherMonth || showOtherMonths) && daySettings[2] ? " title='" + daySettings[2].replace(/'/g, "&#39;") + "'" : "") + // cell title
++                                                      (unselectable ? "" : " data-handler='selectDay' data-event='click' data-month='" + printDate.getMonth() + "' data-year='" + printDate.getFullYear() + "'") + ">" + // actions
++                                                      (otherMonth && !showOtherMonths ? "&#xa0;" : // display for other months
++                                                      (unselectable ? "<span class='ui-state-default'>" + printDate.getDate() + "</span>" : "<a class='ui-state-default" +
++                                                      (printDate.getTime() === today.getTime() ? " ui-state-highlight" : "") +
++                                                      (printDate.getTime() === currentDate.getTime() ? " ui-state-active" : "") + // highlight selected day
++                                                      (otherMonth ? " ui-priority-secondary" : "") + // distinguish dates from other months
++                                                      "' href='#'>" + printDate.getDate() + "</a>")) + "</td>"; // display selectable date
++                                              printDate.setDate(printDate.getDate() + 1);
++                                              printDate = this._daylightSavingAdjust(printDate);
++                                      }
++                                      calender += tbody + "</tr>";
++                              }
++                              drawMonth++;
++                              if (drawMonth > 11) {
++                                      drawMonth = 0;
++                                      drawYear++;
++                              }
++                              calender += "</tbody></table>" + (isMultiMonth ? "</div>" +
++                                                      ((numMonths[0] > 0 && col === numMonths[1]-1) ? "<div class='ui-datepicker-row-break'></div>" : "") : "");
++                              group += calender;
++                      }
++                      html += group;
++              }
++              html += buttonPanel;
++              inst._keyEvent = false;
++              return html;
++      },
++
++      /* Generate the month and year header. */
++      _generateMonthYearHeader: function(inst, drawMonth, drawYear, minDate, maxDate,
++                      secondary, monthNames, monthNamesShort) {
++
++              var inMinYear, inMaxYear, month, years, thisYear, determineYear, year, endYear,
++                      changeMonth = this._get(inst, "changeMonth"),
++                      changeYear = this._get(inst, "changeYear"),
++                      showMonthAfterYear = this._get(inst, "showMonthAfterYear"),
++                      html = "<div class='ui-datepicker-title'>",
++                      monthHtml = "";
++
++              // month selection
++              if (secondary || !changeMonth) {
++                      monthHtml += "<span class='ui-datepicker-month'>" + monthNames[drawMonth] + "</span>";
++              } else {
++                      inMinYear = (minDate && minDate.getFullYear() === drawYear);
++                      inMaxYear = (maxDate && maxDate.getFullYear() === drawYear);
++                      monthHtml += "<select class='ui-datepicker-month' data-handler='selectMonth' data-event='change'>";
++                      for ( month = 0; month < 12; month++) {
++                              if ((!inMinYear || month >= minDate.getMonth()) && (!inMaxYear || month <= maxDate.getMonth())) {
++                                      monthHtml += "<option value='" + month + "'" +
++                                              (month === drawMonth ? " selected='selected'" : "") +
++                                              ">" + monthNamesShort[month] + "</option>";
++                              }
++                      }
++                      monthHtml += "</select>";
++              }
++
++              if (!showMonthAfterYear) {
++                      html += monthHtml + (secondary || !(changeMonth && changeYear) ? "&#xa0;" : "");
++              }
++
++              // year selection
++              if ( !inst.yearshtml ) {
++                      inst.yearshtml = "";
++                      if (secondary || !changeYear) {
++                              html += "<span class='ui-datepicker-year'>" + drawYear + "</span>";
++                      } else {
++                              // determine range of years to display
++                              years = this._get(inst, "yearRange").split(":");
++                              thisYear = new Date().getFullYear();
++                              determineYear = function(value) {
++                                      var year = (value.match(/c[+\-].*/) ? drawYear + parseInt(value.substring(1), 10) :
++                                              (value.match(/[+\-].*/) ? thisYear + parseInt(value, 10) :
++                                              parseInt(value, 10)));
++                                      return (isNaN(year) ? thisYear : year);
++                              };
++                              year = determineYear(years[0]);
++                              endYear = Math.max(year, determineYear(years[1] || ""));
++                              year = (minDate ? Math.max(year, minDate.getFullYear()) : year);
++                              endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear);
++                              inst.yearshtml += "<select class='ui-datepicker-year' data-handler='selectYear' data-event='change'>";
++                              for (; year <= endYear; year++) {
++                                      inst.yearshtml += "<option value='" + year + "'" +
++                                              (year === drawYear ? " selected='selected'" : "") +
++                                              ">" + year + "</option>";
++                              }
++                              inst.yearshtml += "</select>";
++
++                              html += inst.yearshtml;
++                              inst.yearshtml = null;
++                      }
++              }
++
++              html += this._get(inst, "yearSuffix");
++              if (showMonthAfterYear) {
++                      html += (secondary || !(changeMonth && changeYear) ? "&#xa0;" : "") + monthHtml;
++              }
++              html += "</div>"; // Close datepicker_header
++              return html;
++      },
++
++      /* Adjust one of the date sub-fields. */
++      _adjustInstDate: function(inst, offset, period) {
++              var year = inst.drawYear + (period === "Y" ? offset : 0),
++                      month = inst.drawMonth + (period === "M" ? offset : 0),
++                      day = Math.min(inst.selectedDay, this._getDaysInMonth(year, month)) + (period === "D" ? offset : 0),
++                      date = this._restrictMinMax(inst, this._daylightSavingAdjust(new Date(year, month, day)));
++
++              inst.selectedDay = date.getDate();
++              inst.drawMonth = inst.selectedMonth = date.getMonth();
++              inst.drawYear = inst.selectedYear = date.getFullYear();
++              if (period === "M" || period === "Y") {
++                      this._notifyChange(inst);
++              }
++      },
++
++      /* Ensure a date is within any min/max bounds. */
++      _restrictMinMax: function(inst, date) {
++              var minDate = this._getMinMaxDate(inst, "min"),
++                      maxDate = this._getMinMaxDate(inst, "max"),
++                      newDate = (minDate && date < minDate ? minDate : date);
++              return (maxDate && newDate > maxDate ? maxDate : newDate);
++      },
++
++      /* Notify change of month/year. */
++      _notifyChange: function(inst) {
++              var onChange = this._get(inst, "onChangeMonthYear");
++              if (onChange) {
++                      onChange.apply((inst.input ? inst.input[0] : null),
++                              [inst.selectedYear, inst.selectedMonth + 1, inst]);
++              }
++      },
++
++      /* Determine the number of months to show. */
++      _getNumberOfMonths: function(inst) {
++              var numMonths = this._get(inst, "numberOfMonths");
++              return (numMonths == null ? [1, 1] : (typeof numMonths === "number" ? [1, numMonths] : numMonths));
++      },
++
++      /* Determine the current maximum date - ensure no time components are set. */
++      _getMinMaxDate: function(inst, minMax) {
++              return this._determineDate(inst, this._get(inst, minMax + "Date"), null);
++      },
++
++      /* Find the number of days in a given month. */
++      _getDaysInMonth: function(year, month) {
++              return 32 - this._daylightSavingAdjust(new Date(year, month, 32)).getDate();
++      },
++
++      /* Find the day of the week of the first of a month. */
++      _getFirstDayOfMonth: function(year, month) {
++              return new Date(year, month, 1).getDay();
++      },
++
++      /* Determines if we should allow a "next/prev" month display change. */
++      _canAdjustMonth: function(inst, offset, curYear, curMonth) {
++              var numMonths = this._getNumberOfMonths(inst),
++                      date = this._daylightSavingAdjust(new Date(curYear,
++                      curMonth + (offset < 0 ? offset : numMonths[0] * numMonths[1]), 1));
++
++              if (offset < 0) {
++                      date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth()));
++              }
++              return this._isInRange(inst, date);
++      },
++
++      /* Is the given date in the accepted range? */
++      _isInRange: function(inst, date) {
++              var yearSplit, currentYear,
++                      minDate = this._getMinMaxDate(inst, "min"),
++                      maxDate = this._getMinMaxDate(inst, "max"),
++                      minYear = null,
++                      maxYear = null,
++                      years = this._get(inst, "yearRange");
++                      if (years){
++                              yearSplit = years.split(":");
++                              currentYear = new Date().getFullYear();
++                              minYear = parseInt(yearSplit[0], 10);
++                              maxYear = parseInt(yearSplit[1], 10);
++                              if ( yearSplit[0].match(/[+\-].*/) ) {
++                                      minYear += currentYear;
++                              }
++                              if ( yearSplit[1].match(/[+\-].*/) ) {
++                                      maxYear += currentYear;
++                              }
++                      }
++
++              return ((!minDate || date.getTime() >= minDate.getTime()) &&
++                      (!maxDate || date.getTime() <= maxDate.getTime()) &&
++                      (!minYear || date.getFullYear() >= minYear) &&
++                      (!maxYear || date.getFullYear() <= maxYear));
++      },
++
++      /* Provide the configuration settings for formatting/parsing. */
++      _getFormatConfig: function(inst) {
++              var shortYearCutoff = this._get(inst, "shortYearCutoff");
++              shortYearCutoff = (typeof shortYearCutoff !== "string" ? shortYearCutoff :
++                      new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
++              return {shortYearCutoff: shortYearCutoff,
++                      dayNamesShort: this._get(inst, "dayNamesShort"), dayNames: this._get(inst, "dayNames"),
++                      monthNamesShort: this._get(inst, "monthNamesShort"), monthNames: this._get(inst, "monthNames")};
++      },
++
++      /* Format the given date for display. */
++      _formatDate: function(inst, day, month, year) {
++              if (!day) {
++                      inst.currentDay = inst.selectedDay;
++                      inst.currentMonth = inst.selectedMonth;
++                      inst.currentYear = inst.selectedYear;
++              }
++              var date = (day ? (typeof day === "object" ? day :
++                      this._daylightSavingAdjust(new Date(year, month, day))) :
++                      this._daylightSavingAdjust(new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
++              return this.formatDate(this._get(inst, "dateFormat"), date, this._getFormatConfig(inst));
++      }
++});
++
++/*
++ * Bind hover events for datepicker elements.
++ * Done via delegate so the binding only occurs once in the lifetime of the parent div.
++ * Global datepicker_instActive, set by _updateDatepicker allows the handlers to find their way back to the active picker.
++ */
++function datepicker_bindHover(dpDiv) {
++      var selector = "button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";
++      return dpDiv.delegate(selector, "mouseout", function() {
++                      $(this).removeClass("ui-state-hover");
++                      if (this.className.indexOf("ui-datepicker-prev") !== -1) {
++                              $(this).removeClass("ui-datepicker-prev-hover");
++                      }
++                      if (this.className.indexOf("ui-datepicker-next") !== -1) {
++                              $(this).removeClass("ui-datepicker-next-hover");
++                      }
++              })
++              .delegate( selector, "mouseover", datepicker_handleMouseover );
++}
++
++function datepicker_handleMouseover() {
++      if (!$.datepicker._isDisabledDatepicker( datepicker_instActive.inline? datepicker_instActive.dpDiv.parent()[0] : datepicker_instActive.input[0])) {
++              $(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover");
++              $(this).addClass("ui-state-hover");
++              if (this.className.indexOf("ui-datepicker-prev") !== -1) {
++                      $(this).addClass("ui-datepicker-prev-hover");
++              }
++              if (this.className.indexOf("ui-datepicker-next") !== -1) {
++                      $(this).addClass("ui-datepicker-next-hover");
++              }
++      }
++}
++
++/* jQuery extend now ignores nulls! */
++function datepicker_extendRemove(target, props) {
++      $.extend(target, props);
++      for (var name in props) {
++              if (props[name] == null) {
++                      target[name] = props[name];
++              }
++      }
++      return target;
++}
++
++/* Invoke the datepicker functionality.
++   @param  options  string - a command, optionally followed by additional parameters or
++                                      Object - settings for attaching new datepicker functionality
++   @return  jQuery object */
++$.fn.datepicker = function(options){
++
++      /* Verify an empty collection wasn't passed - Fixes #6976 */
++      if ( !this.length ) {
++              return this;
++      }
++
++      /* Initialise the date picker. */
++      if (!$.datepicker.initialized) {
++              $(document).mousedown($.datepicker._checkExternalClick);
++              $.datepicker.initialized = true;
++      }
++
++      /* Append datepicker main container to body if not exist. */
++      if ($("#"+$.datepicker._mainDivId).length === 0) {
++              $("body").append($.datepicker.dpDiv);
++      }
++
++      var otherArgs = Array.prototype.slice.call(arguments, 1);
++      if (typeof options === "string" && (options === "isDisabled" || options === "getDate" || options === "widget")) {
++              return $.datepicker["_" + options + "Datepicker"].
++                      apply($.datepicker, [this[0]].concat(otherArgs));
++      }
++      if (options === "option" && arguments.length === 2 && typeof arguments[1] === "string") {
++              return $.datepicker["_" + options + "Datepicker"].
++                      apply($.datepicker, [this[0]].concat(otherArgs));
++      }
++      return this.each(function() {
++              typeof options === "string" ?
++                      $.datepicker["_" + options + "Datepicker"].
++                              apply($.datepicker, [this].concat(otherArgs)) :
++                      $.datepicker._attachDatepicker(this, options);
++      });
++};
++
++$.datepicker = new Datepicker(); // singleton instance
++$.datepicker.initialized = false;
++$.datepicker.uuid = new Date().getTime();
++$.datepicker.version = "1.11.4";
++
++var datepicker = $.datepicker;
++
++
++/*!
++ * jQuery UI Draggable 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/draggable/
++ */
++
++
++$.widget("ui.draggable", $.ui.mouse, {
++      version: "1.11.4",
++      widgetEventPrefix: "drag",
++      options: {
++              addClasses: true,
++              appendTo: "parent",
++              axis: false,
++              connectToSortable: false,
++              containment: false,
++              cursor: "auto",
++              cursorAt: false,
++              grid: false,
++              handle: false,
++              helper: "original",
++              iframeFix: false,
++              opacity: false,
++              refreshPositions: false,
++              revert: false,
++              revertDuration: 500,
++              scope: "default",
++              scroll: true,
++              scrollSensitivity: 20,
++              scrollSpeed: 20,
++              snap: false,
++              snapMode: "both",
++              snapTolerance: 20,
++              stack: false,
++              zIndex: false,
++
++              // callbacks
++              drag: null,
++              start: null,
++              stop: null
++      },
++      _create: function() {
++
++              if ( this.options.helper === "original" ) {
++                      this._setPositionRelative();
++              }
++              if (this.options.addClasses){
++                      this.element.addClass("ui-draggable");
++              }
++              if (this.options.disabled){
++                      this.element.addClass("ui-draggable-disabled");
++              }
++              this._setHandleClassName();
++
++              this._mouseInit();
++      },
++
++      _setOption: function( key, value ) {
++              this._super( key, value );
++              if ( key === "handle" ) {
++                      this._removeHandleClassName();
++                      this._setHandleClassName();
++              }
++      },
++
++      _destroy: function() {
++              if ( ( this.helper || this.element ).is( ".ui-draggable-dragging" ) ) {
++                      this.destroyOnClear = true;
++                      return;
++              }
++              this.element.removeClass( "ui-draggable ui-draggable-dragging ui-draggable-disabled" );
++              this._removeHandleClassName();
++              this._mouseDestroy();
++      },
++
++      _mouseCapture: function(event) {
++              var o = this.options;
++
++              this._blurActiveElement( event );
++
++              // among others, prevent a drag on a resizable-handle
++              if (this.helper || o.disabled || $(event.target).closest(".ui-resizable-handle").length > 0) {
++                      return false;
++              }
++
++              //Quit if we're not on a valid handle
++              this.handle = this._getHandle(event);
++              if (!this.handle) {
++                      return false;
++              }
++
++              this._blockFrames( o.iframeFix === true ? "iframe" : o.iframeFix );
++
++              return true;
++
++      },
++
++      _blockFrames: function( selector ) {
++              this.iframeBlocks = this.document.find( selector ).map(function() {
++                      var iframe = $( this );
++
++                      return $( "<div>" )
++                              .css( "position", "absolute" )
++                              .appendTo( iframe.parent() )
++                              .outerWidth( iframe.outerWidth() )
++                              .outerHeight( iframe.outerHeight() )
++                              .offset( iframe.offset() )[ 0 ];
++              });
++      },
++
++      _unblockFrames: function() {
++              if ( this.iframeBlocks ) {
++                      this.iframeBlocks.remove();
++                      delete this.iframeBlocks;
++              }
++      },
++
++      _blurActiveElement: function( event ) {
++              var document = this.document[ 0 ];
++
++              // Only need to blur if the event occurred on the draggable itself, see #10527
++              if ( !this.handleElement.is( event.target ) ) {
++                      return;
++              }
++
++              // support: IE9
++              // IE9 throws an "Unspecified error" accessing document.activeElement from an <iframe>
++              try {
++
++                      // Support: IE9, IE10
++                      // If the <body> is blurred, IE will switch windows, see #9520
++                      if ( document.activeElement && document.activeElement.nodeName.toLowerCase() !== "body" ) {
++
++                              // Blur any element that currently has focus, see #4261
++                              $( document.activeElement ).blur();
++                      }
++              } catch ( error ) {}
++      },
++
++      _mouseStart: function(event) {
++
++              var o = this.options;
++
++              //Create and append the visible helper
++              this.helper = this._createHelper(event);
++
++              this.helper.addClass("ui-draggable-dragging");
++
++              //Cache the helper size
++              this._cacheHelperProportions();
++
++              //If ddmanager is used for droppables, set the global draggable
++              if ($.ui.ddmanager) {
++                      $.ui.ddmanager.current = this;
++              }
++
++              /*
++               * - Position generation -
++               * This block generates everything position related - it's the core of draggables.
++               */
++
++              //Cache the margins of the original element
++              this._cacheMargins();
++
++              //Store the helper's css position
++              this.cssPosition = this.helper.css( "position" );
++              this.scrollParent = this.helper.scrollParent( true );
++              this.offsetParent = this.helper.offsetParent();
++              this.hasFixedAncestor = this.helper.parents().filter(function() {
++                              return $( this ).css( "position" ) === "fixed";
++                      }).length > 0;
++
++              //The element's absolute position on the page minus margins
++              this.positionAbs = this.element.offset();
++              this._refreshOffsets( event );
++
++              //Generate the original position
++              this.originalPosition = this.position = this._generatePosition( event, false );
++              this.originalPageX = event.pageX;
++              this.originalPageY = event.pageY;
++
++              //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
++              (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
++
++              //Set a containment if given in the options
++              this._setContainment();
++
++              //Trigger event + callbacks
++              if (this._trigger("start", event) === false) {
++                      this._clear();
++                      return false;
++              }
++
++              //Recache the helper size
++              this._cacheHelperProportions();
++
++              //Prepare the droppable offsets
++              if ($.ui.ddmanager && !o.dropBehaviour) {
++                      $.ui.ddmanager.prepareOffsets(this, event);
++              }
++
++              // Reset helper's right/bottom css if they're set and set explicit width/height instead
++              // as this prevents resizing of elements with right/bottom set (see #7772)
++              this._normalizeRightBottom();
++
++              this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
++
++              //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
++              if ( $.ui.ddmanager ) {
++                      $.ui.ddmanager.dragStart(this, event);
++              }
++
++              return true;
++      },
++
++      _refreshOffsets: function( event ) {
++              this.offset = {
++                      top: this.positionAbs.top - this.margins.top,
++                      left: this.positionAbs.left - this.margins.left,
++                      scroll: false,
++                      parent: this._getParentOffset(),
++                      relative: this._getRelativeOffset()
++              };
++
++              this.offset.click = {
++                      left: event.pageX - this.offset.left,
++                      top: event.pageY - this.offset.top
++              };
++      },
++
++      _mouseDrag: function(event, noPropagation) {
++              // reset any necessary cached properties (see #5009)
++              if ( this.hasFixedAncestor ) {
++                      this.offset.parent = this._getParentOffset();
++              }
++
++              //Compute the helpers position
++              this.position = this._generatePosition( event, true );
++              this.positionAbs = this._convertPositionTo("absolute");
++
++              //Call plugins and callbacks and use the resulting position if something is returned
++              if (!noPropagation) {
++                      var ui = this._uiHash();
++                      if (this._trigger("drag", event, ui) === false) {
++                              this._mouseUp({});
++                              return false;
++                      }
++                      this.position = ui.position;
++              }
++
++              this.helper[ 0 ].style.left = this.position.left + "px";
++              this.helper[ 0 ].style.top = this.position.top + "px";
++
++              if ($.ui.ddmanager) {
++                      $.ui.ddmanager.drag(this, event);
++              }
++
++              return false;
++      },
++
++      _mouseStop: function(event) {
++
++              //If we are using droppables, inform the manager about the drop
++              var that = this,
++                      dropped = false;
++              if ($.ui.ddmanager && !this.options.dropBehaviour) {
++                      dropped = $.ui.ddmanager.drop(this, event);
++              }
++
++              //if a drop comes from outside (a sortable)
++              if (this.dropped) {
++                      dropped = this.dropped;
++                      this.dropped = false;
++              }
++
++              if ((this.options.revert === "invalid" && !dropped) || (this.options.revert === "valid" && dropped) || this.options.revert === true || ($.isFunction(this.options.revert) && this.options.revert.call(this.element, dropped))) {
++                      $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
++                              if (that._trigger("stop", event) !== false) {
++                                      that._clear();
++                              }
++                      });
++              } else {
++                      if (this._trigger("stop", event) !== false) {
++                              this._clear();
++                      }
++              }
++
++              return false;
++      },
++
++      _mouseUp: function( event ) {
++              this._unblockFrames();
++
++              //If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003)
++              if ( $.ui.ddmanager ) {
++                      $.ui.ddmanager.dragStop(this, event);
++              }
++
++              // Only need to focus if the event occurred on the draggable itself, see #10527
++              if ( this.handleElement.is( event.target ) ) {
++                      // The interaction is over; whether or not the click resulted in a drag, focus the element
++                      this.element.focus();
++              }
++
++              return $.ui.mouse.prototype._mouseUp.call(this, event);
++      },
++
++      cancel: function() {
++
++              if (this.helper.is(".ui-draggable-dragging")) {
++                      this._mouseUp({});
++              } else {
++                      this._clear();
++              }
++
++              return this;
++
++      },
++
++      _getHandle: function(event) {
++              return this.options.handle ?
++                      !!$( event.target ).closest( this.element.find( this.options.handle ) ).length :
++                      true;
++      },
++
++      _setHandleClassName: function() {
++              this.handleElement = this.options.handle ?
++                      this.element.find( this.options.handle ) : this.element;
++              this.handleElement.addClass( "ui-draggable-handle" );
++      },
++
++      _removeHandleClassName: function() {
++              this.handleElement.removeClass( "ui-draggable-handle" );
++      },
++
++      _createHelper: function(event) {
++
++              var o = this.options,
++                      helperIsFunction = $.isFunction( o.helper ),
++                      helper = helperIsFunction ?
++                              $( o.helper.apply( this.element[ 0 ], [ event ] ) ) :
++                              ( o.helper === "clone" ?
++                                      this.element.clone().removeAttr( "id" ) :
++                                      this.element );
++
++              if (!helper.parents("body").length) {
++                      helper.appendTo((o.appendTo === "parent" ? this.element[0].parentNode : o.appendTo));
++              }
++
++              // http://bugs.jqueryui.com/ticket/9446
++              // a helper function can return the original element
++              // which wouldn't have been set to relative in _create
++              if ( helperIsFunction && helper[ 0 ] === this.element[ 0 ] ) {
++                      this._setPositionRelative();
++              }
++
++              if (helper[0] !== this.element[0] && !(/(fixed|absolute)/).test(helper.css("position"))) {
++                      helper.css("position", "absolute");
++              }
++
++              return helper;
++
++      },
++
++      _setPositionRelative: function() {
++              if ( !( /^(?:r|a|f)/ ).test( this.element.css( "position" ) ) ) {
++                      this.element[ 0 ].style.position = "relative";
++              }
++      },
++
++      _adjustOffsetFromHelper: function(obj) {
++              if (typeof obj === "string") {
++                      obj = obj.split(" ");
++              }
++              if ($.isArray(obj)) {
++                      obj = { left: +obj[0], top: +obj[1] || 0 };
++              }
++              if ("left" in obj) {
++                      this.offset.click.left = obj.left + this.margins.left;
++              }
++              if ("right" in obj) {
++                      this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
++              }
++              if ("top" in obj) {
++                      this.offset.click.top = obj.top + this.margins.top;
++              }
++              if ("bottom" in obj) {
++                      this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
++              }
++      },
++
++      _isRootNode: function( element ) {
++              return ( /(html|body)/i ).test( element.tagName ) || element === this.document[ 0 ];
++      },
++
++      _getParentOffset: function() {
++
++              //Get the offsetParent and cache its position
++              var po = this.offsetParent.offset(),
++                      document = this.document[ 0 ];
++
++              // This is a special case where we need to modify a offset calculated on start, since the following happened:
++              // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
++              // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
++              //    the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
++              if (this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) {
++                      po.left += this.scrollParent.scrollLeft();
++                      po.top += this.scrollParent.scrollTop();
++              }
++
++              if ( this._isRootNode( this.offsetParent[ 0 ] ) ) {
++                      po = { top: 0, left: 0 };
++              }
++
++              return {
++                      top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"), 10) || 0),
++                      left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"), 10) || 0)
++              };
++
++      },
++
++      _getRelativeOffset: function() {
++              if ( this.cssPosition !== "relative" ) {
++                      return { top: 0, left: 0 };
++              }
++
++              var p = this.element.position(),
++                      scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] );
++
++              return {
++                      top: p.top - ( parseInt(this.helper.css( "top" ), 10) || 0 ) + ( !scrollIsRootNode ? this.scrollParent.scrollTop() : 0 ),
++                      left: p.left - ( parseInt(this.helper.css( "left" ), 10) || 0 ) + ( !scrollIsRootNode ? this.scrollParent.scrollLeft() : 0 )
++              };
++
++      },
++
++      _cacheMargins: function() {
++              this.margins = {
++                      left: (parseInt(this.element.css("marginLeft"), 10) || 0),
++                      top: (parseInt(this.element.css("marginTop"), 10) || 0),
++                      right: (parseInt(this.element.css("marginRight"), 10) || 0),
++                      bottom: (parseInt(this.element.css("marginBottom"), 10) || 0)
++              };
++      },
++
++      _cacheHelperProportions: function() {
++              this.helperProportions = {
++                      width: this.helper.outerWidth(),
++                      height: this.helper.outerHeight()
++              };
++      },
++
++      _setContainment: function() {
++
++              var isUserScrollable, c, ce,
++                      o = this.options,
++                      document = this.document[ 0 ];
++
++              this.relativeContainer = null;
++
++              if ( !o.containment ) {
++                      this.containment = null;
++                      return;
++              }
++
++              if ( o.containment === "window" ) {
++                      this.containment = [
++                              $( window ).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
++                              $( window ).scrollTop() - this.offset.relative.top - this.offset.parent.top,
++                              $( window ).scrollLeft() + $( window ).width() - this.helperProportions.width - this.margins.left,
++                              $( window ).scrollTop() + ( $( window ).height() || document.body.parentNode.scrollHeight ) - this.helperProportions.height - this.margins.top
++                      ];
++                      return;
++              }
++
++              if ( o.containment === "document") {
++                      this.containment = [
++                              0,
++                              0,
++                              $( document ).width() - this.helperProportions.width - this.margins.left,
++                              ( $( document ).height() || document.body.parentNode.scrollHeight ) - this.helperProportions.height - this.margins.top
++                      ];
++                      return;
++              }
++
++              if ( o.containment.constructor === Array ) {
++                      this.containment = o.containment;
++                      return;
++              }
++
++              if ( o.containment === "parent" ) {
++                      o.containment = this.helper[ 0 ].parentNode;
++              }
++
++              c = $( o.containment );
++              ce = c[ 0 ];
++
++              if ( !ce ) {
++                      return;
++              }
++
++              isUserScrollable = /(scroll|auto)/.test( c.css( "overflow" ) );
++
++              this.containment = [
++                      ( parseInt( c.css( "borderLeftWidth" ), 10 ) || 0 ) + ( parseInt( c.css( "paddingLeft" ), 10 ) || 0 ),
++                      ( parseInt( c.css( "borderTopWidth" ), 10 ) || 0 ) + ( parseInt( c.css( "paddingTop" ), 10 ) || 0 ),
++                      ( isUserScrollable ? Math.max( ce.scrollWidth, ce.offsetWidth ) : ce.offsetWidth ) -
++                              ( parseInt( c.css( "borderRightWidth" ), 10 ) || 0 ) -
++                              ( parseInt( c.css( "paddingRight" ), 10 ) || 0 ) -
++                              this.helperProportions.width -
++                              this.margins.left -
++                              this.margins.right,
++                      ( isUserScrollable ? Math.max( ce.scrollHeight, ce.offsetHeight ) : ce.offsetHeight ) -
++                              ( parseInt( c.css( "borderBottomWidth" ), 10 ) || 0 ) -
++                              ( parseInt( c.css( "paddingBottom" ), 10 ) || 0 ) -
++                              this.helperProportions.height -
++                              this.margins.top -
++                              this.margins.bottom
++              ];
++              this.relativeContainer = c;
++      },
++
++      _convertPositionTo: function(d, pos) {
++
++              if (!pos) {
++                      pos = this.position;
++              }
++
++              var mod = d === "absolute" ? 1 : -1,
++                      scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] );
++
++              return {
++                      top: (
++                              pos.top +                                                                                                                               // The absolute mouse position
++                              this.offset.relative.top * mod +                                                                                // Only for relative positioned nodes: Relative offset from element to offset parent
++                              this.offset.parent.top * mod -                                                                          // The offsetParent's offset without borders (offset + border)
++                              ( ( this.cssPosition === "fixed" ? -this.offset.scroll.top : ( scrollIsRootNode ? 0 : this.offset.scroll.top ) ) * mod)
++                      ),
++                      left: (
++                              pos.left +                                                                                                                              // The absolute mouse position
++                              this.offset.relative.left * mod +                                                                               // Only for relative positioned nodes: Relative offset from element to offset parent
++                              this.offset.parent.left * mod   -                                                                               // The offsetParent's offset without borders (offset + border)
++                              ( ( this.cssPosition === "fixed" ? -this.offset.scroll.left : ( scrollIsRootNode ? 0 : this.offset.scroll.left ) ) * mod)
++                      )
++              };
++
++      },
++
++      _generatePosition: function( event, constrainPosition ) {
++
++              var containment, co, top, left,
++                      o = this.options,
++                      scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] ),
++                      pageX = event.pageX,
++                      pageY = event.pageY;
++
++              // Cache the scroll
++              if ( !scrollIsRootNode || !this.offset.scroll ) {
++                      this.offset.scroll = {
++                              top: this.scrollParent.scrollTop(),
++                              left: this.scrollParent.scrollLeft()
++                      };
++              }
++
++              /*
++               * - Position constraining -
++               * Constrain the position to a mix of grid, containment.
++               */
++
++              // If we are not dragging yet, we won't check for options
++              if ( constrainPosition ) {
++                      if ( this.containment ) {
++                              if ( this.relativeContainer ){
++                                      co = this.relativeContainer.offset();
++                                      containment = [
++                                              this.containment[ 0 ] + co.left,
++                                              this.containment[ 1 ] + co.top,
++                                              this.containment[ 2 ] + co.left,
++                                              this.containment[ 3 ] + co.top
++                                      ];
++                              } else {
++                                      containment = this.containment;
++                              }
++
++                              if (event.pageX - this.offset.click.left < containment[0]) {
++                                      pageX = containment[0] + this.offset.click.left;
++                              }
++                              if (event.pageY - this.offset.click.top < containment[1]) {
++                                      pageY = containment[1] + this.offset.click.top;
++                              }
++                              if (event.pageX - this.offset.click.left > containment[2]) {
++                                      pageX = containment[2] + this.offset.click.left;
++                              }
++                              if (event.pageY - this.offset.click.top > containment[3]) {
++                                      pageY = containment[3] + this.offset.click.top;
++                              }
++                      }
++
++                      if (o.grid) {
++                              //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950)
++                              top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY;
++                              pageY = containment ? ((top - this.offset.click.top >= containment[1] || top - this.offset.click.top > containment[3]) ? top : ((top - this.offset.click.top >= containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
++
++                              left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX;
++                              pageX = containment ? ((left - this.offset.click.left >= containment[0] || left - this.offset.click.left > containment[2]) ? left : ((left - this.offset.click.left >= containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
++                      }
++
++                      if ( o.axis === "y" ) {
++                              pageX = this.originalPageX;
++                      }
++
++                      if ( o.axis === "x" ) {
++                              pageY = this.originalPageY;
++                      }
++              }
++
++              return {
++                      top: (
++                              pageY -                                                                                                                                 // The absolute mouse position
++                              this.offset.click.top   -                                                                                               // Click offset (relative to the element)
++                              this.offset.relative.top -                                                                                              // Only for relative positioned nodes: Relative offset from element to offset parent
++                              this.offset.parent.top +                                                                                                // The offsetParent's offset without borders (offset + border)
++                              ( this.cssPosition === "fixed" ? -this.offset.scroll.top : ( scrollIsRootNode ? 0 : this.offset.scroll.top ) )
++                      ),
++                      left: (
++                              pageX -                                                                                                                                 // The absolute mouse position
++                              this.offset.click.left -                                                                                                // Click offset (relative to the element)
++                              this.offset.relative.left -                                                                                             // Only for relative positioned nodes: Relative offset from element to offset parent
++                              this.offset.parent.left +                                                                                               // The offsetParent's offset without borders (offset + border)
++                              ( this.cssPosition === "fixed" ? -this.offset.scroll.left : ( scrollIsRootNode ? 0 : this.offset.scroll.left ) )
++                      )
++              };
++
++      },
++
++      _clear: function() {
++              this.helper.removeClass("ui-draggable-dragging");
++              if (this.helper[0] !== this.element[0] && !this.cancelHelperRemoval) {
++                      this.helper.remove();
++              }
++              this.helper = null;
++              this.cancelHelperRemoval = false;
++              if ( this.destroyOnClear ) {
++                      this.destroy();
++              }
++      },
++
++      _normalizeRightBottom: function() {
++              if ( this.options.axis !== "y" && this.helper.css( "right" ) !== "auto" ) {
++                      this.helper.width( this.helper.width() );
++                      this.helper.css( "right", "auto" );
++              }
++              if ( this.options.axis !== "x" && this.helper.css( "bottom" ) !== "auto" ) {
++                      this.helper.height( this.helper.height() );
++                      this.helper.css( "bottom", "auto" );
++              }
++      },
++
++      // From now on bulk stuff - mainly helpers
++
++      _trigger: function( type, event, ui ) {
++              ui = ui || this._uiHash();
++              $.ui.plugin.call( this, type, [ event, ui, this ], true );
++
++              // Absolute position and offset (see #6884 ) have to be recalculated after plugins
++              if ( /^(drag|start|stop)/.test( type ) ) {
++                      this.positionAbs = this._convertPositionTo( "absolute" );
++                      ui.offset = this.positionAbs;
++              }
++              return $.Widget.prototype._trigger.call( this, type, event, ui );
++      },
++
++      plugins: {},
++
++      _uiHash: function() {
++              return {
++                      helper: this.helper,
++                      position: this.position,
++                      originalPosition: this.originalPosition,
++                      offset: this.positionAbs
++              };
++      }
++
++});
++
++$.ui.plugin.add( "draggable", "connectToSortable", {
++      start: function( event, ui, draggable ) {
++              var uiSortable = $.extend( {}, ui, {
++                      item: draggable.element
++              });
++
++              draggable.sortables = [];
++              $( draggable.options.connectToSortable ).each(function() {
++                      var sortable = $( this ).sortable( "instance" );
++
++                      if ( sortable && !sortable.options.disabled ) {
++                              draggable.sortables.push( sortable );
++
++                              // refreshPositions is called at drag start to refresh the containerCache
++                              // which is used in drag. This ensures it's initialized and synchronized
++                              // with any changes that might have happened on the page since initialization.
++                              sortable.refreshPositions();
++                              sortable._trigger("activate", event, uiSortable);
++                      }
++              });
++      },
++      stop: function( event, ui, draggable ) {
++              var uiSortable = $.extend( {}, ui, {
++                      item: draggable.element
++              });
++
++              draggable.cancelHelperRemoval = false;
++
++              $.each( draggable.sortables, function() {
++                      var sortable = this;
++
++                      if ( sortable.isOver ) {
++                              sortable.isOver = 0;
++
++                              // Allow this sortable to handle removing the helper
++                              draggable.cancelHelperRemoval = true;
++                              sortable.cancelHelperRemoval = false;
++
++                              // Use _storedCSS To restore properties in the sortable,
++                              // as this also handles revert (#9675) since the draggable
++                              // may have modified them in unexpected ways (#8809)
++                              sortable._storedCSS = {
++                                      position: sortable.placeholder.css( "position" ),
++                                      top: sortable.placeholder.css( "top" ),
++                                      left: sortable.placeholder.css( "left" )
++                              };
++
++                              sortable._mouseStop(event);
++
++                              // Once drag has ended, the sortable should return to using
++                              // its original helper, not the shared helper from draggable
++                              sortable.options.helper = sortable.options._helper;
++                      } else {
++                              // Prevent this Sortable from removing the helper.
++                              // However, don't set the draggable to remove the helper
++                              // either as another connected Sortable may yet handle the removal.
++                              sortable.cancelHelperRemoval = true;
++
++                              sortable._trigger( "deactivate", event, uiSortable );
++                      }
++              });
++      },
++      drag: function( event, ui, draggable ) {
++              $.each( draggable.sortables, function() {
++                      var innermostIntersecting = false,
++                              sortable = this;
++
++                      // Copy over variables that sortable's _intersectsWith uses
++                      sortable.positionAbs = draggable.positionAbs;
++                      sortable.helperProportions = draggable.helperProportions;
++                      sortable.offset.click = draggable.offset.click;
++
++                      if ( sortable._intersectsWith( sortable.containerCache ) ) {
++                              innermostIntersecting = true;
++
++                              $.each( draggable.sortables, function() {
++                                      // Copy over variables that sortable's _intersectsWith uses
++                                      this.positionAbs = draggable.positionAbs;
++                                      this.helperProportions = draggable.helperProportions;
++                                      this.offset.click = draggable.offset.click;
++
++                                      if ( this !== sortable &&
++                                                      this._intersectsWith( this.containerCache ) &&
++                                                      $.contains( sortable.element[ 0 ], this.element[ 0 ] ) ) {
++                                              innermostIntersecting = false;
++                                      }
++
++                                      return innermostIntersecting;
++                              });
++                      }
++
++                      if ( innermostIntersecting ) {
++                              // If it intersects, we use a little isOver variable and set it once,
++                              // so that the move-in stuff gets fired only once.
++                              if ( !sortable.isOver ) {
++                                      sortable.isOver = 1;
++
++                                      // Store draggable's parent in case we need to reappend to it later.
++                                      draggable._parent = ui.helper.parent();
++
++                                      sortable.currentItem = ui.helper
++                                              .appendTo( sortable.element )
++                                              .data( "ui-sortable-item", true );
++
++                                      // Store helper option to later restore it
++                                      sortable.options._helper = sortable.options.helper;
++
++                                      sortable.options.helper = function() {
++                                              return ui.helper[ 0 ];
++                                      };
++
++                                      // Fire the start events of the sortable with our passed browser event,
++                                      // and our own helper (so it doesn't create a new one)
++                                      event.target = sortable.currentItem[ 0 ];
++                                      sortable._mouseCapture( event, true );
++                                      sortable._mouseStart( event, true, true );
++
++                                      // Because the browser event is way off the new appended portlet,
++                                      // modify necessary variables to reflect the changes
++                                      sortable.offset.click.top = draggable.offset.click.top;
++                                      sortable.offset.click.left = draggable.offset.click.left;
++                                      sortable.offset.parent.left -= draggable.offset.parent.left -
++                                              sortable.offset.parent.left;
++                                      sortable.offset.parent.top -= draggable.offset.parent.top -
++                                              sortable.offset.parent.top;
++
++                                      draggable._trigger( "toSortable", event );
++
++                                      // Inform draggable that the helper is in a valid drop zone,
++                                      // used solely in the revert option to handle "valid/invalid".
++                                      draggable.dropped = sortable.element;
++
++                                      // Need to refreshPositions of all sortables in the case that
++                                      // adding to one sortable changes the location of the other sortables (#9675)
++                                      $.each( draggable.sortables, function() {
++                                              this.refreshPositions();
++                                      });
++
++                                      // hack so receive/update callbacks work (mostly)
++                                      draggable.currentItem = draggable.element;
++                                      sortable.fromOutside = draggable;
++                              }
++
++                              if ( sortable.currentItem ) {
++                                      sortable._mouseDrag( event );
++                                      // Copy the sortable's position because the draggable's can potentially reflect
++                                      // a relative position, while sortable is always absolute, which the dragged
++                                      // element has now become. (#8809)
++                                      ui.position = sortable.position;
++                              }
++                      } else {
++                              // If it doesn't intersect with the sortable, and it intersected before,
++                              // we fake the drag stop of the sortable, but make sure it doesn't remove
++                              // the helper by using cancelHelperRemoval.
++                              if ( sortable.isOver ) {
++
++                                      sortable.isOver = 0;
++                                      sortable.cancelHelperRemoval = true;
++
++                                      // Calling sortable's mouseStop would trigger a revert,
++                                      // so revert must be temporarily false until after mouseStop is called.
++                                      sortable.options._revert = sortable.options.revert;
++                                      sortable.options.revert = false;
++
++                                      sortable._trigger( "out", event, sortable._uiHash( sortable ) );
++                                      sortable._mouseStop( event, true );
++
++                                      // restore sortable behaviors that were modfied
++                                      // when the draggable entered the sortable area (#9481)
++                                      sortable.options.revert = sortable.options._revert;
++                                      sortable.options.helper = sortable.options._helper;
++
++                                      if ( sortable.placeholder ) {
++                                              sortable.placeholder.remove();
++                                      }
++
++                                      // Restore and recalculate the draggable's offset considering the sortable
++                                      // may have modified them in unexpected ways. (#8809, #10669)
++                                      ui.helper.appendTo( draggable._parent );
++                                      draggable._refreshOffsets( event );
++                                      ui.position = draggable._generatePosition( event, true );
++
++                                      draggable._trigger( "fromSortable", event );
++
++                                      // Inform draggable that the helper is no longer in a valid drop zone
++                                      draggable.dropped = false;
++
++                                      // Need to refreshPositions of all sortables just in case removing
++                                      // from one sortable changes the location of other sortables (#9675)
++                                      $.each( draggable.sortables, function() {
++                                              this.refreshPositions();
++                                      });
++                              }
++                      }
++              });
++      }
++});
++
++$.ui.plugin.add("draggable", "cursor", {
++      start: function( event, ui, instance ) {
++              var t = $( "body" ),
++                      o = instance.options;
++
++              if (t.css("cursor")) {
++                      o._cursor = t.css("cursor");
++              }
++              t.css("cursor", o.cursor);
++      },
++      stop: function( event, ui, instance ) {
++              var o = instance.options;
++              if (o._cursor) {
++                      $("body").css("cursor", o._cursor);
++              }
++      }
++});
++
++$.ui.plugin.add("draggable", "opacity", {
++      start: function( event, ui, instance ) {
++              var t = $( ui.helper ),
++                      o = instance.options;
++              if (t.css("opacity")) {
++                      o._opacity = t.css("opacity");
++              }
++              t.css("opacity", o.opacity);
++      },
++      stop: function( event, ui, instance ) {
++              var o = instance.options;
++              if (o._opacity) {
++                      $(ui.helper).css("opacity", o._opacity);
++              }
++      }
++});
++
++$.ui.plugin.add("draggable", "scroll", {
++      start: function( event, ui, i ) {
++              if ( !i.scrollParentNotHidden ) {
++                      i.scrollParentNotHidden = i.helper.scrollParent( false );
++              }
++
++              if ( i.scrollParentNotHidden[ 0 ] !== i.document[ 0 ] && i.scrollParentNotHidden[ 0 ].tagName !== "HTML" ) {
++                      i.overflowOffset = i.scrollParentNotHidden.offset();
++              }
++      },
++      drag: function( event, ui, i  ) {
++
++              var o = i.options,
++                      scrolled = false,
++                      scrollParent = i.scrollParentNotHidden[ 0 ],
++                      document = i.document[ 0 ];
++
++              if ( scrollParent !== document && scrollParent.tagName !== "HTML" ) {
++                      if ( !o.axis || o.axis !== "x" ) {
++                              if ( ( i.overflowOffset.top + scrollParent.offsetHeight ) - event.pageY < o.scrollSensitivity ) {
++                                      scrollParent.scrollTop = scrolled = scrollParent.scrollTop + o.scrollSpeed;
++                              } else if ( event.pageY - i.overflowOffset.top < o.scrollSensitivity ) {
++                                      scrollParent.scrollTop = scrolled = scrollParent.scrollTop - o.scrollSpeed;
++                              }
++                      }
++
++                      if ( !o.axis || o.axis !== "y" ) {
++                              if ( ( i.overflowOffset.left + scrollParent.offsetWidth ) - event.pageX < o.scrollSensitivity ) {
++                                      scrollParent.scrollLeft = scrolled = scrollParent.scrollLeft + o.scrollSpeed;
++                              } else if ( event.pageX - i.overflowOffset.left < o.scrollSensitivity ) {
++                                      scrollParent.scrollLeft = scrolled = scrollParent.scrollLeft - o.scrollSpeed;
++                              }
++                      }
++
++              } else {
++
++                      if (!o.axis || o.axis !== "x") {
++                              if (event.pageY - $(document).scrollTop() < o.scrollSensitivity) {
++                                      scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
++                              } else if ($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) {
++                                      scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
++                              }
++                      }
++
++                      if (!o.axis || o.axis !== "y") {
++                              if (event.pageX - $(document).scrollLeft() < o.scrollSensitivity) {
++                                      scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
++                              } else if ($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) {
++                                      scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
++                              }
++                      }
++
++              }
++
++              if (scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) {
++                      $.ui.ddmanager.prepareOffsets(i, event);
++              }
++
++      }
++});
++
++$.ui.plugin.add("draggable", "snap", {
++      start: function( event, ui, i ) {
++
++              var o = i.options;
++
++              i.snapElements = [];
++
++              $(o.snap.constructor !== String ? ( o.snap.items || ":data(ui-draggable)" ) : o.snap).each(function() {
++                      var $t = $(this),
++                              $o = $t.offset();
++                      if (this !== i.element[0]) {
++                              i.snapElements.push({
++                                      item: this,
++                                      width: $t.outerWidth(), height: $t.outerHeight(),
++                                      top: $o.top, left: $o.left
++                              });
++                      }
++              });
++
++      },
++      drag: function( event, ui, inst ) {
++
++              var ts, bs, ls, rs, l, r, t, b, i, first,
++                      o = inst.options,
++                      d = o.snapTolerance,
++                      x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
++                      y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
++
++              for (i = inst.snapElements.length - 1; i >= 0; i--){
++
++                      l = inst.snapElements[i].left - inst.margins.left;
++                      r = l + inst.snapElements[i].width;
++                      t = inst.snapElements[i].top - inst.margins.top;
++                      b = t + inst.snapElements[i].height;
++
++                      if ( x2 < l - d || x1 > r + d || y2 < t - d || y1 > b + d || !$.contains( inst.snapElements[ i ].item.ownerDocument, inst.snapElements[ i ].item ) ) {
++                              if (inst.snapElements[i].snapping) {
++                                      (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
++                              }
++                              inst.snapElements[i].snapping = false;
++                              continue;
++                      }
++
++                      if (o.snapMode !== "inner") {
++                              ts = Math.abs(t - y2) <= d;
++                              bs = Math.abs(b - y1) <= d;
++                              ls = Math.abs(l - x2) <= d;
++                              rs = Math.abs(r - x1) <= d;
++                              if (ts) {
++                                      ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top;
++                              }
++                              if (bs) {
++                                      ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top;
++                              }
++                              if (ls) {
++                                      ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left;
++                              }
++                              if (rs) {
++                                      ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left;
++                              }
++                      }
++
++                      first = (ts || bs || ls || rs);
++
++                      if (o.snapMode !== "outer") {
++                              ts = Math.abs(t - y1) <= d;
++                              bs = Math.abs(b - y2) <= d;
++                              ls = Math.abs(l - x1) <= d;
++                              rs = Math.abs(r - x2) <= d;
++                              if (ts) {
++                                      ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top;
++                              }
++                              if (bs) {
++                                      ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top;
++                              }
++                              if (ls) {
++                                      ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left;
++                              }
++                              if (rs) {
++                                      ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left;
++                              }
++                      }
++
++                      if (!inst.snapElements[i].snapping && (ts || bs || ls || rs || first)) {
++                              (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
++                      }
++                      inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
++
++              }
++
++      }
++});
++
++$.ui.plugin.add("draggable", "stack", {
++      start: function( event, ui, instance ) {
++              var min,
++                      o = instance.options,
++                      group = $.makeArray($(o.stack)).sort(function(a, b) {
++                              return (parseInt($(a).css("zIndex"), 10) || 0) - (parseInt($(b).css("zIndex"), 10) || 0);
++                      });
++
++              if (!group.length) { return; }
++
++              min = parseInt($(group[0]).css("zIndex"), 10) || 0;
++              $(group).each(function(i) {
++                      $(this).css("zIndex", min + i);
++              });
++              this.css("zIndex", (min + group.length));
++      }
++});
++
++$.ui.plugin.add("draggable", "zIndex", {
++      start: function( event, ui, instance ) {
++              var t = $( ui.helper ),
++                      o = instance.options;
++
++              if (t.css("zIndex")) {
++                      o._zIndex = t.css("zIndex");
++              }
++              t.css("zIndex", o.zIndex);
++      },
++      stop: function( event, ui, instance ) {
++              var o = instance.options;
++
++              if (o._zIndex) {
++                      $(ui.helper).css("zIndex", o._zIndex);
++              }
++      }
++});
++
++var draggable = $.ui.draggable;
++
++
++/*!
++ * jQuery UI Resizable 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/resizable/
++ */
++
++
++$.widget("ui.resizable", $.ui.mouse, {
++      version: "1.11.4",
++      widgetEventPrefix: "resize",
++      options: {
++              alsoResize: false,
++              animate: false,
++              animateDuration: "slow",
++              animateEasing: "swing",
++              aspectRatio: false,
++              autoHide: false,
++              containment: false,
++              ghost: false,
++              grid: false,
++              handles: "e,s,se",
++              helper: false,
++              maxHeight: null,
++              maxWidth: null,
++              minHeight: 10,
++              minWidth: 10,
++              // See #7960
++              zIndex: 90,
++
++              // callbacks
++              resize: null,
++              start: null,
++              stop: null
++      },
++
++      _num: function( value ) {
++              return parseInt( value, 10 ) || 0;
++      },
++
++      _isNumber: function( value ) {
++              return !isNaN( parseInt( value, 10 ) );
++      },
++
++      _hasScroll: function( el, a ) {
++
++              if ( $( el ).css( "overflow" ) === "hidden") {
++                      return false;
++              }
++
++              var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
++                      has = false;
++
++              if ( el[ scroll ] > 0 ) {
++                      return true;
++              }
++
++              // TODO: determine which cases actually cause this to happen
++              // if the element doesn't have the scroll set, see if it's possible to
++              // set the scroll
++              el[ scroll ] = 1;
++              has = ( el[ scroll ] > 0 );
++              el[ scroll ] = 0;
++              return has;
++      },
++
++      _create: function() {
++
++              var n, i, handle, axis, hname,
++                      that = this,
++                      o = this.options;
++              this.element.addClass("ui-resizable");
++
++              $.extend(this, {
++                      _aspectRatio: !!(o.aspectRatio),
++                      aspectRatio: o.aspectRatio,
++                      originalElement: this.element,
++                      _proportionallyResizeElements: [],
++                      _helper: o.helper || o.ghost || o.animate ? o.helper || "ui-resizable-helper" : null
++              });
++
++              // Wrap the element if it cannot hold child nodes
++              if (this.element[0].nodeName.match(/^(canvas|textarea|input|select|button|img)$/i)) {
++
++                      this.element.wrap(
++                              $("<div class='ui-wrapper' style='overflow: hidden;'></div>").css({
++                                      position: this.element.css("position"),
++                                      width: this.element.outerWidth(),
++                                      height: this.element.outerHeight(),
++                                      top: this.element.css("top"),
++                                      left: this.element.css("left")
++                              })
++                      );
++
++                      this.element = this.element.parent().data(
++                              "ui-resizable", this.element.resizable( "instance" )
++                      );
++
++                      this.elementIsWrapper = true;
++
++                      this.element.css({
++                              marginLeft: this.originalElement.css("marginLeft"),
++                              marginTop: this.originalElement.css("marginTop"),
++                              marginRight: this.originalElement.css("marginRight"),
++                              marginBottom: this.originalElement.css("marginBottom")
++                      });
++                      this.originalElement.css({
++                              marginLeft: 0,
++                              marginTop: 0,
++                              marginRight: 0,
++                              marginBottom: 0
++                      });
++                      // support: Safari
++                      // Prevent Safari textarea resize
++                      this.originalResizeStyle = this.originalElement.css("resize");
++                      this.originalElement.css("resize", "none");
++
++                      this._proportionallyResizeElements.push( this.originalElement.css({
++                              position: "static",
++                              zoom: 1,
++                              display: "block"
++                      }) );
++
++                      // support: IE9
++                      // avoid IE jump (hard set the margin)
++                      this.originalElement.css({ margin: this.originalElement.css("margin") });
++
++                      this._proportionallyResize();
++              }
++
++              this.handles = o.handles ||
++                      ( !$(".ui-resizable-handle", this.element).length ?
++                              "e,s,se" : {
++                                      n: ".ui-resizable-n",
++                                      e: ".ui-resizable-e",
++                                      s: ".ui-resizable-s",
++                                      w: ".ui-resizable-w",
++                                      se: ".ui-resizable-se",
++                                      sw: ".ui-resizable-sw",
++                                      ne: ".ui-resizable-ne",
++                                      nw: ".ui-resizable-nw"
++                              } );
++
++              this._handles = $();
++              if ( this.handles.constructor === String ) {
++
++                      if ( this.handles === "all") {
++                              this.handles = "n,e,s,w,se,sw,ne,nw";
++                      }
++
++                      n = this.handles.split(",");
++                      this.handles = {};
++
++                      for (i = 0; i < n.length; i++) {
++
++                              handle = $.trim(n[i]);
++                              hname = "ui-resizable-" + handle;
++                              axis = $("<div class='ui-resizable-handle " + hname + "'></div>");
++
++                              axis.css({ zIndex: o.zIndex });
++
++                              // TODO : What's going on here?
++                              if ("se" === handle) {
++                                      axis.addClass("ui-icon ui-icon-gripsmall-diagonal-se");
++                              }
++
++                              this.handles[handle] = ".ui-resizable-" + handle;
++                              this.element.append(axis);
++                      }
++
++              }
++
++              this._renderAxis = function(target) {
++
++                      var i, axis, padPos, padWrapper;
++
++                      target = target || this.element;
++
++                      for (i in this.handles) {
++
++                              if (this.handles[i].constructor === String) {
++                                      this.handles[i] = this.element.children( this.handles[ i ] ).first().show();
++                              } else if ( this.handles[ i ].jquery || this.handles[ i ].nodeType ) {
++                                      this.handles[ i ] = $( this.handles[ i ] );
++                                      this._on( this.handles[ i ], { "mousedown": that._mouseDown });
++                              }
++
++                              if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/^(textarea|input|select|button)$/i)) {
++
++                                      axis = $(this.handles[i], this.element);
++
++                                      padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth();
++
++                                      padPos = [ "padding",
++                                              /ne|nw|n/.test(i) ? "Top" :
++                                              /se|sw|s/.test(i) ? "Bottom" :
++                                              /^e$/.test(i) ? "Right" : "Left" ].join("");
++
++                                      target.css(padPos, padWrapper);
++
++                                      this._proportionallyResize();
++                              }
++
++                              this._handles = this._handles.add( this.handles[ i ] );
++                      }
++              };
++
++              // TODO: make renderAxis a prototype function
++              this._renderAxis(this.element);
++
++              this._handles = this._handles.add( this.element.find( ".ui-resizable-handle" ) );
++              this._handles.disableSelection();
++
++              this._handles.mouseover(function() {
++                      if (!that.resizing) {
++                              if (this.className) {
++                                      axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);
++                              }
++                              that.axis = axis && axis[1] ? axis[1] : "se";
++                      }
++              });
++
++              if (o.autoHide) {
++                      this._handles.hide();
++                      $(this.element)
++                              .addClass("ui-resizable-autohide")
++                              .mouseenter(function() {
++                                      if (o.disabled) {
++                                              return;
++                                      }
++                                      $(this).removeClass("ui-resizable-autohide");
++                                      that._handles.show();
++                              })
++                              .mouseleave(function() {
++                                      if (o.disabled) {
++                                              return;
++                                      }
++                                      if (!that.resizing) {
++                                              $(this).addClass("ui-resizable-autohide");
++                                              that._handles.hide();
++                                      }
++                              });
++              }
++
++              this._mouseInit();
++      },
++
++      _destroy: function() {
++
++              this._mouseDestroy();
++
++              var wrapper,
++                      _destroy = function(exp) {
++                              $(exp)
++                                      .removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing")
++                                      .removeData("resizable")
++                                      .removeData("ui-resizable")
++                                      .unbind(".resizable")
++                                      .find(".ui-resizable-handle")
++                                              .remove();
++                      };
++
++              // TODO: Unwrap at same DOM position
++              if (this.elementIsWrapper) {
++                      _destroy(this.element);
++                      wrapper = this.element;
++                      this.originalElement.css({
++                              position: wrapper.css("position"),
++                              width: wrapper.outerWidth(),
++                              height: wrapper.outerHeight(),
++                              top: wrapper.css("top"),
++                              left: wrapper.css("left")
++                      }).insertAfter( wrapper );
++                      wrapper.remove();
++              }
++
++              this.originalElement.css("resize", this.originalResizeStyle);
++              _destroy(this.originalElement);
++
++              return this;
++      },
++
++      _mouseCapture: function(event) {
++              var i, handle,
++                      capture = false;
++
++              for (i in this.handles) {
++                      handle = $(this.handles[i])[0];
++                      if (handle === event.target || $.contains(handle, event.target)) {
++                              capture = true;
++                      }
++              }
++
++              return !this.options.disabled && capture;
++      },
++
++      _mouseStart: function(event) {
++
++              var curleft, curtop, cursor,
++                      o = this.options,
++                      el = this.element;
++
++              this.resizing = true;
++
++              this._renderProxy();
++
++              curleft = this._num(this.helper.css("left"));
++              curtop = this._num(this.helper.css("top"));
++
++              if (o.containment) {
++                      curleft += $(o.containment).scrollLeft() || 0;
++                      curtop += $(o.containment).scrollTop() || 0;
++              }
++
++              this.offset = this.helper.offset();
++              this.position = { left: curleft, top: curtop };
++
++              this.size = this._helper ? {
++                              width: this.helper.width(),
++                              height: this.helper.height()
++                      } : {
++                              width: el.width(),
++                              height: el.height()
++                      };
++
++              this.originalSize = this._helper ? {
++                              width: el.outerWidth(),
++                              height: el.outerHeight()
++                      } : {
++                              width: el.width(),
++                              height: el.height()
++                      };
++
++              this.sizeDiff = {
++                      width: el.outerWidth() - el.width(),
++                      height: el.outerHeight() - el.height()
++              };
++
++              this.originalPosition = { left: curleft, top: curtop };
++              this.originalMousePosition = { left: event.pageX, top: event.pageY };
++
++              this.aspectRatio = (typeof o.aspectRatio === "number") ?
++                      o.aspectRatio :
++                      ((this.originalSize.width / this.originalSize.height) || 1);
++
++              cursor = $(".ui-resizable-" + this.axis).css("cursor");
++              $("body").css("cursor", cursor === "auto" ? this.axis + "-resize" : cursor);
++
++              el.addClass("ui-resizable-resizing");
++              this._propagate("start", event);
++              return true;
++      },
++
++      _mouseDrag: function(event) {
++
++              var data, props,
++                      smp = this.originalMousePosition,
++                      a = this.axis,
++                      dx = (event.pageX - smp.left) || 0,
++                      dy = (event.pageY - smp.top) || 0,
++                      trigger = this._change[a];
++
++              this._updatePrevProperties();
++
++              if (!trigger) {
++                      return false;
++              }
++
++              data = trigger.apply(this, [ event, dx, dy ]);
++
++              this._updateVirtualBoundaries(event.shiftKey);
++              if (this._aspectRatio || event.shiftKey) {
++                      data = this._updateRatio(data, event);
++              }
++
++              data = this._respectSize(data, event);
++
++              this._updateCache(data);
++
++              this._propagate("resize", event);
++
++              props = this._applyChanges();
++
++              if ( !this._helper && this._proportionallyResizeElements.length ) {
++                      this._proportionallyResize();
++              }
++
++              if ( !$.isEmptyObject( props ) ) {
++                      this._updatePrevProperties();
++                      this._trigger( "resize", event, this.ui() );
++                      this._applyChanges();
++              }
++
++              return false;
++      },
++
++      _mouseStop: function(event) {
++
++              this.resizing = false;
++              var pr, ista, soffseth, soffsetw, s, left, top,
++                      o = this.options, that = this;
++
++              if (this._helper) {
++
++                      pr = this._proportionallyResizeElements;
++                      ista = pr.length && (/textarea/i).test(pr[0].nodeName);
++                      soffseth = ista && this._hasScroll(pr[0], "left") ? 0 : that.sizeDiff.height;
++                      soffsetw = ista ? 0 : that.sizeDiff.width;
++
++                      s = {
++                              width: (that.helper.width()  - soffsetw),
++                              height: (that.helper.height() - soffseth)
++                      };
++                      left = (parseInt(that.element.css("left"), 10) +
++                              (that.position.left - that.originalPosition.left)) || null;
++                      top = (parseInt(that.element.css("top"), 10) +
++                              (that.position.top - that.originalPosition.top)) || null;
++
++                      if (!o.animate) {
++                              this.element.css($.extend(s, { top: top, left: left }));
++                      }
++
++                      that.helper.height(that.size.height);
++                      that.helper.width(that.size.width);
++
++                      if (this._helper && !o.animate) {
++                              this._proportionallyResize();
++                      }
++              }
++
++              $("body").css("cursor", "auto");
++
++              this.element.removeClass("ui-resizable-resizing");
++
++              this._propagate("stop", event);
++
++              if (this._helper) {
++                      this.helper.remove();
++              }
++
++              return false;
++
++      },
++
++      _updatePrevProperties: function() {
++              this.prevPosition = {
++                      top: this.position.top,
++                      left: this.position.left
++              };
++              this.prevSize = {
++                      width: this.size.width,
++                      height: this.size.height
++              };
++      },
++
++      _applyChanges: function() {
++              var props = {};
++
++              if ( this.position.top !== this.prevPosition.top ) {
++                      props.top = this.position.top + "px";
++              }
++              if ( this.position.left !== this.prevPosition.left ) {
++                      props.left = this.position.left + "px";
++              }
++              if ( this.size.width !== this.prevSize.width ) {
++                      props.width = this.size.width + "px";
++              }
++              if ( this.size.height !== this.prevSize.height ) {
++                      props.height = this.size.height + "px";
++              }
++
++              this.helper.css( props );
++
++              return props;
++      },
++
++      _updateVirtualBoundaries: function(forceAspectRatio) {
++              var pMinWidth, pMaxWidth, pMinHeight, pMaxHeight, b,
++                      o = this.options;
++
++              b = {
++                      minWidth: this._isNumber(o.minWidth) ? o.minWidth : 0,
++                      maxWidth: this._isNumber(o.maxWidth) ? o.maxWidth : Infinity,
++                      minHeight: this._isNumber(o.minHeight) ? o.minHeight : 0,
++                      maxHeight: this._isNumber(o.maxHeight) ? o.maxHeight : Infinity
++              };
++
++              if (this._aspectRatio || forceAspectRatio) {
++                      pMinWidth = b.minHeight * this.aspectRatio;
++                      pMinHeight = b.minWidth / this.aspectRatio;
++                      pMaxWidth = b.maxHeight * this.aspectRatio;
++                      pMaxHeight = b.maxWidth / this.aspectRatio;
++
++                      if (pMinWidth > b.minWidth) {
++                              b.minWidth = pMinWidth;
++                      }
++                      if (pMinHeight > b.minHeight) {
++                              b.minHeight = pMinHeight;
++                      }
++                      if (pMaxWidth < b.maxWidth) {
++                              b.maxWidth = pMaxWidth;
++                      }
++                      if (pMaxHeight < b.maxHeight) {
++                              b.maxHeight = pMaxHeight;
++                      }
++              }
++              this._vBoundaries = b;
++      },
++
++      _updateCache: function(data) {
++              this.offset = this.helper.offset();
++              if (this._isNumber(data.left)) {
++                      this.position.left = data.left;
++              }
++              if (this._isNumber(data.top)) {
++                      this.position.top = data.top;
++              }
++              if (this._isNumber(data.height)) {
++                      this.size.height = data.height;
++              }
++              if (this._isNumber(data.width)) {
++                      this.size.width = data.width;
++              }
++      },
++
++      _updateRatio: function( data ) {
++
++              var cpos = this.position,
++                      csize = this.size,
++                      a = this.axis;
++
++              if (this._isNumber(data.height)) {
++                      data.width = (data.height * this.aspectRatio);
++              } else if (this._isNumber(data.width)) {
++                      data.height = (data.width / this.aspectRatio);
++              }
++
++              if (a === "sw") {
++                      data.left = cpos.left + (csize.width - data.width);
++                      data.top = null;
++              }
++              if (a === "nw") {
++                      data.top = cpos.top + (csize.height - data.height);
++                      data.left = cpos.left + (csize.width - data.width);
++              }
++
++              return data;
++      },
++
++      _respectSize: function( data ) {
++
++              var o = this._vBoundaries,
++                      a = this.axis,
++                      ismaxw = this._isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width),
++                      ismaxh = this._isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height),
++                      isminw = this._isNumber(data.width) && o.minWidth && (o.minWidth > data.width),
++                      isminh = this._isNumber(data.height) && o.minHeight && (o.minHeight > data.height),
++                      dw = this.originalPosition.left + this.originalSize.width,
++                      dh = this.position.top + this.size.height,
++                      cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a);
++              if (isminw) {
++                      data.width = o.minWidth;
++              }
++              if (isminh) {
++                      data.height = o.minHeight;
++              }
++              if (ismaxw) {
++                      data.width = o.maxWidth;
++              }
++              if (ismaxh) {
++                      data.height = o.maxHeight;
++              }
++
++              if (isminw && cw) {
++                      data.left = dw - o.minWidth;
++              }
++              if (ismaxw && cw) {
++                      data.left = dw - o.maxWidth;
++              }
++              if (isminh && ch) {
++                      data.top = dh - o.minHeight;
++              }
++              if (ismaxh && ch) {
++                      data.top = dh - o.maxHeight;
++              }
++
++              // Fixing jump error on top/left - bug #2330
++              if (!data.width && !data.height && !data.left && data.top) {
++                      data.top = null;
++              } else if (!data.width && !data.height && !data.top && data.left) {
++                      data.left = null;
++              }
++
++              return data;
++      },
++
++      _getPaddingPlusBorderDimensions: function( element ) {
++              var i = 0,
++                      widths = [],
++                      borders = [
++                              element.css( "borderTopWidth" ),
++                              element.css( "borderRightWidth" ),
++                              element.css( "borderBottomWidth" ),
++                              element.css( "borderLeftWidth" )
++                      ],
++                      paddings = [
++                              element.css( "paddingTop" ),
++                              element.css( "paddingRight" ),
++                              element.css( "paddingBottom" ),
++                              element.css( "paddingLeft" )
++                      ];
++
++              for ( ; i < 4; i++ ) {
++                      widths[ i ] = ( parseInt( borders[ i ], 10 ) || 0 );
++                      widths[ i ] += ( parseInt( paddings[ i ], 10 ) || 0 );
++              }
++
++              return {
++                      height: widths[ 0 ] + widths[ 2 ],
++                      width: widths[ 1 ] + widths[ 3 ]
++              };
++      },
++
++      _proportionallyResize: function() {
++
++              if (!this._proportionallyResizeElements.length) {
++                      return;
++              }
++
++              var prel,
++                      i = 0,
++                      element = this.helper || this.element;
++
++              for ( ; i < this._proportionallyResizeElements.length; i++) {
++
++                      prel = this._proportionallyResizeElements[i];
++
++                      // TODO: Seems like a bug to cache this.outerDimensions
++                      // considering that we are in a loop.
++                      if (!this.outerDimensions) {
++                              this.outerDimensions = this._getPaddingPlusBorderDimensions( prel );
++                      }
++
++                      prel.css({
++                              height: (element.height() - this.outerDimensions.height) || 0,
++                              width: (element.width() - this.outerDimensions.width) || 0
++                      });
++
++              }
++
++      },
++
++      _renderProxy: function() {
++
++              var el = this.element, o = this.options;
++              this.elementOffset = el.offset();
++
++              if (this._helper) {
++
++                      this.helper = this.helper || $("<div style='overflow:hidden;'></div>");
++
++                      this.helper.addClass(this._helper).css({
++                              width: this.element.outerWidth() - 1,
++                              height: this.element.outerHeight() - 1,
++                              position: "absolute",
++                              left: this.elementOffset.left + "px",
++                              top: this.elementOffset.top + "px",
++                              zIndex: ++o.zIndex //TODO: Don't modify option
++                      });
++
++                      this.helper
++                              .appendTo("body")
++                              .disableSelection();
++
++              } else {
++                      this.helper = this.element;
++              }
++
++      },
++
++      _change: {
++              e: function(event, dx) {
++                      return { width: this.originalSize.width + dx };
++              },
++              w: function(event, dx) {
++                      var cs = this.originalSize, sp = this.originalPosition;
++                      return { left: sp.left + dx, width: cs.width - dx };
++              },
++              n: function(event, dx, dy) {
++                      var cs = this.originalSize, sp = this.originalPosition;
++                      return { top: sp.top + dy, height: cs.height - dy };
++              },
++              s: function(event, dx, dy) {
++                      return { height: this.originalSize.height + dy };
++              },
++              se: function(event, dx, dy) {
++                      return $.extend(this._change.s.apply(this, arguments),
++                              this._change.e.apply(this, [ event, dx, dy ]));
++              },
++              sw: function(event, dx, dy) {
++                      return $.extend(this._change.s.apply(this, arguments),
++                              this._change.w.apply(this, [ event, dx, dy ]));
++              },
++              ne: function(event, dx, dy) {
++                      return $.extend(this._change.n.apply(this, arguments),
++                              this._change.e.apply(this, [ event, dx, dy ]));
++              },
++              nw: function(event, dx, dy) {
++                      return $.extend(this._change.n.apply(this, arguments),
++                              this._change.w.apply(this, [ event, dx, dy ]));
++              }
++      },
++
++      _propagate: function(n, event) {
++              $.ui.plugin.call(this, n, [ event, this.ui() ]);
++              (n !== "resize" && this._trigger(n, event, this.ui()));
++      },
++
++      plugins: {},
++
++      ui: function() {
++              return {
++                      originalElement: this.originalElement,
++                      element: this.element,
++                      helper: this.helper,
++                      position: this.position,
++                      size: this.size,
++                      originalSize: this.originalSize,
++                      originalPosition: this.originalPosition
++              };
++      }
++
++});
++
++/*
++ * Resizable Extensions
++ */
++
++$.ui.plugin.add("resizable", "animate", {
++
++      stop: function( event ) {
++              var that = $(this).resizable( "instance" ),
++                      o = that.options,
++                      pr = that._proportionallyResizeElements,
++                      ista = pr.length && (/textarea/i).test(pr[0].nodeName),
++                      soffseth = ista && that._hasScroll(pr[0], "left") ? 0 : that.sizeDiff.height,
++                      soffsetw = ista ? 0 : that.sizeDiff.width,
++                      style = { width: (that.size.width - soffsetw), height: (that.size.height - soffseth) },
++                      left = (parseInt(that.element.css("left"), 10) +
++                              (that.position.left - that.originalPosition.left)) || null,
++                      top = (parseInt(that.element.css("top"), 10) +
++                              (that.position.top - that.originalPosition.top)) || null;
++
++              that.element.animate(
++                      $.extend(style, top && left ? { top: top, left: left } : {}), {
++                              duration: o.animateDuration,
++                              easing: o.animateEasing,
++                              step: function() {
++
++                                      var data = {
++                                              width: parseInt(that.element.css("width"), 10),
++                                              height: parseInt(that.element.css("height"), 10),
++                                              top: parseInt(that.element.css("top"), 10),
++                                              left: parseInt(that.element.css("left"), 10)
++                                      };
++
++                                      if (pr && pr.length) {
++                                              $(pr[0]).css({ width: data.width, height: data.height });
++                                      }
++
++                                      // propagating resize, and updating values for each animation step
++                                      that._updateCache(data);
++                                      that._propagate("resize", event);
++
++                              }
++                      }
++              );
++      }
++
++});
++
++$.ui.plugin.add( "resizable", "containment", {
++
++      start: function() {
++              var element, p, co, ch, cw, width, height,
++                      that = $( this ).resizable( "instance" ),
++                      o = that.options,
++                      el = that.element,
++                      oc = o.containment,
++                      ce = ( oc instanceof $ ) ? oc.get( 0 ) : ( /parent/.test( oc ) ) ? el.parent().get( 0 ) : oc;
++
++              if ( !ce ) {
++                      return;
++              }
++
++              that.containerElement = $( ce );
++
++              if ( /document/.test( oc ) || oc === document ) {
++                      that.containerOffset = {
++                              left: 0,
++                              top: 0
++                      };
++                      that.containerPosition = {
++                              left: 0,
++                              top: 0
++                      };
++
++                      that.parentData = {
++                              element: $( document ),
++                              left: 0,
++                              top: 0,
++                              width: $( document ).width(),
++                              height: $( document ).height() || document.body.parentNode.scrollHeight
++                      };
++              } else {
++                      element = $( ce );
++                      p = [];
++                      $([ "Top", "Right", "Left", "Bottom" ]).each(function( i, name ) {
++                              p[ i ] = that._num( element.css( "padding" + name ) );
++                      });
++
++                      that.containerOffset = element.offset();
++                      that.containerPosition = element.position();
++                      that.containerSize = {
++                              height: ( element.innerHeight() - p[ 3 ] ),
++                              width: ( element.innerWidth() - p[ 1 ] )
++                      };
++
++                      co = that.containerOffset;
++                      ch = that.containerSize.height;
++                      cw = that.containerSize.width;
++                      width = ( that._hasScroll ( ce, "left" ) ? ce.scrollWidth : cw );
++                      height = ( that._hasScroll ( ce ) ? ce.scrollHeight : ch ) ;
++
++                      that.parentData = {
++                              element: ce,
++                              left: co.left,
++                              top: co.top,
++                              width: width,
++                              height: height
++                      };
++              }
++      },
++
++      resize: function( event ) {
++              var woset, hoset, isParent, isOffsetRelative,
++                      that = $( this ).resizable( "instance" ),
++                      o = that.options,
++                      co = that.containerOffset,
++                      cp = that.position,
++                      pRatio = that._aspectRatio || event.shiftKey,
++                      cop = {
++                              top: 0,
++                              left: 0
++                      },
++                      ce = that.containerElement,
++                      continueResize = true;
++
++              if ( ce[ 0 ] !== document && ( /static/ ).test( ce.css( "position" ) ) ) {
++                      cop = co;
++              }
++
++              if ( cp.left < ( that._helper ? co.left : 0 ) ) {
++                      that.size.width = that.size.width +
++                              ( that._helper ?
++                                      ( that.position.left - co.left ) :
++                                      ( that.position.left - cop.left ) );
++
++                      if ( pRatio ) {
++                              that.size.height = that.size.width / that.aspectRatio;
++                              continueResize = false;
++                      }
++                      that.position.left = o.helper ? co.left : 0;
++              }
++
++              if ( cp.top < ( that._helper ? co.top : 0 ) ) {
++                      that.size.height = that.size.height +
++                              ( that._helper ?
++                                      ( that.position.top - co.top ) :
++                                      that.position.top );
++
++                      if ( pRatio ) {
++                              that.size.width = that.size.height * that.aspectRatio;
++                              continueResize = false;
++                      }
++                      that.position.top = that._helper ? co.top : 0;
++              }
++
++              isParent = that.containerElement.get( 0 ) === that.element.parent().get( 0 );
++              isOffsetRelative = /relative|absolute/.test( that.containerElement.css( "position" ) );
++
++              if ( isParent && isOffsetRelative ) {
++                      that.offset.left = that.parentData.left + that.position.left;
++                      that.offset.top = that.parentData.top + that.position.top;
++              } else {
++                      that.offset.left = that.element.offset().left;
++                      that.offset.top = that.element.offset().top;
++              }
++
++              woset = Math.abs( that.sizeDiff.width +
++                      (that._helper ?
++                              that.offset.left - cop.left :
++                              (that.offset.left - co.left)) );
++
++              hoset = Math.abs( that.sizeDiff.height +
++                      (that._helper ?
++                              that.offset.top - cop.top :
++                              (that.offset.top - co.top)) );
++
++              if ( woset + that.size.width >= that.parentData.width ) {
++                      that.size.width = that.parentData.width - woset;
++                      if ( pRatio ) {
++                              that.size.height = that.size.width / that.aspectRatio;
++                              continueResize = false;
++                      }
++              }
++
++              if ( hoset + that.size.height >= that.parentData.height ) {
++                      that.size.height = that.parentData.height - hoset;
++                      if ( pRatio ) {
++                              that.size.width = that.size.height * that.aspectRatio;
++                              continueResize = false;
++                      }
++              }
++
++              if ( !continueResize ) {
++                      that.position.left = that.prevPosition.left;
++                      that.position.top = that.prevPosition.top;
++                      that.size.width = that.prevSize.width;
++                      that.size.height = that.prevSize.height;
++              }
++      },
++
++      stop: function() {
++              var that = $( this ).resizable( "instance" ),
++                      o = that.options,
++                      co = that.containerOffset,
++                      cop = that.containerPosition,
++                      ce = that.containerElement,
++                      helper = $( that.helper ),
++                      ho = helper.offset(),
++                      w = helper.outerWidth() - that.sizeDiff.width,
++                      h = helper.outerHeight() - that.sizeDiff.height;
++
++              if ( that._helper && !o.animate && ( /relative/ ).test( ce.css( "position" ) ) ) {
++                      $( this ).css({
++                              left: ho.left - cop.left - co.left,
++                              width: w,
++                              height: h
++                      });
++              }
++
++              if ( that._helper && !o.animate && ( /static/ ).test( ce.css( "position" ) ) ) {
++                      $( this ).css({
++                              left: ho.left - cop.left - co.left,
++                              width: w,
++                              height: h
++                      });
++              }
++      }
++});
++
++$.ui.plugin.add("resizable", "alsoResize", {
++
++      start: function() {
++              var that = $(this).resizable( "instance" ),
++                      o = that.options;
++
++              $(o.alsoResize).each(function() {
++                      var el = $(this);
++                      el.data("ui-resizable-alsoresize", {
++                              width: parseInt(el.width(), 10), height: parseInt(el.height(), 10),
++                              left: parseInt(el.css("left"), 10), top: parseInt(el.css("top"), 10)
++                      });
++              });
++      },
++
++      resize: function(event, ui) {
++              var that = $(this).resizable( "instance" ),
++                      o = that.options,
++                      os = that.originalSize,
++                      op = that.originalPosition,
++                      delta = {
++                              height: (that.size.height - os.height) || 0,
++                              width: (that.size.width - os.width) || 0,
++                              top: (that.position.top - op.top) || 0,
++                              left: (that.position.left - op.left) || 0
++                      };
++
++                      $(o.alsoResize).each(function() {
++                              var el = $(this), start = $(this).data("ui-resizable-alsoresize"), style = {},
++                                      css = el.parents(ui.originalElement[0]).length ?
++                                                      [ "width", "height" ] :
++                                                      [ "width", "height", "top", "left" ];
++
++                              $.each(css, function(i, prop) {
++                                      var sum = (start[prop] || 0) + (delta[prop] || 0);
++                                      if (sum && sum >= 0) {
++                                              style[prop] = sum || null;
++                                      }
++                              });
++
++                              el.css(style);
++                      });
++      },
++
++      stop: function() {
++              $(this).removeData("resizable-alsoresize");
++      }
++});
++
++$.ui.plugin.add("resizable", "ghost", {
++
++      start: function() {
++
++              var that = $(this).resizable( "instance" ), o = that.options, cs = that.size;
++
++              that.ghost = that.originalElement.clone();
++              that.ghost
++                      .css({
++                              opacity: 0.25,
++                              display: "block",
++                              position: "relative",
++                              height: cs.height,
++                              width: cs.width,
++                              margin: 0,
++                              left: 0,
++                              top: 0
++                      })
++                      .addClass("ui-resizable-ghost")
++                      .addClass(typeof o.ghost === "string" ? o.ghost : "");
++
++              that.ghost.appendTo(that.helper);
++
++      },
++
++      resize: function() {
++              var that = $(this).resizable( "instance" );
++              if (that.ghost) {
++                      that.ghost.css({
++                              position: "relative",
++                              height: that.size.height,
++                              width: that.size.width
++                      });
++              }
++      },
++
++      stop: function() {
++              var that = $(this).resizable( "instance" );
++              if (that.ghost && that.helper) {
++                      that.helper.get(0).removeChild(that.ghost.get(0));
++              }
++      }
++
++});
++
++$.ui.plugin.add("resizable", "grid", {
++
++      resize: function() {
++              var outerDimensions,
++                      that = $(this).resizable( "instance" ),
++                      o = that.options,
++                      cs = that.size,
++                      os = that.originalSize,
++                      op = that.originalPosition,
++                      a = that.axis,
++                      grid = typeof o.grid === "number" ? [ o.grid, o.grid ] : o.grid,
++                      gridX = (grid[0] || 1),
++                      gridY = (grid[1] || 1),
++                      ox = Math.round((cs.width - os.width) / gridX) * gridX,
++                      oy = Math.round((cs.height - os.height) / gridY) * gridY,
++                      newWidth = os.width + ox,
++                      newHeight = os.height + oy,
++                      isMaxWidth = o.maxWidth && (o.maxWidth < newWidth),
++                      isMaxHeight = o.maxHeight && (o.maxHeight < newHeight),
++                      isMinWidth = o.minWidth && (o.minWidth > newWidth),
++                      isMinHeight = o.minHeight && (o.minHeight > newHeight);
++
++              o.grid = grid;
++
++              if (isMinWidth) {
++                      newWidth += gridX;
++              }
++              if (isMinHeight) {
++                      newHeight += gridY;
++              }
++              if (isMaxWidth) {
++                      newWidth -= gridX;
++              }
++              if (isMaxHeight) {
++                      newHeight -= gridY;
++              }
++
++              if (/^(se|s|e)$/.test(a)) {
++                      that.size.width = newWidth;
++                      that.size.height = newHeight;
++              } else if (/^(ne)$/.test(a)) {
++                      that.size.width = newWidth;
++                      that.size.height = newHeight;
++                      that.position.top = op.top - oy;
++              } else if (/^(sw)$/.test(a)) {
++                      that.size.width = newWidth;
++                      that.size.height = newHeight;
++                      that.position.left = op.left - ox;
++              } else {
++                      if ( newHeight - gridY <= 0 || newWidth - gridX <= 0) {
++                              outerDimensions = that._getPaddingPlusBorderDimensions( this );
++                      }
++
++                      if ( newHeight - gridY > 0 ) {
++                              that.size.height = newHeight;
++                              that.position.top = op.top - oy;
++                      } else {
++                              newHeight = gridY - outerDimensions.height;
++                              that.size.height = newHeight;
++                              that.position.top = op.top + os.height - newHeight;
++                      }
++                      if ( newWidth - gridX > 0 ) {
++                              that.size.width = newWidth;
++                              that.position.left = op.left - ox;
++                      } else {
++                              newWidth = gridX - outerDimensions.width;
++                              that.size.width = newWidth;
++                              that.position.left = op.left + os.width - newWidth;
++                      }
++              }
++      }
++
++});
++
++var resizable = $.ui.resizable;
++
++
++/*!
++ * jQuery UI Dialog 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/dialog/
++ */
++
++
++var dialog = $.widget( "ui.dialog", {
++      version: "1.11.4",
++      options: {
++              appendTo: "body",
++              autoOpen: true,
++              buttons: [],
++              closeOnEscape: true,
++              closeText: "Close",
++              dialogClass: "",
++              draggable: true,
++              hide: null,
++              height: "auto",
++              maxHeight: null,
++              maxWidth: null,
++              minHeight: 150,
++              minWidth: 150,
++              modal: false,
++              position: {
++                      my: "center",
++                      at: "center",
++                      of: window,
++                      collision: "fit",
++                      // Ensure the titlebar is always visible
++                      using: function( pos ) {
++                              var topOffset = $( this ).css( pos ).offset().top;
++                              if ( topOffset < 0 ) {
++                                      $( this ).css( "top", pos.top - topOffset );
++                              }
++                      }
++              },
++              resizable: true,
++              show: null,
++              title: null,
++              width: 300,
++
++              // callbacks
++              beforeClose: null,
++              close: null,
++              drag: null,
++              dragStart: null,
++              dragStop: null,
++              focus: null,
++              open: null,
++              resize: null,
++              resizeStart: null,
++              resizeStop: null
++      },
++
++      sizeRelatedOptions: {
++              buttons: true,
++              height: true,
++              maxHeight: true,
++              maxWidth: true,
++              minHeight: true,
++              minWidth: true,
++              width: true
++      },
++
++      resizableRelatedOptions: {
++              maxHeight: true,
++              maxWidth: true,
++              minHeight: true,
++              minWidth: true
++      },
++
++      _create: function() {
++              this.originalCss = {
++                      display: this.element[ 0 ].style.display,
++                      width: this.element[ 0 ].style.width,
++                      minHeight: this.element[ 0 ].style.minHeight,
++                      maxHeight: this.element[ 0 ].style.maxHeight,
++                      height: this.element[ 0 ].style.height
++              };
++              this.originalPosition = {
++                      parent: this.element.parent(),
++                      index: this.element.parent().children().index( this.element )
++              };
++              this.originalTitle = this.element.attr( "title" );
++              this.options.title = this.options.title || this.originalTitle;
++
++              this._createWrapper();
++
++              this.element
++                      .show()
++                      .removeAttr( "title" )
++                      .addClass( "ui-dialog-content ui-widget-content" )
++                      .appendTo( this.uiDialog );
++
++              this._createTitlebar();
++              this._createButtonPane();
++
++              if ( this.options.draggable && $.fn.draggable ) {
++                      this._makeDraggable();
++              }
++              if ( this.options.resizable && $.fn.resizable ) {
++                      this._makeResizable();
++              }
++
++              this._isOpen = false;
++
++              this._trackFocus();
++      },
++
++      _init: function() {
++              if ( this.options.autoOpen ) {
++                      this.open();
++              }
++      },
++
++      _appendTo: function() {
++              var element = this.options.appendTo;
++              if ( element && (element.jquery || element.nodeType) ) {
++                      return $( element );
++              }
++              return this.document.find( element || "body" ).eq( 0 );
++      },
++
++      _destroy: function() {
++              var next,
++                      originalPosition = this.originalPosition;
++
++              this._untrackInstance();
++              this._destroyOverlay();
++
++              this.element
++                      .removeUniqueId()
++                      .removeClass( "ui-dialog-content ui-widget-content" )
++                      .css( this.originalCss )
++                      // Without detaching first, the following becomes really slow
++                      .detach();
++
++              this.uiDialog.stop( true, true ).remove();
++
++              if ( this.originalTitle ) {
++                      this.element.attr( "title", this.originalTitle );
++              }
++
++              next = originalPosition.parent.children().eq( originalPosition.index );
++              // Don't try to place the dialog next to itself (#8613)
++              if ( next.length && next[ 0 ] !== this.element[ 0 ] ) {
++                      next.before( this.element );
++              } else {
++                      originalPosition.parent.append( this.element );
++              }
++      },
++
++      widget: function() {
++              return this.uiDialog;
++      },
++
++      disable: $.noop,
++      enable: $.noop,
++
++      close: function( event ) {
++              var activeElement,
++                      that = this;
++
++              if ( !this._isOpen || this._trigger( "beforeClose", event ) === false ) {
++                      return;
++              }
++
++              this._isOpen = false;
++              this._focusedElement = null;
++              this._destroyOverlay();
++              this._untrackInstance();
++
++              if ( !this.opener.filter( ":focusable" ).focus().length ) {
++
++                      // support: IE9
++                      // IE9 throws an "Unspecified error" accessing document.activeElement from an <iframe>
++                      try {
++                              activeElement = this.document[ 0 ].activeElement;
++
++                              // Support: IE9, IE10
++                              // If the <body> is blurred, IE will switch windows, see #4520
++                              if ( activeElement && activeElement.nodeName.toLowerCase() !== "body" ) {
++
++                                      // Hiding a focused element doesn't trigger blur in WebKit
++                                      // so in case we have nothing to focus on, explicitly blur the active element
++                                      // https://bugs.webkit.org/show_bug.cgi?id=47182
++                                      $( activeElement ).blur();
++                              }
++                      } catch ( error ) {}
++              }
++
++              this._hide( this.uiDialog, this.options.hide, function() {
++                      that._trigger( "close", event );
++              });
++      },
++
++      isOpen: function() {
++              return this._isOpen;
++      },
++
++      moveToTop: function() {
++              this._moveToTop();
++      },
++
++      _moveToTop: function( event, silent ) {
++              var moved = false,
++                      zIndices = this.uiDialog.siblings( ".ui-front:visible" ).map(function() {
++                              return +$( this ).css( "z-index" );
++                      }).get(),
++                      zIndexMax = Math.max.apply( null, zIndices );
++
++              if ( zIndexMax >= +this.uiDialog.css( "z-index" ) ) {
++                      this.uiDialog.css( "z-index", zIndexMax + 1 );
++                      moved = true;
++              }
++
++              if ( moved && !silent ) {
++                      this._trigger( "focus", event );
++              }
++              return moved;
++      },
++
++      open: function() {
++              var that = this;
++              if ( this._isOpen ) {
++                      if ( this._moveToTop() ) {
++                              this._focusTabbable();
++                      }
++                      return;
++              }
++
++              this._isOpen = true;
++              this.opener = $( this.document[ 0 ].activeElement );
++
++              this._size();
++              this._position();
++              this._createOverlay();
++              this._moveToTop( null, true );
++
++              // Ensure the overlay is moved to the top with the dialog, but only when
++              // opening. The overlay shouldn't move after the dialog is open so that
++              // modeless dialogs opened after the modal dialog stack properly.
++              if ( this.overlay ) {
++                      this.overlay.css( "z-index", this.uiDialog.css( "z-index" ) - 1 );
++              }
++
++              this._show( this.uiDialog, this.options.show, function() {
++                      that._focusTabbable();
++                      that._trigger( "focus" );
++              });
++
++              // Track the dialog immediately upon openening in case a focus event
++              // somehow occurs outside of the dialog before an element inside the
++              // dialog is focused (#10152)
++              this._makeFocusTarget();
++
++              this._trigger( "open" );
++      },
++
++      _focusTabbable: function() {
++              // Set focus to the first match:
++              // 1. An element that was focused previously
++              // 2. First element inside the dialog matching [autofocus]
++              // 3. Tabbable element inside the content element
++              // 4. Tabbable element inside the buttonpane
++              // 5. The close button
++              // 6. The dialog itself
++              var hasFocus = this._focusedElement;
++              if ( !hasFocus ) {
++                      hasFocus = this.element.find( "[autofocus]" );
++              }
++              if ( !hasFocus.length ) {
++                      hasFocus = this.element.find( ":tabbable" );
++              }
++              if ( !hasFocus.length ) {
++                      hasFocus = this.uiDialogButtonPane.find( ":tabbable" );
++              }
++              if ( !hasFocus.length ) {
++                      hasFocus = this.uiDialogTitlebarClose.filter( ":tabbable" );
++              }
++              if ( !hasFocus.length ) {
++                      hasFocus = this.uiDialog;
++              }
++              hasFocus.eq( 0 ).focus();
++      },
++
++      _keepFocus: function( event ) {
++              function checkFocus() {
++                      var activeElement = this.document[0].activeElement,
++                              isActive = this.uiDialog[0] === activeElement ||
++                                      $.contains( this.uiDialog[0], activeElement );
++                      if ( !isActive ) {
++                              this._focusTabbable();
++                      }
++              }
++              event.preventDefault();
++              checkFocus.call( this );
++              // support: IE
++              // IE <= 8 doesn't prevent moving focus even with event.preventDefault()
++              // so we check again later
++              this._delay( checkFocus );
++      },
++
++      _createWrapper: function() {
++              this.uiDialog = $("<div>")
++                      .addClass( "ui-dialog ui-widget ui-widget-content ui-corner-all ui-front " +
++                              this.options.dialogClass )
++                      .hide()
++                      .attr({
++                              // Setting tabIndex makes the div focusable
++                              tabIndex: -1,
++                              role: "dialog"
++                      })
++                      .appendTo( this._appendTo() );
++
++              this._on( this.uiDialog, {
++                      keydown: function( event ) {
++                              if ( this.options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode &&
++                                              event.keyCode === $.ui.keyCode.ESCAPE ) {
++                                      event.preventDefault();
++                                      this.close( event );
++                                      return;
++                              }
++
++                              // prevent tabbing out of dialogs
++                              if ( event.keyCode !== $.ui.keyCode.TAB || event.isDefaultPrevented() ) {
++                                      return;
++                              }
++                              var tabbables = this.uiDialog.find( ":tabbable" ),
++                                      first = tabbables.filter( ":first" ),
++                                      last = tabbables.filter( ":last" );
++
++                              if ( ( event.target === last[0] || event.target === this.uiDialog[0] ) && !event.shiftKey ) {
++                                      this._delay(function() {
++                                              first.focus();
++                                      });
++                                      event.preventDefault();
++                              } else if ( ( event.target === first[0] || event.target === this.uiDialog[0] ) && event.shiftKey ) {
++                                      this._delay(function() {
++                                              last.focus();
++                                      });
++                                      event.preventDefault();
++                              }
++                      },
++                      mousedown: function( event ) {
++                              if ( this._moveToTop( event ) ) {
++                                      this._focusTabbable();
++                              }
++                      }
++              });
++
++              // We assume that any existing aria-describedby attribute means
++              // that the dialog content is marked up properly
++              // otherwise we brute force the content as the description
++              if ( !this.element.find( "[aria-describedby]" ).length ) {
++                      this.uiDialog.attr({
++                              "aria-describedby": this.element.uniqueId().attr( "id" )
++                      });
++              }
++      },
++
++      _createTitlebar: function() {
++              var uiDialogTitle;
++
++              this.uiDialogTitlebar = $( "<div>" )
++                      .addClass( "ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix" )
++                      .prependTo( this.uiDialog );
++              this._on( this.uiDialogTitlebar, {
++                      mousedown: function( event ) {
++                              // Don't prevent click on close button (#8838)
++                              // Focusing a dialog that is partially scrolled out of view
++                              // causes the browser to scroll it into view, preventing the click event
++                              if ( !$( event.target ).closest( ".ui-dialog-titlebar-close" ) ) {
++                                      // Dialog isn't getting focus when dragging (#8063)
++                                      this.uiDialog.focus();
++                              }
++                      }
++              });
++
++              // support: IE
++              // Use type="button" to prevent enter keypresses in textboxes from closing the
++              // dialog in IE (#9312)
++              this.uiDialogTitlebarClose = $( "<button type='button'></button>" )
++                      .button({
++                              label: this.options.closeText,
++                              icons: {
++                                      primary: "ui-icon-closethick"
++                              },
++                              text: false
++                      })
++                      .addClass( "ui-dialog-titlebar-close" )
++                      .appendTo( this.uiDialogTitlebar );
++              this._on( this.uiDialogTitlebarClose, {
++                      click: function( event ) {
++                              event.preventDefault();
++                              this.close( event );
++                      }
++              });
++
++              uiDialogTitle = $( "<span>" )
++                      .uniqueId()
++                      .addClass( "ui-dialog-title" )
++                      .prependTo( this.uiDialogTitlebar );
++              this._title( uiDialogTitle );
++
++              this.uiDialog.attr({
++                      "aria-labelledby": uiDialogTitle.attr( "id" )
++              });
++      },
++
++      _title: function( title ) {
++              if ( !this.options.title ) {
++                      title.html( "&#160;" );
++              }
++              title.text( this.options.title );
++      },
++
++      _createButtonPane: function() {
++              this.uiDialogButtonPane = $( "<div>" )
++                      .addClass( "ui-dialog-buttonpane ui-widget-content ui-helper-clearfix" );
++
++              this.uiButtonSet = $( "<div>" )
++                      .addClass( "ui-dialog-buttonset" )
++                      .appendTo( this.uiDialogButtonPane );
++
++              this._createButtons();
++      },
++
++      _createButtons: function() {
++              var that = this,
++                      buttons = this.options.buttons;
++
++              // if we already have a button pane, remove it
++              this.uiDialogButtonPane.remove();
++              this.uiButtonSet.empty();
++
++              if ( $.isEmptyObject( buttons ) || ($.isArray( buttons ) && !buttons.length) ) {
++                      this.uiDialog.removeClass( "ui-dialog-buttons" );
++                      return;
++              }
++
++              $.each( buttons, function( name, props ) {
++                      var click, buttonOptions;
++                      props = $.isFunction( props ) ?
++                              { click: props, text: name } :
++                              props;
++                      // Default to a non-submitting button
++                      props = $.extend( { type: "button" }, props );
++                      // Change the context for the click callback to be the main element
++                      click = props.click;
++                      props.click = function() {
++                              click.apply( that.element[ 0 ], arguments );
++                      };
++                      buttonOptions = {
++                              icons: props.icons,
++                              text: props.showText
++                      };
++                      delete props.icons;
++                      delete props.showText;
++                      $( "<button></button>", props )
++                              .button( buttonOptions )
++                              .appendTo( that.uiButtonSet );
++              });
++              this.uiDialog.addClass( "ui-dialog-buttons" );
++              this.uiDialogButtonPane.appendTo( this.uiDialog );
++      },
++
++      _makeDraggable: function() {
++              var that = this,
++                      options = this.options;
++
++              function filteredUi( ui ) {
++                      return {
++                              position: ui.position,
++                              offset: ui.offset
++                      };
++              }
++
++              this.uiDialog.draggable({
++                      cancel: ".ui-dialog-content, .ui-dialog-titlebar-close",
++                      handle: ".ui-dialog-titlebar",
++                      containment: "document",
++                      start: function( event, ui ) {
++                              $( this ).addClass( "ui-dialog-dragging" );
++                              that._blockFrames();
++                              that._trigger( "dragStart", event, filteredUi( ui ) );
++                      },
++                      drag: function( event, ui ) {
++                              that._trigger( "drag", event, filteredUi( ui ) );
++                      },
++                      stop: function( event, ui ) {
++                              var left = ui.offset.left - that.document.scrollLeft(),
++                                      top = ui.offset.top - that.document.scrollTop();
++
++                              options.position = {
++                                      my: "left top",
++                                      at: "left" + (left >= 0 ? "+" : "") + left + " " +
++                                              "top" + (top >= 0 ? "+" : "") + top,
++                                      of: that.window
++                              };
++                              $( this ).removeClass( "ui-dialog-dragging" );
++                              that._unblockFrames();
++                              that._trigger( "dragStop", event, filteredUi( ui ) );
++                      }
++              });
++      },
++
++      _makeResizable: function() {
++              var that = this,
++                      options = this.options,
++                      handles = options.resizable,
++                      // .ui-resizable has position: relative defined in the stylesheet
++                      // but dialogs have to use absolute or fixed positioning
++                      position = this.uiDialog.css("position"),
++                      resizeHandles = typeof handles === "string" ?
++                              handles :
++                              "n,e,s,w,se,sw,ne,nw";
++
++              function filteredUi( ui ) {
++                      return {
++                              originalPosition: ui.originalPosition,
++                              originalSize: ui.originalSize,
++                              position: ui.position,
++                              size: ui.size
++                      };
++              }
++
++              this.uiDialog.resizable({
++                      cancel: ".ui-dialog-content",
++                      containment: "document",
++                      alsoResize: this.element,
++                      maxWidth: options.maxWidth,
++                      maxHeight: options.maxHeight,
++                      minWidth: options.minWidth,
++                      minHeight: this._minHeight(),
++                      handles: resizeHandles,
++                      start: function( event, ui ) {
++                              $( this ).addClass( "ui-dialog-resizing" );
++                              that._blockFrames();
++                              that._trigger( "resizeStart", event, filteredUi( ui ) );
++                      },
++                      resize: function( event, ui ) {
++                              that._trigger( "resize", event, filteredUi( ui ) );
++                      },
++                      stop: function( event, ui ) {
++                              var offset = that.uiDialog.offset(),
++                                      left = offset.left - that.document.scrollLeft(),
++                                      top = offset.top - that.document.scrollTop();
++
++                              options.height = that.uiDialog.height();
++                              options.width = that.uiDialog.width();
++                              options.position = {
++                                      my: "left top",
++                                      at: "left" + (left >= 0 ? "+" : "") + left + " " +
++                                              "top" + (top >= 0 ? "+" : "") + top,
++                                      of: that.window
++                              };
++                              $( this ).removeClass( "ui-dialog-resizing" );
++                              that._unblockFrames();
++                              that._trigger( "resizeStop", event, filteredUi( ui ) );
++                      }
++              })
++              .css( "position", position );
++      },
++
++      _trackFocus: function() {
++              this._on( this.widget(), {
++                      focusin: function( event ) {
++                              this._makeFocusTarget();
++                              this._focusedElement = $( event.target );
++                      }
++              });
++      },
++
++      _makeFocusTarget: function() {
++              this._untrackInstance();
++              this._trackingInstances().unshift( this );
++      },
++
++      _untrackInstance: function() {
++              var instances = this._trackingInstances(),
++                      exists = $.inArray( this, instances );
++              if ( exists !== -1 ) {
++                      instances.splice( exists, 1 );
++              }
++      },
++
++      _trackingInstances: function() {
++              var instances = this.document.data( "ui-dialog-instances" );
++              if ( !instances ) {
++                      instances = [];
++                      this.document.data( "ui-dialog-instances", instances );
++              }
++              return instances;
++      },
++
++      _minHeight: function() {
++              var options = this.options;
++
++              return options.height === "auto" ?
++                      options.minHeight :
++                      Math.min( options.minHeight, options.height );
++      },
++
++      _position: function() {
++              // Need to show the dialog to get the actual offset in the position plugin
++              var isVisible = this.uiDialog.is( ":visible" );
++              if ( !isVisible ) {
++                      this.uiDialog.show();
++              }
++              this.uiDialog.position( this.options.position );
++              if ( !isVisible ) {
++                      this.uiDialog.hide();
++              }
++      },
++
++      _setOptions: function( options ) {
++              var that = this,
++                      resize = false,
++                      resizableOptions = {};
++
++              $.each( options, function( key, value ) {
++                      that._setOption( key, value );
++
++                      if ( key in that.sizeRelatedOptions ) {
++                              resize = true;
++                      }
++                      if ( key in that.resizableRelatedOptions ) {
++                              resizableOptions[ key ] = value;
++                      }
++              });
++
++              if ( resize ) {
++                      this._size();
++                      this._position();
++              }
++              if ( this.uiDialog.is( ":data(ui-resizable)" ) ) {
++                      this.uiDialog.resizable( "option", resizableOptions );
++              }
++      },
++
++      _setOption: function( key, value ) {
++              var isDraggable, isResizable,
++                      uiDialog = this.uiDialog;
++
++              if ( key === "dialogClass" ) {
++                      uiDialog
++                              .removeClass( this.options.dialogClass )
++                              .addClass( value );
++              }
++
++              if ( key === "disabled" ) {
++                      return;
++              }
++
++              this._super( key, value );
++
++              if ( key === "appendTo" ) {
++                      this.uiDialog.appendTo( this._appendTo() );
++              }
++
++              if ( key === "buttons" ) {
++                      this._createButtons();
++              }
++
++              if ( key === "closeText" ) {
++                      this.uiDialogTitlebarClose.button({
++                              // Ensure that we always pass a string
++                              label: "" + value
++                      });
++              }
++
++              if ( key === "draggable" ) {
++                      isDraggable = uiDialog.is( ":data(ui-draggable)" );
++                      if ( isDraggable && !value ) {
++                              uiDialog.draggable( "destroy" );
++                      }
++
++                      if ( !isDraggable && value ) {
++                              this._makeDraggable();
++                      }
++              }
++
++              if ( key === "position" ) {
++                      this._position();
++              }
++
++              if ( key === "resizable" ) {
++                      // currently resizable, becoming non-resizable
++                      isResizable = uiDialog.is( ":data(ui-resizable)" );
++                      if ( isResizable && !value ) {
++                              uiDialog.resizable( "destroy" );
++                      }
++
++                      // currently resizable, changing handles
++                      if ( isResizable && typeof value === "string" ) {
++                              uiDialog.resizable( "option", "handles", value );
++                      }
++
++                      // currently non-resizable, becoming resizable
++                      if ( !isResizable && value !== false ) {
++                              this._makeResizable();
++                      }
++              }
++
++              if ( key === "title" ) {
++                      this._title( this.uiDialogTitlebar.find( ".ui-dialog-title" ) );
++              }
++      },
++
++      _size: function() {
++              // If the user has resized the dialog, the .ui-dialog and .ui-dialog-content
++              // divs will both have width and height set, so we need to reset them
++              var nonContentHeight, minContentHeight, maxContentHeight,
++                      options = this.options;
++
++              // Reset content sizing
++              this.element.show().css({
++                      width: "auto",
++                      minHeight: 0,
++                      maxHeight: "none",
++                      height: 0
++              });
++
++              if ( options.minWidth > options.width ) {
++                      options.width = options.minWidth;
++              }
++
++              // reset wrapper sizing
++              // determine the height of all the non-content elements
++              nonContentHeight = this.uiDialog.css({
++                              height: "auto",
++                              width: options.width
++                      })
++                      .outerHeight();
++              minContentHeight = Math.max( 0, options.minHeight - nonContentHeight );
++              maxContentHeight = typeof options.maxHeight === "number" ?
++                      Math.max( 0, options.maxHeight - nonContentHeight ) :
++                      "none";
++
++              if ( options.height === "auto" ) {
++                      this.element.css({
++                              minHeight: minContentHeight,
++                              maxHeight: maxContentHeight,
++                              height: "auto"
++                      });
++              } else {
++                      this.element.height( Math.max( 0, options.height - nonContentHeight ) );
++              }
++
++              if ( this.uiDialog.is( ":data(ui-resizable)" ) ) {
++                      this.uiDialog.resizable( "option", "minHeight", this._minHeight() );
++              }
++      },
++
++      _blockFrames: function() {
++              this.iframeBlocks = this.document.find( "iframe" ).map(function() {
++                      var iframe = $( this );
++
++                      return $( "<div>" )
++                              .css({
++                                      position: "absolute",
++                                      width: iframe.outerWidth(),
++                                      height: iframe.outerHeight()
++                              })
++                              .appendTo( iframe.parent() )
++                              .offset( iframe.offset() )[0];
++              });
++      },
++
++      _unblockFrames: function() {
++              if ( this.iframeBlocks ) {
++                      this.iframeBlocks.remove();
++                      delete this.iframeBlocks;
++              }
++      },
++
++      _allowInteraction: function( event ) {
++              if ( $( event.target ).closest( ".ui-dialog" ).length ) {
++                      return true;
++              }
++
++              // TODO: Remove hack when datepicker implements
++              // the .ui-front logic (#8989)
++              return !!$( event.target ).closest( ".ui-datepicker" ).length;
++      },
++
++      _createOverlay: function() {
++              if ( !this.options.modal ) {
++                      return;
++              }
++
++              // We use a delay in case the overlay is created from an
++              // event that we're going to be cancelling (#2804)
++              var isOpening = true;
++              this._delay(function() {
++                      isOpening = false;
++              });
++
++              if ( !this.document.data( "ui-dialog-overlays" ) ) {
++
++                      // Prevent use of anchors and inputs
++                      // Using _on() for an event handler shared across many instances is
++                      // safe because the dialogs stack and must be closed in reverse order
++                      this._on( this.document, {
++                              focusin: function( event ) {
++                                      if ( isOpening ) {
++                                              return;
++                                      }
++
++                                      if ( !this._allowInteraction( event ) ) {
++                                              event.preventDefault();
++                                              this._trackingInstances()[ 0 ]._focusTabbable();
++                                      }
++                              }
++                      });
++              }
++
++              this.overlay = $( "<div>" )
++                      .addClass( "ui-widget-overlay ui-front" )
++                      .appendTo( this._appendTo() );
++              this._on( this.overlay, {
++                      mousedown: "_keepFocus"
++              });
++              this.document.data( "ui-dialog-overlays",
++                      (this.document.data( "ui-dialog-overlays" ) || 0) + 1 );
++      },
++
++      _destroyOverlay: function() {
++              if ( !this.options.modal ) {
++                      return;
++              }
++
++              if ( this.overlay ) {
++                      var overlays = this.document.data( "ui-dialog-overlays" ) - 1;
++
++                      if ( !overlays ) {
++                              this.document
++                                      .unbind( "focusin" )
++                                      .removeData( "ui-dialog-overlays" );
++                      } else {
++                              this.document.data( "ui-dialog-overlays", overlays );
++                      }
++
++                      this.overlay.remove();
++                      this.overlay = null;
++              }
++      }
++});
++
++
++/*!
++ * jQuery UI Droppable 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/droppable/
++ */
++
++
++$.widget( "ui.droppable", {
++      version: "1.11.4",
++      widgetEventPrefix: "drop",
++      options: {
++              accept: "*",
++              activeClass: false,
++              addClasses: true,
++              greedy: false,
++              hoverClass: false,
++              scope: "default",
++              tolerance: "intersect",
++
++              // callbacks
++              activate: null,
++              deactivate: null,
++              drop: null,
++              out: null,
++              over: null
++      },
++      _create: function() {
++
++              var proportions,
++                      o = this.options,
++                      accept = o.accept;
++
++              this.isover = false;
++              this.isout = true;
++
++              this.accept = $.isFunction( accept ) ? accept : function( d ) {
++                      return d.is( accept );
++              };
++
++              this.proportions = function( /* valueToWrite */ ) {
++                      if ( arguments.length ) {
++                              // Store the droppable's proportions
++                              proportions = arguments[ 0 ];
++                      } else {
++                              // Retrieve or derive the droppable's proportions
++                              return proportions ?
++                                      proportions :
++                                      proportions = {
++                                              width: this.element[ 0 ].offsetWidth,
++                                              height: this.element[ 0 ].offsetHeight
++                                      };
++                      }
++              };
++
++              this._addToManager( o.scope );
++
++              o.addClasses && this.element.addClass( "ui-droppable" );
++
++      },
++
++      _addToManager: function( scope ) {
++              // Add the reference and positions to the manager
++              $.ui.ddmanager.droppables[ scope ] = $.ui.ddmanager.droppables[ scope ] || [];
++              $.ui.ddmanager.droppables[ scope ].push( this );
++      },
++
++      _splice: function( drop ) {
++              var i = 0;
++              for ( ; i < drop.length; i++ ) {
++                      if ( drop[ i ] === this ) {
++                              drop.splice( i, 1 );
++                      }
++              }
++      },
++
++      _destroy: function() {
++              var drop = $.ui.ddmanager.droppables[ this.options.scope ];
++
++              this._splice( drop );
++
++              this.element.removeClass( "ui-droppable ui-droppable-disabled" );
++      },
++
++      _setOption: function( key, value ) {
++
++              if ( key === "accept" ) {
++                      this.accept = $.isFunction( value ) ? value : function( d ) {
++                              return d.is( value );
++                      };
++              } else if ( key === "scope" ) {
++                      var drop = $.ui.ddmanager.droppables[ this.options.scope ];
++
++                      this._splice( drop );
++                      this._addToManager( value );
++              }
++
++              this._super( key, value );
++      },
++
++      _activate: function( event ) {
++              var draggable = $.ui.ddmanager.current;
++              if ( this.options.activeClass ) {
++                      this.element.addClass( this.options.activeClass );
++              }
++              if ( draggable ){
++                      this._trigger( "activate", event, this.ui( draggable ) );
++              }
++      },
++
++      _deactivate: function( event ) {
++              var draggable = $.ui.ddmanager.current;
++              if ( this.options.activeClass ) {
++                      this.element.removeClass( this.options.activeClass );
++              }
++              if ( draggable ){
++                      this._trigger( "deactivate", event, this.ui( draggable ) );
++              }
++      },
++
++      _over: function( event ) {
++
++              var draggable = $.ui.ddmanager.current;
++
++              // Bail if draggable and droppable are same element
++              if ( !draggable || ( draggable.currentItem || draggable.element )[ 0 ] === this.element[ 0 ] ) {
++                      return;
++              }
++
++              if ( this.accept.call( this.element[ 0 ], ( draggable.currentItem || draggable.element ) ) ) {
++                      if ( this.options.hoverClass ) {
++                              this.element.addClass( this.options.hoverClass );
++                      }
++                      this._trigger( "over", event, this.ui( draggable ) );
++              }
++
++      },
++
++      _out: function( event ) {
++
++              var draggable = $.ui.ddmanager.current;
++
++              // Bail if draggable and droppable are same element
++              if ( !draggable || ( draggable.currentItem || draggable.element )[ 0 ] === this.element[ 0 ] ) {
++                      return;
++              }
++
++              if ( this.accept.call( this.element[ 0 ], ( draggable.currentItem || draggable.element ) ) ) {
++                      if ( this.options.hoverClass ) {
++                              this.element.removeClass( this.options.hoverClass );
++                      }
++                      this._trigger( "out", event, this.ui( draggable ) );
++              }
++
++      },
++
++      _drop: function( event, custom ) {
++
++              var draggable = custom || $.ui.ddmanager.current,
++                      childrenIntersection = false;
++
++              // Bail if draggable and droppable are same element
++              if ( !draggable || ( draggable.currentItem || draggable.element )[ 0 ] === this.element[ 0 ] ) {
++                      return false;
++              }
++
++              this.element.find( ":data(ui-droppable)" ).not( ".ui-draggable-dragging" ).each(function() {
++                      var inst = $( this ).droppable( "instance" );
++                      if (
++                              inst.options.greedy &&
++                              !inst.options.disabled &&
++                              inst.options.scope === draggable.options.scope &&
++                              inst.accept.call( inst.element[ 0 ], ( draggable.currentItem || draggable.element ) ) &&
++                              $.ui.intersect( draggable, $.extend( inst, { offset: inst.element.offset() } ), inst.options.tolerance, event )
++                      ) { childrenIntersection = true; return false; }
++              });
++              if ( childrenIntersection ) {
++                      return false;
++              }
++
++              if ( this.accept.call( this.element[ 0 ], ( draggable.currentItem || draggable.element ) ) ) {
++                      if ( this.options.activeClass ) {
++                              this.element.removeClass( this.options.activeClass );
++                      }
++                      if ( this.options.hoverClass ) {
++                              this.element.removeClass( this.options.hoverClass );
++                      }
++                      this._trigger( "drop", event, this.ui( draggable ) );
++                      return this.element;
++              }
++
++              return false;
++
++      },
++
++      ui: function( c ) {
++              return {
++                      draggable: ( c.currentItem || c.element ),
++                      helper: c.helper,
++                      position: c.position,
++                      offset: c.positionAbs
++              };
++      }
++
++});
++
++$.ui.intersect = (function() {
++      function isOverAxis( x, reference, size ) {
++              return ( x >= reference ) && ( x < ( reference + size ) );
++      }
++
++      return function( draggable, droppable, toleranceMode, event ) {
++
++              if ( !droppable.offset ) {
++                      return false;
++              }
++
++              var x1 = ( draggable.positionAbs || draggable.position.absolute ).left + draggable.margins.left,
++                      y1 = ( draggable.positionAbs || draggable.position.absolute ).top + draggable.margins.top,
++                      x2 = x1 + draggable.helperProportions.width,
++                      y2 = y1 + draggable.helperProportions.height,
++                      l = droppable.offset.left,
++                      t = droppable.offset.top,
++                      r = l + droppable.proportions().width,
++                      b = t + droppable.proportions().height;
++
++              switch ( toleranceMode ) {
++              case "fit":
++                      return ( l <= x1 && x2 <= r && t <= y1 && y2 <= b );
++              case "intersect":
++                      return ( l < x1 + ( draggable.helperProportions.width / 2 ) && // Right Half
++                              x2 - ( draggable.helperProportions.width / 2 ) < r && // Left Half
++                              t < y1 + ( draggable.helperProportions.height / 2 ) && // Bottom Half
++                              y2 - ( draggable.helperProportions.height / 2 ) < b ); // Top Half
++              case "pointer":
++                      return isOverAxis( event.pageY, t, droppable.proportions().height ) && isOverAxis( event.pageX, l, droppable.proportions().width );
++              case "touch":
++                      return (
++                              ( y1 >= t && y1 <= b ) || // Top edge touching
++                              ( y2 >= t && y2 <= b ) || // Bottom edge touching
++                              ( y1 < t && y2 > b ) // Surrounded vertically
++                      ) && (
++                              ( x1 >= l && x1 <= r ) || // Left edge touching
++                              ( x2 >= l && x2 <= r ) || // Right edge touching
++                              ( x1 < l && x2 > r ) // Surrounded horizontally
++                      );
++              default:
++                      return false;
++              }
++      };
++})();
++
++/*
++      This manager tracks offsets of draggables and droppables
++*/
++$.ui.ddmanager = {
++      current: null,
++      droppables: { "default": [] },
++      prepareOffsets: function( t, event ) {
++
++              var i, j,
++                      m = $.ui.ddmanager.droppables[ t.options.scope ] || [],
++                      type = event ? event.type : null, // workaround for #2317
++                      list = ( t.currentItem || t.element ).find( ":data(ui-droppable)" ).addBack();
++
++              droppablesLoop: for ( i = 0; i < m.length; i++ ) {
++
++                      // No disabled and non-accepted
++                      if ( m[ i ].options.disabled || ( t && !m[ i ].accept.call( m[ i ].element[ 0 ], ( t.currentItem || t.element ) ) ) ) {
++                              continue;
++                      }
++
++                      // Filter out elements in the current dragged item
++                      for ( j = 0; j < list.length; j++ ) {
++                              if ( list[ j ] === m[ i ].element[ 0 ] ) {
++                                      m[ i ].proportions().height = 0;
++                                      continue droppablesLoop;
++                              }
++                      }
++
++                      m[ i ].visible = m[ i ].element.css( "display" ) !== "none";
++                      if ( !m[ i ].visible ) {
++                              continue;
++                      }
++
++                      // Activate the droppable if used directly from draggables
++                      if ( type === "mousedown" ) {
++                              m[ i ]._activate.call( m[ i ], event );
++                      }
++
++                      m[ i ].offset = m[ i ].element.offset();
++                      m[ i ].proportions({ width: m[ i ].element[ 0 ].offsetWidth, height: m[ i ].element[ 0 ].offsetHeight });
++
++              }
++
++      },
++      drop: function( draggable, event ) {
++
++              var dropped = false;
++              // Create a copy of the droppables in case the list changes during the drop (#9116)
++              $.each( ( $.ui.ddmanager.droppables[ draggable.options.scope ] || [] ).slice(), function() {
++
++                      if ( !this.options ) {
++                              return;
++                      }
++                      if ( !this.options.disabled && this.visible && $.ui.intersect( draggable, this, this.options.tolerance, event ) ) {
++                              dropped = this._drop.call( this, event ) || dropped;
++                      }
++
++                      if ( !this.options.disabled && this.visible && this.accept.call( this.element[ 0 ], ( draggable.currentItem || draggable.element ) ) ) {
++                              this.isout = true;
++                              this.isover = false;
++                              this._deactivate.call( this, event );
++                      }
++
++              });
++              return dropped;
++
++      },
++      dragStart: function( draggable, event ) {
++              // Listen for scrolling so that if the dragging causes scrolling the position of the droppables can be recalculated (see #5003)
++              draggable.element.parentsUntil( "body" ).bind( "scroll.droppable", function() {
++                      if ( !draggable.options.refreshPositions ) {
++                              $.ui.ddmanager.prepareOffsets( draggable, event );
++                      }
++              });
++      },
++      drag: function( draggable, event ) {
++
++              // If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
++              if ( draggable.options.refreshPositions ) {
++                      $.ui.ddmanager.prepareOffsets( draggable, event );
++              }
++
++              // Run through all droppables and check their positions based on specific tolerance options
++              $.each( $.ui.ddmanager.droppables[ draggable.options.scope ] || [], function() {
++
++                      if ( this.options.disabled || this.greedyChild || !this.visible ) {
++                              return;
++                      }
++
++                      var parentInstance, scope, parent,
++                              intersects = $.ui.intersect( draggable, this, this.options.tolerance, event ),
++                              c = !intersects && this.isover ? "isout" : ( intersects && !this.isover ? "isover" : null );
++                      if ( !c ) {
++                              return;
++                      }
++
++                      if ( this.options.greedy ) {
++                              // find droppable parents with same scope
++                              scope = this.options.scope;
++                              parent = this.element.parents( ":data(ui-droppable)" ).filter(function() {
++                                      return $( this ).droppable( "instance" ).options.scope === scope;
++                              });
++
++                              if ( parent.length ) {
++                                      parentInstance = $( parent[ 0 ] ).droppable( "instance" );
++                                      parentInstance.greedyChild = ( c === "isover" );
++                              }
++                      }
++
++                      // we just moved into a greedy child
++                      if ( parentInstance && c === "isover" ) {
++                              parentInstance.isover = false;
++                              parentInstance.isout = true;
++                              parentInstance._out.call( parentInstance, event );
++                      }
++
++                      this[ c ] = true;
++                      this[c === "isout" ? "isover" : "isout"] = false;
++                      this[c === "isover" ? "_over" : "_out"].call( this, event );
++
++                      // we just moved out of a greedy child
++                      if ( parentInstance && c === "isout" ) {
++                              parentInstance.isout = false;
++                              parentInstance.isover = true;
++                              parentInstance._over.call( parentInstance, event );
++                      }
++              });
++
++      },
++      dragStop: function( draggable, event ) {
++              draggable.element.parentsUntil( "body" ).unbind( "scroll.droppable" );
++              // Call prepareOffsets one final time since IE does not fire return scroll events when overflow was caused by drag (see #5003)
++              if ( !draggable.options.refreshPositions ) {
++                      $.ui.ddmanager.prepareOffsets( draggable, event );
++              }
++      }
++};
++
++var droppable = $.ui.droppable;
++
++
++/*!
++ * jQuery UI Effects 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/category/effects-core/
++ */
++
++
++var dataSpace = "ui-effects-",
++
++      // Create a local jQuery because jQuery Color relies on it and the
++      // global may not exist with AMD and a custom build (#10199)
++      jQuery = $;
++
++$.effects = {
++      effect: {}
++};
++
++/*!
++ * jQuery Color Animations v2.1.2
++ * https://github.com/jquery/jquery-color
++ *
++ * Copyright 2014 jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * Date: Wed Jan 16 08:47:09 2013 -0600
++ */
++(function( jQuery, undefined ) {
++
++      var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",
++
++      // plusequals test for += 100 -= 100
++      rplusequals = /^([\-+])=\s*(\d+\.?\d*)/,
++      // a set of RE's that can match strings and generate color tuples.
++      stringParsers = [ {
++                      re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
++                      parse: function( execResult ) {
++                              return [
++                                      execResult[ 1 ],
++                                      execResult[ 2 ],
++                                      execResult[ 3 ],
++                                      execResult[ 4 ]
++                              ];
++                      }
++              }, {
++                      re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
++                      parse: function( execResult ) {
++                              return [
++                                      execResult[ 1 ] * 2.55,
++                                      execResult[ 2 ] * 2.55,
++                                      execResult[ 3 ] * 2.55,
++                                      execResult[ 4 ]
++                              ];
++                      }
++              }, {
++                      // this regex ignores A-F because it's compared against an already lowercased string
++                      re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,
++                      parse: function( execResult ) {
++                              return [
++                                      parseInt( execResult[ 1 ], 16 ),
++                                      parseInt( execResult[ 2 ], 16 ),
++                                      parseInt( execResult[ 3 ], 16 )
++                              ];
++                      }
++              }, {
++                      // this regex ignores A-F because it's compared against an already lowercased string
++                      re: /#([a-f0-9])([a-f0-9])([a-f0-9])/,
++                      parse: function( execResult ) {
++                              return [
++                                      parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ),
++                                      parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ),
++                                      parseInt( execResult[ 3 ] + execResult[ 3 ], 16 )
++                              ];
++                      }
++              }, {
++                      re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
++                      space: "hsla",
++                      parse: function( execResult ) {
++                              return [
++                                      execResult[ 1 ],
++                                      execResult[ 2 ] / 100,
++                                      execResult[ 3 ] / 100,
++                                      execResult[ 4 ]
++                              ];
++                      }
++              } ],
++
++      // jQuery.Color( )
++      color = jQuery.Color = function( color, green, blue, alpha ) {
++              return new jQuery.Color.fn.parse( color, green, blue, alpha );
++      },
++      spaces = {
++              rgba: {
++                      props: {
++                              red: {
++                                      idx: 0,
++                                      type: "byte"
++                              },
++                              green: {
++                                      idx: 1,
++                                      type: "byte"
++                              },
++                              blue: {
++                                      idx: 2,
++                                      type: "byte"
++                              }
++                      }
++              },
++
++              hsla: {
++                      props: {
++                              hue: {
++                                      idx: 0,
++                                      type: "degrees"
++                              },
++                              saturation: {
++                                      idx: 1,
++                                      type: "percent"
++                              },
++                              lightness: {
++                                      idx: 2,
++                                      type: "percent"
++                              }
++                      }
++              }
++      },
++      propTypes = {
++              "byte": {
++                      floor: true,
++                      max: 255
++              },
++              "percent": {
++                      max: 1
++              },
++              "degrees": {
++                      mod: 360,
++                      floor: true
++              }
++      },
++      support = color.support = {},
++
++      // element for support tests
++      supportElem = jQuery( "<p>" )[ 0 ],
++
++      // colors = jQuery.Color.names
++      colors,
++
++      // local aliases of functions called often
++      each = jQuery.each;
++
++// determine rgba support immediately
++supportElem.style.cssText = "background-color:rgba(1,1,1,.5)";
++support.rgba = supportElem.style.backgroundColor.indexOf( "rgba" ) > -1;
++
++// define cache name and alpha properties
++// for rgba and hsla spaces
++each( spaces, function( spaceName, space ) {
++      space.cache = "_" + spaceName;
++      space.props.alpha = {
++              idx: 3,
++              type: "percent",
++              def: 1
++      };
++});
++
++function clamp( value, prop, allowEmpty ) {
++      var type = propTypes[ prop.type ] || {};
++
++      if ( value == null ) {
++              return (allowEmpty || !prop.def) ? null : prop.def;
++      }
++
++      // ~~ is an short way of doing floor for positive numbers
++      value = type.floor ? ~~value : parseFloat( value );
++
++      // IE will pass in empty strings as value for alpha,
++      // which will hit this case
++      if ( isNaN( value ) ) {
++              return prop.def;
++      }
++
++      if ( type.mod ) {
++              // we add mod before modding to make sure that negatives values
++              // get converted properly: -10 -> 350
++              return (value + type.mod) % type.mod;
++      }
++
++      // for now all property types without mod have min and max
++      return 0 > value ? 0 : type.max < value ? type.max : value;
++}
++
++function stringParse( string ) {
++      var inst = color(),
++              rgba = inst._rgba = [];
++
++      string = string.toLowerCase();
++
++      each( stringParsers, function( i, parser ) {
++              var parsed,
++                      match = parser.re.exec( string ),
++                      values = match && parser.parse( match ),
++                      spaceName = parser.space || "rgba";
++
++              if ( values ) {
++                      parsed = inst[ spaceName ]( values );
++
++                      // if this was an rgba parse the assignment might happen twice
++                      // oh well....
++                      inst[ spaces[ spaceName ].cache ] = parsed[ spaces[ spaceName ].cache ];
++                      rgba = inst._rgba = parsed._rgba;
++
++                      // exit each( stringParsers ) here because we matched
++                      return false;
++              }
++      });
++
++      // Found a stringParser that handled it
++      if ( rgba.length ) {
++
++              // if this came from a parsed string, force "transparent" when alpha is 0
++              // chrome, (and maybe others) return "transparent" as rgba(0,0,0,0)
++              if ( rgba.join() === "0,0,0,0" ) {
++                      jQuery.extend( rgba, colors.transparent );
++              }
++              return inst;
++      }
++
++      // named colors
++      return colors[ string ];
++}
++
++color.fn = jQuery.extend( color.prototype, {
++      parse: function( red, green, blue, alpha ) {
++              if ( red === undefined ) {
++                      this._rgba = [ null, null, null, null ];
++                      return this;
++              }
++              if ( red.jquery || red.nodeType ) {
++                      red = jQuery( red ).css( green );
++                      green = undefined;
++              }
++
++              var inst = this,
++                      type = jQuery.type( red ),
++                      rgba = this._rgba = [];
++
++              // more than 1 argument specified - assume ( red, green, blue, alpha )
++              if ( green !== undefined ) {
++                      red = [ red, green, blue, alpha ];
++                      type = "array";
++              }
++
++              if ( type === "string" ) {
++                      return this.parse( stringParse( red ) || colors._default );
++              }
++
++              if ( type === "array" ) {
++                      each( spaces.rgba.props, function( key, prop ) {
++                              rgba[ prop.idx ] = clamp( red[ prop.idx ], prop );
++                      });
++                      return this;
++              }
++
++              if ( type === "object" ) {
++                      if ( red instanceof color ) {
++                              each( spaces, function( spaceName, space ) {
++                                      if ( red[ space.cache ] ) {
++                                              inst[ space.cache ] = red[ space.cache ].slice();
++                                      }
++                              });
++                      } else {
++                              each( spaces, function( spaceName, space ) {
++                                      var cache = space.cache;
++                                      each( space.props, function( key, prop ) {
++
++                                              // if the cache doesn't exist, and we know how to convert
++                                              if ( !inst[ cache ] && space.to ) {
++
++                                                      // if the value was null, we don't need to copy it
++                                                      // if the key was alpha, we don't need to copy it either
++                                                      if ( key === "alpha" || red[ key ] == null ) {
++                                                              return;
++                                                      }
++                                                      inst[ cache ] = space.to( inst._rgba );
++                                              }
++
++                                              // this is the only case where we allow nulls for ALL properties.
++                                              // call clamp with alwaysAllowEmpty
++                                              inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true );
++                                      });
++
++                                      // everything defined but alpha?
++                                      if ( inst[ cache ] && jQuery.inArray( null, inst[ cache ].slice( 0, 3 ) ) < 0 ) {
++                                              // use the default of 1
++                                              inst[ cache ][ 3 ] = 1;
++                                              if ( space.from ) {
++                                                      inst._rgba = space.from( inst[ cache ] );
++                                              }
++                                      }
++                              });
++                      }
++                      return this;
++              }
++      },
++      is: function( compare ) {
++              var is = color( compare ),
++                      same = true,
++                      inst = this;
++
++              each( spaces, function( _, space ) {
++                      var localCache,
++                              isCache = is[ space.cache ];
++                      if (isCache) {
++                              localCache = inst[ space.cache ] || space.to && space.to( inst._rgba ) || [];
++                              each( space.props, function( _, prop ) {
++                                      if ( isCache[ prop.idx ] != null ) {
++                                              same = ( isCache[ prop.idx ] === localCache[ prop.idx ] );
++                                              return same;
++                                      }
++                              });
++                      }
++                      return same;
++              });
++              return same;
++      },
++      _space: function() {
++              var used = [],
++                      inst = this;
++              each( spaces, function( spaceName, space ) {
++                      if ( inst[ space.cache ] ) {
++                              used.push( spaceName );
++                      }
++              });
++              return used.pop();
++      },
++      transition: function( other, distance ) {
++              var end = color( other ),
++                      spaceName = end._space(),
++                      space = spaces[ spaceName ],
++                      startColor = this.alpha() === 0 ? color( "transparent" ) : this,
++                      start = startColor[ space.cache ] || space.to( startColor._rgba ),
++                      result = start.slice();
++
++              end = end[ space.cache ];
++              each( space.props, function( key, prop ) {
++                      var index = prop.idx,
++                              startValue = start[ index ],
++                              endValue = end[ index ],
++                              type = propTypes[ prop.type ] || {};
++
++                      // if null, don't override start value
++                      if ( endValue === null ) {
++                              return;
++                      }
++                      // if null - use end
++                      if ( startValue === null ) {
++                              result[ index ] = endValue;
++                      } else {
++                              if ( type.mod ) {
++                                      if ( endValue - startValue > type.mod / 2 ) {
++                                              startValue += type.mod;
++                                      } else if ( startValue - endValue > type.mod / 2 ) {
++                                              startValue -= type.mod;
++                                      }
++                              }
++                              result[ index ] = clamp( ( endValue - startValue ) * distance + startValue, prop );
++                      }
++              });
++              return this[ spaceName ]( result );
++      },
++      blend: function( opaque ) {
++              // if we are already opaque - return ourself
++              if ( this._rgba[ 3 ] === 1 ) {
++                      return this;
++              }
++
++              var rgb = this._rgba.slice(),
++                      a = rgb.pop(),
++                      blend = color( opaque )._rgba;
++
++              return color( jQuery.map( rgb, function( v, i ) {
++                      return ( 1 - a ) * blend[ i ] + a * v;
++              }));
++      },
++      toRgbaString: function() {
++              var prefix = "rgba(",
++                      rgba = jQuery.map( this._rgba, function( v, i ) {
++                              return v == null ? ( i > 2 ? 1 : 0 ) : v;
++                      });
++
++              if ( rgba[ 3 ] === 1 ) {
++                      rgba.pop();
++                      prefix = "rgb(";
++              }
++
++              return prefix + rgba.join() + ")";
++      },
++      toHslaString: function() {
++              var prefix = "hsla(",
++                      hsla = jQuery.map( this.hsla(), function( v, i ) {
++                              if ( v == null ) {
++                                      v = i > 2 ? 1 : 0;
++                              }
++
++                              // catch 1 and 2
++                              if ( i && i < 3 ) {
++                                      v = Math.round( v * 100 ) + "%";
++                              }
++                              return v;
++                      });
++
++              if ( hsla[ 3 ] === 1 ) {
++                      hsla.pop();
++                      prefix = "hsl(";
++              }
++              return prefix + hsla.join() + ")";
++      },
++      toHexString: function( includeAlpha ) {
++              var rgba = this._rgba.slice(),
++                      alpha = rgba.pop();
++
++              if ( includeAlpha ) {
++                      rgba.push( ~~( alpha * 255 ) );
++              }
++
++              return "#" + jQuery.map( rgba, function( v ) {
++
++                      // default to 0 when nulls exist
++                      v = ( v || 0 ).toString( 16 );
++                      return v.length === 1 ? "0" + v : v;
++              }).join("");
++      },
++      toString: function() {
++              return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString();
++      }
++});
++color.fn.parse.prototype = color.fn;
++
++// hsla conversions adapted from:
++// https://code.google.com/p/maashaack/source/browse/packages/graphics/trunk/src/graphics/colors/HUE2RGB.as?r=5021
++
++function hue2rgb( p, q, h ) {
++      h = ( h + 1 ) % 1;
++      if ( h * 6 < 1 ) {
++              return p + ( q - p ) * h * 6;
++      }
++      if ( h * 2 < 1) {
++              return q;
++      }
++      if ( h * 3 < 2 ) {
++              return p + ( q - p ) * ( ( 2 / 3 ) - h ) * 6;
++      }
++      return p;
++}
++
++spaces.hsla.to = function( rgba ) {
++      if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) {
++              return [ null, null, null, rgba[ 3 ] ];
++      }
++      var r = rgba[ 0 ] / 255,
++              g = rgba[ 1 ] / 255,
++              b = rgba[ 2 ] / 255,
++              a = rgba[ 3 ],
++              max = Math.max( r, g, b ),
++              min = Math.min( r, g, b ),
++              diff = max - min,
++              add = max + min,
++              l = add * 0.5,
++              h, s;
++
++      if ( min === max ) {
++              h = 0;
++      } else if ( r === max ) {
++              h = ( 60 * ( g - b ) / diff ) + 360;
++      } else if ( g === max ) {
++              h = ( 60 * ( b - r ) / diff ) + 120;
++      } else {
++              h = ( 60 * ( r - g ) / diff ) + 240;
++      }
++
++      // chroma (diff) == 0 means greyscale which, by definition, saturation = 0%
++      // otherwise, saturation is based on the ratio of chroma (diff) to lightness (add)
++      if ( diff === 0 ) {
++              s = 0;
++      } else if ( l <= 0.5 ) {
++              s = diff / add;
++      } else {
++              s = diff / ( 2 - add );
++      }
++      return [ Math.round(h) % 360, s, l, a == null ? 1 : a ];
++};
++
++spaces.hsla.from = function( hsla ) {
++      if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) {
++              return [ null, null, null, hsla[ 3 ] ];
++      }
++      var h = hsla[ 0 ] / 360,
++              s = hsla[ 1 ],
++              l = hsla[ 2 ],
++              a = hsla[ 3 ],
++              q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s,
++              p = 2 * l - q;
++
++      return [
++              Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ),
++              Math.round( hue2rgb( p, q, h ) * 255 ),
++              Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ),
++              a
++      ];
++};
++
++each( spaces, function( spaceName, space ) {
++      var props = space.props,
++              cache = space.cache,
++              to = space.to,
++              from = space.from;
++
++      // makes rgba() and hsla()
++      color.fn[ spaceName ] = function( value ) {
++
++              // generate a cache for this space if it doesn't exist
++              if ( to && !this[ cache ] ) {
++                      this[ cache ] = to( this._rgba );
++              }
++              if ( value === undefined ) {
++                      return this[ cache ].slice();
++              }
++
++              var ret,
++                      type = jQuery.type( value ),
++                      arr = ( type === "array" || type === "object" ) ? value : arguments,
++                      local = this[ cache ].slice();
++
++              each( props, function( key, prop ) {
++                      var val = arr[ type === "object" ? key : prop.idx ];
++                      if ( val == null ) {
++                              val = local[ prop.idx ];
++                      }
++                      local[ prop.idx ] = clamp( val, prop );
++              });
++
++              if ( from ) {
++                      ret = color( from( local ) );
++                      ret[ cache ] = local;
++                      return ret;
++              } else {
++                      return color( local );
++              }
++      };
++
++      // makes red() green() blue() alpha() hue() saturation() lightness()
++      each( props, function( key, prop ) {
++              // alpha is included in more than one space
++              if ( color.fn[ key ] ) {
++                      return;
++              }
++              color.fn[ key ] = function( value ) {
++                      var vtype = jQuery.type( value ),
++                              fn = ( key === "alpha" ? ( this._hsla ? "hsla" : "rgba" ) : spaceName ),
++                              local = this[ fn ](),
++                              cur = local[ prop.idx ],
++                              match;
++
++                      if ( vtype === "undefined" ) {
++                              return cur;
++                      }
++
++                      if ( vtype === "function" ) {
++                              value = value.call( this, cur );
++                              vtype = jQuery.type( value );
++                      }
++                      if ( value == null && prop.empty ) {
++                              return this;
++                      }
++                      if ( vtype === "string" ) {
++                              match = rplusequals.exec( value );
++                              if ( match ) {
++                                      value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 );
++                              }
++                      }
++                      local[ prop.idx ] = value;
++                      return this[ fn ]( local );
++              };
++      });
++});
++
++// add cssHook and .fx.step function for each named hook.
++// accept a space separated string of properties
++color.hook = function( hook ) {
++      var hooks = hook.split( " " );
++      each( hooks, function( i, hook ) {
++              jQuery.cssHooks[ hook ] = {
++                      set: function( elem, value ) {
++                              var parsed, curElem,
++                                      backgroundColor = "";
++
++                              if ( value !== "transparent" && ( jQuery.type( value ) !== "string" || ( parsed = stringParse( value ) ) ) ) {
++                                      value = color( parsed || value );
++                                      if ( !support.rgba && value._rgba[ 3 ] !== 1 ) {
++                                              curElem = hook === "backgroundColor" ? elem.parentNode : elem;
++                                              while (
++                                                      (backgroundColor === "" || backgroundColor === "transparent") &&
++                                                      curElem && curElem.style
++                                              ) {
++                                                      try {
++                                                              backgroundColor = jQuery.css( curElem, "backgroundColor" );
++                                                              curElem = curElem.parentNode;
++                                                      } catch ( e ) {
++                                                      }
++                                              }
++
++                                              value = value.blend( backgroundColor && backgroundColor !== "transparent" ?
++                                                      backgroundColor :
++                                                      "_default" );
++                                      }
++
++                                      value = value.toRgbaString();
++                              }
++                              try {
++                                      elem.style[ hook ] = value;
++                              } catch ( e ) {
++                                      // wrapped to prevent IE from throwing errors on "invalid" values like 'auto' or 'inherit'
++                              }
++                      }
++              };
++              jQuery.fx.step[ hook ] = function( fx ) {
++                      if ( !fx.colorInit ) {
++                              fx.start = color( fx.elem, hook );
++                              fx.end = color( fx.end );
++                              fx.colorInit = true;
++                      }
++                      jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) );
++              };
++      });
++
++};
++
++color.hook( stepHooks );
++
++jQuery.cssHooks.borderColor = {
++      expand: function( value ) {
++              var expanded = {};
++
++              each( [ "Top", "Right", "Bottom", "Left" ], function( i, part ) {
++                      expanded[ "border" + part + "Color" ] = value;
++              });
++              return expanded;
++      }
++};
++
++// Basic color names only.
++// Usage of any of the other color names requires adding yourself or including
++// jquery.color.svg-names.js.
++colors = jQuery.Color.names = {
++      // 4.1. Basic color keywords
++      aqua: "#00ffff",
++      black: "#000000",
++      blue: "#0000ff",
++      fuchsia: "#ff00ff",
++      gray: "#808080",
++      green: "#008000",
++      lime: "#00ff00",
++      maroon: "#800000",
++      navy: "#000080",
++      olive: "#808000",
++      purple: "#800080",
++      red: "#ff0000",
++      silver: "#c0c0c0",
++      teal: "#008080",
++      white: "#ffffff",
++      yellow: "#ffff00",
++
++      // 4.2.3. "transparent" color keyword
++      transparent: [ null, null, null, 0 ],
++
++      _default: "#ffffff"
++};
++
++})( jQuery );
++
++/******************************************************************************/
++/****************************** CLASS ANIMATIONS ******************************/
++/******************************************************************************/
++(function() {
++
++var classAnimationActions = [ "add", "remove", "toggle" ],
++      shorthandStyles = {
++              border: 1,
++              borderBottom: 1,
++              borderColor: 1,
++              borderLeft: 1,
++              borderRight: 1,
++              borderTop: 1,
++              borderWidth: 1,
++              margin: 1,
++              padding: 1
++      };
++
++$.each([ "borderLeftStyle", "borderRightStyle", "borderBottomStyle", "borderTopStyle" ], function( _, prop ) {
++      $.fx.step[ prop ] = function( fx ) {
++              if ( fx.end !== "none" && !fx.setAttr || fx.pos === 1 && !fx.setAttr ) {
++                      jQuery.style( fx.elem, prop, fx.end );
++                      fx.setAttr = true;
++              }
++      };
++});
++
++function getElementStyles( elem ) {
++      var key, len,
++              style = elem.ownerDocument.defaultView ?
++                      elem.ownerDocument.defaultView.getComputedStyle( elem, null ) :
++                      elem.currentStyle,
++              styles = {};
++
++      if ( style && style.length && style[ 0 ] && style[ style[ 0 ] ] ) {
++              len = style.length;
++              while ( len-- ) {
++                      key = style[ len ];
++                      if ( typeof style[ key ] === "string" ) {
++                              styles[ $.camelCase( key ) ] = style[ key ];
++                      }
++              }
++      // support: Opera, IE <9
++      } else {
++              for ( key in style ) {
++                      if ( typeof style[ key ] === "string" ) {
++                              styles[ key ] = style[ key ];
++                      }
++              }
++      }
++
++      return styles;
++}
++
++function styleDifference( oldStyle, newStyle ) {
++      var diff = {},
++              name, value;
++
++      for ( name in newStyle ) {
++              value = newStyle[ name ];
++              if ( oldStyle[ name ] !== value ) {
++                      if ( !shorthandStyles[ name ] ) {
++                              if ( $.fx.step[ name ] || !isNaN( parseFloat( value ) ) ) {
++                                      diff[ name ] = value;
++                              }
++                      }
++              }
++      }
++
++      return diff;
++}
++
++// support: jQuery <1.8
++if ( !$.fn.addBack ) {
++      $.fn.addBack = function( selector ) {
++              return this.add( selector == null ?
++                      this.prevObject : this.prevObject.filter( selector )
++              );
++      };
++}
++
++$.effects.animateClass = function( value, duration, easing, callback ) {
++      var o = $.speed( duration, easing, callback );
++
++      return this.queue( function() {
++              var animated = $( this ),
++                      baseClass = animated.attr( "class" ) || "",
++                      applyClassChange,
++                      allAnimations = o.children ? animated.find( "*" ).addBack() : animated;
++
++              // map the animated objects to store the original styles.
++              allAnimations = allAnimations.map(function() {
++                      var el = $( this );
++                      return {
++                              el: el,
++                              start: getElementStyles( this )
++                      };
++              });
++
++              // apply class change
++              applyClassChange = function() {
++                      $.each( classAnimationActions, function(i, action) {
++                              if ( value[ action ] ) {
++                                      animated[ action + "Class" ]( value[ action ] );
++                              }
++                      });
++              };
++              applyClassChange();
++
++              // map all animated objects again - calculate new styles and diff
++              allAnimations = allAnimations.map(function() {
++                      this.end = getElementStyles( this.el[ 0 ] );
++                      this.diff = styleDifference( this.start, this.end );
++                      return this;
++              });
++
++              // apply original class
++              animated.attr( "class", baseClass );
++
++              // map all animated objects again - this time collecting a promise
++              allAnimations = allAnimations.map(function() {
++                      var styleInfo = this,
++                              dfd = $.Deferred(),
++                              opts = $.extend({}, o, {
++                                      queue: false,
++                                      complete: function() {
++                                              dfd.resolve( styleInfo );
++                                      }
++                              });
++
++                      this.el.animate( this.diff, opts );
++                      return dfd.promise();
++              });
++
++              // once all animations have completed:
++              $.when.apply( $, allAnimations.get() ).done(function() {
++
++                      // set the final class
++                      applyClassChange();
++
++                      // for each animated element,
++                      // clear all css properties that were animated
++                      $.each( arguments, function() {
++                              var el = this.el;
++                              $.each( this.diff, function(key) {
++                                      el.css( key, "" );
++                              });
++                      });
++
++                      // this is guarnteed to be there if you use jQuery.speed()
++                      // it also handles dequeuing the next anim...
++                      o.complete.call( animated[ 0 ] );
++              });
++      });
++};
++
++$.fn.extend({
++      addClass: (function( orig ) {
++              return function( classNames, speed, easing, callback ) {
++                      return speed ?
++                              $.effects.animateClass.call( this,
++                                      { add: classNames }, speed, easing, callback ) :
++                              orig.apply( this, arguments );
++              };
++      })( $.fn.addClass ),
++
++      removeClass: (function( orig ) {
++              return function( classNames, speed, easing, callback ) {
++                      return arguments.length > 1 ?
++                              $.effects.animateClass.call( this,
++                                      { remove: classNames }, speed, easing, callback ) :
++                              orig.apply( this, arguments );
++              };
++      })( $.fn.removeClass ),
++
++      toggleClass: (function( orig ) {
++              return function( classNames, force, speed, easing, callback ) {
++                      if ( typeof force === "boolean" || force === undefined ) {
++                              if ( !speed ) {
++                                      // without speed parameter
++                                      return orig.apply( this, arguments );
++                              } else {
++                                      return $.effects.animateClass.call( this,
++                                              (force ? { add: classNames } : { remove: classNames }),
++                                              speed, easing, callback );
++                              }
++                      } else {
++                              // without force parameter
++                              return $.effects.animateClass.call( this,
++                                      { toggle: classNames }, force, speed, easing );
++                      }
++              };
++      })( $.fn.toggleClass ),
++
++      switchClass: function( remove, add, speed, easing, callback) {
++              return $.effects.animateClass.call( this, {
++                      add: add,
++                      remove: remove
++              }, speed, easing, callback );
++      }
++});
++
++})();
++
++/******************************************************************************/
++/*********************************** EFFECTS **********************************/
++/******************************************************************************/
++
++(function() {
++
++$.extend( $.effects, {
++      version: "1.11.4",
++
++      // Saves a set of properties in a data storage
++      save: function( element, set ) {
++              for ( var i = 0; i < set.length; i++ ) {
++                      if ( set[ i ] !== null ) {
++                              element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] );
++                      }
++              }
++      },
++
++      // Restores a set of previously saved properties from a data storage
++      restore: function( element, set ) {
++              var val, i;
++              for ( i = 0; i < set.length; i++ ) {
++                      if ( set[ i ] !== null ) {
++                              val = element.data( dataSpace + set[ i ] );
++                              // support: jQuery 1.6.2
++                              // http://bugs.jquery.com/ticket/9917
++                              // jQuery 1.6.2 incorrectly returns undefined for any falsy value.
++                              // We can't differentiate between "" and 0 here, so we just assume
++                              // empty string since it's likely to be a more common value...
++                              if ( val === undefined ) {
++                                      val = "";
++                              }
++                              element.css( set[ i ], val );
++                      }
++              }
++      },
++
++      setMode: function( el, mode ) {
++              if (mode === "toggle") {
++                      mode = el.is( ":hidden" ) ? "show" : "hide";
++              }
++              return mode;
++      },
++
++      // Translates a [top,left] array into a baseline value
++      // this should be a little more flexible in the future to handle a string & hash
++      getBaseline: function( origin, original ) {
++              var y, x;
++              switch ( origin[ 0 ] ) {
++                      case "top": y = 0; break;
++                      case "middle": y = 0.5; break;
++                      case "bottom": y = 1; break;
++                      default: y = origin[ 0 ] / original.height;
++              }
++              switch ( origin[ 1 ] ) {
++                      case "left": x = 0; break;
++                      case "center": x = 0.5; break;
++                      case "right": x = 1; break;
++                      default: x = origin[ 1 ] / original.width;
++              }
++              return {
++                      x: x,
++                      y: y
++              };
++      },
++
++      // Wraps the element around a wrapper that copies position properties
++      createWrapper: function( element ) {
++
++              // if the element is already wrapped, return it
++              if ( element.parent().is( ".ui-effects-wrapper" )) {
++                      return element.parent();
++              }
++
++              // wrap the element
++              var props = {
++                              width: element.outerWidth(true),
++                              height: element.outerHeight(true),
++                              "float": element.css( "float" )
++                      },
++                      wrapper = $( "<div></div>" )
++                              .addClass( "ui-effects-wrapper" )
++                              .css({
++                                      fontSize: "100%",
++                                      background: "transparent",
++                                      border: "none",
++                                      margin: 0,
++                                      padding: 0
++                              }),
++                      // Store the size in case width/height are defined in % - Fixes #5245
++                      size = {
++                              width: element.width(),
++                              height: element.height()
++                      },
++                      active = document.activeElement;
++
++              // support: Firefox
++              // Firefox incorrectly exposes anonymous content
++              // https://bugzilla.mozilla.org/show_bug.cgi?id=561664
++              try {
++                      active.id;
++              } catch ( e ) {
++                      active = document.body;
++              }
++
++              element.wrap( wrapper );
++
++              // Fixes #7595 - Elements lose focus when wrapped.
++              if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
++                      $( active ).focus();
++              }
++
++              wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually lose the reference to the wrapped element
++
++              // transfer positioning properties to the wrapper
++              if ( element.css( "position" ) === "static" ) {
++                      wrapper.css({ position: "relative" });
++                      element.css({ position: "relative" });
++              } else {
++                      $.extend( props, {
++                              position: element.css( "position" ),
++                              zIndex: element.css( "z-index" )
++                      });
++                      $.each([ "top", "left", "bottom", "right" ], function(i, pos) {
++                              props[ pos ] = element.css( pos );
++                              if ( isNaN( parseInt( props[ pos ], 10 ) ) ) {
++                                      props[ pos ] = "auto";
++                              }
++                      });
++                      element.css({
++                              position: "relative",
++                              top: 0,
++                              left: 0,
++                              right: "auto",
++                              bottom: "auto"
++                      });
++              }
++              element.css(size);
++
++              return wrapper.css( props ).show();
++      },
++
++      removeWrapper: function( element ) {
++              var active = document.activeElement;
++
++              if ( element.parent().is( ".ui-effects-wrapper" ) ) {
++                      element.parent().replaceWith( element );
++
++                      // Fixes #7595 - Elements lose focus when wrapped.
++                      if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
++                              $( active ).focus();
++                      }
++              }
++
++              return element;
++      },
++
++      setTransition: function( element, list, factor, value ) {
++              value = value || {};
++              $.each( list, function( i, x ) {
++                      var unit = element.cssUnit( x );
++                      if ( unit[ 0 ] > 0 ) {
++                              value[ x ] = unit[ 0 ] * factor + unit[ 1 ];
++                      }
++              });
++              return value;
++      }
++});
++
++// return an effect options object for the given parameters:
++function _normalizeArguments( effect, options, speed, callback ) {
++
++      // allow passing all options as the first parameter
++      if ( $.isPlainObject( effect ) ) {
++              options = effect;
++              effect = effect.effect;
++      }
++
++      // convert to an object
++      effect = { effect: effect };
++
++      // catch (effect, null, ...)
++      if ( options == null ) {
++              options = {};
++      }
++
++      // catch (effect, callback)
++      if ( $.isFunction( options ) ) {
++              callback = options;
++              speed = null;
++              options = {};
++      }
++
++      // catch (effect, speed, ?)
++      if ( typeof options === "number" || $.fx.speeds[ options ] ) {
++              callback = speed;
++              speed = options;
++              options = {};
++      }
++
++      // catch (effect, options, callback)
++      if ( $.isFunction( speed ) ) {
++              callback = speed;
++              speed = null;
++      }
++
++      // add options to effect
++      if ( options ) {
++              $.extend( effect, options );
++      }
++
++      speed = speed || options.duration;
++      effect.duration = $.fx.off ? 0 :
++              typeof speed === "number" ? speed :
++              speed in $.fx.speeds ? $.fx.speeds[ speed ] :
++              $.fx.speeds._default;
++
++      effect.complete = callback || options.complete;
++
++      return effect;
++}
++
++function standardAnimationOption( option ) {
++      // Valid standard speeds (nothing, number, named speed)
++      if ( !option || typeof option === "number" || $.fx.speeds[ option ] ) {
++              return true;
++      }
++
++      // Invalid strings - treat as "normal" speed
++      if ( typeof option === "string" && !$.effects.effect[ option ] ) {
++              return true;
++      }
++
++      // Complete callback
++      if ( $.isFunction( option ) ) {
++              return true;
++      }
++
++      // Options hash (but not naming an effect)
++      if ( typeof option === "object" && !option.effect ) {
++              return true;
++      }
++
++      // Didn't match any standard API
++      return false;
++}
++
++$.fn.extend({
++      effect: function( /* effect, options, speed, callback */ ) {
++              var args = _normalizeArguments.apply( this, arguments ),
++                      mode = args.mode,
++                      queue = args.queue,
++                      effectMethod = $.effects.effect[ args.effect ];
++
++              if ( $.fx.off || !effectMethod ) {
++                      // delegate to the original method (e.g., .show()) if possible
++                      if ( mode ) {
++                              return this[ mode ]( args.duration, args.complete );
++                      } else {
++                              return this.each( function() {
++                                      if ( args.complete ) {
++                                              args.complete.call( this );
++                                      }
++                              });
++                      }
++              }
++
++              function run( next ) {
++                      var elem = $( this ),
++                              complete = args.complete,
++                              mode = args.mode;
++
++                      function done() {
++                              if ( $.isFunction( complete ) ) {
++                                      complete.call( elem[0] );
++                              }
++                              if ( $.isFunction( next ) ) {
++                                      next();
++                              }
++                      }
++
++                      // If the element already has the correct final state, delegate to
++                      // the core methods so the internal tracking of "olddisplay" works.
++                      if ( elem.is( ":hidden" ) ? mode === "hide" : mode === "show" ) {
++                              elem[ mode ]();
++                              done();
++                      } else {
++                              effectMethod.call( elem[0], args, done );
++                      }
++              }
++
++              return queue === false ? this.each( run ) : this.queue( queue || "fx", run );
++      },
++
++      show: (function( orig ) {
++              return function( option ) {
++                      if ( standardAnimationOption( option ) ) {
++                              return orig.apply( this, arguments );
++                      } else {
++                              var args = _normalizeArguments.apply( this, arguments );
++                              args.mode = "show";
++                              return this.effect.call( this, args );
++                      }
++              };
++      })( $.fn.show ),
++
++      hide: (function( orig ) {
++              return function( option ) {
++                      if ( standardAnimationOption( option ) ) {
++                              return orig.apply( this, arguments );
++                      } else {
++                              var args = _normalizeArguments.apply( this, arguments );
++                              args.mode = "hide";
++                              return this.effect.call( this, args );
++                      }
++              };
++      })( $.fn.hide ),
++
++      toggle: (function( orig ) {
++              return function( option ) {
++                      if ( standardAnimationOption( option ) || typeof option === "boolean" ) {
++                              return orig.apply( this, arguments );
++                      } else {
++                              var args = _normalizeArguments.apply( this, arguments );
++                              args.mode = "toggle";
++                              return this.effect.call( this, args );
++                      }
++              };
++      })( $.fn.toggle ),
++
++      // helper functions
++      cssUnit: function(key) {
++              var style = this.css( key ),
++                      val = [];
++
++              $.each( [ "em", "px", "%", "pt" ], function( i, unit ) {
++                      if ( style.indexOf( unit ) > 0 ) {
++                              val = [ parseFloat( style ), unit ];
++                      }
++              });
++              return val;
++      }
++});
++
++})();
++
++/******************************************************************************/
++/*********************************** EASING ***********************************/
++/******************************************************************************/
++
++(function() {
++
++// based on easing equations from Robert Penner (http://www.robertpenner.com/easing)
++
++var baseEasings = {};
++
++$.each( [ "Quad", "Cubic", "Quart", "Quint", "Expo" ], function( i, name ) {
++      baseEasings[ name ] = function( p ) {
++              return Math.pow( p, i + 2 );
++      };
++});
++
++$.extend( baseEasings, {
++      Sine: function( p ) {
++              return 1 - Math.cos( p * Math.PI / 2 );
++      },
++      Circ: function( p ) {
++              return 1 - Math.sqrt( 1 - p * p );
++      },
++      Elastic: function( p ) {
++              return p === 0 || p === 1 ? p :
++                      -Math.pow( 2, 8 * (p - 1) ) * Math.sin( ( (p - 1) * 80 - 7.5 ) * Math.PI / 15 );
++      },
++      Back: function( p ) {
++              return p * p * ( 3 * p - 2 );
++      },
++      Bounce: function( p ) {
++              var pow2,
++                      bounce = 4;
++
++              while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {}
++              return 1 / Math.pow( 4, 3 - bounce ) - 7.5625 * Math.pow( ( pow2 * 3 - 2 ) / 22 - p, 2 );
++      }
++});
++
++$.each( baseEasings, function( name, easeIn ) {
++      $.easing[ "easeIn" + name ] = easeIn;
++      $.easing[ "easeOut" + name ] = function( p ) {
++              return 1 - easeIn( 1 - p );
++      };
++      $.easing[ "easeInOut" + name ] = function( p ) {
++              return p < 0.5 ?
++                      easeIn( p * 2 ) / 2 :
++                      1 - easeIn( p * -2 + 2 ) / 2;
++      };
++});
++
++})();
++
++var effect = $.effects;
++
++
++/*!
++ * jQuery UI Effects Blind 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/blind-effect/
++ */
++
++
++var effectBlind = $.effects.effect.blind = function( o, done ) {
++      // Create element
++      var el = $( this ),
++              rvertical = /up|down|vertical/,
++              rpositivemotion = /up|left|vertical|horizontal/,
++              props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
++              mode = $.effects.setMode( el, o.mode || "hide" ),
++              direction = o.direction || "up",
++              vertical = rvertical.test( direction ),
++              ref = vertical ? "height" : "width",
++              ref2 = vertical ? "top" : "left",
++              motion = rpositivemotion.test( direction ),
++              animation = {},
++              show = mode === "show",
++              wrapper, distance, margin;
++
++      // if already wrapped, the wrapper's properties are my property. #6245
++      if ( el.parent().is( ".ui-effects-wrapper" ) ) {
++              $.effects.save( el.parent(), props );
++      } else {
++              $.effects.save( el, props );
++      }
++      el.show();
++      wrapper = $.effects.createWrapper( el ).css({
++              overflow: "hidden"
++      });
++
++      distance = wrapper[ ref ]();
++      margin = parseFloat( wrapper.css( ref2 ) ) || 0;
++
++      animation[ ref ] = show ? distance : 0;
++      if ( !motion ) {
++              el
++                      .css( vertical ? "bottom" : "right", 0 )
++                      .css( vertical ? "top" : "left", "auto" )
++                      .css({ position: "absolute" });
++
++              animation[ ref2 ] = show ? margin : distance + margin;
++      }
++
++      // start at 0 if we are showing
++      if ( show ) {
++              wrapper.css( ref, 0 );
++              if ( !motion ) {
++                      wrapper.css( ref2, margin + distance );
++              }
++      }
++
++      // Animate
++      wrapper.animate( animation, {
++              duration: o.duration,
++              easing: o.easing,
++              queue: false,
++              complete: function() {
++                      if ( mode === "hide" ) {
++                              el.hide();
++                      }
++                      $.effects.restore( el, props );
++                      $.effects.removeWrapper( el );
++                      done();
++              }
++      });
++};
++
++
++/*!
++ * jQuery UI Effects Bounce 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/bounce-effect/
++ */
++
++
++var effectBounce = $.effects.effect.bounce = function( o, done ) {
++      var el = $( this ),
++              props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
++
++              // defaults:
++              mode = $.effects.setMode( el, o.mode || "effect" ),
++              hide = mode === "hide",
++              show = mode === "show",
++              direction = o.direction || "up",
++              distance = o.distance,
++              times = o.times || 5,
++
++              // number of internal animations
++              anims = times * 2 + ( show || hide ? 1 : 0 ),
++              speed = o.duration / anims,
++              easing = o.easing,
++
++              // utility:
++              ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
++              motion = ( direction === "up" || direction === "left" ),
++              i,
++              upAnim,
++              downAnim,
++
++              // we will need to re-assemble the queue to stack our animations in place
++              queue = el.queue(),
++              queuelen = queue.length;
++
++      // Avoid touching opacity to prevent clearType and PNG issues in IE
++      if ( show || hide ) {
++              props.push( "opacity" );
++      }
++
++      $.effects.save( el, props );
++      el.show();
++      $.effects.createWrapper( el ); // Create Wrapper
++
++      // default distance for the BIGGEST bounce is the outer Distance / 3
++      if ( !distance ) {
++              distance = el[ ref === "top" ? "outerHeight" : "outerWidth" ]() / 3;
++      }
++
++      if ( show ) {
++              downAnim = { opacity: 1 };
++              downAnim[ ref ] = 0;
++
++              // if we are showing, force opacity 0 and set the initial position
++              // then do the "first" animation
++              el.css( "opacity", 0 )
++                      .css( ref, motion ? -distance * 2 : distance * 2 )
++                      .animate( downAnim, speed, easing );
++      }
++
++      // start at the smallest distance if we are hiding
++      if ( hide ) {
++              distance = distance / Math.pow( 2, times - 1 );
++      }
++
++      downAnim = {};
++      downAnim[ ref ] = 0;
++      // Bounces up/down/left/right then back to 0 -- times * 2 animations happen here
++      for ( i = 0; i < times; i++ ) {
++              upAnim = {};
++              upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance;
++
++              el.animate( upAnim, speed, easing )
++                      .animate( downAnim, speed, easing );
++
++              distance = hide ? distance * 2 : distance / 2;
++      }
++
++      // Last Bounce when Hiding
++      if ( hide ) {
++              upAnim = { opacity: 0 };
++              upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance;
++
++              el.animate( upAnim, speed, easing );
++      }
++
++      el.queue(function() {
++              if ( hide ) {
++                      el.hide();
++              }
++              $.effects.restore( el, props );
++              $.effects.removeWrapper( el );
++              done();
++      });
++
++      // inject all the animations we just queued to be first in line (after "inprogress")
++      if ( queuelen > 1) {
++              queue.splice.apply( queue,
++                      [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
++      }
++      el.dequeue();
++
++};
++
++
++/*!
++ * jQuery UI Effects Clip 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/clip-effect/
++ */
++
++
++var effectClip = $.effects.effect.clip = function( o, done ) {
++      // Create element
++      var el = $( this ),
++              props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
++              mode = $.effects.setMode( el, o.mode || "hide" ),
++              show = mode === "show",
++              direction = o.direction || "vertical",
++              vert = direction === "vertical",
++              size = vert ? "height" : "width",
++              position = vert ? "top" : "left",
++              animation = {},
++              wrapper, animate, distance;
++
++      // Save & Show
++      $.effects.save( el, props );
++      el.show();
++
++      // Create Wrapper
++      wrapper = $.effects.createWrapper( el ).css({
++              overflow: "hidden"
++      });
++      animate = ( el[0].tagName === "IMG" ) ? wrapper : el;
++      distance = animate[ size ]();
++
++      // Shift
++      if ( show ) {
++              animate.css( size, 0 );
++              animate.css( position, distance / 2 );
++      }
++
++      // Create Animation Object:
++      animation[ size ] = show ? distance : 0;
++      animation[ position ] = show ? 0 : distance / 2;
++
++      // Animate
++      animate.animate( animation, {
++              queue: false,
++              duration: o.duration,
++              easing: o.easing,
++              complete: function() {
++                      if ( !show ) {
++                              el.hide();
++                      }
++                      $.effects.restore( el, props );
++                      $.effects.removeWrapper( el );
++                      done();
++              }
++      });
++
++};
++
++
++/*!
++ * jQuery UI Effects Drop 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/drop-effect/
++ */
++
++
++var effectDrop = $.effects.effect.drop = function( o, done ) {
++
++      var el = $( this ),
++              props = [ "position", "top", "bottom", "left", "right", "opacity", "height", "width" ],
++              mode = $.effects.setMode( el, o.mode || "hide" ),
++              show = mode === "show",
++              direction = o.direction || "left",
++              ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
++              motion = ( direction === "up" || direction === "left" ) ? "pos" : "neg",
++              animation = {
++                      opacity: show ? 1 : 0
++              },
++              distance;
++
++      // Adjust
++      $.effects.save( el, props );
++      el.show();
++      $.effects.createWrapper( el );
++
++      distance = o.distance || el[ ref === "top" ? "outerHeight" : "outerWidth" ]( true ) / 2;
++
++      if ( show ) {
++              el
++                      .css( "opacity", 0 )
++                      .css( ref, motion === "pos" ? -distance : distance );
++      }
++
++      // Animation
++      animation[ ref ] = ( show ?
++              ( motion === "pos" ? "+=" : "-=" ) :
++              ( motion === "pos" ? "-=" : "+=" ) ) +
++              distance;
++
++      // Animate
++      el.animate( animation, {
++              queue: false,
++              duration: o.duration,
++              easing: o.easing,
++              complete: function() {
++                      if ( mode === "hide" ) {
++                              el.hide();
++                      }
++                      $.effects.restore( el, props );
++                      $.effects.removeWrapper( el );
++                      done();
++              }
++      });
++};
++
++
++/*!
++ * jQuery UI Effects Explode 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/explode-effect/
++ */
++
++
++var effectExplode = $.effects.effect.explode = function( o, done ) {
++
++      var rows = o.pieces ? Math.round( Math.sqrt( o.pieces ) ) : 3,
++              cells = rows,
++              el = $( this ),
++              mode = $.effects.setMode( el, o.mode || "hide" ),
++              show = mode === "show",
++
++              // show and then visibility:hidden the element before calculating offset
++              offset = el.show().css( "visibility", "hidden" ).offset(),
++
++              // width and height of a piece
++              width = Math.ceil( el.outerWidth() / cells ),
++              height = Math.ceil( el.outerHeight() / rows ),
++              pieces = [],
++
++              // loop
++              i, j, left, top, mx, my;
++
++      // children animate complete:
++      function childComplete() {
++              pieces.push( this );
++              if ( pieces.length === rows * cells ) {
++                      animComplete();
++              }
++      }
++
++      // clone the element for each row and cell.
++      for ( i = 0; i < rows ; i++ ) { // ===>
++              top = offset.top + i * height;
++              my = i - ( rows - 1 ) / 2 ;
++
++              for ( j = 0; j < cells ; j++ ) { // |||
++                      left = offset.left + j * width;
++                      mx = j - ( cells - 1 ) / 2 ;
++
++                      // Create a clone of the now hidden main element that will be absolute positioned
++                      // within a wrapper div off the -left and -top equal to size of our pieces
++                      el
++                              .clone()
++                              .appendTo( "body" )
++                              .wrap( "<div></div>" )
++                              .css({
++                                      position: "absolute",
++                                      visibility: "visible",
++                                      left: -j * width,
++                                      top: -i * height
++                              })
++
++                      // select the wrapper - make it overflow: hidden and absolute positioned based on
++                      // where the original was located +left and +top equal to the size of pieces
++                              .parent()
++                              .addClass( "ui-effects-explode" )
++                              .css({
++                                      position: "absolute",
++                                      overflow: "hidden",
++                                      width: width,
++                                      height: height,
++                                      left: left + ( show ? mx * width : 0 ),
++                                      top: top + ( show ? my * height : 0 ),
++                                      opacity: show ? 0 : 1
++                              }).animate({
++                                      left: left + ( show ? 0 : mx * width ),
++                                      top: top + ( show ? 0 : my * height ),
++                                      opacity: show ? 1 : 0
++                              }, o.duration || 500, o.easing, childComplete );
++              }
++      }
++
++      function animComplete() {
++              el.css({
++                      visibility: "visible"
++              });
++              $( pieces ).remove();
++              if ( !show ) {
++                      el.hide();
++              }
++              done();
++      }
++};
++
++
++/*!
++ * jQuery UI Effects Fade 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/fade-effect/
++ */
++
++
++var effectFade = $.effects.effect.fade = function( o, done ) {
++      var el = $( this ),
++              mode = $.effects.setMode( el, o.mode || "toggle" );
++
++      el.animate({
++              opacity: mode
++      }, {
++              queue: false,
++              duration: o.duration,
++              easing: o.easing,
++              complete: done
++      });
++};
++
++
++/*!
++ * jQuery UI Effects Fold 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/fold-effect/
++ */
++
++
++var effectFold = $.effects.effect.fold = function( o, done ) {
++
++      // Create element
++      var el = $( this ),
++              props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
++              mode = $.effects.setMode( el, o.mode || "hide" ),
++              show = mode === "show",
++              hide = mode === "hide",
++              size = o.size || 15,
++              percent = /([0-9]+)%/.exec( size ),
++              horizFirst = !!o.horizFirst,
++              widthFirst = show !== horizFirst,
++              ref = widthFirst ? [ "width", "height" ] : [ "height", "width" ],
++              duration = o.duration / 2,
++              wrapper, distance,
++              animation1 = {},
++              animation2 = {};
++
++      $.effects.save( el, props );
++      el.show();
++
++      // Create Wrapper
++      wrapper = $.effects.createWrapper( el ).css({
++              overflow: "hidden"
++      });
++      distance = widthFirst ?
++              [ wrapper.width(), wrapper.height() ] :
++              [ wrapper.height(), wrapper.width() ];
++
++      if ( percent ) {
++              size = parseInt( percent[ 1 ], 10 ) / 100 * distance[ hide ? 0 : 1 ];
++      }
++      if ( show ) {
++              wrapper.css( horizFirst ? {
++                      height: 0,
++                      width: size
++              } : {
++                      height: size,
++                      width: 0
++              });
++      }
++
++      // Animation
++      animation1[ ref[ 0 ] ] = show ? distance[ 0 ] : size;
++      animation2[ ref[ 1 ] ] = show ? distance[ 1 ] : 0;
++
++      // Animate
++      wrapper
++              .animate( animation1, duration, o.easing )
++              .animate( animation2, duration, o.easing, function() {
++                      if ( hide ) {
++                              el.hide();
++                      }
++                      $.effects.restore( el, props );
++                      $.effects.removeWrapper( el );
++                      done();
++              });
++
++};
++
++
++/*!
++ * jQuery UI Effects Highlight 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/highlight-effect/
++ */
++
++
++var effectHighlight = $.effects.effect.highlight = function( o, done ) {
++      var elem = $( this ),
++              props = [ "backgroundImage", "backgroundColor", "opacity" ],
++              mode = $.effects.setMode( elem, o.mode || "show" ),
++              animation = {
++                      backgroundColor: elem.css( "backgroundColor" )
++              };
++
++      if (mode === "hide") {
++              animation.opacity = 0;
++      }
++
++      $.effects.save( elem, props );
++
++      elem
++              .show()
++              .css({
++                      backgroundImage: "none",
++                      backgroundColor: o.color || "#ffff99"
++              })
++              .animate( animation, {
++                      queue: false,
++                      duration: o.duration,
++                      easing: o.easing,
++                      complete: function() {
++                              if ( mode === "hide" ) {
++                                      elem.hide();
++                              }
++                              $.effects.restore( elem, props );
++                              done();
++                      }
++              });
++};
++
++
++/*!
++ * jQuery UI Effects Size 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/size-effect/
++ */
++
++
++var effectSize = $.effects.effect.size = function( o, done ) {
++
++      // Create element
++      var original, baseline, factor,
++              el = $( this ),
++              props0 = [ "position", "top", "bottom", "left", "right", "width", "height", "overflow", "opacity" ],
++
++              // Always restore
++              props1 = [ "position", "top", "bottom", "left", "right", "overflow", "opacity" ],
++
++              // Copy for children
++              props2 = [ "width", "height", "overflow" ],
++              cProps = [ "fontSize" ],
++              vProps = [ "borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom" ],
++              hProps = [ "borderLeftWidth", "borderRightWidth", "paddingLeft", "paddingRight" ],
++
++              // Set options
++              mode = $.effects.setMode( el, o.mode || "effect" ),
++              restore = o.restore || mode !== "effect",
++              scale = o.scale || "both",
++              origin = o.origin || [ "middle", "center" ],
++              position = el.css( "position" ),
++              props = restore ? props0 : props1,
++              zero = {
++                      height: 0,
++                      width: 0,
++                      outerHeight: 0,
++                      outerWidth: 0
++              };
++
++      if ( mode === "show" ) {
++              el.show();
++      }
++      original = {
++              height: el.height(),
++              width: el.width(),
++              outerHeight: el.outerHeight(),
++              outerWidth: el.outerWidth()
++      };
++
++      if ( o.mode === "toggle" && mode === "show" ) {
++              el.from = o.to || zero;
++              el.to = o.from || original;
++      } else {
++              el.from = o.from || ( mode === "show" ? zero : original );
++              el.to = o.to || ( mode === "hide" ? zero : original );
++      }
++
++      // Set scaling factor
++      factor = {
++              from: {
++                      y: el.from.height / original.height,
++                      x: el.from.width / original.width
++              },
++              to: {
++                      y: el.to.height / original.height,
++                      x: el.to.width / original.width
++              }
++      };
++
++      // Scale the css box
++      if ( scale === "box" || scale === "both" ) {
++
++              // Vertical props scaling
++              if ( factor.from.y !== factor.to.y ) {
++                      props = props.concat( vProps );
++                      el.from = $.effects.setTransition( el, vProps, factor.from.y, el.from );
++                      el.to = $.effects.setTransition( el, vProps, factor.to.y, el.to );
++              }
++
++              // Horizontal props scaling
++              if ( factor.from.x !== factor.to.x ) {
++                      props = props.concat( hProps );
++                      el.from = $.effects.setTransition( el, hProps, factor.from.x, el.from );
++                      el.to = $.effects.setTransition( el, hProps, factor.to.x, el.to );
++              }
++      }
++
++      // Scale the content
++      if ( scale === "content" || scale === "both" ) {
++
++              // Vertical props scaling
++              if ( factor.from.y !== factor.to.y ) {
++                      props = props.concat( cProps ).concat( props2 );
++                      el.from = $.effects.setTransition( el, cProps, factor.from.y, el.from );
++                      el.to = $.effects.setTransition( el, cProps, factor.to.y, el.to );
++              }
++      }
++
++      $.effects.save( el, props );
++      el.show();
++      $.effects.createWrapper( el );
++      el.css( "overflow", "hidden" ).css( el.from );
++
++      // Adjust
++      if (origin) { // Calculate baseline shifts
++              baseline = $.effects.getBaseline( origin, original );
++              el.from.top = ( original.outerHeight - el.outerHeight() ) * baseline.y;
++              el.from.left = ( original.outerWidth - el.outerWidth() ) * baseline.x;
++              el.to.top = ( original.outerHeight - el.to.outerHeight ) * baseline.y;
++              el.to.left = ( original.outerWidth - el.to.outerWidth ) * baseline.x;
++      }
++      el.css( el.from ); // set top & left
++
++      // Animate
++      if ( scale === "content" || scale === "both" ) { // Scale the children
++
++              // Add margins/font-size
++              vProps = vProps.concat([ "marginTop", "marginBottom" ]).concat(cProps);
++              hProps = hProps.concat([ "marginLeft", "marginRight" ]);
++              props2 = props0.concat(vProps).concat(hProps);
++
++              el.find( "*[width]" ).each( function() {
++                      var child = $( this ),
++                              c_original = {
++                                      height: child.height(),
++                                      width: child.width(),
++                                      outerHeight: child.outerHeight(),
++                                      outerWidth: child.outerWidth()
++                              };
++                      if (restore) {
++                              $.effects.save(child, props2);
++                      }
++
++                      child.from = {
++                              height: c_original.height * factor.from.y,
++                              width: c_original.width * factor.from.x,
++                              outerHeight: c_original.outerHeight * factor.from.y,
++                              outerWidth: c_original.outerWidth * factor.from.x
++                      };
++                      child.to = {
++                              height: c_original.height * factor.to.y,
++                              width: c_original.width * factor.to.x,
++                              outerHeight: c_original.height * factor.to.y,
++                              outerWidth: c_original.width * factor.to.x
++                      };
++
++                      // Vertical props scaling
++                      if ( factor.from.y !== factor.to.y ) {
++                              child.from = $.effects.setTransition( child, vProps, factor.from.y, child.from );
++                              child.to = $.effects.setTransition( child, vProps, factor.to.y, child.to );
++                      }
++
++                      // Horizontal props scaling
++                      if ( factor.from.x !== factor.to.x ) {
++                              child.from = $.effects.setTransition( child, hProps, factor.from.x, child.from );
++                              child.to = $.effects.setTransition( child, hProps, factor.to.x, child.to );
++                      }
++
++                      // Animate children
++                      child.css( child.from );
++                      child.animate( child.to, o.duration, o.easing, function() {
++
++                              // Restore children
++                              if ( restore ) {
++                                      $.effects.restore( child, props2 );
++                              }
++                      });
++              });
++      }
++
++      // Animate
++      el.animate( el.to, {
++              queue: false,
++              duration: o.duration,
++              easing: o.easing,
++              complete: function() {
++                      if ( el.to.opacity === 0 ) {
++                              el.css( "opacity", el.from.opacity );
++                      }
++                      if ( mode === "hide" ) {
++                              el.hide();
++                      }
++                      $.effects.restore( el, props );
++                      if ( !restore ) {
++
++                              // we need to calculate our new positioning based on the scaling
++                              if ( position === "static" ) {
++                                      el.css({
++                                              position: "relative",
++                                              top: el.to.top,
++                                              left: el.to.left
++                                      });
++                              } else {
++                                      $.each([ "top", "left" ], function( idx, pos ) {
++                                              el.css( pos, function( _, str ) {
++                                                      var val = parseInt( str, 10 ),
++                                                              toRef = idx ? el.to.left : el.to.top;
++
++                                                      // if original was "auto", recalculate the new value from wrapper
++                                                      if ( str === "auto" ) {
++                                                              return toRef + "px";
++                                                      }
++
++                                                      return val + toRef + "px";
++                                              });
++                                      });
++                              }
++                      }
++
++                      $.effects.removeWrapper( el );
++                      done();
++              }
++      });
++
++};
++
++
++/*!
++ * jQuery UI Effects Scale 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/scale-effect/
++ */
++
++
++var effectScale = $.effects.effect.scale = function( o, done ) {
++
++      // Create element
++      var el = $( this ),
++              options = $.extend( true, {}, o ),
++              mode = $.effects.setMode( el, o.mode || "effect" ),
++              percent = parseInt( o.percent, 10 ) ||
++                      ( parseInt( o.percent, 10 ) === 0 ? 0 : ( mode === "hide" ? 0 : 100 ) ),
++              direction = o.direction || "both",
++              origin = o.origin,
++              original = {
++                      height: el.height(),
++                      width: el.width(),
++                      outerHeight: el.outerHeight(),
++                      outerWidth: el.outerWidth()
++              },
++              factor = {
++                      y: direction !== "horizontal" ? (percent / 100) : 1,
++                      x: direction !== "vertical" ? (percent / 100) : 1
++              };
++
++      // We are going to pass this effect to the size effect:
++      options.effect = "size";
++      options.queue = false;
++      options.complete = done;
++
++      // Set default origin and restore for show/hide
++      if ( mode !== "effect" ) {
++              options.origin = origin || [ "middle", "center" ];
++              options.restore = true;
++      }
++
++      options.from = o.from || ( mode === "show" ? {
++              height: 0,
++              width: 0,
++              outerHeight: 0,
++              outerWidth: 0
++      } : original );
++      options.to = {
++              height: original.height * factor.y,
++              width: original.width * factor.x,
++              outerHeight: original.outerHeight * factor.y,
++              outerWidth: original.outerWidth * factor.x
++      };
++
++      // Fade option to support puff
++      if ( options.fade ) {
++              if ( mode === "show" ) {
++                      options.from.opacity = 0;
++                      options.to.opacity = 1;
++              }
++              if ( mode === "hide" ) {
++                      options.from.opacity = 1;
++                      options.to.opacity = 0;
++              }
++      }
++
++      // Animate
++      el.effect( options );
++
++};
++
++
++/*!
++ * jQuery UI Effects Puff 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/puff-effect/
++ */
++
++
++var effectPuff = $.effects.effect.puff = function( o, done ) {
++      var elem = $( this ),
++              mode = $.effects.setMode( elem, o.mode || "hide" ),
++              hide = mode === "hide",
++              percent = parseInt( o.percent, 10 ) || 150,
++              factor = percent / 100,
++              original = {
++                      height: elem.height(),
++                      width: elem.width(),
++                      outerHeight: elem.outerHeight(),
++                      outerWidth: elem.outerWidth()
++              };
++
++      $.extend( o, {
++              effect: "scale",
++              queue: false,
++              fade: true,
++              mode: mode,
++              complete: done,
++              percent: hide ? percent : 100,
++              from: hide ?
++                      original :
++                      {
++                              height: original.height * factor,
++                              width: original.width * factor,
++                              outerHeight: original.outerHeight * factor,
++                              outerWidth: original.outerWidth * factor
++                      }
++      });
++
++      elem.effect( o );
++};
++
++
++/*!
++ * jQuery UI Effects Pulsate 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/pulsate-effect/
++ */
++
++
++var effectPulsate = $.effects.effect.pulsate = function( o, done ) {
++      var elem = $( this ),
++              mode = $.effects.setMode( elem, o.mode || "show" ),
++              show = mode === "show",
++              hide = mode === "hide",
++              showhide = ( show || mode === "hide" ),
++
++              // showing or hiding leaves of the "last" animation
++              anims = ( ( o.times || 5 ) * 2 ) + ( showhide ? 1 : 0 ),
++              duration = o.duration / anims,
++              animateTo = 0,
++              queue = elem.queue(),
++              queuelen = queue.length,
++              i;
++
++      if ( show || !elem.is(":visible")) {
++              elem.css( "opacity", 0 ).show();
++              animateTo = 1;
++      }
++
++      // anims - 1 opacity "toggles"
++      for ( i = 1; i < anims; i++ ) {
++              elem.animate({
++                      opacity: animateTo
++              }, duration, o.easing );
++              animateTo = 1 - animateTo;
++      }
++
++      elem.animate({
++              opacity: animateTo
++      }, duration, o.easing);
++
++      elem.queue(function() {
++              if ( hide ) {
++                      elem.hide();
++              }
++              done();
++      });
++
++      // We just queued up "anims" animations, we need to put them next in the queue
++      if ( queuelen > 1 ) {
++              queue.splice.apply( queue,
++                      [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
++      }
++      elem.dequeue();
++};
++
++
++/*!
++ * jQuery UI Effects Shake 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/shake-effect/
++ */
++
++
++var effectShake = $.effects.effect.shake = function( o, done ) {
++
++      var el = $( this ),
++              props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
++              mode = $.effects.setMode( el, o.mode || "effect" ),
++              direction = o.direction || "left",
++              distance = o.distance || 20,
++              times = o.times || 3,
++              anims = times * 2 + 1,
++              speed = Math.round( o.duration / anims ),
++              ref = (direction === "up" || direction === "down") ? "top" : "left",
++              positiveMotion = (direction === "up" || direction === "left"),
++              animation = {},
++              animation1 = {},
++              animation2 = {},
++              i,
++
++              // we will need to re-assemble the queue to stack our animations in place
++              queue = el.queue(),
++              queuelen = queue.length;
++
++      $.effects.save( el, props );
++      el.show();
++      $.effects.createWrapper( el );
++
++      // Animation
++      animation[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance;
++      animation1[ ref ] = ( positiveMotion ? "+=" : "-=" ) + distance * 2;
++      animation2[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance * 2;
++
++      // Animate
++      el.animate( animation, speed, o.easing );
++
++      // Shakes
++      for ( i = 1; i < times; i++ ) {
++              el.animate( animation1, speed, o.easing ).animate( animation2, speed, o.easing );
++      }
++      el
++              .animate( animation1, speed, o.easing )
++              .animate( animation, speed / 2, o.easing )
++              .queue(function() {
++                      if ( mode === "hide" ) {
++                              el.hide();
++                      }
++                      $.effects.restore( el, props );
++                      $.effects.removeWrapper( el );
++                      done();
++              });
++
++      // inject all the animations we just queued to be first in line (after "inprogress")
++      if ( queuelen > 1) {
++              queue.splice.apply( queue,
++                      [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
++      }
++      el.dequeue();
++
++};
++
++
++/*!
++ * jQuery UI Effects Slide 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/slide-effect/
++ */
++
++
++var effectSlide = $.effects.effect.slide = function( o, done ) {
++
++      // Create element
++      var el = $( this ),
++              props = [ "position", "top", "bottom", "left", "right", "width", "height" ],
++              mode = $.effects.setMode( el, o.mode || "show" ),
++              show = mode === "show",
++              direction = o.direction || "left",
++              ref = (direction === "up" || direction === "down") ? "top" : "left",
++              positiveMotion = (direction === "up" || direction === "left"),
++              distance,
++              animation = {};
++
++      // Adjust
++      $.effects.save( el, props );
++      el.show();
++      distance = o.distance || el[ ref === "top" ? "outerHeight" : "outerWidth" ]( true );
++
++      $.effects.createWrapper( el ).css({
++              overflow: "hidden"
++      });
++
++      if ( show ) {
++              el.css( ref, positiveMotion ? (isNaN(distance) ? "-" + distance : -distance) : distance );
++      }
++
++      // Animation
++      animation[ ref ] = ( show ?
++              ( positiveMotion ? "+=" : "-=") :
++              ( positiveMotion ? "-=" : "+=")) +
++              distance;
++
++      // Animate
++      el.animate( animation, {
++              queue: false,
++              duration: o.duration,
++              easing: o.easing,
++              complete: function() {
++                      if ( mode === "hide" ) {
++                              el.hide();
++                      }
++                      $.effects.restore( el, props );
++                      $.effects.removeWrapper( el );
++                      done();
++              }
++      });
++};
++
++
++/*!
++ * jQuery UI Effects Transfer 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/transfer-effect/
++ */
++
++
++var effectTransfer = $.effects.effect.transfer = function( o, done ) {
++      var elem = $( this ),
++              target = $( o.to ),
++              targetFixed = target.css( "position" ) === "fixed",
++              body = $("body"),
++              fixTop = targetFixed ? body.scrollTop() : 0,
++              fixLeft = targetFixed ? body.scrollLeft() : 0,
++              endPosition = target.offset(),
++              animation = {
++                      top: endPosition.top - fixTop,
++                      left: endPosition.left - fixLeft,
++                      height: target.innerHeight(),
++                      width: target.innerWidth()
++              },
++              startPosition = elem.offset(),
++              transfer = $( "<div class='ui-effects-transfer'></div>" )
++                      .appendTo( document.body )
++                      .addClass( o.className )
++                      .css({
++                              top: startPosition.top - fixTop,
++                              left: startPosition.left - fixLeft,
++                              height: elem.innerHeight(),
++                              width: elem.innerWidth(),
++                              position: targetFixed ? "fixed" : "absolute"
++                      })
++                      .animate( animation, o.duration, o.easing, function() {
++                              transfer.remove();
++                              done();
++                      });
++};
++
++
++/*!
++ * jQuery UI Progressbar 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/progressbar/
++ */
++
++
++var progressbar = $.widget( "ui.progressbar", {
++      version: "1.11.4",
++      options: {
++              max: 100,
++              value: 0,
++
++              change: null,
++              complete: null
++      },
++
++      min: 0,
++
++      _create: function() {
++              // Constrain initial value
++              this.oldValue = this.options.value = this._constrainedValue();
++
++              this.element
++                      .addClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" )
++                      .attr({
++                              // Only set static values, aria-valuenow and aria-valuemax are
++                              // set inside _refreshValue()
++                              role: "progressbar",
++                              "aria-valuemin": this.min
++                      });
++
++              this.valueDiv = $( "<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>" )
++                      .appendTo( this.element );
++
++              this._refreshValue();
++      },
++
++      _destroy: function() {
++              this.element
++                      .removeClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" )
++                      .removeAttr( "role" )
++                      .removeAttr( "aria-valuemin" )
++                      .removeAttr( "aria-valuemax" )
++                      .removeAttr( "aria-valuenow" );
++
++              this.valueDiv.remove();
++      },
++
++      value: function( newValue ) {
++              if ( newValue === undefined ) {
++                      return this.options.value;
++              }
++
++              this.options.value = this._constrainedValue( newValue );
++              this._refreshValue();
++      },
++
++      _constrainedValue: function( newValue ) {
++              if ( newValue === undefined ) {
++                      newValue = this.options.value;
++              }
++
++              this.indeterminate = newValue === false;
++
++              // sanitize value
++              if ( typeof newValue !== "number" ) {
++                      newValue = 0;
++              }
++
++              return this.indeterminate ? false :
++                      Math.min( this.options.max, Math.max( this.min, newValue ) );
++      },
++
++      _setOptions: function( options ) {
++              // Ensure "value" option is set after other values (like max)
++              var value = options.value;
++              delete options.value;
++
++              this._super( options );
++
++              this.options.value = this._constrainedValue( value );
++              this._refreshValue();
++      },
++
++      _setOption: function( key, value ) {
++              if ( key === "max" ) {
++                      // Don't allow a max less than min
++                      value = Math.max( this.min, value );
++              }
++              if ( key === "disabled" ) {
++                      this.element
++                              .toggleClass( "ui-state-disabled", !!value )
++                              .attr( "aria-disabled", value );
++              }
++              this._super( key, value );
++      },
++
++      _percentage: function() {
++              return this.indeterminate ? 100 : 100 * ( this.options.value - this.min ) / ( this.options.max - this.min );
++      },
++
++      _refreshValue: function() {
++              var value = this.options.value,
++                      percentage = this._percentage();
++
++              this.valueDiv
++                      .toggle( this.indeterminate || value > this.min )
++                      .toggleClass( "ui-corner-right", value === this.options.max )
++                      .width( percentage.toFixed(0) + "%" );
++
++              this.element.toggleClass( "ui-progressbar-indeterminate", this.indeterminate );
++
++              if ( this.indeterminate ) {
++                      this.element.removeAttr( "aria-valuenow" );
++                      if ( !this.overlayDiv ) {
++                              this.overlayDiv = $( "<div class='ui-progressbar-overlay'></div>" ).appendTo( this.valueDiv );
++                      }
++              } else {
++                      this.element.attr({
++                              "aria-valuemax": this.options.max,
++                              "aria-valuenow": value
++                      });
++                      if ( this.overlayDiv ) {
++                              this.overlayDiv.remove();
++                              this.overlayDiv = null;
++                      }
++              }
++
++              if ( this.oldValue !== value ) {
++                      this.oldValue = value;
++                      this._trigger( "change" );
++              }
++              if ( value === this.options.max ) {
++                      this._trigger( "complete" );
++              }
++      }
++});
++
++
++/*!
++ * jQuery UI Selectable 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/selectable/
++ */
++
++
++var selectable = $.widget("ui.selectable", $.ui.mouse, {
++      version: "1.11.4",
++      options: {
++              appendTo: "body",
++              autoRefresh: true,
++              distance: 0,
++              filter: "*",
++              tolerance: "touch",
++
++              // callbacks
++              selected: null,
++              selecting: null,
++              start: null,
++              stop: null,
++              unselected: null,
++              unselecting: null
++      },
++      _create: function() {
++              var selectees,
++                      that = this;
++
++              this.element.addClass("ui-selectable");
++
++              this.dragged = false;
++
++              // cache selectee children based on filter
++              this.refresh = function() {
++                      selectees = $(that.options.filter, that.element[0]);
++                      selectees.addClass("ui-selectee");
++                      selectees.each(function() {
++                              var $this = $(this),
++                                      pos = $this.offset();
++                              $.data(this, "selectable-item", {
++                                      element: this,
++                                      $element: $this,
++                                      left: pos.left,
++                                      top: pos.top,
++                                      right: pos.left + $this.outerWidth(),
++                                      bottom: pos.top + $this.outerHeight(),
++                                      startselected: false,
++                                      selected: $this.hasClass("ui-selected"),
++                                      selecting: $this.hasClass("ui-selecting"),
++                                      unselecting: $this.hasClass("ui-unselecting")
++                              });
++                      });
++              };
++              this.refresh();
++
++              this.selectees = selectees.addClass("ui-selectee");
++
++              this._mouseInit();
++
++              this.helper = $("<div class='ui-selectable-helper'></div>");
++      },
++
++      _destroy: function() {
++              this.selectees
++                      .removeClass("ui-selectee")
++                      .removeData("selectable-item");
++              this.element
++                      .removeClass("ui-selectable ui-selectable-disabled");
++              this._mouseDestroy();
++      },
++
++      _mouseStart: function(event) {
++              var that = this,
++                      options = this.options;
++
++              this.opos = [ event.pageX, event.pageY ];
++
++              if (this.options.disabled) {
++                      return;
++              }
++
++              this.selectees = $(options.filter, this.element[0]);
++
++              this._trigger("start", event);
++
++              $(options.appendTo).append(this.helper);
++              // position helper (lasso)
++              this.helper.css({
++                      "left": event.pageX,
++                      "top": event.pageY,
++                      "width": 0,
++                      "height": 0
++              });
++
++              if (options.autoRefresh) {
++                      this.refresh();
++              }
++
++              this.selectees.filter(".ui-selected").each(function() {
++                      var selectee = $.data(this, "selectable-item");
++                      selectee.startselected = true;
++                      if (!event.metaKey && !event.ctrlKey) {
++                              selectee.$element.removeClass("ui-selected");
++                              selectee.selected = false;
++                              selectee.$element.addClass("ui-unselecting");
++                              selectee.unselecting = true;
++                              // selectable UNSELECTING callback
++                              that._trigger("unselecting", event, {
++                                      unselecting: selectee.element
++                              });
++                      }
++              });
++
++              $(event.target).parents().addBack().each(function() {
++                      var doSelect,
++                              selectee = $.data(this, "selectable-item");
++                      if (selectee) {
++                              doSelect = (!event.metaKey && !event.ctrlKey) || !selectee.$element.hasClass("ui-selected");
++                              selectee.$element
++                                      .removeClass(doSelect ? "ui-unselecting" : "ui-selected")
++                                      .addClass(doSelect ? "ui-selecting" : "ui-unselecting");
++                              selectee.unselecting = !doSelect;
++                              selectee.selecting = doSelect;
++                              selectee.selected = doSelect;
++                              // selectable (UN)SELECTING callback
++                              if (doSelect) {
++                                      that._trigger("selecting", event, {
++                                              selecting: selectee.element
++                                      });
++                              } else {
++                                      that._trigger("unselecting", event, {
++                                              unselecting: selectee.element
++                                      });
++                              }
++                              return false;
++                      }
++              });
++
++      },
++
++      _mouseDrag: function(event) {
++
++              this.dragged = true;
++
++              if (this.options.disabled) {
++                      return;
++              }
++
++              var tmp,
++                      that = this,
++                      options = this.options,
++                      x1 = this.opos[0],
++                      y1 = this.opos[1],
++                      x2 = event.pageX,
++                      y2 = event.pageY;
++
++              if (x1 > x2) { tmp = x2; x2 = x1; x1 = tmp; }
++              if (y1 > y2) { tmp = y2; y2 = y1; y1 = tmp; }
++              this.helper.css({ left: x1, top: y1, width: x2 - x1, height: y2 - y1 });
++
++              this.selectees.each(function() {
++                      var selectee = $.data(this, "selectable-item"),
++                              hit = false;
++
++                      //prevent helper from being selected if appendTo: selectable
++                      if (!selectee || selectee.element === that.element[0]) {
++                              return;
++                      }
++
++                      if (options.tolerance === "touch") {
++                              hit = ( !(selectee.left > x2 || selectee.right < x1 || selectee.top > y2 || selectee.bottom < y1) );
++                      } else if (options.tolerance === "fit") {
++                              hit = (selectee.left > x1 && selectee.right < x2 && selectee.top > y1 && selectee.bottom < y2);
++                      }
++
++                      if (hit) {
++                              // SELECT
++                              if (selectee.selected) {
++                                      selectee.$element.removeClass("ui-selected");
++                                      selectee.selected = false;
++                              }
++                              if (selectee.unselecting) {
++                                      selectee.$element.removeClass("ui-unselecting");
++                                      selectee.unselecting = false;
++                              }
++                              if (!selectee.selecting) {
++                                      selectee.$element.addClass("ui-selecting");
++                                      selectee.selecting = true;
++                                      // selectable SELECTING callback
++                                      that._trigger("selecting", event, {
++                                              selecting: selectee.element
++                                      });
++                              }
++                      } else {
++                              // UNSELECT
++                              if (selectee.selecting) {
++                                      if ((event.metaKey || event.ctrlKey) && selectee.startselected) {
++                                              selectee.$element.removeClass("ui-selecting");
++                                              selectee.selecting = false;
++                                              selectee.$element.addClass("ui-selected");
++                                              selectee.selected = true;
++                                      } else {
++                                              selectee.$element.removeClass("ui-selecting");
++                                              selectee.selecting = false;
++                                              if (selectee.startselected) {
++                                                      selectee.$element.addClass("ui-unselecting");
++                                                      selectee.unselecting = true;
++                                              }
++                                              // selectable UNSELECTING callback
++                                              that._trigger("unselecting", event, {
++                                                      unselecting: selectee.element
++                                              });
++                                      }
++                              }
++                              if (selectee.selected) {
++                                      if (!event.metaKey && !event.ctrlKey && !selectee.startselected) {
++                                              selectee.$element.removeClass("ui-selected");
++                                              selectee.selected = false;
++
++                                              selectee.$element.addClass("ui-unselecting");
++                                              selectee.unselecting = true;
++                                              // selectable UNSELECTING callback
++                                              that._trigger("unselecting", event, {
++                                                      unselecting: selectee.element
++                                              });
++                                      }
++                              }
++                      }
++              });
++
++              return false;
++      },
++
++      _mouseStop: function(event) {
++              var that = this;
++
++              this.dragged = false;
++
++              $(".ui-unselecting", this.element[0]).each(function() {
++                      var selectee = $.data(this, "selectable-item");
++                      selectee.$element.removeClass("ui-unselecting");
++                      selectee.unselecting = false;
++                      selectee.startselected = false;
++                      that._trigger("unselected", event, {
++                              unselected: selectee.element
++                      });
++              });
++              $(".ui-selecting", this.element[0]).each(function() {
++                      var selectee = $.data(this, "selectable-item");
++                      selectee.$element.removeClass("ui-selecting").addClass("ui-selected");
++                      selectee.selecting = false;
++                      selectee.selected = true;
++                      selectee.startselected = true;
++                      that._trigger("selected", event, {
++                              selected: selectee.element
++                      });
++              });
++              this._trigger("stop", event);
++
++              this.helper.remove();
++
++              return false;
++      }
++
++});
++
++
++/*!
++ * jQuery UI Selectmenu 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/selectmenu
++ */
++
++
++var selectmenu = $.widget( "ui.selectmenu", {
++      version: "1.11.4",
++      defaultElement: "<select>",
++      options: {
++              appendTo: null,
++              disabled: null,
++              icons: {
++                      button: "ui-icon-triangle-1-s"
++              },
++              position: {
++                      my: "left top",
++                      at: "left bottom",
++                      collision: "none"
++              },
++              width: null,
++
++              // callbacks
++              change: null,
++              close: null,
++              focus: null,
++              open: null,
++              select: null
++      },
++
++      _create: function() {
++              var selectmenuId = this.element.uniqueId().attr( "id" );
++              this.ids = {
++                      element: selectmenuId,
++                      button: selectmenuId + "-button",
++                      menu: selectmenuId + "-menu"
++              };
++
++              this._drawButton();
++              this._drawMenu();
++
++              if ( this.options.disabled ) {
++                      this.disable();
++              }
++      },
++
++      _drawButton: function() {
++              var that = this;
++
++              // Associate existing label with the new button
++              this.label = $( "label[for='" + this.ids.element + "']" ).attr( "for", this.ids.button );
++              this._on( this.label, {
++                      click: function( event ) {
++                              this.button.focus();
++                              event.preventDefault();
++                      }
++              });
++
++              // Hide original select element
++              this.element.hide();
++
++              // Create button
++              this.button = $( "<span>", {
++                      "class": "ui-selectmenu-button ui-widget ui-state-default ui-corner-all",
++                      tabindex: this.options.disabled ? -1 : 0,
++                      id: this.ids.button,
++                      role: "combobox",
++                      "aria-expanded": "false",
++                      "aria-autocomplete": "list",
++                      "aria-owns": this.ids.menu,
++                      "aria-haspopup": "true"
++              })
++                      .insertAfter( this.element );
++
++              $( "<span>", {
++                      "class": "ui-icon " + this.options.icons.button
++              })
++                      .prependTo( this.button );
++
++              this.buttonText = $( "<span>", {
++                      "class": "ui-selectmenu-text"
++              })
++                      .appendTo( this.button );
++
++              this._setText( this.buttonText, this.element.find( "option:selected" ).text() );
++              this._resizeButton();
++
++              this._on( this.button, this._buttonEvents );
++              this.button.one( "focusin", function() {
++
++                      // Delay rendering the menu items until the button receives focus.
++                      // The menu may have already been rendered via a programmatic open.
++                      if ( !that.menuItems ) {
++                              that._refreshMenu();
++                      }
++              });
++              this._hoverable( this.button );
++              this._focusable( this.button );
++      },
++
++      _drawMenu: function() {
++              var that = this;
++
++              // Create menu
++              this.menu = $( "<ul>", {
++                      "aria-hidden": "true",
++                      "aria-labelledby": this.ids.button,
++                      id: this.ids.menu
++              });
++
++              // Wrap menu
++              this.menuWrap = $( "<div>", {
++                      "class": "ui-selectmenu-menu ui-front"
++              })
++                      .append( this.menu )
++                      .appendTo( this._appendTo() );
++
++              // Initialize menu widget
++              this.menuInstance = this.menu
++                      .menu({
++                              role: "listbox",
++                              select: function( event, ui ) {
++                                      event.preventDefault();
++
++                                      // support: IE8
++                                      // If the item was selected via a click, the text selection
++                                      // will be destroyed in IE
++                                      that._setSelection();
++
++                                      that._select( ui.item.data( "ui-selectmenu-item" ), event );
++                              },
++                              focus: function( event, ui ) {
++                                      var item = ui.item.data( "ui-selectmenu-item" );
++
++                                      // Prevent inital focus from firing and check if its a newly focused item
++                                      if ( that.focusIndex != null && item.index !== that.focusIndex ) {
++                                              that._trigger( "focus", event, { item: item } );
++                                              if ( !that.isOpen ) {
++                                                      that._select( item, event );
++                                              }
++                                      }
++                                      that.focusIndex = item.index;
++
++                                      that.button.attr( "aria-activedescendant",
++                                              that.menuItems.eq( item.index ).attr( "id" ) );
++                              }
++                      })
++                      .menu( "instance" );
++
++              // Adjust menu styles to dropdown
++              this.menu
++                      .addClass( "ui-corner-bottom" )
++                      .removeClass( "ui-corner-all" );
++
++              // Don't close the menu on mouseleave
++              this.menuInstance._off( this.menu, "mouseleave" );
++
++              // Cancel the menu's collapseAll on document click
++              this.menuInstance._closeOnDocumentClick = function() {
++                      return false;
++              };
++
++              // Selects often contain empty items, but never contain dividers
++              this.menuInstance._isDivider = function() {
++                      return false;
++              };
++      },
++
++      refresh: function() {
++              this._refreshMenu();
++              this._setText( this.buttonText, this._getSelectedItem().text() );
++              if ( !this.options.width ) {
++                      this._resizeButton();
++              }
++      },
++
++      _refreshMenu: function() {
++              this.menu.empty();
++
++              var item,
++                      options = this.element.find( "option" );
++
++              if ( !options.length ) {
++                      return;
++              }
++
++              this._parseOptions( options );
++              this._renderMenu( this.menu, this.items );
++
++              this.menuInstance.refresh();
++              this.menuItems = this.menu.find( "li" ).not( ".ui-selectmenu-optgroup" );
++
++              item = this._getSelectedItem();
++
++              // Update the menu to have the correct item focused
++              this.menuInstance.focus( null, item );
++              this._setAria( item.data( "ui-selectmenu-item" ) );
++
++              // Set disabled state
++              this._setOption( "disabled", this.element.prop( "disabled" ) );
++      },
++
++      open: function( event ) {
++              if ( this.options.disabled ) {
++                      return;
++              }
++
++              // If this is the first time the menu is being opened, render the items
++              if ( !this.menuItems ) {
++                      this._refreshMenu();
++              } else {
++
++                      // Menu clears focus on close, reset focus to selected item
++                      this.menu.find( ".ui-state-focus" ).removeClass( "ui-state-focus" );
++                      this.menuInstance.focus( null, this._getSelectedItem() );
++              }
++
++              this.isOpen = true;
++              this._toggleAttr();
++              this._resizeMenu();
++              this._position();
++
++              this._on( this.document, this._documentClick );
++
++              this._trigger( "open", event );
++      },
++
++      _position: function() {
++              this.menuWrap.position( $.extend( { of: this.button }, this.options.position ) );
++      },
++
++      close: function( event ) {
++              if ( !this.isOpen ) {
++                      return;
++              }
++
++              this.isOpen = false;
++              this._toggleAttr();
++
++              this.range = null;
++              this._off( this.document );
++
++              this._trigger( "close", event );
++      },
++
++      widget: function() {
++              return this.button;
++      },
++
++      menuWidget: function() {
++              return this.menu;
++      },
++
++      _renderMenu: function( ul, items ) {
++              var that = this,
++                      currentOptgroup = "";
++
++              $.each( items, function( index, item ) {
++                      if ( item.optgroup !== currentOptgroup ) {
++                              $( "<li>", {
++                                      "class": "ui-selectmenu-optgroup ui-menu-divider" +
++                                              ( item.element.parent( "optgroup" ).prop( "disabled" ) ?
++                                                      " ui-state-disabled" :
++                                                      "" ),
++                                      text: item.optgroup
++                              })
++                                      .appendTo( ul );
++
++                              currentOptgroup = item.optgroup;
++                      }
++
++                      that._renderItemData( ul, item );
++              });
++      },
++
++      _renderItemData: function( ul, item ) {
++              return this._renderItem( ul, item ).data( "ui-selectmenu-item", item );
++      },
++
++      _renderItem: function( ul, item ) {
++              var li = $( "<li>" );
++
++              if ( item.disabled ) {
++                      li.addClass( "ui-state-disabled" );
++              }
++              this._setText( li, item.label );
++
++              return li.appendTo( ul );
++      },
++
++      _setText: function( element, value ) {
++              if ( value ) {
++                      element.text( value );
++              } else {
++                      element.html( "&#160;" );
++              }
++      },
++
++      _move: function( direction, event ) {
++              var item, next,
++                      filter = ".ui-menu-item";
++
++              if ( this.isOpen ) {
++                      item = this.menuItems.eq( this.focusIndex );
++              } else {
++                      item = this.menuItems.eq( this.element[ 0 ].selectedIndex );
++                      filter += ":not(.ui-state-disabled)";
++              }
++
++              if ( direction === "first" || direction === "last" ) {
++                      next = item[ direction === "first" ? "prevAll" : "nextAll" ]( filter ).eq( -1 );
++              } else {
++                      next = item[ direction + "All" ]( filter ).eq( 0 );
++              }
++
++              if ( next.length ) {
++                      this.menuInstance.focus( event, next );
++              }
++      },
++
++      _getSelectedItem: function() {
++              return this.menuItems.eq( this.element[ 0 ].selectedIndex );
++      },
++
++      _toggle: function( event ) {
++              this[ this.isOpen ? "close" : "open" ]( event );
++      },
++
++      _setSelection: function() {
++              var selection;
++
++              if ( !this.range ) {
++                      return;
++              }
++
++              if ( window.getSelection ) {
++                      selection = window.getSelection();
++                      selection.removeAllRanges();
++                      selection.addRange( this.range );
++
++              // support: IE8
++              } else {
++                      this.range.select();
++              }
++
++              // support: IE
++              // Setting the text selection kills the button focus in IE, but
++              // restoring the focus doesn't kill the selection.
++              this.button.focus();
++      },
++
++      _documentClick: {
++              mousedown: function( event ) {
++                      if ( !this.isOpen ) {
++                              return;
++                      }
++
++                      if ( !$( event.target ).closest( ".ui-selectmenu-menu, #" + this.ids.button ).length ) {
++                              this.close( event );
++                      }
++              }
++      },
++
++      _buttonEvents: {
++
++              // Prevent text selection from being reset when interacting with the selectmenu (#10144)
++              mousedown: function() {
++                      var selection;
++
++                      if ( window.getSelection ) {
++                              selection = window.getSelection();
++                              if ( selection.rangeCount ) {
++                                      this.range = selection.getRangeAt( 0 );
++                              }
++
++                      // support: IE8
++                      } else {
++                              this.range = document.selection.createRange();
++                      }
++              },
++
++              click: function( event ) {
++                      this._setSelection();
++                      this._toggle( event );
++              },
++
++              keydown: function( event ) {
++                      var preventDefault = true;
++                      switch ( event.keyCode ) {
++                              case $.ui.keyCode.TAB:
++                              case $.ui.keyCode.ESCAPE:
++                                      this.close( event );
++                                      preventDefault = false;
++                                      break;
++                              case $.ui.keyCode.ENTER:
++                                      if ( this.isOpen ) {
++                                              this._selectFocusedItem( event );
++                                      }
++                                      break;
++                              case $.ui.keyCode.UP:
++                                      if ( event.altKey ) {
++                                              this._toggle( event );
++                                      } else {
++                                              this._move( "prev", event );
++                                      }
++                                      break;
++                              case $.ui.keyCode.DOWN:
++                                      if ( event.altKey ) {
++                                              this._toggle( event );
++                                      } else {
++                                              this._move( "next", event );
++                                      }
++                                      break;
++                              case $.ui.keyCode.SPACE:
++                                      if ( this.isOpen ) {
++                                              this._selectFocusedItem( event );
++                                      } else {
++                                              this._toggle( event );
++                                      }
++                                      break;
++                              case $.ui.keyCode.LEFT:
++                                      this._move( "prev", event );
++                                      break;
++                              case $.ui.keyCode.RIGHT:
++                                      this._move( "next", event );
++                                      break;
++                              case $.ui.keyCode.HOME:
++                              case $.ui.keyCode.PAGE_UP:
++                                      this._move( "first", event );
++                                      break;
++                              case $.ui.keyCode.END:
++                              case $.ui.keyCode.PAGE_DOWN:
++                                      this._move( "last", event );
++                                      break;
++                              default:
++                                      this.menu.trigger( event );
++                                      preventDefault = false;
++                      }
++
++                      if ( preventDefault ) {
++                              event.preventDefault();
++                      }
++              }
++      },
++
++      _selectFocusedItem: function( event ) {
++              var item = this.menuItems.eq( this.focusIndex );
++              if ( !item.hasClass( "ui-state-disabled" ) ) {
++                      this._select( item.data( "ui-selectmenu-item" ), event );
++              }
++      },
++
++      _select: function( item, event ) {
++              var oldIndex = this.element[ 0 ].selectedIndex;
++
++              // Change native select element
++              this.element[ 0 ].selectedIndex = item.index;
++              this._setText( this.buttonText, item.label );
++              this._setAria( item );
++              this._trigger( "select", event, { item: item } );
++
++              if ( item.index !== oldIndex ) {
++                      this._trigger( "change", event, { item: item } );
++              }
++
++              this.close( event );
++      },
++
++      _setAria: function( item ) {
++              var id = this.menuItems.eq( item.index ).attr( "id" );
++
++              this.button.attr({
++                      "aria-labelledby": id,
++                      "aria-activedescendant": id
++              });
++              this.menu.attr( "aria-activedescendant", id );
++      },
++
++      _setOption: function( key, value ) {
++              if ( key === "icons" ) {
++                      this.button.find( "span.ui-icon" )
++                              .removeClass( this.options.icons.button )
++                              .addClass( value.button );
++              }
++
++              this._super( key, value );
++
++              if ( key === "appendTo" ) {
++                      this.menuWrap.appendTo( this._appendTo() );
++              }
++
++              if ( key === "disabled" ) {
++                      this.menuInstance.option( "disabled", value );
++                      this.button
++                              .toggleClass( "ui-state-disabled", value )
++                              .attr( "aria-disabled", value );
++
++                      this.element.prop( "disabled", value );
++                      if ( value ) {
++                              this.button.attr( "tabindex", -1 );
++                              this.close();
++                      } else {
++                              this.button.attr( "tabindex", 0 );
++                      }
++              }
++
++              if ( key === "width" ) {
++                      this._resizeButton();
++              }
++      },
++
++      _appendTo: function() {
++              var element = this.options.appendTo;
++
++              if ( element ) {
++                      element = element.jquery || element.nodeType ?
++                              $( element ) :
++                              this.document.find( element ).eq( 0 );
++              }
++
++              if ( !element || !element[ 0 ] ) {
++                      element = this.element.closest( ".ui-front" );
++              }
++
++              if ( !element.length ) {
++                      element = this.document[ 0 ].body;
++              }
++
++              return element;
++      },
++
++      _toggleAttr: function() {
++              this.button
++                      .toggleClass( "ui-corner-top", this.isOpen )
++                      .toggleClass( "ui-corner-all", !this.isOpen )
++                      .attr( "aria-expanded", this.isOpen );
++              this.menuWrap.toggleClass( "ui-selectmenu-open", this.isOpen );
++              this.menu.attr( "aria-hidden", !this.isOpen );
++      },
++
++      _resizeButton: function() {
++              var width = this.options.width;
++
++              if ( !width ) {
++                      width = this.element.show().outerWidth();
++                      this.element.hide();
++              }
++
++              this.button.outerWidth( width );
++      },
++
++      _resizeMenu: function() {
++              this.menu.outerWidth( Math.max(
++                      this.button.outerWidth(),
++
++                      // support: IE10
++                      // IE10 wraps long text (possibly a rounding bug)
++                      // so we add 1px to avoid the wrapping
++                      this.menu.width( "" ).outerWidth() + 1
++              ) );
++      },
++
++      _getCreateOptions: function() {
++              return { disabled: this.element.prop( "disabled" ) };
++      },
++
++      _parseOptions: function( options ) {
++              var data = [];
++              options.each(function( index, item ) {
++                      var option = $( item ),
++                              optgroup = option.parent( "optgroup" );
++                      data.push({
++                              element: option,
++                              index: index,
++                              value: option.val(),
++                              label: option.text(),
++                              optgroup: optgroup.attr( "label" ) || "",
++                              disabled: optgroup.prop( "disabled" ) || option.prop( "disabled" )
++                      });
++              });
++              this.items = data;
++      },
++
++      _destroy: function() {
++              this.menuWrap.remove();
++              this.button.remove();
++              this.element.show();
++              this.element.removeUniqueId();
++              this.label.attr( "for", this.ids.element );
++      }
++});
++
++
++/*!
++ * jQuery UI Slider 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/slider/
++ */
++
++
++var slider = $.widget( "ui.slider", $.ui.mouse, {
++      version: "1.11.4",
++      widgetEventPrefix: "slide",
++
++      options: {
++              animate: false,
++              distance: 0,
++              max: 100,
++              min: 0,
++              orientation: "horizontal",
++              range: false,
++              step: 1,
++              value: 0,
++              values: null,
++
++              // callbacks
++              change: null,
++              slide: null,
++              start: null,
++              stop: null
++      },
++
++      // number of pages in a slider
++      // (how many times can you page up/down to go through the whole range)
++      numPages: 5,
++
++      _create: function() {
++              this._keySliding = false;
++              this._mouseSliding = false;
++              this._animateOff = true;
++              this._handleIndex = null;
++              this._detectOrientation();
++              this._mouseInit();
++              this._calculateNewMax();
++
++              this.element
++                      .addClass( "ui-slider" +
++                              " ui-slider-" + this.orientation +
++                              " ui-widget" +
++                              " ui-widget-content" +
++                              " ui-corner-all");
++
++              this._refresh();
++              this._setOption( "disabled", this.options.disabled );
++
++              this._animateOff = false;
++      },
++
++      _refresh: function() {
++              this._createRange();
++              this._createHandles();
++              this._setupEvents();
++              this._refreshValue();
++      },
++
++      _createHandles: function() {
++              var i, handleCount,
++                      options = this.options,
++                      existingHandles = this.element.find( ".ui-slider-handle" ).addClass( "ui-state-default ui-corner-all" ),
++                      handle = "<span class='ui-slider-handle ui-state-default ui-corner-all' tabindex='0'></span>",
++                      handles = [];
++
++              handleCount = ( options.values && options.values.length ) || 1;
++
++              if ( existingHandles.length > handleCount ) {
++                      existingHandles.slice( handleCount ).remove();
++                      existingHandles = existingHandles.slice( 0, handleCount );
++              }
++
++              for ( i = existingHandles.length; i < handleCount; i++ ) {
++                      handles.push( handle );
++              }
++
++              this.handles = existingHandles.add( $( handles.join( "" ) ).appendTo( this.element ) );
++
++              this.handle = this.handles.eq( 0 );
++
++              this.handles.each(function( i ) {
++                      $( this ).data( "ui-slider-handle-index", i );
++              });
++      },
++
++      _createRange: function() {
++              var options = this.options,
++                      classes = "";
++
++              if ( options.range ) {
++                      if ( options.range === true ) {
++                              if ( !options.values ) {
++                                      options.values = [ this._valueMin(), this._valueMin() ];
++                              } else if ( options.values.length && options.values.length !== 2 ) {
++                                      options.values = [ options.values[0], options.values[0] ];
++                              } else if ( $.isArray( options.values ) ) {
++                                      options.values = options.values.slice(0);
++                              }
++                      }
++
++                      if ( !this.range || !this.range.length ) {
++                              this.range = $( "<div></div>" )
++                                      .appendTo( this.element );
++
++                              classes = "ui-slider-range" +
++                              // note: this isn't the most fittingly semantic framework class for this element,
++                              // but worked best visually with a variety of themes
++                              " ui-widget-header ui-corner-all";
++                      } else {
++                              this.range.removeClass( "ui-slider-range-min ui-slider-range-max" )
++                                      // Handle range switching from true to min/max
++                                      .css({
++                                              "left": "",
++                                              "bottom": ""
++                                      });
++                      }
++
++                      this.range.addClass( classes +
++                              ( ( options.range === "min" || options.range === "max" ) ? " ui-slider-range-" + options.range : "" ) );
++              } else {
++                      if ( this.range ) {
++                              this.range.remove();
++                      }
++                      this.range = null;
++              }
++      },
++
++      _setupEvents: function() {
++              this._off( this.handles );
++              this._on( this.handles, this._handleEvents );
++              this._hoverable( this.handles );
++              this._focusable( this.handles );
++      },
++
++      _destroy: function() {
++              this.handles.remove();
++              if ( this.range ) {
++                      this.range.remove();
++              }
++
++              this.element
++                      .removeClass( "ui-slider" +
++                              " ui-slider-horizontal" +
++                              " ui-slider-vertical" +
++                              " ui-widget" +
++                              " ui-widget-content" +
++                              " ui-corner-all" );
++
++              this._mouseDestroy();
++      },
++
++      _mouseCapture: function( event ) {
++              var position, normValue, distance, closestHandle, index, allowed, offset, mouseOverHandle,
++                      that = this,
++                      o = this.options;
++
++              if ( o.disabled ) {
++                      return false;
++              }
++
++              this.elementSize = {
++                      width: this.element.outerWidth(),
++                      height: this.element.outerHeight()
++              };
++              this.elementOffset = this.element.offset();
++
++              position = { x: event.pageX, y: event.pageY };
++              normValue = this._normValueFromMouse( position );
++              distance = this._valueMax() - this._valueMin() + 1;
++              this.handles.each(function( i ) {
++                      var thisDistance = Math.abs( normValue - that.values(i) );
++                      if (( distance > thisDistance ) ||
++                              ( distance === thisDistance &&
++                                      (i === that._lastChangedValue || that.values(i) === o.min ))) {
++                              distance = thisDistance;
++                              closestHandle = $( this );
++                              index = i;
++                      }
++              });
++
++              allowed = this._start( event, index );
++              if ( allowed === false ) {
++                      return false;
++              }
++              this._mouseSliding = true;
++
++              this._handleIndex = index;
++
++              closestHandle
++                      .addClass( "ui-state-active" )
++                      .focus();
++
++              offset = closestHandle.offset();
++              mouseOverHandle = !$( event.target ).parents().addBack().is( ".ui-slider-handle" );
++              this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : {
++                      left: event.pageX - offset.left - ( closestHandle.width() / 2 ),
++                      top: event.pageY - offset.top -
++                              ( closestHandle.height() / 2 ) -
++                              ( parseInt( closestHandle.css("borderTopWidth"), 10 ) || 0 ) -
++                              ( parseInt( closestHandle.css("borderBottomWidth"), 10 ) || 0) +
++                              ( parseInt( closestHandle.css("marginTop"), 10 ) || 0)
++              };
++
++              if ( !this.handles.hasClass( "ui-state-hover" ) ) {
++                      this._slide( event, index, normValue );
++              }
++              this._animateOff = true;
++              return true;
++      },
++
++      _mouseStart: function() {
++              return true;
++      },
++
++      _mouseDrag: function( event ) {
++              var position = { x: event.pageX, y: event.pageY },
++                      normValue = this._normValueFromMouse( position );
++
++              this._slide( event, this._handleIndex, normValue );
++
++              return false;
++      },
++
++      _mouseStop: function( event ) {
++              this.handles.removeClass( "ui-state-active" );
++              this._mouseSliding = false;
++
++              this._stop( event, this._handleIndex );
++              this._change( event, this._handleIndex );
++
++              this._handleIndex = null;
++              this._clickOffset = null;
++              this._animateOff = false;
++
++              return false;
++      },
++
++      _detectOrientation: function() {
++              this.orientation = ( this.options.orientation === "vertical" ) ? "vertical" : "horizontal";
++      },
++
++      _normValueFromMouse: function( position ) {
++              var pixelTotal,
++                      pixelMouse,
++                      percentMouse,
++                      valueTotal,
++                      valueMouse;
++
++              if ( this.orientation === "horizontal" ) {
++                      pixelTotal = this.elementSize.width;
++                      pixelMouse = position.x - this.elementOffset.left - ( this._clickOffset ? this._clickOffset.left : 0 );
++              } else {
++                      pixelTotal = this.elementSize.height;
++                      pixelMouse = position.y - this.elementOffset.top - ( this._clickOffset ? this._clickOffset.top : 0 );
++              }
++
++              percentMouse = ( pixelMouse / pixelTotal );
++              if ( percentMouse > 1 ) {
++                      percentMouse = 1;
++              }
++              if ( percentMouse < 0 ) {
++                      percentMouse = 0;
++              }
++              if ( this.orientation === "vertical" ) {
++                      percentMouse = 1 - percentMouse;
++              }
++
++              valueTotal = this._valueMax() - this._valueMin();
++              valueMouse = this._valueMin() + percentMouse * valueTotal;
++
++              return this._trimAlignValue( valueMouse );
++      },
++
++      _start: function( event, index ) {
++              var uiHash = {
++                      handle: this.handles[ index ],
++                      value: this.value()
++              };
++              if ( this.options.values && this.options.values.length ) {
++                      uiHash.value = this.values( index );
++                      uiHash.values = this.values();
++              }
++              return this._trigger( "start", event, uiHash );
++      },
++
++      _slide: function( event, index, newVal ) {
++              var otherVal,
++                      newValues,
++                      allowed;
++
++              if ( this.options.values && this.options.values.length ) {
++                      otherVal = this.values( index ? 0 : 1 );
++
++                      if ( ( this.options.values.length === 2 && this.options.range === true ) &&
++                                      ( ( index === 0 && newVal > otherVal) || ( index === 1 && newVal < otherVal ) )
++                              ) {
++                              newVal = otherVal;
++                      }
++
++                      if ( newVal !== this.values( index ) ) {
++                              newValues = this.values();
++                              newValues[ index ] = newVal;
++                              // A slide can be canceled by returning false from the slide callback
++                              allowed = this._trigger( "slide", event, {
++                                      handle: this.handles[ index ],
++                                      value: newVal,
++                                      values: newValues
++                              } );
++                              otherVal = this.values( index ? 0 : 1 );
++                              if ( allowed !== false ) {
++                                      this.values( index, newVal );
++                              }
++                      }
++              } else {
++                      if ( newVal !== this.value() ) {
++                              // A slide can be canceled by returning false from the slide callback
++                              allowed = this._trigger( "slide", event, {
++                                      handle: this.handles[ index ],
++                                      value: newVal
++                              } );
++                              if ( allowed !== false ) {
++                                      this.value( newVal );
++                              }
++                      }
++              }
++      },
++
++      _stop: function( event, index ) {
++              var uiHash = {
++                      handle: this.handles[ index ],
++                      value: this.value()
++              };
++              if ( this.options.values && this.options.values.length ) {
++                      uiHash.value = this.values( index );
++                      uiHash.values = this.values();
++              }
++
++              this._trigger( "stop", event, uiHash );
++      },
++
++      _change: function( event, index ) {
++              if ( !this._keySliding && !this._mouseSliding ) {
++                      var uiHash = {
++                              handle: this.handles[ index ],
++                              value: this.value()
++                      };
++                      if ( this.options.values && this.options.values.length ) {
++                              uiHash.value = this.values( index );
++                              uiHash.values = this.values();
++                      }
++
++                      //store the last changed value index for reference when handles overlap
++                      this._lastChangedValue = index;
++
++                      this._trigger( "change", event, uiHash );
++              }
++      },
++
++      value: function( newValue ) {
++              if ( arguments.length ) {
++                      this.options.value = this._trimAlignValue( newValue );
++                      this._refreshValue();
++                      this._change( null, 0 );
++                      return;
++              }
++
++              return this._value();
++      },
++
++      values: function( index, newValue ) {
++              var vals,
++                      newValues,
++                      i;
++
++              if ( arguments.length > 1 ) {
++                      this.options.values[ index ] = this._trimAlignValue( newValue );
++                      this._refreshValue();
++                      this._change( null, index );
++                      return;
++              }
++
++              if ( arguments.length ) {
++                      if ( $.isArray( arguments[ 0 ] ) ) {
++                              vals = this.options.values;
++                              newValues = arguments[ 0 ];
++                              for ( i = 0; i < vals.length; i += 1 ) {
++                                      vals[ i ] = this._trimAlignValue( newValues[ i ] );
++                                      this._change( null, i );
++                              }
++                              this._refreshValue();
++                      } else {
++                              if ( this.options.values && this.options.values.length ) {
++                                      return this._values( index );
++                              } else {
++                                      return this.value();
++                              }
++                      }
++              } else {
++                      return this._values();
++              }
++      },
++
++      _setOption: function( key, value ) {
++              var i,
++                      valsLength = 0;
++
++              if ( key === "range" && this.options.range === true ) {
++                      if ( value === "min" ) {
++                              this.options.value = this._values( 0 );
++                              this.options.values = null;
++                      } else if ( value === "max" ) {
++                              this.options.value = this._values( this.options.values.length - 1 );
++                              this.options.values = null;
++                      }
++              }
++
++              if ( $.isArray( this.options.values ) ) {
++                      valsLength = this.options.values.length;
++              }
++
++              if ( key === "disabled" ) {
++                      this.element.toggleClass( "ui-state-disabled", !!value );
++              }
++
++              this._super( key, value );
++
++              switch ( key ) {
++                      case "orientation":
++                              this._detectOrientation();
++                              this.element
++                                      .removeClass( "ui-slider-horizontal ui-slider-vertical" )
++                                      .addClass( "ui-slider-" + this.orientation );
++                              this._refreshValue();
++
++                              // Reset positioning from previous orientation
++                              this.handles.css( value === "horizontal" ? "bottom" : "left", "" );
++                              break;
++                      case "value":
++                              this._animateOff = true;
++                              this._refreshValue();
++                              this._change( null, 0 );
++                              this._animateOff = false;
++                              break;
++                      case "values":
++                              this._animateOff = true;
++                              this._refreshValue();
++                              for ( i = 0; i < valsLength; i += 1 ) {
++                                      this._change( null, i );
++                              }
++                              this._animateOff = false;
++                              break;
++                      case "step":
++                      case "min":
++                      case "max":
++                              this._animateOff = true;
++                              this._calculateNewMax();
++                              this._refreshValue();
++                              this._animateOff = false;
++                              break;
++                      case "range":
++                              this._animateOff = true;
++                              this._refresh();
++                              this._animateOff = false;
++                              break;
++              }
++      },
++
++      //internal value getter
++      // _value() returns value trimmed by min and max, aligned by step
++      _value: function() {
++              var val = this.options.value;
++              val = this._trimAlignValue( val );
++
++              return val;
++      },
++
++      //internal values getter
++      // _values() returns array of values trimmed by min and max, aligned by step
++      // _values( index ) returns single value trimmed by min and max, aligned by step
++      _values: function( index ) {
++              var val,
++                      vals,
++                      i;
++
++              if ( arguments.length ) {
++                      val = this.options.values[ index ];
++                      val = this._trimAlignValue( val );
++
++                      return val;
++              } else if ( this.options.values && this.options.values.length ) {
++                      // .slice() creates a copy of the array
++                      // this copy gets trimmed by min and max and then returned
++                      vals = this.options.values.slice();
++                      for ( i = 0; i < vals.length; i += 1) {
++                              vals[ i ] = this._trimAlignValue( vals[ i ] );
++                      }
++
++                      return vals;
++              } else {
++                      return [];
++              }
++      },
++
++      // returns the step-aligned value that val is closest to, between (inclusive) min and max
++      _trimAlignValue: function( val ) {
++              if ( val <= this._valueMin() ) {
++                      return this._valueMin();
++              }
++              if ( val >= this._valueMax() ) {
++                      return this._valueMax();
++              }
++              var step = ( this.options.step > 0 ) ? this.options.step : 1,
++                      valModStep = (val - this._valueMin()) % step,
++                      alignValue = val - valModStep;
++
++              if ( Math.abs(valModStep) * 2 >= step ) {
++                      alignValue += ( valModStep > 0 ) ? step : ( -step );
++              }
++
++              // Since JavaScript has problems with large floats, round
++              // the final value to 5 digits after the decimal point (see #4124)
++              return parseFloat( alignValue.toFixed(5) );
++      },
++
++      _calculateNewMax: function() {
++              var max = this.options.max,
++                      min = this._valueMin(),
++                      step = this.options.step,
++                      aboveMin = Math.floor( ( +( max - min ).toFixed( this._precision() ) ) / step ) * step;
++              max = aboveMin + min;
++              this.max = parseFloat( max.toFixed( this._precision() ) );
++      },
++
++      _precision: function() {
++              var precision = this._precisionOf( this.options.step );
++              if ( this.options.min !== null ) {
++                      precision = Math.max( precision, this._precisionOf( this.options.min ) );
++              }
++              return precision;
++      },
++
++      _precisionOf: function( num ) {
++              var str = num.toString(),
++                      decimal = str.indexOf( "." );
++              return decimal === -1 ? 0 : str.length - decimal - 1;
++      },
++
++      _valueMin: function() {
++              return this.options.min;
++      },
++
++      _valueMax: function() {
++              return this.max;
++      },
++
++      _refreshValue: function() {
++              var lastValPercent, valPercent, value, valueMin, valueMax,
++                      oRange = this.options.range,
++                      o = this.options,
++                      that = this,
++                      animate = ( !this._animateOff ) ? o.animate : false,
++                      _set = {};
++
++              if ( this.options.values && this.options.values.length ) {
++                      this.handles.each(function( i ) {
++                              valPercent = ( that.values(i) - that._valueMin() ) / ( that._valueMax() - that._valueMin() ) * 100;
++                              _set[ that.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
++                              $( this ).stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
++                              if ( that.options.range === true ) {
++                                      if ( that.orientation === "horizontal" ) {
++                                              if ( i === 0 ) {
++                                                      that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { left: valPercent + "%" }, o.animate );
++                                              }
++                                              if ( i === 1 ) {
++                                                      that.range[ animate ? "animate" : "css" ]( { width: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
++                                              }
++                                      } else {
++                                              if ( i === 0 ) {
++                                                      that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { bottom: ( valPercent ) + "%" }, o.animate );
++                                              }
++                                              if ( i === 1 ) {
++                                                      that.range[ animate ? "animate" : "css" ]( { height: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
++                                              }
++                                      }
++                              }
++                              lastValPercent = valPercent;
++                      });
++              } else {
++                      value = this.value();
++                      valueMin = this._valueMin();
++                      valueMax = this._valueMax();
++                      valPercent = ( valueMax !== valueMin ) ?
++                                      ( value - valueMin ) / ( valueMax - valueMin ) * 100 :
++                                      0;
++                      _set[ this.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
++                      this.handle.stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
++
++                      if ( oRange === "min" && this.orientation === "horizontal" ) {
++                              this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { width: valPercent + "%" }, o.animate );
++                      }
++                      if ( oRange === "max" && this.orientation === "horizontal" ) {
++                              this.range[ animate ? "animate" : "css" ]( { width: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
++                      }
++                      if ( oRange === "min" && this.orientation === "vertical" ) {
++                              this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { height: valPercent + "%" }, o.animate );
++                      }
++                      if ( oRange === "max" && this.orientation === "vertical" ) {
++                              this.range[ animate ? "animate" : "css" ]( { height: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
++                      }
++              }
++      },
++
++      _handleEvents: {
++              keydown: function( event ) {
++                      var allowed, curVal, newVal, step,
++                              index = $( event.target ).data( "ui-slider-handle-index" );
++
++                      switch ( event.keyCode ) {
++                              case $.ui.keyCode.HOME:
++                              case $.ui.keyCode.END:
++                              case $.ui.keyCode.PAGE_UP:
++                              case $.ui.keyCode.PAGE_DOWN:
++                              case $.ui.keyCode.UP:
++                              case $.ui.keyCode.RIGHT:
++                              case $.ui.keyCode.DOWN:
++                              case $.ui.keyCode.LEFT:
++                                      event.preventDefault();
++                                      if ( !this._keySliding ) {
++                                              this._keySliding = true;
++                                              $( event.target ).addClass( "ui-state-active" );
++                                              allowed = this._start( event, index );
++                                              if ( allowed === false ) {
++                                                      return;
++                                              }
++                                      }
++                                      break;
++                      }
++
++                      step = this.options.step;
++                      if ( this.options.values && this.options.values.length ) {
++                              curVal = newVal = this.values( index );
++                      } else {
++                              curVal = newVal = this.value();
++                      }
++
++                      switch ( event.keyCode ) {
++                              case $.ui.keyCode.HOME:
++                                      newVal = this._valueMin();
++                                      break;
++                              case $.ui.keyCode.END:
++                                      newVal = this._valueMax();
++                                      break;
++                              case $.ui.keyCode.PAGE_UP:
++                                      newVal = this._trimAlignValue(
++                                              curVal + ( ( this._valueMax() - this._valueMin() ) / this.numPages )
++                                      );
++                                      break;
++                              case $.ui.keyCode.PAGE_DOWN:
++                                      newVal = this._trimAlignValue(
++                                              curVal - ( (this._valueMax() - this._valueMin()) / this.numPages ) );
++                                      break;
++                              case $.ui.keyCode.UP:
++                              case $.ui.keyCode.RIGHT:
++                                      if ( curVal === this._valueMax() ) {
++                                              return;
++                                      }
++                                      newVal = this._trimAlignValue( curVal + step );
++                                      break;
++                              case $.ui.keyCode.DOWN:
++                              case $.ui.keyCode.LEFT:
++                                      if ( curVal === this._valueMin() ) {
++                                              return;
++                                      }
++                                      newVal = this._trimAlignValue( curVal - step );
++                                      break;
++                      }
++
++                      this._slide( event, index, newVal );
++              },
++              keyup: function( event ) {
++                      var index = $( event.target ).data( "ui-slider-handle-index" );
++
++                      if ( this._keySliding ) {
++                              this._keySliding = false;
++                              this._stop( event, index );
++                              this._change( event, index );
++                              $( event.target ).removeClass( "ui-state-active" );
++                      }
++              }
++      }
++});
++
++
++/*!
++ * jQuery UI Sortable 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/sortable/
++ */
++
++
++var sortable = $.widget("ui.sortable", $.ui.mouse, {
++      version: "1.11.4",
++      widgetEventPrefix: "sort",
++      ready: false,
++      options: {
++              appendTo: "parent",
++              axis: false,
++              connectWith: false,
++              containment: false,
++              cursor: "auto",
++              cursorAt: false,
++              dropOnEmpty: true,
++              forcePlaceholderSize: false,
++              forceHelperSize: false,
++              grid: false,
++              handle: false,
++              helper: "original",
++              items: "> *",
++              opacity: false,
++              placeholder: false,
++              revert: false,
++              scroll: true,
++              scrollSensitivity: 20,
++              scrollSpeed: 20,
++              scope: "default",
++              tolerance: "intersect",
++              zIndex: 1000,
++
++              // callbacks
++              activate: null,
++              beforeStop: null,
++              change: null,
++              deactivate: null,
++              out: null,
++              over: null,
++              receive: null,
++              remove: null,
++              sort: null,
++              start: null,
++              stop: null,
++              update: null
++      },
++
++      _isOverAxis: function( x, reference, size ) {
++              return ( x >= reference ) && ( x < ( reference + size ) );
++      },
++
++      _isFloating: function( item ) {
++              return (/left|right/).test(item.css("float")) || (/inline|table-cell/).test(item.css("display"));
++      },
++
++      _create: function() {
++              this.containerCache = {};
++              this.element.addClass("ui-sortable");
++
++              //Get the items
++              this.refresh();
++
++              //Let's determine the parent's offset
++              this.offset = this.element.offset();
++
++              //Initialize mouse events for interaction
++              this._mouseInit();
++
++              this._setHandleClassName();
++
++              //We're ready to go
++              this.ready = true;
++
++      },
++
++      _setOption: function( key, value ) {
++              this._super( key, value );
++
++              if ( key === "handle" ) {
++                      this._setHandleClassName();
++              }
++      },
++
++      _setHandleClassName: function() {
++              this.element.find( ".ui-sortable-handle" ).removeClass( "ui-sortable-handle" );
++              $.each( this.items, function() {
++                      ( this.instance.options.handle ?
++                              this.item.find( this.instance.options.handle ) : this.item )
++                              .addClass( "ui-sortable-handle" );
++              });
++      },
++
++      _destroy: function() {
++              this.element
++                      .removeClass( "ui-sortable ui-sortable-disabled" )
++                      .find( ".ui-sortable-handle" )
++                              .removeClass( "ui-sortable-handle" );
++              this._mouseDestroy();
++
++              for ( var i = this.items.length - 1; i >= 0; i-- ) {
++                      this.items[i].item.removeData(this.widgetName + "-item");
++              }
++
++              return this;
++      },
++
++      _mouseCapture: function(event, overrideHandle) {
++              var currentItem = null,
++                      validHandle = false,
++                      that = this;
++
++              if (this.reverting) {
++                      return false;
++              }
++
++              if(this.options.disabled || this.options.type === "static") {
++                      return false;
++              }
++
++              //We have to refresh the items data once first
++              this._refreshItems(event);
++
++              //Find out if the clicked node (or one of its parents) is a actual item in this.items
++              $(event.target).parents().each(function() {
++                      if($.data(this, that.widgetName + "-item") === that) {
++                              currentItem = $(this);
++                              return false;
++                      }
++              });
++              if($.data(event.target, that.widgetName + "-item") === that) {
++                      currentItem = $(event.target);
++              }
++
++              if(!currentItem) {
++                      return false;
++              }
++              if(this.options.handle && !overrideHandle) {
++                      $(this.options.handle, currentItem).find("*").addBack().each(function() {
++                              if(this === event.target) {
++                                      validHandle = true;
++                              }
++                      });
++                      if(!validHandle) {
++                              return false;
++                      }
++              }
++
++              this.currentItem = currentItem;
++              this._removeCurrentsFromItems();
++              return true;
++
++      },
++
++      _mouseStart: function(event, overrideHandle, noActivation) {
++
++              var i, body,
++                      o = this.options;
++
++              this.currentContainer = this;
++
++              //We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture
++              this.refreshPositions();
++
++              //Create and append the visible helper
++              this.helper = this._createHelper(event);
++
++              //Cache the helper size
++              this._cacheHelperProportions();
++
++              /*
++               * - Position generation -
++               * This block generates everything position related - it's the core of draggables.
++               */
++
++              //Cache the margins of the original element
++              this._cacheMargins();
++
++              //Get the next scrolling parent
++              this.scrollParent = this.helper.scrollParent();
++
++              //The element's absolute position on the page minus margins
++              this.offset = this.currentItem.offset();
++              this.offset = {
++                      top: this.offset.top - this.margins.top,
++                      left: this.offset.left - this.margins.left
++              };
++
++              $.extend(this.offset, {
++                      click: { //Where the click happened, relative to the element
++                              left: event.pageX - this.offset.left,
++                              top: event.pageY - this.offset.top
++                      },
++                      parent: this._getParentOffset(),
++                      relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
++              });
++
++              // Only after we got the offset, we can change the helper's position to absolute
++              // TODO: Still need to figure out a way to make relative sorting possible
++              this.helper.css("position", "absolute");
++              this.cssPosition = this.helper.css("position");
++
++              //Generate the original position
++              this.originalPosition = this._generatePosition(event);
++              this.originalPageX = event.pageX;
++              this.originalPageY = event.pageY;
++
++              //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
++              (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
++
++              //Cache the former DOM position
++              this.domPosition = { prev: this.currentItem.prev()[0], parent: this.currentItem.parent()[0] };
++
++              //If the helper is not the original, hide the original so it's not playing any role during the drag, won't cause anything bad this way
++              if(this.helper[0] !== this.currentItem[0]) {
++                      this.currentItem.hide();
++              }
++
++              //Create the placeholder
++              this._createPlaceholder();
++
++              //Set a containment if given in the options
++              if(o.containment) {
++                      this._setContainment();
++              }
++
++              if( o.cursor && o.cursor !== "auto" ) { // cursor option
++                      body = this.document.find( "body" );
++
++                      // support: IE
++                      this.storedCursor = body.css( "cursor" );
++                      body.css( "cursor", o.cursor );
++
++                      this.storedStylesheet = $( "<style>*{ cursor: "+o.cursor+" !important; }</style>" ).appendTo( body );
++              }
++
++              if(o.opacity) { // opacity option
++                      if (this.helper.css("opacity")) {
++                              this._storedOpacity = this.helper.css("opacity");
++                      }
++                      this.helper.css("opacity", o.opacity);
++              }
++
++              if(o.zIndex) { // zIndex option
++                      if (this.helper.css("zIndex")) {
++                              this._storedZIndex = this.helper.css("zIndex");
++                      }
++                      this.helper.css("zIndex", o.zIndex);
++              }
++
++              //Prepare scrolling
++              if(this.scrollParent[0] !== this.document[0] && this.scrollParent[0].tagName !== "HTML") {
++                      this.overflowOffset = this.scrollParent.offset();
++              }
++
++              //Call callbacks
++              this._trigger("start", event, this._uiHash());
++
++              //Recache the helper size
++              if(!this._preserveHelperProportions) {
++                      this._cacheHelperProportions();
++              }
++
++
++              //Post "activate" events to possible containers
++              if( !noActivation ) {
++                      for ( i = this.containers.length - 1; i >= 0; i-- ) {
++                              this.containers[ i ]._trigger( "activate", event, this._uiHash( this ) );
++                      }
++              }
++
++              //Prepare possible droppables
++              if($.ui.ddmanager) {
++                      $.ui.ddmanager.current = this;
++              }
++
++              if ($.ui.ddmanager && !o.dropBehaviour) {
++                      $.ui.ddmanager.prepareOffsets(this, event);
++              }
++
++              this.dragging = true;
++
++              this.helper.addClass("ui-sortable-helper");
++              this._mouseDrag(event); //Execute the drag once - this causes the helper not to be visible before getting its correct position
++              return true;
++
++      },
++
++      _mouseDrag: function(event) {
++              var i, item, itemElement, intersection,
++                      o = this.options,
++                      scrolled = false;
++
++              //Compute the helpers position
++              this.position = this._generatePosition(event);
++              this.positionAbs = this._convertPositionTo("absolute");
++
++              if (!this.lastPositionAbs) {
++                      this.lastPositionAbs = this.positionAbs;
++              }
++
++              //Do scrolling
++              if(this.options.scroll) {
++                      if(this.scrollParent[0] !== this.document[0] && this.scrollParent[0].tagName !== "HTML") {
++
++                              if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) {
++                                      this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
++                              } else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity) {
++                                      this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
++                              }
++
++                              if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) {
++                                      this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
++                              } else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity) {
++                                      this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
++                              }
++
++                      } else {
++
++                              if(event.pageY - this.document.scrollTop() < o.scrollSensitivity) {
++                                      scrolled = this.document.scrollTop(this.document.scrollTop() - o.scrollSpeed);
++                              } else if(this.window.height() - (event.pageY - this.document.scrollTop()) < o.scrollSensitivity) {
++                                      scrolled = this.document.scrollTop(this.document.scrollTop() + o.scrollSpeed);
++                              }
++
++                              if(event.pageX - this.document.scrollLeft() < o.scrollSensitivity) {
++                                      scrolled = this.document.scrollLeft(this.document.scrollLeft() - o.scrollSpeed);
++                              } else if(this.window.width() - (event.pageX - this.document.scrollLeft()) < o.scrollSensitivity) {
++                                      scrolled = this.document.scrollLeft(this.document.scrollLeft() + o.scrollSpeed);
++                              }
++
++                      }
++
++                      if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) {
++                              $.ui.ddmanager.prepareOffsets(this, event);
++                      }
++              }
++
++              //Regenerate the absolute position used for position checks
++              this.positionAbs = this._convertPositionTo("absolute");
++
++              //Set the helper position
++              if(!this.options.axis || this.options.axis !== "y") {
++                      this.helper[0].style.left = this.position.left+"px";
++              }
++              if(!this.options.axis || this.options.axis !== "x") {
++                      this.helper[0].style.top = this.position.top+"px";
++              }
++
++              //Rearrange
++              for (i = this.items.length - 1; i >= 0; i--) {
++
++                      //Cache variables and intersection, continue if no intersection
++                      item = this.items[i];
++                      itemElement = item.item[0];
++                      intersection = this._intersectsWithPointer(item);
++                      if (!intersection) {
++                              continue;
++                      }
++
++                      // Only put the placeholder inside the current Container, skip all
++                      // items from other containers. This works because when moving
++                      // an item from one container to another the
++                      // currentContainer is switched before the placeholder is moved.
++                      //
++                      // Without this, moving items in "sub-sortables" can cause
++                      // the placeholder to jitter between the outer and inner container.
++                      if (item.instance !== this.currentContainer) {
++                              continue;
++                      }
++
++                      // cannot intersect with itself
++                      // no useless actions that have been done before
++                      // no action if the item moved is the parent of the item checked
++                      if (itemElement !== this.currentItem[0] &&
++                              this.placeholder[intersection === 1 ? "next" : "prev"]()[0] !== itemElement &&
++                              !$.contains(this.placeholder[0], itemElement) &&
++                              (this.options.type === "semi-dynamic" ? !$.contains(this.element[0], itemElement) : true)
++                      ) {
++
++                              this.direction = intersection === 1 ? "down" : "up";
++
++                              if (this.options.tolerance === "pointer" || this._intersectsWithSides(item)) {
++                                      this._rearrange(event, item);
++                              } else {
++                                      break;
++                              }
++
++                              this._trigger("change", event, this._uiHash());
++                              break;
++                      }
++              }
++
++              //Post events to containers
++              this._contactContainers(event);
++
++              //Interconnect with droppables
++              if($.ui.ddmanager) {
++                      $.ui.ddmanager.drag(this, event);
++              }
++
++              //Call callbacks
++              this._trigger("sort", event, this._uiHash());
++
++              this.lastPositionAbs = this.positionAbs;
++              return false;
++
++      },
++
++      _mouseStop: function(event, noPropagation) {
++
++              if(!event) {
++                      return;
++              }
++
++              //If we are using droppables, inform the manager about the drop
++              if ($.ui.ddmanager && !this.options.dropBehaviour) {
++                      $.ui.ddmanager.drop(this, event);
++              }
++
++              if(this.options.revert) {
++                      var that = this,
++                              cur = this.placeholder.offset(),
++                              axis = this.options.axis,
++                              animation = {};
++
++                      if ( !axis || axis === "x" ) {
++                              animation.left = cur.left - this.offset.parent.left - this.margins.left + (this.offsetParent[0] === this.document[0].body ? 0 : this.offsetParent[0].scrollLeft);
++                      }
++                      if ( !axis || axis === "y" ) {
++                              animation.top = cur.top - this.offset.parent.top - this.margins.top + (this.offsetParent[0] === this.document[0].body ? 0 : this.offsetParent[0].scrollTop);
++                      }
++                      this.reverting = true;
++                      $(this.helper).animate( animation, parseInt(this.options.revert, 10) || 500, function() {
++                              that._clear(event);
++                      });
++              } else {
++                      this._clear(event, noPropagation);
++              }
++
++              return false;
++
++      },
++
++      cancel: function() {
++
++              if(this.dragging) {
++
++                      this._mouseUp({ target: null });
++
++                      if(this.options.helper === "original") {
++                              this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
++                      } else {
++                              this.currentItem.show();
++                      }
++
++                      //Post deactivating events to containers
++                      for (var i = this.containers.length - 1; i >= 0; i--){
++                              this.containers[i]._trigger("deactivate", null, this._uiHash(this));
++                              if(this.containers[i].containerCache.over) {
++                                      this.containers[i]._trigger("out", null, this._uiHash(this));
++                                      this.containers[i].containerCache.over = 0;
++                              }
++                      }
++
++              }
++
++              if (this.placeholder) {
++                      //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
++                      if(this.placeholder[0].parentNode) {
++                              this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
++                      }
++                      if(this.options.helper !== "original" && this.helper && this.helper[0].parentNode) {
++                              this.helper.remove();
++                      }
++
++                      $.extend(this, {
++                              helper: null,
++                              dragging: false,
++                              reverting: false,
++                              _noFinalSort: null
++                      });
++
++                      if(this.domPosition.prev) {
++                              $(this.domPosition.prev).after(this.currentItem);
++                      } else {
++                              $(this.domPosition.parent).prepend(this.currentItem);
++                      }
++              }
++
++              return this;
++
++      },
++
++      serialize: function(o) {
++
++              var items = this._getItemsAsjQuery(o && o.connected),
++                      str = [];
++              o = o || {};
++
++              $(items).each(function() {
++                      var res = ($(o.item || this).attr(o.attribute || "id") || "").match(o.expression || (/(.+)[\-=_](.+)/));
++                      if (res) {
++                              str.push((o.key || res[1]+"[]")+"="+(o.key && o.expression ? res[1] : res[2]));
++                      }
++              });
++
++              if(!str.length && o.key) {
++                      str.push(o.key + "=");
++              }
++
++              return str.join("&");
++
++      },
++
++      toArray: function(o) {
++
++              var items = this._getItemsAsjQuery(o && o.connected),
++                      ret = [];
++
++              o = o || {};
++
++              items.each(function() { ret.push($(o.item || this).attr(o.attribute || "id") || ""); });
++              return ret;
++
++      },
++
++      /* Be careful with the following core functions */
++      _intersectsWith: function(item) {
++
++              var x1 = this.positionAbs.left,
++                      x2 = x1 + this.helperProportions.width,
++                      y1 = this.positionAbs.top,
++                      y2 = y1 + this.helperProportions.height,
++                      l = item.left,
++                      r = l + item.width,
++                      t = item.top,
++                      b = t + item.height,
++                      dyClick = this.offset.click.top,
++                      dxClick = this.offset.click.left,
++                      isOverElementHeight = ( this.options.axis === "x" ) || ( ( y1 + dyClick ) > t && ( y1 + dyClick ) < b ),
++                      isOverElementWidth = ( this.options.axis === "y" ) || ( ( x1 + dxClick ) > l && ( x1 + dxClick ) < r ),
++                      isOverElement = isOverElementHeight && isOverElementWidth;
++
++              if ( this.options.tolerance === "pointer" ||
++                      this.options.forcePointerForContainers ||
++                      (this.options.tolerance !== "pointer" && this.helperProportions[this.floating ? "width" : "height"] > item[this.floating ? "width" : "height"])
++              ) {
++                      return isOverElement;
++              } else {
++
++                      return (l < x1 + (this.helperProportions.width / 2) && // Right Half
++                              x2 - (this.helperProportions.width / 2) < r && // Left Half
++                              t < y1 + (this.helperProportions.height / 2) && // Bottom Half
++                              y2 - (this.helperProportions.height / 2) < b ); // Top Half
++
++              }
++      },
++
++      _intersectsWithPointer: function(item) {
++
++              var isOverElementHeight = (this.options.axis === "x") || this._isOverAxis(this.positionAbs.top + this.offset.click.top, item.top, item.height),
++                      isOverElementWidth = (this.options.axis === "y") || this._isOverAxis(this.positionAbs.left + this.offset.click.left, item.left, item.width),
++                      isOverElement = isOverElementHeight && isOverElementWidth,
++                      verticalDirection = this._getDragVerticalDirection(),
++                      horizontalDirection = this._getDragHorizontalDirection();
++
++              if (!isOverElement) {
++                      return false;
++              }
++
++              return this.floating ?
++                      ( ((horizontalDirection && horizontalDirection === "right") || verticalDirection === "down") ? 2 : 1 )
++                      : ( verticalDirection && (verticalDirection === "down" ? 2 : 1) );
++
++      },
++
++      _intersectsWithSides: function(item) {
++
++              var isOverBottomHalf = this._isOverAxis(this.positionAbs.top + this.offset.click.top, item.top + (item.height/2), item.height),
++                      isOverRightHalf = this._isOverAxis(this.positionAbs.left + this.offset.click.left, item.left + (item.width/2), item.width),
++                      verticalDirection = this._getDragVerticalDirection(),
++                      horizontalDirection = this._getDragHorizontalDirection();
++
++              if (this.floating && horizontalDirection) {
++                      return ((horizontalDirection === "right" && isOverRightHalf) || (horizontalDirection === "left" && !isOverRightHalf));
++              } else {
++                      return verticalDirection && ((verticalDirection === "down" && isOverBottomHalf) || (verticalDirection === "up" && !isOverBottomHalf));
++              }
++
++      },
++
++      _getDragVerticalDirection: function() {
++              var delta = this.positionAbs.top - this.lastPositionAbs.top;
++              return delta !== 0 && (delta > 0 ? "down" : "up");
++      },
++
++      _getDragHorizontalDirection: function() {
++              var delta = this.positionAbs.left - this.lastPositionAbs.left;
++              return delta !== 0 && (delta > 0 ? "right" : "left");
++      },
++
++      refresh: function(event) {
++              this._refreshItems(event);
++              this._setHandleClassName();
++              this.refreshPositions();
++              return this;
++      },
++
++      _connectWith: function() {
++              var options = this.options;
++              return options.connectWith.constructor === String ? [options.connectWith] : options.connectWith;
++      },
++
++      _getItemsAsjQuery: function(connected) {
++
++              var i, j, cur, inst,
++                      items = [],
++                      queries = [],
++                      connectWith = this._connectWith();
++
++              if(connectWith && connected) {
++                      for (i = connectWith.length - 1; i >= 0; i--){
++                              cur = $(connectWith[i], this.document[0]);
++                              for ( j = cur.length - 1; j >= 0; j--){
++                                      inst = $.data(cur[j], this.widgetFullName);
++                                      if(inst && inst !== this && !inst.options.disabled) {
++                                              queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element) : $(inst.options.items, inst.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), inst]);
++                                      }
++                              }
++                      }
++              }
++
++              queries.push([$.isFunction(this.options.items) ? this.options.items.call(this.element, null, { options: this.options, item: this.currentItem }) : $(this.options.items, this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), this]);
++
++              function addItems() {
++                      items.push( this );
++              }
++              for (i = queries.length - 1; i >= 0; i--){
++                      queries[i][0].each( addItems );
++              }
++
++              return $(items);
++
++      },
++
++      _removeCurrentsFromItems: function() {
++
++              var list = this.currentItem.find(":data(" + this.widgetName + "-item)");
++
++              this.items = $.grep(this.items, function (item) {
++                      for (var j=0; j < list.length; j++) {
++                              if(list[j] === item.item[0]) {
++                                      return false;
++                              }
++                      }
++                      return true;
++              });
++
++      },
++
++      _refreshItems: function(event) {
++
++              this.items = [];
++              this.containers = [this];
++
++              var i, j, cur, inst, targetData, _queries, item, queriesLength,
++                      items = this.items,
++                      queries = [[$.isFunction(this.options.items) ? this.options.items.call(this.element[0], event, { item: this.currentItem }) : $(this.options.items, this.element), this]],
++                      connectWith = this._connectWith();
++
++              if(connectWith && this.ready) { //Shouldn't be run the first time through due to massive slow-down
++                      for (i = connectWith.length - 1; i >= 0; i--){
++                              cur = $(connectWith[i], this.document[0]);
++                              for (j = cur.length - 1; j >= 0; j--){
++                                      inst = $.data(cur[j], this.widgetFullName);
++                                      if(inst && inst !== this && !inst.options.disabled) {
++                                              queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element[0], event, { item: this.currentItem }) : $(inst.options.items, inst.element), inst]);
++                                              this.containers.push(inst);
++                                      }
++                              }
++                      }
++              }
++
++              for (i = queries.length - 1; i >= 0; i--) {
++                      targetData = queries[i][1];
++                      _queries = queries[i][0];
++
++                      for (j=0, queriesLength = _queries.length; j < queriesLength; j++) {
++                              item = $(_queries[j]);
++
++                              item.data(this.widgetName + "-item", targetData); // Data for target checking (mouse manager)
++
++                              items.push({
++                                      item: item,
++                                      instance: targetData,
++                                      width: 0, height: 0,
++                                      left: 0, top: 0
++                              });
++                      }
++              }
++
++      },
++
++      refreshPositions: function(fast) {
++
++              // Determine whether items are being displayed horizontally
++              this.floating = this.items.length ?
++                      this.options.axis === "x" || this._isFloating( this.items[ 0 ].item ) :
++                      false;
++
++              //This has to be redone because due to the item being moved out/into the offsetParent, the offsetParent's position will change
++              if(this.offsetParent && this.helper) {
++                      this.offset.parent = this._getParentOffset();
++              }
++
++              var i, item, t, p;
++
++              for (i = this.items.length - 1; i >= 0; i--){
++                      item = this.items[i];
++
++                      //We ignore calculating positions of all connected containers when we're not over them
++                      if(item.instance !== this.currentContainer && this.currentContainer && item.item[0] !== this.currentItem[0]) {
++                              continue;
++                      }
++
++                      t = this.options.toleranceElement ? $(this.options.toleranceElement, item.item) : item.item;
++
++                      if (!fast) {
++                              item.width = t.outerWidth();
++                              item.height = t.outerHeight();
++                      }
++
++                      p = t.offset();
++                      item.left = p.left;
++                      item.top = p.top;
++              }
++
++              if(this.options.custom && this.options.custom.refreshContainers) {
++                      this.options.custom.refreshContainers.call(this);
++              } else {
++                      for (i = this.containers.length - 1; i >= 0; i--){
++                              p = this.containers[i].element.offset();
++                              this.containers[i].containerCache.left = p.left;
++                              this.containers[i].containerCache.top = p.top;
++                              this.containers[i].containerCache.width = this.containers[i].element.outerWidth();
++                              this.containers[i].containerCache.height = this.containers[i].element.outerHeight();
++                      }
++              }
++
++              return this;
++      },
++
++      _createPlaceholder: function(that) {
++              that = that || this;
++              var className,
++                      o = that.options;
++
++              if(!o.placeholder || o.placeholder.constructor === String) {
++                      className = o.placeholder;
++                      o.placeholder = {
++                              element: function() {
++
++                                      var nodeName = that.currentItem[0].nodeName.toLowerCase(),
++                                              element = $( "<" + nodeName + ">", that.document[0] )
++                                                      .addClass(className || that.currentItem[0].className+" ui-sortable-placeholder")
++                                                      .removeClass("ui-sortable-helper");
++
++                                      if ( nodeName === "tbody" ) {
++                                              that._createTrPlaceholder(
++                                                      that.currentItem.find( "tr" ).eq( 0 ),
++                                                      $( "<tr>", that.document[ 0 ] ).appendTo( element )
++                                              );
++                                      } else if ( nodeName === "tr" ) {
++                                              that._createTrPlaceholder( that.currentItem, element );
++                                      } else if ( nodeName === "img" ) {
++                                              element.attr( "src", that.currentItem.attr( "src" ) );
++                                      }
++
++                                      if ( !className ) {
++                                              element.css( "visibility", "hidden" );
++                                      }
++
++                                      return element;
++                              },
++                              update: function(container, p) {
++
++                                      // 1. If a className is set as 'placeholder option, we don't force sizes - the class is responsible for that
++                                      // 2. The option 'forcePlaceholderSize can be enabled to force it even if a class name is specified
++                                      if(className && !o.forcePlaceholderSize) {
++                                              return;
++                                      }
++
++                                      //If the element doesn't have a actual height by itself (without styles coming from a stylesheet), it receives the inline height from the dragged item
++                                      if(!p.height()) { p.height(that.currentItem.innerHeight() - parseInt(that.currentItem.css("paddingTop")||0, 10) - parseInt(that.currentItem.css("paddingBottom")||0, 10)); }
++                                      if(!p.width()) { p.width(that.currentItem.innerWidth() - parseInt(that.currentItem.css("paddingLeft")||0, 10) - parseInt(that.currentItem.css("paddingRight")||0, 10)); }
++                              }
++                      };
++              }
++
++              //Create the placeholder
++              that.placeholder = $(o.placeholder.element.call(that.element, that.currentItem));
++
++              //Append it after the actual current item
++              that.currentItem.after(that.placeholder);
++
++              //Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317)
++              o.placeholder.update(that, that.placeholder);
++
++      },
++
++      _createTrPlaceholder: function( sourceTr, targetTr ) {
++              var that = this;
++
++              sourceTr.children().each(function() {
++                      $( "<td>&#160;</td>", that.document[ 0 ] )
++                              .attr( "colspan", $( this ).attr( "colspan" ) || 1 )
++                              .appendTo( targetTr );
++              });
++      },
++
++      _contactContainers: function(event) {
++              var i, j, dist, itemWithLeastDistance, posProperty, sizeProperty, cur, nearBottom, floating, axis,
++                      innermostContainer = null,
++                      innermostIndex = null;
++
++              // get innermost container that intersects with item
++              for (i = this.containers.length - 1; i >= 0; i--) {
++
++                      // never consider a container that's located within the item itself
++                      if($.contains(this.currentItem[0], this.containers[i].element[0])) {
++                              continue;
++                      }
++
++                      if(this._intersectsWith(this.containers[i].containerCache)) {
++
++                              // if we've already found a container and it's more "inner" than this, then continue
++                              if(innermostContainer && $.contains(this.containers[i].element[0], innermostContainer.element[0])) {
++                                      continue;
++                              }
++
++                              innermostContainer = this.containers[i];
++                              innermostIndex = i;
++
++                      } else {
++                              // container doesn't intersect. trigger "out" event if necessary
++                              if(this.containers[i].containerCache.over) {
++                                      this.containers[i]._trigger("out", event, this._uiHash(this));
++                                      this.containers[i].containerCache.over = 0;
++                              }
++                      }
++
++              }
++
++              // if no intersecting containers found, return
++              if(!innermostContainer) {
++                      return;
++              }
++
++              // move the item into the container if it's not there already
++              if(this.containers.length === 1) {
++                      if (!this.containers[innermostIndex].containerCache.over) {
++                              this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
++                              this.containers[innermostIndex].containerCache.over = 1;
++                      }
++              } else {
++
++                      //When entering a new container, we will find the item with the least distance and append our item near it
++                      dist = 10000;
++                      itemWithLeastDistance = null;
++                      floating = innermostContainer.floating || this._isFloating(this.currentItem);
++                      posProperty = floating ? "left" : "top";
++                      sizeProperty = floating ? "width" : "height";
++                      axis = floating ? "clientX" : "clientY";
++
++                      for (j = this.items.length - 1; j >= 0; j--) {
++                              if(!$.contains(this.containers[innermostIndex].element[0], this.items[j].item[0])) {
++                                      continue;
++                              }
++                              if(this.items[j].item[0] === this.currentItem[0]) {
++                                      continue;
++                              }
++
++                              cur = this.items[j].item.offset()[posProperty];
++                              nearBottom = false;
++                              if ( event[ axis ] - cur > this.items[ j ][ sizeProperty ] / 2 ) {
++                                      nearBottom = true;
++                              }
++
++                              if ( Math.abs( event[ axis ] - cur ) < dist ) {
++                                      dist = Math.abs( event[ axis ] - cur );
++                                      itemWithLeastDistance = this.items[ j ];
++                                      this.direction = nearBottom ? "up": "down";
++                              }
++                      }
++
++                      //Check if dropOnEmpty is enabled
++                      if(!itemWithLeastDistance && !this.options.dropOnEmpty) {
++                              return;
++                      }
++
++                      if(this.currentContainer === this.containers[innermostIndex]) {
++                              if ( !this.currentContainer.containerCache.over ) {
++                                      this.containers[ innermostIndex ]._trigger( "over", event, this._uiHash() );
++                                      this.currentContainer.containerCache.over = 1;
++                              }
++                              return;
++                      }
++
++                      itemWithLeastDistance ? this._rearrange(event, itemWithLeastDistance, null, true) : this._rearrange(event, null, this.containers[innermostIndex].element, true);
++                      this._trigger("change", event, this._uiHash());
++                      this.containers[innermostIndex]._trigger("change", event, this._uiHash(this));
++                      this.currentContainer = this.containers[innermostIndex];
++
++                      //Update the placeholder
++                      this.options.placeholder.update(this.currentContainer, this.placeholder);
++
++                      this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
++                      this.containers[innermostIndex].containerCache.over = 1;
++              }
++
++
++      },
++
++      _createHelper: function(event) {
++
++              var o = this.options,
++                      helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event, this.currentItem])) : (o.helper === "clone" ? this.currentItem.clone() : this.currentItem);
++
++              //Add the helper to the DOM if that didn't happen already
++              if(!helper.parents("body").length) {
++                      $(o.appendTo !== "parent" ? o.appendTo : this.currentItem[0].parentNode)[0].appendChild(helper[0]);
++              }
++
++              if(helper[0] === this.currentItem[0]) {
++                      this._storedCSS = { width: this.currentItem[0].style.width, height: this.currentItem[0].style.height, position: this.currentItem.css("position"), top: this.currentItem.css("top"), left: this.currentItem.css("left") };
++              }
++
++              if(!helper[0].style.width || o.forceHelperSize) {
++                      helper.width(this.currentItem.width());
++              }
++              if(!helper[0].style.height || o.forceHelperSize) {
++                      helper.height(this.currentItem.height());
++              }
++
++              return helper;
++
++      },
++
++      _adjustOffsetFromHelper: function(obj) {
++              if (typeof obj === "string") {
++                      obj = obj.split(" ");
++              }
++              if ($.isArray(obj)) {
++                      obj = {left: +obj[0], top: +obj[1] || 0};
++              }
++              if ("left" in obj) {
++                      this.offset.click.left = obj.left + this.margins.left;
++              }
++              if ("right" in obj) {
++                      this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
++              }
++              if ("top" in obj) {
++                      this.offset.click.top = obj.top + this.margins.top;
++              }
++              if ("bottom" in obj) {
++                      this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
++              }
++      },
++
++      _getParentOffset: function() {
++
++
++              //Get the offsetParent and cache its position
++              this.offsetParent = this.helper.offsetParent();
++              var po = this.offsetParent.offset();
++
++              // This is a special case where we need to modify a offset calculated on start, since the following happened:
++              // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
++              // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
++              //    the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
++              if(this.cssPosition === "absolute" && this.scrollParent[0] !== this.document[0] && $.contains(this.scrollParent[0], this.offsetParent[0])) {
++                      po.left += this.scrollParent.scrollLeft();
++                      po.top += this.scrollParent.scrollTop();
++              }
++
++              // This needs to be actually done for all browsers, since pageX/pageY includes this information
++              // with an ugly IE fix
++              if( this.offsetParent[0] === this.document[0].body || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) {
++                      po = { top: 0, left: 0 };
++              }
++
++              return {
++                      top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
++                      left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
++              };
++
++      },
++
++      _getRelativeOffset: function() {
++
++              if(this.cssPosition === "relative") {
++                      var p = this.currentItem.position();
++                      return {
++                              top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
++                              left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
++                      };
++              } else {
++                      return { top: 0, left: 0 };
++              }
++
++      },
++
++      _cacheMargins: function() {
++              this.margins = {
++                      left: (parseInt(this.currentItem.css("marginLeft"),10) || 0),
++                      top: (parseInt(this.currentItem.css("marginTop"),10) || 0)
++              };
++      },
++
++      _cacheHelperProportions: function() {
++              this.helperProportions = {
++                      width: this.helper.outerWidth(),
++                      height: this.helper.outerHeight()
++              };
++      },
++
++      _setContainment: function() {
++
++              var ce, co, over,
++                      o = this.options;
++              if(o.containment === "parent") {
++                      o.containment = this.helper[0].parentNode;
++              }
++              if(o.containment === "document" || o.containment === "window") {
++                      this.containment = [
++                              0 - this.offset.relative.left - this.offset.parent.left,
++                              0 - this.offset.relative.top - this.offset.parent.top,
++                              o.containment === "document" ? this.document.width() : this.window.width() - this.helperProportions.width - this.margins.left,
++                              (o.containment === "document" ? this.document.width() : this.window.height() || this.document[0].body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
++                      ];
++              }
++
++              if(!(/^(document|window|parent)$/).test(o.containment)) {
++                      ce = $(o.containment)[0];
++                      co = $(o.containment).offset();
++                      over = ($(ce).css("overflow") !== "hidden");
++
++                      this.containment = [
++                              co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left,
++                              co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top,
++                              co.left+(over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left,
++                              co.top+(over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top
++                      ];
++              }
++
++      },
++
++      _convertPositionTo: function(d, pos) {
++
++              if(!pos) {
++                      pos = this.position;
++              }
++              var mod = d === "absolute" ? 1 : -1,
++                      scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== this.document[0] && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent,
++                      scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
++
++              return {
++                      top: (
++                              pos.top +                                                                                                                               // The absolute mouse position
++                              this.offset.relative.top * mod +                                                                                // Only for relative positioned nodes: Relative offset from element to offset parent
++                              this.offset.parent.top * mod -                                                                                  // The offsetParent's offset without borders (offset + border)
++                              ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
++                      ),
++                      left: (
++                              pos.left +                                                                                                                              // The absolute mouse position
++                              this.offset.relative.left * mod +                                                                               // Only for relative positioned nodes: Relative offset from element to offset parent
++                              this.offset.parent.left * mod   -                                                                               // The offsetParent's offset without borders (offset + border)
++                              ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
++                      )
++              };
++
++      },
++
++      _generatePosition: function(event) {
++
++              var top, left,
++                      o = this.options,
++                      pageX = event.pageX,
++                      pageY = event.pageY,
++                      scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== this.document[0] && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
++
++              // This is another very weird special case that only happens for relative elements:
++              // 1. If the css position is relative
++              // 2. and the scroll parent is the document or similar to the offset parent
++              // we have to refresh the relative offset during the scroll so there are no jumps
++              if(this.cssPosition === "relative" && !(this.scrollParent[0] !== this.document[0] && this.scrollParent[0] !== this.offsetParent[0])) {
++                      this.offset.relative = this._getRelativeOffset();
++              }
++
++              /*
++               * - Position constraining -
++               * Constrain the position to a mix of grid, containment.
++               */
++
++              if(this.originalPosition) { //If we are not dragging yet, we won't check for options
++
++                      if(this.containment) {
++                              if(event.pageX - this.offset.click.left < this.containment[0]) {
++                                      pageX = this.containment[0] + this.offset.click.left;
++                              }
++                              if(event.pageY - this.offset.click.top < this.containment[1]) {
++                                      pageY = this.containment[1] + this.offset.click.top;
++                              }
++                              if(event.pageX - this.offset.click.left > this.containment[2]) {
++                                      pageX = this.containment[2] + this.offset.click.left;
++                              }
++                              if(event.pageY - this.offset.click.top > this.containment[3]) {
++                                      pageY = this.containment[3] + this.offset.click.top;
++                              }
++                      }
++
++                      if(o.grid) {
++                              top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1];
++                              pageY = this.containment ? ( (top - this.offset.click.top >= this.containment[1] && top - this.offset.click.top <= this.containment[3]) ? top : ((top - this.offset.click.top >= this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
++
++                              left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0];
++                              pageX = this.containment ? ( (left - this.offset.click.left >= this.containment[0] && left - this.offset.click.left <= this.containment[2]) ? left : ((left - this.offset.click.left >= this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
++                      }
++
++              }
++
++              return {
++                      top: (
++                              pageY -                                                                                                                         // The absolute mouse position
++                              this.offset.click.top -                                                                                                 // Click offset (relative to the element)
++                              this.offset.relative.top        -                                                                                       // Only for relative positioned nodes: Relative offset from element to offset parent
++                              this.offset.parent.top +                                                                                                // The offsetParent's offset without borders (offset + border)
++                              ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
++                      ),
++                      left: (
++                              pageX -                                                                                                                         // The absolute mouse position
++                              this.offset.click.left -                                                                                                // Click offset (relative to the element)
++                              this.offset.relative.left       -                                                                                       // Only for relative positioned nodes: Relative offset from element to offset parent
++                              this.offset.parent.left +                                                                                               // The offsetParent's offset without borders (offset + border)
++                              ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
++                      )
++              };
++
++      },
++
++      _rearrange: function(event, i, a, hardRefresh) {
++
++              a ? a[0].appendChild(this.placeholder[0]) : i.item[0].parentNode.insertBefore(this.placeholder[0], (this.direction === "down" ? i.item[0] : i.item[0].nextSibling));
++
++              //Various things done here to improve the performance:
++              // 1. we create a setTimeout, that calls refreshPositions
++              // 2. on the instance, we have a counter variable, that get's higher after every append
++              // 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same
++              // 4. this lets only the last addition to the timeout stack through
++              this.counter = this.counter ? ++this.counter : 1;
++              var counter = this.counter;
++
++              this._delay(function() {
++                      if(counter === this.counter) {
++                              this.refreshPositions(!hardRefresh); //Precompute after each DOM insertion, NOT on mousemove
++                      }
++              });
++
++      },
++
++      _clear: function(event, noPropagation) {
++
++              this.reverting = false;
++              // We delay all events that have to be triggered to after the point where the placeholder has been removed and
++              // everything else normalized again
++              var i,
++                      delayedTriggers = [];
++
++              // We first have to update the dom position of the actual currentItem
++              // Note: don't do it if the current item is already removed (by a user), or it gets reappended (see #4088)
++              if(!this._noFinalSort && this.currentItem.parent().length) {
++                      this.placeholder.before(this.currentItem);
++              }
++              this._noFinalSort = null;
++
++              if(this.helper[0] === this.currentItem[0]) {
++                      for(i in this._storedCSS) {
++                              if(this._storedCSS[i] === "auto" || this._storedCSS[i] === "static") {
++                                      this._storedCSS[i] = "";
++                              }
++                      }
++                      this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
++              } else {
++                      this.currentItem.show();
++              }
++
++              if(this.fromOutside && !noPropagation) {
++                      delayedTriggers.push(function(event) { this._trigger("receive", event, this._uiHash(this.fromOutside)); });
++              }
++              if((this.fromOutside || this.domPosition.prev !== this.currentItem.prev().not(".ui-sortable-helper")[0] || this.domPosition.parent !== this.currentItem.parent()[0]) && !noPropagation) {
++                      delayedTriggers.push(function(event) { this._trigger("update", event, this._uiHash()); }); //Trigger update callback if the DOM position has changed
++              }
++
++              // Check if the items Container has Changed and trigger appropriate
++              // events.
++              if (this !== this.currentContainer) {
++                      if(!noPropagation) {
++                              delayedTriggers.push(function(event) { this._trigger("remove", event, this._uiHash()); });
++                              delayedTriggers.push((function(c) { return function(event) { c._trigger("receive", event, this._uiHash(this)); };  }).call(this, this.currentContainer));
++                              delayedTriggers.push((function(c) { return function(event) { c._trigger("update", event, this._uiHash(this));  }; }).call(this, this.currentContainer));
++                      }
++              }
++
++
++              //Post events to containers
++              function delayEvent( type, instance, container ) {
++                      return function( event ) {
++                              container._trigger( type, event, instance._uiHash( instance ) );
++                      };
++              }
++              for (i = this.containers.length - 1; i >= 0; i--){
++                      if (!noPropagation) {
++                              delayedTriggers.push( delayEvent( "deactivate", this, this.containers[ i ] ) );
++                      }
++                      if(this.containers[i].containerCache.over) {
++                              delayedTriggers.push( delayEvent( "out", this, this.containers[ i ] ) );
++                              this.containers[i].containerCache.over = 0;
++                      }
++              }
++
++              //Do what was originally in plugins
++              if ( this.storedCursor ) {
++                      this.document.find( "body" ).css( "cursor", this.storedCursor );
++                      this.storedStylesheet.remove();
++              }
++              if(this._storedOpacity) {
++                      this.helper.css("opacity", this._storedOpacity);
++              }
++              if(this._storedZIndex) {
++                      this.helper.css("zIndex", this._storedZIndex === "auto" ? "" : this._storedZIndex);
++              }
++
++              this.dragging = false;
++
++              if(!noPropagation) {
++                      this._trigger("beforeStop", event, this._uiHash());
++              }
++
++              //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
++              this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
++
++              if ( !this.cancelHelperRemoval ) {
++                      if ( this.helper[ 0 ] !== this.currentItem[ 0 ] ) {
++                              this.helper.remove();
++                      }
++                      this.helper = null;
++              }
++
++              if(!noPropagation) {
++                      for (i=0; i < delayedTriggers.length; i++) {
++                              delayedTriggers[i].call(this, event);
++                      } //Trigger all delayed events
++                      this._trigger("stop", event, this._uiHash());
++              }
++
++              this.fromOutside = false;
++              return !this.cancelHelperRemoval;
++
++      },
++
++      _trigger: function() {
++              if ($.Widget.prototype._trigger.apply(this, arguments) === false) {
++                      this.cancel();
++              }
++      },
++
++      _uiHash: function(_inst) {
++              var inst = _inst || this;
++              return {
++                      helper: inst.helper,
++                      placeholder: inst.placeholder || $([]),
++                      position: inst.position,
++                      originalPosition: inst.originalPosition,
++                      offset: inst.positionAbs,
++                      item: inst.currentItem,
++                      sender: _inst ? _inst.element : null
++              };
++      }
++
++});
++
++
++/*!
++ * jQuery UI Spinner 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/spinner/
++ */
++
++
++function spinner_modifier( fn ) {
++      return function() {
++              var previous = this.element.val();
++              fn.apply( this, arguments );
++              this._refresh();
++              if ( previous !== this.element.val() ) {
++                      this._trigger( "change" );
++              }
++      };
++}
++
++var spinner = $.widget( "ui.spinner", {
++      version: "1.11.4",
++      defaultElement: "<input>",
++      widgetEventPrefix: "spin",
++      options: {
++              culture: null,
++              icons: {
++                      down: "ui-icon-triangle-1-s",
++                      up: "ui-icon-triangle-1-n"
++              },
++              incremental: true,
++              max: null,
++              min: null,
++              numberFormat: null,
++              page: 10,
++              step: 1,
++
++              change: null,
++              spin: null,
++              start: null,
++              stop: null
++      },
++
++      _create: function() {
++              // handle string values that need to be parsed
++              this._setOption( "max", this.options.max );
++              this._setOption( "min", this.options.min );
++              this._setOption( "step", this.options.step );
++
++              // Only format if there is a value, prevents the field from being marked
++              // as invalid in Firefox, see #9573.
++              if ( this.value() !== "" ) {
++                      // Format the value, but don't constrain.
++                      this._value( this.element.val(), true );
++              }
++
++              this._draw();
++              this._on( this._events );
++              this._refresh();
++
++              // turning off autocomplete prevents the browser from remembering the
++              // value when navigating through history, so we re-enable autocomplete
++              // if the page is unloaded before the widget is destroyed. #7790
++              this._on( this.window, {
++                      beforeunload: function() {
++                              this.element.removeAttr( "autocomplete" );
++                      }
++              });
++      },
++
++      _getCreateOptions: function() {
++              var options = {},
++                      element = this.element;
++
++              $.each( [ "min", "max", "step" ], function( i, option ) {
++                      var value = element.attr( option );
++                      if ( value !== undefined && value.length ) {
++                              options[ option ] = value;
++                      }
++              });
++
++              return options;
++      },
++
++      _events: {
++              keydown: function( event ) {
++                      if ( this._start( event ) && this._keydown( event ) ) {
++                              event.preventDefault();
++                      }
++              },
++              keyup: "_stop",
++              focus: function() {
++                      this.previous = this.element.val();
++              },
++              blur: function( event ) {
++                      if ( this.cancelBlur ) {
++                              delete this.cancelBlur;
++                              return;
++                      }
++
++                      this._stop();
++                      this._refresh();
++                      if ( this.previous !== this.element.val() ) {
++                              this._trigger( "change", event );
++                      }
++              },
++              mousewheel: function( event, delta ) {
++                      if ( !delta ) {
++                              return;
++                      }
++                      if ( !this.spinning && !this._start( event ) ) {
++                              return false;
++                      }
++
++                      this._spin( (delta > 0 ? 1 : -1) * this.options.step, event );
++                      clearTimeout( this.mousewheelTimer );
++                      this.mousewheelTimer = this._delay(function() {
++                              if ( this.spinning ) {
++                                      this._stop( event );
++                              }
++                      }, 100 );
++                      event.preventDefault();
++              },
++              "mousedown .ui-spinner-button": function( event ) {
++                      var previous;
++
++                      // We never want the buttons to have focus; whenever the user is
++                      // interacting with the spinner, the focus should be on the input.
++                      // If the input is focused then this.previous is properly set from
++                      // when the input first received focus. If the input is not focused
++                      // then we need to set this.previous based on the value before spinning.
++                      previous = this.element[0] === this.document[0].activeElement ?
++                              this.previous : this.element.val();
++                      function checkFocus() {
++                              var isActive = this.element[0] === this.document[0].activeElement;
++                              if ( !isActive ) {
++                                      this.element.focus();
++                                      this.previous = previous;
++                                      // support: IE
++                                      // IE sets focus asynchronously, so we need to check if focus
++                                      // moved off of the input because the user clicked on the button.
++                                      this._delay(function() {
++                                              this.previous = previous;
++                                      });
++                              }
++                      }
++
++                      // ensure focus is on (or stays on) the text field
++                      event.preventDefault();
++                      checkFocus.call( this );
++
++                      // support: IE
++                      // IE doesn't prevent moving focus even with event.preventDefault()
++                      // so we set a flag to know when we should ignore the blur event
++                      // and check (again) if focus moved off of the input.
++                      this.cancelBlur = true;
++                      this._delay(function() {
++                              delete this.cancelBlur;
++                              checkFocus.call( this );
++                      });
++
++                      if ( this._start( event ) === false ) {
++                              return;
++                      }
++
++                      this._repeat( null, $( event.currentTarget ).hasClass( "ui-spinner-up" ) ? 1 : -1, event );
++              },
++              "mouseup .ui-spinner-button": "_stop",
++              "mouseenter .ui-spinner-button": function( event ) {
++                      // button will add ui-state-active if mouse was down while mouseleave and kept down
++                      if ( !$( event.currentTarget ).hasClass( "ui-state-active" ) ) {
++                              return;
++                      }
++
++                      if ( this._start( event ) === false ) {
++                              return false;
++                      }
++                      this._repeat( null, $( event.currentTarget ).hasClass( "ui-spinner-up" ) ? 1 : -1, event );
++              },
++              // TODO: do we really want to consider this a stop?
++              // shouldn't we just stop the repeater and wait until mouseup before
++              // we trigger the stop event?
++              "mouseleave .ui-spinner-button": "_stop"
++      },
++
++      _draw: function() {
++              var uiSpinner = this.uiSpinner = this.element
++                      .addClass( "ui-spinner-input" )
++                      .attr( "autocomplete", "off" )
++                      .wrap( this._uiSpinnerHtml() )
++                      .parent()
++                              // add buttons
++                              .append( this._buttonHtml() );
++
++              this.element.attr( "role", "spinbutton" );
++
++              // button bindings
++              this.buttons = uiSpinner.find( ".ui-spinner-button" )
++                      .attr( "tabIndex", -1 )
++                      .button()
++                      .removeClass( "ui-corner-all" );
++
++              // IE 6 doesn't understand height: 50% for the buttons
++              // unless the wrapper has an explicit height
++              if ( this.buttons.height() > Math.ceil( uiSpinner.height() * 0.5 ) &&
++                              uiSpinner.height() > 0 ) {
++                      uiSpinner.height( uiSpinner.height() );
++              }
++
++              // disable spinner if element was already disabled
++              if ( this.options.disabled ) {
++                      this.disable();
++              }
++      },
++
++      _keydown: function( event ) {
++              var options = this.options,
++                      keyCode = $.ui.keyCode;
++
++              switch ( event.keyCode ) {
++              case keyCode.UP:
++                      this._repeat( null, 1, event );
++                      return true;
++              case keyCode.DOWN:
++                      this._repeat( null, -1, event );
++                      return true;
++              case keyCode.PAGE_UP:
++                      this._repeat( null, options.page, event );
++                      return true;
++              case keyCode.PAGE_DOWN:
++                      this._repeat( null, -options.page, event );
++                      return true;
++              }
++
++              return false;
++      },
++
++      _uiSpinnerHtml: function() {
++              return "<span class='ui-spinner ui-widget ui-widget-content ui-corner-all'></span>";
++      },
++
++      _buttonHtml: function() {
++              return "" +
++                      "<a class='ui-spinner-button ui-spinner-up ui-corner-tr'>" +
++                              "<span class='ui-icon " + this.options.icons.up + "'>&#9650;</span>" +
++                      "</a>" +
++                      "<a class='ui-spinner-button ui-spinner-down ui-corner-br'>" +
++                              "<span class='ui-icon " + this.options.icons.down + "'>&#9660;</span>" +
++                      "</a>";
++      },
++
++      _start: function( event ) {
++              if ( !this.spinning && this._trigger( "start", event ) === false ) {
++                      return false;
++              }
++
++              if ( !this.counter ) {
++                      this.counter = 1;
++              }
++              this.spinning = true;
++              return true;
++      },
++
++      _repeat: function( i, steps, event ) {
++              i = i || 500;
++
++              clearTimeout( this.timer );
++              this.timer = this._delay(function() {
++                      this._repeat( 40, steps, event );
++              }, i );
++
++              this._spin( steps * this.options.step, event );
++      },
++
++      _spin: function( step, event ) {
++              var value = this.value() || 0;
++
++              if ( !this.counter ) {
++                      this.counter = 1;
++              }
++
++              value = this._adjustValue( value + step * this._increment( this.counter ) );
++
++              if ( !this.spinning || this._trigger( "spin", event, { value: value } ) !== false) {
++                      this._value( value );
++                      this.counter++;
++              }
++      },
++
++      _increment: function( i ) {
++              var incremental = this.options.incremental;
++
++              if ( incremental ) {
++                      return $.isFunction( incremental ) ?
++                              incremental( i ) :
++                              Math.floor( i * i * i / 50000 - i * i / 500 + 17 * i / 200 + 1 );
++              }
++
++              return 1;
++      },
++
++      _precision: function() {
++              var precision = this._precisionOf( this.options.step );
++              if ( this.options.min !== null ) {
++                      precision = Math.max( precision, this._precisionOf( this.options.min ) );
++              }
++              return precision;
++      },
++
++      _precisionOf: function( num ) {
++              var str = num.toString(),
++                      decimal = str.indexOf( "." );
++              return decimal === -1 ? 0 : str.length - decimal - 1;
++      },
++
++      _adjustValue: function( value ) {
++              var base, aboveMin,
++                      options = this.options;
++
++              // make sure we're at a valid step
++              // - find out where we are relative to the base (min or 0)
++              base = options.min !== null ? options.min : 0;
++              aboveMin = value - base;
++              // - round to the nearest step
++              aboveMin = Math.round(aboveMin / options.step) * options.step;
++              // - rounding is based on 0, so adjust back to our base
++              value = base + aboveMin;
++
++              // fix precision from bad JS floating point math
++              value = parseFloat( value.toFixed( this._precision() ) );
++
++              // clamp the value
++              if ( options.max !== null && value > options.max) {
++                      return options.max;
++              }
++              if ( options.min !== null && value < options.min ) {
++                      return options.min;
++              }
++
++              return value;
++      },
++
++      _stop: function( event ) {
++              if ( !this.spinning ) {
++                      return;
++              }
++
++              clearTimeout( this.timer );
++              clearTimeout( this.mousewheelTimer );
++              this.counter = 0;
++              this.spinning = false;
++              this._trigger( "stop", event );
++      },
++
++      _setOption: function( key, value ) {
++              if ( key === "culture" || key === "numberFormat" ) {
++                      var prevValue = this._parse( this.element.val() );
++                      this.options[ key ] = value;
++                      this.element.val( this._format( prevValue ) );
++                      return;
++              }
++
++              if ( key === "max" || key === "min" || key === "step" ) {
++                      if ( typeof value === "string" ) {
++                              value = this._parse( value );
++                      }
++              }
++              if ( key === "icons" ) {
++                      this.buttons.first().find( ".ui-icon" )
++                              .removeClass( this.options.icons.up )
++                              .addClass( value.up );
++                      this.buttons.last().find( ".ui-icon" )
++                              .removeClass( this.options.icons.down )
++                              .addClass( value.down );
++              }
++
++              this._super( key, value );
++
++              if ( key === "disabled" ) {
++                      this.widget().toggleClass( "ui-state-disabled", !!value );
++                      this.element.prop( "disabled", !!value );
++                      this.buttons.button( value ? "disable" : "enable" );
++              }
++      },
++
++      _setOptions: spinner_modifier(function( options ) {
++              this._super( options );
++      }),
++
++      _parse: function( val ) {
++              if ( typeof val === "string" && val !== "" ) {
++                      val = window.Globalize && this.options.numberFormat ?
++                              Globalize.parseFloat( val, 10, this.options.culture ) : +val;
++              }
++              return val === "" || isNaN( val ) ? null : val;
++      },
++
++      _format: function( value ) {
++              if ( value === "" ) {
++                      return "";
++              }
++              return window.Globalize && this.options.numberFormat ?
++                      Globalize.format( value, this.options.numberFormat, this.options.culture ) :
++                      value;
++      },
++
++      _refresh: function() {
++              this.element.attr({
++                      "aria-valuemin": this.options.min,
++                      "aria-valuemax": this.options.max,
++                      // TODO: what should we do with values that can't be parsed?
++                      "aria-valuenow": this._parse( this.element.val() )
++              });
++      },
++
++      isValid: function() {
++              var value = this.value();
++
++              // null is invalid
++              if ( value === null ) {
++                      return false;
++              }
++
++              // if value gets adjusted, it's invalid
++              return value === this._adjustValue( value );
++      },
++
++      // update the value without triggering change
++      _value: function( value, allowAny ) {
++              var parsed;
++              if ( value !== "" ) {
++                      parsed = this._parse( value );
++                      if ( parsed !== null ) {
++                              if ( !allowAny ) {
++                                      parsed = this._adjustValue( parsed );
++                              }
++                              value = this._format( parsed );
++                      }
++              }
++              this.element.val( value );
++              this._refresh();
++      },
++
++      _destroy: function() {
++              this.element
++                      .removeClass( "ui-spinner-input" )
++                      .prop( "disabled", false )
++                      .removeAttr( "autocomplete" )
++                      .removeAttr( "role" )
++                      .removeAttr( "aria-valuemin" )
++                      .removeAttr( "aria-valuemax" )
++                      .removeAttr( "aria-valuenow" );
++              this.uiSpinner.replaceWith( this.element );
++      },
++
++      stepUp: spinner_modifier(function( steps ) {
++              this._stepUp( steps );
++      }),
++      _stepUp: function( steps ) {
++              if ( this._start() ) {
++                      this._spin( (steps || 1) * this.options.step );
++                      this._stop();
++              }
++      },
++
++      stepDown: spinner_modifier(function( steps ) {
++              this._stepDown( steps );
++      }),
++      _stepDown: function( steps ) {
++              if ( this._start() ) {
++                      this._spin( (steps || 1) * -this.options.step );
++                      this._stop();
++              }
++      },
++
++      pageUp: spinner_modifier(function( pages ) {
++              this._stepUp( (pages || 1) * this.options.page );
++      }),
++
++      pageDown: spinner_modifier(function( pages ) {
++              this._stepDown( (pages || 1) * this.options.page );
++      }),
++
++      value: function( newVal ) {
++              if ( !arguments.length ) {
++                      return this._parse( this.element.val() );
++              }
++              spinner_modifier( this._value ).call( this, newVal );
++      },
++
++      widget: function() {
++              return this.uiSpinner;
++      }
++});
++
++
++/*!
++ * jQuery UI Tabs 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/tabs/
++ */
++
++
++var tabs = $.widget( "ui.tabs", {
++      version: "1.11.4",
++      delay: 300,
++      options: {
++              active: null,
++              collapsible: false,
++              event: "click",
++              heightStyle: "content",
++              hide: null,
++              show: null,
++
++              // callbacks
++              activate: null,
++              beforeActivate: null,
++              beforeLoad: null,
++              load: null
++      },
++
++      _isLocal: (function() {
++              var rhash = /#.*$/;
++
++              return function( anchor ) {
++                      var anchorUrl, locationUrl;
++
++                      // support: IE7
++                      // IE7 doesn't normalize the href property when set via script (#9317)
++                      anchor = anchor.cloneNode( false );
++
++                      anchorUrl = anchor.href.replace( rhash, "" );
++                      locationUrl = location.href.replace( rhash, "" );
++
++                      // decoding may throw an error if the URL isn't UTF-8 (#9518)
++                      try {
++                              anchorUrl = decodeURIComponent( anchorUrl );
++                      } catch ( error ) {}
++                      try {
++                              locationUrl = decodeURIComponent( locationUrl );
++                      } catch ( error ) {}
++
++                      return anchor.hash.length > 1 && anchorUrl === locationUrl;
++              };
++      })(),
++
++      _create: function() {
++              var that = this,
++                      options = this.options;
++
++              this.running = false;
++
++              this.element
++                      .addClass( "ui-tabs ui-widget ui-widget-content ui-corner-all" )
++                      .toggleClass( "ui-tabs-collapsible", options.collapsible );
++
++              this._processTabs();
++              options.active = this._initialActive();
++
++              // Take disabling tabs via class attribute from HTML
++              // into account and update option properly.
++              if ( $.isArray( options.disabled ) ) {
++                      options.disabled = $.unique( options.disabled.concat(
++                              $.map( this.tabs.filter( ".ui-state-disabled" ), function( li ) {
++                                      return that.tabs.index( li );
++                              })
++                      ) ).sort();
++              }
++
++              // check for length avoids error when initializing empty list
++              if ( this.options.active !== false && this.anchors.length ) {
++                      this.active = this._findActive( options.active );
++              } else {
++                      this.active = $();
++              }
++
++              this._refresh();
++
++              if ( this.active.length ) {
++                      this.load( options.active );
++              }
++      },
++
++      _initialActive: function() {
++              var active = this.options.active,
++                      collapsible = this.options.collapsible,
++                      locationHash = location.hash.substring( 1 );
++
++              if ( active === null ) {
++                      // check the fragment identifier in the URL
++                      if ( locationHash ) {
++                              this.tabs.each(function( i, tab ) {
++                                      if ( $( tab ).attr( "aria-controls" ) === locationHash ) {
++                                              active = i;
++                                              return false;
++                                      }
++                              });
++                      }
++
++                      // check for a tab marked active via a class
++                      if ( active === null ) {
++                              active = this.tabs.index( this.tabs.filter( ".ui-tabs-active" ) );
++                      }
++
++                      // no active tab, set to false
++                      if ( active === null || active === -1 ) {
++                              active = this.tabs.length ? 0 : false;
++                      }
++              }
++
++              // handle numbers: negative, out of range
++              if ( active !== false ) {
++                      active = this.tabs.index( this.tabs.eq( active ) );
++                      if ( active === -1 ) {
++                              active = collapsible ? false : 0;
++                      }
++              }
++
++              // don't allow collapsible: false and active: false
++              if ( !collapsible && active === false && this.anchors.length ) {
++                      active = 0;
++              }
++
++              return active;
++      },
++
++      _getCreateEventData: function() {
++              return {
++                      tab: this.active,
++                      panel: !this.active.length ? $() : this._getPanelForTab( this.active )
++              };
++      },
++
++      _tabKeydown: function( event ) {
++              var focusedTab = $( this.document[0].activeElement ).closest( "li" ),
++                      selectedIndex = this.tabs.index( focusedTab ),
++                      goingForward = true;
++
++              if ( this._handlePageNav( event ) ) {
++                      return;
++              }
++
++              switch ( event.keyCode ) {
++                      case $.ui.keyCode.RIGHT:
++                      case $.ui.keyCode.DOWN:
++                              selectedIndex++;
++                              break;
++                      case $.ui.keyCode.UP:
++                      case $.ui.keyCode.LEFT:
++                              goingForward = false;
++                              selectedIndex--;
++                              break;
++                      case $.ui.keyCode.END:
++                              selectedIndex = this.anchors.length - 1;
++                              break;
++                      case $.ui.keyCode.HOME:
++                              selectedIndex = 0;
++                              break;
++                      case $.ui.keyCode.SPACE:
++                              // Activate only, no collapsing
++                              event.preventDefault();
++                              clearTimeout( this.activating );
++                              this._activate( selectedIndex );
++                              return;
++                      case $.ui.keyCode.ENTER:
++                              // Toggle (cancel delayed activation, allow collapsing)
++                              event.preventDefault();
++                              clearTimeout( this.activating );
++                              // Determine if we should collapse or activate
++                              this._activate( selectedIndex === this.options.active ? false : selectedIndex );
++                              return;
++                      default:
++                              return;
++              }
++
++              // Focus the appropriate tab, based on which key was pressed
++              event.preventDefault();
++              clearTimeout( this.activating );
++              selectedIndex = this._focusNextTab( selectedIndex, goingForward );
++
++              // Navigating with control/command key will prevent automatic activation
++              if ( !event.ctrlKey && !event.metaKey ) {
++
++                      // Update aria-selected immediately so that AT think the tab is already selected.
++                      // Otherwise AT may confuse the user by stating that they need to activate the tab,
++                      // but the tab will already be activated by the time the announcement finishes.
++                      focusedTab.attr( "aria-selected", "false" );
++                      this.tabs.eq( selectedIndex ).attr( "aria-selected", "true" );
++
++                      this.activating = this._delay(function() {
++                              this.option( "active", selectedIndex );
++                      }, this.delay );
++              }
++      },
++
++      _panelKeydown: function( event ) {
++              if ( this._handlePageNav( event ) ) {
++                      return;
++              }
++
++              // Ctrl+up moves focus to the current tab
++              if ( event.ctrlKey && event.keyCode === $.ui.keyCode.UP ) {
++                      event.preventDefault();
++                      this.active.focus();
++              }
++      },
++
++      // Alt+page up/down moves focus to the previous/next tab (and activates)
++      _handlePageNav: function( event ) {
++              if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_UP ) {
++                      this._activate( this._focusNextTab( this.options.active - 1, false ) );
++                      return true;
++              }
++              if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_DOWN ) {
++                      this._activate( this._focusNextTab( this.options.active + 1, true ) );
++                      return true;
++              }
++      },
++
++      _findNextTab: function( index, goingForward ) {
++              var lastTabIndex = this.tabs.length - 1;
++
++              function constrain() {
++                      if ( index > lastTabIndex ) {
++                              index = 0;
++                      }
++                      if ( index < 0 ) {
++                              index = lastTabIndex;
++                      }
++                      return index;
++              }
++
++              while ( $.inArray( constrain(), this.options.disabled ) !== -1 ) {
++                      index = goingForward ? index + 1 : index - 1;
++              }
++
++              return index;
++      },
++
++      _focusNextTab: function( index, goingForward ) {
++              index = this._findNextTab( index, goingForward );
++              this.tabs.eq( index ).focus();
++              return index;
++      },
++
++      _setOption: function( key, value ) {
++              if ( key === "active" ) {
++                      // _activate() will handle invalid values and update this.options
++                      this._activate( value );
++                      return;
++              }
++
++              if ( key === "disabled" ) {
++                      // don't use the widget factory's disabled handling
++                      this._setupDisabled( value );
++                      return;
++              }
++
++              this._super( key, value);
++
++              if ( key === "collapsible" ) {
++                      this.element.toggleClass( "ui-tabs-collapsible", value );
++                      // Setting collapsible: false while collapsed; open first panel
++                      if ( !value && this.options.active === false ) {
++                              this._activate( 0 );
++                      }
++              }
++
++              if ( key === "event" ) {
++                      this._setupEvents( value );
++              }
++
++              if ( key === "heightStyle" ) {
++                      this._setupHeightStyle( value );
++              }
++      },
++
++      _sanitizeSelector: function( hash ) {
++              return hash ? hash.replace( /[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g, "\\$&" ) : "";
++      },
++
++      refresh: function() {
++              var options = this.options,
++                      lis = this.tablist.children( ":has(a[href])" );
++
++              // get disabled tabs from class attribute from HTML
++              // this will get converted to a boolean if needed in _refresh()
++              options.disabled = $.map( lis.filter( ".ui-state-disabled" ), function( tab ) {
++                      return lis.index( tab );
++              });
++
++              this._processTabs();
++
++              // was collapsed or no tabs
++              if ( options.active === false || !this.anchors.length ) {
++                      options.active = false;
++                      this.active = $();
++              // was active, but active tab is gone
++              } else if ( this.active.length && !$.contains( this.tablist[ 0 ], this.active[ 0 ] ) ) {
++                      // all remaining tabs are disabled
++                      if ( this.tabs.length === options.disabled.length ) {
++                              options.active = false;
++                              this.active = $();
++                      // activate previous tab
++                      } else {
++                              this._activate( this._findNextTab( Math.max( 0, options.active - 1 ), false ) );
++                      }
++              // was active, active tab still exists
++              } else {
++                      // make sure active index is correct
++                      options.active = this.tabs.index( this.active );
++              }
++
++              this._refresh();
++      },
++
++      _refresh: function() {
++              this._setupDisabled( this.options.disabled );
++              this._setupEvents( this.options.event );
++              this._setupHeightStyle( this.options.heightStyle );
++
++              this.tabs.not( this.active ).attr({
++                      "aria-selected": "false",
++                      "aria-expanded": "false",
++                      tabIndex: -1
++              });
++              this.panels.not( this._getPanelForTab( this.active ) )
++                      .hide()
++                      .attr({
++                              "aria-hidden": "true"
++                      });
++
++              // Make sure one tab is in the tab order
++              if ( !this.active.length ) {
++                      this.tabs.eq( 0 ).attr( "tabIndex", 0 );
++              } else {
++                      this.active
++                              .addClass( "ui-tabs-active ui-state-active" )
++                              .attr({
++                                      "aria-selected": "true",
++                                      "aria-expanded": "true",
++                                      tabIndex: 0
++                              });
++                      this._getPanelForTab( this.active )
++                              .show()
++                              .attr({
++                                      "aria-hidden": "false"
++                              });
++              }
++      },
++
++      _processTabs: function() {
++              var that = this,
++                      prevTabs = this.tabs,
++                      prevAnchors = this.anchors,
++                      prevPanels = this.panels;
++
++              this.tablist = this._getList()
++                      .addClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" )
++                      .attr( "role", "tablist" )
++
++                      // Prevent users from focusing disabled tabs via click
++                      .delegate( "> li", "mousedown" + this.eventNamespace, function( event ) {
++                              if ( $( this ).is( ".ui-state-disabled" ) ) {
++                                      event.preventDefault();
++                              }
++                      })
++
++                      // support: IE <9
++                      // Preventing the default action in mousedown doesn't prevent IE
++                      // from focusing the element, so if the anchor gets focused, blur.
++                      // We don't have to worry about focusing the previously focused
++                      // element since clicking on a non-focusable element should focus
++                      // the body anyway.
++                      .delegate( ".ui-tabs-anchor", "focus" + this.eventNamespace, function() {
++                              if ( $( this ).closest( "li" ).is( ".ui-state-disabled" ) ) {
++                                      this.blur();
++                              }
++                      });
++
++              this.tabs = this.tablist.find( "> li:has(a[href])" )
++                      .addClass( "ui-state-default ui-corner-top" )
++                      .attr({
++                              role: "tab",
++                              tabIndex: -1
++                      });
++
++              this.anchors = this.tabs.map(function() {
++                              return $( "a", this )[ 0 ];
++                      })
++                      .addClass( "ui-tabs-anchor" )
++                      .attr({
++                              role: "presentation",
++                              tabIndex: -1
++                      });
++
++              this.panels = $();
++
++              this.anchors.each(function( i, anchor ) {
++                      var selector, panel, panelId,
++                              anchorId = $( anchor ).uniqueId().attr( "id" ),
++                              tab = $( anchor ).closest( "li" ),
++                              originalAriaControls = tab.attr( "aria-controls" );
++
++                      // inline tab
++                      if ( that._isLocal( anchor ) ) {
++                              selector = anchor.hash;
++                              panelId = selector.substring( 1 );
++                              panel = that.element.find( that._sanitizeSelector( selector ) );
++                      // remote tab
++                      } else {
++                              // If the tab doesn't already have aria-controls,
++                              // generate an id by using a throw-away element
++                              panelId = tab.attr( "aria-controls" ) || $( {} ).uniqueId()[ 0 ].id;
++                              selector = "#" + panelId;
++                              panel = that.element.find( selector );
++                              if ( !panel.length ) {
++                                      panel = that._createPanel( panelId );
++                                      panel.insertAfter( that.panels[ i - 1 ] || that.tablist );
++                              }
++                              panel.attr( "aria-live", "polite" );
++                      }
++
++                      if ( panel.length) {
++                              that.panels = that.panels.add( panel );
++                      }
++                      if ( originalAriaControls ) {
++                              tab.data( "ui-tabs-aria-controls", originalAriaControls );
++                      }
++                      tab.attr({
++                              "aria-controls": panelId,
++                              "aria-labelledby": anchorId
++                      });
++                      panel.attr( "aria-labelledby", anchorId );
++              });
++
++              this.panels
++                      .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
++                      .attr( "role", "tabpanel" );
++
++              // Avoid memory leaks (#10056)
++              if ( prevTabs ) {
++                      this._off( prevTabs.not( this.tabs ) );
++                      this._off( prevAnchors.not( this.anchors ) );
++                      this._off( prevPanels.not( this.panels ) );
++              }
++      },
++
++      // allow overriding how to find the list for rare usage scenarios (#7715)
++      _getList: function() {
++              return this.tablist || this.element.find( "ol,ul" ).eq( 0 );
++      },
++
++      _createPanel: function( id ) {
++              return $( "<div>" )
++                      .attr( "id", id )
++                      .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
++                      .data( "ui-tabs-destroy", true );
++      },
++
++      _setupDisabled: function( disabled ) {
++              if ( $.isArray( disabled ) ) {
++                      if ( !disabled.length ) {
++                              disabled = false;
++                      } else if ( disabled.length === this.anchors.length ) {
++                              disabled = true;
++                      }
++              }
++
++              // disable tabs
++              for ( var i = 0, li; ( li = this.tabs[ i ] ); i++ ) {
++                      if ( disabled === true || $.inArray( i, disabled ) !== -1 ) {
++                              $( li )
++                                      .addClass( "ui-state-disabled" )
++                                      .attr( "aria-disabled", "true" );
++                      } else {
++                              $( li )
++                                      .removeClass( "ui-state-disabled" )
++                                      .removeAttr( "aria-disabled" );
++                      }
++              }
++
++              this.options.disabled = disabled;
++      },
++
++      _setupEvents: function( event ) {
++              var events = {};
++              if ( event ) {
++                      $.each( event.split(" "), function( index, eventName ) {
++                              events[ eventName ] = "_eventHandler";
++                      });
++              }
++
++              this._off( this.anchors.add( this.tabs ).add( this.panels ) );
++              // Always prevent the default action, even when disabled
++              this._on( true, this.anchors, {
++                      click: function( event ) {
++                              event.preventDefault();
++                      }
++              });
++              this._on( this.anchors, events );
++              this._on( this.tabs, { keydown: "_tabKeydown" } );
++              this._on( this.panels, { keydown: "_panelKeydown" } );
++
++              this._focusable( this.tabs );
++              this._hoverable( this.tabs );
++      },
++
++      _setupHeightStyle: function( heightStyle ) {
++              var maxHeight,
++                      parent = this.element.parent();
++
++              if ( heightStyle === "fill" ) {
++                      maxHeight = parent.height();
++                      maxHeight -= this.element.outerHeight() - this.element.height();
++
++                      this.element.siblings( ":visible" ).each(function() {
++                              var elem = $( this ),
++                                      position = elem.css( "position" );
++
++                              if ( position === "absolute" || position === "fixed" ) {
++                                      return;
++                              }
++                              maxHeight -= elem.outerHeight( true );
++                      });
++
++                      this.element.children().not( this.panels ).each(function() {
++                              maxHeight -= $( this ).outerHeight( true );
++                      });
++
++                      this.panels.each(function() {
++                              $( this ).height( Math.max( 0, maxHeight -
++                                      $( this ).innerHeight() + $( this ).height() ) );
++                      })
++                      .css( "overflow", "auto" );
++              } else if ( heightStyle === "auto" ) {
++                      maxHeight = 0;
++                      this.panels.each(function() {
++                              maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() );
++                      }).height( maxHeight );
++              }
++      },
++
++      _eventHandler: function( event ) {
++              var options = this.options,
++                      active = this.active,
++                      anchor = $( event.currentTarget ),
++                      tab = anchor.closest( "li" ),
++                      clickedIsActive = tab[ 0 ] === active[ 0 ],
++                      collapsing = clickedIsActive && options.collapsible,
++                      toShow = collapsing ? $() : this._getPanelForTab( tab ),
++                      toHide = !active.length ? $() : this._getPanelForTab( active ),
++                      eventData = {
++                              oldTab: active,
++                              oldPanel: toHide,
++                              newTab: collapsing ? $() : tab,
++                              newPanel: toShow
++                      };
++
++              event.preventDefault();
++
++              if ( tab.hasClass( "ui-state-disabled" ) ||
++                              // tab is already loading
++                              tab.hasClass( "ui-tabs-loading" ) ||
++                              // can't switch durning an animation
++                              this.running ||
++                              // click on active header, but not collapsible
++                              ( clickedIsActive && !options.collapsible ) ||
++                              // allow canceling activation
++                              ( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
++                      return;
++              }
++
++              options.active = collapsing ? false : this.tabs.index( tab );
++
++              this.active = clickedIsActive ? $() : tab;
++              if ( this.xhr ) {
++                      this.xhr.abort();
++              }
++
++              if ( !toHide.length && !toShow.length ) {
++                      $.error( "jQuery UI Tabs: Mismatching fragment identifier." );
++              }
++
++              if ( toShow.length ) {
++                      this.load( this.tabs.index( tab ), event );
++              }
++              this._toggle( event, eventData );
++      },
++
++      // handles show/hide for selecting tabs
++      _toggle: function( event, eventData ) {
++              var that = this,
++                      toShow = eventData.newPanel,
++                      toHide = eventData.oldPanel;
++
++              this.running = true;
++
++              function complete() {
++                      that.running = false;
++                      that._trigger( "activate", event, eventData );
++              }
++
++              function show() {
++                      eventData.newTab.closest( "li" ).addClass( "ui-tabs-active ui-state-active" );
++
++                      if ( toShow.length && that.options.show ) {
++                              that._show( toShow, that.options.show, complete );
++                      } else {
++                              toShow.show();
++                              complete();
++                      }
++              }
++
++              // start out by hiding, then showing, then completing
++              if ( toHide.length && this.options.hide ) {
++                      this._hide( toHide, this.options.hide, function() {
++                              eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
++                              show();
++                      });
++              } else {
++                      eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
++                      toHide.hide();
++                      show();
++              }
++
++              toHide.attr( "aria-hidden", "true" );
++              eventData.oldTab.attr({
++                      "aria-selected": "false",
++                      "aria-expanded": "false"
++              });
++              // If we're switching tabs, remove the old tab from the tab order.
++              // If we're opening from collapsed state, remove the previous tab from the tab order.
++              // If we're collapsing, then keep the collapsing tab in the tab order.
++              if ( toShow.length && toHide.length ) {
++                      eventData.oldTab.attr( "tabIndex", -1 );
++              } else if ( toShow.length ) {
++                      this.tabs.filter(function() {
++                              return $( this ).attr( "tabIndex" ) === 0;
++                      })
++                      .attr( "tabIndex", -1 );
++              }
++
++              toShow.attr( "aria-hidden", "false" );
++              eventData.newTab.attr({
++                      "aria-selected": "true",
++                      "aria-expanded": "true",
++                      tabIndex: 0
++              });
++      },
++
++      _activate: function( index ) {
++              var anchor,
++                      active = this._findActive( index );
++
++              // trying to activate the already active panel
++              if ( active[ 0 ] === this.active[ 0 ] ) {
++                      return;
++              }
++
++              // trying to collapse, simulate a click on the current active header
++              if ( !active.length ) {
++                      active = this.active;
++              }
++
++              anchor = active.find( ".ui-tabs-anchor" )[ 0 ];
++              this._eventHandler({
++                      target: anchor,
++                      currentTarget: anchor,
++                      preventDefault: $.noop
++              });
++      },
++
++      _findActive: function( index ) {
++              return index === false ? $() : this.tabs.eq( index );
++      },
++
++      _getIndex: function( index ) {
++              // meta-function to give users option to provide a href string instead of a numerical index.
++              if ( typeof index === "string" ) {
++                      index = this.anchors.index( this.anchors.filter( "[href$='" + index + "']" ) );
++              }
++
++              return index;
++      },
++
++      _destroy: function() {
++              if ( this.xhr ) {
++                      this.xhr.abort();
++              }
++
++              this.element.removeClass( "ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible" );
++
++              this.tablist
++                      .removeClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" )
++                      .removeAttr( "role" );
++
++              this.anchors
++                      .removeClass( "ui-tabs-anchor" )
++                      .removeAttr( "role" )
++                      .removeAttr( "tabIndex" )
++                      .removeUniqueId();
++
++              this.tablist.unbind( this.eventNamespace );
++
++              this.tabs.add( this.panels ).each(function() {
++                      if ( $.data( this, "ui-tabs-destroy" ) ) {
++                              $( this ).remove();
++                      } else {
++                              $( this )
++                                      .removeClass( "ui-state-default ui-state-active ui-state-disabled " +
++                                              "ui-corner-top ui-corner-bottom ui-widget-content ui-tabs-active ui-tabs-panel" )
++                                      .removeAttr( "tabIndex" )
++                                      .removeAttr( "aria-live" )
++                                      .removeAttr( "aria-busy" )
++                                      .removeAttr( "aria-selected" )
++                                      .removeAttr( "aria-labelledby" )
++                                      .removeAttr( "aria-hidden" )
++                                      .removeAttr( "aria-expanded" )
++                                      .removeAttr( "role" );
++                      }
++              });
++
++              this.tabs.each(function() {
++                      var li = $( this ),
++                              prev = li.data( "ui-tabs-aria-controls" );
++                      if ( prev ) {
++                              li
++                                      .attr( "aria-controls", prev )
++                                      .removeData( "ui-tabs-aria-controls" );
++                      } else {
++                              li.removeAttr( "aria-controls" );
++                      }
++              });
++
++              this.panels.show();
++
++              if ( this.options.heightStyle !== "content" ) {
++                      this.panels.css( "height", "" );
++              }
++      },
++
++      enable: function( index ) {
++              var disabled = this.options.disabled;
++              if ( disabled === false ) {
++                      return;
++              }
++
++              if ( index === undefined ) {
++                      disabled = false;
++              } else {
++                      index = this._getIndex( index );
++                      if ( $.isArray( disabled ) ) {
++                              disabled = $.map( disabled, function( num ) {
++                                      return num !== index ? num : null;
++                              });
++                      } else {
++                              disabled = $.map( this.tabs, function( li, num ) {
++                                      return num !== index ? num : null;
++                              });
++                      }
++              }
++              this._setupDisabled( disabled );
++      },
++
++      disable: function( index ) {
++              var disabled = this.options.disabled;
++              if ( disabled === true ) {
++                      return;
++              }
++
++              if ( index === undefined ) {
++                      disabled = true;
++              } else {
++                      index = this._getIndex( index );
++                      if ( $.inArray( index, disabled ) !== -1 ) {
++                              return;
++                      }
++                      if ( $.isArray( disabled ) ) {
++                              disabled = $.merge( [ index ], disabled ).sort();
++                      } else {
++                              disabled = [ index ];
++                      }
++              }
++              this._setupDisabled( disabled );
++      },
++
++      load: function( index, event ) {
++              index = this._getIndex( index );
++              var that = this,
++                      tab = this.tabs.eq( index ),
++                      anchor = tab.find( ".ui-tabs-anchor" ),
++                      panel = this._getPanelForTab( tab ),
++                      eventData = {
++                              tab: tab,
++                              panel: panel
++                      },
++                      complete = function( jqXHR, status ) {
++                              if ( status === "abort" ) {
++                                      that.panels.stop( false, true );
++                              }
++
++                              tab.removeClass( "ui-tabs-loading" );
++                              panel.removeAttr( "aria-busy" );
++
++                              if ( jqXHR === that.xhr ) {
++                                      delete that.xhr;
++                              }
++                      };
++
++              // not remote
++              if ( this._isLocal( anchor[ 0 ] ) ) {
++                      return;
++              }
++
++              this.xhr = $.ajax( this._ajaxSettings( anchor, event, eventData ) );
++
++              // support: jQuery <1.8
++              // jQuery <1.8 returns false if the request is canceled in beforeSend,
++              // but as of 1.8, $.ajax() always returns a jqXHR object.
++              if ( this.xhr && this.xhr.statusText !== "canceled" ) {
++                      tab.addClass( "ui-tabs-loading" );
++                      panel.attr( "aria-busy", "true" );
++
++                      this.xhr
++                              .done(function( response, status, jqXHR ) {
++                                      // support: jQuery <1.8
++                                      // http://bugs.jquery.com/ticket/11778
++                                      setTimeout(function() {
++                                              panel.html( response );
++                                              that._trigger( "load", event, eventData );
++
++                                              complete( jqXHR, status );
++                                      }, 1 );
++                              })
++                              .fail(function( jqXHR, status ) {
++                                      // support: jQuery <1.8
++                                      // http://bugs.jquery.com/ticket/11778
++                                      setTimeout(function() {
++                                              complete( jqXHR, status );
++                                      }, 1 );
++                              });
++              }
++      },
++
++      _ajaxSettings: function( anchor, event, eventData ) {
++              var that = this;
++              return {
++                      url: anchor.attr( "href" ),
++                      beforeSend: function( jqXHR, settings ) {
++                              return that._trigger( "beforeLoad", event,
++                                      $.extend( { jqXHR: jqXHR, ajaxSettings: settings }, eventData ) );
++                      }
++              };
++      },
++
++      _getPanelForTab: function( tab ) {
++              var id = $( tab ).attr( "aria-controls" );
++              return this.element.find( this._sanitizeSelector( "#" + id ) );
++      }
++});
++
++
++/*!
++ * jQuery UI Tooltip 1.11.4
++ * http://jqueryui.com
++ *
++ * Copyright jQuery Foundation and other contributors
++ * Released under the MIT license.
++ * http://jquery.org/license
++ *
++ * http://api.jqueryui.com/tooltip/
++ */
++
++
++var tooltip = $.widget( "ui.tooltip", {
++      version: "1.11.4",
++      options: {
++              content: function() {
++                      // support: IE<9, Opera in jQuery <1.7
++                      // .text() can't accept undefined, so coerce to a string
++                      var title = $( this ).attr( "title" ) || "";
++                      // Escape title, since we're going from an attribute to raw HTML
++                      return $( "<a>" ).text( title ).html();
++              },
++              hide: true,
++              // Disabled elements have inconsistent behavior across browsers (#8661)
++              items: "[title]:not([disabled])",
++              position: {
++                      my: "left top+15",
++                      at: "left bottom",
++                      collision: "flipfit flip"
++              },
++              show: true,
++              tooltipClass: null,
++              track: false,
++
++              // callbacks
++              close: null,
++              open: null
++      },
++
++      _addDescribedBy: function( elem, id ) {
++              var describedby = (elem.attr( "aria-describedby" ) || "").split( /\s+/ );
++              describedby.push( id );
++              elem
++                      .data( "ui-tooltip-id", id )
++                      .attr( "aria-describedby", $.trim( describedby.join( " " ) ) );
++      },
++
++      _removeDescribedBy: function( elem ) {
++              var id = elem.data( "ui-tooltip-id" ),
++                      describedby = (elem.attr( "aria-describedby" ) || "").split( /\s+/ ),
++                      index = $.inArray( id, describedby );
++
++              if ( index !== -1 ) {
++                      describedby.splice( index, 1 );
++              }
++
++              elem.removeData( "ui-tooltip-id" );
++              describedby = $.trim( describedby.join( " " ) );
++              if ( describedby ) {
++                      elem.attr( "aria-describedby", describedby );
++              } else {
++                      elem.removeAttr( "aria-describedby" );
++              }
++      },
++
++      _create: function() {
++              this._on({
++                      mouseover: "open",
++                      focusin: "open"
++              });
++
++              // IDs of generated tooltips, needed for destroy
++              this.tooltips = {};
++
++              // IDs of parent tooltips where we removed the title attribute
++              this.parents = {};
++
++              if ( this.options.disabled ) {
++                      this._disable();
++              }
++
++              // Append the aria-live region so tooltips announce correctly
++              this.liveRegion = $( "<div>" )
++                      .attr({
++                              role: "log",
++                              "aria-live": "assertive",
++                              "aria-relevant": "additions"
++                      })
++                      .addClass( "ui-helper-hidden-accessible" )
++                      .appendTo( this.document[ 0 ].body );
++      },
++
++      _setOption: function( key, value ) {
++              var that = this;
++
++              if ( key === "disabled" ) {
++                      this[ value ? "_disable" : "_enable" ]();
++                      this.options[ key ] = value;
++                      // disable element style changes
++                      return;
++              }
++
++              this._super( key, value );
++
++              if ( key === "content" ) {
++                      $.each( this.tooltips, function( id, tooltipData ) {
++                              that._updateContent( tooltipData.element );
++                      });
++              }
++      },
++
++      _disable: function() {
++              var that = this;
++
++              // close open tooltips
++              $.each( this.tooltips, function( id, tooltipData ) {
++                      var event = $.Event( "blur" );
++                      event.target = event.currentTarget = tooltipData.element[ 0 ];
++                      that.close( event, true );
++              });
++
++              // remove title attributes to prevent native tooltips
++              this.element.find( this.options.items ).addBack().each(function() {
++                      var element = $( this );
++                      if ( element.is( "[title]" ) ) {
++                              element
++                                      .data( "ui-tooltip-title", element.attr( "title" ) )
++                                      .removeAttr( "title" );
++                      }
++              });
++      },
++
++      _enable: function() {
++              // restore title attributes
++              this.element.find( this.options.items ).addBack().each(function() {
++                      var element = $( this );
++                      if ( element.data( "ui-tooltip-title" ) ) {
++                              element.attr( "title", element.data( "ui-tooltip-title" ) );
++                      }
++              });
++      },
++
++      open: function( event ) {
++              var that = this,
++                      target = $( event ? event.target : this.element )
++                              // we need closest here due to mouseover bubbling,
++                              // but always pointing at the same event target
++                              .closest( this.options.items );
++
++              // No element to show a tooltip for or the tooltip is already open
++              if ( !target.length || target.data( "ui-tooltip-id" ) ) {
++                      return;
++              }
++
++              if ( target.attr( "title" ) ) {
++                      target.data( "ui-tooltip-title", target.attr( "title" ) );
++              }
++
++              target.data( "ui-tooltip-open", true );
++
++              // kill parent tooltips, custom or native, for hover
++              if ( event && event.type === "mouseover" ) {
++                      target.parents().each(function() {
++                              var parent = $( this ),
++                                      blurEvent;
++                              if ( parent.data( "ui-tooltip-open" ) ) {
++                                      blurEvent = $.Event( "blur" );
++                                      blurEvent.target = blurEvent.currentTarget = this;
++                                      that.close( blurEvent, true );
++                              }
++                              if ( parent.attr( "title" ) ) {
++                                      parent.uniqueId();
++                                      that.parents[ this.id ] = {
++                                              element: this,
++                                              title: parent.attr( "title" )
++                                      };
++                                      parent.attr( "title", "" );
++                              }
++                      });
++              }
++
++              this._registerCloseHandlers( event, target );
++              this._updateContent( target, event );
++      },
++
++      _updateContent: function( target, event ) {
++              var content,
++                      contentOption = this.options.content,
++                      that = this,
++                      eventType = event ? event.type : null;
++
++              if ( typeof contentOption === "string" ) {
++                      return this._open( event, target, contentOption );
++              }
++
++              content = contentOption.call( target[0], function( response ) {
++
++                      // IE may instantly serve a cached response for ajax requests
++                      // delay this call to _open so the other call to _open runs first
++                      that._delay(function() {
++
++                              // Ignore async response if tooltip was closed already
++                              if ( !target.data( "ui-tooltip-open" ) ) {
++                                      return;
++                              }
++
++                              // jQuery creates a special event for focusin when it doesn't
++                              // exist natively. To improve performance, the native event
++                              // object is reused and the type is changed. Therefore, we can't
++                              // rely on the type being correct after the event finished
++                              // bubbling, so we set it back to the previous value. (#8740)
++                              if ( event ) {
++                                      event.type = eventType;
++                              }
++                              this._open( event, target, response );
++                      });
++              });
++              if ( content ) {
++                      this._open( event, target, content );
++              }
++      },
++
++      _open: function( event, target, content ) {
++              var tooltipData, tooltip, delayedShow, a11yContent,
++                      positionOption = $.extend( {}, this.options.position );
++
++              if ( !content ) {
++                      return;
++              }
++
++              // Content can be updated multiple times. If the tooltip already
++              // exists, then just update the content and bail.
++              tooltipData = this._find( target );
++              if ( tooltipData ) {
++                      tooltipData.tooltip.find( ".ui-tooltip-content" ).html( content );
++                      return;
++              }
++
++              // if we have a title, clear it to prevent the native tooltip
++              // we have to check first to avoid defining a title if none exists
++              // (we don't want to cause an element to start matching [title])
++              //
++              // We use removeAttr only for key events, to allow IE to export the correct
++              // accessible attributes. For mouse events, set to empty string to avoid
++              // native tooltip showing up (happens only when removing inside mouseover).
++              if ( target.is( "[title]" ) ) {
++                      if ( event && event.type === "mouseover" ) {
++                              target.attr( "title", "" );
++                      } else {
++                              target.removeAttr( "title" );
++                      }
++              }
++
++              tooltipData = this._tooltip( target );
++              tooltip = tooltipData.tooltip;
++              this._addDescribedBy( target, tooltip.attr( "id" ) );
++              tooltip.find( ".ui-tooltip-content" ).html( content );
++
++              // Support: Voiceover on OS X, JAWS on IE <= 9
++              // JAWS announces deletions even when aria-relevant="additions"
++              // Voiceover will sometimes re-read the entire log region's contents from the beginning
++              this.liveRegion.children().hide();
++              if ( content.clone ) {
++                      a11yContent = content.clone();
++                      a11yContent.removeAttr( "id" ).find( "[id]" ).removeAttr( "id" );
++              } else {
++                      a11yContent = content;
++              }
++              $( "<div>" ).html( a11yContent ).appendTo( this.liveRegion );
++
++              function position( event ) {
++                      positionOption.of = event;
++                      if ( tooltip.is( ":hidden" ) ) {
++                              return;
++                      }
++                      tooltip.position( positionOption );
++              }
++              if ( this.options.track && event && /^mouse/.test( event.type ) ) {
++                      this._on( this.document, {
++                              mousemove: position
++                      });
++                      // trigger once to override element-relative positioning
++                      position( event );
++              } else {
++                      tooltip.position( $.extend({
++                              of: target
++                      }, this.options.position ) );
++              }
++
++              tooltip.hide();
++
++              this._show( tooltip, this.options.show );
++              // Handle tracking tooltips that are shown with a delay (#8644). As soon
++              // as the tooltip is visible, position the tooltip using the most recent
++              // event.
++              if ( this.options.show && this.options.show.delay ) {
++                      delayedShow = this.delayedShow = setInterval(function() {
++                              if ( tooltip.is( ":visible" ) ) {
++                                      position( positionOption.of );
++                                      clearInterval( delayedShow );
++                              }
++                      }, $.fx.interval );
++              }
++
++              this._trigger( "open", event, { tooltip: tooltip } );
++      },
++
++      _registerCloseHandlers: function( event, target ) {
++              var events = {
++                      keyup: function( event ) {
++                              if ( event.keyCode === $.ui.keyCode.ESCAPE ) {
++                                      var fakeEvent = $.Event(event);
++                                      fakeEvent.currentTarget = target[0];
++                                      this.close( fakeEvent, true );
++                              }
++                      }
++              };
++
++              // Only bind remove handler for delegated targets. Non-delegated
++              // tooltips will handle this in destroy.
++              if ( target[ 0 ] !== this.element[ 0 ] ) {
++                      events.remove = function() {
++                              this._removeTooltip( this._find( target ).tooltip );
++                      };
++              }
++
++              if ( !event || event.type === "mouseover" ) {
++                      events.mouseleave = "close";
++              }
++              if ( !event || event.type === "focusin" ) {
++                      events.focusout = "close";
++              }
++              this._on( true, target, events );
++      },
++
++      close: function( event ) {
++              var tooltip,
++                      that = this,
++                      target = $( event ? event.currentTarget : this.element ),
++                      tooltipData = this._find( target );
++
++              // The tooltip may already be closed
++              if ( !tooltipData ) {
++
++                      // We set ui-tooltip-open immediately upon open (in open()), but only set the
++                      // additional data once there's actually content to show (in _open()). So even if the
++                      // tooltip doesn't have full data, we always remove ui-tooltip-open in case we're in
++                      // the period between open() and _open().
++                      target.removeData( "ui-tooltip-open" );
++                      return;
++              }
++
++              tooltip = tooltipData.tooltip;
++
++              // disabling closes the tooltip, so we need to track when we're closing
++              // to avoid an infinite loop in case the tooltip becomes disabled on close
++              if ( tooltipData.closing ) {
++                      return;
++              }
++
++              // Clear the interval for delayed tracking tooltips
++              clearInterval( this.delayedShow );
++
++              // only set title if we had one before (see comment in _open())
++              // If the title attribute has changed since open(), don't restore
++              if ( target.data( "ui-tooltip-title" ) && !target.attr( "title" ) ) {
++                      target.attr( "title", target.data( "ui-tooltip-title" ) );
++              }
++
++              this._removeDescribedBy( target );
++
++              tooltipData.hiding = true;
++              tooltip.stop( true );
++              this._hide( tooltip, this.options.hide, function() {
++                      that._removeTooltip( $( this ) );
++              });
++
++              target.removeData( "ui-tooltip-open" );
++              this._off( target, "mouseleave focusout keyup" );
++
++              // Remove 'remove' binding only on delegated targets
++              if ( target[ 0 ] !== this.element[ 0 ] ) {
++                      this._off( target, "remove" );
++              }
++              this._off( this.document, "mousemove" );
++
++              if ( event && event.type === "mouseleave" ) {
++                      $.each( this.parents, function( id, parent ) {
++                              $( parent.element ).attr( "title", parent.title );
++                              delete that.parents[ id ];
++                      });
++              }
++
++              tooltipData.closing = true;
++              this._trigger( "close", event, { tooltip: tooltip } );
++              if ( !tooltipData.hiding ) {
++                      tooltipData.closing = false;
++              }
++      },
++
++      _tooltip: function( element ) {
++              var tooltip = $( "<div>" )
++                              .attr( "role", "tooltip" )
++                              .addClass( "ui-tooltip ui-widget ui-corner-all ui-widget-content " +
++                                      ( this.options.tooltipClass || "" ) ),
++                      id = tooltip.uniqueId().attr( "id" );
++
++              $( "<div>" )
++                      .addClass( "ui-tooltip-content" )
++                      .appendTo( tooltip );
++
++              tooltip.appendTo( this.document[0].body );
++
++              return this.tooltips[ id ] = {
++                      element: element,
++                      tooltip: tooltip
++              };
++      },
++
++      _find: function( target ) {
++              var id = target.data( "ui-tooltip-id" );
++              return id ? this.tooltips[ id ] : null;
++      },
++
++      _removeTooltip: function( tooltip ) {
++              tooltip.remove();
++              delete this.tooltips[ tooltip.attr( "id" ) ];
++      },
++
++      _destroy: function() {
++              var that = this;
++
++              // close open tooltips
++              $.each( this.tooltips, function( id, tooltipData ) {
++                      // Delegate to close method to handle common cleanup
++                      var event = $.Event( "blur" ),
++                              element = tooltipData.element;
++                      event.target = event.currentTarget = element[ 0 ];
++                      that.close( event, true );
++
++                      // Remove immediately; destroying an open tooltip doesn't use the
++                      // hide animation
++                      $( "#" + id ).remove();
++
++                      // Restore the title
++                      if ( element.data( "ui-tooltip-title" ) ) {
++                              // If the title attribute has changed since open(), don't restore
++                              if ( !element.attr( "title" ) ) {
++                                      element.attr( "title", element.data( "ui-tooltip-title" ) );
++                              }
++                              element.removeData( "ui-tooltip-title" );
++                      }
++              });
++              this.liveRegion.remove();
++      }
++});
++
++
++
++}));
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0ba378b1822e75a190a7705d75d83d882dcc7e9d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,408 @@@
++/*! DataTables 1.10.16
++ * ©2008-2017 SpryMedia Ltd - datatables.net/license
++ */
++
++/**
++ * @summary     DataTables
++ * @description Paginate, search and order HTML tables
++ * @version     1.10.16
++ * @file        jquery.dataTables.js
++ * @author      SpryMedia Ltd
++ * @contact     www.datatables.net
++ * @copyright   Copyright 2008-2017 SpryMedia Ltd.
++ *
++ * This source file is free software, available under the following license:
++ *   MIT license - http://datatables.net/license
++ *
++ * This source file is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
++ *
++ * For details please refer to: http://www.datatables.net
++ */
++
++/*jslint evil: true, undef: true, browser: true */
++/*globals $,require,jQuery,define,_selector_run,_selector_opts,_selector_first,_selector_row_indexes,_ext,_Api,_api_register,_api_registerPlural,_re_new_lines,_re_html,_re_formatted_numeric,_re_escape_regex,_empty,_intVal,_numToDecimal,_isNumber,_isHtml,_htmlNumeric,_pluck,_pluck_order,_range,_stripHtml,_unique,_fnBuildAjax,_fnAjaxUpdate,_fnAjaxParameters,_fnAjaxUpdateDraw,_fnAjaxDataSrc,_fnAddColumn,_fnColumnOptions,_fnAdjustColumnSizing,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnVisbleColumns,_fnGetColumns,_fnColumnTypes,_fnApplyColumnDefs,_fnHungarianMap,_fnCamelToHungarian,_fnLanguageCompat,_fnBrowserDetect,_fnAddData,_fnAddTr,_fnNodeToDataIndex,_fnNodeToColumnIndex,_fnGetCellData,_fnSetCellData,_fnSplitObjNotation,_fnGetObjectDataFn,_fnSetObjectDataFn,_fnGetDataMaster,_fnClearTable,_fnDeleteIndex,_fnInvalidate,_fnGetRowElements,_fnCreateTr,_fnBuildHead,_fnDrawHead,_fnDraw,_fnReDraw,_fnAddOptionsHtml,_fnDetectHeader,_fnGetUniqueThs,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnFilterCreateSearch,_fnEscapeRegex,_fnFilterData,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnInfoMacros,_fnInitialise,_fnInitComplete,_fnLengthChange,_fnFeatureHtmlLength,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnFeatureHtmlTable,_fnScrollDraw,_fnApplyToChildren,_fnCalculateColumnWidths,_fnThrottle,_fnConvertToWidth,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnSortFlatten,_fnSort,_fnSortAria,_fnSortListener,_fnSortAttachListener,_fnSortingClasses,_fnSortData,_fnSaveState,_fnLoadState,_fnSettingsFromNode,_fnLog,_fnMap,_fnBindAction,_fnCallbackReg,_fnCallbackFire,_fnLengthOverflow,_fnRenderer,_fnDataSource,_fnRowAttributes*/
++
++(function( factory ) {
++      "use strict";
++
++      if ( typeof define === 'function' && define.amd ) {
++              // AMD
++              define( ['jquery'], function ( $ ) {
++                      return factory( $, window, document );
++              } );
++      }
++      else if ( typeof exports === 'object' ) {
++              // CommonJS
++              module.exports = function (root, $) {
++                      if ( ! root ) {
++                              // CommonJS environments without a window global must pass a
++                              // root. This will give an error otherwise
++                              root = window;
++                      }
++
++                      if ( ! $ ) {
++                              $ = typeof window !== 'undefined' ? // jQuery's factory checks for a global window
++                                      require('jquery') :
++                                      require('jquery')( root );
++                      }
++
++                      return factory( $, root, root.document );
++              };
++      }
++      else {
++              // Browser
++              factory( jQuery, window, document );
++      }
++}
++(function( $, window, document, undefined ) {
++      "use strict";
++
++      /**
++       * DataTables is a plug-in for the jQuery Javascript library. It is a highly
++       * flexible tool, based upon the foundations of progressive enhancement,
++       * which will add advanced interaction controls to any HTML table. For a
++       * full list of features please refer to
++       * [DataTables.net](href="http://datatables.net).
++       *
++       * Note that the `DataTable` object is not a global variable but is aliased
++       * to `jQuery.fn.DataTable` and `jQuery.fn.dataTable` through which it may
++       * be  accessed.
++       *
++       *  @class
++       *  @param {object} [init={}] Configuration object for DataTables. Options
++       *    are defined by {@link DataTable.defaults}
++       *  @requires jQuery 1.7+
++       *
++       *  @example
++       *    // Basic initialisation
++       *    $(document).ready( function {
++       *      $('#example').dataTable();
++       *    } );
++       *
++       *  @example
++       *    // Initialisation with configuration options - in this case, disable
++       *    // pagination and sorting.
++       *    $(document).ready( function {
++       *      $('#example').dataTable( {
++       *        "paginate": false,
++       *        "sort": false
++       *      } );
++       *    } );
++       */
++      var DataTable = function ( options )
++      {
++              _buildInclude('api.legacy.js');
++
++              var _that = this;
++              var emptyInit = options === undefined;
++              var len = this.length;
++
++              if ( emptyInit ) {
++                      options = {};
++              }
++
++              this.oApi = this.internal = _ext.internal;
++
++              // Extend with old style plug-in API methods
++              for ( var fn in DataTable.ext.internal ) {
++                      if ( fn ) {
++                              this[fn] = _fnExternApiFunc(fn);
++                      }
++              }
++
++              this.each(function() {
++                      // For each initialisation we want to give it a clean initialisation
++                      // object that can be bashed around
++                      var o = {};
++                      var oInit = len > 1 ? // optimisation for single table case
++                              _fnExtend( o, options, true ) :
++                              options;
++
++                      _buildInclude('core.constructor.js');
++              } );
++              _that = null;
++              return this;
++      };
++
++      _buildInclude('core.internal.js');
++      _buildInclude('api.util.js');
++      _buildInclude('core.compat.js');
++      _buildInclude('core.columns.js');
++      _buildInclude('core.data.js');
++      _buildInclude('core.draw.js');
++      _buildInclude('core.ajax.js');
++      _buildInclude('core.filter.js');
++      _buildInclude('core.info.js');
++      _buildInclude('core.init.js');
++      _buildInclude('core.length.js');
++      _buildInclude('core.page.js');
++      _buildInclude('core.processing.js');
++      _buildInclude('core.scrolling.js');
++      _buildInclude('core.sizing.js');
++      _buildInclude('core.sort.js');
++      _buildInclude('core.state.js');
++      _buildInclude('core.support.js');
++
++      _buildInclude('api.base.js');
++      _buildInclude('api.table.js');
++      _buildInclude('api.draw.js');
++      _buildInclude('api.page.js');
++      _buildInclude('api.ajax.js');
++      _buildInclude('api.selectors.js');
++      _buildInclude('api.rows.js');
++      _buildInclude('api.row.details.js');
++      _buildInclude('api.columns.js');
++      _buildInclude('api.cells.js');
++      _buildInclude('api.order.js');
++      _buildInclude('api.search.js');
++      _buildInclude('api.state.js');
++      _buildInclude('api.static.js');
++      _buildInclude('api.core.js');
++
++      /**
++       * Version string for plug-ins to check compatibility. Allowed format is
++       * `a.b.c-d` where: a:int, b:int, c:int, d:string(dev|beta|alpha). `d` is used
++       * only for non-release builds. See http://semver.org/ for more information.
++       *  @member
++       *  @type string
++       *  @default Version number
++       */
++      DataTable.version = "1.10.16";
++
++      /**
++       * Private data store, containing all of the settings objects that are
++       * created for the tables on a given page.
++       *
++       * Note that the `DataTable.settings` object is aliased to
++       * `jQuery.fn.dataTableExt` through which it may be accessed and
++       * manipulated, or `jQuery.fn.dataTable.settings`.
++       *  @member
++       *  @type array
++       *  @default []
++       *  @private
++       */
++      DataTable.settings = [];
++
++      /**
++       * Object models container, for the various models that DataTables has
++       * available to it. These models define the objects that are used to hold
++       * the active state and configuration of the table.
++       *  @namespace
++       */
++      DataTable.models = {};
++      _buildInclude('model.search.js');
++      _buildInclude('model.row.js');
++      _buildInclude('model.column.js');
++      _buildInclude('model.defaults.js');
++      _buildInclude('model.defaults.columns.js');
++      _buildInclude('model.settings.js');
++
++      /**
++       * Extension object for DataTables that is used to provide all extension
++       * options.
++       *
++       * Note that the `DataTable.ext` object is available through
++       * `jQuery.fn.dataTable.ext` where it may be accessed and manipulated. It is
++       * also aliased to `jQuery.fn.dataTableExt` for historic reasons.
++       *  @namespace
++       *  @extends DataTable.models.ext
++       */
++      _buildInclude('ext.js');
++      _buildInclude('ext.classes.js');
++      _buildInclude('ext.paging.js');
++      _buildInclude('ext.types.js');
++      _buildInclude('ext.filter.js');
++      _buildInclude('ext.sorting.js');
++      _buildInclude('ext.renderer.js');
++      _buildInclude('ext.helpers.js');
++      _buildInclude('api.internal.js');
++
++      // jQuery access
++      $.fn.dataTable = DataTable;
++
++      // Provide access to the host jQuery object (circular reference)
++      DataTable.$ = $;
++
++      // Legacy aliases
++      $.fn.dataTableSettings = DataTable.settings;
++      $.fn.dataTableExt = DataTable.ext;
++
++      // With a capital `D` we return a DataTables API instance rather than a
++      // jQuery object
++      $.fn.DataTable = function ( opts ) {
++              return $(this).dataTable( opts ).api();
++      };
++
++      // All properties that are available to $.fn.dataTable should also be
++      // available on $.fn.DataTable
++      $.each( DataTable, function ( prop, val ) {
++              $.fn.DataTable[ prop ] = val;
++      } );
++
++
++      // Information about events fired by DataTables - for documentation.
++      /**
++       * Draw event, fired whenever the table is redrawn on the page, at the same
++       * point as fnDrawCallback. This may be useful for binding events or
++       * performing calculations when the table is altered at all.
++       *  @name DataTable#draw.dt
++       *  @event
++       *  @param {event} e jQuery event object
++       *  @param {object} o DataTables settings object {@link DataTable.models.oSettings}
++       */
++
++      /**
++       * Search event, fired when the searching applied to the table (using the
++       * built-in global search, or column filters) is altered.
++       *  @name DataTable#search.dt
++       *  @event
++       *  @param {event} e jQuery event object
++       *  @param {object} o DataTables settings object {@link DataTable.models.oSettings}
++       */
++
++      /**
++       * Page change event, fired when the paging of the table is altered.
++       *  @name DataTable#page.dt
++       *  @event
++       *  @param {event} e jQuery event object
++       *  @param {object} o DataTables settings object {@link DataTable.models.oSettings}
++       */
++
++      /**
++       * Order event, fired when the ordering applied to the table is altered.
++       *  @name DataTable#order.dt
++       *  @event
++       *  @param {event} e jQuery event object
++       *  @param {object} o DataTables settings object {@link DataTable.models.oSettings}
++       */
++
++      /**
++       * DataTables initialisation complete event, fired when the table is fully
++       * drawn, including Ajax data loaded, if Ajax data is required.
++       *  @name DataTable#init.dt
++       *  @event
++       *  @param {event} e jQuery event object
++       *  @param {object} oSettings DataTables settings object
++       *  @param {object} json The JSON object request from the server - only
++       *    present if client-side Ajax sourced data is used</li></ol>
++       */
++
++      /**
++       * State save event, fired when the table has changed state a new state save
++       * is required. This event allows modification of the state saving object
++       * prior to actually doing the save, including addition or other state
++       * properties (for plug-ins) or modification of a DataTables core property.
++       *  @name DataTable#stateSaveParams.dt
++       *  @event
++       *  @param {event} e jQuery event object
++       *  @param {object} oSettings DataTables settings object
++       *  @param {object} json The state information to be saved
++       */
++
++      /**
++       * State load event, fired when the table is loading state from the stored
++       * data, but prior to the settings object being modified by the saved state
++       * - allowing modification of the saved state is required or loading of
++       * state for a plug-in.
++       *  @name DataTable#stateLoadParams.dt
++       *  @event
++       *  @param {event} e jQuery event object
++       *  @param {object} oSettings DataTables settings object
++       *  @param {object} json The saved state information
++       */
++
++      /**
++       * State loaded event, fired when state has been loaded from stored data and
++       * the settings object has been modified by the loaded data.
++       *  @name DataTable#stateLoaded.dt
++       *  @event
++       *  @param {event} e jQuery event object
++       *  @param {object} oSettings DataTables settings object
++       *  @param {object} json The saved state information
++       */
++
++      /**
++       * Processing event, fired when DataTables is doing some kind of processing
++       * (be it, order, searcg or anything else). It can be used to indicate to
++       * the end user that there is something happening, or that something has
++       * finished.
++       *  @name DataTable#processing.dt
++       *  @event
++       *  @param {event} e jQuery event object
++       *  @param {object} oSettings DataTables settings object
++       *  @param {boolean} bShow Flag for if DataTables is doing processing or not
++       */
++
++      /**
++       * Ajax (XHR) event, fired whenever an Ajax request is completed from a
++       * request to made to the server for new data. This event is called before
++       * DataTables processed the returned data, so it can also be used to pre-
++       * process the data returned from the server, if needed.
++       *
++       * Note that this trigger is called in `fnServerData`, if you override
++       * `fnServerData` and which to use this event, you need to trigger it in you
++       * success function.
++       *  @name DataTable#xhr.dt
++       *  @event
++       *  @param {event} e jQuery event object
++       *  @param {object} o DataTables settings object {@link DataTable.models.oSettings}
++       *  @param {object} json JSON returned from the server
++       *
++       *  @example
++       *     // Use a custom property returned from the server in another DOM element
++       *     $('#table').dataTable().on('xhr.dt', function (e, settings, json) {
++       *       $('#status').html( json.status );
++       *     } );
++       *
++       *  @example
++       *     // Pre-process the data returned from the server
++       *     $('#table').dataTable().on('xhr.dt', function (e, settings, json) {
++       *       for ( var i=0, ien=json.aaData.length ; i<ien ; i++ ) {
++       *         json.aaData[i].sum = json.aaData[i].one + json.aaData[i].two;
++       *       }
++       *       // Note no return - manipulate the data directly in the JSON object.
++       *     } );
++       */
++
++      /**
++       * Destroy event, fired when the DataTable is destroyed by calling fnDestroy
++       * or passing the bDestroy:true parameter in the initialisation object. This
++       * can be used to remove bound events, added DOM nodes, etc.
++       *  @name DataTable#destroy.dt
++       *  @event
++       *  @param {event} e jQuery event object
++       *  @param {object} o DataTables settings object {@link DataTable.models.oSettings}
++       */
++
++      /**
++       * Page length change event, fired when number of records to show on each
++       * page (the length) is changed.
++       *  @name DataTable#length.dt
++       *  @event
++       *  @param {event} e jQuery event object
++       *  @param {object} o DataTables settings object {@link DataTable.models.oSettings}
++       *  @param {integer} len New length
++       */
++
++      /**
++       * Column sizing has changed.
++       *  @name DataTable#column-sizing.dt
++       *  @event
++       *  @param {event} e jQuery event object
++       *  @param {object} o DataTables settings object {@link DataTable.models.oSettings}
++       */
++
++      /**
++       * Column visibility has changed.
++       *  @name DataTable#column-visibility.dt
++       *  @event
++       *  @param {event} e jQuery event object
++       *  @param {object} o DataTables settings object {@link DataTable.models.oSettings}
++       *  @param {int} column Column index
++       *  @param {bool} vis `false` if column now hidden, or `true` if visible
++       */
++
++      return $.fn.dataTable;
++}));
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..74333c69973495aabccd543db276e7993132de9d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1023 @@@
++/*! Select for DataTables 1.0.1
++ * 2015 SpryMedia Ltd - datatables.net/license/mit
++ */
++
++/**
++ * @summary     Select for DataTables
++ * @description A collection of API methods, events and buttons for DataTables
++ *   that provides selection options of the items in a DataTable
++ * @version     1.0.1
++ * @file        dataTables.select.js
++ * @author      SpryMedia Ltd (www.sprymedia.co.uk)
++ * @contact     datatables.net/forums
++ * @copyright   Copyright 2015 SpryMedia Ltd.
++ *
++ * This source file is free software, available under the following license:
++ *   MIT license - http://datatables.net/license/mit
++ *
++ * This source file is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
++ *
++ * For details please refer to: http://www.datatables.net/extensions/select
++ */
++(function(window, document, undefined) {
++
++
++var factory = function( $, DataTable ) {
++"use strict";
++
++// Version information for debugger
++DataTable.select = {};
++DataTable.select.version = '1.0.1';
++
++/*
++
++Select is a collection of API methods, event handlers, event emitters and
++buttons (for the `Buttons` extension) for DataTables. It provides the following
++features, with an overview of how they are implemented:
++
++## Selection of rows, columns and cells. Whether an item is selected or not is
++   stored in:
++
++* rows: a `_select_selected` property which contains a boolean value of the
++  DataTables' `aoData` object for each row
++* columns: a `_select_selected` property which contains a boolean value of the
++  DataTables' `aoColumns` object for each column
++* cells: a `_selected_cells` property which contains an array of boolean values
++  of the `aoData` object for each row. The array is the same length as the
++  columns array, with each element of it representing a cell.
++
++This method of using boolean flags allows Select to operate when nodes have not
++been created for rows / cells (DataTables' defer rendering feature).
++
++## API methods
++
++A range of API methods are available for triggering selection and de-selection
++of rows. Methods are also available to configure the selection events that can
++be triggered by an end user (such as which items are to be selected). To a large
++extent, these of API methods *is* Select. It is basically a collection of helper
++functions that can be used to select items in a DataTable.
++
++Configuration of select is held in the object `_select` which is attached to the
++DataTables settings object on initialisation. Select being available on a table
++is not optional when Select is loaded, but its default is for selection only to
++be available via the API - so the end user wouldn't be able to select rows
++without additional configuration.
++
++The `_select` object contains the following properties:
++
++```
++{
++      items:string     - Can be `rows`, `columns` or `cells`. Defines what item 
++                         will be selected if the user is allowed to activate row
++                         selection using the mouse.
++      style:string     - Can be `none`, `single`, `multi` or `os`. Defines the
++                         interaction style when selecting items
++      blurable:boolean - If row selection can be cleared by clicking outside of
++                         the table
++      info:boolean     - If the selection summary should be shown in the table
++                         information elements
++}
++```
++
++In addition to the API methods, Select also extends the DataTables selector
++options for rows, columns and cells adding a `selected` option to the selector
++options object, allowing the developer to select only selected items or
++unselected items.
++
++## Mouse selection of items
++
++Clicking on items can be used to select items. This is done by a simple event
++handler that will select the items using the API methods.
++
++ */
++
++
++/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
++ * Local functions
++ */
++
++/**
++ * Add one or more cells to the selection when shift clicking in OS selection
++ * style cell selection.
++ *
++ * Cell range is more complicated than row and column as we want to select
++ * in the visible grid rather than by index in sequence. For example, if you
++ * click first in cell 1-1 and then shift click in 2-2 - cells 1-2 and 2-1
++ * should also be selected (and not 1-3, 1-4. etc)
++ * 
++ * @param  {DataTable.Api} dt   DataTable
++ * @param  {object}        idx  Cell index to select to
++ * @param  {object}        last Cell index to select from
++ * @private
++ */
++function cellRange( dt, idx, last )
++{
++      var indexes;
++      var columnIndexes;
++      var rowIndexes;
++      var selectColumns = function ( start, end ) {
++              if ( start > end ) {
++                      var tmp = end;
++                      end = start;
++                      start = tmp;
++              }
++              
++              var record = false;
++              return dt.columns( ':visible' ).indexes().filter( function (i) {
++                      if ( i === start ) {
++                              record = true;
++                      }
++                      
++                      if ( i === end ) { // not else if, as start might === end
++                              record = false;
++                              return true;
++                      }
++
++                      return record;
++              } );
++      };
++
++      var selectRows = function ( start, end ) {
++              var indexes = dt.rows( { search: 'applied' } ).indexes();
++
++              // Which comes first - might need to swap
++              if ( indexes.indexOf( start ) > indexes.indexOf( end ) ) {
++                      var tmp = end;
++                      end = start;
++                      start = tmp;
++              }
++
++              var record = false;
++              return indexes.filter( function (i) {
++                      if ( i === start ) {
++                              record = true;
++                      }
++                      
++                      if ( i === end ) {
++                              record = false;
++                              return true;
++                      }
++
++                      return record;
++              } );
++      };
++
++      if ( ! dt.cells( { selected: true } ).any() && ! last ) {
++              // select from the top left cell to this one
++              columnIndexes = selectColumns( 0, idx.column );
++              rowIndexes = selectRows( 0 , idx.row );
++      }
++      else {
++              // Get column indexes between old and new
++              columnIndexes = selectColumns( last.column, idx.column );
++              rowIndexes = selectRows( last.row , idx.row );
++      }
++
++      indexes = dt.cells( rowIndexes, columnIndexes ).flatten();
++
++      if ( ! dt.cells( idx, { selected: true } ).any() ) {
++              // Select range
++              dt.cells( indexes ).select();
++      }
++      else {
++              // Deselect range
++              dt.cells( indexes ).deselect();
++      }
++}
++
++/**
++ * Disable mouse selection by removing the selectors
++ *
++ * @param {DataTable.Api} dt DataTable to remove events from
++ * @private
++ */
++function disableMouseSelection( dt )
++{
++      var ctx = dt.settings()[0];
++      var selector = ctx._select.selector;
++
++      $( dt.table().body() )
++              .off( 'mousedown.dtSelect', selector )
++              .off( 'mouseup.dtSelect', selector )
++              .off( 'click.dtSelect', selector );
++
++      $('body').off( 'click.dtSelect' );
++}
++
++/**
++ * Attach mouse listeners to the table to allow mouse selection of items
++ *
++ * @param {DataTable.Api} dt DataTable to remove events from
++ * @private
++ */
++function enableMouseSelection ( dt )
++{
++      var body = $( dt.table().body() );
++      var ctx = dt.settings()[0];
++      var selector = ctx._select.selector;
++
++      body
++              .on( 'mousedown.dtSelect', selector, function(e) {
++                      // Disallow text selection for shift clicking on the table so multi
++                      // element selection doesn't look terrible!
++                      if ( e.shiftKey ) {
++                              body
++                                      .css( '-moz-user-select', 'none' )
++                                      .one('selectstart.dtSelect', selector, function () {
++                                              return false;
++                                      } );
++                      }
++              } )
++              .on( 'mouseup.dtSelect', selector, function(e) {
++                      // Allow text selection to occur again, Mozilla style (tested in FF
++                      // 35.0.1 - still required)
++                      body.css( '-moz-user-select', '' );
++              } )
++              .on( 'click.dtSelect', selector, function ( e ) {
++                      var items = dt.select.items();
++                      var cellIndex = dt.cell( this ).index();
++                      var idx;
++
++                      var ctx = dt.settings()[0];
++
++                      // Ignore clicks inside a sub-table
++                      if ( $(e.target).closest('tbody')[0] != body[0] ) {
++                              return;
++                      }
++
++                      // Check the cell actually belongs to the host DataTable (so child rows,
++                      // etc, are ignored)
++                      if ( ! dt.cell( e.target ).any() ) {
++                              return;
++                      }
++
++                      if ( items === 'row' ) {
++                              idx = cellIndex.row;
++                              typeSelect( e, dt, ctx, 'row', idx );
++                      }
++                      else if ( items === 'column' ) {
++                              idx = dt.cell( e.target ).index().column;
++                              typeSelect( e, dt, ctx, 'column', idx );
++                      }
++                      else if ( items === 'cell' ) {
++                              idx = dt.cell( e.target ).index();
++                              typeSelect( e, dt, ctx, 'cell', idx );
++                      }
++
++                      ctx._select_lastCell = cellIndex;
++              } );
++
++      // Blurable
++      $('body').on( 'click.dtSelect', function ( e ) {
++              if ( ctx._select.blurable ) {
++                      // If the click was inside the DataTables container, don't blur
++                      if ( $(e.target).parents().filter( dt.table().container() ).length ) {
++                              return;
++                      }
++
++                      // Don't blur in Editor form
++                      if ( $(e.target).parents('div.DTE').length ) {
++                              return;
++                      }
++
++                      clear( ctx, true );
++              }
++      } );
++}
++
++/**
++ * Trigger an event on a DataTable
++ *
++ * @param {DataTable.Api} api      DataTable to trigger events on
++ * @param  {boolean}      selected true if selected, false if deselected
++ * @param  {string}       type     Item type acting on
++ * @param  {boolean}      any      Require that there are values before
++ *     triggering
++ * @private
++ */
++function eventTrigger ( api, type, args, any )
++{
++      if ( any && ! api.flatten().length ) {
++              return;
++      }
++
++      args.unshift( api );
++
++      $(api.table().node()).triggerHandler( type+'.dt', args );
++}
++
++/**
++ * Update the information element of the DataTable showing information about the
++ * items selected. This is done by adding tags to the existing text
++ * 
++ * @param {DataTable.Api} api DataTable to update
++ * @private
++ */
++function info ( api )
++{
++      var ctx = api.settings()[0];
++
++      if ( ! ctx._select.info || ! ctx.aanFeatures.i ) {
++              return;
++      }
++
++      var output  = $('<span class="select-info"/>');
++      var add = function ( name, num ) {
++              output.append( $('<span class="select-item"/>').append( api.i18n(
++                      'select.'+name+'s',
++                      { _: '%d '+name+'s selected', 0: '', 1: '1 '+name+' selected' },
++                      num
++              ) ) );
++      };
++
++      add( 'row',    api.rows( { selected: true } ).flatten().length );
++      add( 'column', api.columns( { selected: true } ).flatten().length );
++      add( 'cell',   api.cells( { selected: true } ).flatten().length );
++
++      // Internal knowledge of DataTables to loop over all information elements
++      $.each( ctx.aanFeatures.i, function ( i, el ) {
++              el = $(el);
++
++              var exisiting = el.children('span.select-info');
++              if ( exisiting.length ) {
++                      exisiting.remove();
++              }
++
++              if ( output.text() !== '' ) {
++                      el.append( output );
++              }
++      } );
++}
++
++/**
++ * Initialisation of a new table. Attach event handlers and callbacks to allow
++ * Select to operate correctly.
++ *
++ * This will occur _after_ the initial DataTables initialisation, although
++ * before Ajax data is rendered, if there is ajax data
++ *
++ * @param  {DataTable.settings} ctx Settings object to operate on
++ * @private
++ */
++function init ( ctx ) {
++      var api = new DataTable.Api( ctx );
++
++      // Row callback so that classes can be added to rows and cells if the item
++      // was selected before the element was created. This will happen with the
++      // `deferRender` option enabled.
++      // 
++      // This method of attaching to `aoRowCreatedCallback` is a hack until
++      // DataTables has proper events for row manipulation If you are reviewing
++      // this code to create your own plug-ins, please do not do this!
++      ctx.aoRowCreatedCallback.push( {
++              fn: function ( row, data, index ) {
++                      var i, ien;
++                      var d = ctx.aoData[ index ];
++
++                      // Row
++                      if ( d._select_selected ) {
++                              $( row ).addClass( 'selected' );
++                      }
++
++                      // Cells and columns - if separated out, we would need to do two
++                      // loops, so it makes sense to combine them into a single one
++                      for ( i=0, ien=ctx.aoColumns.length ; i<ien ; i++ ) {
++                              if ( ctx.aoColumns[i]._select_selected || (d._selected_cells && d._selected_cells[i]) ) {
++                                      $(d.anCells[i]).addClass( 'selected' );
++                              }
++                      }
++              },
++              sName: 'select-deferRender'
++      } );
++
++      // On Ajax reload we want to reselect all rows which are currently selected,
++      // if there is an rowId (i.e. a unique value to identify each row with)
++      api.on( 'preXhr.dt.dtSelect', function () {
++              // note that column selection doesn't need to be cached and then
++              // reselected, as they are already selected
++              var rows = api.rows( { selected: true } ).ids( true ).filter( function ( d ) {
++                      return d !== undefined;
++              } );
++
++              var cells = api.cells( { selected: true } ).eq(0).map( function ( cellIdx ) {
++                      var id = api.row( cellIdx.row ).id( true );
++                      return id ?
++                              { row: id, column: cellIdx.column } :
++                              undefined;
++              } ).filter( function ( d ) {
++                      return d !== undefined;
++              } );
++
++              // On the next draw, reselect the currently selected items
++              api.one( 'draw.dt.dtSelect', function () {
++                      api.rows( rows ).select();
++
++                      // `cells` is not a cell index selector, so it needs a loop
++                      if ( cells.any() ) {
++                              cells.each( function ( id ) {
++                                      api.cells( id.row, id.column ).select();
++                              } );
++                      }
++              } );
++      } );
++
++      // Update the table information element with selected item summary
++      api.on( 'draw.dtSelect.dt select.dtSelect.dt deselect.dtSelect.dt', function () {
++              info( api );
++      } );
++
++      // Clean up and release
++      api.on( 'destroy.dtSelect', function () {
++              disableMouseSelection( api );
++              api.off( '.dtSelect' );
++      } );
++}
++
++/**
++ * Add one or more items (rows or columns) to the selection when shift clicking
++ * in OS selection style
++ *
++ * @param  {DataTable.Api} dt   DataTable
++ * @param  {string}        type Row or column range selector
++ * @param  {object}        idx  Item index to select to
++ * @param  {object}        last Item index to select from
++ * @private
++ */
++function rowColumnRange( dt, type, idx, last )
++{
++      // Add a range of rows from the last selected row to this one
++      var indexes = dt[type+'s']( { search: 'applied' } ).indexes();
++      var idx1 = $.inArray( last, indexes );
++      var idx2 = $.inArray( idx, indexes );
++
++      if ( ! dt[type+'s']( { selected: true } ).any() && idx1 === -1 ) {
++              // select from top to here - slightly odd, but both Windows and Mac OS
++              // do this
++              indexes.splice( $.inArray( idx, indexes )+1, indexes.length );
++      }
++      else {
++              // reverse so we can shift click 'up' as well as down
++              if ( idx1 > idx2 ) {
++                      var tmp = idx2;
++                      idx2 = idx1;
++                      idx1 = tmp;
++              }
++
++              indexes.splice( idx2+1, indexes.length );
++              indexes.splice( 0, idx1 );
++      }
++
++      if ( ! dt[type]( idx, { selected: true } ).any() ) {
++              // Select range
++              dt[type+'s']( indexes ).select();
++      }
++      else {
++              // Deselect range - need to keep the clicked on row selected
++              indexes.splice( $.inArray( idx, indexes ), 1 );
++              dt[type+'s']( indexes ).deselect();
++      }
++}
++
++/**
++ * Clear all selected items
++ *
++ * @param  {DataTable.settings} ctx Settings object of the host DataTable
++ * @param  {boolean} [force=false] Force the de-selection to happen, regardless
++ *     of selection style
++ * @private
++ */
++function clear( ctx, force )
++{
++      if ( force || ctx._select.style === 'single' ) {
++              var api = new DataTable.Api( ctx );
++              
++              api.rows( { selected: true } ).deselect();
++              api.columns( { selected: true } ).deselect();
++              api.cells( { selected: true } ).deselect();
++      }
++}
++
++/**
++ * Select items based on the current configuration for style and items.
++ *
++ * @param  {object}             e    Mouse event object
++ * @param  {DataTables.Api}     dt   DataTable
++ * @param  {DataTable.settings} ctx  Settings object of the host DataTable
++ * @param  {string}             type Items to select
++ * @param  {int|object}         idx  Index of the item to select
++ * @private
++ */
++function typeSelect ( e, dt, ctx, type, idx )
++{
++      var style = dt.select.style();
++      var isSelected = dt[type]( idx, { selected: true } ).any();
++
++      if ( style === 'os' ) {
++              if ( e.ctrlKey || e.metaKey ) {
++                      // Add or remove from the selection
++                      dt[type]( idx ).select( ! isSelected );
++              }
++              else if ( e.shiftKey ) {
++                      if ( type === 'cell' ) {
++                              cellRange( dt, idx, ctx._select_lastCell || null );
++                      }
++                      else {
++                              rowColumnRange( dt, type, idx, ctx._select_lastCell ?
++                                      ctx._select_lastCell[type] :
++                                      null
++                              );
++                      }
++              }
++              else {
++                      // No cmd or shift click - deselect if selected, or select
++                      // this row only
++                      var selected = dt[type+'s']( { selected: true } );
++
++                      if ( isSelected && selected.flatten().length === 1 ) {
++                              dt[type]( idx ).deselect();
++                      }
++                      else {
++                              selected.deselect();
++                              dt[type]( idx ).select();
++                      }
++              }
++      }
++      else {
++              dt[ type ]( idx ).select( ! isSelected );
++      }
++}
++
++
++
++/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
++ * DataTables selectors
++ */
++
++// row and column are basically identical just assigned to different properties
++// and checking a different array, so we can dynamically create the functions to
++// reduce the code size
++$.each( [
++      { type: 'row', prop: 'aoData' },
++      { type: 'column', prop: 'aoColumns' }
++], function ( i, o ) {
++      DataTable.ext.selector[ o.type ].push( function ( settings, opts, indexes ) {
++              var selected = opts.selected;
++              var data;
++              var out = [];
++
++              if ( selected === undefined ) {
++                      return indexes;
++              }
++
++              for ( var i=0, ien=indexes.length ; i<ien ; i++ ) {
++                      data = settings[ o.prop ][ indexes[i] ];
++
++                      if ( (selected === true && data._select_selected === true) ||
++                               (selected === false && ! data._select_selected )
++                      ) {
++                              out.push( indexes[i] );
++                      }
++              }
++
++              return out;
++      } );
++} );
++
++DataTable.ext.selector.cell.push( function ( settings, opts, cells ) {
++      var selected = opts.selected;
++      var rowData;
++      var out = [];
++
++      if ( selected === undefined ) {
++              return cells;
++      }
++
++      for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
++              rowData = settings.aoData[ cells[i].row ];
++
++              if ( (selected === true && rowData._selected_cells && rowData._selected_cells[ cells[i].column ] === true) ||
++                       (selected === false && ( ! rowData._selected_cells || ! rowData._selected_cells[ cells[i].column ] ) )
++              ) {
++                      out.push( cells[i] );
++              }
++      }
++
++      return out;
++} );
++
++
++
++/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
++ * DataTables API
++ *
++ * For complete documentation, please refer to the docs/api directory or the
++ * DataTables site
++ */
++
++// Local variables to improve compression
++var apiRegister = DataTable.Api.register;
++var apiRegisterPlural = DataTable.Api.registerPlural;
++
++apiRegister( 'select()', function () {} );
++
++apiRegister( 'select.blurable()', function ( flag ) {
++      if ( flag === undefined ) {
++              return this.context[0]._select.blurable;
++      }
++
++      return this.iterator( 'table', function ( ctx ) {
++              ctx._select.blurable = flag;
++      } );
++} );
++
++apiRegister( 'select.info()', function ( flag ) {
++      if ( info === undefined ) {
++              return this.context[0]._select.info;
++      }
++
++      return this.iterator( 'table', function ( ctx ) {
++              ctx._select.info = flag;
++      } );
++} );
++
++apiRegister( 'select.items()', function ( items ) {
++      if ( items === undefined ) {
++              return this.context[0]._select.items;
++      }
++
++      return this.iterator( 'table', function ( ctx ) {
++              ctx._select.items = items;
++
++              eventTrigger( new DataTable.Api( ctx ), 'selectItems', [ items ] );
++      } );
++} );
++
++// Takes effect from the _next_ selection. None disables future selection, but
++// does not clear the current selection. Use the `deselect` methods for that
++apiRegister( 'select.style()', function ( style ) {
++      if ( style === undefined ) {
++              return this.context[0]._select.style;
++      }
++
++      return this.iterator( 'table', function ( ctx ) {
++              ctx._select.style = style;
++
++              if ( ! ctx._select_init ) {
++                      init( ctx );
++              }
++
++              // Add / remove mouse event handlers. They aren't required when only
++              // API selection is available
++              var dt = new DataTable.Api( ctx );
++              disableMouseSelection( dt );
++              
++              if ( style !== 'api' ) {
++                      enableMouseSelection( dt );
++              }
++
++              eventTrigger( new DataTable.Api( ctx ), 'selectStyle', [ style ] );
++      } );
++} );
++
++apiRegister( 'select.selector()', function ( selector ) {
++      if ( selector === undefined ) {
++              return this.context[0]._select.selector;
++      }
++
++      return this.iterator( 'table', function ( ctx ) {
++              disableMouseSelection( new DataTable.Api( ctx ) );
++
++              ctx._select.selector = selector;
++
++              if ( ctx._select.style !== 'api' ) {
++                      enableMouseSelection( new DataTable.Api( ctx ) );
++              }
++      } );
++} );
++
++
++
++apiRegisterPlural( 'rows().select()', 'row().select()', function ( select ) {
++      var api = this;
++
++      if ( select === false ) {
++              return this.deselect();
++      }
++
++      this.iterator( 'row', function ( ctx, idx ) {
++              clear( ctx );
++
++              ctx.aoData[ idx ]._select_selected = true;
++              $( ctx.aoData[ idx ].nTr ).addClass( 'selected' );
++      } );
++
++      this.iterator( 'table', function ( ctx, i ) {
++              eventTrigger( api, 'select', [ 'row', api[i] ], true );
++      } );
++
++      return this;
++} );
++
++apiRegisterPlural( 'columns().select()', 'column().select()', function ( select ) {
++      var api = this;
++
++      if ( select === false ) {
++              return this.deselect();
++      }
++
++      this.iterator( 'column', function ( ctx, idx ) {
++              clear( ctx );
++
++              ctx.aoColumns[ idx ]._select_selected = true;
++
++              var column = new DataTable.Api( ctx ).column( idx );
++
++              $( column.header() ).addClass( 'selected' );
++              $( column.footer() ).addClass( 'selected' );
++
++              column.nodes().to$().addClass( 'selected' );
++      } );
++
++      this.iterator( 'table', function ( ctx, i ) {
++              eventTrigger( api, 'select', [ 'column', api[i] ], true );
++      } );
++
++      return this;
++} );
++
++apiRegisterPlural( 'cells().select()', 'cell().select()', function ( select ) {
++      var api = this;
++
++      if ( select === false ) {
++              return this.deselect();
++      }
++
++      this.iterator( 'cell', function ( ctx, rowIdx, colIdx ) {
++              clear( ctx );
++
++              var data = ctx.aoData[ rowIdx ];
++
++              if ( data._selected_cells === undefined ) {
++                      data._selected_cells = [];
++              }
++
++              data._selected_cells[ colIdx ] = true;
++
++              if ( data.anCells ) {
++                      $( data.anCells[ colIdx ] ).addClass( 'selected' );
++              }
++      } );
++
++      this.iterator( 'table', function ( ctx, i ) {
++              eventTrigger( api, 'select', [ 'cell', api[i] ], true );
++      } );
++
++      return this;
++} );
++
++
++apiRegisterPlural( 'rows().deselect()', 'row().deselect()', function () {
++      var api = this;
++
++      this.iterator( 'row', function ( ctx, idx ) {
++              ctx.aoData[ idx ]._select_selected = false;
++              $( ctx.aoData[ idx ].nTr ).removeClass( 'selected' );
++      } );
++
++      this.iterator( 'table', function ( ctx, i ) {
++              eventTrigger( api, 'deselect', [ 'row', api[i] ], true );
++      } );
++
++      return this;
++} );
++
++apiRegisterPlural( 'columns().deselect()', 'column().deselect()', function () {
++      var api = this;
++
++      this.iterator( 'column', function ( ctx, idx ) {
++              ctx.aoColumns[ idx ]._select_selected = false;
++
++              var api = new DataTable.Api( ctx );
++              var column = api.column( idx );
++
++              $( column.header() ).removeClass( 'selected' );
++              $( column.footer() ).removeClass( 'selected' );
++
++              // Need to loop over each cell, rather than just using
++              // `column().nodes()` as cells which are individually selected should
++              // not have the `selected` class removed from them
++              api.cells( null, idx ).indexes().each( function (cellIdx) {
++                      var data = ctx.aoData[ cellIdx.row ];
++                      var cellSelected = data._selected_cells;
++
++                      if ( data.anCells && (! cellSelected || ! cellSelected[ cellIdx.column ]) ) {
++                              $( data.anCells[ cellIdx.column  ] ).removeClass( 'selected' );
++                      }
++              } );
++      } );
++
++      this.iterator( 'table', function ( ctx, i ) {
++              eventTrigger( api, 'deselect', [ 'column', api[i] ], true );
++      } );
++
++      return this;
++} );
++
++apiRegisterPlural( 'cells().deselect()', 'cell().deselect()', function () {
++      var api = this;
++
++      this.iterator( 'cell', function ( ctx, rowIdx, colIdx ) {
++              var data = ctx.aoData[ rowIdx ];
++
++              data._selected_cells[ colIdx ] = false;
++
++              // Remove class only if the cells exist, and the cell is not column
++              // selected, in which case the class should remain (since it is selected
++              // in the column)
++              if ( data.anCells && ! ctx.aoColumns[ colIdx ]._select_selected ) {
++                      $( data.anCells[ colIdx ] ).removeClass( 'selected' );
++              }
++      } );
++
++      this.iterator( 'table', function ( ctx, i ) {
++              eventTrigger( api, 'deselect', [ 'cell', api[i] ], true );
++      } );
++
++      return this;
++} );
++
++
++
++/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
++ * Buttons
++ */
++function i18n( label, def ) {
++      return function (dt) {
++              return dt.i18n( 'buttons.'+label, def );
++      };
++}
++
++$.extend( DataTable.ext.buttons, {
++      selected: {
++              text: i18n( 'selected', 'Selected' ),
++              className: 'buttons-selected',
++              init: function ( dt, button, config ) {
++                      var that = this;
++
++                      // .DT namespace listeners are removed by DataTables automatically
++                      // on table destroy
++                      dt.on( 'draw.dt.DT select.dt.DT deselect.dt.DT', function () {
++                              var enable = that.rows( { selected: true } ).any() ||
++                                           that.columns( { selected: true } ).any() ||
++                                           that.cells( { selected: true } ).any();
++
++                              that.enable( enable );
++                      } );
++
++                      this.disable();
++              }
++      },
++      selectedSingle: {
++              text: i18n( 'selectedSingle', 'Selected single' ),
++              className: 'buttons-selected-single',
++              init: function ( dt, button, config ) {
++                      var that = this;
++
++                      dt.on( 'draw.dt.DT select.dt.DT deselect.dt.DT', function () {
++                              var count = dt.rows( { selected: true } ).flatten().length +
++                                          dt.columns( { selected: true } ).flatten().length +
++                                          dt.cells( { selected: true } ).flatten().length;
++
++                              that.enable( count === 1 );
++                      } );
++
++                      this.disable();
++              }
++      },
++      selectAll: {
++              text: i18n( 'selectAll', 'Select all' ),
++              className: 'buttons-select-all',
++              action: function () {
++                      var items = this.select.items();
++                      this[ items+'s' ]().select();
++              }
++      },
++      selectNone: {
++              text: i18n( 'selectNone', 'Deselect all' ),
++              className: 'buttons-select-none',
++              action: function () {
++                      clear( this.settings()[0], true );
++              }
++      }
++} );
++
++$.each( [ 'Row', 'Column', 'Cell' ], function ( i, item ) {
++      var lc = item.toLowerCase();
++
++      DataTable.ext.buttons[ 'select'+item+'s' ] = {
++              text: i18n( 'select'+item+'s', 'Select '+lc+'s' ),
++              className: 'buttons-select-'+lc+'s',
++              action: function () {
++                      this.select.items( lc );
++              },
++              init: function ( dt, button, config ) {
++                      var that = this;
++
++                      dt.on( 'selectItems.dt.DT', function ( e, ctx, items ) {
++                              that.active( items === lc );
++                      } );
++              }
++      };
++} );
++
++
++
++/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
++ * Initialisation
++ */
++
++// DataTables creation - check if the buttons have been defined for this table,
++// they will have been if the `B` option was used in `dom`, otherwise we should
++// create the buttons instance here so they can be inserted into the document
++// using the API
++$(document).on( 'init.dt.dtSelect', function (e, ctx, json) {
++      if ( e.namespace !== 'dt' ) {
++              return;
++      }
++
++      var opts = ctx.oInit.select || DataTable.defaults.select;
++      var dt = new DataTable.Api( ctx );
++
++      // Set defaults
++      var items = 'row';
++      var style = 'api';
++      var blurable = false;
++      var info = true;
++      var selector = 'td, th';
++
++      ctx._select = {};
++
++      // Initialisation customisations
++      if ( opts === true ) {
++              style = 'os';
++      }
++      else if ( typeof opts === 'string' ) {
++              style = opts;
++      }
++      else if ( $.isPlainObject( opts ) ) {
++              if ( opts.blurable !== undefined ) {
++                      blurable = opts.blurable;
++              }
++
++              if ( opts.info !== undefined ) {
++                      info = opts.info;
++              }
++
++              if ( opts.items !== undefined ) {
++                      items = opts.items;
++              }
++
++              if ( opts.style !== undefined ) {
++                      style = opts.style;
++              }
++
++              if ( opts.selector !== undefined ) {
++                      selector = opts.selector;
++              }
++      }
++
++      dt.select.selector( selector );
++      dt.select.items( items );
++      dt.select.style( style );
++      dt.select.blurable( blurable );
++      dt.select.info( info );
++
++      // If the init options haven't enabled select, but there is a selectable
++      // class name, then enable
++      if ( $( dt.table().node() ).hasClass( 'selectable' ) ) {
++              dt.select.style( 'os' );
++      }
++} );
++
++
++}; // /factory
++
++
++// Define as an AMD module if possible
++if ( typeof define === 'function' && define.amd ) {
++      define( ['jquery', 'datatables'], factory );
++}
++else if ( typeof exports === 'object' ) {
++    // Node/CommonJS
++    factory( require('jquery'), require('datatables') );
++}
++else if ( jQuery && !jQuery.fn.dataTable.select ) {
++      // Otherwise simply initialise as normal, stopping multiple evaluation
++      factory( jQuery, jQuery.fn.dataTable );
++}
++
++
++})(window, document);
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f83f239ebb3fc2ef36697ea85d58f70814cc14fd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,147 @@@
++/*
++ * jQuery Dropdown: A simple dropdown plugin
++ *
++ * Contribute: https://github.com/claviska/jquery-dropdown
++ *
++ * @license: MIT license: http://opensource.org/licenses/MIT
++ *
++ */
++if (jQuery) (function ($) {
++
++    $.extend($.fn, {
++        jqDropdown: function (method, data) {
++
++            switch (method) {
++                case 'show':
++                    show(null, $(this));
++                    return $(this);
++                case 'hide':
++                    hide();
++                    return $(this);
++                case 'attach':
++                    return $(this).attr('data-jq-dropdown', data);
++                case 'detach':
++                    hide();
++                    return $(this).removeAttr('data-jq-dropdown');
++                case 'disable':
++                    return $(this).addClass('jq-dropdown-disabled');
++                case 'enable':
++                    hide();
++                    return $(this).removeClass('jq-dropdown-disabled');
++            }
++
++        }
++    });
++
++    function show(event, object) {
++
++        var trigger = event ? $(this) : object,
++            jqDropdown = $(trigger.attr('data-jq-dropdown')),
++            isOpen = trigger.hasClass('jq-dropdown-open');
++
++        // In some cases we don't want to show it
++        if (event) {
++            if ($(event.target).hasClass('jq-dropdown-ignore')) return;
++
++            event.preventDefault();
++            event.stopPropagation();
++        } else {
++            if (trigger !== object.target && $(object.target).hasClass('jq-dropdown-ignore')) return;
++        }
++        hide();
++
++        if (isOpen || trigger.hasClass('jq-dropdown-disabled')) return;
++
++        // Show it
++        trigger.addClass('jq-dropdown-open');
++        jqDropdown
++            .data('jq-dropdown-trigger', trigger)
++            .show();
++
++        // Position it
++        position();
++
++        // Trigger the show callback
++        jqDropdown
++            .trigger('show', {
++                jqDropdown: jqDropdown,
++                trigger: trigger
++            });
++
++    }
++
++    function hide(event) {
++
++        // In some cases we don't hide them
++        var targetGroup = event ? $(event.target).parents().addBack() : null;
++
++        // Are we clicking anywhere in a jq-dropdown?
++        if (targetGroup && targetGroup.is('.jq-dropdown')) {
++            // Is it a jq-dropdown menu?
++            if (targetGroup.is('.jq-dropdown-menu')) {
++                // Did we click on an option? If so close it.
++                if (!targetGroup.is('A')) return;
++            } else {
++                // Nope, it's a panel. Leave it open.
++                return;
++            }
++        }
++
++        // Trigger the event early, so that it might be prevented on the visible popups
++        var hideEvent = jQuery.Event("hide");
++
++        $(document).find('.jq-dropdown:visible').each(function () {
++            var jqDropdown = $(this);
++            jqDropdown
++                .hide()
++                .removeData('jq-dropdown-trigger')
++                .trigger('hide', { jqDropdown: jqDropdown });
++        });
++
++        if(!hideEvent.isDefaultPrevented()) {
++            // Hide any jq-dropdown that may be showing
++            $(document).find('.jq-dropdown:visible').each(function () {
++                var jqDropdown = $(this);
++                jqDropdown
++                    .hide()
++                    .removeData('jq-dropdown-trigger')
++                    .trigger('hide', { jqDropdown: jqDropdown });
++            });
++
++            // Remove all jq-dropdown-open classes
++            $(document).find('.jq-dropdown-open').removeClass('jq-dropdown-open');
++        }
++    }
++
++    function position() {
++
++        var jqDropdown = $('.jq-dropdown:visible').eq(0),
++            trigger = jqDropdown.data('jq-dropdown-trigger'),
++            hOffset = trigger ? parseInt(trigger.attr('data-horizontal-offset') || 0, 10) : null,
++            vOffset = trigger ? parseInt(trigger.attr('data-vertical-offset') || 0, 10) : null;
++
++        if (jqDropdown.length === 0 || !trigger) return;
++
++        // Position the jq-dropdown relative-to-parent...
++        if (jqDropdown.hasClass('jq-dropdown-relative')) {
++            jqDropdown.css({
++                left: jqDropdown.hasClass('jq-dropdown-anchor-right') ?
++                    trigger.position().left - (jqDropdown.outerWidth(true) - trigger.outerWidth(true)) - parseInt(trigger.css('margin-right'), 10) + hOffset :
++                    trigger.position().left + parseInt(trigger.css('margin-left'), 10) + hOffset,
++                top: trigger.position().top + trigger.outerHeight(true) - parseInt(trigger.css('margin-top'), 10) + vOffset
++            });
++        } else {
++            // ...or relative to document
++            jqDropdown.css({
++                left: jqDropdown.hasClass('jq-dropdown-anchor-right') ?
++                    trigger.offset().left - (jqDropdown.outerWidth() - trigger.outerWidth()) + hOffset : trigger.offset().left + hOffset,
++                top: trigger.offset().top + trigger.outerHeight() + vOffset
++            });
++        }
++    }
++
++    $(document).on('click.jq-dropdown', '[data-jq-dropdown]', show);
++    $(document).on('click.jq-dropdown', hide);
++    $(window).on('resize', position);
++
++})(jQuery);
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b7f3edfd9f607092d516cffc980ee93d4ab28b0c
new file mode 120000 (symlink)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++jquery-3.3.1.js
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0d9206ee97325a67e907f26097277b3f2cd38751
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1383 @@@
++/*!
++ * jquery-timepicker v1.11.13 - A jQuery timepicker plugin inspired by Google Calendar. It supports both mouse and keyboard navigation.
++ * Copyright (c) 2015 Jon Thornton - http://jonthornton.github.com/jquery-timepicker/
++ * License: MIT
++ */
++
++(function(factory) {
++  if (
++    typeof exports === "object" &&
++    exports &&
++    typeof module === "object" &&
++    module &&
++    module.exports === exports
++  ) {
++    // Browserify. Attach to jQuery module.
++    factory(require("jquery"));
++  } else if (typeof define === "function" && define.amd) {
++    // AMD. Register as an anonymous module.
++    define(["jquery"], factory);
++  } else {
++    // Browser globals
++    factory(jQuery);
++  }
++})(function($) {
++  var _ONE_DAY = 86400;
++  var _lang = {
++    am: "am",
++    pm: "pm",
++    AM: "AM",
++    PM: "PM",
++    decimal: ".",
++    mins: "mins",
++    hr: "hr",
++    hrs: "hrs"
++  };
++
++  var _DEFAULTS = {
++    appendTo: "body",
++    className: null,
++    closeOnWindowScroll: false,
++    disableTextInput: false,
++    disableTimeRanges: [],
++    disableTouchKeyboard: false,
++    durationTime: null,
++    forceRoundTime: false,
++    maxTime: null,
++    minTime: null,
++    noneOption: false,
++    orientation: "l",
++    roundingFunction: function(seconds, settings) {
++      if (seconds === null) {
++        return null;
++      } else if (typeof settings.step !== "number") {
++        // TODO: nearest fit irregular steps
++        return seconds;
++      } else {
++        var offset = seconds % (settings.step * 60); // step is in minutes
++
++        var start = settings.minTime || 0;
++
++        // adjust offset by start mod step so that the offset is aligned not to 00:00 but to the start
++        offset -= start % (settings.step * 60);
++
++        if (offset >= settings.step * 30) {
++          // if offset is larger than a half step, round up
++          seconds += settings.step * 60 - offset;
++        } else {
++          // round down
++          seconds -= offset;
++        }
++
++        return _moduloSeconds(seconds, settings);
++      }
++    },
++    scrollDefault: null,
++    selectOnBlur: false,
++    show2400: false,
++    showDuration: false,
++    showOn: ["click", "focus"],
++    showOnFocus: true,
++    step: 30,
++    stopScrollPropagation: false,
++    timeFormat: "g:ia",
++    typeaheadHighlight: true,
++    useSelect: false,
++    wrapHours: true
++  };
++
++  var methods = {
++    init: function(options) {
++      return this.each(function() {
++        var self = $(this);
++
++        // pick up settings from data attributes
++        var attributeOptions = [];
++        for (var key in _DEFAULTS) {
++          if (self.data(key)) {
++            attributeOptions[key] = self.data(key);
++          }
++        }
++
++        var settings = $.extend({}, _DEFAULTS, options, attributeOptions);
++
++        if (settings.lang) {
++          _lang = $.extend(_lang, settings.lang);
++        }
++
++        settings = _parseSettings(settings);
++        self.data("timepicker-settings", settings);
++        self.addClass("ui-timepicker-input");
++
++        if (settings.useSelect) {
++          _render(self);
++        } else {
++          self.prop("autocomplete", "off");
++          if (settings.showOn) {
++            for (var i in settings.showOn) {
++              self.on(settings.showOn[i] + ".timepicker", methods.show);
++            }
++          }
++          self.on("change.timepicker", _formatValue);
++          self.on("keydown.timepicker", _keydownhandler);
++          self.on("keyup.timepicker", _keyuphandler);
++          if (settings.disableTextInput) {
++            self.on("keydown.timepicker", _disableTextInputHandler);
++          }
++          self.on("cut.timepicker", _keyuphandler);
++          self.on("paste.timepicker", _keyuphandler);
++
++          _formatValue.call(self.get(0), null, "initial");
++        }
++      });
++    },
++
++    show: function(e) {
++      var self = $(this);
++      var settings = self.data("timepicker-settings");
++
++      if (e) {
++        e.preventDefault();
++      }
++
++      if (settings.useSelect) {
++        self.data("timepicker-list").focus();
++        return;
++      }
++
++      if (_hideKeyboard(self)) {
++        // block the keyboard on mobile devices
++        self.blur();
++      }
++
++      var list = self.data("timepicker-list");
++
++      // check if input is readonly
++      if (self.prop("readonly")) {
++        return;
++      }
++
++      // check if list needs to be rendered
++      if (
++        !list ||
++        list.length === 0 ||
++        typeof settings.durationTime === "function"
++      ) {
++        _render(self);
++        list = self.data("timepicker-list");
++      }
++
++      if (_isVisible(list)) {
++        return;
++      }
++
++      self.data("ui-timepicker-value", self.val());
++      _setSelected(self, list);
++
++      // make sure other pickers are hidden
++      methods.hide();
++
++      // position the dropdown relative to the input
++      list.show();
++      var listOffset = {};
++
++      if (settings.orientation.match(/r/)) {
++        // right-align the dropdown
++        listOffset.left =
++          self.offset().left +
++          self.outerWidth() -
++          list.outerWidth() +
++          parseInt(list.css("marginLeft").replace("px", ""), 10);
++      } else {
++        // left-align the dropdown
++        listOffset.left =
++          self.offset().left +
++          parseInt(list.css("marginLeft").replace("px", ""), 10);
++      }
++
++      var verticalOrientation;
++      if (settings.orientation.match(/t/)) {
++        verticalOrientation = "t";
++      } else if (settings.orientation.match(/b/)) {
++        verticalOrientation = "b";
++      } else if (
++        self.offset().top + self.outerHeight(true) + list.outerHeight() >
++        $(window).height() + $(window).scrollTop()
++      ) {
++        verticalOrientation = "t";
++      } else {
++        verticalOrientation = "b";
++      }
++
++      if (verticalOrientation == "t") {
++        // position the dropdown on top
++        list.addClass("ui-timepicker-positioned-top");
++        listOffset.top =
++          self.offset().top -
++          list.outerHeight() +
++          parseInt(list.css("marginTop").replace("px", ""), 10);
++      } else {
++        // put it under the input
++        list.removeClass("ui-timepicker-positioned-top");
++        listOffset.top =
++          self.offset().top +
++          self.outerHeight() +
++          parseInt(list.css("marginTop").replace("px", ""), 10);
++      }
++
++      list.offset(listOffset);
++
++      // position scrolling
++      var selected = list.find(".ui-timepicker-selected");
++
++      if (!selected.length) {
++        var timeInt = _time2int(_getTimeValue(self));
++        if (timeInt !== null) {
++          selected = _findRow(self, list, timeInt);
++        } else if (settings.scrollDefault) {
++          selected = _findRow(self, list, settings.scrollDefault());
++        }
++      }
++
++      // if not found or disabled, intelligently find first selectable element
++      if (!selected.length || selected.hasClass("ui-timepicker-disabled")) {
++        selected = list.find("li:not(.ui-timepicker-disabled):first");
++      }
++
++      if (selected && selected.length) {
++        var topOffset =
++          list.scrollTop() + selected.position().top - selected.outerHeight();
++        list.scrollTop(topOffset);
++      } else {
++        list.scrollTop(0);
++      }
++
++      // prevent scroll propagation
++      if (settings.stopScrollPropagation) {
++        $(
++          document
++        ).on("wheel.ui-timepicker", ".ui-timepicker-wrapper", function(e) {
++          e.preventDefault();
++          var currentScroll = $(this).scrollTop();
++          $(this).scrollTop(currentScroll + e.originalEvent.deltaY);
++        });
++      }
++
++      // attach close handlers
++      $(document).on(
++        "touchstart.ui-timepicker mousedown.ui-timepicker",
++        _closeHandler
++      );
++      $(window).on("resize.ui-timepicker", _closeHandler);
++      if (settings.closeOnWindowScroll) {
++        $(document).on("scroll.ui-timepicker", _closeHandler);
++      }
++
++      self.trigger("showTimepicker");
++
++      return this;
++    },
++
++    hide: function(e) {
++      var self = $(this);
++      var settings = self.data("timepicker-settings");
++
++      if (settings && settings.useSelect) {
++        self.blur();
++      }
++
++      $(".ui-timepicker-wrapper").each(function() {
++        var list = $(this);
++        if (!_isVisible(list)) {
++          return;
++        }
++
++        var self = list.data("timepicker-input");
++        var settings = self.data("timepicker-settings");
++
++        if (settings && settings.selectOnBlur) {
++          _selectValue(self);
++        }
++
++        list.hide();
++        self.trigger("hideTimepicker");
++      });
++
++      return this;
++    },
++
++    option: function(key, value) {
++      if (typeof key == "string" && typeof value == "undefined") {
++        return $(this).data("timepicker-settings")[key];
++      }
++
++      return this.each(function() {
++        var self = $(this);
++        var settings = self.data("timepicker-settings");
++        var list = self.data("timepicker-list");
++
++        if (typeof key == "object") {
++          settings = $.extend(settings, key);
++        } else if (typeof key == "string") {
++          settings[key] = value;
++        }
++
++        settings = _parseSettings(settings);
++
++        self.data("timepicker-settings", settings);
++
++        _formatValue.call(self.get(0), { type: "change" }, "initial");
++
++        if (list) {
++          list.remove();
++          self.data("timepicker-list", false);
++        }
++
++        if (settings.useSelect) {
++          _render(self);
++        }
++      });
++    },
++
++    getSecondsFromMidnight: function() {
++      return _time2int(_getTimeValue(this));
++    },
++
++    getTime: function(relative_date) {
++      var self = this;
++
++      var time_string = _getTimeValue(self);
++      if (!time_string) {
++        return null;
++      }
++
++      var offset = _time2int(time_string);
++      if (offset === null) {
++        return null;
++      }
++
++      if (!relative_date) {
++        relative_date = new Date();
++      }
++
++      // construct a Date from relative date, and offset's time
++      var time = new Date(relative_date);
++      time.setHours(offset / 3600);
++      time.setMinutes((offset % 3600) / 60);
++      time.setSeconds(offset % 60);
++      time.setMilliseconds(0);
++
++      return time;
++    },
++
++    isVisible: function() {
++      var self = this;
++      var list = self.data("timepicker-list");
++      return !!(list && _isVisible(list));
++    },
++
++    setTime: function(value) {
++      var self = this;
++      var settings = self.data("timepicker-settings");
++
++      if (settings.forceRoundTime) {
++        var prettyTime = _roundAndFormatTime(_time2int(value), settings);
++      } else {
++        var prettyTime = _int2time(_time2int(value), settings);
++      }
++
++      if (value && prettyTime === null && settings.noneOption) {
++        prettyTime = value;
++      }
++
++      _setTimeValue(self, prettyTime, "initial");
++      _formatValue.call(self.get(0), { type: "change" }, "initial");
++
++      if (self.data("timepicker-list")) {
++        _setSelected(self, self.data("timepicker-list"));
++      }
++
++      return this;
++    },
++
++    remove: function() {
++      var self = this;
++
++      // check if this element is a timepicker
++      if (!self.hasClass("ui-timepicker-input")) {
++        return;
++      }
++
++      var settings = self.data("timepicker-settings");
++      self.removeAttr("autocomplete", "off");
++      self.removeClass("ui-timepicker-input");
++      self.removeData("timepicker-settings");
++      self.off(".timepicker");
++
++      // timepicker-list won't be present unless the user has interacted with this timepicker
++      if (self.data("timepicker-list")) {
++        self.data("timepicker-list").remove();
++      }
++
++      if (settings.useSelect) {
++        self.show();
++      }
++
++      self.removeData("timepicker-list");
++
++      return this;
++    }
++  };
++
++  // private methods
++
++  function _isVisible(elem) {
++    var el = elem[0];
++    return el.offsetWidth > 0 && el.offsetHeight > 0;
++  }
++
++  function _parseSettings(settings) {
++    if (settings.minTime) {
++      settings.minTime = _time2int(settings.minTime);
++    }
++
++    if (settings.maxTime) {
++      settings.maxTime = _time2int(settings.maxTime);
++    }
++
++    if (settings.durationTime && typeof settings.durationTime !== "function") {
++      settings.durationTime = _time2int(settings.durationTime);
++    }
++
++    if (settings.scrollDefault == "now") {
++      settings.scrollDefault = function() {
++        return settings.roundingFunction(_time2int(new Date()), settings);
++      };
++    } else if (
++      settings.scrollDefault &&
++      typeof settings.scrollDefault != "function"
++    ) {
++      var val = settings.scrollDefault;
++      settings.scrollDefault = function() {
++        return settings.roundingFunction(_time2int(val), settings);
++      };
++    } else if (settings.minTime) {
++      settings.scrollDefault = function() {
++        return settings.roundingFunction(settings.minTime, settings);
++      };
++    }
++
++    if (
++      $.type(settings.timeFormat) === "string" &&
++      settings.timeFormat.match(/[gh]/)
++    ) {
++      settings._twelveHourTime = true;
++    }
++
++    if (
++      settings.showOnFocus === false &&
++      settings.showOn.indexOf("focus") != -1
++    ) {
++      settings.showOn.splice(settings.showOn.indexOf("focus"), 1);
++    }
++
++    if (settings.disableTimeRanges.length > 0) {
++      // convert string times to integers
++      for (var i in settings.disableTimeRanges) {
++        settings.disableTimeRanges[i] = [
++          _time2int(settings.disableTimeRanges[i][0]),
++          _time2int(settings.disableTimeRanges[i][1])
++        ];
++      }
++
++      // sort by starting time
++      settings.disableTimeRanges = settings.disableTimeRanges.sort(function(
++        a,
++        b
++      ) {
++        return a[0] - b[0];
++      });
++
++      // merge any overlapping ranges
++      for (var i = settings.disableTimeRanges.length - 1; i > 0; i--) {
++        if (
++          settings.disableTimeRanges[i][0] <=
++          settings.disableTimeRanges[i - 1][1]
++        ) {
++          settings.disableTimeRanges[i - 1] = [
++            Math.min(
++              settings.disableTimeRanges[i][0],
++              settings.disableTimeRanges[i - 1][0]
++            ),
++            Math.max(
++              settings.disableTimeRanges[i][1],
++              settings.disableTimeRanges[i - 1][1]
++            )
++          ];
++          settings.disableTimeRanges.splice(i, 1);
++        }
++      }
++    }
++
++    return settings;
++  }
++
++  function _render(self) {
++    var settings = self.data("timepicker-settings");
++    var list = self.data("timepicker-list");
++
++    if (list && list.length) {
++      list.remove();
++      self.data("timepicker-list", false);
++    }
++
++    if (settings.useSelect) {
++      list = $("<select />", { class: "ui-timepicker-select" });
++      var wrapped_list = list;
++    } else {
++      list = $("<ul />", { class: "ui-timepicker-list" });
++
++      var wrapped_list = $("<div />", {
++        class: "ui-timepicker-wrapper",
++        tabindex: -1
++      });
++      wrapped_list.css({ display: "none", position: "absolute" }).append(list);
++    }
++
++    if (settings.noneOption) {
++      if (settings.noneOption === true) {
++        settings.noneOption = settings.useSelect ? "Time..." : "None";
++      }
++
++      if ($.isArray(settings.noneOption)) {
++        for (var i in settings.noneOption) {
++          if (parseInt(i, 10) == i) {
++            var noneElement = _generateNoneElement(
++              settings.noneOption[i],
++              settings.useSelect
++            );
++            list.append(noneElement);
++          }
++        }
++      } else {
++        var noneElement = _generateNoneElement(
++          settings.noneOption,
++          settings.useSelect
++        );
++        list.append(noneElement);
++      }
++    }
++
++    if (settings.className) {
++      wrapped_list.addClass(settings.className);
++    }
++
++    if (
++      (settings.minTime !== null || settings.durationTime !== null) &&
++      settings.showDuration
++    ) {
++      var stepval =
++        typeof settings.step == "function" ? "function" : settings.step;
++      wrapped_list.addClass("ui-timepicker-with-duration");
++      wrapped_list.addClass("ui-timepicker-step-" + settings.step);
++    }
++
++    var durStart = settings.minTime;
++    if (typeof settings.durationTime === "function") {
++      durStart = _time2int(settings.durationTime());
++    } else if (settings.durationTime !== null) {
++      durStart = settings.durationTime;
++    }
++    var start = settings.minTime !== null ? settings.minTime : 0;
++    var end =
++      settings.maxTime !== null ? settings.maxTime : start + _ONE_DAY - 1;
++
++    if (end < start) {
++      // make sure the end time is greater than start time, otherwise there will be no list to show
++      end += _ONE_DAY;
++    }
++
++    if (
++      end === _ONE_DAY - 1 &&
++      $.type(settings.timeFormat) === "string" &&
++      settings.show2400
++    ) {
++      // show a 24:00 option when using military time
++      end = _ONE_DAY;
++    }
++
++    var dr = settings.disableTimeRanges;
++    var drCur = 0;
++    var drLen = dr.length;
++
++    var stepFunc = settings.step;
++    if (typeof stepFunc != "function") {
++      stepFunc = function() {
++        return settings.step;
++      };
++    }
++
++    for (var i = start, j = 0; i <= end; j++, i += stepFunc(j) * 60) {
++      var timeInt = i;
++      var timeString = _int2time(timeInt, settings);
++
++      if (settings.useSelect) {
++        var row = $("<option />", { value: timeString });
++        row.text(timeString);
++      } else {
++        var row = $("<li />");
++        row.addClass(
++          timeInt % _ONE_DAY < _ONE_DAY / 2
++            ? "ui-timepicker-am"
++            : "ui-timepicker-pm"
++        );
++        row.data("time", _moduloSeconds(timeInt, settings));
++        row.text(timeString);
++      }
++
++      if (
++        (settings.minTime !== null || settings.durationTime !== null) &&
++        settings.showDuration
++      ) {
++        var durationString = _int2duration(i - durStart, settings.step);
++        if (settings.useSelect) {
++          row.text(row.text() + " (" + durationString + ")");
++        } else {
++          var duration = $("<span />", { class: "ui-timepicker-duration" });
++          duration.text(" (" + durationString + ")");
++          row.append(duration);
++        }
++      }
++
++      if (drCur < drLen) {
++        if (timeInt >= dr[drCur][1]) {
++          drCur += 1;
++        }
++
++        if (dr[drCur] && timeInt >= dr[drCur][0] && timeInt < dr[drCur][1]) {
++          if (settings.useSelect) {
++            row.prop("disabled", true);
++          } else {
++            row.addClass("ui-timepicker-disabled");
++          }
++        }
++      }
++
++      list.append(row);
++    }
++
++    wrapped_list.data("timepicker-input", self);
++    self.data("timepicker-list", wrapped_list);
++
++    if (settings.useSelect) {
++      if (self.val()) {
++        list.val(_roundAndFormatTime(_time2int(self.val()), settings));
++      }
++
++      list.on("focus", function() {
++        $(this)
++          .data("timepicker-input")
++          .trigger("showTimepicker");
++      });
++      list.on("blur", function() {
++        $(this)
++          .data("timepicker-input")
++          .trigger("hideTimepicker");
++      });
++      list.on("change", function() {
++        _setTimeValue(self, $(this).val(), "select");
++      });
++
++      _setTimeValue(self, list.val(), "initial");
++      self.hide().after(list);
++    } else {
++      var appendTo = settings.appendTo;
++      if (typeof appendTo === "string") {
++        appendTo = $(appendTo);
++      } else if (typeof appendTo === "function") {
++        appendTo = appendTo(self);
++      }
++      appendTo.append(wrapped_list);
++      _setSelected(self, list);
++
++      list.on("mousedown click", "li", function(e) {
++        // hack: temporarily disable the focus handler
++        // to deal with the fact that IE fires 'focus'
++        // events asynchronously
++        self.off("focus.timepicker");
++        self.on("focus.timepicker-ie-hack", function() {
++          self.off("focus.timepicker-ie-hack");
++          self.on("focus.timepicker", methods.show);
++        });
++
++        if (!_hideKeyboard(self)) {
++          self[0].focus();
++        }
++
++        // make sure only the clicked row is selected
++        list.find("li").removeClass("ui-timepicker-selected");
++        $(this).addClass("ui-timepicker-selected");
++
++        if (_selectValue(self)) {
++          self.trigger("hideTimepicker");
++
++          list.on("mouseup.timepicker click.timepicker", "li", function(e) {
++            list.off("mouseup.timepicker click.timepicker");
++            wrapped_list.hide();
++          });
++        }
++      });
++    }
++  }
++
++  function _generateNoneElement(optionValue, useSelect) {
++    var label, className, value;
++
++    if (typeof optionValue == "object") {
++      label = optionValue.label;
++      className = optionValue.className;
++      value = optionValue.value;
++    } else if (typeof optionValue == "string") {
++      label = optionValue;
++      value = '';
++    } else {
++      $.error("Invalid noneOption value");
++    }
++
++    if (useSelect) {
++      return $("<option />", {
++        value: value,
++        class: className,
++        text: label
++      });
++    } else {
++      return $("<li />", {
++        class: className,
++        text: label
++      }).data("time", String(value));
++    }
++  }
++
++  function _roundAndFormatTime(seconds, settings) {
++    seconds = settings.roundingFunction(seconds, settings);
++    if (seconds !== null) {
++      return _int2time(seconds, settings);
++    }
++  }
++
++  // event handler to decide whether to close timepicker
++  function _closeHandler(e) {
++    if (e.target == window) {
++      // mobile Chrome fires focus events against window for some reason
++      return;
++    }
++
++    var target = $(e.target);
++
++    if (
++      target.closest(".ui-timepicker-input").length ||
++      target.closest(".ui-timepicker-wrapper").length
++    ) {
++      // active timepicker was focused. ignore
++      return;
++    }
++
++    methods.hide();
++    $(document).unbind(".ui-timepicker");
++    $(window).unbind(".ui-timepicker");
++  }
++
++  function _hideKeyboard(self) {
++    var settings = self.data("timepicker-settings");
++    return (
++      (window.navigator.msMaxTouchPoints || "ontouchstart" in document) &&
++      settings.disableTouchKeyboard
++    );
++  }
++
++  function _findRow(self, list, value) {
++    if (!value && value !== 0) {
++      return false;
++    }
++
++    var settings = self.data("timepicker-settings");
++    var out = false;
++    var value = settings.roundingFunction(value, settings);
++
++    // loop through the menu items
++    list.find("li").each(function(i, obj) {
++      var jObj = $(obj);
++      if (typeof jObj.data("time") != "number") {
++        return;
++      }
++
++      if (jObj.data("time") == value) {
++        out = jObj;
++        return false;
++      }
++    });
++
++    return out;
++  }
++
++  function _setSelected(self, list) {
++    list.find("li").removeClass("ui-timepicker-selected");
++
++    var settings = self.data("timepicker-settings");
++    var timeValue = _time2int(_getTimeValue(self), settings);
++    if (timeValue === null) {
++      return;
++    }
++
++    var selected = _findRow(self, list, timeValue);
++    if (selected) {
++      var topDelta = selected.offset().top - list.offset().top;
++
++      if (
++        topDelta + selected.outerHeight() > list.outerHeight() ||
++        topDelta < 0
++      ) {
++        list.scrollTop(
++          list.scrollTop() + selected.position().top - selected.outerHeight()
++        );
++      }
++
++      if (settings.forceRoundTime || selected.data("time") === timeValue) {
++        selected.addClass("ui-timepicker-selected");
++      }
++    }
++  }
++
++  function _formatValue(e, origin) {
++    if (origin == "timepicker") {
++      return;
++    }
++
++    var self = $(this);
++
++    if (this.value === "") {
++      _setTimeValue(self, null, origin);
++      return;
++    }
++
++    if (self.is(":focus") && (!e || e.type != "change")) {
++      return;
++    }
++
++    var settings = self.data("timepicker-settings");
++    var seconds = _time2int(this.value, settings);
++
++    if (seconds === null) {
++      self.trigger("timeFormatError");
++      return;
++    }
++
++    var rangeError = false;
++    // check that the time in within bounds
++    if (
++      settings.minTime !== null &&
++      settings.maxTime !== null &&
++      (seconds < settings.minTime || seconds > settings.maxTime)
++    ) {
++      rangeError = true;
++    }
++
++    // check that time isn't within disabled time ranges
++    $.each(settings.disableTimeRanges, function() {
++      if (seconds >= this[0] && seconds < this[1]) {
++        rangeError = true;
++        return false;
++      }
++    });
++
++    if (settings.forceRoundTime) {
++      var roundSeconds = settings.roundingFunction(seconds, settings);
++      if (roundSeconds != seconds) {
++        seconds = roundSeconds;
++        origin = null;
++      }
++    }
++
++    var prettyTime = _int2time(seconds, settings);
++
++    if (rangeError) {
++      if (
++        _setTimeValue(self, prettyTime, "error") ||
++        (e && e.type == "change")
++      ) {
++        self.trigger("timeRangeError");
++      }
++    } else {
++      _setTimeValue(self, prettyTime, origin);
++    }
++  }
++
++  function _getTimeValue(self) {
++    if (self.is("input")) {
++      return self.val();
++    } else {
++      // use the element's data attributes to store values
++      return self.data("ui-timepicker-value");
++    }
++  }
++
++  function _setTimeValue(self, value, source) {
++    if (self.is("input")) {
++      self.val(value);
++
++      var settings = self.data("timepicker-settings");
++      if (settings.useSelect && source != "select") {
++        self
++          .data("timepicker-list")
++          .val(_roundAndFormatTime(_time2int(value), settings));
++      }
++    }
++
++    if (self.data("ui-timepicker-value") != value) {
++      self.data("ui-timepicker-value", value);
++      if (source == "select") {
++        self
++          .trigger("selectTime")
++          .trigger("changeTime")
++          .trigger("change", "timepicker");
++      } else if (["error", "initial"].indexOf(source) == -1) {
++        self.trigger("changeTime");
++      }
++
++      return true;
++    } else {
++      if (["error", "initial"].indexOf(source) == -1) {
++        self.trigger("selectTime");
++      }
++      return false;
++    }
++  }
++
++  /*
++      *  Filter freeform input
++      */
++  function _disableTextInputHandler(e) {
++    switch (e.keyCode) {
++      case 13: // return
++      case 9: //tab
++        return;
++
++      default:
++        e.preventDefault();
++    }
++  }
++
++  /*
++      *  Keyboard navigation via arrow keys
++      */
++  function _keydownhandler(e) {
++    var self = $(this);
++    var list = self.data("timepicker-list");
++
++    if (!list || !_isVisible(list)) {
++      if (e.keyCode == 40) {
++        // show the list!
++        methods.show.call(self.get(0));
++        list = self.data("timepicker-list");
++        if (!_hideKeyboard(self)) {
++          self.focus();
++        }
++      } else {
++        return true;
++      }
++    }
++
++    switch (e.keyCode) {
++      case 13: // return
++        if (_selectValue(self)) {
++          _formatValue.call(self.get(0), { type: "change" });
++          methods.hide.apply(this);
++        }
++
++        e.preventDefault();
++        return false;
++
++      case 38: // up
++        var selected = list.find(".ui-timepicker-selected");
++
++        if (!selected.length) {
++          list.find("li").each(function(i, obj) {
++            if ($(obj).position().top > 0) {
++              selected = $(obj);
++              return false;
++            }
++          });
++          selected.addClass("ui-timepicker-selected");
++        } else if (!selected.is(":first-child")) {
++          selected.removeClass("ui-timepicker-selected");
++          selected.prev().addClass("ui-timepicker-selected");
++
++          if (selected.prev().position().top < selected.outerHeight()) {
++            list.scrollTop(list.scrollTop() - selected.outerHeight());
++          }
++        }
++
++        return false;
++
++      case 40: // down
++        selected = list.find(".ui-timepicker-selected");
++
++        if (selected.length === 0) {
++          list.find("li").each(function(i, obj) {
++            if ($(obj).position().top > 0) {
++              selected = $(obj);
++              return false;
++            }
++          });
++
++          selected.addClass("ui-timepicker-selected");
++        } else if (!selected.is(":last-child")) {
++          selected.removeClass("ui-timepicker-selected");
++          selected.next().addClass("ui-timepicker-selected");
++
++          if (
++            selected.next().position().top + 2 * selected.outerHeight() >
++            list.outerHeight()
++          ) {
++            list.scrollTop(list.scrollTop() + selected.outerHeight());
++          }
++        }
++
++        return false;
++
++      case 27: // escape
++        list.find("li").removeClass("ui-timepicker-selected");
++        methods.hide();
++        break;
++
++      case 9: //tab
++        methods.hide();
++        break;
++
++      default:
++        return true;
++    }
++  }
++
++  /*
++      *       Time typeahead
++      */
++  function _keyuphandler(e) {
++    var self = $(this);
++    var list = self.data("timepicker-list");
++    var settings = self.data("timepicker-settings");
++
++    if (!list || !_isVisible(list) || settings.disableTextInput) {
++      return true;
++    }
++
++    if (e.type === "paste" || e.type === "cut") {
++      setTimeout(function() {
++        if (settings.typeaheadHighlight) {
++          _setSelected(self, list);
++        } else {
++          list.hide();
++        }
++      }, 0);
++      return;
++    }
++
++    switch (e.keyCode) {
++      case 96: // numpad numerals
++      case 97:
++      case 98:
++      case 99:
++      case 100:
++      case 101:
++      case 102:
++      case 103:
++      case 104:
++      case 105:
++      case 48: // numerals
++      case 49:
++      case 50:
++      case 51:
++      case 52:
++      case 53:
++      case 54:
++      case 55:
++      case 56:
++      case 57:
++      case 65: // a
++      case 77: // m
++      case 80: // p
++      case 186: // colon
++      case 8: // backspace
++      case 46: // delete
++        if (settings.typeaheadHighlight) {
++          _setSelected(self, list);
++        } else {
++          list.hide();
++        }
++        break;
++    }
++  }
++
++  function _selectValue(self) {
++    var settings = self.data("timepicker-settings");
++    var list = self.data("timepicker-list");
++    var timeValue = null;
++
++    var cursor = list.find(".ui-timepicker-selected");
++
++    if (cursor.hasClass("ui-timepicker-disabled")) {
++      return false;
++    }
++
++    if (cursor.length) {
++      // selected value found
++      timeValue = cursor.data("time");
++    }
++
++    if (timeValue !== null) {
++      if (typeof timeValue != "string") {
++        timeValue = _int2time(timeValue, settings);
++      }
++
++      _setTimeValue(self, timeValue, "select");
++    }
++
++    return true;
++  }
++
++  function _int2duration(seconds, step) {
++    seconds = Math.abs(seconds);
++    var minutes = Math.round(seconds / 60),
++      duration = [],
++      hours,
++      mins;
++
++    if (minutes < 60) {
++      // Only show (x mins) under 1 hour
++      duration = [minutes, _lang.mins];
++    } else {
++      hours = Math.floor(minutes / 60);
++      mins = minutes % 60;
++
++      // Show decimal notation (eg: 1.5 hrs) for 30 minute steps
++      if (step == 30 && mins == 30) {
++        hours += _lang.decimal + 5;
++      }
++
++      duration.push(hours);
++      duration.push(hours == 1 ? _lang.hr : _lang.hrs);
++
++      // Show remainder minutes notation (eg: 1 hr 15 mins) for non-30 minute steps
++      // and only if there are remainder minutes to show
++      if (step != 30 && mins) {
++        duration.push(mins);
++        duration.push(_lang.mins);
++      }
++    }
++
++    return duration.join(" ");
++  }
++
++  function _int2time(timeInt, settings) {
++    if (typeof timeInt != "number") {
++      return null;
++    }
++
++    var seconds = parseInt(timeInt % 60),
++      minutes = parseInt((timeInt / 60) % 60),
++      hours = parseInt((timeInt / (60 * 60)) % 24);
++
++    var time = new Date(1970, 0, 2, hours, minutes, seconds, 0);
++
++    if (isNaN(time.getTime())) {
++      return null;
++    }
++
++    if ($.type(settings.timeFormat) === "function") {
++      return settings.timeFormat(time);
++    }
++
++    var output = "";
++    var hour, code;
++    for (var i = 0; i < settings.timeFormat.length; i++) {
++      code = settings.timeFormat.charAt(i);
++      switch (code) {
++        case "a":
++          output += time.getHours() > 11 ? _lang.pm : _lang.am;
++          break;
++
++        case "A":
++          output += time.getHours() > 11 ? _lang.PM : _lang.AM;
++          break;
++
++        case "g":
++          hour = time.getHours() % 12;
++          output += hour === 0 ? "12" : hour;
++          break;
++
++        case "G":
++          hour = time.getHours();
++          if (timeInt === _ONE_DAY) hour = settings.show2400 ? 24 : 0;
++          output += hour;
++          break;
++
++        case "h":
++          hour = time.getHours() % 12;
++
++          if (hour !== 0 && hour < 10) {
++            hour = "0" + hour;
++          }
++
++          output += hour === 0 ? "12" : hour;
++          break;
++
++        case "H":
++          hour = time.getHours();
++          if (timeInt === _ONE_DAY) hour = settings.show2400 ? 24 : 0;
++          output += hour > 9 ? hour : "0" + hour;
++          break;
++
++        case "i":
++          var minutes = time.getMinutes();
++          output += minutes > 9 ? minutes : "0" + minutes;
++          break;
++
++        case "s":
++          seconds = time.getSeconds();
++          output += seconds > 9 ? seconds : "0" + seconds;
++          break;
++
++        case "\\":
++          // escape character; add the next character and skip ahead
++          i++;
++          output += settings.timeFormat.charAt(i);
++          break;
++
++        default:
++          output += code;
++      }
++    }
++
++    return output;
++  }
++
++  function _time2int(timeString, settings) {
++    if (timeString === "" || timeString === null) return null;
++    if (typeof timeString == "object") {
++      return (
++        timeString.getHours() * 3600 +
++        timeString.getMinutes() * 60 +
++        timeString.getSeconds()
++      );
++    }
++    if (typeof timeString != "string") {
++      return timeString;
++    }
++
++    timeString = timeString.toLowerCase().replace(/[\s\.]/g, "");
++
++    // if the last character is an "a" or "p", add the "m"
++    if (timeString.slice(-1) == "a" || timeString.slice(-1) == "p") {
++      timeString += "m";
++    }
++
++    var ampmRegex =
++      "(" +
++      _lang.am.replace(".", "") +
++      "|" +
++      _lang.pm.replace(".", "") +
++      "|" +
++      _lang.AM.replace(".", "") +
++      "|" +
++      _lang.PM.replace(".", "") +
++      ")?";
++
++    // try to parse time input
++    var pattern = new RegExp(
++      "^" +
++        ampmRegex +
++        "([0-9]?[0-9])\\W?([0-5][0-9])?\\W?([0-5][0-9])?" +
++        ampmRegex +
++        "$"
++    );
++
++    var time = timeString.match(pattern);
++    if (!time) {
++      return null;
++    }
++
++    var hour = parseInt(time[2] * 1, 10);
++    var ampm = time[1] || time[5];
++    var hours = hour;
++    var minutes = time[3] * 1 || 0;
++    var seconds = time[4] * 1 || 0;
++
++    if (hour <= 12 && ampm) {
++      var isPm = ampm == _lang.pm || ampm == _lang.PM;
++
++      if (hour == 12) {
++        hours = isPm ? 12 : 0;
++      } else {
++        hours = hour + (isPm ? 12 : 0);
++      }
++    } else if (settings) {
++      var t = hour * 3600 + minutes * 60 + seconds;
++      if (t >= _ONE_DAY + (settings.show2400 ? 1 : 0)) {
++        if (settings.wrapHours === false) {
++          return null;
++        }
++
++        hours = hour % 24;
++      }
++    }
++
++    var timeInt = hours * 3600 + minutes * 60 + seconds;
++
++    // if no am/pm provided, intelligently guess based on the scrollDefault
++    if (
++      hour < 12 &&
++      !ampm &&
++      settings &&
++      settings._twelveHourTime &&
++      settings.scrollDefault
++    ) {
++      var delta = timeInt - settings.scrollDefault();
++      if (delta < 0 && delta >= _ONE_DAY / -2) {
++        timeInt = (timeInt + _ONE_DAY / 2) % _ONE_DAY;
++      }
++    }
++
++    return timeInt;
++  }
++
++  function _pad2(n) {
++    return ("0" + n).slice(-2);
++  }
++
++  function _moduloSeconds(seconds, settings) {
++    if (seconds == _ONE_DAY && settings.show2400) {
++      return seconds;
++    }
++
++    return seconds % _ONE_DAY;
++  }
++
++  // Plugin entry
++  $.fn.timepicker = function(method) {
++    if (!this.length) return this;
++    if (methods[method]) {
++      // check if this element is a timepicker
++      if (!this.hasClass("ui-timepicker-input")) {
++        return this;
++      }
++      return methods[method].apply(
++        this,
++        Array.prototype.slice.call(arguments, 1)
++      );
++    } else if (typeof method === "object" || !method) {
++      return methods.init.apply(this, arguments);
++    } else {
++      $.error("Method " + method + " does not exist on jQuery.timepicker");
++    }
++  };
++});
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..df63f8923ae2da871bd3567053887540f99099f3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4920 @@@
++/*!
++ * jsTree {{VERSION}}
++ * http://jstree.com/
++ *
++ * Copyright (c) 2014 Ivan Bozhanov (http://vakata.com)
++ *
++ * Licensed same as jquery - under the terms of the MIT License
++ *   http://www.opensource.org/licenses/mit-license.php
++ */
++/*!
++ * if using jslint please allow for the jQuery global and use following options:
++ * jslint: loopfunc: true, browser: true, ass: true, bitwise: true, continue: true, nomen: true, plusplus: true, regexp: true, unparam: true, todo: true, white: true
++ */
++/*jshint -W083 */
++/*globals jQuery, define, module, exports, require, window, document, postMessage */
++(function (factory) {
++      "use strict";
++      if (typeof define === 'function' && define.amd) {
++              define(['jquery'], factory);
++      }
++      else if(typeof module !== 'undefined' && module.exports) {
++              module.exports = factory(require('jquery'));
++      }
++      else {
++              factory(jQuery);
++      }
++}(function ($, undefined) {
++      "use strict";
++
++      // prevent another load? maybe there is a better way?
++      if($.jstree) {
++              return;
++      }
++
++      /**
++       * ### jsTree core functionality
++       */
++
++      // internal variables
++      var instance_counter = 0,
++              ccp_node = false,
++              ccp_mode = false,
++              ccp_inst = false,
++              themes_loaded = [],
++              src = $('script:last').attr('src'),
++              document = window.document; // local variable is always faster to access then a global
++
++      /**
++       * holds all jstree related functions and variables, including the actual class and methods to create, access and manipulate instances.
++       * @name $.jstree
++       */
++      $.jstree = {
++              /**
++               * specifies the jstree version in use
++               * @name $.jstree.version
++               */
++              version : '{{VERSION}}',
++              /**
++               * holds all the default options used when creating new instances
++               * @name $.jstree.defaults
++               */
++              defaults : {
++                      /**
++                       * configure which plugins will be active on an instance. Should be an array of strings, where each element is a plugin name. The default is `[]`
++                       * @name $.jstree.defaults.plugins
++                       */
++                      plugins : []
++              },
++              /**
++               * stores all loaded jstree plugins (used internally)
++               * @name $.jstree.plugins
++               */
++              plugins : {},
++              path : src && src.indexOf('/') !== -1 ? src.replace(/\/[^\/]+$/,'') : '',
++              idregex : /[\\:&!^|()\[\]<>@*'+~#";.,=\- \/${}%?`]/g,
++              root : '#'
++      };
++      
++      /**
++       * creates a jstree instance
++       * @name $.jstree.create(el [, options])
++       * @param {DOMElement|jQuery|String} el the element to create the instance on, can be jQuery extended or a selector
++       * @param {Object} options options for this instance (extends `$.jstree.defaults`)
++       * @return {jsTree} the new instance
++       */
++      $.jstree.create = function (el, options) {
++              var tmp = new $.jstree.core(++instance_counter),
++                      opt = options;
++              options = $.extend(true, {}, $.jstree.defaults, options);
++              if(opt && opt.plugins) {
++                      options.plugins = opt.plugins;
++              }
++              $.each(options.plugins, function (i, k) {
++                      if(i !== 'core') {
++                              tmp = tmp.plugin(k, options[k]);
++                      }
++              });
++              $(el).data('jstree', tmp);
++              tmp.init(el, options);
++              return tmp;
++      };
++      /**
++       * remove all traces of jstree from the DOM and destroy all instances
++       * @name $.jstree.destroy()
++       */
++      $.jstree.destroy = function () {
++              $('.jstree:jstree').jstree('destroy');
++              $(document).off('.jstree');
++      };
++      /**
++       * the jstree class constructor, used only internally
++       * @private
++       * @name $.jstree.core(id)
++       * @param {Number} id this instance's index
++       */
++      $.jstree.core = function (id) {
++              this._id = id;
++              this._cnt = 0;
++              this._wrk = null;
++              this._data = {
++                      core : {
++                              themes : {
++                                      name : false,
++                                      dots : false,
++                                      icons : false,
++                                      ellipsis : false
++                              },
++                              selected : [],
++                              last_error : {},
++                              working : false,
++                              worker_queue : [],
++                              focused : null
++                      }
++              };
++      };
++      /**
++       * get a reference to an existing instance
++       *
++       * __Examples__
++       *
++       *      // provided a container with an ID of "tree", and a nested node with an ID of "branch"
++       *      // all of there will return the same instance
++       *      $.jstree.reference('tree');
++       *      $.jstree.reference('#tree');
++       *      $.jstree.reference($('#tree'));
++       *      $.jstree.reference(document.getElementByID('tree'));
++       *      $.jstree.reference('branch');
++       *      $.jstree.reference('#branch');
++       *      $.jstree.reference($('#branch'));
++       *      $.jstree.reference(document.getElementByID('branch'));
++       *
++       * @name $.jstree.reference(needle)
++       * @param {DOMElement|jQuery|String} needle
++       * @return {jsTree|null} the instance or `null` if not found
++       */
++      $.jstree.reference = function (needle) {
++              var tmp = null,
++                      obj = null;
++              if(needle && needle.id && (!needle.tagName || !needle.nodeType)) { needle = needle.id; }
++
++              if(!obj || !obj.length) {
++                      try { obj = $(needle); } catch (ignore) { }
++              }
++              if(!obj || !obj.length) {
++                      try { obj = $('#' + needle.replace($.jstree.idregex,'\\$&')); } catch (ignore) { }
++              }
++              if(obj && obj.length && (obj = obj.closest('.jstree')).length && (obj = obj.data('jstree'))) {
++                      tmp = obj;
++              }
++              else {
++                      $('.jstree').each(function () {
++                              var inst = $(this).data('jstree');
++                              if(inst && inst._model.data[needle]) {
++                                      tmp = inst;
++                                      return false;
++                              }
++                      });
++              }
++              return tmp;
++      };
++      /**
++       * Create an instance, get an instance or invoke a command on a instance.
++       *
++       * If there is no instance associated with the current node a new one is created and `arg` is used to extend `$.jstree.defaults` for this new instance. There would be no return value (chaining is not broken).
++       *
++       * If there is an existing instance and `arg` is a string the command specified by `arg` is executed on the instance, with any additional arguments passed to the function. If the function returns a value it will be returned (chaining could break depending on function).
++       *
++       * If there is an existing instance and `arg` is not a string the instance itself is returned (similar to `$.jstree.reference`).
++       *
++       * In any other case - nothing is returned and chaining is not broken.
++       *
++       * __Examples__
++       *
++       *      $('#tree1').jstree(); // creates an instance
++       *      $('#tree2').jstree({ plugins : [] }); // create an instance with some options
++       *      $('#tree1').jstree('open_node', '#branch_1'); // call a method on an existing instance, passing additional arguments
++       *      $('#tree2').jstree(); // get an existing instance (or create an instance)
++       *      $('#tree2').jstree(true); // get an existing instance (will not create new instance)
++       *      $('#branch_1').jstree().select_node('#branch_1'); // get an instance (using a nested element and call a method)
++       *
++       * @name $().jstree([arg])
++       * @param {String|Object} arg
++       * @return {Mixed}
++       */
++      $.fn.jstree = function (arg) {
++              // check for string argument
++              var is_method   = (typeof arg === 'string'),
++                      args            = Array.prototype.slice.call(arguments, 1),
++                      result          = null;
++              if(arg === true && !this.length) { return false; }
++              this.each(function () {
++                      // get the instance (if there is one) and method (if it exists)
++                      var instance = $.jstree.reference(this),
++                              method = is_method && instance ? instance[arg] : null;
++                      // if calling a method, and method is available - execute on the instance
++                      result = is_method && method ?
++                              method.apply(instance, args) :
++                              null;
++                      // if there is no instance and no method is being called - create one
++                      if(!instance && !is_method && (arg === undefined || $.isPlainObject(arg))) {
++                              $.jstree.create(this, arg);
++                      }
++                      // if there is an instance and no method is called - return the instance
++                      if( (instance && !is_method) || arg === true ) {
++                              result = instance || false;
++                      }
++                      // if there was a method call which returned a result - break and return the value
++                      if(result !== null && result !== undefined) {
++                              return false;
++                      }
++              });
++              // if there was a method call with a valid return value - return that, otherwise continue the chain
++              return result !== null && result !== undefined ?
++                      result : this;
++      };
++      /**
++       * used to find elements containing an instance
++       *
++       * __Examples__
++       *
++       *      $('div:jstree').each(function () {
++       *              $(this).jstree('destroy');
++       *      });
++       *
++       * @name $(':jstree')
++       * @return {jQuery}
++       */
++      $.expr.pseudos.jstree = $.expr.createPseudo(function(search) {
++              return function(a) {
++                      return $(a).hasClass('jstree') &&
++                              $(a).data('jstree') !== undefined;
++              };
++      });
++
++      /**
++       * stores all defaults for the core
++       * @name $.jstree.defaults.core
++       */
++      $.jstree.defaults.core = {
++              /**
++               * data configuration
++               *
++               * If left as `false` the HTML inside the jstree container element is used to populate the tree (that should be an unordered list with list items).
++               *
++               * You can also pass in a HTML string or a JSON array here.
++               *
++               * It is possible to pass in a standard jQuery-like AJAX config and jstree will automatically determine if the response is JSON or HTML and use that to populate the tree.
++               * In addition to the standard jQuery ajax options here you can suppy functions for `data` and `url`, the functions will be run in the current instance's scope and a param will be passed indicating which node is being loaded, the return value of those functions will be used.
++               *
++               * The last option is to specify a function, that function will receive the node being loaded as argument and a second param which is a function which should be called with the result.
++               *
++               * __Examples__
++               *
++               *      // AJAX
++               *      $('#tree').jstree({
++               *              'core' : {
++               *                      'data' : {
++               *                              'url' : '/get/children/',
++               *                              'data' : function (node) {
++               *                                      return { 'id' : node.id };
++               *                              }
++               *                      }
++               *              });
++               *
++               *      // direct data
++               *      $('#tree').jstree({
++               *              'core' : {
++               *                      'data' : [
++               *                              'Simple root node',
++               *                              {
++               *                                      'id' : 'node_2',
++               *                                      'text' : 'Root node with options',
++               *                                      'state' : { 'opened' : true, 'selected' : true },
++               *                                      'children' : [ { 'text' : 'Child 1' }, 'Child 2']
++               *                              }
++               *                      ]
++               *              }
++               *      });
++               *
++               *      // function
++               *      $('#tree').jstree({
++               *              'core' : {
++               *                      'data' : function (obj, callback) {
++               *                              callback.call(this, ['Root 1', 'Root 2']);
++               *                      }
++               *              });
++               *
++               * @name $.jstree.defaults.core.data
++               */
++              data                    : false,
++              /**
++               * configure the various strings used throughout the tree
++               *
++               * You can use an object where the key is the string you need to replace and the value is your replacement.
++               * Another option is to specify a function which will be called with an argument of the needed string and should return the replacement.
++               * If left as `false` no replacement is made.
++               *
++               * __Examples__
++               *
++               *      $('#tree').jstree({
++               *              'core' : {
++               *                      'strings' : {
++               *                              'Loading ...' : 'Please wait ...'
++               *                      }
++               *              }
++               *      });
++               *
++               * @name $.jstree.defaults.core.strings
++               */
++              strings                 : false,
++              /**
++               * determines what happens when a user tries to modify the structure of the tree
++               * If left as `false` all operations like create, rename, delete, move or copy are prevented.
++               * You can set this to `true` to allow all interactions or use a function to have better control.
++               *
++               * __Examples__
++               *
++               *      $('#tree').jstree({
++               *              'core' : {
++               *                      'check_callback' : function (operation, node, node_parent, node_position, more) {
++               *                              // operation can be 'create_node', 'rename_node', 'delete_node', 'move_node', 'copy_node' or 'edit'
++               *                              // in case of 'rename_node' node_position is filled with the new node name
++               *                              return operation === 'rename_node' ? true : false;
++               *                      }
++               *              }
++               *      });
++               *
++               * @name $.jstree.defaults.core.check_callback
++               */
++              check_callback  : false,
++              /**
++               * a callback called with a single object parameter in the instance's scope when something goes wrong (operation prevented, ajax failed, etc)
++               * @name $.jstree.defaults.core.error
++               */
++              error                   : $.noop,
++              /**
++               * the open / close animation duration in milliseconds - set this to `false` to disable the animation (default is `200`)
++               * @name $.jstree.defaults.core.animation
++               */
++              animation               : 200,
++              /**
++               * a boolean indicating if multiple nodes can be selected
++               * @name $.jstree.defaults.core.multiple
++               */
++              multiple                : true,
++              /**
++               * theme configuration object
++               * @name $.jstree.defaults.core.themes
++               */
++              themes                  : {
++                      /**
++                       * the name of the theme to use (if left as `false` the default theme is used)
++                       * @name $.jstree.defaults.core.themes.name
++                       */
++                      name                    : false,
++                      /**
++                       * the URL of the theme's CSS file, leave this as `false` if you have manually included the theme CSS (recommended). You can set this to `true` too which will try to autoload the theme.
++                       * @name $.jstree.defaults.core.themes.url
++                       */
++                      url                             : false,
++                      /**
++                       * the location of all jstree themes - only used if `url` is set to `true`
++                       * @name $.jstree.defaults.core.themes.dir
++                       */
++                      dir                             : false,
++                      /**
++                       * a boolean indicating if connecting dots are shown
++                       * @name $.jstree.defaults.core.themes.dots
++                       */
++                      dots                    : true,
++                      /**
++                       * a boolean indicating if node icons are shown
++                       * @name $.jstree.defaults.core.themes.icons
++                       */
++                      icons                   : true,
++                      /**
++                       * a boolean indicating if node ellipsis should be shown - this only works with a fixed with on the container
++                       * @name $.jstree.defaults.core.themes.ellipsis
++                       */
++                      ellipsis                : false,
++                      /**
++                       * a boolean indicating if the tree background is striped
++                       * @name $.jstree.defaults.core.themes.stripes
++                       */
++                      stripes                 : false,
++                      /**
++                       * a string (or boolean `false`) specifying the theme variant to use (if the theme supports variants)
++                       * @name $.jstree.defaults.core.themes.variant
++                       */
++                      variant                 : false,
++                      /**
++                       * a boolean specifying if a reponsive version of the theme should kick in on smaller screens (if the theme supports it). Defaults to `false`.
++                       * @name $.jstree.defaults.core.themes.responsive
++                       */
++                      responsive              : false
++              },
++              /**
++               * if left as `true` all parents of all selected nodes will be opened once the tree loads (so that all selected nodes are visible to the user)
++               * @name $.jstree.defaults.core.expand_selected_onload
++               */
++              expand_selected_onload : true,
++              /**
++               * if left as `true` web workers will be used to parse incoming JSON data where possible, so that the UI will not be blocked by large requests. Workers are however about 30% slower. Defaults to `true`
++               * @name $.jstree.defaults.core.worker
++               */
++              worker : true,
++              /**
++               * Force node text to plain text (and escape HTML). Defaults to `false`
++               * @name $.jstree.defaults.core.force_text
++               */
++              force_text : false,
++              /**
++               * Should the node should be toggled if the text is double clicked . Defaults to `true`
++               * @name $.jstree.defaults.core.dblclick_toggle
++               */
++              dblclick_toggle : true,
++              /**
++               * Should the loaded nodes be part of the state. Defaults to `false`
++               * @name $.jstree.defaults.core.loaded_state
++               */
++              loaded_state : false,
++              /**
++               * Should the last active node be focused when the tree container is blurred and the focused again. This helps working with screen readers. Defaults to `true`
++               * @name $.jstree.defaults.core.restore_focus
++               */
++              restore_focus : true,
++              /**
++               * Default keyboard shortcuts (an object where each key is the button name or combo - like 'enter', 'ctrl-space', 'p', etc and the value is the function to execute in the instance's scope)
++               * @name $.jstree.defaults.core.keyboard
++               */
++              keyboard : {
++                      'ctrl-space': function (e) {
++                              // aria defines space only with Ctrl
++                              e.type = "click";
++                              $(e.currentTarget).trigger(e);
++                      },
++                      'enter': function (e) {
++                              // enter
++                              e.type = "click";
++                              $(e.currentTarget).trigger(e);
++                      },
++                      'left': function (e) {
++                              // left
++                              e.preventDefault();
++                              if(this.is_open(e.currentTarget)) {
++                                      this.close_node(e.currentTarget);
++                              }
++                              else {
++                                      var o = this.get_parent(e.currentTarget);
++                                      if(o && o.id !== $.jstree.root) { this.get_node(o, true).children('.jstree-anchor').focus(); }
++                              }
++                      },
++                      'up': function (e) {
++                              // up
++                              e.preventDefault();
++                              var o = this.get_prev_dom(e.currentTarget);
++                              if(o && o.length) { o.children('.jstree-anchor').focus(); }
++                      },
++                      'right': function (e) {
++                              // right
++                              e.preventDefault();
++                              if(this.is_closed(e.currentTarget)) {
++                                      this.open_node(e.currentTarget, function (o) { this.get_node(o, true).children('.jstree-anchor').focus(); });
++                              }
++                              else if (this.is_open(e.currentTarget)) {
++                                      var o = this.get_node(e.currentTarget, true).children('.jstree-children')[0];
++                                      if(o) { $(this._firstChild(o)).children('.jstree-anchor').focus(); }
++                              }
++                      },
++                      'down': function (e) {
++                              // down
++                              e.preventDefault();
++                              var o = this.get_next_dom(e.currentTarget);
++                              if(o && o.length) { o.children('.jstree-anchor').focus(); }
++                      },
++                      '*': function (e) {
++                              // aria defines * on numpad as open_all - not very common
++                              this.open_all();
++                      },
++                      'home': function (e) {
++                              // home
++                              e.preventDefault();
++                              var o = this._firstChild(this.get_container_ul()[0]);
++                              if(o) { $(o).children('.jstree-anchor').filter(':visible').focus(); }
++                      },
++                      'end': function (e) {
++                              // end
++                              e.preventDefault();
++                              this.element.find('.jstree-anchor').filter(':visible').last().focus();
++                      },
++                      'f2': function (e) {
++                              // f2 - safe to include - if check_callback is false it will fail
++                              e.preventDefault();
++                              this.edit(e.currentTarget);
++                      }
++              }
++      };
++      $.jstree.core.prototype = {
++              /**
++               * used to decorate an instance with a plugin. Used internally.
++               * @private
++               * @name plugin(deco [, opts])
++               * @param  {String} deco the plugin to decorate with
++               * @param  {Object} opts options for the plugin
++               * @return {jsTree}
++               */
++              plugin : function (deco, opts) {
++                      var Child = $.jstree.plugins[deco];
++                      if(Child) {
++                              this._data[deco] = {};
++                              Child.prototype = this;
++                              return new Child(opts, this);
++                      }
++                      return this;
++              },
++              /**
++               * initialize the instance. Used internally.
++               * @private
++               * @name init(el, optons)
++               * @param {DOMElement|jQuery|String} el the element we are transforming
++               * @param {Object} options options for this instance
++               * @trigger init.jstree, loading.jstree, loaded.jstree, ready.jstree, changed.jstree
++               */
++              init : function (el, options) {
++                      this._model = {
++                              data : {},
++                              changed : [],
++                              force_full_redraw : false,
++                              redraw_timeout : false,
++                              default_state : {
++                                      loaded : true,
++                                      opened : false,
++                                      selected : false,
++                                      disabled : false
++                              }
++                      };
++                      this._model.data[$.jstree.root] = {
++                              id : $.jstree.root,
++                              parent : null,
++                              parents : [],
++                              children : [],
++                              children_d : [],
++                              state : { loaded : false }
++                      };
++
++                      this.element = $(el).addClass('jstree jstree-' + this._id);
++                      this.settings = options;
++
++                      this._data.core.ready = false;
++                      this._data.core.loaded = false;
++                      this._data.core.rtl = (this.element.css("direction") === "rtl");
++                      this.element[this._data.core.rtl ? 'addClass' : 'removeClass']("jstree-rtl");
++                      this.element.attr('role','tree');
++                      if(this.settings.core.multiple) {
++                              this.element.attr('aria-multiselectable', true);
++                      }
++                      if(!this.element.attr('tabindex')) {
++                              this.element.attr('tabindex','0');
++                      }
++
++                      this.bind();
++                      /**
++                       * triggered after all events are bound
++                       * @event
++                       * @name init.jstree
++                       */
++                      this.trigger("init");
++
++                      this._data.core.original_container_html = this.element.find(" > ul > li").clone(true);
++                      this._data.core.original_container_html
++                              .find("li").addBack()
++                              .contents().filter(function() {
++                                      return this.nodeType === 3 && (!this.nodeValue || /^\s+$/.test(this.nodeValue));
++                              })
++                              .remove();
++                      this.element.html("<"+"ul class='jstree-container-ul jstree-children' role='group'><"+"li id='j"+this._id+"_loading' class='jstree-initial-node jstree-loading jstree-leaf jstree-last' role='tree-item'><i class='jstree-icon jstree-ocl'></i><"+"a class='jstree-anchor' href='#'><i class='jstree-icon jstree-themeicon-hidden'></i>" + this.get_string("Loading ...") + "</a></li></ul>");
++                      this.element.attr('aria-activedescendant','j' + this._id + '_loading');
++                      this._data.core.li_height = this.get_container_ul().children("li").first().outerHeight() || 24;
++                      this._data.core.node = this._create_prototype_node();
++                      /**
++                       * triggered after the loading text is shown and before loading starts
++                       * @event
++                       * @name loading.jstree
++                       */
++                      this.trigger("loading");
++                      this.load_node($.jstree.root);
++              },
++              /**
++               * destroy an instance
++               * @name destroy()
++               * @param  {Boolean} keep_html if not set to `true` the container will be emptied, otherwise the current DOM elements will be kept intact
++               */
++              destroy : function (keep_html) {
++                      /**
++                       * triggered before the tree is destroyed
++                       * @event
++                       * @name destroy.jstree
++                       */
++                      this.trigger("destroy");
++                      if(this._wrk) {
++                              try {
++                                      window.URL.revokeObjectURL(this._wrk);
++                                      this._wrk = null;
++                              }
++                              catch (ignore) { }
++                      }
++                      if(!keep_html) { this.element.empty(); }
++                      this.teardown();
++              },
++              /**
++               * Create a prototype node
++               * @name _create_prototype_node()
++               * @return {DOMElement}
++               */
++              _create_prototype_node : function () {
++                      var _node = document.createElement('LI'), _temp1, _temp2;
++                      _node.setAttribute('role', 'treeitem');
++                      _temp1 = document.createElement('I');
++                      _temp1.className = 'jstree-icon jstree-ocl';
++                      _temp1.setAttribute('role', 'presentation');
++                      _node.appendChild(_temp1);
++                      _temp1 = document.createElement('A');
++                      _temp1.className = 'jstree-anchor';
++                      _temp1.setAttribute('href','#');
++                      _temp1.setAttribute('tabindex','-1');
++                      _temp2 = document.createElement('I');
++                      _temp2.className = 'jstree-icon jstree-themeicon';
++                      _temp2.setAttribute('role', 'presentation');
++                      _temp1.appendChild(_temp2);
++                      _node.appendChild(_temp1);
++                      _temp1 = _temp2 = null;
++
++                      return _node;
++              },
++              _kbevent_to_func : function (e) {
++                      var keys = {
++                              8: "Backspace", 9: "Tab", 13: "Return", 19: "Pause", 27: "Esc",
++                              32: "Space", 33: "PageUp", 34: "PageDown", 35: "End", 36: "Home",
++                              37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "Print", 45: "Insert",
++                              46: "Delete", 96: "Numpad0", 97: "Numpad1", 98: "Numpad2", 99 : "Numpad3",
++                              100: "Numpad4", 101: "Numpad5", 102: "Numpad6", 103: "Numpad7",
++                              104: "Numpad8", 105: "Numpad9", '-13': "NumpadEnter", 112: "F1",
++                              113: "F2", 114: "F3", 115: "F4", 116: "F5", 117: "F6", 118: "F7",
++                              119: "F8", 120: "F9", 121: "F10", 122: "F11", 123: "F12", 144: "Numlock",
++                              145: "Scrolllock", 16: 'Shift', 17: 'Ctrl', 18: 'Alt',
++                              48: '0',  49: '1',  50: '2',  51: '3',  52: '4', 53:  '5',
++                              54: '6',  55: '7',  56: '8',  57: '9',  59: ';',  61: '=', 65:  'a',
++                              66: 'b',  67: 'c',  68: 'd',  69: 'e',  70: 'f',  71: 'g', 72:  'h',
++                              73: 'i',  74: 'j',  75: 'k',  76: 'l',  77: 'm',  78: 'n', 79:  'o',
++                              80: 'p',  81: 'q',  82: 'r',  83: 's',  84: 't',  85: 'u', 86:  'v',
++                              87: 'w',  88: 'x',  89: 'y',  90: 'z', 107: '+', 109: '-', 110: '.',
++                              186: ';', 187: '=', 188: ',', 189: '-', 190: '.', 191: '/', 192: '`',
++                              219: '[', 220: '\\',221: ']', 222: "'", 111: '/', 106: '*', 173: '-'
++                      };
++                      var parts = [];
++                      if (e.ctrlKey) { parts.push('ctrl'); }
++                      if (e.altKey) { parts.push('alt'); }
++                      if (e.shiftKey) { parts.push('shift'); }
++                      parts.push(keys[e.which] || e.which);
++                      parts = parts.sort().join('-').toLowerCase();
++
++                      var kb = this.settings.core.keyboard, i, tmp;
++                      for (i in kb) {
++                              if (kb.hasOwnProperty(i)) {
++                                      tmp = i;
++                                      if (tmp !== '-' && tmp !== '+') {
++                                              tmp = tmp.replace('--', '-MINUS').replace('+-', '-MINUS').replace('++', '-PLUS').replace('-+', '-PLUS');
++                                              tmp = tmp.split(/-|\+/).sort().join('-').replace('MINUS', '-').replace('PLUS', '+').toLowerCase();
++                                      }
++                                      if (tmp === parts) {
++                                              return kb[i];
++                                      }
++                              }
++                      }
++                      return null;
++              },
++              /**
++               * part of the destroying of an instance. Used internally.
++               * @private
++               * @name teardown()
++               */
++              teardown : function () {
++                      this.unbind();
++                      this.element
++                              .removeClass('jstree')
++                              .removeData('jstree')
++                              .find("[class^='jstree']")
++                                      .addBack()
++                                      .attr("class", function () { return this.className.replace(/jstree[^ ]*|$/ig,''); });
++                      this.element = null;
++              },
++              /**
++               * bind all events. Used internally.
++               * @private
++               * @name bind()
++               */
++              bind : function () {
++                      var word = '',
++                              tout = null,
++                              was_click = 0;
++                      this.element
++                              .on("dblclick.jstree", function (e) {
++                                              if(e.target.tagName && e.target.tagName.toLowerCase() === "input") { return true; }
++                                              if(document.selection && document.selection.empty) {
++                                                      document.selection.empty();
++                                              }
++                                              else {
++                                                      if(window.getSelection) {
++                                                              var sel = window.getSelection();
++                                                              try {
++                                                                      sel.removeAllRanges();
++                                                                      sel.collapse();
++                                                              } catch (ignore) { }
++                                                      }
++                                              }
++                                      })
++                              .on("mousedown.jstree", $.proxy(function (e) {
++                                              if(e.target === this.element[0]) {
++                                                      e.preventDefault(); // prevent losing focus when clicking scroll arrows (FF, Chrome)
++                                                      was_click = +(new Date()); // ie does not allow to prevent losing focus
++                                              }
++                                      }, this))
++                              .on("mousedown.jstree", ".jstree-ocl", function (e) {
++                                              e.preventDefault(); // prevent any node inside from losing focus when clicking the open/close icon
++                                      })
++                              .on("click.jstree", ".jstree-ocl", $.proxy(function (e) {
++                                              this.toggle_node(e.target);
++                                      }, this))
++                              .on("dblclick.jstree", ".jstree-anchor", $.proxy(function (e) {
++                                              if(e.target.tagName && e.target.tagName.toLowerCase() === "input") { return true; }
++                                              if(this.settings.core.dblclick_toggle) {
++                                                      this.toggle_node(e.target);
++                                              }
++                                      }, this))
++                              .on("click.jstree", ".jstree-anchor", $.proxy(function (e) {
++                                              e.preventDefault();
++                                              if(e.currentTarget !== document.activeElement) { $(e.currentTarget).focus(); }
++                                              this.activate_node(e.currentTarget, e);
++                                      }, this))
++                              .on('keydown.jstree', '.jstree-anchor', $.proxy(function (e) {
++                                              if(e.target.tagName && e.target.tagName.toLowerCase() === "input") { return true; }
++                                              if(this._data.core.rtl) {
++                                                      if(e.which === 37) { e.which = 39; }
++                                                      else if(e.which === 39) { e.which = 37; }
++                                              }
++                                              var f = this._kbevent_to_func(e);
++                                              if (f) {
++                                                      var r = f.call(this, e);
++                                                      if (r === false || r === true) {
++                                                              return r;
++                                                      }
++                                              }
++                                      }, this))
++                              .on("load_node.jstree", $.proxy(function (e, data) {
++                                              if(data.status) {
++                                                      if(data.node.id === $.jstree.root && !this._data.core.loaded) {
++                                                              this._data.core.loaded = true;
++                                                              if(this._firstChild(this.get_container_ul()[0])) {
++                                                                      this.element.attr('aria-activedescendant',this._firstChild(this.get_container_ul()[0]).id);
++                                                              }
++                                                              /**
++                                                               * triggered after the root node is loaded for the first time
++                                                               * @event
++                                                               * @name loaded.jstree
++                                                               */
++                                                              this.trigger("loaded");
++                                                      }
++                                                      if(!this._data.core.ready) {
++                                                              setTimeout($.proxy(function() {
++                                                                      if(this.element && !this.get_container_ul().find('.jstree-loading').length) {
++                                                                              this._data.core.ready = true;
++                                                                              if(this._data.core.selected.length) {
++                                                                                      if(this.settings.core.expand_selected_onload) {
++                                                                                              var tmp = [], i, j;
++                                                                                              for(i = 0, j = this._data.core.selected.length; i < j; i++) {
++                                                                                                      tmp = tmp.concat(this._model.data[this._data.core.selected[i]].parents);
++                                                                                              }
++                                                                                              tmp = $.vakata.array_unique(tmp);
++                                                                                              for(i = 0, j = tmp.length; i < j; i++) {
++                                                                                                      this.open_node(tmp[i], false, 0);
++                                                                                              }
++                                                                                      }
++                                                                                      this.trigger('changed', { 'action' : 'ready', 'selected' : this._data.core.selected });
++                                                                              }
++                                                                              /**
++                                                                               * triggered after all nodes are finished loading
++                                                                               * @event
++                                                                               * @name ready.jstree
++                                                                               */
++                                                                              this.trigger("ready");
++                                                                      }
++                                                              }, this), 0);
++                                                      }
++                                              }
++                                      }, this))
++                              // quick searching when the tree is focused
++                              .on('keypress.jstree', $.proxy(function (e) {
++                                              if(e.target.tagName && e.target.tagName.toLowerCase() === "input") { return true; }
++                                              if(tout) { clearTimeout(tout); }
++                                              tout = setTimeout(function () {
++                                                      word = '';
++                                              }, 500);
++
++                                              var chr = String.fromCharCode(e.which).toLowerCase(),
++                                                      col = this.element.find('.jstree-anchor').filter(':visible'),
++                                                      ind = col.index(document.activeElement) || 0,
++                                                      end = false;
++                                              word += chr;
++
++                                              // match for whole word from current node down (including the current node)
++                                              if(word.length > 1) {
++                                                      col.slice(ind).each($.proxy(function (i, v) {
++                                                              if($(v).text().toLowerCase().indexOf(word) === 0) {
++                                                                      $(v).focus();
++                                                                      end = true;
++                                                                      return false;
++                                                              }
++                                                      }, this));
++                                                      if(end) { return; }
++
++                                                      // match for whole word from the beginning of the tree
++                                                      col.slice(0, ind).each($.proxy(function (i, v) {
++                                                              if($(v).text().toLowerCase().indexOf(word) === 0) {
++                                                                      $(v).focus();
++                                                                      end = true;
++                                                                      return false;
++                                                              }
++                                                      }, this));
++                                                      if(end) { return; }
++                                              }
++                                              // list nodes that start with that letter (only if word consists of a single char)
++                                              if(new RegExp('^' + chr.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') + '+$').test(word)) {
++                                                      // search for the next node starting with that letter
++                                                      col.slice(ind + 1).each($.proxy(function (i, v) {
++                                                              if($(v).text().toLowerCase().charAt(0) === chr) {
++                                                                      $(v).focus();
++                                                                      end = true;
++                                                                      return false;
++                                                              }
++                                                      }, this));
++                                                      if(end) { return; }
++
++                                                      // search from the beginning
++                                                      col.slice(0, ind + 1).each($.proxy(function (i, v) {
++                                                              if($(v).text().toLowerCase().charAt(0) === chr) {
++                                                                      $(v).focus();
++                                                                      end = true;
++                                                                      return false;
++                                                              }
++                                                      }, this));
++                                                      if(end) { return; }
++                                              }
++                                      }, this))
++                              // THEME RELATED
++                              .on("init.jstree", $.proxy(function () {
++                                              var s = this.settings.core.themes;
++                                              this._data.core.themes.dots                     = s.dots;
++                                              this._data.core.themes.stripes          = s.stripes;
++                                              this._data.core.themes.icons            = s.icons;
++                                              this._data.core.themes.ellipsis         = s.ellipsis;
++                                              this.set_theme(s.name || "default", s.url);
++                                              this.set_theme_variant(s.variant);
++                                      }, this))
++                              .on("loading.jstree", $.proxy(function () {
++                                              this[ this._data.core.themes.dots ? "show_dots" : "hide_dots" ]();
++                                              this[ this._data.core.themes.icons ? "show_icons" : "hide_icons" ]();
++                                              this[ this._data.core.themes.stripes ? "show_stripes" : "hide_stripes" ]();
++                                              this[ this._data.core.themes.ellipsis ? "show_ellipsis" : "hide_ellipsis" ]();
++                                      }, this))
++                              .on('blur.jstree', '.jstree-anchor', $.proxy(function (e) {
++                                              this._data.core.focused = null;
++                                              $(e.currentTarget).filter('.jstree-hovered').mouseleave();
++                                              this.element.attr('tabindex', '0');
++                                      }, this))
++                              .on('focus.jstree', '.jstree-anchor', $.proxy(function (e) {
++                                              var tmp = this.get_node(e.currentTarget);
++                                              if(tmp && tmp.id) {
++                                                      this._data.core.focused = tmp.id;
++                                              }
++                                              this.element.find('.jstree-hovered').not(e.currentTarget).mouseleave();
++                                              $(e.currentTarget).mouseenter();
++                                              this.element.attr('tabindex', '-1');
++                                      }, this))
++                              .on('focus.jstree', $.proxy(function () {
++                                              if(+(new Date()) - was_click > 500 && !this._data.core.focused && this.settings.core.restore_focus) {
++                                                      was_click = 0;
++                                                      var act = this.get_node(this.element.attr('aria-activedescendant'), true);
++                                                      if(act) {
++                                                              act.find('> .jstree-anchor').focus();
++                                                      }
++                                              }
++                                      }, this))
++                              .on('mouseenter.jstree', '.jstree-anchor', $.proxy(function (e) {
++                                              this.hover_node(e.currentTarget);
++                                      }, this))
++                              .on('mouseleave.jstree', '.jstree-anchor', $.proxy(function (e) {
++                                              this.dehover_node(e.currentTarget);
++                                      }, this));
++              },
++              /**
++               * part of the destroying of an instance. Used internally.
++               * @private
++               * @name unbind()
++               */
++              unbind : function () {
++                      this.element.off('.jstree');
++                      $(document).off('.jstree-' + this._id);
++              },
++              /**
++               * trigger an event. Used internally.
++               * @private
++               * @name trigger(ev [, data])
++               * @param  {String} ev the name of the event to trigger
++               * @param  {Object} data additional data to pass with the event
++               */
++              trigger : function (ev, data) {
++                      if(!data) {
++                              data = {};
++                      }
++                      data.instance = this;
++                      this.element.triggerHandler(ev.replace('.jstree','') + '.jstree', data);
++              },
++              /**
++               * returns the jQuery extended instance container
++               * @name get_container()
++               * @return {jQuery}
++               */
++              get_container : function () {
++                      return this.element;
++              },
++              /**
++               * returns the jQuery extended main UL node inside the instance container. Used internally.
++               * @private
++               * @name get_container_ul()
++               * @return {jQuery}
++               */
++              get_container_ul : function () {
++                      return this.element.children(".jstree-children").first();
++              },
++              /**
++               * gets string replacements (localization). Used internally.
++               * @private
++               * @name get_string(key)
++               * @param  {String} key
++               * @return {String}
++               */
++              get_string : function (key) {
++                      var a = this.settings.core.strings;
++                      if($.isFunction(a)) { return a.call(this, key); }
++                      if(a && a[key]) { return a[key]; }
++                      return key;
++              },
++              /**
++               * gets the first child of a DOM node. Used internally.
++               * @private
++               * @name _firstChild(dom)
++               * @param  {DOMElement} dom
++               * @return {DOMElement}
++               */
++              _firstChild : function (dom) {
++                      dom = dom ? dom.firstChild : null;
++                      while(dom !== null && dom.nodeType !== 1) {
++                              dom = dom.nextSibling;
++                      }
++                      return dom;
++              },
++              /**
++               * gets the next sibling of a DOM node. Used internally.
++               * @private
++               * @name _nextSibling(dom)
++               * @param  {DOMElement} dom
++               * @return {DOMElement}
++               */
++              _nextSibling : function (dom) {
++                      dom = dom ? dom.nextSibling : null;
++                      while(dom !== null && dom.nodeType !== 1) {
++                              dom = dom.nextSibling;
++                      }
++                      return dom;
++              },
++              /**
++               * gets the previous sibling of a DOM node. Used internally.
++               * @private
++               * @name _previousSibling(dom)
++               * @param  {DOMElement} dom
++               * @return {DOMElement}
++               */
++              _previousSibling : function (dom) {
++                      dom = dom ? dom.previousSibling : null;
++                      while(dom !== null && dom.nodeType !== 1) {
++                              dom = dom.previousSibling;
++                      }
++                      return dom;
++              },
++              /**
++               * get the JSON representation of a node (or the actual jQuery extended DOM node) by using any input (child DOM element, ID string, selector, etc)
++               * @name get_node(obj [, as_dom])
++               * @param  {mixed} obj
++               * @param  {Boolean} as_dom
++               * @return {Object|jQuery}
++               */
++              get_node : function (obj, as_dom) {
++                      if(obj && obj.id) {
++                              obj = obj.id;
++                      }
++                      var dom;
++                      try {
++                              if(this._model.data[obj]) {
++                                      obj = this._model.data[obj];
++                              }
++                              else if(typeof obj === "string" && this._model.data[obj.replace(/^#/, '')]) {
++                                      obj = this._model.data[obj.replace(/^#/, '')];
++                              }
++                              else if(typeof obj === "string" && (dom = $('#' + obj.replace($.jstree.idregex,'\\$&'), this.element)).length && this._model.data[dom.closest('.jstree-node').attr('id')]) {
++                                      obj = this._model.data[dom.closest('.jstree-node').attr('id')];
++                              }
++                              else if((dom = $(obj, this.element)).length && this._model.data[dom.closest('.jstree-node').attr('id')]) {
++                                      obj = this._model.data[dom.closest('.jstree-node').attr('id')];
++                              }
++                              else if((dom = $(obj, this.element)).length && dom.hasClass('jstree')) {
++                                      obj = this._model.data[$.jstree.root];
++                              }
++                              else {
++                                      return false;
++                              }
++
++                              if(as_dom) {
++                                      obj = obj.id === $.jstree.root ? this.element : $('#' + obj.id.replace($.jstree.idregex,'\\$&'), this.element);
++                              }
++                              return obj;
++                      } catch (ex) { return false; }
++              },
++              /**
++               * get the path to a node, either consisting of node texts, or of node IDs, optionally glued together (otherwise an array)
++               * @name get_path(obj [, glue, ids])
++               * @param  {mixed} obj the node
++               * @param  {String} glue if you want the path as a string - pass the glue here (for example '/'), if a falsy value is supplied here, an array is returned
++               * @param  {Boolean} ids if set to true build the path using ID, otherwise node text is used
++               * @return {mixed}
++               */
++              get_path : function (obj, glue, ids) {
++                      obj = obj.parents ? obj : this.get_node(obj);
++                      if(!obj || obj.id === $.jstree.root || !obj.parents) {
++                              return false;
++                      }
++                      var i, j, p = [];
++                      p.push(ids ? obj.id : obj.text);
++                      for(i = 0, j = obj.parents.length; i < j; i++) {
++                              p.push(ids ? obj.parents[i] : this.get_text(obj.parents[i]));
++                      }
++                      p = p.reverse().slice(1);
++                      return glue ? p.join(glue) : p;
++              },
++              /**
++               * get the next visible node that is below the `obj` node. If `strict` is set to `true` only sibling nodes are returned.
++               * @name get_next_dom(obj [, strict])
++               * @param  {mixed} obj
++               * @param  {Boolean} strict
++               * @return {jQuery}
++               */
++              get_next_dom : function (obj, strict) {
++                      var tmp;
++                      obj = this.get_node(obj, true);
++                      if(obj[0] === this.element[0]) {
++                              tmp = this._firstChild(this.get_container_ul()[0]);
++                              while (tmp && tmp.offsetHeight === 0) {
++                                      tmp = this._nextSibling(tmp);
++                              }
++                              return tmp ? $(tmp) : false;
++                      }
++                      if(!obj || !obj.length) {
++                              return false;
++                      }
++                      if(strict) {
++                              tmp = obj[0];
++                              do {
++                                      tmp = this._nextSibling(tmp);
++                              } while (tmp && tmp.offsetHeight === 0);
++                              return tmp ? $(tmp) : false;
++                      }
++                      if(obj.hasClass("jstree-open")) {
++                              tmp = this._firstChild(obj.children('.jstree-children')[0]);
++                              while (tmp && tmp.offsetHeight === 0) {
++                                      tmp = this._nextSibling(tmp);
++                              }
++                              if(tmp !== null) {
++                                      return $(tmp);
++                              }
++                      }
++                      tmp = obj[0];
++                      do {
++                              tmp = this._nextSibling(tmp);
++                      } while (tmp && tmp.offsetHeight === 0);
++                      if(tmp !== null) {
++                              return $(tmp);
++                      }
++                      return obj.parentsUntil(".jstree",".jstree-node").nextAll(".jstree-node:visible").first();
++              },
++              /**
++               * get the previous visible node that is above the `obj` node. If `strict` is set to `true` only sibling nodes are returned.
++               * @name get_prev_dom(obj [, strict])
++               * @param  {mixed} obj
++               * @param  {Boolean} strict
++               * @return {jQuery}
++               */
++              get_prev_dom : function (obj, strict) {
++                      var tmp;
++                      obj = this.get_node(obj, true);
++                      if(obj[0] === this.element[0]) {
++                              tmp = this.get_container_ul()[0].lastChild;
++                              while (tmp && tmp.offsetHeight === 0) {
++                                      tmp = this._previousSibling(tmp);
++                              }
++                              return tmp ? $(tmp) : false;
++                      }
++                      if(!obj || !obj.length) {
++                              return false;
++                      }
++                      if(strict) {
++                              tmp = obj[0];
++                              do {
++                                      tmp = this._previousSibling(tmp);
++                              } while (tmp && tmp.offsetHeight === 0);
++                              return tmp ? $(tmp) : false;
++                      }
++                      tmp = obj[0];
++                      do {
++                              tmp = this._previousSibling(tmp);
++                      } while (tmp && tmp.offsetHeight === 0);
++                      if(tmp !== null) {
++                              obj = $(tmp);
++                              while(obj.hasClass("jstree-open")) {
++                                      obj = obj.children(".jstree-children").first().children(".jstree-node:visible:last");
++                              }
++                              return obj;
++                      }
++                      tmp = obj[0].parentNode.parentNode;
++                      return tmp && tmp.className && tmp.className.indexOf('jstree-node') !== -1 ? $(tmp) : false;
++              },
++              /**
++               * get the parent ID of a node
++               * @name get_parent(obj)
++               * @param  {mixed} obj
++               * @return {String}
++               */
++              get_parent : function (obj) {
++                      obj = this.get_node(obj);
++                      if(!obj || obj.id === $.jstree.root) {
++                              return false;
++                      }
++                      return obj.parent;
++              },
++              /**
++               * get a jQuery collection of all the children of a node (node must be rendered), returns false on error
++               * @name get_children_dom(obj)
++               * @param  {mixed} obj
++               * @return {jQuery}
++               */
++              get_children_dom : function (obj) {
++                      obj = this.get_node(obj, true);
++                      if(obj[0] === this.element[0]) {
++                              return this.get_container_ul().children(".jstree-node");
++                      }
++                      if(!obj || !obj.length) {
++                              return false;
++                      }
++                      return obj.children(".jstree-children").children(".jstree-node");
++              },
++              /**
++               * checks if a node has children
++               * @name is_parent(obj)
++               * @param  {mixed} obj
++               * @return {Boolean}
++               */
++              is_parent : function (obj) {
++                      obj = this.get_node(obj);
++                      return obj && (obj.state.loaded === false || obj.children.length > 0);
++              },
++              /**
++               * checks if a node is loaded (its children are available)
++               * @name is_loaded(obj)
++               * @param  {mixed} obj
++               * @return {Boolean}
++               */
++              is_loaded : function (obj) {
++                      obj = this.get_node(obj);
++                      return obj && obj.state.loaded;
++              },
++              /**
++               * check if a node is currently loading (fetching children)
++               * @name is_loading(obj)
++               * @param  {mixed} obj
++               * @return {Boolean}
++               */
++              is_loading : function (obj) {
++                      obj = this.get_node(obj);
++                      return obj && obj.state && obj.state.loading;
++              },
++              /**
++               * check if a node is opened
++               * @name is_open(obj)
++               * @param  {mixed} obj
++               * @return {Boolean}
++               */
++              is_open : function (obj) {
++                      obj = this.get_node(obj);
++                      return obj && obj.state.opened;
++              },
++              /**
++               * check if a node is in a closed state
++               * @name is_closed(obj)
++               * @param  {mixed} obj
++               * @return {Boolean}
++               */
++              is_closed : function (obj) {
++                      obj = this.get_node(obj);
++                      return obj && this.is_parent(obj) && !obj.state.opened;
++              },
++              /**
++               * check if a node has no children
++               * @name is_leaf(obj)
++               * @param  {mixed} obj
++               * @return {Boolean}
++               */
++              is_leaf : function (obj) {
++                      return !this.is_parent(obj);
++              },
++              /**
++               * loads a node (fetches its children using the `core.data` setting). Multiple nodes can be passed to by using an array.
++               * @name load_node(obj [, callback])
++               * @param  {mixed} obj
++               * @param  {function} callback a function to be executed once loading is complete, the function is executed in the instance's scope and receives two arguments - the node and a boolean status
++               * @return {Boolean}
++               * @trigger load_node.jstree
++               */
++              load_node : function (obj, callback) {
++                      var k, l, i, j, c;
++                      if($.isArray(obj)) {
++                              this._load_nodes(obj.slice(), callback);
++                              return true;
++                      }
++                      obj = this.get_node(obj);
++                      if(!obj) {
++                              if(callback) { callback.call(this, obj, false); }
++                              return false;
++                      }
++                      // if(obj.state.loading) { } // the node is already loading - just wait for it to load and invoke callback? but if called implicitly it should be loaded again?
++                      if(obj.state.loaded) {
++                              obj.state.loaded = false;
++                              for(i = 0, j = obj.parents.length; i < j; i++) {
++                                      this._model.data[obj.parents[i]].children_d = $.vakata.array_filter(this._model.data[obj.parents[i]].children_d, function (v) {
++                                              return $.inArray(v, obj.children_d) === -1;
++                                      });
++                              }
++                              for(k = 0, l = obj.children_d.length; k < l; k++) {
++                                      if(this._model.data[obj.children_d[k]].state.selected) {
++                                              c = true;
++                                      }
++                                      delete this._model.data[obj.children_d[k]];
++                              }
++                              if (c) {
++                                      this._data.core.selected = $.vakata.array_filter(this._data.core.selected, function (v) {
++                                              return $.inArray(v, obj.children_d) === -1;
++                                      });
++                              }
++                              obj.children = [];
++                              obj.children_d = [];
++                              if(c) {
++                                      this.trigger('changed', { 'action' : 'load_node', 'node' : obj, 'selected' : this._data.core.selected });
++                              }
++                      }
++                      obj.state.failed = false;
++                      obj.state.loading = true;
++                      this.get_node(obj, true).addClass("jstree-loading").attr('aria-busy',true);
++                      this._load_node(obj, $.proxy(function (status) {
++                              obj = this._model.data[obj.id];
++                              obj.state.loading = false;
++                              obj.state.loaded = status;
++                              obj.state.failed = !obj.state.loaded;
++                              var dom = this.get_node(obj, true), i = 0, j = 0, m = this._model.data, has_children = false;
++                              for(i = 0, j = obj.children.length; i < j; i++) {
++                                      if(m[obj.children[i]] && !m[obj.children[i]].state.hidden) {
++                                              has_children = true;
++                                              break;
++                                      }
++                              }
++                              if(obj.state.loaded && dom && dom.length) {
++                                      dom.removeClass('jstree-closed jstree-open jstree-leaf');
++                                      if (!has_children) {
++                                              dom.addClass('jstree-leaf');
++                                      }
++                                      else {
++                                              if (obj.id !== '#') {
++                                                      dom.addClass(obj.state.opened ? 'jstree-open' : 'jstree-closed');
++                                              }
++                                      }
++                              }
++                              dom.removeClass("jstree-loading").attr('aria-busy',false);
++                              /**
++                               * triggered after a node is loaded
++                               * @event
++                               * @name load_node.jstree
++                               * @param {Object} node the node that was loading
++                               * @param {Boolean} status was the node loaded successfully
++                               */
++                              this.trigger('load_node', { "node" : obj, "status" : status });
++                              if(callback) {
++                                      callback.call(this, obj, status);
++                              }
++                      }, this));
++                      return true;
++              },
++              /**
++               * load an array of nodes (will also load unavailable nodes as soon as the appear in the structure). Used internally.
++               * @private
++               * @name _load_nodes(nodes [, callback])
++               * @param  {array} nodes
++               * @param  {function} callback a function to be executed once loading is complete, the function is executed in the instance's scope and receives one argument - the array passed to _load_nodes
++               */
++              _load_nodes : function (nodes, callback, is_callback, force_reload) {
++                      var r = true,
++                              c = function () { this._load_nodes(nodes, callback, true); },
++                              m = this._model.data, i, j, tmp = [];
++                      for(i = 0, j = nodes.length; i < j; i++) {
++                              if(m[nodes[i]] && ( (!m[nodes[i]].state.loaded && !m[nodes[i]].state.failed) || (!is_callback && force_reload) )) {
++                                      if(!this.is_loading(nodes[i])) {
++                                              this.load_node(nodes[i], c);
++                                      }
++                                      r = false;
++                              }
++                      }
++                      if(r) {
++                              for(i = 0, j = nodes.length; i < j; i++) {
++                                      if(m[nodes[i]] && m[nodes[i]].state.loaded) {
++                                              tmp.push(nodes[i]);
++                                      }
++                              }
++                              if(callback && !callback.done) {
++                                      callback.call(this, tmp);
++                                      callback.done = true;
++                              }
++                      }
++              },
++              /**
++               * loads all unloaded nodes
++               * @name load_all([obj, callback])
++               * @param {mixed} obj the node to load recursively, omit to load all nodes in the tree
++               * @param {function} callback a function to be executed once loading all the nodes is complete,
++               * @trigger load_all.jstree
++               */
++              load_all : function (obj, callback) {
++                      if(!obj) { obj = $.jstree.root; }
++                      obj = this.get_node(obj);
++                      if(!obj) { return false; }
++                      var to_load = [],
++                              m = this._model.data,
++                              c = m[obj.id].children_d,
++                              i, j;
++                      if(obj.state && !obj.state.loaded) {
++                              to_load.push(obj.id);
++                      }
++                      for(i = 0, j = c.length; i < j; i++) {
++                              if(m[c[i]] && m[c[i]].state && !m[c[i]].state.loaded) {
++                                      to_load.push(c[i]);
++                              }
++                      }
++                      if(to_load.length) {
++                              this._load_nodes(to_load, function () {
++                                      this.load_all(obj, callback);
++                              });
++                      }
++                      else {
++                              /**
++                               * triggered after a load_all call completes
++                               * @event
++                               * @name load_all.jstree
++                               * @param {Object} node the recursively loaded node
++                               */
++                              if(callback) { callback.call(this, obj); }
++                              this.trigger('load_all', { "node" : obj });
++                      }
++              },
++              /**
++               * handles the actual loading of a node. Used only internally.
++               * @private
++               * @name _load_node(obj [, callback])
++               * @param  {mixed} obj
++               * @param  {function} callback a function to be executed once loading is complete, the function is executed in the instance's scope and receives one argument - a boolean status
++               * @return {Boolean}
++               */
++              _load_node : function (obj, callback) {
++                      var s = this.settings.core.data, t;
++                      var notTextOrCommentNode = function notTextOrCommentNode () {
++                              return this.nodeType !== 3 && this.nodeType !== 8;
++                      };
++                      // use original HTML
++                      if(!s) {
++                              if(obj.id === $.jstree.root) {
++                                      return this._append_html_data(obj, this._data.core.original_container_html.clone(true), function (status) {
++                                              callback.call(this, status);
++                                      });
++                              }
++                              else {
++                                      return callback.call(this, false);
++                              }
++                              // return callback.call(this, obj.id === $.jstree.root ? this._append_html_data(obj, this._data.core.original_container_html.clone(true)) : false);
++                      }
++                      if($.isFunction(s)) {
++                              return s.call(this, obj, $.proxy(function (d) {
++                                      if(d === false) {
++                                              callback.call(this, false);
++                                      }
++                                      else {
++                                              this[typeof d === 'string' ? '_append_html_data' : '_append_json_data'](obj, typeof d === 'string' ? $($.parseHTML(d)).filter(notTextOrCommentNode) : d, function (status) {
++                                                      callback.call(this, status);
++                                              });
++                                      }
++                                      // return d === false ? callback.call(this, false) : callback.call(this, this[typeof d === 'string' ? '_append_html_data' : '_append_json_data'](obj, typeof d === 'string' ? $(d) : d));
++                              }, this));
++                      }
++                      if(typeof s === 'object') {
++                              if(s.url) {
++                                      s = $.extend(true, {}, s);
++                                      if($.isFunction(s.url)) {
++                                              s.url = s.url.call(this, obj);
++                                      }
++                                      if($.isFunction(s.data)) {
++                                              s.data = s.data.call(this, obj);
++                                      }
++                                      return $.ajax(s)
++                                              .done($.proxy(function (d,t,x) {
++                                                              var type = x.getResponseHeader('Content-Type');
++                                                              if((type && type.indexOf('json') !== -1) || typeof d === "object") {
++                                                                      return this._append_json_data(obj, d, function (status) { callback.call(this, status); });
++                                                                      //return callback.call(this, this._append_json_data(obj, d));
++                                                              }
++                                                              if((type && type.indexOf('html') !== -1) || typeof d === "string") {
++                                                                      return this._append_html_data(obj, $($.parseHTML(d)).filter(notTextOrCommentNode), function (status) { callback.call(this, status); });
++                                                                      // return callback.call(this, this._append_html_data(obj, $(d)));
++                                                              }
++                                                              this._data.core.last_error = { 'error' : 'ajax', 'plugin' : 'core', 'id' : 'core_04', 'reason' : 'Could not load node', 'data' : JSON.stringify({ 'id' : obj.id, 'xhr' : x }) };
++                                                              this.settings.core.error.call(this, this._data.core.last_error);
++                                                              return callback.call(this, false);
++                                                      }, this))
++                                              .fail($.proxy(function (f) {
++                                                              this._data.core.last_error = { 'error' : 'ajax', 'plugin' : 'core', 'id' : 'core_04', 'reason' : 'Could not load node', 'data' : JSON.stringify({ 'id' : obj.id, 'xhr' : f }) };
++                                                              callback.call(this, false);
++                                                              this.settings.core.error.call(this, this._data.core.last_error);
++                                                      }, this));
++                              }
++                              if ($.isArray(s)) {
++                                      t = $.extend(true, [], s);
++                              } else if ($.isPlainObject(s)) {
++                                      t = $.extend(true, {}, s);
++                              } else {
++                                      t = s;
++                              }
++                              if(obj.id === $.jstree.root) {
++                                      return this._append_json_data(obj, t, function (status) {
++                                              callback.call(this, status);
++                                      });
++                              }
++                              else {
++                                      this._data.core.last_error = { 'error' : 'nodata', 'plugin' : 'core', 'id' : 'core_05', 'reason' : 'Could not load node', 'data' : JSON.stringify({ 'id' : obj.id }) };
++                                      this.settings.core.error.call(this, this._data.core.last_error);
++                                      return callback.call(this, false);
++                              }
++                              //return callback.call(this, (obj.id === $.jstree.root ? this._append_json_data(obj, t) : false) );
++                      }
++                      if(typeof s === 'string') {
++                              if(obj.id === $.jstree.root) {
++                                      return this._append_html_data(obj, $($.parseHTML(s)).filter(notTextOrCommentNode), function (status) {
++                                              callback.call(this, status);
++                                      });
++                              }
++                              else {
++                                      this._data.core.last_error = { 'error' : 'nodata', 'plugin' : 'core', 'id' : 'core_06', 'reason' : 'Could not load node', 'data' : JSON.stringify({ 'id' : obj.id }) };
++                                      this.settings.core.error.call(this, this._data.core.last_error);
++                                      return callback.call(this, false);
++                              }
++                              //return callback.call(this, (obj.id === $.jstree.root ? this._append_html_data(obj, $(s)) : false) );
++                      }
++                      return callback.call(this, false);
++              },
++              /**
++               * adds a node to the list of nodes to redraw. Used only internally.
++               * @private
++               * @name _node_changed(obj [, callback])
++               * @param  {mixed} obj
++               */
++              _node_changed : function (obj) {
++                      obj = this.get_node(obj);
++      if (obj && $.inArray(obj.id, this._model.changed) === -1) {
++                              this._model.changed.push(obj.id);
++                      }
++              },
++              /**
++               * appends HTML content to the tree. Used internally.
++               * @private
++               * @name _append_html_data(obj, data)
++               * @param  {mixed} obj the node to append to
++               * @param  {String} data the HTML string to parse and append
++               * @trigger model.jstree, changed.jstree
++               */
++              _append_html_data : function (dom, data, cb) {
++                      dom = this.get_node(dom);
++                      dom.children = [];
++                      dom.children_d = [];
++                      var dat = data.is('ul') ? data.children() : data,
++                              par = dom.id,
++                              chd = [],
++                              dpc = [],
++                              m = this._model.data,
++                              p = m[par],
++                              s = this._data.core.selected.length,
++                              tmp, i, j;
++                      dat.each($.proxy(function (i, v) {
++                              tmp = this._parse_model_from_html($(v), par, p.parents.concat());
++                              if(tmp) {
++                                      chd.push(tmp);
++                                      dpc.push(tmp);
++                                      if(m[tmp].children_d.length) {
++                                              dpc = dpc.concat(m[tmp].children_d);
++                                      }
++                              }
++                      }, this));
++                      p.children = chd;
++                      p.children_d = dpc;
++                      for(i = 0, j = p.parents.length; i < j; i++) {
++                              m[p.parents[i]].children_d = m[p.parents[i]].children_d.concat(dpc);
++                      }
++                      /**
++                       * triggered when new data is inserted to the tree model
++                       * @event
++                       * @name model.jstree
++                       * @param {Array} nodes an array of node IDs
++                       * @param {String} parent the parent ID of the nodes
++                       */
++                      this.trigger('model', { "nodes" : dpc, 'parent' : par });
++                      if(par !== $.jstree.root) {
++                              this._node_changed(par);
++                              this.redraw();
++                      }
++                      else {
++                              this.get_container_ul().children('.jstree-initial-node').remove();
++                              this.redraw(true);
++                      }
++                      if(this._data.core.selected.length !== s) {
++                              this.trigger('changed', { 'action' : 'model', 'selected' : this._data.core.selected });
++                      }
++                      cb.call(this, true);
++              },
++              /**
++               * appends JSON content to the tree. Used internally.
++               * @private
++               * @name _append_json_data(obj, data)
++               * @param  {mixed} obj the node to append to
++               * @param  {String} data the JSON object to parse and append
++               * @param  {Boolean} force_processing internal param - do not set
++               * @trigger model.jstree, changed.jstree
++               */
++              _append_json_data : function (dom, data, cb, force_processing) {
++                      if(this.element === null) { return; }
++                      dom = this.get_node(dom);
++                      dom.children = [];
++                      dom.children_d = [];
++                      // *%$@!!!
++                      if(data.d) {
++                              data = data.d;
++                              if(typeof data === "string") {
++                                      data = JSON.parse(data);
++                              }
++                      }
++                      if(!$.isArray(data)) { data = [data]; }
++                      var w = null,
++                              args = {
++                                      'df'    : this._model.default_state,
++                                      'dat'   : data,
++                                      'par'   : dom.id,
++                                      'm'             : this._model.data,
++                                      't_id'  : this._id,
++                                      't_cnt' : this._cnt,
++                                      'sel'   : this._data.core.selected
++                              },
++                              func = function (data, undefined) {
++                                      if(data.data) { data = data.data; }
++                                      var dat = data.dat,
++                                              par = data.par,
++                                              chd = [],
++                                              dpc = [],
++                                              add = [],
++                                              df = data.df,
++                                              t_id = data.t_id,
++                                              t_cnt = data.t_cnt,
++                                              m = data.m,
++                                              p = m[par],
++                                              sel = data.sel,
++                                              tmp, i, j, rslt,
++                                              parse_flat = function (d, p, ps) {
++                                                      if(!ps) { ps = []; }
++                                                      else { ps = ps.concat(); }
++                                                      if(p) { ps.unshift(p); }
++                                                      var tid = d.id.toString(),
++                                                              i, j, c, e,
++                                                              tmp = {
++                                                                      id                      : tid,
++                                                                      text            : d.text || '',
++                                                                      icon            : d.icon !== undefined ? d.icon : true,
++                                                                      parent          : p,
++                                                                      parents         : ps,
++                                                                      children        : d.children || [],
++                                                                      children_d      : d.children_d || [],
++                                                                      data            : d.data,
++                                                                      state           : { },
++                                                                      li_attr         : { id : false },
++                                                                      a_attr          : { href : '#' },
++                                                                      original        : false
++                                                              };
++                                                      for(i in df) {
++                                                              if(df.hasOwnProperty(i)) {
++                                                                      tmp.state[i] = df[i];
++                                                              }
++                                                      }
++                                                      if(d && d.data && d.data.jstree && d.data.jstree.icon) {
++                                                              tmp.icon = d.data.jstree.icon;
++                                                      }
++                                                      if(tmp.icon === undefined || tmp.icon === null || tmp.icon === "") {
++                                                              tmp.icon = true;
++                                                      }
++                                                      if(d && d.data) {
++                                                              tmp.data = d.data;
++                                                              if(d.data.jstree) {
++                                                                      for(i in d.data.jstree) {
++                                                                              if(d.data.jstree.hasOwnProperty(i)) {
++                                                                                      tmp.state[i] = d.data.jstree[i];
++                                                                              }
++                                                                      }
++                                                              }
++                                                      }
++                                                      if(d && typeof d.state === 'object') {
++                                                              for (i in d.state) {
++                                                                      if(d.state.hasOwnProperty(i)) {
++                                                                              tmp.state[i] = d.state[i];
++                                                                      }
++                                                              }
++                                                      }
++                                                      if(d && typeof d.li_attr === 'object') {
++                                                              for (i in d.li_attr) {
++                                                                      if(d.li_attr.hasOwnProperty(i)) {
++                                                                              tmp.li_attr[i] = d.li_attr[i];
++                                                                      }
++                                                              }
++                                                      }
++                                                      if(!tmp.li_attr.id) {
++                                                              tmp.li_attr.id = tid;
++                                                      }
++                                                      if(d && typeof d.a_attr === 'object') {
++                                                              for (i in d.a_attr) {
++                                                                      if(d.a_attr.hasOwnProperty(i)) {
++                                                                              tmp.a_attr[i] = d.a_attr[i];
++                                                                      }
++                                                              }
++                                                      }
++                                                      if(d && d.children && d.children === true) {
++                                                              tmp.state.loaded = false;
++                                                              tmp.children = [];
++                                                              tmp.children_d = [];
++                                                      }
++                                                      m[tmp.id] = tmp;
++                                                      for(i = 0, j = tmp.children.length; i < j; i++) {
++                                                              c = parse_flat(m[tmp.children[i]], tmp.id, ps);
++                                                              e = m[c];
++                                                              tmp.children_d.push(c);
++                                                              if(e.children_d.length) {
++                                                                      tmp.children_d = tmp.children_d.concat(e.children_d);
++                                                              }
++                                                      }
++                                                      delete d.data;
++                                                      delete d.children;
++                                                      m[tmp.id].original = d;
++                                                      if(tmp.state.selected) {
++                                                              add.push(tmp.id);
++                                                      }
++                                                      return tmp.id;
++                                              },
++                                              parse_nest = function (d, p, ps) {
++                                                      if(!ps) { ps = []; }
++                                                      else { ps = ps.concat(); }
++                                                      if(p) { ps.unshift(p); }
++                                                      var tid = false, i, j, c, e, tmp;
++                                                      do {
++                                                              tid = 'j' + t_id + '_' + (++t_cnt);
++                                                      } while(m[tid]);
++
++                                                      tmp = {
++                                                              id                      : false,
++                                                              text            : typeof d === 'string' ? d : '',
++                                                              icon            : typeof d === 'object' && d.icon !== undefined ? d.icon : true,
++                                                              parent          : p,
++                                                              parents         : ps,
++                                                              children        : [],
++                                                              children_d      : [],
++                                                              data            : null,
++                                                              state           : { },
++                                                              li_attr         : { id : false },
++                                                              a_attr          : { href : '#' },
++                                                              original        : false
++                                                      };
++                                                      for(i in df) {
++                                                              if(df.hasOwnProperty(i)) {
++                                                                      tmp.state[i] = df[i];
++                                                              }
++                                                      }
++                                                      if(d && d.id) { tmp.id = d.id.toString(); }
++                                                      if(d && d.text) { tmp.text = d.text; }
++                                                      if(d && d.data && d.data.jstree && d.data.jstree.icon) {
++                                                              tmp.icon = d.data.jstree.icon;
++                                                      }
++                                                      if(tmp.icon === undefined || tmp.icon === null || tmp.icon === "") {
++                                                              tmp.icon = true;
++                                                      }
++                                                      if(d && d.data) {
++                                                              tmp.data = d.data;
++                                                              if(d.data.jstree) {
++                                                                      for(i in d.data.jstree) {
++                                                                              if(d.data.jstree.hasOwnProperty(i)) {
++                                                                                      tmp.state[i] = d.data.jstree[i];
++                                                                              }
++                                                                      }
++                                                              }
++                                                      }
++                                                      if(d && typeof d.state === 'object') {
++                                                              for (i in d.state) {
++                                                                      if(d.state.hasOwnProperty(i)) {
++                                                                              tmp.state[i] = d.state[i];
++                                                                      }
++                                                              }
++                                                      }
++                                                      if(d && typeof d.li_attr === 'object') {
++                                                              for (i in d.li_attr) {
++                                                                      if(d.li_attr.hasOwnProperty(i)) {
++                                                                              tmp.li_attr[i] = d.li_attr[i];
++                                                                      }
++                                                              }
++                                                      }
++                                                      if(tmp.li_attr.id && !tmp.id) {
++                                                              tmp.id = tmp.li_attr.id.toString();
++                                                      }
++                                                      if(!tmp.id) {
++                                                              tmp.id = tid;
++                                                      }
++                                                      if(!tmp.li_attr.id) {
++                                                              tmp.li_attr.id = tmp.id;
++                                                      }
++                                                      if(d && typeof d.a_attr === 'object') {
++                                                              for (i in d.a_attr) {
++                                                                      if(d.a_attr.hasOwnProperty(i)) {
++                                                                              tmp.a_attr[i] = d.a_attr[i];
++                                                                      }
++                                                              }
++                                                      }
++                                                      if(d && d.children && d.children.length) {
++                                                              for(i = 0, j = d.children.length; i < j; i++) {
++                                                                      c = parse_nest(d.children[i], tmp.id, ps);
++                                                                      e = m[c];
++                                                                      tmp.children.push(c);
++                                                                      if(e.children_d.length) {
++                                                                              tmp.children_d = tmp.children_d.concat(e.children_d);
++                                                                      }
++                                                              }
++                                                              tmp.children_d = tmp.children_d.concat(tmp.children);
++                                                      }
++                                                      if(d && d.children && d.children === true) {
++                                                              tmp.state.loaded = false;
++                                                              tmp.children = [];
++                                                              tmp.children_d = [];
++                                                      }
++                                                      delete d.data;
++                                                      delete d.children;
++                                                      tmp.original = d;
++                                                      m[tmp.id] = tmp;
++                                                      if(tmp.state.selected) {
++                                                              add.push(tmp.id);
++                                                      }
++                                                      return tmp.id;
++                                              };
++
++                                      if(dat.length && dat[0].id !== undefined && dat[0].parent !== undefined) {
++                                              // Flat JSON support (for easy import from DB):
++                                              // 1) convert to object (foreach)
++                                              for(i = 0, j = dat.length; i < j; i++) {
++                                                      if(!dat[i].children) {
++                                                              dat[i].children = [];
++                                                      }
++                                                      if(!dat[i].state) {
++                                                              dat[i].state = {};
++                                                      }
++                                                      m[dat[i].id.toString()] = dat[i];
++                                              }
++                                              // 2) populate children (foreach)
++                                              for(i = 0, j = dat.length; i < j; i++) {
++                                                      if (!m[dat[i].parent.toString()]) {
++                                                              this._data.core.last_error = { 'error' : 'parse', 'plugin' : 'core', 'id' : 'core_07', 'reason' : 'Node with invalid parent', 'data' : JSON.stringify({ 'id' : dat[i].id.toString(), 'parent' : dat[i].parent.toString() }) };
++                                                              this.settings.core.error.call(this, this._data.core.last_error);
++                                                              continue;
++                                                      }
++
++                                                      m[dat[i].parent.toString()].children.push(dat[i].id.toString());
++                                                      // populate parent.children_d
++                                                      p.children_d.push(dat[i].id.toString());
++                                              }
++                                              // 3) normalize && populate parents and children_d with recursion
++                                              for(i = 0, j = p.children.length; i < j; i++) {
++                                                      tmp = parse_flat(m[p.children[i]], par, p.parents.concat());
++                                                      dpc.push(tmp);
++                                                      if(m[tmp].children_d.length) {
++                                                              dpc = dpc.concat(m[tmp].children_d);
++                                                      }
++                                              }
++                                              for(i = 0, j = p.parents.length; i < j; i++) {
++                                                      m[p.parents[i]].children_d = m[p.parents[i]].children_d.concat(dpc);
++                                              }
++                                              // ?) three_state selection - p.state.selected && t - (if three_state foreach(dat => ch) -> foreach(parents) if(parent.selected) child.selected = true;
++                                              rslt = {
++                                                      'cnt' : t_cnt,
++                                                      'mod' : m,
++                                                      'sel' : sel,
++                                                      'par' : par,
++                                                      'dpc' : dpc,
++                                                      'add' : add
++                                              };
++                                      }
++                                      else {
++                                              for(i = 0, j = dat.length; i < j; i++) {
++                                                      tmp = parse_nest(dat[i], par, p.parents.concat());
++                                                      if(tmp) {
++                                                              chd.push(tmp);
++                                                              dpc.push(tmp);
++                                                              if(m[tmp].children_d.length) {
++                                                                      dpc = dpc.concat(m[tmp].children_d);
++                                                              }
++                                                      }
++                                              }
++                                              p.children = chd;
++                                              p.children_d = dpc;
++                                              for(i = 0, j = p.parents.length; i < j; i++) {
++                                                      m[p.parents[i]].children_d = m[p.parents[i]].children_d.concat(dpc);
++                                              }
++                                              rslt = {
++                                                      'cnt' : t_cnt,
++                                                      'mod' : m,
++                                                      'sel' : sel,
++                                                      'par' : par,
++                                                      'dpc' : dpc,
++                                                      'add' : add
++                                              };
++                                      }
++                                      if(typeof window === 'undefined' || typeof window.document === 'undefined') {
++                                              postMessage(rslt);
++                                      }
++                                      else {
++                                              return rslt;
++                                      }
++                              },
++                              rslt = function (rslt, worker) {
++                                      if(this.element === null) { return; }
++                                      this._cnt = rslt.cnt;
++                                      var i, m = this._model.data;
++                                      for (i in m) {
++                                              if (m.hasOwnProperty(i) && m[i].state && m[i].state.loading && rslt.mod[i]) {
++                                                      rslt.mod[i].state.loading = true;
++                                              }
++                                      }
++                                      this._model.data = rslt.mod; // breaks the reference in load_node - careful
++
++                                      if(worker) {
++                                              var j, a = rslt.add, r = rslt.sel, s = this._data.core.selected.slice();
++                                              m = this._model.data;
++                                              // if selection was changed while calculating in worker
++                                              if(r.length !== s.length || $.vakata.array_unique(r.concat(s)).length !== r.length) {
++                                                      // deselect nodes that are no longer selected
++                                                      for(i = 0, j = r.length; i < j; i++) {
++                                                              if($.inArray(r[i], a) === -1 && $.inArray(r[i], s) === -1) {
++                                                                      m[r[i]].state.selected = false;
++                                                              }
++                                                      }
++                                                      // select nodes that were selected in the mean time
++                                                      for(i = 0, j = s.length; i < j; i++) {
++                                                              if($.inArray(s[i], r) === -1) {
++                                                                      m[s[i]].state.selected = true;
++                                                              }
++                                                      }
++                                              }
++                                      }
++                                      if(rslt.add.length) {
++                                              this._data.core.selected = this._data.core.selected.concat(rslt.add);
++                                      }
++
++                                      this.trigger('model', { "nodes" : rslt.dpc, 'parent' : rslt.par });
++
++                                      if(rslt.par !== $.jstree.root) {
++                                              this._node_changed(rslt.par);
++                                              this.redraw();
++                                      }
++                                      else {
++                                              // this.get_container_ul().children('.jstree-initial-node').remove();
++                                              this.redraw(true);
++                                      }
++                                      if(rslt.add.length) {
++                                              this.trigger('changed', { 'action' : 'model', 'selected' : this._data.core.selected });
++                                      }
++                                      cb.call(this, true);
++                              };
++                      if(this.settings.core.worker && window.Blob && window.URL && window.Worker) {
++                              try {
++                                      if(this._wrk === null) {
++                                              this._wrk = window.URL.createObjectURL(
++                                                      new window.Blob(
++                                                              ['self.onmessage = ' + func.toString()],
++                                                              {type:"text/javascript"}
++                                                      )
++                                              );
++                                      }
++                                      if(!this._data.core.working || force_processing) {
++                                              this._data.core.working = true;
++                                              w = new window.Worker(this._wrk);
++                                              w.onmessage = $.proxy(function (e) {
++                                                      rslt.call(this, e.data, true);
++                                                      try { w.terminate(); w = null; } catch(ignore) { }
++                                                      if(this._data.core.worker_queue.length) {
++                                                              this._append_json_data.apply(this, this._data.core.worker_queue.shift());
++                                                      }
++                                                      else {
++                                                              this._data.core.working = false;
++                                                      }
++                                              }, this);
++                                              if(!args.par) {
++                                                      if(this._data.core.worker_queue.length) {
++                                                              this._append_json_data.apply(this, this._data.core.worker_queue.shift());
++                                                      }
++                                                      else {
++                                                              this._data.core.working = false;
++                                                      }
++                                              }
++                                              else {
++                                                      w.postMessage(args);
++                                              }
++                                      }
++                                      else {
++                                              this._data.core.worker_queue.push([dom, data, cb, true]);
++                                      }
++                              }
++                              catch(e) {
++                                      rslt.call(this, func(args), false);
++                                      if(this._data.core.worker_queue.length) {
++                                              this._append_json_data.apply(this, this._data.core.worker_queue.shift());
++                                      }
++                                      else {
++                                              this._data.core.working = false;
++                                      }
++                              }
++                      }
++                      else {
++                              rslt.call(this, func(args), false);
++                      }
++              },
++              /**
++               * parses a node from a jQuery object and appends them to the in memory tree model. Used internally.
++               * @private
++               * @name _parse_model_from_html(d [, p, ps])
++               * @param  {jQuery} d the jQuery object to parse
++               * @param  {String} p the parent ID
++               * @param  {Array} ps list of all parents
++               * @return {String} the ID of the object added to the model
++               */
++              _parse_model_from_html : function (d, p, ps) {
++                      if(!ps) { ps = []; }
++                      else { ps = [].concat(ps); }
++                      if(p) { ps.unshift(p); }
++                      var c, e, m = this._model.data,
++                              data = {
++                                      id                      : false,
++                                      text            : false,
++                                      icon            : true,
++                                      parent          : p,
++                                      parents         : ps,
++                                      children        : [],
++                                      children_d      : [],
++                                      data            : null,
++                                      state           : { },
++                                      li_attr         : { id : false },
++                                      a_attr          : { href : '#' },
++                                      original        : false
++                              }, i, tmp, tid;
++                      for(i in this._model.default_state) {
++                              if(this._model.default_state.hasOwnProperty(i)) {
++                                      data.state[i] = this._model.default_state[i];
++                              }
++                      }
++                      tmp = $.vakata.attributes(d, true);
++                      $.each(tmp, function (i, v) {
++                              v = $.trim(v);
++                              if(!v.length) { return true; }
++                              data.li_attr[i] = v;
++                              if(i === 'id') {
++                                      data.id = v.toString();
++                              }
++                      });
++                      tmp = d.children('a').first();
++                      if(tmp.length) {
++                              tmp = $.vakata.attributes(tmp, true);
++                              $.each(tmp, function (i, v) {
++                                      v = $.trim(v);
++                                      if(v.length) {
++                                              data.a_attr[i] = v;
++                                      }
++                              });
++                      }
++                      tmp = d.children("a").first().length ? d.children("a").first().clone() : d.clone();
++                      tmp.children("ins, i, ul").remove();
++                      tmp = tmp.html();
++                      tmp = $('<div />').html(tmp);
++                      data.text = this.settings.core.force_text ? tmp.text() : tmp.html();
++                      tmp = d.data();
++                      data.data = tmp ? $.extend(true, {}, tmp) : null;
++                      data.state.opened = d.hasClass('jstree-open');
++                      data.state.selected = d.children('a').hasClass('jstree-clicked');
++                      data.state.disabled = d.children('a').hasClass('jstree-disabled');
++                      if(data.data && data.data.jstree) {
++                              for(i in data.data.jstree) {
++                                      if(data.data.jstree.hasOwnProperty(i)) {
++                                              data.state[i] = data.data.jstree[i];
++                                      }
++                              }
++                      }
++                      tmp = d.children("a").children(".jstree-themeicon");
++                      if(tmp.length) {
++                              data.icon = tmp.hasClass('jstree-themeicon-hidden') ? false : tmp.attr('rel');
++                      }
++                      if(data.state.icon !== undefined) {
++                              data.icon = data.state.icon;
++                      }
++                      if(data.icon === undefined || data.icon === null || data.icon === "") {
++                              data.icon = true;
++                      }
++                      tmp = d.children("ul").children("li");
++                      do {
++                              tid = 'j' + this._id + '_' + (++this._cnt);
++                      } while(m[tid]);
++                      data.id = data.li_attr.id ? data.li_attr.id.toString() : tid;
++                      if(tmp.length) {
++                              tmp.each($.proxy(function (i, v) {
++                                      c = this._parse_model_from_html($(v), data.id, ps);
++                                      e = this._model.data[c];
++                                      data.children.push(c);
++                                      if(e.children_d.length) {
++                                              data.children_d = data.children_d.concat(e.children_d);
++                                      }
++                              }, this));
++                              data.children_d = data.children_d.concat(data.children);
++                      }
++                      else {
++                              if(d.hasClass('jstree-closed')) {
++                                      data.state.loaded = false;
++                              }
++                      }
++                      if(data.li_attr['class']) {
++                              data.li_attr['class'] = data.li_attr['class'].replace('jstree-closed','').replace('jstree-open','');
++                      }
++                      if(data.a_attr['class']) {
++                              data.a_attr['class'] = data.a_attr['class'].replace('jstree-clicked','').replace('jstree-disabled','');
++                      }
++                      m[data.id] = data;
++                      if(data.state.selected) {
++                              this._data.core.selected.push(data.id);
++                      }
++                      return data.id;
++              },
++              /**
++               * parses a node from a JSON object (used when dealing with flat data, which has no nesting of children, but has id and parent properties) and appends it to the in memory tree model. Used internally.
++               * @private
++               * @name _parse_model_from_flat_json(d [, p, ps])
++               * @param  {Object} d the JSON object to parse
++               * @param  {String} p the parent ID
++               * @param  {Array} ps list of all parents
++               * @return {String} the ID of the object added to the model
++               */
++              _parse_model_from_flat_json : function (d, p, ps) {
++                      if(!ps) { ps = []; }
++                      else { ps = ps.concat(); }
++                      if(p) { ps.unshift(p); }
++                      var tid = d.id.toString(),
++                              m = this._model.data,
++                              df = this._model.default_state,
++                              i, j, c, e,
++                              tmp = {
++                                      id                      : tid,
++                                      text            : d.text || '',
++                                      icon            : d.icon !== undefined ? d.icon : true,
++                                      parent          : p,
++                                      parents         : ps,
++                                      children        : d.children || [],
++                                      children_d      : d.children_d || [],
++                                      data            : d.data,
++                                      state           : { },
++                                      li_attr         : { id : false },
++                                      a_attr          : { href : '#' },
++                                      original        : false
++                              };
++                      for(i in df) {
++                              if(df.hasOwnProperty(i)) {
++                                      tmp.state[i] = df[i];
++                              }
++                      }
++                      if(d && d.data && d.data.jstree && d.data.jstree.icon) {
++                              tmp.icon = d.data.jstree.icon;
++                      }
++                      if(tmp.icon === undefined || tmp.icon === null || tmp.icon === "") {
++                              tmp.icon = true;
++                      }
++                      if(d && d.data) {
++                              tmp.data = d.data;
++                              if(d.data.jstree) {
++                                      for(i in d.data.jstree) {
++                                              if(d.data.jstree.hasOwnProperty(i)) {
++                                                      tmp.state[i] = d.data.jstree[i];
++                                              }
++                                      }
++                              }
++                      }
++                      if(d && typeof d.state === 'object') {
++                              for (i in d.state) {
++                                      if(d.state.hasOwnProperty(i)) {
++                                              tmp.state[i] = d.state[i];
++                                      }
++                              }
++                      }
++                      if(d && typeof d.li_attr === 'object') {
++                              for (i in d.li_attr) {
++                                      if(d.li_attr.hasOwnProperty(i)) {
++                                              tmp.li_attr[i] = d.li_attr[i];
++                                      }
++                              }
++                      }
++                      if(!tmp.li_attr.id) {
++                              tmp.li_attr.id = tid;
++                      }
++                      if(d && typeof d.a_attr === 'object') {
++                              for (i in d.a_attr) {
++                                      if(d.a_attr.hasOwnProperty(i)) {
++                                              tmp.a_attr[i] = d.a_attr[i];
++                                      }
++                              }
++                      }
++                      if(d && d.children && d.children === true) {
++                              tmp.state.loaded = false;
++                              tmp.children = [];
++                              tmp.children_d = [];
++                      }
++                      m[tmp.id] = tmp;
++                      for(i = 0, j = tmp.children.length; i < j; i++) {
++                              c = this._parse_model_from_flat_json(m[tmp.children[i]], tmp.id, ps);
++                              e = m[c];
++                              tmp.children_d.push(c);
++                              if(e.children_d.length) {
++                                      tmp.children_d = tmp.children_d.concat(e.children_d);
++                              }
++                      }
++                      delete d.data;
++                      delete d.children;
++                      m[tmp.id].original = d;
++                      if(tmp.state.selected) {
++                              this._data.core.selected.push(tmp.id);
++                      }
++                      return tmp.id;
++              },
++              /**
++               * parses a node from a JSON object and appends it to the in memory tree model. Used internally.
++               * @private
++               * @name _parse_model_from_json(d [, p, ps])
++               * @param  {Object} d the JSON object to parse
++               * @param  {String} p the parent ID
++               * @param  {Array} ps list of all parents
++               * @return {String} the ID of the object added to the model
++               */
++              _parse_model_from_json : function (d, p, ps) {
++                      if(!ps) { ps = []; }
++                      else { ps = ps.concat(); }
++                      if(p) { ps.unshift(p); }
++                      var tid = false, i, j, c, e, m = this._model.data, df = this._model.default_state, tmp;
++                      do {
++                              tid = 'j' + this._id + '_' + (++this._cnt);
++                      } while(m[tid]);
++
++                      tmp = {
++                              id                      : false,
++                              text            : typeof d === 'string' ? d : '',
++                              icon            : typeof d === 'object' && d.icon !== undefined ? d.icon : true,
++                              parent          : p,
++                              parents         : ps,
++                              children        : [],
++                              children_d      : [],
++                              data            : null,
++                              state           : { },
++                              li_attr         : { id : false },
++                              a_attr          : { href : '#' },
++                              original        : false
++                      };
++                      for(i in df) {
++                              if(df.hasOwnProperty(i)) {
++                                      tmp.state[i] = df[i];
++                              }
++                      }
++                      if(d && d.id) { tmp.id = d.id.toString(); }
++                      if(d && d.text) { tmp.text = d.text; }
++                      if(d && d.data && d.data.jstree && d.data.jstree.icon) {
++                              tmp.icon = d.data.jstree.icon;
++                      }
++                      if(tmp.icon === undefined || tmp.icon === null || tmp.icon === "") {
++                              tmp.icon = true;
++                      }
++                      if(d && d.data) {
++                              tmp.data = d.data;
++                              if(d.data.jstree) {
++                                      for(i in d.data.jstree) {
++                                              if(d.data.jstree.hasOwnProperty(i)) {
++                                                      tmp.state[i] = d.data.jstree[i];
++                                              }
++                                      }
++                              }
++                      }
++                      if(d && typeof d.state === 'object') {
++                              for (i in d.state) {
++                                      if(d.state.hasOwnProperty(i)) {
++                                              tmp.state[i] = d.state[i];
++                                      }
++                              }
++                      }
++                      if(d && typeof d.li_attr === 'object') {
++                              for (i in d.li_attr) {
++                                      if(d.li_attr.hasOwnProperty(i)) {
++                                              tmp.li_attr[i] = d.li_attr[i];
++                                      }
++                              }
++                      }
++                      if(tmp.li_attr.id && !tmp.id) {
++                              tmp.id = tmp.li_attr.id.toString();
++                      }
++                      if(!tmp.id) {
++                              tmp.id = tid;
++                      }
++                      if(!tmp.li_attr.id) {
++                              tmp.li_attr.id = tmp.id;
++                      }
++                      if(d && typeof d.a_attr === 'object') {
++                              for (i in d.a_attr) {
++                                      if(d.a_attr.hasOwnProperty(i)) {
++                                              tmp.a_attr[i] = d.a_attr[i];
++                                      }
++                              }
++                      }
++                      if(d && d.children && d.children.length) {
++                              for(i = 0, j = d.children.length; i < j; i++) {
++                                      c = this._parse_model_from_json(d.children[i], tmp.id, ps);
++                                      e = m[c];
++                                      tmp.children.push(c);
++                                      if(e.children_d.length) {
++                                              tmp.children_d = tmp.children_d.concat(e.children_d);
++                                      }
++                              }
++                              tmp.children_d = tmp.children_d.concat(tmp.children);
++                      }
++                      if(d && d.children && d.children === true) {
++                              tmp.state.loaded = false;
++                              tmp.children = [];
++                              tmp.children_d = [];
++                      }
++                      delete d.data;
++                      delete d.children;
++                      tmp.original = d;
++                      m[tmp.id] = tmp;
++                      if(tmp.state.selected) {
++                              this._data.core.selected.push(tmp.id);
++                      }
++                      return tmp.id;
++              },
++              /**
++               * redraws all nodes that need to be redrawn. Used internally.
++               * @private
++               * @name _redraw()
++               * @trigger redraw.jstree
++               */
++              _redraw : function () {
++                      var nodes = this._model.force_full_redraw ? this._model.data[$.jstree.root].children.concat([]) : this._model.changed.concat([]),
++                              f = document.createElement('UL'), tmp, i, j, fe = this._data.core.focused;
++                      for(i = 0, j = nodes.length; i < j; i++) {
++                              tmp = this.redraw_node(nodes[i], true, this._model.force_full_redraw);
++                              if(tmp && this._model.force_full_redraw) {
++                                      f.appendChild(tmp);
++                              }
++                      }
++                      if(this._model.force_full_redraw) {
++                              f.className = this.get_container_ul()[0].className;
++                              f.setAttribute('role','group');
++                              this.element.empty().append(f);
++                              //this.get_container_ul()[0].appendChild(f);
++                      }
++                      if(fe !== null) {
++                              tmp = this.get_node(fe, true);
++                              if(tmp && tmp.length && tmp.children('.jstree-anchor')[0] !== document.activeElement) {
++                                      tmp.children('.jstree-anchor').focus();
++                              }
++                              else {
++                                      this._data.core.focused = null;
++                              }
++                      }
++                      this._model.force_full_redraw = false;
++                      this._model.changed = [];
++                      /**
++                       * triggered after nodes are redrawn
++                       * @event
++                       * @name redraw.jstree
++                       * @param {array} nodes the redrawn nodes
++                       */
++                      this.trigger('redraw', { "nodes" : nodes });
++              },
++              /**
++               * redraws all nodes that need to be redrawn or optionally - the whole tree
++               * @name redraw([full])
++               * @param {Boolean} full if set to `true` all nodes are redrawn.
++               */
++              redraw : function (full) {
++                      if(full) {
++                              this._model.force_full_redraw = true;
++                      }
++                      //if(this._model.redraw_timeout) {
++                      //      clearTimeout(this._model.redraw_timeout);
++                      //}
++                      //this._model.redraw_timeout = setTimeout($.proxy(this._redraw, this),0);
++                      this._redraw();
++              },
++              /**
++               * redraws a single node's children. Used internally.
++               * @private
++               * @name draw_children(node)
++               * @param {mixed} node the node whose children will be redrawn
++               */
++              draw_children : function (node) {
++                      var obj = this.get_node(node),
++                              i = false,
++                              j = false,
++                              k = false,
++                              d = document;
++                      if(!obj) { return false; }
++                      if(obj.id === $.jstree.root) { return this.redraw(true); }
++                      node = this.get_node(node, true);
++                      if(!node || !node.length) { return false; } // TODO: quick toggle
++
++                      node.children('.jstree-children').remove();
++                      node = node[0];
++                      if(obj.children.length && obj.state.loaded) {
++                              k = d.createElement('UL');
++                              k.setAttribute('role', 'group');
++                              k.className = 'jstree-children';
++                              for(i = 0, j = obj.children.length; i < j; i++) {
++                                      k.appendChild(this.redraw_node(obj.children[i], true, true));
++                              }
++                              node.appendChild(k);
++                      }
++              },
++              /**
++               * redraws a single node. Used internally.
++               * @private
++               * @name redraw_node(node, deep, is_callback, force_render)
++               * @param {mixed} node the node to redraw
++               * @param {Boolean} deep should child nodes be redrawn too
++               * @param {Boolean} is_callback is this a recursion call
++               * @param {Boolean} force_render should children of closed parents be drawn anyway
++               */
++              redraw_node : function (node, deep, is_callback, force_render) {
++                      var obj = this.get_node(node),
++                              par = false,
++                              ind = false,
++                              old = false,
++                              i = false,
++                              j = false,
++                              k = false,
++                              c = '',
++                              d = document,
++                              m = this._model.data,
++                              f = false,
++                              s = false,
++                              tmp = null,
++                              t = 0,
++                              l = 0,
++                              has_children = false,
++                              last_sibling = false;
++                      if(!obj) { return false; }
++                      if(obj.id === $.jstree.root) {  return this.redraw(true); }
++                      deep = deep || obj.children.length === 0;
++                      node = !document.querySelector ? document.getElementById(obj.id) : this.element[0].querySelector('#' + ("0123456789".indexOf(obj.id[0]) !== -1 ? '\\3' + obj.id[0] + ' ' + obj.id.substr(1).replace($.jstree.idregex,'\\$&') : obj.id.replace($.jstree.idregex,'\\$&')) ); //, this.element);
++                      if(!node) {
++                              deep = true;
++                              //node = d.createElement('LI');
++                              if(!is_callback) {
++                                      par = obj.parent !== $.jstree.root ? $('#' + obj.parent.replace($.jstree.idregex,'\\$&'), this.element)[0] : null;
++                                      if(par !== null && (!par || !m[obj.parent].state.opened)) {
++                                              return false;
++                                      }
++                                      ind = $.inArray(obj.id, par === null ? m[$.jstree.root].children : m[obj.parent].children);
++                              }
++                      }
++                      else {
++                              node = $(node);
++                              if(!is_callback) {
++                                      par = node.parent().parent()[0];
++                                      if(par === this.element[0]) {
++                                              par = null;
++                                      }
++                                      ind = node.index();
++                              }
++                              // m[obj.id].data = node.data(); // use only node's data, no need to touch jquery storage
++                              if(!deep && obj.children.length && !node.children('.jstree-children').length) {
++                                      deep = true;
++                              }
++                              if(!deep) {
++                                      old = node.children('.jstree-children')[0];
++                              }
++                              f = node.children('.jstree-anchor')[0] === document.activeElement;
++                              node.remove();
++                              //node = d.createElement('LI');
++                              //node = node[0];
++                      }
++                      node = this._data.core.node.cloneNode(true);
++                      // node is DOM, deep is boolean
++
++                      c = 'jstree-node ';
++                      for(i in obj.li_attr) {
++                              if(obj.li_attr.hasOwnProperty(i)) {
++                                      if(i === 'id') { continue; }
++                                      if(i !== 'class') {
++                                              node.setAttribute(i, obj.li_attr[i]);
++                                      }
++                                      else {
++                                              c += obj.li_attr[i];
++                                      }
++                              }
++                      }
++                      if(!obj.a_attr.id) {
++                              obj.a_attr.id = obj.id + '_anchor';
++                      }
++                      node.setAttribute('aria-selected', !!obj.state.selected);
++                      node.setAttribute('aria-level', obj.parents.length);
++                      node.setAttribute('aria-labelledby', obj.a_attr.id);
++                      if(obj.state.disabled) {
++                              node.setAttribute('aria-disabled', true);
++                      }
++
++                      for(i = 0, j = obj.children.length; i < j; i++) {
++                              if(!m[obj.children[i]].state.hidden) {
++                                      has_children = true;
++                                      break;
++                              }
++                      }
++                      if(obj.parent !== null && m[obj.parent] && !obj.state.hidden) {
++                              i = $.inArray(obj.id, m[obj.parent].children);
++                              last_sibling = obj.id;
++                              if(i !== -1) {
++                                      i++;
++                                      for(j = m[obj.parent].children.length; i < j; i++) {
++                                              if(!m[m[obj.parent].children[i]].state.hidden) {
++                                                      last_sibling = m[obj.parent].children[i];
++                                              }
++                                              if(last_sibling !== obj.id) {
++                                                      break;
++                                              }
++                                      }
++                              }
++                      }
++
++                      if(obj.state.hidden) {
++                              c += ' jstree-hidden';
++                      }
++                      if (obj.state.loading) {
++                              c += ' jstree-loading';
++                      }
++                      if(obj.state.loaded && !has_children) {
++                              c += ' jstree-leaf';
++                      }
++                      else {
++                              c += obj.state.opened && obj.state.loaded ? ' jstree-open' : ' jstree-closed';
++                              node.setAttribute('aria-expanded', (obj.state.opened && obj.state.loaded) );
++                      }
++                      if(last_sibling === obj.id) {
++                              c += ' jstree-last';
++                      }
++                      node.id = obj.id;
++                      node.className = c;
++                      c = ( obj.state.selected ? ' jstree-clicked' : '') + ( obj.state.disabled ? ' jstree-disabled' : '');
++                      for(j in obj.a_attr) {
++                              if(obj.a_attr.hasOwnProperty(j)) {
++                                      if(j === 'href' && obj.a_attr[j] === '#') { continue; }
++                                      if(j !== 'class') {
++                                              node.childNodes[1].setAttribute(j, obj.a_attr[j]);
++                                      }
++                                      else {
++                                              c += ' ' + obj.a_attr[j];
++                                      }
++                              }
++                      }
++                      if(c.length) {
++                              node.childNodes[1].className = 'jstree-anchor ' + c;
++                      }
++                      if((obj.icon && obj.icon !== true) || obj.icon === false) {
++                              if(obj.icon === false) {
++                                      node.childNodes[1].childNodes[0].className += ' jstree-themeicon-hidden';
++                              }
++                              else if(obj.icon.indexOf('/') === -1 && obj.icon.indexOf('.') === -1) {
++                                      node.childNodes[1].childNodes[0].className += ' ' + obj.icon + ' jstree-themeicon-custom';
++                              }
++                              else {
++                                      node.childNodes[1].childNodes[0].style.backgroundImage = 'url("'+obj.icon+'")';
++                                      node.childNodes[1].childNodes[0].style.backgroundPosition = 'center center';
++                                      node.childNodes[1].childNodes[0].style.backgroundSize = 'auto';
++                                      node.childNodes[1].childNodes[0].className += ' jstree-themeicon-custom';
++                              }
++                      }
++
++                      if(this.settings.core.force_text) {
++                              node.childNodes[1].appendChild(d.createTextNode(obj.text));
++                      }
++                      else {
++                              node.childNodes[1].innerHTML += obj.text;
++                      }
++
++
++                      if(deep && obj.children.length && (obj.state.opened || force_render) && obj.state.loaded) {
++                              k = d.createElement('UL');
++                              k.setAttribute('role', 'group');
++                              k.className = 'jstree-children';
++                              for(i = 0, j = obj.children.length; i < j; i++) {
++                                      k.appendChild(this.redraw_node(obj.children[i], deep, true));
++                              }
++                              node.appendChild(k);
++                      }
++                      if(old) {
++                              node.appendChild(old);
++                      }
++                      if(!is_callback) {
++                              // append back using par / ind
++                              if(!par) {
++                                      par = this.element[0];
++                              }
++                              for(i = 0, j = par.childNodes.length; i < j; i++) {
++                                      if(par.childNodes[i] && par.childNodes[i].className && par.childNodes[i].className.indexOf('jstree-children') !== -1) {
++                                              tmp = par.childNodes[i];
++                                              break;
++                                      }
++                              }
++                              if(!tmp) {
++                                      tmp = d.createElement('UL');
++                                      tmp.setAttribute('role', 'group');
++                                      tmp.className = 'jstree-children';
++                                      par.appendChild(tmp);
++                              }
++                              par = tmp;
++
++                              if(ind < par.childNodes.length) {
++                                      par.insertBefore(node, par.childNodes[ind]);
++                              }
++                              else {
++                                      par.appendChild(node);
++                              }
++                              if(f) {
++                                      t = this.element[0].scrollTop;
++                                      l = this.element[0].scrollLeft;
++                                      node.childNodes[1].focus();
++                                      this.element[0].scrollTop = t;
++                                      this.element[0].scrollLeft = l;
++                              }
++                      }
++                      if(obj.state.opened && !obj.state.loaded) {
++                              obj.state.opened = false;
++                              setTimeout($.proxy(function () {
++                                      this.open_node(obj.id, false, 0);
++                              }, this), 0);
++                      }
++                      return node;
++              },
++              /**
++               * opens a node, revaling its children. If the node is not loaded it will be loaded and opened once ready.
++               * @name open_node(obj [, callback, animation])
++               * @param {mixed} obj the node to open
++               * @param {Function} callback a function to execute once the node is opened
++               * @param {Number} animation the animation duration in milliseconds when opening the node (overrides the `core.animation` setting). Use `false` for no animation.
++               * @trigger open_node.jstree, after_open.jstree, before_open.jstree
++               */
++              open_node : function (obj, callback, animation) {
++                      var t1, t2, d, t;
++                      if($.isArray(obj)) {
++                              obj = obj.slice();
++                              for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
++                                      this.open_node(obj[t1], callback, animation);
++                              }
++                              return true;
++                      }
++                      obj = this.get_node(obj);
++                      if(!obj || obj.id === $.jstree.root) {
++                              return false;
++                      }
++                      animation = animation === undefined ? this.settings.core.animation : animation;
++                      if(!this.is_closed(obj)) {
++                              if(callback) {
++                                      callback.call(this, obj, false);
++                              }
++                              return false;
++                      }
++                      if(!this.is_loaded(obj)) {
++                              if(this.is_loading(obj)) {
++                                      return setTimeout($.proxy(function () {
++                                              this.open_node(obj, callback, animation);
++                                      }, this), 500);
++                              }
++                              this.load_node(obj, function (o, ok) {
++                                      return ok ? this.open_node(o, callback, animation) : (callback ? callback.call(this, o, false) : false);
++                              });
++                      }
++                      else {
++                              d = this.get_node(obj, true);
++                              t = this;
++                              if(d.length) {
++                                      if(animation && d.children(".jstree-children").length) {
++                                              d.children(".jstree-children").stop(true, true);
++                                      }
++                                      if(obj.children.length && !this._firstChild(d.children('.jstree-children')[0])) {
++                                              this.draw_children(obj);
++                                              //d = this.get_node(obj, true);
++                                      }
++                                      if(!animation) {
++                                              this.trigger('before_open', { "node" : obj });
++                                              d[0].className = d[0].className.replace('jstree-closed', 'jstree-open');
++                                              d[0].setAttribute("aria-expanded", true);
++                                      }
++                                      else {
++                                              this.trigger('before_open', { "node" : obj });
++                                              d
++                                                      .children(".jstree-children").css("display","none").end()
++                                                      .removeClass("jstree-closed").addClass("jstree-open").attr("aria-expanded", true)
++                                                      .children(".jstree-children").stop(true, true)
++                                                              .slideDown(animation, function () {
++                                                                      this.style.display = "";
++                                                                      if (t.element) {
++                                                                              t.trigger("after_open", { "node" : obj });
++                                                                      }
++                                                              });
++                                      }
++                              }
++                              obj.state.opened = true;
++                              if(callback) {
++                                      callback.call(this, obj, true);
++                              }
++                              if(!d.length) {
++                                      /**
++                                       * triggered when a node is about to be opened (if the node is supposed to be in the DOM, it will be, but it won't be visible yet)
++                                       * @event
++                                       * @name before_open.jstree
++                                       * @param {Object} node the opened node
++                                       */
++                                      this.trigger('before_open', { "node" : obj });
++                              }
++                              /**
++                               * triggered when a node is opened (if there is an animation it will not be completed yet)
++                               * @event
++                               * @name open_node.jstree
++                               * @param {Object} node the opened node
++                               */
++                              this.trigger('open_node', { "node" : obj });
++                              if(!animation || !d.length) {
++                                      /**
++                                       * triggered when a node is opened and the animation is complete
++                                       * @event
++                                       * @name after_open.jstree
++                                       * @param {Object} node the opened node
++                                       */
++                                      this.trigger("after_open", { "node" : obj });
++                              }
++                              return true;
++                      }
++              },
++              /**
++               * opens every parent of a node (node should be loaded)
++               * @name _open_to(obj)
++               * @param {mixed} obj the node to reveal
++               * @private
++               */
++              _open_to : function (obj) {
++                      obj = this.get_node(obj);
++                      if(!obj || obj.id === $.jstree.root) {
++                              return false;
++                      }
++                      var i, j, p = obj.parents;
++                      for(i = 0, j = p.length; i < j; i+=1) {
++                              if(i !== $.jstree.root) {
++                                      this.open_node(p[i], false, 0);
++                              }
++                      }
++                      return $('#' + obj.id.replace($.jstree.idregex,'\\$&'), this.element);
++              },
++              /**
++               * closes a node, hiding its children
++               * @name close_node(obj [, animation])
++               * @param {mixed} obj the node to close
++               * @param {Number} animation the animation duration in milliseconds when closing the node (overrides the `core.animation` setting). Use `false` for no animation.
++               * @trigger close_node.jstree, after_close.jstree
++               */
++              close_node : function (obj, animation) {
++                      var t1, t2, t, d;
++                      if($.isArray(obj)) {
++                              obj = obj.slice();
++                              for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
++                                      this.close_node(obj[t1], animation);
++                              }
++                              return true;
++                      }
++                      obj = this.get_node(obj);
++                      if(!obj || obj.id === $.jstree.root) {
++                              return false;
++                      }
++                      if(this.is_closed(obj)) {
++                              return false;
++                      }
++                      animation = animation === undefined ? this.settings.core.animation : animation;
++                      t = this;
++                      d = this.get_node(obj, true);
++
++                      obj.state.opened = false;
++                      /**
++                       * triggered when a node is closed (if there is an animation it will not be complete yet)
++                       * @event
++                       * @name close_node.jstree
++                       * @param {Object} node the closed node
++                       */
++                      this.trigger('close_node',{ "node" : obj });
++                      if(!d.length) {
++                              /**
++                               * triggered when a node is closed and the animation is complete
++                               * @event
++                               * @name after_close.jstree
++                               * @param {Object} node the closed node
++                               */
++                              this.trigger("after_close", { "node" : obj });
++                      }
++                      else {
++                              if(!animation) {
++                                      d[0].className = d[0].className.replace('jstree-open', 'jstree-closed');
++                                      d.attr("aria-expanded", false).children('.jstree-children').remove();
++                                      this.trigger("after_close", { "node" : obj });
++                              }
++                              else {
++                                      d
++                                              .children(".jstree-children").attr("style","display:block !important").end()
++                                              .removeClass("jstree-open").addClass("jstree-closed").attr("aria-expanded", false)
++                                              .children(".jstree-children").stop(true, true).slideUp(animation, function () {
++                                                      this.style.display = "";
++                                                      d.children('.jstree-children').remove();
++                                                      if (t.element) {
++                                                              t.trigger("after_close", { "node" : obj });
++                                                      }
++                                              });
++                              }
++                      }
++              },
++              /**
++               * toggles a node - closing it if it is open, opening it if it is closed
++               * @name toggle_node(obj)
++               * @param {mixed} obj the node to toggle
++               */
++              toggle_node : function (obj) {
++                      var t1, t2;
++                      if($.isArray(obj)) {
++                              obj = obj.slice();
++                              for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
++                                      this.toggle_node(obj[t1]);
++                              }
++                              return true;
++                      }
++                      if(this.is_closed(obj)) {
++                              return this.open_node(obj);
++                      }
++                      if(this.is_open(obj)) {
++                              return this.close_node(obj);
++                      }
++              },
++              /**
++               * opens all nodes within a node (or the tree), revaling their children. If the node is not loaded it will be loaded and opened once ready.
++               * @name open_all([obj, animation, original_obj])
++               * @param {mixed} obj the node to open recursively, omit to open all nodes in the tree
++               * @param {Number} animation the animation duration in milliseconds when opening the nodes, the default is no animation
++               * @param {jQuery} reference to the node that started the process (internal use)
++               * @trigger open_all.jstree
++               */
++              open_all : function (obj, animation, original_obj) {
++                      if(!obj) { obj = $.jstree.root; }
++                      obj = this.get_node(obj);
++                      if(!obj) { return false; }
++                      var dom = obj.id === $.jstree.root ? this.get_container_ul() : this.get_node(obj, true), i, j, _this;
++                      if(!dom.length) {
++                              for(i = 0, j = obj.children_d.length; i < j; i++) {
++                                      if(this.is_closed(this._model.data[obj.children_d[i]])) {
++                                              this._model.data[obj.children_d[i]].state.opened = true;
++                                      }
++                              }
++                              return this.trigger('open_all', { "node" : obj });
++                      }
++                      original_obj = original_obj || dom;
++                      _this = this;
++                      dom = this.is_closed(obj) ? dom.find('.jstree-closed').addBack() : dom.find('.jstree-closed');
++                      dom.each(function () {
++                              _this.open_node(
++                                      this,
++                                      function(node, status) { if(status && this.is_parent(node)) { this.open_all(node, animation, original_obj); } },
++                                      animation || 0
++                              );
++                      });
++                      if(original_obj.find('.jstree-closed').length === 0) {
++                              /**
++                               * triggered when an `open_all` call completes
++                               * @event
++                               * @name open_all.jstree
++                               * @param {Object} node the opened node
++                               */
++                              this.trigger('open_all', { "node" : this.get_node(original_obj) });
++                      }
++              },
++              /**
++               * closes all nodes within a node (or the tree), revaling their children
++               * @name close_all([obj, animation])
++               * @param {mixed} obj the node to close recursively, omit to close all nodes in the tree
++               * @param {Number} animation the animation duration in milliseconds when closing the nodes, the default is no animation
++               * @trigger close_all.jstree
++               */
++              close_all : function (obj, animation) {
++                      if(!obj) { obj = $.jstree.root; }
++                      obj = this.get_node(obj);
++                      if(!obj) { return false; }
++                      var dom = obj.id === $.jstree.root ? this.get_container_ul() : this.get_node(obj, true),
++                              _this = this, i, j;
++                      if(dom.length) {
++                              dom = this.is_open(obj) ? dom.find('.jstree-open').addBack() : dom.find('.jstree-open');
++                              $(dom.get().reverse()).each(function () { _this.close_node(this, animation || 0); });
++                      }
++                      for(i = 0, j = obj.children_d.length; i < j; i++) {
++                              this._model.data[obj.children_d[i]].state.opened = false;
++                      }
++                      /**
++                       * triggered when an `close_all` call completes
++                       * @event
++                       * @name close_all.jstree
++                       * @param {Object} node the closed node
++                       */
++                      this.trigger('close_all', { "node" : obj });
++              },
++              /**
++               * checks if a node is disabled (not selectable)
++               * @name is_disabled(obj)
++               * @param  {mixed} obj
++               * @return {Boolean}
++               */
++              is_disabled : function (obj) {
++                      obj = this.get_node(obj);
++                      return obj && obj.state && obj.state.disabled;
++              },
++              /**
++               * enables a node - so that it can be selected
++               * @name enable_node(obj)
++               * @param {mixed} obj the node to enable
++               * @trigger enable_node.jstree
++               */
++              enable_node : function (obj) {
++                      var t1, t2;
++                      if($.isArray(obj)) {
++                              obj = obj.slice();
++                              for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
++                                      this.enable_node(obj[t1]);
++                              }
++                              return true;
++                      }
++                      obj = this.get_node(obj);
++                      if(!obj || obj.id === $.jstree.root) {
++                              return false;
++                      }
++                      obj.state.disabled = false;
++                      this.get_node(obj,true).children('.jstree-anchor').removeClass('jstree-disabled').attr('aria-disabled', false);
++                      /**
++                       * triggered when an node is enabled
++                       * @event
++                       * @name enable_node.jstree
++                       * @param {Object} node the enabled node
++                       */
++                      this.trigger('enable_node', { 'node' : obj });
++              },
++              /**
++               * disables a node - so that it can not be selected
++               * @name disable_node(obj)
++               * @param {mixed} obj the node to disable
++               * @trigger disable_node.jstree
++               */
++              disable_node : function (obj) {
++                      var t1, t2;
++                      if($.isArray(obj)) {
++                              obj = obj.slice();
++                              for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
++                                      this.disable_node(obj[t1]);
++                              }
++                              return true;
++                      }
++                      obj = this.get_node(obj);
++                      if(!obj || obj.id === $.jstree.root) {
++                              return false;
++                      }
++                      obj.state.disabled = true;
++                      this.get_node(obj,true).children('.jstree-anchor').addClass('jstree-disabled').attr('aria-disabled', true);
++                      /**
++                       * triggered when an node is disabled
++                       * @event
++                       * @name disable_node.jstree
++                       * @param {Object} node the disabled node
++                       */
++                      this.trigger('disable_node', { 'node' : obj });
++              },
++              /**
++               * determines if a node is hidden
++               * @name is_hidden(obj)
++               * @param {mixed} obj the node
++               */
++              is_hidden : function (obj) {
++                      obj = this.get_node(obj);
++                      return obj.state.hidden === true;
++              },
++              /**
++               * hides a node - it is still in the structure but will not be visible
++               * @name hide_node(obj)
++               * @param {mixed} obj the node to hide
++               * @param {Boolean} skip_redraw internal parameter controlling if redraw is called
++               * @trigger hide_node.jstree
++               */
++              hide_node : function (obj, skip_redraw) {
++                      var t1, t2;
++                      if($.isArray(obj)) {
++                              obj = obj.slice();
++                              for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
++                                      this.hide_node(obj[t1], true);
++                              }
++                              if (!skip_redraw) {
++                                      this.redraw();
++                              }
++                              return true;
++                      }
++                      obj = this.get_node(obj);
++                      if(!obj || obj.id === $.jstree.root) {
++                              return false;
++                      }
++                      if(!obj.state.hidden) {
++                              obj.state.hidden = true;
++                              this._node_changed(obj.parent);
++                              if(!skip_redraw) {
++                                      this.redraw();
++                              }
++                              /**
++                               * triggered when an node is hidden
++                               * @event
++                               * @name hide_node.jstree
++                               * @param {Object} node the hidden node
++                               */
++                              this.trigger('hide_node', { 'node' : obj });
++                      }
++              },
++              /**
++               * shows a node
++               * @name show_node(obj)
++               * @param {mixed} obj the node to show
++               * @param {Boolean} skip_redraw internal parameter controlling if redraw is called
++               * @trigger show_node.jstree
++               */
++              show_node : function (obj, skip_redraw) {
++                      var t1, t2;
++                      if($.isArray(obj)) {
++                              obj = obj.slice();
++                              for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
++                                      this.show_node(obj[t1], true);
++                              }
++                              if (!skip_redraw) {
++                                      this.redraw();
++                              }
++                              return true;
++                      }
++                      obj = this.get_node(obj);
++                      if(!obj || obj.id === $.jstree.root) {
++                              return false;
++                      }
++                      if(obj.state.hidden) {
++                              obj.state.hidden = false;
++                              this._node_changed(obj.parent);
++                              if(!skip_redraw) {
++                                      this.redraw();
++                              }
++                              /**
++                               * triggered when an node is shown
++                               * @event
++                               * @name show_node.jstree
++                               * @param {Object} node the shown node
++                               */
++                              this.trigger('show_node', { 'node' : obj });
++                      }
++              },
++              /**
++               * hides all nodes
++               * @name hide_all()
++               * @trigger hide_all.jstree
++               */
++              hide_all : function (skip_redraw) {
++                      var i, m = this._model.data, ids = [];
++                      for(i in m) {
++                              if(m.hasOwnProperty(i) && i !== $.jstree.root && !m[i].state.hidden) {
++                                      m[i].state.hidden = true;
++                                      ids.push(i);
++                              }
++                      }
++                      this._model.force_full_redraw = true;
++                      if(!skip_redraw) {
++                              this.redraw();
++                      }
++                      /**
++                       * triggered when all nodes are hidden
++                       * @event
++                       * @name hide_all.jstree
++                       * @param {Array} nodes the IDs of all hidden nodes
++                       */
++                      this.trigger('hide_all', { 'nodes' : ids });
++                      return ids;
++              },
++              /**
++               * shows all nodes
++               * @name show_all()
++               * @trigger show_all.jstree
++               */
++              show_all : function (skip_redraw) {
++                      var i, m = this._model.data, ids = [];
++                      for(i in m) {
++                              if(m.hasOwnProperty(i) && i !== $.jstree.root && m[i].state.hidden) {
++                                      m[i].state.hidden = false;
++                                      ids.push(i);
++                              }
++                      }
++                      this._model.force_full_redraw = true;
++                      if(!skip_redraw) {
++                              this.redraw();
++                      }
++                      /**
++                       * triggered when all nodes are shown
++                       * @event
++                       * @name show_all.jstree
++                       * @param {Array} nodes the IDs of all shown nodes
++                       */
++                      this.trigger('show_all', { 'nodes' : ids });
++                      return ids;
++              },
++              /**
++               * called when a node is selected by the user. Used internally.
++               * @private
++               * @name activate_node(obj, e)
++               * @param {mixed} obj the node
++               * @param {Object} e the related event
++               * @trigger activate_node.jstree, changed.jstree
++               */
++              activate_node : function (obj, e) {
++                      if(this.is_disabled(obj)) {
++                              return false;
++                      }
++                      if(!e || typeof e !== 'object') {
++                              e = {};
++                      }
++
++                      // ensure last_clicked is still in the DOM, make it fresh (maybe it was moved?) and make sure it is still selected, if not - make last_clicked the last selected node
++                      this._data.core.last_clicked = this._data.core.last_clicked && this._data.core.last_clicked.id !== undefined ? this.get_node(this._data.core.last_clicked.id) : null;
++                      if(this._data.core.last_clicked && !this._data.core.last_clicked.state.selected) { this._data.core.last_clicked = null; }
++                      if(!this._data.core.last_clicked && this._data.core.selected.length) { this._data.core.last_clicked = this.get_node(this._data.core.selected[this._data.core.selected.length - 1]); }
++
++                      if(!this.settings.core.multiple || (!e.metaKey && !e.ctrlKey && !e.shiftKey) || (e.shiftKey && (!this._data.core.last_clicked || !this.get_parent(obj) || this.get_parent(obj) !== this._data.core.last_clicked.parent ) )) {
++                              if(!this.settings.core.multiple && (e.metaKey || e.ctrlKey || e.shiftKey) && this.is_selected(obj)) {
++                                      this.deselect_node(obj, false, e);
++                              }
++                              else {
++                                      this.deselect_all(true);
++                                      this.select_node(obj, false, false, e);
++                                      this._data.core.last_clicked = this.get_node(obj);
++                              }
++                      }
++                      else {
++                              if(e.shiftKey) {
++                                      var o = this.get_node(obj).id,
++                                              l = this._data.core.last_clicked.id,
++                                              p = this.get_node(this._data.core.last_clicked.parent).children,
++                                              c = false,
++                                              i, j;
++                                      for(i = 0, j = p.length; i < j; i += 1) {
++                                              // separate IFs work whem o and l are the same
++                                              if(p[i] === o) {
++                                                      c = !c;
++                                              }
++                                              if(p[i] === l) {
++                                                      c = !c;
++                                              }
++                                              if(!this.is_disabled(p[i]) && (c || p[i] === o || p[i] === l)) {
++                                                      if (!this.is_hidden(p[i])) {
++                                                              this.select_node(p[i], true, false, e);
++                                                      }
++                                              }
++                                              else {
++                                                      this.deselect_node(p[i], true, e);
++                                              }
++                                      }
++                                      this.trigger('changed', { 'action' : 'select_node', 'node' : this.get_node(obj), 'selected' : this._data.core.selected, 'event' : e });
++                              }
++                              else {
++                                      if(!this.is_selected(obj)) {
++                                              this.select_node(obj, false, false, e);
++                                      }
++                                      else {
++                                              this.deselect_node(obj, false, e);
++                                      }
++                              }
++                      }
++                      /**
++                       * triggered when an node is clicked or intercated with by the user
++                       * @event
++                       * @name activate_node.jstree
++                       * @param {Object} node
++                       * @param {Object} event the ooriginal event (if any) which triggered the call (may be an empty object)
++                       */
++                      this.trigger('activate_node', { 'node' : this.get_node(obj), 'event' : e });
++              },
++              /**
++               * applies the hover state on a node, called when a node is hovered by the user. Used internally.
++               * @private
++               * @name hover_node(obj)
++               * @param {mixed} obj
++               * @trigger hover_node.jstree
++               */
++              hover_node : function (obj) {
++                      obj = this.get_node(obj, true);
++                      if(!obj || !obj.length || obj.children('.jstree-hovered').length) {
++                              return false;
++                      }
++                      var o = this.element.find('.jstree-hovered'), t = this.element;
++                      if(o && o.length) { this.dehover_node(o); }
++
++                      obj.children('.jstree-anchor').addClass('jstree-hovered');
++                      /**
++                       * triggered when an node is hovered
++                       * @event
++                       * @name hover_node.jstree
++                       * @param {Object} node
++                       */
++                      this.trigger('hover_node', { 'node' : this.get_node(obj) });
++                      setTimeout(function () { t.attr('aria-activedescendant', obj[0].id); }, 0);
++              },
++              /**
++               * removes the hover state from a nodecalled when a node is no longer hovered by the user. Used internally.
++               * @private
++               * @name dehover_node(obj)
++               * @param {mixed} obj
++               * @trigger dehover_node.jstree
++               */
++              dehover_node : function (obj) {
++                      obj = this.get_node(obj, true);
++                      if(!obj || !obj.length || !obj.children('.jstree-hovered').length) {
++                              return false;
++                      }
++                      obj.children('.jstree-anchor').removeClass('jstree-hovered');
++                      /**
++                       * triggered when an node is no longer hovered
++                       * @event
++                       * @name dehover_node.jstree
++                       * @param {Object} node
++                       */
++                      this.trigger('dehover_node', { 'node' : this.get_node(obj) });
++              },
++              /**
++               * select a node
++               * @name select_node(obj [, supress_event, prevent_open])
++               * @param {mixed} obj an array can be used to select multiple nodes
++               * @param {Boolean} supress_event if set to `true` the `changed.jstree` event won't be triggered
++               * @param {Boolean} prevent_open if set to `true` parents of the selected node won't be opened
++               * @trigger select_node.jstree, changed.jstree
++               */
++              select_node : function (obj, supress_event, prevent_open, e) {
++                      var dom, t1, t2, th;
++                      if($.isArray(obj)) {
++                              obj = obj.slice();
++                              for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
++                                      this.select_node(obj[t1], supress_event, prevent_open, e);
++                              }
++                              return true;
++                      }
++                      obj = this.get_node(obj);
++                      if(!obj || obj.id === $.jstree.root) {
++                              return false;
++                      }
++                      dom = this.get_node(obj, true);
++                      if(!obj.state.selected) {
++                              obj.state.selected = true;
++                              this._data.core.selected.push(obj.id);
++                              if(!prevent_open) {
++                                      dom = this._open_to(obj);
++                              }
++                              if(dom && dom.length) {
++                                      dom.attr('aria-selected', true).children('.jstree-anchor').addClass('jstree-clicked');
++                              }
++                              /**
++                               * triggered when an node is selected
++                               * @event
++                               * @name select_node.jstree
++                               * @param {Object} node
++                               * @param {Array} selected the current selection
++                               * @param {Object} event the event (if any) that triggered this select_node
++                               */
++                              this.trigger('select_node', { 'node' : obj, 'selected' : this._data.core.selected, 'event' : e });
++                              if(!supress_event) {
++                                      /**
++                                       * triggered when selection changes
++                                       * @event
++                                       * @name changed.jstree
++                                       * @param {Object} node
++                                       * @param {Object} action the action that caused the selection to change
++                                       * @param {Array} selected the current selection
++                                       * @param {Object} event the event (if any) that triggered this changed event
++                                       */
++                                      this.trigger('changed', { 'action' : 'select_node', 'node' : obj, 'selected' : this._data.core.selected, 'event' : e });
++                              }
++                      }
++              },
++              /**
++               * deselect a node
++               * @name deselect_node(obj [, supress_event])
++               * @param {mixed} obj an array can be used to deselect multiple nodes
++               * @param {Boolean} supress_event if set to `true` the `changed.jstree` event won't be triggered
++               * @trigger deselect_node.jstree, changed.jstree
++               */
++              deselect_node : function (obj, supress_event, e) {
++                      var t1, t2, dom;
++                      if($.isArray(obj)) {
++                              obj = obj.slice();
++                              for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
++                                      this.deselect_node(obj[t1], supress_event, e);
++                              }
++                              return true;
++                      }
++                      obj = this.get_node(obj);
++                      if(!obj || obj.id === $.jstree.root) {
++                              return false;
++                      }
++                      dom = this.get_node(obj, true);
++                      if(obj.state.selected) {
++                              obj.state.selected = false;
++                              this._data.core.selected = $.vakata.array_remove_item(this._data.core.selected, obj.id);
++                              if(dom.length) {
++                                      dom.attr('aria-selected', false).children('.jstree-anchor').removeClass('jstree-clicked');
++                              }
++                              /**
++                               * triggered when an node is deselected
++                               * @event
++                               * @name deselect_node.jstree
++                               * @param {Object} node
++                               * @param {Array} selected the current selection
++                               * @param {Object} event the event (if any) that triggered this deselect_node
++                               */
++                              this.trigger('deselect_node', { 'node' : obj, 'selected' : this._data.core.selected, 'event' : e });
++                              if(!supress_event) {
++                                      this.trigger('changed', { 'action' : 'deselect_node', 'node' : obj, 'selected' : this._data.core.selected, 'event' : e });
++                              }
++                      }
++              },
++              /**
++               * select all nodes in the tree
++               * @name select_all([supress_event])
++               * @param {Boolean} supress_event if set to `true` the `changed.jstree` event won't be triggered
++               * @trigger select_all.jstree, changed.jstree
++               */
++              select_all : function (supress_event) {
++                      var tmp = this._data.core.selected.concat([]), i, j;
++                      this._data.core.selected = this._model.data[$.jstree.root].children_d.concat();
++                      for(i = 0, j = this._data.core.selected.length; i < j; i++) {
++                              if(this._model.data[this._data.core.selected[i]]) {
++                                      this._model.data[this._data.core.selected[i]].state.selected = true;
++                              }
++                      }
++                      this.redraw(true);
++                      /**
++                       * triggered when all nodes are selected
++                       * @event
++                       * @name select_all.jstree
++                       * @param {Array} selected the current selection
++                       */
++                      this.trigger('select_all', { 'selected' : this._data.core.selected });
++                      if(!supress_event) {
++                              this.trigger('changed', { 'action' : 'select_all', 'selected' : this._data.core.selected, 'old_selection' : tmp });
++                      }
++              },
++              /**
++               * deselect all selected nodes
++               * @name deselect_all([supress_event])
++               * @param {Boolean} supress_event if set to `true` the `changed.jstree` event won't be triggered
++               * @trigger deselect_all.jstree, changed.jstree
++               */
++              deselect_all : function (supress_event) {
++                      var tmp = this._data.core.selected.concat([]), i, j;
++                      for(i = 0, j = this._data.core.selected.length; i < j; i++) {
++                              if(this._model.data[this._data.core.selected[i]]) {
++                                      this._model.data[this._data.core.selected[i]].state.selected = false;
++                              }
++                      }
++                      this._data.core.selected = [];
++                      this.element.find('.jstree-clicked').removeClass('jstree-clicked').parent().attr('aria-selected', false);
++                      /**
++                       * triggered when all nodes are deselected
++                       * @event
++                       * @name deselect_all.jstree
++                       * @param {Object} node the previous selection
++                       * @param {Array} selected the current selection
++                       */
++                      this.trigger('deselect_all', { 'selected' : this._data.core.selected, 'node' : tmp });
++                      if(!supress_event) {
++                              this.trigger('changed', { 'action' : 'deselect_all', 'selected' : this._data.core.selected, 'old_selection' : tmp });
++                      }
++              },
++              /**
++               * checks if a node is selected
++               * @name is_selected(obj)
++               * @param  {mixed}  obj
++               * @return {Boolean}
++               */
++              is_selected : function (obj) {
++                      obj = this.get_node(obj);
++                      if(!obj || obj.id === $.jstree.root) {
++                              return false;
++                      }
++                      return obj.state.selected;
++              },
++              /**
++               * get an array of all selected nodes
++               * @name get_selected([full])
++               * @param  {mixed}  full if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned
++               * @return {Array}
++               */
++              get_selected : function (full) {
++                      return full ? $.map(this._data.core.selected, $.proxy(function (i) { return this.get_node(i); }, this)) : this._data.core.selected.slice();
++              },
++              /**
++               * get an array of all top level selected nodes (ignoring children of selected nodes)
++               * @name get_top_selected([full])
++               * @param  {mixed}  full if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned
++               * @return {Array}
++               */
++              get_top_selected : function (full) {
++                      var tmp = this.get_selected(true),
++                              obj = {}, i, j, k, l;
++                      for(i = 0, j = tmp.length; i < j; i++) {
++                              obj[tmp[i].id] = tmp[i];
++                      }
++                      for(i = 0, j = tmp.length; i < j; i++) {
++                              for(k = 0, l = tmp[i].children_d.length; k < l; k++) {
++                                      if(obj[tmp[i].children_d[k]]) {
++                                              delete obj[tmp[i].children_d[k]];
++                                      }
++                              }
++                      }
++                      tmp = [];
++                      for(i in obj) {
++                              if(obj.hasOwnProperty(i)) {
++                                      tmp.push(i);
++                              }
++                      }
++                      return full ? $.map(tmp, $.proxy(function (i) { return this.get_node(i); }, this)) : tmp;
++              },
++              /**
++               * get an array of all bottom level selected nodes (ignoring selected parents)
++               * @name get_bottom_selected([full])
++               * @param  {mixed}  full if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned
++               * @return {Array}
++               */
++              get_bottom_selected : function (full) {
++                      var tmp = this.get_selected(true),
++                              obj = [], i, j;
++                      for(i = 0, j = tmp.length; i < j; i++) {
++                              if(!tmp[i].children.length) {
++                                      obj.push(tmp[i].id);
++                              }
++                      }
++                      return full ? $.map(obj, $.proxy(function (i) { return this.get_node(i); }, this)) : obj;
++              },
++              /**
++               * gets the current state of the tree so that it can be restored later with `set_state(state)`. Used internally.
++               * @name get_state()
++               * @private
++               * @return {Object}
++               */
++              get_state : function () {
++                      var state       = {
++                              'core' : {
++                                      'open' : [],
++                                      'loaded' : [],
++                                      'scroll' : {
++                                              'left' : this.element.scrollLeft(),
++                                              'top' : this.element.scrollTop()
++                                      },
++                                      /*!
++                                      'themes' : {
++                                              'name' : this.get_theme(),
++                                              'icons' : this._data.core.themes.icons,
++                                              'dots' : this._data.core.themes.dots
++                                      },
++                                      */
++                                      'selected' : []
++                              }
++                      }, i;
++                      for(i in this._model.data) {
++                              if(this._model.data.hasOwnProperty(i)) {
++                                      if(i !== $.jstree.root) {
++                                              if(this._model.data[i].state.loaded && this.settings.core.loaded_state) {
++                                                      state.core.loaded.push(i);
++                                              }
++                                              if(this._model.data[i].state.opened) {
++                                                      state.core.open.push(i);
++                                              }
++                                              if(this._model.data[i].state.selected) {
++                                                      state.core.selected.push(i);
++                                              }
++                                      }
++                              }
++                      }
++                      return state;
++              },
++              /**
++               * sets the state of the tree. Used internally.
++               * @name set_state(state [, callback])
++               * @private
++               * @param {Object} state the state to restore. Keep in mind this object is passed by reference and jstree will modify it.
++               * @param {Function} callback an optional function to execute once the state is restored.
++               * @trigger set_state.jstree
++               */
++              set_state : function (state, callback) {
++                      if(state) {
++                              if(state.core && state.core.selected && state.core.initial_selection === undefined) {
++                                      state.core.initial_selection = this._data.core.selected.concat([]).sort().join(',');
++                              }
++                              if(state.core) {
++                                      var res, n, t, _this, i;
++                                      if(state.core.loaded) {
++                                              if(!this.settings.core.loaded_state || !$.isArray(state.core.loaded) || !state.core.loaded.length) {
++                                                      delete state.core.loaded;
++                                                      this.set_state(state, callback);
++                                              }
++                                              else {
++                                                      this._load_nodes(state.core.loaded, function (nodes) {
++                                                              delete state.core.loaded;
++                                                              this.set_state(state, callback);
++                                                      });
++                                              }
++                                              return false;
++                                      }
++                                      if(state.core.open) {
++                                              if(!$.isArray(state.core.open) || !state.core.open.length) {
++                                                      delete state.core.open;
++                                                      this.set_state(state, callback);
++                                              }
++                                              else {
++                                                      this._load_nodes(state.core.open, function (nodes) {
++                                                              this.open_node(nodes, false, 0);
++                                                              delete state.core.open;
++                                                              this.set_state(state, callback);
++                                                      });
++                                              }
++                                              return false;
++                                      }
++                                      if(state.core.scroll) {
++                                              if(state.core.scroll && state.core.scroll.left !== undefined) {
++                                                      this.element.scrollLeft(state.core.scroll.left);
++                                              }
++                                              if(state.core.scroll && state.core.scroll.top !== undefined) {
++                                                      this.element.scrollTop(state.core.scroll.top);
++                                              }
++                                              delete state.core.scroll;
++                                              this.set_state(state, callback);
++                                              return false;
++                                      }
++                                      if(state.core.selected) {
++                                              _this = this;
++                                              if (state.core.initial_selection === undefined ||
++                                                      state.core.initial_selection === this._data.core.selected.concat([]).sort().join(',')
++                                              ) {
++                                                      this.deselect_all();
++                                                      $.each(state.core.selected, function (i, v) {
++                                                              _this.select_node(v, false, true);
++                                                      });
++                                              }
++                                              delete state.core.initial_selection;
++                                              delete state.core.selected;
++                                              this.set_state(state, callback);
++                                              return false;
++                                      }
++                                      for(i in state) {
++                                              if(state.hasOwnProperty(i) && i !== "core" && $.inArray(i, this.settings.plugins) === -1) {
++                                                      delete state[i];
++                                              }
++                                      }
++                                      if($.isEmptyObject(state.core)) {
++                                              delete state.core;
++                                              this.set_state(state, callback);
++                                              return false;
++                                      }
++                              }
++                              if($.isEmptyObject(state)) {
++                                      state = null;
++                                      if(callback) { callback.call(this); }
++                                      /**
++                                       * triggered when a `set_state` call completes
++                                       * @event
++                                       * @name set_state.jstree
++                                       */
++                                      this.trigger('set_state');
++                                      return false;
++                              }
++                              return true;
++                      }
++                      return false;
++              },
++              /**
++               * refreshes the tree - all nodes are reloaded with calls to `load_node`.
++               * @name refresh()
++               * @param {Boolean} skip_loading an option to skip showing the loading indicator
++               * @param {Mixed} forget_state if set to `true` state will not be reapplied, if set to a function (receiving the current state as argument) the result of that function will be used as state
++               * @trigger refresh.jstree
++               */
++              refresh : function (skip_loading, forget_state) {
++                      this._data.core.state = forget_state === true ? {} : this.get_state();
++                      if(forget_state && $.isFunction(forget_state)) { this._data.core.state = forget_state.call(this, this._data.core.state); }
++                      this._cnt = 0;
++                      this._model.data = {};
++                      this._model.data[$.jstree.root] = {
++                              id : $.jstree.root,
++                              parent : null,
++                              parents : [],
++                              children : [],
++                              children_d : [],
++                              state : { loaded : false }
++                      };
++                      this._data.core.selected = [];
++                      this._data.core.last_clicked = null;
++                      this._data.core.focused = null;
++
++                      var c = this.get_container_ul()[0].className;
++                      if(!skip_loading) {
++                              this.element.html("<"+"ul class='"+c+"' role='group'><"+"li class='jstree-initial-node jstree-loading jstree-leaf jstree-last' role='treeitem' id='j"+this._id+"_loading'><i class='jstree-icon jstree-ocl'></i><"+"a class='jstree-anchor' href='#'><i class='jstree-icon jstree-themeicon-hidden'></i>" + this.get_string("Loading ...") + "</a></li></ul>");
++                              this.element.attr('aria-activedescendant','j'+this._id+'_loading');
++                      }
++                      this.load_node($.jstree.root, function (o, s) {
++                              if(s) {
++                                      this.get_container_ul()[0].className = c;
++                                      if(this._firstChild(this.get_container_ul()[0])) {
++                                              this.element.attr('aria-activedescendant',this._firstChild(this.get_container_ul()[0]).id);
++                                      }
++                                      this.set_state($.extend(true, {}, this._data.core.state), function () {
++                                              /**
++                                               * triggered when a `refresh` call completes
++                                               * @event
++                                               * @name refresh.jstree
++                                               */
++                                              this.trigger('refresh');
++                                      });
++                              }
++                              this._data.core.state = null;
++                      });
++              },
++              /**
++               * refreshes a node in the tree (reload its children) all opened nodes inside that node are reloaded with calls to `load_node`.
++               * @name refresh_node(obj)
++               * @param  {mixed} obj the node
++               * @trigger refresh_node.jstree
++               */
++              refresh_node : function (obj) {
++                      obj = this.get_node(obj);
++                      if(!obj || obj.id === $.jstree.root) { return false; }
++                      var opened = [], to_load = [], s = this._data.core.selected.concat([]);
++                      to_load.push(obj.id);
++                      if(obj.state.opened === true) { opened.push(obj.id); }
++                      this.get_node(obj, true).find('.jstree-open').each(function() { to_load.push(this.id); opened.push(this.id); });
++                      this._load_nodes(to_load, $.proxy(function (nodes) {
++                              this.open_node(opened, false, 0);
++                              this.select_node(s);
++                              /**
++                               * triggered when a node is refreshed
++                               * @event
++                               * @name refresh_node.jstree
++                               * @param {Object} node - the refreshed node
++                               * @param {Array} nodes - an array of the IDs of the nodes that were reloaded
++                               */
++                              this.trigger('refresh_node', { 'node' : obj, 'nodes' : nodes });
++                      }, this), false, true);
++              },
++              /**
++               * set (change) the ID of a node
++               * @name set_id(obj, id)
++               * @param  {mixed} obj the node
++               * @param  {String} id the new ID
++               * @return {Boolean}
++               * @trigger set_id.jstree
++               */
++              set_id : function (obj, id) {
++                      obj = this.get_node(obj);
++                      if(!obj || obj.id === $.jstree.root) { return false; }
++                      var i, j, m = this._model.data, old = obj.id;
++                      id = id.toString();
++                      // update parents (replace current ID with new one in children and children_d)
++                      m[obj.parent].children[$.inArray(obj.id, m[obj.parent].children)] = id;
++                      for(i = 0, j = obj.parents.length; i < j; i++) {
++                              m[obj.parents[i]].children_d[$.inArray(obj.id, m[obj.parents[i]].children_d)] = id;
++                      }
++                      // update children (replace current ID with new one in parent and parents)
++                      for(i = 0, j = obj.children.length; i < j; i++) {
++                              m[obj.children[i]].parent = id;
++                      }
++                      for(i = 0, j = obj.children_d.length; i < j; i++) {
++                              m[obj.children_d[i]].parents[$.inArray(obj.id, m[obj.children_d[i]].parents)] = id;
++                      }
++                      i = $.inArray(obj.id, this._data.core.selected);
++                      if(i !== -1) { this._data.core.selected[i] = id; }
++                      // update model and obj itself (obj.id, this._model.data[KEY])
++                      i = this.get_node(obj.id, true);
++                      if(i) {
++                              i.attr('id', id); //.children('.jstree-anchor').attr('id', id + '_anchor').end().attr('aria-labelledby', id + '_anchor');
++                              if(this.element.attr('aria-activedescendant') === obj.id) {
++                                      this.element.attr('aria-activedescendant', id);
++                              }
++                      }
++                      delete m[obj.id];
++                      obj.id = id;
++                      obj.li_attr.id = id;
++                      m[id] = obj;
++                      /**
++                       * triggered when a node id value is changed
++                       * @event
++                       * @name set_id.jstree
++                       * @param {Object} node
++                       * @param {String} old the old id
++                       */
++                      this.trigger('set_id',{ "node" : obj, "new" : obj.id, "old" : old });
++                      return true;
++              },
++              /**
++               * get the text value of a node
++               * @name get_text(obj)
++               * @param  {mixed} obj the node
++               * @return {String}
++               */
++              get_text : function (obj) {
++                      obj = this.get_node(obj);
++                      return (!obj || obj.id === $.jstree.root) ? false : obj.text;
++              },
++              /**
++               * set the text value of a node. Used internally, please use `rename_node(obj, val)`.
++               * @private
++               * @name set_text(obj, val)
++               * @param  {mixed} obj the node, you can pass an array to set the text on multiple nodes
++               * @param  {String} val the new text value
++               * @return {Boolean}
++               * @trigger set_text.jstree
++               */
++              set_text : function (obj, val) {
++                      var t1, t2;
++                      if($.isArray(obj)) {
++                              obj = obj.slice();
++                              for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
++                                      this.set_text(obj[t1], val);
++                              }
++                              return true;
++                      }
++                      obj = this.get_node(obj);
++                      if(!obj || obj.id === $.jstree.root) { return false; }
++                      obj.text = val;
++                      if(this.get_node(obj, true).length) {
++                              this.redraw_node(obj.id);
++                      }
++                      /**
++                       * triggered when a node text value is changed
++                       * @event
++                       * @name set_text.jstree
++                       * @param {Object} obj
++                       * @param {String} text the new value
++                       */
++                      this.trigger('set_text',{ "obj" : obj, "text" : val });
++                      return true;
++              },
++              /**
++               * gets a JSON representation of a node (or the whole tree)
++               * @name get_json([obj, options])
++               * @param  {mixed} obj
++               * @param  {Object} options
++               * @param  {Boolean} options.no_state do not return state information
++               * @param  {Boolean} options.no_id do not return ID
++               * @param  {Boolean} options.no_children do not include children
++               * @param  {Boolean} options.no_data do not include node data
++               * @param  {Boolean} options.no_li_attr do not include LI attributes
++               * @param  {Boolean} options.no_a_attr do not include A attributes
++               * @param  {Boolean} options.flat return flat JSON instead of nested
++               * @return {Object}
++               */
++              get_json : function (obj, options, flat) {
++                      obj = this.get_node(obj || $.jstree.root);
++                      if(!obj) { return false; }
++                      if(options && options.flat && !flat) { flat = []; }
++                      var tmp = {
++                              'id' : obj.id,
++                              'text' : obj.text,
++                              'icon' : this.get_icon(obj),
++                              'li_attr' : $.extend(true, {}, obj.li_attr),
++                              'a_attr' : $.extend(true, {}, obj.a_attr),
++                              'state' : {},
++                              'data' : options && options.no_data ? false : $.extend(true, $.isArray(obj.data)?[]:{}, obj.data)
++                              //( this.get_node(obj, true).length ? this.get_node(obj, true).data() : obj.data ),
++                      }, i, j;
++                      if(options && options.flat) {
++                              tmp.parent = obj.parent;
++                      }
++                      else {
++                              tmp.children = [];
++                      }
++                      if(!options || !options.no_state) {
++                              for(i in obj.state) {
++                                      if(obj.state.hasOwnProperty(i)) {
++                                              tmp.state[i] = obj.state[i];
++                                      }
++                              }
++                      } else {
++                              delete tmp.state;
++                      }
++                      if(options && options.no_li_attr) {
++                              delete tmp.li_attr;
++                      }
++                      if(options && options.no_a_attr) {
++                              delete tmp.a_attr;
++                      }
++                      if(options && options.no_id) {
++                              delete tmp.id;
++                              if(tmp.li_attr && tmp.li_attr.id) {
++                                      delete tmp.li_attr.id;
++                              }
++                              if(tmp.a_attr && tmp.a_attr.id) {
++                                      delete tmp.a_attr.id;
++                              }
++                      }
++                      if(options && options.flat && obj.id !== $.jstree.root) {
++                              flat.push(tmp);
++                      }
++                      if(!options || !options.no_children) {
++                              for(i = 0, j = obj.children.length; i < j; i++) {
++                                      if(options && options.flat) {
++                                              this.get_json(obj.children[i], options, flat);
++                                      }
++                                      else {
++                                              tmp.children.push(this.get_json(obj.children[i], options));
++                                      }
++                              }
++                      }
++                      return options && options.flat ? flat : (obj.id === $.jstree.root ? tmp.children : tmp);
++              },
++              /**
++               * create a new node (do not confuse with load_node)
++               * @name create_node([par, node, pos, callback, is_loaded])
++               * @param  {mixed}   par       the parent node (to create a root node use either "#" (string) or `null`)
++               * @param  {mixed}   node      the data for the new node (a valid JSON object, or a simple string with the name)
++               * @param  {mixed}   pos       the index at which to insert the node, "first" and "last" are also supported, default is "last"
++               * @param  {Function} callback a function to be called once the node is created
++               * @param  {Boolean} is_loaded internal argument indicating if the parent node was succesfully loaded
++               * @return {String}            the ID of the newly create node
++               * @trigger model.jstree, create_node.jstree
++               */
++              create_node : function (par, node, pos, callback, is_loaded) {
++                      if(par === null) { par = $.jstree.root; }
++                      par = this.get_node(par);
++                      if(!par) { return false; }
++                      pos = pos === undefined ? "last" : pos;
++                      if(!pos.toString().match(/^(before|after)$/) && !is_loaded && !this.is_loaded(par)) {
++                              return this.load_node(par, function () { this.create_node(par, node, pos, callback, true); });
++                      }
++                      if(!node) { node = { "text" : this.get_string('New node') }; }
++                      if(typeof node === "string") {
++                              node = { "text" : node };
++                      } else {
++                              node = $.extend(true, {}, node);
++                      }
++                      if(node.text === undefined) { node.text = this.get_string('New node'); }
++                      var tmp, dpc, i, j;
++
++                      if(par.id === $.jstree.root) {
++                              if(pos === "before") { pos = "first"; }
++                              if(pos === "after") { pos = "last"; }
++                      }
++                      switch(pos) {
++                              case "before":
++                                      tmp = this.get_node(par.parent);
++                                      pos = $.inArray(par.id, tmp.children);
++                                      par = tmp;
++                                      break;
++                              case "after" :
++                                      tmp = this.get_node(par.parent);
++                                      pos = $.inArray(par.id, tmp.children) + 1;
++                                      par = tmp;
++                                      break;
++                              case "inside":
++                              case "first":
++                                      pos = 0;
++                                      break;
++                              case "last":
++                                      pos = par.children.length;
++                                      break;
++                              default:
++                                      if(!pos) { pos = 0; }
++                                      break;
++                      }
++                      if(pos > par.children.length) { pos = par.children.length; }
++                      if(!node.id) { node.id = true; }
++                      if(!this.check("create_node", node, par, pos)) {
++                              this.settings.core.error.call(this, this._data.core.last_error);
++                              return false;
++                      }
++                      if(node.id === true) { delete node.id; }
++                      node = this._parse_model_from_json(node, par.id, par.parents.concat());
++                      if(!node) { return false; }
++                      tmp = this.get_node(node);
++                      dpc = [];
++                      dpc.push(node);
++                      dpc = dpc.concat(tmp.children_d);
++                      this.trigger('model', { "nodes" : dpc, "parent" : par.id });
++
++                      par.children_d = par.children_d.concat(dpc);
++                      for(i = 0, j = par.parents.length; i < j; i++) {
++                              this._model.data[par.parents[i]].children_d = this._model.data[par.parents[i]].children_d.concat(dpc);
++                      }
++                      node = tmp;
++                      tmp = [];
++                      for(i = 0, j = par.children.length; i < j; i++) {
++                              tmp[i >= pos ? i+1 : i] = par.children[i];
++                      }
++                      tmp[pos] = node.id;
++                      par.children = tmp;
++
++                      this.redraw_node(par, true);
++                      /**
++                       * triggered when a node is created
++                       * @event
++                       * @name create_node.jstree
++                       * @param {Object} node
++                       * @param {String} parent the parent's ID
++                       * @param {Number} position the position of the new node among the parent's children
++                       */
++                      this.trigger('create_node', { "node" : this.get_node(node), "parent" : par.id, "position" : pos });
++                      if(callback) { callback.call(this, this.get_node(node)); }
++                      return node.id;
++              },
++              /**
++               * set the text value of a node
++               * @name rename_node(obj, val)
++               * @param  {mixed} obj the node, you can pass an array to rename multiple nodes to the same name
++               * @param  {String} val the new text value
++               * @return {Boolean}
++               * @trigger rename_node.jstree
++               */
++              rename_node : function (obj, val) {
++                      var t1, t2, old;
++                      if($.isArray(obj)) {
++                              obj = obj.slice();
++                              for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
++                                      this.rename_node(obj[t1], val);
++                              }
++                              return true;
++                      }
++                      obj = this.get_node(obj);
++                      if(!obj || obj.id === $.jstree.root) { return false; }
++                      old = obj.text;
++                      if(!this.check("rename_node", obj, this.get_parent(obj), val)) {
++                              this.settings.core.error.call(this, this._data.core.last_error);
++                              return false;
++                      }
++                      this.set_text(obj, val); // .apply(this, Array.prototype.slice.call(arguments))
++                      /**
++                       * triggered when a node is renamed
++                       * @event
++                       * @name rename_node.jstree
++                       * @param {Object} node
++                       * @param {String} text the new value
++                       * @param {String} old the old value
++                       */
++                      this.trigger('rename_node', { "node" : obj, "text" : val, "old" : old });
++                      return true;
++              },
++              /**
++               * remove a node
++               * @name delete_node(obj)
++               * @param  {mixed} obj the node, you can pass an array to delete multiple nodes
++               * @return {Boolean}
++               * @trigger delete_node.jstree, changed.jstree
++               */
++              delete_node : function (obj) {
++                      var t1, t2, par, pos, tmp, i, j, k, l, c, top, lft;
++                      if($.isArray(obj)) {
++                              obj = obj.slice();
++                              for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
++                                      this.delete_node(obj[t1]);
++                              }
++                              return true;
++                      }
++                      obj = this.get_node(obj);
++                      if(!obj || obj.id === $.jstree.root) { return false; }
++                      par = this.get_node(obj.parent);
++                      pos = $.inArray(obj.id, par.children);
++                      c = false;
++                      if(!this.check("delete_node", obj, par, pos)) {
++                              this.settings.core.error.call(this, this._data.core.last_error);
++                              return false;
++                      }
++                      if(pos !== -1) {
++                              par.children = $.vakata.array_remove(par.children, pos);
++                      }
++                      tmp = obj.children_d.concat([]);
++                      tmp.push(obj.id);
++                      for(i = 0, j = obj.parents.length; i < j; i++) {
++                              this._model.data[obj.parents[i]].children_d = $.vakata.array_filter(this._model.data[obj.parents[i]].children_d, function (v) {
++                                      return $.inArray(v, tmp) === -1;
++                              });
++                      }
++                      for(k = 0, l = tmp.length; k < l; k++) {
++                              if(this._model.data[tmp[k]].state.selected) {
++                                      c = true;
++                                      break;
++                              }
++                      }
++                      if (c) {
++                              this._data.core.selected = $.vakata.array_filter(this._data.core.selected, function (v) {
++                                      return $.inArray(v, tmp) === -1;
++                              });
++                      }
++                      /**
++                       * triggered when a node is deleted
++                       * @event
++                       * @name delete_node.jstree
++                       * @param {Object} node
++                       * @param {String} parent the parent's ID
++                       */
++                      this.trigger('delete_node', { "node" : obj, "parent" : par.id });
++                      if(c) {
++                              this.trigger('changed', { 'action' : 'delete_node', 'node' : obj, 'selected' : this._data.core.selected, 'parent' : par.id });
++                      }
++                      for(k = 0, l = tmp.length; k < l; k++) {
++                              delete this._model.data[tmp[k]];
++                      }
++                      if($.inArray(this._data.core.focused, tmp) !== -1) {
++                              this._data.core.focused = null;
++                              top = this.element[0].scrollTop;
++                              lft = this.element[0].scrollLeft;
++                              if(par.id === $.jstree.root) {
++                                      if (this._model.data[$.jstree.root].children[0]) {
++                                              this.get_node(this._model.data[$.jstree.root].children[0], true).children('.jstree-anchor').focus();
++                                      }
++                              }
++                              else {
++                                      this.get_node(par, true).children('.jstree-anchor').focus();
++                              }
++                              this.element[0].scrollTop  = top;
++                              this.element[0].scrollLeft = lft;
++                      }
++                      this.redraw_node(par, true);
++                      return true;
++              },
++              /**
++               * check if an operation is premitted on the tree. Used internally.
++               * @private
++               * @name check(chk, obj, par, pos)
++               * @param  {String} chk the operation to check, can be "create_node", "rename_node", "delete_node", "copy_node" or "move_node"
++               * @param  {mixed} obj the node
++               * @param  {mixed} par the parent
++               * @param  {mixed} pos the position to insert at, or if "rename_node" - the new name
++               * @param  {mixed} more some various additional information, for example if a "move_node" operations is triggered by DND this will be the hovered node
++               * @return {Boolean}
++               */
++              check : function (chk, obj, par, pos, more) {
++                      obj = obj && obj.id ? obj : this.get_node(obj);
++                      par = par && par.id ? par : this.get_node(par);
++                      var tmp = chk.match(/^move_node|copy_node|create_node$/i) ? par : obj,
++                              chc = this.settings.core.check_callback;
++                      if(chk === "move_node" || chk === "copy_node") {
++                              if((!more || !more.is_multi) && (obj.id === par.id || (chk === "move_node" && $.inArray(obj.id, par.children) === pos) || $.inArray(par.id, obj.children_d) !== -1)) {
++                                      this._data.core.last_error = { 'error' : 'check', 'plugin' : 'core', 'id' : 'core_01', 'reason' : 'Moving parent inside child', 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };
++                                      return false;
++                              }
++                      }
++                      if(tmp && tmp.data) { tmp = tmp.data; }
++                      if(tmp && tmp.functions && (tmp.functions[chk] === false || tmp.functions[chk] === true)) {
++                              if(tmp.functions[chk] === false) {
++                                      this._data.core.last_error = { 'error' : 'check', 'plugin' : 'core', 'id' : 'core_02', 'reason' : 'Node data prevents function: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };
++                              }
++                              return tmp.functions[chk];
++                      }
++                      if(chc === false || ($.isFunction(chc) && chc.call(this, chk, obj, par, pos, more) === false) || (chc && chc[chk] === false)) {
++                              this._data.core.last_error = { 'error' : 'check', 'plugin' : 'core', 'id' : 'core_03', 'reason' : 'User config for core.check_callback prevents function: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) };
++                              return false;
++                      }
++                      return true;
++              },
++              /**
++               * get the last error
++               * @name last_error()
++               * @return {Object}
++               */
++              last_error : function () {
++                      return this._data.core.last_error;
++              },
++              /**
++               * move a node to a new parent
++               * @name move_node(obj, par [, pos, callback, is_loaded])
++               * @param  {mixed} obj the node to move, pass an array to move multiple nodes
++               * @param  {mixed} par the new parent
++               * @param  {mixed} pos the position to insert at (besides integer values, "first" and "last" are supported, as well as "before" and "after"), defaults to integer `0`
++               * @param  {function} callback a function to call once the move is completed, receives 3 arguments - the node, the new parent and the position
++               * @param  {Boolean} is_loaded internal parameter indicating if the parent node has been loaded
++               * @param  {Boolean} skip_redraw internal parameter indicating if the tree should be redrawn
++               * @param  {Boolean} instance internal parameter indicating if the node comes from another instance
++               * @trigger move_node.jstree
++               */
++              move_node : function (obj, par, pos, callback, is_loaded, skip_redraw, origin) {
++                      var t1, t2, old_par, old_pos, new_par, old_ins, is_multi, dpc, tmp, i, j, k, l, p;
++
++                      par = this.get_node(par);
++                      pos = pos === undefined ? 0 : pos;
++                      if(!par) { return false; }
++                      if(!pos.toString().match(/^(before|after)$/) && !is_loaded && !this.is_loaded(par)) {
++                              return this.load_node(par, function () { this.move_node(obj, par, pos, callback, true, false, origin); });
++                      }
++
++                      if($.isArray(obj)) {
++                              if(obj.length === 1) {
++                                      obj = obj[0];
++                              }
++                              else {
++                                      //obj = obj.slice();
++                                      for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
++                                              if((tmp = this.move_node(obj[t1], par, pos, callback, is_loaded, false, origin))) {
++                                                      par = tmp;
++                                                      pos = "after";
++                                              }
++                                      }
++                                      this.redraw();
++                                      return true;
++                              }
++                      }
++                      obj = obj && obj.id ? obj : this.get_node(obj);
++
++                      if(!obj || obj.id === $.jstree.root) { return false; }
++
++                      old_par = (obj.parent || $.jstree.root).toString();
++                      new_par = (!pos.toString().match(/^(before|after)$/) || par.id === $.jstree.root) ? par : this.get_node(par.parent);
++                      old_ins = origin ? origin : (this._model.data[obj.id] ? this : $.jstree.reference(obj.id));
++                      is_multi = !old_ins || !old_ins._id || (this._id !== old_ins._id);
++                      old_pos = old_ins && old_ins._id && old_par && old_ins._model.data[old_par] && old_ins._model.data[old_par].children ? $.inArray(obj.id, old_ins._model.data[old_par].children) : -1;
++                      if(old_ins && old_ins._id) {
++                              obj = old_ins._model.data[obj.id];
++                      }
++
++                      if(is_multi) {
++                              if((tmp = this.copy_node(obj, par, pos, callback, is_loaded, false, origin))) {
++                                      if(old_ins) { old_ins.delete_node(obj); }
++                                      return tmp;
++                              }
++                              return false;
++                      }
++                      //var m = this._model.data;
++                      if(par.id === $.jstree.root) {
++                              if(pos === "before") { pos = "first"; }
++                              if(pos === "after") { pos = "last"; }
++                      }
++                      switch(pos) {
++                              case "before":
++                                      pos = $.inArray(par.id, new_par.children);
++                                      break;
++                              case "after" :
++                                      pos = $.inArray(par.id, new_par.children) + 1;
++                                      break;
++                              case "inside":
++                              case "first":
++                                      pos = 0;
++                                      break;
++                              case "last":
++                                      pos = new_par.children.length;
++                                      break;
++                              default:
++                                      if(!pos) { pos = 0; }
++                                      break;
++                      }
++                      if(pos > new_par.children.length) { pos = new_par.children.length; }
++                      if(!this.check("move_node", obj, new_par, pos, { 'core' : true, 'origin' : origin, 'is_multi' : (old_ins && old_ins._id && old_ins._id !== this._id), 'is_foreign' : (!old_ins || !old_ins._id) })) {
++                              this.settings.core.error.call(this, this._data.core.last_error);
++                              return false;
++                      }
++                      if(obj.parent === new_par.id) {
++                              dpc = new_par.children.concat();
++                              tmp = $.inArray(obj.id, dpc);
++                              if(tmp !== -1) {
++                                      dpc = $.vakata.array_remove(dpc, tmp);
++                                      if(pos > tmp) { pos--; }
++                              }
++                              tmp = [];
++                              for(i = 0, j = dpc.length; i < j; i++) {
++                                      tmp[i >= pos ? i+1 : i] = dpc[i];
++                              }
++                              tmp[pos] = obj.id;
++                              new_par.children = tmp;
++                              this._node_changed(new_par.id);
++                              this.redraw(new_par.id === $.jstree.root);
++                      }
++                      else {
++                              // clean old parent and up
++                              tmp = obj.children_d.concat();
++                              tmp.push(obj.id);
++                              for(i = 0, j = obj.parents.length; i < j; i++) {
++                                      dpc = [];
++                                      p = old_ins._model.data[obj.parents[i]].children_d;
++                                      for(k = 0, l = p.length; k < l; k++) {
++                                              if($.inArray(p[k], tmp) === -1) {
++                                                      dpc.push(p[k]);
++                                              }
++                                      }
++                                      old_ins._model.data[obj.parents[i]].children_d = dpc;
++                              }
++                              old_ins._model.data[old_par].children = $.vakata.array_remove_item(old_ins._model.data[old_par].children, obj.id);
++
++                              // insert into new parent and up
++                              for(i = 0, j = new_par.parents.length; i < j; i++) {
++                                      this._model.data[new_par.parents[i]].children_d = this._model.data[new_par.parents[i]].children_d.concat(tmp);
++                              }
++                              dpc = [];
++                              for(i = 0, j = new_par.children.length; i < j; i++) {
++                                      dpc[i >= pos ? i+1 : i] = new_par.children[i];
++                              }
++                              dpc[pos] = obj.id;
++                              new_par.children = dpc;
++                              new_par.children_d.push(obj.id);
++                              new_par.children_d = new_par.children_d.concat(obj.children_d);
++
++                              // update object
++                              obj.parent = new_par.id;
++                              tmp = new_par.parents.concat();
++                              tmp.unshift(new_par.id);
++                              p = obj.parents.length;
++                              obj.parents = tmp;
++
++                              // update object children
++                              tmp = tmp.concat();
++                              for(i = 0, j = obj.children_d.length; i < j; i++) {
++                                      this._model.data[obj.children_d[i]].parents = this._model.data[obj.children_d[i]].parents.slice(0,p*-1);
++                                      Array.prototype.push.apply(this._model.data[obj.children_d[i]].parents, tmp);
++                              }
++
++                              if(old_par === $.jstree.root || new_par.id === $.jstree.root) {
++                                      this._model.force_full_redraw = true;
++                              }
++                              if(!this._model.force_full_redraw) {
++                                      this._node_changed(old_par);
++                                      this._node_changed(new_par.id);
++                              }
++                              if(!skip_redraw) {
++                                      this.redraw();
++                              }
++                      }
++                      if(callback) { callback.call(this, obj, new_par, pos); }
++                      /**
++                       * triggered when a node is moved
++                       * @event
++                       * @name move_node.jstree
++                       * @param {Object} node
++                       * @param {String} parent the parent's ID
++                       * @param {Number} position the position of the node among the parent's children
++                       * @param {String} old_parent the old parent of the node
++                       * @param {Number} old_position the old position of the node
++                       * @param {Boolean} is_multi do the node and new parent belong to different instances
++                       * @param {jsTree} old_instance the instance the node came from
++                       * @param {jsTree} new_instance the instance of the new parent
++                       */
++                      this.trigger('move_node', { "node" : obj, "parent" : new_par.id, "position" : pos, "old_parent" : old_par, "old_position" : old_pos, 'is_multi' : (old_ins && old_ins._id && old_ins._id !== this._id), 'is_foreign' : (!old_ins || !old_ins._id), 'old_instance' : old_ins, 'new_instance' : this });
++                      return obj.id;
++              },
++              /**
++               * copy a node to a new parent
++               * @name copy_node(obj, par [, pos, callback, is_loaded])
++               * @param  {mixed} obj the node to copy, pass an array to copy multiple nodes
++               * @param  {mixed} par the new parent
++               * @param  {mixed} pos the position to insert at (besides integer values, "first" and "last" are supported, as well as "before" and "after"), defaults to integer `0`
++               * @param  {function} callback a function to call once the move is completed, receives 3 arguments - the node, the new parent and the position
++               * @param  {Boolean} is_loaded internal parameter indicating if the parent node has been loaded
++               * @param  {Boolean} skip_redraw internal parameter indicating if the tree should be redrawn
++               * @param  {Boolean} instance internal parameter indicating if the node comes from another instance
++               * @trigger model.jstree copy_node.jstree
++               */
++              copy_node : function (obj, par, pos, callback, is_loaded, skip_redraw, origin) {
++                      var t1, t2, dpc, tmp, i, j, node, old_par, new_par, old_ins, is_multi;
++
++                      par = this.get_node(par);
++                      pos = pos === undefined ? 0 : pos;
++                      if(!par) { return false; }
++                      if(!pos.toString().match(/^(before|after)$/) && !is_loaded && !this.is_loaded(par)) {
++                              return this.load_node(par, function () { this.copy_node(obj, par, pos, callback, true, false, origin); });
++                      }
++
++                      if($.isArray(obj)) {
++                              if(obj.length === 1) {
++                                      obj = obj[0];
++                              }
++                              else {
++                                      //obj = obj.slice();
++                                      for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
++                                              if((tmp = this.copy_node(obj[t1], par, pos, callback, is_loaded, true, origin))) {
++                                                      par = tmp;
++                                                      pos = "after";
++                                              }
++                                      }
++                                      this.redraw();
++                                      return true;
++                              }
++                      }
++                      obj = obj && obj.id ? obj : this.get_node(obj);
++                      if(!obj || obj.id === $.jstree.root) { return false; }
++
++                      old_par = (obj.parent || $.jstree.root).toString();
++                      new_par = (!pos.toString().match(/^(before|after)$/) || par.id === $.jstree.root) ? par : this.get_node(par.parent);
++                      old_ins = origin ? origin : (this._model.data[obj.id] ? this : $.jstree.reference(obj.id));
++                      is_multi = !old_ins || !old_ins._id || (this._id !== old_ins._id);
++
++                      if(old_ins && old_ins._id) {
++                              obj = old_ins._model.data[obj.id];
++                      }
++
++                      if(par.id === $.jstree.root) {
++                              if(pos === "before") { pos = "first"; }
++                              if(pos === "after") { pos = "last"; }
++                      }
++                      switch(pos) {
++                              case "before":
++                                      pos = $.inArray(par.id, new_par.children);
++                                      break;
++                              case "after" :
++                                      pos = $.inArray(par.id, new_par.children) + 1;
++                                      break;
++                              case "inside":
++                              case "first":
++                                      pos = 0;
++                                      break;
++                              case "last":
++                                      pos = new_par.children.length;
++                                      break;
++                              default:
++                                      if(!pos) { pos = 0; }
++                                      break;
++                      }
++                      if(pos > new_par.children.length) { pos = new_par.children.length; }
++                      if(!this.check("copy_node", obj, new_par, pos, { 'core' : true, 'origin' : origin, 'is_multi' : (old_ins && old_ins._id && old_ins._id !== this._id), 'is_foreign' : (!old_ins || !old_ins._id) })) {
++                              this.settings.core.error.call(this, this._data.core.last_error);
++                              return false;
++                      }
++                      node = old_ins ? old_ins.get_json(obj, { no_id : true, no_data : true, no_state : true }) : obj;
++                      if(!node) { return false; }
++                      if(node.id === true) { delete node.id; }
++                      node = this._parse_model_from_json(node, new_par.id, new_par.parents.concat());
++                      if(!node) { return false; }
++                      tmp = this.get_node(node);
++                      if(obj && obj.state && obj.state.loaded === false) { tmp.state.loaded = false; }
++                      dpc = [];
++                      dpc.push(node);
++                      dpc = dpc.concat(tmp.children_d);
++                      this.trigger('model', { "nodes" : dpc, "parent" : new_par.id });
++
++                      // insert into new parent and up
++                      for(i = 0, j = new_par.parents.length; i < j; i++) {
++                              this._model.data[new_par.parents[i]].children_d = this._model.data[new_par.parents[i]].children_d.concat(dpc);
++                      }
++                      dpc = [];
++                      for(i = 0, j = new_par.children.length; i < j; i++) {
++                              dpc[i >= pos ? i+1 : i] = new_par.children[i];
++                      }
++                      dpc[pos] = tmp.id;
++                      new_par.children = dpc;
++                      new_par.children_d.push(tmp.id);
++                      new_par.children_d = new_par.children_d.concat(tmp.children_d);
++
++                      if(new_par.id === $.jstree.root) {
++                              this._model.force_full_redraw = true;
++                      }
++                      if(!this._model.force_full_redraw) {
++                              this._node_changed(new_par.id);
++                      }
++                      if(!skip_redraw) {
++                              this.redraw(new_par.id === $.jstree.root);
++                      }
++                      if(callback) { callback.call(this, tmp, new_par, pos); }
++                      /**
++                       * triggered when a node is copied
++                       * @event
++                       * @name copy_node.jstree
++                       * @param {Object} node the copied node
++                       * @param {Object} original the original node
++                       * @param {String} parent the parent's ID
++                       * @param {Number} position the position of the node among the parent's children
++                       * @param {String} old_parent the old parent of the node
++                       * @param {Number} old_position the position of the original node
++                       * @param {Boolean} is_multi do the node and new parent belong to different instances
++                       * @param {jsTree} old_instance the instance the node came from
++                       * @param {jsTree} new_instance the instance of the new parent
++                       */
++                      this.trigger('copy_node', { "node" : tmp, "original" : obj, "parent" : new_par.id, "position" : pos, "old_parent" : old_par, "old_position" : old_ins && old_ins._id && old_par && old_ins._model.data[old_par] && old_ins._model.data[old_par].children ? $.inArray(obj.id, old_ins._model.data[old_par].children) : -1,'is_multi' : (old_ins && old_ins._id && old_ins._id !== this._id), 'is_foreign' : (!old_ins || !old_ins._id), 'old_instance' : old_ins, 'new_instance' : this });
++                      return tmp.id;
++              },
++              /**
++               * cut a node (a later call to `paste(obj)` would move the node)
++               * @name cut(obj)
++               * @param  {mixed} obj multiple objects can be passed using an array
++               * @trigger cut.jstree
++               */
++              cut : function (obj) {
++                      if(!obj) { obj = this._data.core.selected.concat(); }
++                      if(!$.isArray(obj)) { obj = [obj]; }
++                      if(!obj.length) { return false; }
++                      var tmp = [], o, t1, t2;
++                      for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
++                              o = this.get_node(obj[t1]);
++                              if(o && o.id && o.id !== $.jstree.root) { tmp.push(o); }
++                      }
++                      if(!tmp.length) { return false; }
++                      ccp_node = tmp;
++                      ccp_inst = this;
++                      ccp_mode = 'move_node';
++                      /**
++                       * triggered when nodes are added to the buffer for moving
++                       * @event
++                       * @name cut.jstree
++                       * @param {Array} node
++                       */
++                      this.trigger('cut', { "node" : obj });
++              },
++              /**
++               * copy a node (a later call to `paste(obj)` would copy the node)
++               * @name copy(obj)
++               * @param  {mixed} obj multiple objects can be passed using an array
++               * @trigger copy.jstree
++               */
++              copy : function (obj) {
++                      if(!obj) { obj = this._data.core.selected.concat(); }
++                      if(!$.isArray(obj)) { obj = [obj]; }
++                      if(!obj.length) { return false; }
++                      var tmp = [], o, t1, t2;
++                      for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
++                              o = this.get_node(obj[t1]);
++                              if(o && o.id && o.id !== $.jstree.root) { tmp.push(o); }
++                      }
++                      if(!tmp.length) { return false; }
++                      ccp_node = tmp;
++                      ccp_inst = this;
++                      ccp_mode = 'copy_node';
++                      /**
++                       * triggered when nodes are added to the buffer for copying
++                       * @event
++                       * @name copy.jstree
++                       * @param {Array} node
++                       */
++                      this.trigger('copy', { "node" : obj });
++              },
++              /**
++               * get the current buffer (any nodes that are waiting for a paste operation)
++               * @name get_buffer()
++               * @return {Object} an object consisting of `mode` ("copy_node" or "move_node"), `node` (an array of objects) and `inst` (the instance)
++               */
++              get_buffer : function () {
++                      return { 'mode' : ccp_mode, 'node' : ccp_node, 'inst' : ccp_inst };
++              },
++              /**
++               * check if there is something in the buffer to paste
++               * @name can_paste()
++               * @return {Boolean}
++               */
++              can_paste : function () {
++                      return ccp_mode !== false && ccp_node !== false; // && ccp_inst._model.data[ccp_node];
++              },
++              /**
++               * copy or move the previously cut or copied nodes to a new parent
++               * @name paste(obj [, pos])
++               * @param  {mixed} obj the new parent
++               * @param  {mixed} pos the position to insert at (besides integer, "first" and "last" are supported), defaults to integer `0`
++               * @trigger paste.jstree
++               */
++              paste : function (obj, pos) {
++                      obj = this.get_node(obj);
++                      if(!obj || !ccp_mode || !ccp_mode.match(/^(copy_node|move_node)$/) || !ccp_node) { return false; }
++                      if(this[ccp_mode](ccp_node, obj, pos, false, false, false, ccp_inst)) {
++                              /**
++                               * triggered when paste is invoked
++                               * @event
++                               * @name paste.jstree
++                               * @param {String} parent the ID of the receiving node
++                               * @param {Array} node the nodes in the buffer
++                               * @param {String} mode the performed operation - "copy_node" or "move_node"
++                               */
++                              this.trigger('paste', { "parent" : obj.id, "node" : ccp_node, "mode" : ccp_mode });
++                      }
++                      ccp_node = false;
++                      ccp_mode = false;
++                      ccp_inst = false;
++              },
++              /**
++               * clear the buffer of previously copied or cut nodes
++               * @name clear_buffer()
++               * @trigger clear_buffer.jstree
++               */
++              clear_buffer : function () {
++                      ccp_node = false;
++                      ccp_mode = false;
++                      ccp_inst = false;
++                      /**
++                       * triggered when the copy / cut buffer is cleared
++                       * @event
++                       * @name clear_buffer.jstree
++                       */
++                      this.trigger('clear_buffer');
++              },
++              /**
++               * put a node in edit mode (input field to rename the node)
++               * @name edit(obj [, default_text, callback])
++               * @param  {mixed} obj
++               * @param  {String} default_text the text to populate the input with (if omitted or set to a non-string value the node's text value is used)
++               * @param  {Function} callback a function to be called once the text box is blurred, it is called in the instance's scope and receives the node, a status parameter (true if the rename is successful, false otherwise) and a boolean indicating if the user cancelled the edit. You can access the node's title using .text
++               */
++              edit : function (obj, default_text, callback) {
++                      var rtl, w, a, s, t, h1, h2, fn, tmp, cancel = false;
++                      obj = this.get_node(obj);
++                      if(!obj) { return false; }
++                      if(!this.check("edit", obj, this.get_parent(obj))) {
++                              this.settings.core.error.call(this, this._data.core.last_error);
++                              return false;
++                      }
++                      tmp = obj;
++                      default_text = typeof default_text === 'string' ? default_text : obj.text;
++                      this.set_text(obj, "");
++                      obj = this._open_to(obj);
++                      tmp.text = default_text;
++
++                      rtl = this._data.core.rtl;
++                      w  = this.element.width();
++                      this._data.core.focused = tmp.id;
++                      a  = obj.children('.jstree-anchor').focus();
++                      s  = $('<span>');
++                      /*!
++                      oi = obj.children("i:visible"),
++                      ai = a.children("i:visible"),
++                      w1 = oi.width() * oi.length,
++                      w2 = ai.width() * ai.length,
++                      */
++                      t  = default_text;
++                      h1 = $("<"+"div />", { css : { "position" : "absolute", "top" : "-200px", "left" : (rtl ? "0px" : "-1000px"), "visibility" : "hidden" } }).appendTo("body");
++                      h2 = $("<"+"input />", {
++                                              "value" : t,
++                                              "class" : "jstree-rename-input",
++                                              // "size" : t.length,
++                                              "css" : {
++                                                      "padding" : "0",
++                                                      "border" : "1px solid silver",
++                                                      "box-sizing" : "border-box",
++                                                      "display" : "inline-block",
++                                                      "height" : (this._data.core.li_height) + "px",
++                                                      "lineHeight" : (this._data.core.li_height) + "px",
++                                                      "width" : "150px" // will be set a bit further down
++                                              },
++                                              "blur" : $.proxy(function (e) {
++                                                      e.stopImmediatePropagation();
++                                                      e.preventDefault();
++                                                      var i = s.children(".jstree-rename-input"),
++                                                              v = i.val(),
++                                                              f = this.settings.core.force_text,
++                                                              nv;
++                                                      if(v === "") { v = t; }
++                                                      h1.remove();
++                                                      s.replaceWith(a);
++                                                      s.remove();
++                                                      t = f ? t : $('<div></div>').append($.parseHTML(t)).html();
++                                                      this.set_text(obj, t);
++                                                      nv = !!this.rename_node(obj, f ? $('<div></div>').text(v).text() : $('<div></div>').append($.parseHTML(v)).html());
++                                                      if(!nv) {
++                                                              this.set_text(obj, t); // move this up? and fix #483
++                                                      }
++                                                      this._data.core.focused = tmp.id;
++                                                      setTimeout($.proxy(function () {
++                                                              var node = this.get_node(tmp.id, true);
++                                                              if(node.length) {
++                                                                      this._data.core.focused = tmp.id;
++                                                                      node.children('.jstree-anchor').focus();
++                                                              }
++                                                      }, this), 0);
++                                                      if(callback) {
++                                                              callback.call(this, tmp, nv, cancel);
++                                                      }
++                                                      h2 = null;
++                                              }, this),
++                                              "keydown" : function (e) {
++                                                      var key = e.which;
++                                                      if(key === 27) {
++                                                              cancel = true;
++                                                              this.value = t;
++                                                      }
++                                                      if(key === 27 || key === 13 || key === 37 || key === 38 || key === 39 || key === 40 || key === 32) {
++                                                              e.stopImmediatePropagation();
++                                                      }
++                                                      if(key === 27 || key === 13) {
++                                                              e.preventDefault();
++                                                              this.blur();
++                                                      }
++                                              },
++                                              "click" : function (e) { e.stopImmediatePropagation(); },
++                                              "mousedown" : function (e) { e.stopImmediatePropagation(); },
++                                              "keyup" : function (e) {
++                                                      h2.width(Math.min(h1.text("pW" + this.value).width(),w));
++                                              },
++                                              "keypress" : function(e) {
++                                                      if(e.which === 13) { return false; }
++                                              }
++                                      });
++                              fn = {
++                                              fontFamily              : a.css('fontFamily')           || '',
++                                              fontSize                : a.css('fontSize')                     || '',
++                                              fontWeight              : a.css('fontWeight')           || '',
++                                              fontStyle               : a.css('fontStyle')            || '',
++                                              fontStretch             : a.css('fontStretch')          || '',
++                                              fontVariant             : a.css('fontVariant')          || '',
++                                              letterSpacing   : a.css('letterSpacing')        || '',
++                                              wordSpacing             : a.css('wordSpacing')          || ''
++                              };
++                      s.attr('class', a.attr('class')).append(a.contents().clone()).append(h2);
++                      a.replaceWith(s);
++                      h1.css(fn);
++                      h2.css(fn).width(Math.min(h1.text("pW" + h2[0].value).width(),w))[0].select();
++                      $(document).one('mousedown.jstree touchstart.jstree dnd_start.vakata', function (e) {
++                              if (h2 && e.target !== h2) {
++                                      $(h2).blur();
++                              }
++                      });
++              },
++
++
++              /**
++               * changes the theme
++               * @name set_theme(theme_name [, theme_url])
++               * @param {String} theme_name the name of the new theme to apply
++               * @param {mixed} theme_url  the location of the CSS file for this theme. Omit or set to `false` if you manually included the file. Set to `true` to autoload from the `core.themes.dir` directory.
++               * @trigger set_theme.jstree
++               */
++              set_theme : function (theme_name, theme_url) {
++                      if(!theme_name) { return false; }
++                      if(theme_url === true) {
++                              var dir = this.settings.core.themes.dir;
++                              if(!dir) { dir = $.jstree.path + '/themes'; }
++                              theme_url = dir + '/' + theme_name + '/style.css';
++                      }
++                      if(theme_url && $.inArray(theme_url, themes_loaded) === -1) {
++                              $('head').append('<'+'link rel="stylesheet" href="' + theme_url + '" type="text/css" />');
++                              themes_loaded.push(theme_url);
++                      }
++                      if(this._data.core.themes.name) {
++                              this.element.removeClass('jstree-' + this._data.core.themes.name);
++                      }
++                      this._data.core.themes.name = theme_name;
++                      this.element.addClass('jstree-' + theme_name);
++                      this.element[this.settings.core.themes.responsive ? 'addClass' : 'removeClass' ]('jstree-' + theme_name + '-responsive');
++                      /**
++                       * triggered when a theme is set
++                       * @event
++                       * @name set_theme.jstree
++                       * @param {String} theme the new theme
++                       */
++                      this.trigger('set_theme', { 'theme' : theme_name });
++              },
++              /**
++               * gets the name of the currently applied theme name
++               * @name get_theme()
++               * @return {String}
++               */
++              get_theme : function () { return this._data.core.themes.name; },
++              /**
++               * changes the theme variant (if the theme has variants)
++               * @name set_theme_variant(variant_name)
++               * @param {String|Boolean} variant_name the variant to apply (if `false` is used the current variant is removed)
++               */
++              set_theme_variant : function (variant_name) {
++                      if(this._data.core.themes.variant) {
++                              this.element.removeClass('jstree-' + this._data.core.themes.name + '-' + this._data.core.themes.variant);
++                      }
++                      this._data.core.themes.variant = variant_name;
++                      if(variant_name) {
++                              this.element.addClass('jstree-' + this._data.core.themes.name + '-' + this._data.core.themes.variant);
++                      }
++              },
++              /**
++               * gets the name of the currently applied theme variant
++               * @name get_theme()
++               * @return {String}
++               */
++              get_theme_variant : function () { return this._data.core.themes.variant; },
++              /**
++               * shows a striped background on the container (if the theme supports it)
++               * @name show_stripes()
++               */
++              show_stripes : function () {
++                      this._data.core.themes.stripes = true;
++                      this.get_container_ul().addClass("jstree-striped");
++                      /**
++                       * triggered when stripes are shown
++                       * @event
++                       * @name show_stripes.jstree
++                       */
++                      this.trigger('show_stripes');
++              },
++              /**
++               * hides the striped background on the container
++               * @name hide_stripes()
++               */
++              hide_stripes : function () {
++                      this._data.core.themes.stripes = false;
++                      this.get_container_ul().removeClass("jstree-striped");
++                      /**
++                       * triggered when stripes are hidden
++                       * @event
++                       * @name hide_stripes.jstree
++                       */
++                      this.trigger('hide_stripes');
++              },
++              /**
++               * toggles the striped background on the container
++               * @name toggle_stripes()
++               */
++              toggle_stripes : function () { if(this._data.core.themes.stripes) { this.hide_stripes(); } else { this.show_stripes(); } },
++              /**
++               * shows the connecting dots (if the theme supports it)
++               * @name show_dots()
++               */
++              show_dots : function () {
++                      this._data.core.themes.dots = true;
++                      this.get_container_ul().removeClass("jstree-no-dots");
++                      /**
++                       * triggered when dots are shown
++                       * @event
++                       * @name show_dots.jstree
++                       */
++                      this.trigger('show_dots');
++              },
++              /**
++               * hides the connecting dots
++               * @name hide_dots()
++               */
++              hide_dots : function () {
++                      this._data.core.themes.dots = false;
++                      this.get_container_ul().addClass("jstree-no-dots");
++                      /**
++                       * triggered when dots are hidden
++                       * @event
++                       * @name hide_dots.jstree
++                       */
++                      this.trigger('hide_dots');
++              },
++              /**
++               * toggles the connecting dots
++               * @name toggle_dots()
++               */
++              toggle_dots : function () { if(this._data.core.themes.dots) { this.hide_dots(); } else { this.show_dots(); } },
++              /**
++               * show the node icons
++               * @name show_icons()
++               */
++              show_icons : function () {
++                      this._data.core.themes.icons = true;
++                      this.get_container_ul().removeClass("jstree-no-icons");
++                      /**
++                       * triggered when icons are shown
++                       * @event
++                       * @name show_icons.jstree
++                       */
++                      this.trigger('show_icons');
++              },
++              /**
++               * hide the node icons
++               * @name hide_icons()
++               */
++              hide_icons : function () {
++                      this._data.core.themes.icons = false;
++                      this.get_container_ul().addClass("jstree-no-icons");
++                      /**
++                       * triggered when icons are hidden
++                       * @event
++                       * @name hide_icons.jstree
++                       */
++                      this.trigger('hide_icons');
++              },
++              /**
++               * toggle the node icons
++               * @name toggle_icons()
++               */
++              toggle_icons : function () { if(this._data.core.themes.icons) { this.hide_icons(); } else { this.show_icons(); } },
++              /**
++               * show the node ellipsis
++               * @name show_icons()
++               */
++              show_ellipsis : function () {
++                      this._data.core.themes.ellipsis = true;
++                      this.get_container_ul().addClass("jstree-ellipsis");
++                      /**
++                       * triggered when ellisis is shown
++                       * @event
++                       * @name show_ellipsis.jstree
++                       */
++                      this.trigger('show_ellipsis');
++              },
++              /**
++               * hide the node ellipsis
++               * @name hide_ellipsis()
++               */
++              hide_ellipsis : function () {
++                      this._data.core.themes.ellipsis = false;
++                      this.get_container_ul().removeClass("jstree-ellipsis");
++                      /**
++                       * triggered when ellisis is hidden
++                       * @event
++                       * @name hide_ellipsis.jstree
++                       */
++                      this.trigger('hide_ellipsis');
++              },
++              /**
++               * toggle the node ellipsis
++               * @name toggle_icons()
++               */
++              toggle_ellipsis : function () { if(this._data.core.themes.ellipsis) { this.hide_ellipsis(); } else { this.show_ellipsis(); } },
++              /**
++               * set the node icon for a node
++               * @name set_icon(obj, icon)
++               * @param {mixed} obj
++               * @param {String} icon the new icon - can be a path to an icon or a className, if using an image that is in the current directory use a `./` prefix, otherwise it will be detected as a class
++               */
++              set_icon : function (obj, icon) {
++                      var t1, t2, dom, old;
++                      if($.isArray(obj)) {
++                              obj = obj.slice();
++                              for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
++                                      this.set_icon(obj[t1], icon);
++                              }
++                              return true;
++                      }
++                      obj = this.get_node(obj);
++                      if(!obj || obj.id === $.jstree.root) { return false; }
++                      old = obj.icon;
++                      obj.icon = icon === true || icon === null || icon === undefined || icon === '' ? true : icon;
++                      dom = this.get_node(obj, true).children(".jstree-anchor").children(".jstree-themeicon");
++                      if(icon === false) {
++                              dom.removeClass('jstree-themeicon-custom ' + old).css("background","").removeAttr("rel");
++                              this.hide_icon(obj);
++                      }
++                      else if(icon === true || icon === null || icon === undefined || icon === '') {
++                              dom.removeClass('jstree-themeicon-custom ' + old).css("background","").removeAttr("rel");
++                              if(old === false) { this.show_icon(obj); }
++                      }
++                      else if(icon.indexOf("/") === -1 && icon.indexOf(".") === -1) {
++                              dom.removeClass(old).css("background","");
++                              dom.addClass(icon + ' jstree-themeicon-custom').attr("rel",icon);
++                              if(old === false) { this.show_icon(obj); }
++                      }
++                      else {
++                              dom.removeClass(old).css("background","");
++                              dom.addClass('jstree-themeicon-custom').css("background", "url('" + icon + "') center center no-repeat").attr("rel",icon);
++                              if(old === false) { this.show_icon(obj); }
++                      }
++                      return true;
++              },
++              /**
++               * get the node icon for a node
++               * @name get_icon(obj)
++               * @param {mixed} obj
++               * @return {String}
++               */
++              get_icon : function (obj) {
++                      obj = this.get_node(obj);
++                      return (!obj || obj.id === $.jstree.root) ? false : obj.icon;
++              },
++              /**
++               * hide the icon on an individual node
++               * @name hide_icon(obj)
++               * @param {mixed} obj
++               */
++              hide_icon : function (obj) {
++                      var t1, t2;
++                      if($.isArray(obj)) {
++                              obj = obj.slice();
++                              for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
++                                      this.hide_icon(obj[t1]);
++                              }
++                              return true;
++                      }
++                      obj = this.get_node(obj);
++                      if(!obj || obj === $.jstree.root) { return false; }
++                      obj.icon = false;
++                      this.get_node(obj, true).children(".jstree-anchor").children(".jstree-themeicon").addClass('jstree-themeicon-hidden');
++                      return true;
++              },
++              /**
++               * show the icon on an individual node
++               * @name show_icon(obj)
++               * @param {mixed} obj
++               */
++              show_icon : function (obj) {
++                      var t1, t2, dom;
++                      if($.isArray(obj)) {
++                              obj = obj.slice();
++                              for(t1 = 0, t2 = obj.length; t1 < t2; t1++) {
++                                      this.show_icon(obj[t1]);
++                              }
++                              return true;
++                      }
++                      obj = this.get_node(obj);
++                      if(!obj || obj === $.jstree.root) { return false; }
++                      dom = this.get_node(obj, true);
++                      obj.icon = dom.length ? dom.children(".jstree-anchor").children(".jstree-themeicon").attr('rel') : true;
++                      if(!obj.icon) { obj.icon = true; }
++                      dom.children(".jstree-anchor").children(".jstree-themeicon").removeClass('jstree-themeicon-hidden');
++                      return true;
++              }
++      };
++
++      // helpers
++      $.vakata = {};
++      // collect attributes
++      $.vakata.attributes = function(node, with_values) {
++              node = $(node)[0];
++              var attr = with_values ? {} : [];
++              if(node && node.attributes) {
++                      $.each(node.attributes, function (i, v) {
++                              if($.inArray(v.name.toLowerCase(),['style','contenteditable','hasfocus','tabindex']) !== -1) { return; }
++                              if(v.value !== null && $.trim(v.value) !== '') {
++                                      if(with_values) { attr[v.name] = v.value; }
++                                      else { attr.push(v.name); }
++                              }
++                      });
++              }
++              return attr;
++      };
++      $.vakata.array_unique = function(array) {
++              var a = [], i, j, l, o = {};
++              for(i = 0, l = array.length; i < l; i++) {
++                      if(o[array[i]] === undefined) {
++                              a.push(array[i]);
++                              o[array[i]] = true;
++                      }
++              }
++              return a;
++      };
++      // remove item from array
++      $.vakata.array_remove = function(array, from) {
++              array.splice(from, 1);
++              return array;
++              //var rest = array.slice((to || from) + 1 || array.length);
++              //array.length = from < 0 ? array.length + from : from;
++              //array.push.apply(array, rest);
++              //return array;
++      };
++      // remove item from array
++      $.vakata.array_remove_item = function(array, item) {
++              var tmp = $.inArray(item, array);
++              return tmp !== -1 ? $.vakata.array_remove(array, tmp) : array;
++      };
++      $.vakata.array_filter = function(c,a,b,d,e) {
++              if (c.filter) {
++                      return c.filter(a, b);
++              }
++              d=[];
++              for (e in c) {
++                      if (~~e+''===e+'' && e>=0 && a.call(b,c[e],+e,c)) {
++                              d.push(c[e]);
++                      }
++              }
++              return d;
++      };
++}));
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9428c66bb9faafa4ebce20850e9e4f5034139f6f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4535 @@@
++//! moment.js
++//! version : 2.20.1
++//! authors : Tim Wood, Iskren Chernev, Moment.js contributors
++//! license : MIT
++//! momentjs.com
++
++;(function (global, factory) {
++    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
++    typeof define === 'function' && define.amd ? define(factory) :
++    global.moment = factory()
++}(this, (function () { 'use strict';
++
++var hookCallback;
++
++function hooks () {
++    return hookCallback.apply(null, arguments);
++}
++
++// This is done to register the method called with moment()
++// without creating circular dependencies.
++function setHookCallback (callback) {
++    hookCallback = callback;
++}
++
++function isArray(input) {
++    return input instanceof Array || Object.prototype.toString.call(input) === '[object Array]';
++}
++
++function isObject(input) {
++    // IE8 will treat undefined and null as object if it wasn't for
++    // input != null
++    return input != null && Object.prototype.toString.call(input) === '[object Object]';
++}
++
++function isObjectEmpty(obj) {
++    if (Object.getOwnPropertyNames) {
++        return (Object.getOwnPropertyNames(obj).length === 0);
++    } else {
++        var k;
++        for (k in obj) {
++            if (obj.hasOwnProperty(k)) {
++                return false;
++            }
++        }
++        return true;
++    }
++}
++
++function isUndefined(input) {
++    return input === void 0;
++}
++
++function isNumber(input) {
++    return typeof input === 'number' || Object.prototype.toString.call(input) === '[object Number]';
++}
++
++function isDate(input) {
++    return input instanceof Date || Object.prototype.toString.call(input) === '[object Date]';
++}
++
++function map(arr, fn) {
++    var res = [], i;
++    for (i = 0; i < arr.length; ++i) {
++        res.push(fn(arr[i], i));
++    }
++    return res;
++}
++
++function hasOwnProp(a, b) {
++    return Object.prototype.hasOwnProperty.call(a, b);
++}
++
++function extend(a, b) {
++    for (var i in b) {
++        if (hasOwnProp(b, i)) {
++            a[i] = b[i];
++        }
++    }
++
++    if (hasOwnProp(b, 'toString')) {
++        a.toString = b.toString;
++    }
++
++    if (hasOwnProp(b, 'valueOf')) {
++        a.valueOf = b.valueOf;
++    }
++
++    return a;
++}
++
++function createUTC (input, format, locale, strict) {
++    return createLocalOrUTC(input, format, locale, strict, true).utc();
++}
++
++function defaultParsingFlags() {
++    // We need to deep clone this object.
++    return {
++        empty           : false,
++        unusedTokens    : [],
++        unusedInput     : [],
++        overflow        : -2,
++        charsLeftOver   : 0,
++        nullInput       : false,
++        invalidMonth    : null,
++        invalidFormat   : false,
++        userInvalidated : false,
++        iso             : false,
++        parsedDateParts : [],
++        meridiem        : null,
++        rfc2822         : false,
++        weekdayMismatch : false
++    };
++}
++
++function getParsingFlags(m) {
++    if (m._pf == null) {
++        m._pf = defaultParsingFlags();
++    }
++    return m._pf;
++}
++
++var some;
++if (Array.prototype.some) {
++    some = Array.prototype.some;
++} else {
++    some = function (fun) {
++        var t = Object(this);
++        var len = t.length >>> 0;
++
++        for (var i = 0; i < len; i++) {
++            if (i in t && fun.call(this, t[i], i, t)) {
++                return true;
++            }
++        }
++
++        return false;
++    };
++}
++
++function isValid(m) {
++    if (m._isValid == null) {
++        var flags = getParsingFlags(m);
++        var parsedParts = some.call(flags.parsedDateParts, function (i) {
++            return i != null;
++        });
++        var isNowValid = !isNaN(m._d.getTime()) &&
++            flags.overflow < 0 &&
++            !flags.empty &&
++            !flags.invalidMonth &&
++            !flags.invalidWeekday &&
++            !flags.weekdayMismatch &&
++            !flags.nullInput &&
++            !flags.invalidFormat &&
++            !flags.userInvalidated &&
++            (!flags.meridiem || (flags.meridiem && parsedParts));
++
++        if (m._strict) {
++            isNowValid = isNowValid &&
++                flags.charsLeftOver === 0 &&
++                flags.unusedTokens.length === 0 &&
++                flags.bigHour === undefined;
++        }
++
++        if (Object.isFrozen == null || !Object.isFrozen(m)) {
++            m._isValid = isNowValid;
++        }
++        else {
++            return isNowValid;
++        }
++    }
++    return m._isValid;
++}
++
++function createInvalid (flags) {
++    var m = createUTC(NaN);
++    if (flags != null) {
++        extend(getParsingFlags(m), flags);
++    }
++    else {
++        getParsingFlags(m).userInvalidated = true;
++    }
++
++    return m;
++}
++
++// Plugins that add properties should also add the key here (null value),
++// so we can properly clone ourselves.
++var momentProperties = hooks.momentProperties = [];
++
++function copyConfig(to, from) {
++    var i, prop, val;
++
++    if (!isUndefined(from._isAMomentObject)) {
++        to._isAMomentObject = from._isAMomentObject;
++    }
++    if (!isUndefined(from._i)) {
++        to._i = from._i;
++    }
++    if (!isUndefined(from._f)) {
++        to._f = from._f;
++    }
++    if (!isUndefined(from._l)) {
++        to._l = from._l;
++    }
++    if (!isUndefined(from._strict)) {
++        to._strict = from._strict;
++    }
++    if (!isUndefined(from._tzm)) {
++        to._tzm = from._tzm;
++    }
++    if (!isUndefined(from._isUTC)) {
++        to._isUTC = from._isUTC;
++    }
++    if (!isUndefined(from._offset)) {
++        to._offset = from._offset;
++    }
++    if (!isUndefined(from._pf)) {
++        to._pf = getParsingFlags(from);
++    }
++    if (!isUndefined(from._locale)) {
++        to._locale = from._locale;
++    }
++
++    if (momentProperties.length > 0) {
++        for (i = 0; i < momentProperties.length; i++) {
++            prop = momentProperties[i];
++            val = from[prop];
++            if (!isUndefined(val)) {
++                to[prop] = val;
++            }
++        }
++    }
++
++    return to;
++}
++
++var updateInProgress = false;
++
++// Moment prototype object
++function Moment(config) {
++    copyConfig(this, config);
++    this._d = new Date(config._d != null ? config._d.getTime() : NaN);
++    if (!this.isValid()) {
++        this._d = new Date(NaN);
++    }
++    // Prevent infinite loop in case updateOffset creates new moment
++    // objects.
++    if (updateInProgress === false) {
++        updateInProgress = true;
++        hooks.updateOffset(this);
++        updateInProgress = false;
++    }
++}
++
++function isMoment (obj) {
++    return obj instanceof Moment || (obj != null && obj._isAMomentObject != null);
++}
++
++function absFloor (number) {
++    if (number < 0) {
++        // -0 -> 0
++        return Math.ceil(number) || 0;
++    } else {
++        return Math.floor(number);
++    }
++}
++
++function toInt(argumentForCoercion) {
++    var coercedNumber = +argumentForCoercion,
++        value = 0;
++
++    if (coercedNumber !== 0 && isFinite(coercedNumber)) {
++        value = absFloor(coercedNumber);
++    }
++
++    return value;
++}
++
++// compare two arrays, return the number of differences
++function compareArrays(array1, array2, dontConvert) {
++    var len = Math.min(array1.length, array2.length),
++        lengthDiff = Math.abs(array1.length - array2.length),
++        diffs = 0,
++        i;
++    for (i = 0; i < len; i++) {
++        if ((dontConvert && array1[i] !== array2[i]) ||
++            (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) {
++            diffs++;
++        }
++    }
++    return diffs + lengthDiff;
++}
++
++function warn(msg) {
++    if (hooks.suppressDeprecationWarnings === false &&
++            (typeof console !==  'undefined') && console.warn) {
++        console.warn('Deprecation warning: ' + msg);
++    }
++}
++
++function deprecate(msg, fn) {
++    var firstTime = true;
++
++    return extend(function () {
++        if (hooks.deprecationHandler != null) {
++            hooks.deprecationHandler(null, msg);
++        }
++        if (firstTime) {
++            var args = [];
++            var arg;
++            for (var i = 0; i < arguments.length; i++) {
++                arg = '';
++                if (typeof arguments[i] === 'object') {
++                    arg += '\n[' + i + '] ';
++                    for (var key in arguments[0]) {
++                        arg += key + ': ' + arguments[0][key] + ', ';
++                    }
++                    arg = arg.slice(0, -2); // Remove trailing comma and space
++                } else {
++                    arg = arguments[i];
++                }
++                args.push(arg);
++            }
++            warn(msg + '\nArguments: ' + Array.prototype.slice.call(args).join('') + '\n' + (new Error()).stack);
++            firstTime = false;
++        }
++        return fn.apply(this, arguments);
++    }, fn);
++}
++
++var deprecations = {};
++
++function deprecateSimple(name, msg) {
++    if (hooks.deprecationHandler != null) {
++        hooks.deprecationHandler(name, msg);
++    }
++    if (!deprecations[name]) {
++        warn(msg);
++        deprecations[name] = true;
++    }
++}
++
++hooks.suppressDeprecationWarnings = false;
++hooks.deprecationHandler = null;
++
++function isFunction(input) {
++    return input instanceof Function || Object.prototype.toString.call(input) === '[object Function]';
++}
++
++function set (config) {
++    var prop, i;
++    for (i in config) {
++        prop = config[i];
++        if (isFunction(prop)) {
++            this[i] = prop;
++        } else {
++            this['_' + i] = prop;
++        }
++    }
++    this._config = config;
++    // Lenient ordinal parsing accepts just a number in addition to
++    // number + (possibly) stuff coming from _dayOfMonthOrdinalParse.
++    // TODO: Remove "ordinalParse" fallback in next major release.
++    this._dayOfMonthOrdinalParseLenient = new RegExp(
++        (this._dayOfMonthOrdinalParse.source || this._ordinalParse.source) +
++            '|' + (/\d{1,2}/).source);
++}
++
++function mergeConfigs(parentConfig, childConfig) {
++    var res = extend({}, parentConfig), prop;
++    for (prop in childConfig) {
++        if (hasOwnProp(childConfig, prop)) {
++            if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) {
++                res[prop] = {};
++                extend(res[prop], parentConfig[prop]);
++                extend(res[prop], childConfig[prop]);
++            } else if (childConfig[prop] != null) {
++                res[prop] = childConfig[prop];
++            } else {
++                delete res[prop];
++            }
++        }
++    }
++    for (prop in parentConfig) {
++        if (hasOwnProp(parentConfig, prop) &&
++                !hasOwnProp(childConfig, prop) &&
++                isObject(parentConfig[prop])) {
++            // make sure changes to properties don't modify parent config
++            res[prop] = extend({}, res[prop]);
++        }
++    }
++    return res;
++}
++
++function Locale(config) {
++    if (config != null) {
++        this.set(config);
++    }
++}
++
++var keys;
++
++if (Object.keys) {
++    keys = Object.keys;
++} else {
++    keys = function (obj) {
++        var i, res = [];
++        for (i in obj) {
++            if (hasOwnProp(obj, i)) {
++                res.push(i);
++            }
++        }
++        return res;
++    };
++}
++
++var defaultCalendar = {
++    sameDay : '[Today at] LT',
++    nextDay : '[Tomorrow at] LT',
++    nextWeek : 'dddd [at] LT',
++    lastDay : '[Yesterday at] LT',
++    lastWeek : '[Last] dddd [at] LT',
++    sameElse : 'L'
++};
++
++function calendar (key, mom, now) {
++    var output = this._calendar[key] || this._calendar['sameElse'];
++    return isFunction(output) ? output.call(mom, now) : output;
++}
++
++var defaultLongDateFormat = {
++    LTS  : 'h:mm:ss A',
++    LT   : 'h:mm A',
++    L    : 'MM/DD/YYYY',
++    LL   : 'MMMM D, YYYY',
++    LLL  : 'MMMM D, YYYY h:mm A',
++    LLLL : 'dddd, MMMM D, YYYY h:mm A'
++};
++
++function longDateFormat (key) {
++    var format = this._longDateFormat[key],
++        formatUpper = this._longDateFormat[key.toUpperCase()];
++
++    if (format || !formatUpper) {
++        return format;
++    }
++
++    this._longDateFormat[key] = formatUpper.replace(/MMMM|MM|DD|dddd/g, function (val) {
++        return val.slice(1);
++    });
++
++    return this._longDateFormat[key];
++}
++
++var defaultInvalidDate = 'Invalid date';
++
++function invalidDate () {
++    return this._invalidDate;
++}
++
++var defaultOrdinal = '%d';
++var defaultDayOfMonthOrdinalParse = /\d{1,2}/;
++
++function ordinal (number) {
++    return this._ordinal.replace('%d', number);
++}
++
++var defaultRelativeTime = {
++    future : 'in %s',
++    past   : '%s ago',
++    s  : 'a few seconds',
++    ss : '%d seconds',
++    m  : 'a minute',
++    mm : '%d minutes',
++    h  : 'an hour',
++    hh : '%d hours',
++    d  : 'a day',
++    dd : '%d days',
++    M  : 'a month',
++    MM : '%d months',
++    y  : 'a year',
++    yy : '%d years'
++};
++
++function relativeTime (number, withoutSuffix, string, isFuture) {
++    var output = this._relativeTime[string];
++    return (isFunction(output)) ?
++        output(number, withoutSuffix, string, isFuture) :
++        output.replace(/%d/i, number);
++}
++
++function pastFuture (diff, output) {
++    var format = this._relativeTime[diff > 0 ? 'future' : 'past'];
++    return isFunction(format) ? format(output) : format.replace(/%s/i, output);
++}
++
++var aliases = {};
++
++function addUnitAlias (unit, shorthand) {
++    var lowerCase = unit.toLowerCase();
++    aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit;
++}
++
++function normalizeUnits(units) {
++    return typeof units === 'string' ? aliases[units] || aliases[units.toLowerCase()] : undefined;
++}
++
++function normalizeObjectUnits(inputObject) {
++    var normalizedInput = {},
++        normalizedProp,
++        prop;
++
++    for (prop in inputObject) {
++        if (hasOwnProp(inputObject, prop)) {
++            normalizedProp = normalizeUnits(prop);
++            if (normalizedProp) {
++                normalizedInput[normalizedProp] = inputObject[prop];
++            }
++        }
++    }
++
++    return normalizedInput;
++}
++
++var priorities = {};
++
++function addUnitPriority(unit, priority) {
++    priorities[unit] = priority;
++}
++
++function getPrioritizedUnits(unitsObj) {
++    var units = [];
++    for (var u in unitsObj) {
++        units.push({unit: u, priority: priorities[u]});
++    }
++    units.sort(function (a, b) {
++        return a.priority - b.priority;
++    });
++    return units;
++}
++
++function zeroFill(number, targetLength, forceSign) {
++    var absNumber = '' + Math.abs(number),
++        zerosToFill = targetLength - absNumber.length,
++        sign = number >= 0;
++    return (sign ? (forceSign ? '+' : '') : '-') +
++        Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber;
++}
++
++var formattingTokens = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g;
++
++var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g;
++
++var formatFunctions = {};
++
++var formatTokenFunctions = {};
++
++// token:    'M'
++// padded:   ['MM', 2]
++// ordinal:  'Mo'
++// callback: function () { this.month() + 1 }
++function addFormatToken (token, padded, ordinal, callback) {
++    var func = callback;
++    if (typeof callback === 'string') {
++        func = function () {
++            return this[callback]();
++        };
++    }
++    if (token) {
++        formatTokenFunctions[token] = func;
++    }
++    if (padded) {
++        formatTokenFunctions[padded[0]] = function () {
++            return zeroFill(func.apply(this, arguments), padded[1], padded[2]);
++        };
++    }
++    if (ordinal) {
++        formatTokenFunctions[ordinal] = function () {
++            return this.localeData().ordinal(func.apply(this, arguments), token);
++        };
++    }
++}
++
++function removeFormattingTokens(input) {
++    if (input.match(/\[[\s\S]/)) {
++        return input.replace(/^\[|\]$/g, '');
++    }
++    return input.replace(/\\/g, '');
++}
++
++function makeFormatFunction(format) {
++    var array = format.match(formattingTokens), i, length;
++
++    for (i = 0, length = array.length; i < length; i++) {
++        if (formatTokenFunctions[array[i]]) {
++            array[i] = formatTokenFunctions[array[i]];
++        } else {
++            array[i] = removeFormattingTokens(array[i]);
++        }
++    }
++
++    return function (mom) {
++        var output = '', i;
++        for (i = 0; i < length; i++) {
++            output += isFunction(array[i]) ? array[i].call(mom, format) : array[i];
++        }
++        return output;
++    };
++}
++
++// format date using native date object
++function formatMoment(m, format) {
++    if (!m.isValid()) {
++        return m.localeData().invalidDate();
++    }
++
++    format = expandFormat(format, m.localeData());
++    formatFunctions[format] = formatFunctions[format] || makeFormatFunction(format);
++
++    return formatFunctions[format](m);
++}
++
++function expandFormat(format, locale) {
++    var i = 5;
++
++    function replaceLongDateFormatTokens(input) {
++        return locale.longDateFormat(input) || input;
++    }
++
++    localFormattingTokens.lastIndex = 0;
++    while (i >= 0 && localFormattingTokens.test(format)) {
++        format = format.replace(localFormattingTokens, replaceLongDateFormatTokens);
++        localFormattingTokens.lastIndex = 0;
++        i -= 1;
++    }
++
++    return format;
++}
++
++var match1         = /\d/;            //       0 - 9
++var match2         = /\d\d/;          //      00 - 99
++var match3         = /\d{3}/;         //     000 - 999
++var match4         = /\d{4}/;         //    0000 - 9999
++var match6         = /[+-]?\d{6}/;    // -999999 - 999999
++var match1to2      = /\d\d?/;         //       0 - 99
++var match3to4      = /\d\d\d\d?/;     //     999 - 9999
++var match5to6      = /\d\d\d\d\d\d?/; //   99999 - 999999
++var match1to3      = /\d{1,3}/;       //       0 - 999
++var match1to4      = /\d{1,4}/;       //       0 - 9999
++var match1to6      = /[+-]?\d{1,6}/;  // -999999 - 999999
++
++var matchUnsigned  = /\d+/;           //       0 - inf
++var matchSigned    = /[+-]?\d+/;      //    -inf - inf
++
++var matchOffset    = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z
++var matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi; // +00 -00 +00:00 -00:00 +0000 -0000 or Z
++
++var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123
++
++// any word (or two) characters or numbers including two/three word month in arabic.
++// includes scottish gaelic two word and hyphenated months
++var matchWord = /[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFF07\uFF10-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i;
++
++
++var regexes = {};
++
++function addRegexToken (token, regex, strictRegex) {
++    regexes[token] = isFunction(regex) ? regex : function (isStrict, localeData) {
++        return (isStrict && strictRegex) ? strictRegex : regex;
++    };
++}
++
++function getParseRegexForToken (token, config) {
++    if (!hasOwnProp(regexes, token)) {
++        return new RegExp(unescapeFormat(token));
++    }
++
++    return regexes[token](config._strict, config._locale);
++}
++
++// Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
++function unescapeFormat(s) {
++    return regexEscape(s.replace('\\', '').replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) {
++        return p1 || p2 || p3 || p4;
++    }));
++}
++
++function regexEscape(s) {
++    return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
++}
++
++var tokens = {};
++
++function addParseToken (token, callback) {
++    var i, func = callback;
++    if (typeof token === 'string') {
++        token = [token];
++    }
++    if (isNumber(callback)) {
++        func = function (input, array) {
++            array[callback] = toInt(input);
++        };
++    }
++    for (i = 0; i < token.length; i++) {
++        tokens[token[i]] = func;
++    }
++}
++
++function addWeekParseToken (token, callback) {
++    addParseToken(token, function (input, array, config, token) {
++        config._w = config._w || {};
++        callback(input, config._w, config, token);
++    });
++}
++
++function addTimeToArrayFromToken(token, input, config) {
++    if (input != null && hasOwnProp(tokens, token)) {
++        tokens[token](input, config._a, config, token);
++    }
++}
++
++var YEAR = 0;
++var MONTH = 1;
++var DATE = 2;
++var HOUR = 3;
++var MINUTE = 4;
++var SECOND = 5;
++var MILLISECOND = 6;
++var WEEK = 7;
++var WEEKDAY = 8;
++
++// FORMATTING
++
++addFormatToken('Y', 0, 0, function () {
++    var y = this.year();
++    return y <= 9999 ? '' + y : '+' + y;
++});
++
++addFormatToken(0, ['YY', 2], 0, function () {
++    return this.year() % 100;
++});
++
++addFormatToken(0, ['YYYY',   4],       0, 'year');
++addFormatToken(0, ['YYYYY',  5],       0, 'year');
++addFormatToken(0, ['YYYYYY', 6, true], 0, 'year');
++
++// ALIASES
++
++addUnitAlias('year', 'y');
++
++// PRIORITIES
++
++addUnitPriority('year', 1);
++
++// PARSING
++
++addRegexToken('Y',      matchSigned);
++addRegexToken('YY',     match1to2, match2);
++addRegexToken('YYYY',   match1to4, match4);
++addRegexToken('YYYYY',  match1to6, match6);
++addRegexToken('YYYYYY', match1to6, match6);
++
++addParseToken(['YYYYY', 'YYYYYY'], YEAR);
++addParseToken('YYYY', function (input, array) {
++    array[YEAR] = input.length === 2 ? hooks.parseTwoDigitYear(input) : toInt(input);
++});
++addParseToken('YY', function (input, array) {
++    array[YEAR] = hooks.parseTwoDigitYear(input);
++});
++addParseToken('Y', function (input, array) {
++    array[YEAR] = parseInt(input, 10);
++});
++
++// HELPERS
++
++function daysInYear(year) {
++    return isLeapYear(year) ? 366 : 365;
++}
++
++function isLeapYear(year) {
++    return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
++}
++
++// HOOKS
++
++hooks.parseTwoDigitYear = function (input) {
++    return toInt(input) + (toInt(input) > 68 ? 1900 : 2000);
++};
++
++// MOMENTS
++
++var getSetYear = makeGetSet('FullYear', true);
++
++function getIsLeapYear () {
++    return isLeapYear(this.year());
++}
++
++function makeGetSet (unit, keepTime) {
++    return function (value) {
++        if (value != null) {
++            set$1(this, unit, value);
++            hooks.updateOffset(this, keepTime);
++            return this;
++        } else {
++            return get(this, unit);
++        }
++    };
++}
++
++function get (mom, unit) {
++    return mom.isValid() ?
++        mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]() : NaN;
++}
++
++function set$1 (mom, unit, value) {
++    if (mom.isValid() && !isNaN(value)) {
++        if (unit === 'FullYear' && isLeapYear(mom.year()) && mom.month() === 1 && mom.date() === 29) {
++            mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value, mom.month(), daysInMonth(value, mom.month()));
++        }
++        else {
++            mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value);
++        }
++    }
++}
++
++// MOMENTS
++
++function stringGet (units) {
++    units = normalizeUnits(units);
++    if (isFunction(this[units])) {
++        return this[units]();
++    }
++    return this;
++}
++
++
++function stringSet (units, value) {
++    if (typeof units === 'object') {
++        units = normalizeObjectUnits(units);
++        var prioritized = getPrioritizedUnits(units);
++        for (var i = 0; i < prioritized.length; i++) {
++            this[prioritized[i].unit](units[prioritized[i].unit]);
++        }
++    } else {
++        units = normalizeUnits(units);
++        if (isFunction(this[units])) {
++            return this[units](value);
++        }
++    }
++    return this;
++}
++
++function mod(n, x) {
++    return ((n % x) + x) % x;
++}
++
++var indexOf;
++
++if (Array.prototype.indexOf) {
++    indexOf = Array.prototype.indexOf;
++} else {
++    indexOf = function (o) {
++        // I know
++        var i;
++        for (i = 0; i < this.length; ++i) {
++            if (this[i] === o) {
++                return i;
++            }
++        }
++        return -1;
++    };
++}
++
++function daysInMonth(year, month) {
++    if (isNaN(year) || isNaN(month)) {
++        return NaN;
++    }
++    var modMonth = mod(month, 12);
++    year += (month - modMonth) / 12;
++    return modMonth === 1 ? (isLeapYear(year) ? 29 : 28) : (31 - modMonth % 7 % 2);
++}
++
++// FORMATTING
++
++addFormatToken('M', ['MM', 2], 'Mo', function () {
++    return this.month() + 1;
++});
++
++addFormatToken('MMM', 0, 0, function (format) {
++    return this.localeData().monthsShort(this, format);
++});
++
++addFormatToken('MMMM', 0, 0, function (format) {
++    return this.localeData().months(this, format);
++});
++
++// ALIASES
++
++addUnitAlias('month', 'M');
++
++// PRIORITY
++
++addUnitPriority('month', 8);
++
++// PARSING
++
++addRegexToken('M',    match1to2);
++addRegexToken('MM',   match1to2, match2);
++addRegexToken('MMM',  function (isStrict, locale) {
++    return locale.monthsShortRegex(isStrict);
++});
++addRegexToken('MMMM', function (isStrict, locale) {
++    return locale.monthsRegex(isStrict);
++});
++
++addParseToken(['M', 'MM'], function (input, array) {
++    array[MONTH] = toInt(input) - 1;
++});
++
++addParseToken(['MMM', 'MMMM'], function (input, array, config, token) {
++    var month = config._locale.monthsParse(input, token, config._strict);
++    // if we didn't find a month name, mark the date as invalid.
++    if (month != null) {
++        array[MONTH] = month;
++    } else {
++        getParsingFlags(config).invalidMonth = input;
++    }
++});
++
++// LOCALES
++
++var MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/;
++var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_');
++function localeMonths (m, format) {
++    if (!m) {
++        return isArray(this._months) ? this._months :
++            this._months['standalone'];
++    }
++    return isArray(this._months) ? this._months[m.month()] :
++        this._months[(this._months.isFormat || MONTHS_IN_FORMAT).test(format) ? 'format' : 'standalone'][m.month()];
++}
++
++var defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_');
++function localeMonthsShort (m, format) {
++    if (!m) {
++        return isArray(this._monthsShort) ? this._monthsShort :
++            this._monthsShort['standalone'];
++    }
++    return isArray(this._monthsShort) ? this._monthsShort[m.month()] :
++        this._monthsShort[MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'][m.month()];
++}
++
++function handleStrictParse(monthName, format, strict) {
++    var i, ii, mom, llc = monthName.toLocaleLowerCase();
++    if (!this._monthsParse) {
++        // this is not used
++        this._monthsParse = [];
++        this._longMonthsParse = [];
++        this._shortMonthsParse = [];
++        for (i = 0; i < 12; ++i) {
++            mom = createUTC([2000, i]);
++            this._shortMonthsParse[i] = this.monthsShort(mom, '').toLocaleLowerCase();
++            this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase();
++        }
++    }
++
++    if (strict) {
++        if (format === 'MMM') {
++            ii = indexOf.call(this._shortMonthsParse, llc);
++            return ii !== -1 ? ii : null;
++        } else {
++            ii = indexOf.call(this._longMonthsParse, llc);
++            return ii !== -1 ? ii : null;
++        }
++    } else {
++        if (format === 'MMM') {
++            ii = indexOf.call(this._shortMonthsParse, llc);
++            if (ii !== -1) {
++                return ii;
++            }
++            ii = indexOf.call(this._longMonthsParse, llc);
++            return ii !== -1 ? ii : null;
++        } else {
++            ii = indexOf.call(this._longMonthsParse, llc);
++            if (ii !== -1) {
++                return ii;
++            }
++            ii = indexOf.call(this._shortMonthsParse, llc);
++            return ii !== -1 ? ii : null;
++        }
++    }
++}
++
++function localeMonthsParse (monthName, format, strict) {
++    var i, mom, regex;
++
++    if (this._monthsParseExact) {
++        return handleStrictParse.call(this, monthName, format, strict);
++    }
++
++    if (!this._monthsParse) {
++        this._monthsParse = [];
++        this._longMonthsParse = [];
++        this._shortMonthsParse = [];
++    }
++
++    // TODO: add sorting
++    // Sorting makes sure if one month (or abbr) is a prefix of another
++    // see sorting in computeMonthsParse
++    for (i = 0; i < 12; i++) {
++        // make the regex if we don't have it already
++        mom = createUTC([2000, i]);
++        if (strict && !this._longMonthsParse[i]) {
++            this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i');
++            this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i');
++        }
++        if (!strict && !this._monthsParse[i]) {
++            regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, '');
++            this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i');
++        }
++        // test the regex
++        if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) {
++            return i;
++        } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) {
++            return i;
++        } else if (!strict && this._monthsParse[i].test(monthName)) {
++            return i;
++        }
++    }
++}
++
++// MOMENTS
++
++function setMonth (mom, value) {
++    var dayOfMonth;
++
++    if (!mom.isValid()) {
++        // No op
++        return mom;
++    }
++
++    if (typeof value === 'string') {
++        if (/^\d+$/.test(value)) {
++            value = toInt(value);
++        } else {
++            value = mom.localeData().monthsParse(value);
++            // TODO: Another silent failure?
++            if (!isNumber(value)) {
++                return mom;
++            }
++        }
++    }
++
++    dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value));
++    mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth);
++    return mom;
++}
++
++function getSetMonth (value) {
++    if (value != null) {
++        setMonth(this, value);
++        hooks.updateOffset(this, true);
++        return this;
++    } else {
++        return get(this, 'Month');
++    }
++}
++
++function getDaysInMonth () {
++    return daysInMonth(this.year(), this.month());
++}
++
++var defaultMonthsShortRegex = matchWord;
++function monthsShortRegex (isStrict) {
++    if (this._monthsParseExact) {
++        if (!hasOwnProp(this, '_monthsRegex')) {
++            computeMonthsParse.call(this);
++        }
++        if (isStrict) {
++            return this._monthsShortStrictRegex;
++        } else {
++            return this._monthsShortRegex;
++        }
++    } else {
++        if (!hasOwnProp(this, '_monthsShortRegex')) {
++            this._monthsShortRegex = defaultMonthsShortRegex;
++        }
++        return this._monthsShortStrictRegex && isStrict ?
++            this._monthsShortStrictRegex : this._monthsShortRegex;
++    }
++}
++
++var defaultMonthsRegex = matchWord;
++function monthsRegex (isStrict) {
++    if (this._monthsParseExact) {
++        if (!hasOwnProp(this, '_monthsRegex')) {
++            computeMonthsParse.call(this);
++        }
++        if (isStrict) {
++            return this._monthsStrictRegex;
++        } else {
++            return this._monthsRegex;
++        }
++    } else {
++        if (!hasOwnProp(this, '_monthsRegex')) {
++            this._monthsRegex = defaultMonthsRegex;
++        }
++        return this._monthsStrictRegex && isStrict ?
++            this._monthsStrictRegex : this._monthsRegex;
++    }
++}
++
++function computeMonthsParse () {
++    function cmpLenRev(a, b) {
++        return b.length - a.length;
++    }
++
++    var shortPieces = [], longPieces = [], mixedPieces = [],
++        i, mom;
++    for (i = 0; i < 12; i++) {
++        // make the regex if we don't have it already
++        mom = createUTC([2000, i]);
++        shortPieces.push(this.monthsShort(mom, ''));
++        longPieces.push(this.months(mom, ''));
++        mixedPieces.push(this.months(mom, ''));
++        mixedPieces.push(this.monthsShort(mom, ''));
++    }
++    // Sorting makes sure if one month (or abbr) is a prefix of another it
++    // will match the longer piece.
++    shortPieces.sort(cmpLenRev);
++    longPieces.sort(cmpLenRev);
++    mixedPieces.sort(cmpLenRev);
++    for (i = 0; i < 12; i++) {
++        shortPieces[i] = regexEscape(shortPieces[i]);
++        longPieces[i] = regexEscape(longPieces[i]);
++    }
++    for (i = 0; i < 24; i++) {
++        mixedPieces[i] = regexEscape(mixedPieces[i]);
++    }
++
++    this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');
++    this._monthsShortRegex = this._monthsRegex;
++    this._monthsStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i');
++    this._monthsShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i');
++}
++
++function createDate (y, m, d, h, M, s, ms) {
++    // can't just apply() to create a date:
++    // https://stackoverflow.com/q/181348
++    var date = new Date(y, m, d, h, M, s, ms);
++
++    // the date constructor remaps years 0-99 to 1900-1999
++    if (y < 100 && y >= 0 && isFinite(date.getFullYear())) {
++        date.setFullYear(y);
++    }
++    return date;
++}
++
++function createUTCDate (y) {
++    var date = new Date(Date.UTC.apply(null, arguments));
++
++    // the Date.UTC function remaps years 0-99 to 1900-1999
++    if (y < 100 && y >= 0 && isFinite(date.getUTCFullYear())) {
++        date.setUTCFullYear(y);
++    }
++    return date;
++}
++
++// start-of-first-week - start-of-year
++function firstWeekOffset(year, dow, doy) {
++    var // first-week day -- which january is always in the first week (4 for iso, 1 for other)
++        fwd = 7 + dow - doy,
++        // first-week day local weekday -- which local weekday is fwd
++        fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7;
++
++    return -fwdlw + fwd - 1;
++}
++
++// https://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday
++function dayOfYearFromWeeks(year, week, weekday, dow, doy) {
++    var localWeekday = (7 + weekday - dow) % 7,
++        weekOffset = firstWeekOffset(year, dow, doy),
++        dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset,
++        resYear, resDayOfYear;
++
++    if (dayOfYear <= 0) {
++        resYear = year - 1;
++        resDayOfYear = daysInYear(resYear) + dayOfYear;
++    } else if (dayOfYear > daysInYear(year)) {
++        resYear = year + 1;
++        resDayOfYear = dayOfYear - daysInYear(year);
++    } else {
++        resYear = year;
++        resDayOfYear = dayOfYear;
++    }
++
++    return {
++        year: resYear,
++        dayOfYear: resDayOfYear
++    };
++}
++
++function weekOfYear(mom, dow, doy) {
++    var weekOffset = firstWeekOffset(mom.year(), dow, doy),
++        week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1,
++        resWeek, resYear;
++
++    if (week < 1) {
++        resYear = mom.year() - 1;
++        resWeek = week + weeksInYear(resYear, dow, doy);
++    } else if (week > weeksInYear(mom.year(), dow, doy)) {
++        resWeek = week - weeksInYear(mom.year(), dow, doy);
++        resYear = mom.year() + 1;
++    } else {
++        resYear = mom.year();
++        resWeek = week;
++    }
++
++    return {
++        week: resWeek,
++        year: resYear
++    };
++}
++
++function weeksInYear(year, dow, doy) {
++    var weekOffset = firstWeekOffset(year, dow, doy),
++        weekOffsetNext = firstWeekOffset(year + 1, dow, doy);
++    return (daysInYear(year) - weekOffset + weekOffsetNext) / 7;
++}
++
++// FORMATTING
++
++addFormatToken('w', ['ww', 2], 'wo', 'week');
++addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek');
++
++// ALIASES
++
++addUnitAlias('week', 'w');
++addUnitAlias('isoWeek', 'W');
++
++// PRIORITIES
++
++addUnitPriority('week', 5);
++addUnitPriority('isoWeek', 5);
++
++// PARSING
++
++addRegexToken('w',  match1to2);
++addRegexToken('ww', match1to2, match2);
++addRegexToken('W',  match1to2);
++addRegexToken('WW', match1to2, match2);
++
++addWeekParseToken(['w', 'ww', 'W', 'WW'], function (input, week, config, token) {
++    week[token.substr(0, 1)] = toInt(input);
++});
++
++// HELPERS
++
++// LOCALES
++
++function localeWeek (mom) {
++    return weekOfYear(mom, this._week.dow, this._week.doy).week;
++}
++
++var defaultLocaleWeek = {
++    dow : 0, // Sunday is the first day of the week.
++    doy : 6  // The week that contains Jan 1st is the first week of the year.
++};
++
++function localeFirstDayOfWeek () {
++    return this._week.dow;
++}
++
++function localeFirstDayOfYear () {
++    return this._week.doy;
++}
++
++// MOMENTS
++
++function getSetWeek (input) {
++    var week = this.localeData().week(this);
++    return input == null ? week : this.add((input - week) * 7, 'd');
++}
++
++function getSetISOWeek (input) {
++    var week = weekOfYear(this, 1, 4).week;
++    return input == null ? week : this.add((input - week) * 7, 'd');
++}
++
++// FORMATTING
++
++addFormatToken('d', 0, 'do', 'day');
++
++addFormatToken('dd', 0, 0, function (format) {
++    return this.localeData().weekdaysMin(this, format);
++});
++
++addFormatToken('ddd', 0, 0, function (format) {
++    return this.localeData().weekdaysShort(this, format);
++});
++
++addFormatToken('dddd', 0, 0, function (format) {
++    return this.localeData().weekdays(this, format);
++});
++
++addFormatToken('e', 0, 0, 'weekday');
++addFormatToken('E', 0, 0, 'isoWeekday');
++
++// ALIASES
++
++addUnitAlias('day', 'd');
++addUnitAlias('weekday', 'e');
++addUnitAlias('isoWeekday', 'E');
++
++// PRIORITY
++addUnitPriority('day', 11);
++addUnitPriority('weekday', 11);
++addUnitPriority('isoWeekday', 11);
++
++// PARSING
++
++addRegexToken('d',    match1to2);
++addRegexToken('e',    match1to2);
++addRegexToken('E',    match1to2);
++addRegexToken('dd',   function (isStrict, locale) {
++    return locale.weekdaysMinRegex(isStrict);
++});
++addRegexToken('ddd',   function (isStrict, locale) {
++    return locale.weekdaysShortRegex(isStrict);
++});
++addRegexToken('dddd',   function (isStrict, locale) {
++    return locale.weekdaysRegex(isStrict);
++});
++
++addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config, token) {
++    var weekday = config._locale.weekdaysParse(input, token, config._strict);
++    // if we didn't get a weekday name, mark the date as invalid
++    if (weekday != null) {
++        week.d = weekday;
++    } else {
++        getParsingFlags(config).invalidWeekday = input;
++    }
++});
++
++addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) {
++    week[token] = toInt(input);
++});
++
++// HELPERS
++
++function parseWeekday(input, locale) {
++    if (typeof input !== 'string') {
++        return input;
++    }
++
++    if (!isNaN(input)) {
++        return parseInt(input, 10);
++    }
++
++    input = locale.weekdaysParse(input);
++    if (typeof input === 'number') {
++        return input;
++    }
++
++    return null;
++}
++
++function parseIsoWeekday(input, locale) {
++    if (typeof input === 'string') {
++        return locale.weekdaysParse(input) % 7 || 7;
++    }
++    return isNaN(input) ? null : input;
++}
++
++// LOCALES
++
++var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_');
++function localeWeekdays (m, format) {
++    if (!m) {
++        return isArray(this._weekdays) ? this._weekdays :
++            this._weekdays['standalone'];
++    }
++    return isArray(this._weekdays) ? this._weekdays[m.day()] :
++        this._weekdays[this._weekdays.isFormat.test(format) ? 'format' : 'standalone'][m.day()];
++}
++
++var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_');
++function localeWeekdaysShort (m) {
++    return (m) ? this._weekdaysShort[m.day()] : this._weekdaysShort;
++}
++
++var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_');
++function localeWeekdaysMin (m) {
++    return (m) ? this._weekdaysMin[m.day()] : this._weekdaysMin;
++}
++
++function handleStrictParse$1(weekdayName, format, strict) {
++    var i, ii, mom, llc = weekdayName.toLocaleLowerCase();
++    if (!this._weekdaysParse) {
++        this._weekdaysParse = [];
++        this._shortWeekdaysParse = [];
++        this._minWeekdaysParse = [];
++
++        for (i = 0; i < 7; ++i) {
++            mom = createUTC([2000, 1]).day(i);
++            this._minWeekdaysParse[i] = this.weekdaysMin(mom, '').toLocaleLowerCase();
++            this._shortWeekdaysParse[i] = this.weekdaysShort(mom, '').toLocaleLowerCase();
++            this._weekdaysParse[i] = this.weekdays(mom, '').toLocaleLowerCase();
++        }
++    }
++
++    if (strict) {
++        if (format === 'dddd') {
++            ii = indexOf.call(this._weekdaysParse, llc);
++            return ii !== -1 ? ii : null;
++        } else if (format === 'ddd') {
++            ii = indexOf.call(this._shortWeekdaysParse, llc);
++            return ii !== -1 ? ii : null;
++        } else {
++            ii = indexOf.call(this._minWeekdaysParse, llc);
++            return ii !== -1 ? ii : null;
++        }
++    } else {
++        if (format === 'dddd') {
++            ii = indexOf.call(this._weekdaysParse, llc);
++            if (ii !== -1) {
++                return ii;
++            }
++            ii = indexOf.call(this._shortWeekdaysParse, llc);
++            if (ii !== -1) {
++                return ii;
++            }
++            ii = indexOf.call(this._minWeekdaysParse, llc);
++            return ii !== -1 ? ii : null;
++        } else if (format === 'ddd') {
++            ii = indexOf.call(this._shortWeekdaysParse, llc);
++            if (ii !== -1) {
++                return ii;
++            }
++            ii = indexOf.call(this._weekdaysParse, llc);
++            if (ii !== -1) {
++                return ii;
++            }
++            ii = indexOf.call(this._minWeekdaysParse, llc);
++            return ii !== -1 ? ii : null;
++        } else {
++            ii = indexOf.call(this._minWeekdaysParse, llc);
++            if (ii !== -1) {
++                return ii;
++            }
++            ii = indexOf.call(this._weekdaysParse, llc);
++            if (ii !== -1) {
++                return ii;
++            }
++            ii = indexOf.call(this._shortWeekdaysParse, llc);
++            return ii !== -1 ? ii : null;
++        }
++    }
++}
++
++function localeWeekdaysParse (weekdayName, format, strict) {
++    var i, mom, regex;
++
++    if (this._weekdaysParseExact) {
++        return handleStrictParse$1.call(this, weekdayName, format, strict);
++    }
++
++    if (!this._weekdaysParse) {
++        this._weekdaysParse = [];
++        this._minWeekdaysParse = [];
++        this._shortWeekdaysParse = [];
++        this._fullWeekdaysParse = [];
++    }
++
++    for (i = 0; i < 7; i++) {
++        // make the regex if we don't have it already
++
++        mom = createUTC([2000, 1]).day(i);
++        if (strict && !this._fullWeekdaysParse[i]) {
++            this._fullWeekdaysParse[i] = new RegExp('^' + this.weekdays(mom, '').replace('.', '\.?') + '$', 'i');
++            this._shortWeekdaysParse[i] = new RegExp('^' + this.weekdaysShort(mom, '').replace('.', '\.?') + '$', 'i');
++            this._minWeekdaysParse[i] = new RegExp('^' + this.weekdaysMin(mom, '').replace('.', '\.?') + '$', 'i');
++        }
++        if (!this._weekdaysParse[i]) {
++            regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, '');
++            this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i');
++        }
++        // test the regex
++        if (strict && format === 'dddd' && this._fullWeekdaysParse[i].test(weekdayName)) {
++            return i;
++        } else if (strict && format === 'ddd' && this._shortWeekdaysParse[i].test(weekdayName)) {
++            return i;
++        } else if (strict && format === 'dd' && this._minWeekdaysParse[i].test(weekdayName)) {
++            return i;
++        } else if (!strict && this._weekdaysParse[i].test(weekdayName)) {
++            return i;
++        }
++    }
++}
++
++// MOMENTS
++
++function getSetDayOfWeek (input) {
++    if (!this.isValid()) {
++        return input != null ? this : NaN;
++    }
++    var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay();
++    if (input != null) {
++        input = parseWeekday(input, this.localeData());
++        return this.add(input - day, 'd');
++    } else {
++        return day;
++    }
++}
++
++function getSetLocaleDayOfWeek (input) {
++    if (!this.isValid()) {
++        return input != null ? this : NaN;
++    }
++    var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7;
++    return input == null ? weekday : this.add(input - weekday, 'd');
++}
++
++function getSetISODayOfWeek (input) {
++    if (!this.isValid()) {
++        return input != null ? this : NaN;
++    }
++
++    // behaves the same as moment#day except
++    // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6)
++    // as a setter, sunday should belong to the previous week.
++
++    if (input != null) {
++        var weekday = parseIsoWeekday(input, this.localeData());
++        return this.day(this.day() % 7 ? weekday : weekday - 7);
++    } else {
++        return this.day() || 7;
++    }
++}
++
++var defaultWeekdaysRegex = matchWord;
++function weekdaysRegex (isStrict) {
++    if (this._weekdaysParseExact) {
++        if (!hasOwnProp(this, '_weekdaysRegex')) {
++            computeWeekdaysParse.call(this);
++        }
++        if (isStrict) {
++            return this._weekdaysStrictRegex;
++        } else {
++            return this._weekdaysRegex;
++        }
++    } else {
++        if (!hasOwnProp(this, '_weekdaysRegex')) {
++            this._weekdaysRegex = defaultWeekdaysRegex;
++        }
++        return this._weekdaysStrictRegex && isStrict ?
++            this._weekdaysStrictRegex : this._weekdaysRegex;
++    }
++}
++
++var defaultWeekdaysShortRegex = matchWord;
++function weekdaysShortRegex (isStrict) {
++    if (this._weekdaysParseExact) {
++        if (!hasOwnProp(this, '_weekdaysRegex')) {
++            computeWeekdaysParse.call(this);
++        }
++        if (isStrict) {
++            return this._weekdaysShortStrictRegex;
++        } else {
++            return this._weekdaysShortRegex;
++        }
++    } else {
++        if (!hasOwnProp(this, '_weekdaysShortRegex')) {
++            this._weekdaysShortRegex = defaultWeekdaysShortRegex;
++        }
++        return this._weekdaysShortStrictRegex && isStrict ?
++            this._weekdaysShortStrictRegex : this._weekdaysShortRegex;
++    }
++}
++
++var defaultWeekdaysMinRegex = matchWord;
++function weekdaysMinRegex (isStrict) {
++    if (this._weekdaysParseExact) {
++        if (!hasOwnProp(this, '_weekdaysRegex')) {
++            computeWeekdaysParse.call(this);
++        }
++        if (isStrict) {
++            return this._weekdaysMinStrictRegex;
++        } else {
++            return this._weekdaysMinRegex;
++        }
++    } else {
++        if (!hasOwnProp(this, '_weekdaysMinRegex')) {
++            this._weekdaysMinRegex = defaultWeekdaysMinRegex;
++        }
++        return this._weekdaysMinStrictRegex && isStrict ?
++            this._weekdaysMinStrictRegex : this._weekdaysMinRegex;
++    }
++}
++
++
++function computeWeekdaysParse () {
++    function cmpLenRev(a, b) {
++        return b.length - a.length;
++    }
++
++    var minPieces = [], shortPieces = [], longPieces = [], mixedPieces = [],
++        i, mom, minp, shortp, longp;
++    for (i = 0; i < 7; i++) {
++        // make the regex if we don't have it already
++        mom = createUTC([2000, 1]).day(i);
++        minp = this.weekdaysMin(mom, '');
++        shortp = this.weekdaysShort(mom, '');
++        longp = this.weekdays(mom, '');
++        minPieces.push(minp);
++        shortPieces.push(shortp);
++        longPieces.push(longp);
++        mixedPieces.push(minp);
++        mixedPieces.push(shortp);
++        mixedPieces.push(longp);
++    }
++    // Sorting makes sure if one weekday (or abbr) is a prefix of another it
++    // will match the longer piece.
++    minPieces.sort(cmpLenRev);
++    shortPieces.sort(cmpLenRev);
++    longPieces.sort(cmpLenRev);
++    mixedPieces.sort(cmpLenRev);
++    for (i = 0; i < 7; i++) {
++        shortPieces[i] = regexEscape(shortPieces[i]);
++        longPieces[i] = regexEscape(longPieces[i]);
++        mixedPieces[i] = regexEscape(mixedPieces[i]);
++    }
++
++    this._weekdaysRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');
++    this._weekdaysShortRegex = this._weekdaysRegex;
++    this._weekdaysMinRegex = this._weekdaysRegex;
++
++    this._weekdaysStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i');
++    this._weekdaysShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i');
++    this._weekdaysMinStrictRegex = new RegExp('^(' + minPieces.join('|') + ')', 'i');
++}
++
++// FORMATTING
++
++function hFormat() {
++    return this.hours() % 12 || 12;
++}
++
++function kFormat() {
++    return this.hours() || 24;
++}
++
++addFormatToken('H', ['HH', 2], 0, 'hour');
++addFormatToken('h', ['hh', 2], 0, hFormat);
++addFormatToken('k', ['kk', 2], 0, kFormat);
++
++addFormatToken('hmm', 0, 0, function () {
++    return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2);
++});
++
++addFormatToken('hmmss', 0, 0, function () {
++    return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2) +
++        zeroFill(this.seconds(), 2);
++});
++
++addFormatToken('Hmm', 0, 0, function () {
++    return '' + this.hours() + zeroFill(this.minutes(), 2);
++});
++
++addFormatToken('Hmmss', 0, 0, function () {
++    return '' + this.hours() + zeroFill(this.minutes(), 2) +
++        zeroFill(this.seconds(), 2);
++});
++
++function meridiem (token, lowercase) {
++    addFormatToken(token, 0, 0, function () {
++        return this.localeData().meridiem(this.hours(), this.minutes(), lowercase);
++    });
++}
++
++meridiem('a', true);
++meridiem('A', false);
++
++// ALIASES
++
++addUnitAlias('hour', 'h');
++
++// PRIORITY
++addUnitPriority('hour', 13);
++
++// PARSING
++
++function matchMeridiem (isStrict, locale) {
++    return locale._meridiemParse;
++}
++
++addRegexToken('a',  matchMeridiem);
++addRegexToken('A',  matchMeridiem);
++addRegexToken('H',  match1to2);
++addRegexToken('h',  match1to2);
++addRegexToken('k',  match1to2);
++addRegexToken('HH', match1to2, match2);
++addRegexToken('hh', match1to2, match2);
++addRegexToken('kk', match1to2, match2);
++
++addRegexToken('hmm', match3to4);
++addRegexToken('hmmss', match5to6);
++addRegexToken('Hmm', match3to4);
++addRegexToken('Hmmss', match5to6);
++
++addParseToken(['H', 'HH'], HOUR);
++addParseToken(['k', 'kk'], function (input, array, config) {
++    var kInput = toInt(input);
++    array[HOUR] = kInput === 24 ? 0 : kInput;
++});
++addParseToken(['a', 'A'], function (input, array, config) {
++    config._isPm = config._locale.isPM(input);
++    config._meridiem = input;
++});
++addParseToken(['h', 'hh'], function (input, array, config) {
++    array[HOUR] = toInt(input);
++    getParsingFlags(config).bigHour = true;
++});
++addParseToken('hmm', function (input, array, config) {
++    var pos = input.length - 2;
++    array[HOUR] = toInt(input.substr(0, pos));
++    array[MINUTE] = toInt(input.substr(pos));
++    getParsingFlags(config).bigHour = true;
++});
++addParseToken('hmmss', function (input, array, config) {
++    var pos1 = input.length - 4;
++    var pos2 = input.length - 2;
++    array[HOUR] = toInt(input.substr(0, pos1));
++    array[MINUTE] = toInt(input.substr(pos1, 2));
++    array[SECOND] = toInt(input.substr(pos2));
++    getParsingFlags(config).bigHour = true;
++});
++addParseToken('Hmm', function (input, array, config) {
++    var pos = input.length - 2;
++    array[HOUR] = toInt(input.substr(0, pos));
++    array[MINUTE] = toInt(input.substr(pos));
++});
++addParseToken('Hmmss', function (input, array, config) {
++    var pos1 = input.length - 4;
++    var pos2 = input.length - 2;
++    array[HOUR] = toInt(input.substr(0, pos1));
++    array[MINUTE] = toInt(input.substr(pos1, 2));
++    array[SECOND] = toInt(input.substr(pos2));
++});
++
++// LOCALES
++
++function localeIsPM (input) {
++    // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays
++    // Using charAt should be more compatible.
++    return ((input + '').toLowerCase().charAt(0) === 'p');
++}
++
++var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i;
++function localeMeridiem (hours, minutes, isLower) {
++    if (hours > 11) {
++        return isLower ? 'pm' : 'PM';
++    } else {
++        return isLower ? 'am' : 'AM';
++    }
++}
++
++
++// MOMENTS
++
++// Setting the hour should keep the time, because the user explicitly
++// specified which hour he wants. So trying to maintain the same hour (in
++// a new timezone) makes sense. Adding/subtracting hours does not follow
++// this rule.
++var getSetHour = makeGetSet('Hours', true);
++
++// months
++// week
++// weekdays
++// meridiem
++var baseConfig = {
++    calendar: defaultCalendar,
++    longDateFormat: defaultLongDateFormat,
++    invalidDate: defaultInvalidDate,
++    ordinal: defaultOrdinal,
++    dayOfMonthOrdinalParse: defaultDayOfMonthOrdinalParse,
++    relativeTime: defaultRelativeTime,
++
++    months: defaultLocaleMonths,
++    monthsShort: defaultLocaleMonthsShort,
++
++    week: defaultLocaleWeek,
++
++    weekdays: defaultLocaleWeekdays,
++    weekdaysMin: defaultLocaleWeekdaysMin,
++    weekdaysShort: defaultLocaleWeekdaysShort,
++
++    meridiemParse: defaultLocaleMeridiemParse
++};
++
++// internal storage for locale config files
++var locales = {};
++var localeFamilies = {};
++var globalLocale;
++
++function normalizeLocale(key) {
++    return key ? key.toLowerCase().replace('_', '-') : key;
++}
++
++// pick the locale from the array
++// try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each
++// substring from most specific to least, but move to the next array item if it's a more specific variant than the current root
++function chooseLocale(names) {
++    var i = 0, j, next, locale, split;
++
++    while (i < names.length) {
++        split = normalizeLocale(names[i]).split('-');
++        j = split.length;
++        next = normalizeLocale(names[i + 1]);
++        next = next ? next.split('-') : null;
++        while (j > 0) {
++            locale = loadLocale(split.slice(0, j).join('-'));
++            if (locale) {
++                return locale;
++            }
++            if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) {
++                //the next array item is better than a shallower substring of this one
++                break;
++            }
++            j--;
++        }
++        i++;
++    }
++    return null;
++}
++
++function loadLocale(name) {
++    var oldLocale = null;
++    // TODO: Find a better way to register and load all the locales in Node
++    if (!locales[name] && (typeof module !== 'undefined') &&
++            module && module.exports) {
++        try {
++            oldLocale = globalLocale._abbr;
++            var aliasedRequire = require;
++            aliasedRequire('./locale/' + name);
++            getSetGlobalLocale(oldLocale);
++        } catch (e) {}
++    }
++    return locales[name];
++}
++
++// This function will load locale and then set the global locale.  If
++// no arguments are passed in, it will simply return the current global
++// locale key.
++function getSetGlobalLocale (key, values) {
++    var data;
++    if (key) {
++        if (isUndefined(values)) {
++            data = getLocale(key);
++        }
++        else {
++            data = defineLocale(key, values);
++        }
++
++        if (data) {
++            // moment.duration._locale = moment._locale = data;
++            globalLocale = data;
++        }
++    }
++
++    return globalLocale._abbr;
++}
++
++function defineLocale (name, config) {
++    if (config !== null) {
++        var parentConfig = baseConfig;
++        config.abbr = name;
++        if (locales[name] != null) {
++            deprecateSimple('defineLocaleOverride',
++                    'use moment.updateLocale(localeName, config) to change ' +
++                    'an existing locale. moment.defineLocale(localeName, ' +
++                    'config) should only be used for creating a new locale ' +
++                    'See http://momentjs.com/guides/#/warnings/define-locale/ for more info.');
++            parentConfig = locales[name]._config;
++        } else if (config.parentLocale != null) {
++            if (locales[config.parentLocale] != null) {
++                parentConfig = locales[config.parentLocale]._config;
++            } else {
++                if (!localeFamilies[config.parentLocale]) {
++                    localeFamilies[config.parentLocale] = [];
++                }
++                localeFamilies[config.parentLocale].push({
++                    name: name,
++                    config: config
++                });
++                return null;
++            }
++        }
++        locales[name] = new Locale(mergeConfigs(parentConfig, config));
++
++        if (localeFamilies[name]) {
++            localeFamilies[name].forEach(function (x) {
++                defineLocale(x.name, x.config);
++            });
++        }
++
++        // backwards compat for now: also set the locale
++        // make sure we set the locale AFTER all child locales have been
++        // created, so we won't end up with the child locale set.
++        getSetGlobalLocale(name);
++
++
++        return locales[name];
++    } else {
++        // useful for testing
++        delete locales[name];
++        return null;
++    }
++}
++
++function updateLocale(name, config) {
++    if (config != null) {
++        var locale, tmpLocale, parentConfig = baseConfig;
++        // MERGE
++        tmpLocale = loadLocale(name);
++        if (tmpLocale != null) {
++            parentConfig = tmpLocale._config;
++        }
++        config = mergeConfigs(parentConfig, config);
++        locale = new Locale(config);
++        locale.parentLocale = locales[name];
++        locales[name] = locale;
++
++        // backwards compat for now: also set the locale
++        getSetGlobalLocale(name);
++    } else {
++        // pass null for config to unupdate, useful for tests
++        if (locales[name] != null) {
++            if (locales[name].parentLocale != null) {
++                locales[name] = locales[name].parentLocale;
++            } else if (locales[name] != null) {
++                delete locales[name];
++            }
++        }
++    }
++    return locales[name];
++}
++
++// returns locale data
++function getLocale (key) {
++    var locale;
++
++    if (key && key._locale && key._locale._abbr) {
++        key = key._locale._abbr;
++    }
++
++    if (!key) {
++        return globalLocale;
++    }
++
++    if (!isArray(key)) {
++        //short-circuit everything else
++        locale = loadLocale(key);
++        if (locale) {
++            return locale;
++        }
++        key = [key];
++    }
++
++    return chooseLocale(key);
++}
++
++function listLocales() {
++    return keys(locales);
++}
++
++function checkOverflow (m) {
++    var overflow;
++    var a = m._a;
++
++    if (a && getParsingFlags(m).overflow === -2) {
++        overflow =
++            a[MONTH]       < 0 || a[MONTH]       > 11  ? MONTH :
++            a[DATE]        < 1 || a[DATE]        > daysInMonth(a[YEAR], a[MONTH]) ? DATE :
++            a[HOUR]        < 0 || a[HOUR]        > 24 || (a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0)) ? HOUR :
++            a[MINUTE]      < 0 || a[MINUTE]      > 59  ? MINUTE :
++            a[SECOND]      < 0 || a[SECOND]      > 59  ? SECOND :
++            a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND :
++            -1;
++
++        if (getParsingFlags(m)._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) {
++            overflow = DATE;
++        }
++        if (getParsingFlags(m)._overflowWeeks && overflow === -1) {
++            overflow = WEEK;
++        }
++        if (getParsingFlags(m)._overflowWeekday && overflow === -1) {
++            overflow = WEEKDAY;
++        }
++
++        getParsingFlags(m).overflow = overflow;
++    }
++
++    return m;
++}
++
++// Pick the first defined of two or three arguments.
++function defaults(a, b, c) {
++    if (a != null) {
++        return a;
++    }
++    if (b != null) {
++        return b;
++    }
++    return c;
++}
++
++function currentDateArray(config) {
++    // hooks is actually the exported moment object
++    var nowValue = new Date(hooks.now());
++    if (config._useUTC) {
++        return [nowValue.getUTCFullYear(), nowValue.getUTCMonth(), nowValue.getUTCDate()];
++    }
++    return [nowValue.getFullYear(), nowValue.getMonth(), nowValue.getDate()];
++}
++
++// convert an array to a date.
++// the array should mirror the parameters below
++// note: all values past the year are optional and will default to the lowest possible value.
++// [year, month, day , hour, minute, second, millisecond]
++function configFromArray (config) {
++    var i, date, input = [], currentDate, expectedWeekday, yearToUse;
++
++    if (config._d) {
++        return;
++    }
++
++    currentDate = currentDateArray(config);
++
++    //compute day of the year from weeks and weekdays
++    if (config._w && config._a[DATE] == null && config._a[MONTH] == null) {
++        dayOfYearFromWeekInfo(config);
++    }
++
++    //if the day of the year is set, figure out what it is
++    if (config._dayOfYear != null) {
++        yearToUse = defaults(config._a[YEAR], currentDate[YEAR]);
++
++        if (config._dayOfYear > daysInYear(yearToUse) || config._dayOfYear === 0) {
++            getParsingFlags(config)._overflowDayOfYear = true;
++        }
++
++        date = createUTCDate(yearToUse, 0, config._dayOfYear);
++        config._a[MONTH] = date.getUTCMonth();
++        config._a[DATE] = date.getUTCDate();
++    }
++
++    // Default to current date.
++    // * if no year, month, day of month are given, default to today
++    // * if day of month is given, default month and year
++    // * if month is given, default only year
++    // * if year is given, don't default anything
++    for (i = 0; i < 3 && config._a[i] == null; ++i) {
++        config._a[i] = input[i] = currentDate[i];
++    }
++
++    // Zero out whatever was not defaulted, including time
++    for (; i < 7; i++) {
++        config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i];
++    }
++
++    // Check for 24:00:00.000
++    if (config._a[HOUR] === 24 &&
++            config._a[MINUTE] === 0 &&
++            config._a[SECOND] === 0 &&
++            config._a[MILLISECOND] === 0) {
++        config._nextDay = true;
++        config._a[HOUR] = 0;
++    }
++
++    config._d = (config._useUTC ? createUTCDate : createDate).apply(null, input);
++    expectedWeekday = config._useUTC ? config._d.getUTCDay() : config._d.getDay();
++
++    // Apply timezone offset from input. The actual utcOffset can be changed
++    // with parseZone.
++    if (config._tzm != null) {
++        config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm);
++    }
++
++    if (config._nextDay) {
++        config._a[HOUR] = 24;
++    }
++
++    // check for mismatching day of week
++    if (config._w && typeof config._w.d !== 'undefined' && config._w.d !== expectedWeekday) {
++        getParsingFlags(config).weekdayMismatch = true;
++    }
++}
++
++function dayOfYearFromWeekInfo(config) {
++    var w, weekYear, week, weekday, dow, doy, temp, weekdayOverflow;
++
++    w = config._w;
++    if (w.GG != null || w.W != null || w.E != null) {
++        dow = 1;
++        doy = 4;
++
++        // TODO: We need to take the current isoWeekYear, but that depends on
++        // how we interpret now (local, utc, fixed offset). So create
++        // a now version of current config (take local/utc/offset flags, and
++        // create now).
++        weekYear = defaults(w.GG, config._a[YEAR], weekOfYear(createLocal(), 1, 4).year);
++        week = defaults(w.W, 1);
++        weekday = defaults(w.E, 1);
++        if (weekday < 1 || weekday > 7) {
++            weekdayOverflow = true;
++        }
++    } else {
++        dow = config._locale._week.dow;
++        doy = config._locale._week.doy;
++
++        var curWeek = weekOfYear(createLocal(), dow, doy);
++
++        weekYear = defaults(w.gg, config._a[YEAR], curWeek.year);
++
++        // Default to current week.
++        week = defaults(w.w, curWeek.week);
++
++        if (w.d != null) {
++            // weekday -- low day numbers are considered next week
++            weekday = w.d;
++            if (weekday < 0 || weekday > 6) {
++                weekdayOverflow = true;
++            }
++        } else if (w.e != null) {
++            // local weekday -- counting starts from begining of week
++            weekday = w.e + dow;
++            if (w.e < 0 || w.e > 6) {
++                weekdayOverflow = true;
++            }
++        } else {
++            // default to begining of week
++            weekday = dow;
++        }
++    }
++    if (week < 1 || week > weeksInYear(weekYear, dow, doy)) {
++        getParsingFlags(config)._overflowWeeks = true;
++    } else if (weekdayOverflow != null) {
++        getParsingFlags(config)._overflowWeekday = true;
++    } else {
++        temp = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy);
++        config._a[YEAR] = temp.year;
++        config._dayOfYear = temp.dayOfYear;
++    }
++}
++
++// iso 8601 regex
++// 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00)
++var extendedIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/;
++var basicIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/;
++
++var tzRegex = /Z|[+-]\d\d(?::?\d\d)?/;
++
++var isoDates = [
++    ['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/],
++    ['YYYY-MM-DD', /\d{4}-\d\d-\d\d/],
++    ['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/],
++    ['GGGG-[W]WW', /\d{4}-W\d\d/, false],
++    ['YYYY-DDD', /\d{4}-\d{3}/],
++    ['YYYY-MM', /\d{4}-\d\d/, false],
++    ['YYYYYYMMDD', /[+-]\d{10}/],
++    ['YYYYMMDD', /\d{8}/],
++    // YYYYMM is NOT allowed by the standard
++    ['GGGG[W]WWE', /\d{4}W\d{3}/],
++    ['GGGG[W]WW', /\d{4}W\d{2}/, false],
++    ['YYYYDDD', /\d{7}/]
++];
++
++// iso time formats and regexes
++var isoTimes = [
++    ['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/],
++    ['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/],
++    ['HH:mm:ss', /\d\d:\d\d:\d\d/],
++    ['HH:mm', /\d\d:\d\d/],
++    ['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/],
++    ['HHmmss,SSSS', /\d\d\d\d\d\d,\d+/],
++    ['HHmmss', /\d\d\d\d\d\d/],
++    ['HHmm', /\d\d\d\d/],
++    ['HH', /\d\d/]
++];
++
++var aspNetJsonRegex = /^\/?Date\((\-?\d+)/i;
++
++// date from iso format
++function configFromISO(config) {
++    var i, l,
++        string = config._i,
++        match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string),
++        allowTime, dateFormat, timeFormat, tzFormat;
++
++    if (match) {
++        getParsingFlags(config).iso = true;
++
++        for (i = 0, l = isoDates.length; i < l; i++) {
++            if (isoDates[i][1].exec(match[1])) {
++                dateFormat = isoDates[i][0];
++                allowTime = isoDates[i][2] !== false;
++                break;
++            }
++        }
++        if (dateFormat == null) {
++            config._isValid = false;
++            return;
++        }
++        if (match[3]) {
++            for (i = 0, l = isoTimes.length; i < l; i++) {
++                if (isoTimes[i][1].exec(match[3])) {
++                    // match[2] should be 'T' or space
++                    timeFormat = (match[2] || ' ') + isoTimes[i][0];
++                    break;
++                }
++            }
++            if (timeFormat == null) {
++                config._isValid = false;
++                return;
++            }
++        }
++        if (!allowTime && timeFormat != null) {
++            config._isValid = false;
++            return;
++        }
++        if (match[4]) {
++            if (tzRegex.exec(match[4])) {
++                tzFormat = 'Z';
++            } else {
++                config._isValid = false;
++                return;
++            }
++        }
++        config._f = dateFormat + (timeFormat || '') + (tzFormat || '');
++        configFromStringAndFormat(config);
++    } else {
++        config._isValid = false;
++    }
++}
++
++// RFC 2822 regex: For details see https://tools.ietf.org/html/rfc2822#section-3.3
++var rfc2822 = /^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/;
++
++function extractFromRFC2822Strings(yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr) {
++    var result = [
++        untruncateYear(yearStr),
++        defaultLocaleMonthsShort.indexOf(monthStr),
++        parseInt(dayStr, 10),
++        parseInt(hourStr, 10),
++        parseInt(minuteStr, 10)
++    ];
++
++    if (secondStr) {
++        result.push(parseInt(secondStr, 10));
++    }
++
++    return result;
++}
++
++function untruncateYear(yearStr) {
++    var year = parseInt(yearStr, 10);
++    if (year <= 49) {
++        return 2000 + year;
++    } else if (year <= 999) {
++        return 1900 + year;
++    }
++    return year;
++}
++
++function preprocessRFC2822(s) {
++    // Remove comments and folding whitespace and replace multiple-spaces with a single space
++    return s.replace(/\([^)]*\)|[\n\t]/g, ' ').replace(/(\s\s+)/g, ' ').trim();
++}
++
++function checkWeekday(weekdayStr, parsedInput, config) {
++    if (weekdayStr) {
++        // TODO: Replace the vanilla JS Date object with an indepentent day-of-week check.
++        var weekdayProvided = defaultLocaleWeekdaysShort.indexOf(weekdayStr),
++            weekdayActual = new Date(parsedInput[0], parsedInput[1], parsedInput[2]).getDay();
++        if (weekdayProvided !== weekdayActual) {
++            getParsingFlags(config).weekdayMismatch = true;
++            config._isValid = false;
++            return false;
++        }
++    }
++    return true;
++}
++
++var obsOffsets = {
++    UT: 0,
++    GMT: 0,
++    EDT: -4 * 60,
++    EST: -5 * 60,
++    CDT: -5 * 60,
++    CST: -6 * 60,
++    MDT: -6 * 60,
++    MST: -7 * 60,
++    PDT: -7 * 60,
++    PST: -8 * 60
++};
++
++function calculateOffset(obsOffset, militaryOffset, numOffset) {
++    if (obsOffset) {
++        return obsOffsets[obsOffset];
++    } else if (militaryOffset) {
++        // the only allowed military tz is Z
++        return 0;
++    } else {
++        var hm = parseInt(numOffset, 10);
++        var m = hm % 100, h = (hm - m) / 100;
++        return h * 60 + m;
++    }
++}
++
++// date and time from ref 2822 format
++function configFromRFC2822(config) {
++    var match = rfc2822.exec(preprocessRFC2822(config._i));
++    if (match) {
++        var parsedArray = extractFromRFC2822Strings(match[4], match[3], match[2], match[5], match[6], match[7]);
++        if (!checkWeekday(match[1], parsedArray, config)) {
++            return;
++        }
++
++        config._a = parsedArray;
++        config._tzm = calculateOffset(match[8], match[9], match[10]);
++
++        config._d = createUTCDate.apply(null, config._a);
++        config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm);
++
++        getParsingFlags(config).rfc2822 = true;
++    } else {
++        config._isValid = false;
++    }
++}
++
++// date from iso format or fallback
++function configFromString(config) {
++    var matched = aspNetJsonRegex.exec(config._i);
++
++    if (matched !== null) {
++        config._d = new Date(+matched[1]);
++        return;
++    }
++
++    configFromISO(config);
++    if (config._isValid === false) {
++        delete config._isValid;
++    } else {
++        return;
++    }
++
++    configFromRFC2822(config);
++    if (config._isValid === false) {
++        delete config._isValid;
++    } else {
++        return;
++    }
++
++    // Final attempt, use Input Fallback
++    hooks.createFromInputFallback(config);
++}
++
++hooks.createFromInputFallback = deprecate(
++    'value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), ' +
++    'which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are ' +
++    'discouraged and will be removed in an upcoming major release. Please refer to ' +
++    'http://momentjs.com/guides/#/warnings/js-date/ for more info.',
++    function (config) {
++        config._d = new Date(config._i + (config._useUTC ? ' UTC' : ''));
++    }
++);
++
++// constant that refers to the ISO standard
++hooks.ISO_8601 = function () {};
++
++// constant that refers to the RFC 2822 form
++hooks.RFC_2822 = function () {};
++
++// date from string and format string
++function configFromStringAndFormat(config) {
++    // TODO: Move this to another part of the creation flow to prevent circular deps
++    if (config._f === hooks.ISO_8601) {
++        configFromISO(config);
++        return;
++    }
++    if (config._f === hooks.RFC_2822) {
++        configFromRFC2822(config);
++        return;
++    }
++    config._a = [];
++    getParsingFlags(config).empty = true;
++
++    // This array is used to make a Date, either with `new Date` or `Date.UTC`
++    var string = '' + config._i,
++        i, parsedInput, tokens, token, skipped,
++        stringLength = string.length,
++        totalParsedInputLength = 0;
++
++    tokens = expandFormat(config._f, config._locale).match(formattingTokens) || [];
++
++    for (i = 0; i < tokens.length; i++) {
++        token = tokens[i];
++        parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0];
++        // console.log('token', token, 'parsedInput', parsedInput,
++        //         'regex', getParseRegexForToken(token, config));
++        if (parsedInput) {
++            skipped = string.substr(0, string.indexOf(parsedInput));
++            if (skipped.length > 0) {
++                getParsingFlags(config).unusedInput.push(skipped);
++            }
++            string = string.slice(string.indexOf(parsedInput) + parsedInput.length);
++            totalParsedInputLength += parsedInput.length;
++        }
++        // don't parse if it's not a known token
++        if (formatTokenFunctions[token]) {
++            if (parsedInput) {
++                getParsingFlags(config).empty = false;
++            }
++            else {
++                getParsingFlags(config).unusedTokens.push(token);
++            }
++            addTimeToArrayFromToken(token, parsedInput, config);
++        }
++        else if (config._strict && !parsedInput) {
++            getParsingFlags(config).unusedTokens.push(token);
++        }
++    }
++
++    // add remaining unparsed input length to the string
++    getParsingFlags(config).charsLeftOver = stringLength - totalParsedInputLength;
++    if (string.length > 0) {
++        getParsingFlags(config).unusedInput.push(string);
++    }
++
++    // clear _12h flag if hour is <= 12
++    if (config._a[HOUR] <= 12 &&
++        getParsingFlags(config).bigHour === true &&
++        config._a[HOUR] > 0) {
++        getParsingFlags(config).bigHour = undefined;
++    }
++
++    getParsingFlags(config).parsedDateParts = config._a.slice(0);
++    getParsingFlags(config).meridiem = config._meridiem;
++    // handle meridiem
++    config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem);
++
++    configFromArray(config);
++    checkOverflow(config);
++}
++
++
++function meridiemFixWrap (locale, hour, meridiem) {
++    var isPm;
++
++    if (meridiem == null) {
++        // nothing to do
++        return hour;
++    }
++    if (locale.meridiemHour != null) {
++        return locale.meridiemHour(hour, meridiem);
++    } else if (locale.isPM != null) {
++        // Fallback
++        isPm = locale.isPM(meridiem);
++        if (isPm && hour < 12) {
++            hour += 12;
++        }
++        if (!isPm && hour === 12) {
++            hour = 0;
++        }
++        return hour;
++    } else {
++        // this is not supposed to happen
++        return hour;
++    }
++}
++
++// date from string and array of format strings
++function configFromStringAndArray(config) {
++    var tempConfig,
++        bestMoment,
++
++        scoreToBeat,
++        i,
++        currentScore;
++
++    if (config._f.length === 0) {
++        getParsingFlags(config).invalidFormat = true;
++        config._d = new Date(NaN);
++        return;
++    }
++
++    for (i = 0; i < config._f.length; i++) {
++        currentScore = 0;
++        tempConfig = copyConfig({}, config);
++        if (config._useUTC != null) {
++            tempConfig._useUTC = config._useUTC;
++        }
++        tempConfig._f = config._f[i];
++        configFromStringAndFormat(tempConfig);
++
++        if (!isValid(tempConfig)) {
++            continue;
++        }
++
++        // if there is any input that was not parsed add a penalty for that format
++        currentScore += getParsingFlags(tempConfig).charsLeftOver;
++
++        //or tokens
++        currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10;
++
++        getParsingFlags(tempConfig).score = currentScore;
++
++        if (scoreToBeat == null || currentScore < scoreToBeat) {
++            scoreToBeat = currentScore;
++            bestMoment = tempConfig;
++        }
++    }
++
++    extend(config, bestMoment || tempConfig);
++}
++
++function configFromObject(config) {
++    if (config._d) {
++        return;
++    }
++
++    var i = normalizeObjectUnits(config._i);
++    config._a = map([i.year, i.month, i.day || i.date, i.hour, i.minute, i.second, i.millisecond], function (obj) {
++        return obj && parseInt(obj, 10);
++    });
++
++    configFromArray(config);
++}
++
++function createFromConfig (config) {
++    var res = new Moment(checkOverflow(prepareConfig(config)));
++    if (res._nextDay) {
++        // Adding is smart enough around DST
++        res.add(1, 'd');
++        res._nextDay = undefined;
++    }
++
++    return res;
++}
++
++function prepareConfig (config) {
++    var input = config._i,
++        format = config._f;
++
++    config._locale = config._locale || getLocale(config._l);
++
++    if (input === null || (format === undefined && input === '')) {
++        return createInvalid({nullInput: true});
++    }
++
++    if (typeof input === 'string') {
++        config._i = input = config._locale.preparse(input);
++    }
++
++    if (isMoment(input)) {
++        return new Moment(checkOverflow(input));
++    } else if (isDate(input)) {
++        config._d = input;
++    } else if (isArray(format)) {
++        configFromStringAndArray(config);
++    } else if (format) {
++        configFromStringAndFormat(config);
++    }  else {
++        configFromInput(config);
++    }
++
++    if (!isValid(config)) {
++        config._d = null;
++    }
++
++    return config;
++}
++
++function configFromInput(config) {
++    var input = config._i;
++    if (isUndefined(input)) {
++        config._d = new Date(hooks.now());
++    } else if (isDate(input)) {
++        config._d = new Date(input.valueOf());
++    } else if (typeof input === 'string') {
++        configFromString(config);
++    } else if (isArray(input)) {
++        config._a = map(input.slice(0), function (obj) {
++            return parseInt(obj, 10);
++        });
++        configFromArray(config);
++    } else if (isObject(input)) {
++        configFromObject(config);
++    } else if (isNumber(input)) {
++        // from milliseconds
++        config._d = new Date(input);
++    } else {
++        hooks.createFromInputFallback(config);
++    }
++}
++
++function createLocalOrUTC (input, format, locale, strict, isUTC) {
++    var c = {};
++
++    if (locale === true || locale === false) {
++        strict = locale;
++        locale = undefined;
++    }
++
++    if ((isObject(input) && isObjectEmpty(input)) ||
++            (isArray(input) && input.length === 0)) {
++        input = undefined;
++    }
++    // object construction must be done this way.
++    // https://github.com/moment/moment/issues/1423
++    c._isAMomentObject = true;
++    c._useUTC = c._isUTC = isUTC;
++    c._l = locale;
++    c._i = input;
++    c._f = format;
++    c._strict = strict;
++
++    return createFromConfig(c);
++}
++
++function createLocal (input, format, locale, strict) {
++    return createLocalOrUTC(input, format, locale, strict, false);
++}
++
++var prototypeMin = deprecate(
++    'moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/',
++    function () {
++        var other = createLocal.apply(null, arguments);
++        if (this.isValid() && other.isValid()) {
++            return other < this ? this : other;
++        } else {
++            return createInvalid();
++        }
++    }
++);
++
++var prototypeMax = deprecate(
++    'moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/',
++    function () {
++        var other = createLocal.apply(null, arguments);
++        if (this.isValid() && other.isValid()) {
++            return other > this ? this : other;
++        } else {
++            return createInvalid();
++        }
++    }
++);
++
++// Pick a moment m from moments so that m[fn](other) is true for all
++// other. This relies on the function fn to be transitive.
++//
++// moments should either be an array of moment objects or an array, whose
++// first element is an array of moment objects.
++function pickBy(fn, moments) {
++    var res, i;
++    if (moments.length === 1 && isArray(moments[0])) {
++        moments = moments[0];
++    }
++    if (!moments.length) {
++        return createLocal();
++    }
++    res = moments[0];
++    for (i = 1; i < moments.length; ++i) {
++        if (!moments[i].isValid() || moments[i][fn](res)) {
++            res = moments[i];
++        }
++    }
++    return res;
++}
++
++// TODO: Use [].sort instead?
++function min () {
++    var args = [].slice.call(arguments, 0);
++
++    return pickBy('isBefore', args);
++}
++
++function max () {
++    var args = [].slice.call(arguments, 0);
++
++    return pickBy('isAfter', args);
++}
++
++var now = function () {
++    return Date.now ? Date.now() : +(new Date());
++};
++
++var ordering = ['year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', 'millisecond'];
++
++function isDurationValid(m) {
++    for (var key in m) {
++        if (!(indexOf.call(ordering, key) !== -1 && (m[key] == null || !isNaN(m[key])))) {
++            return false;
++        }
++    }
++
++    var unitHasDecimal = false;
++    for (var i = 0; i < ordering.length; ++i) {
++        if (m[ordering[i]]) {
++            if (unitHasDecimal) {
++                return false; // only allow non-integers for smallest unit
++            }
++            if (parseFloat(m[ordering[i]]) !== toInt(m[ordering[i]])) {
++                unitHasDecimal = true;
++            }
++        }
++    }
++
++    return true;
++}
++
++function isValid$1() {
++    return this._isValid;
++}
++
++function createInvalid$1() {
++    return createDuration(NaN);
++}
++
++function Duration (duration) {
++    var normalizedInput = normalizeObjectUnits(duration),
++        years = normalizedInput.year || 0,
++        quarters = normalizedInput.quarter || 0,
++        months = normalizedInput.month || 0,
++        weeks = normalizedInput.week || 0,
++        days = normalizedInput.day || 0,
++        hours = normalizedInput.hour || 0,
++        minutes = normalizedInput.minute || 0,
++        seconds = normalizedInput.second || 0,
++        milliseconds = normalizedInput.millisecond || 0;
++
++    this._isValid = isDurationValid(normalizedInput);
++
++    // representation for dateAddRemove
++    this._milliseconds = +milliseconds +
++        seconds * 1e3 + // 1000
++        minutes * 6e4 + // 1000 * 60
++        hours * 1000 * 60 * 60; //using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978
++    // Because of dateAddRemove treats 24 hours as different from a
++    // day when working around DST, we need to store them separately
++    this._days = +days +
++        weeks * 7;
++    // It is impossible to translate months into days without knowing
++    // which months you are are talking about, so we have to store
++    // it separately.
++    this._months = +months +
++        quarters * 3 +
++        years * 12;
++
++    this._data = {};
++
++    this._locale = getLocale();
++
++    this._bubble();
++}
++
++function isDuration (obj) {
++    return obj instanceof Duration;
++}
++
++function absRound (number) {
++    if (number < 0) {
++        return Math.round(-1 * number) * -1;
++    } else {
++        return Math.round(number);
++    }
++}
++
++// FORMATTING
++
++function offset (token, separator) {
++    addFormatToken(token, 0, 0, function () {
++        var offset = this.utcOffset();
++        var sign = '+';
++        if (offset < 0) {
++            offset = -offset;
++            sign = '-';
++        }
++        return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~(offset) % 60, 2);
++    });
++}
++
++offset('Z', ':');
++offset('ZZ', '');
++
++// PARSING
++
++addRegexToken('Z',  matchShortOffset);
++addRegexToken('ZZ', matchShortOffset);
++addParseToken(['Z', 'ZZ'], function (input, array, config) {
++    config._useUTC = true;
++    config._tzm = offsetFromString(matchShortOffset, input);
++});
++
++// HELPERS
++
++// timezone chunker
++// '+10:00' > ['10',  '00']
++// '-1530'  > ['-15', '30']
++var chunkOffset = /([\+\-]|\d\d)/gi;
++
++function offsetFromString(matcher, string) {
++    var matches = (string || '').match(matcher);
++
++    if (matches === null) {
++        return null;
++    }
++
++    var chunk   = matches[matches.length - 1] || [];
++    var parts   = (chunk + '').match(chunkOffset) || ['-', 0, 0];
++    var minutes = +(parts[1] * 60) + toInt(parts[2]);
++
++    return minutes === 0 ?
++      0 :
++      parts[0] === '+' ? minutes : -minutes;
++}
++
++// Return a moment from input, that is local/utc/zone equivalent to model.
++function cloneWithOffset(input, model) {
++    var res, diff;
++    if (model._isUTC) {
++        res = model.clone();
++        diff = (isMoment(input) || isDate(input) ? input.valueOf() : createLocal(input).valueOf()) - res.valueOf();
++        // Use low-level api, because this fn is low-level api.
++        res._d.setTime(res._d.valueOf() + diff);
++        hooks.updateOffset(res, false);
++        return res;
++    } else {
++        return createLocal(input).local();
++    }
++}
++
++function getDateOffset (m) {
++    // On Firefox.24 Date#getTimezoneOffset returns a floating point.
++    // https://github.com/moment/moment/pull/1871
++    return -Math.round(m._d.getTimezoneOffset() / 15) * 15;
++}
++
++// HOOKS
++
++// This function will be called whenever a moment is mutated.
++// It is intended to keep the offset in sync with the timezone.
++hooks.updateOffset = function () {};
++
++// MOMENTS
++
++// keepLocalTime = true means only change the timezone, without
++// affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]-->
++// 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset
++// +0200, so we adjust the time as needed, to be valid.
++//
++// Keeping the time actually adds/subtracts (one hour)
++// from the actual represented time. That is why we call updateOffset
++// a second time. In case it wants us to change the offset again
++// _changeInProgress == true case, then we have to adjust, because
++// there is no such time in the given timezone.
++function getSetOffset (input, keepLocalTime, keepMinutes) {
++    var offset = this._offset || 0,
++        localAdjust;
++    if (!this.isValid()) {
++        return input != null ? this : NaN;
++    }
++    if (input != null) {
++        if (typeof input === 'string') {
++            input = offsetFromString(matchShortOffset, input);
++            if (input === null) {
++                return this;
++            }
++        } else if (Math.abs(input) < 16 && !keepMinutes) {
++            input = input * 60;
++        }
++        if (!this._isUTC && keepLocalTime) {
++            localAdjust = getDateOffset(this);
++        }
++        this._offset = input;
++        this._isUTC = true;
++        if (localAdjust != null) {
++            this.add(localAdjust, 'm');
++        }
++        if (offset !== input) {
++            if (!keepLocalTime || this._changeInProgress) {
++                addSubtract(this, createDuration(input - offset, 'm'), 1, false);
++            } else if (!this._changeInProgress) {
++                this._changeInProgress = true;
++                hooks.updateOffset(this, true);
++                this._changeInProgress = null;
++            }
++        }
++        return this;
++    } else {
++        return this._isUTC ? offset : getDateOffset(this);
++    }
++}
++
++function getSetZone (input, keepLocalTime) {
++    if (input != null) {
++        if (typeof input !== 'string') {
++            input = -input;
++        }
++
++        this.utcOffset(input, keepLocalTime);
++
++        return this;
++    } else {
++        return -this.utcOffset();
++    }
++}
++
++function setOffsetToUTC (keepLocalTime) {
++    return this.utcOffset(0, keepLocalTime);
++}
++
++function setOffsetToLocal (keepLocalTime) {
++    if (this._isUTC) {
++        this.utcOffset(0, keepLocalTime);
++        this._isUTC = false;
++
++        if (keepLocalTime) {
++            this.subtract(getDateOffset(this), 'm');
++        }
++    }
++    return this;
++}
++
++function setOffsetToParsedOffset () {
++    if (this._tzm != null) {
++        this.utcOffset(this._tzm, false, true);
++    } else if (typeof this._i === 'string') {
++        var tZone = offsetFromString(matchOffset, this._i);
++        if (tZone != null) {
++            this.utcOffset(tZone);
++        }
++        else {
++            this.utcOffset(0, true);
++        }
++    }
++    return this;
++}
++
++function hasAlignedHourOffset (input) {
++    if (!this.isValid()) {
++        return false;
++    }
++    input = input ? createLocal(input).utcOffset() : 0;
++
++    return (this.utcOffset() - input) % 60 === 0;
++}
++
++function isDaylightSavingTime () {
++    return (
++        this.utcOffset() > this.clone().month(0).utcOffset() ||
++        this.utcOffset() > this.clone().month(5).utcOffset()
++    );
++}
++
++function isDaylightSavingTimeShifted () {
++    if (!isUndefined(this._isDSTShifted)) {
++        return this._isDSTShifted;
++    }
++
++    var c = {};
++
++    copyConfig(c, this);
++    c = prepareConfig(c);
++
++    if (c._a) {
++        var other = c._isUTC ? createUTC(c._a) : createLocal(c._a);
++        this._isDSTShifted = this.isValid() &&
++            compareArrays(c._a, other.toArray()) > 0;
++    } else {
++        this._isDSTShifted = false;
++    }
++
++    return this._isDSTShifted;
++}
++
++function isLocal () {
++    return this.isValid() ? !this._isUTC : false;
++}
++
++function isUtcOffset () {
++    return this.isValid() ? this._isUTC : false;
++}
++
++function isUtc () {
++    return this.isValid() ? this._isUTC && this._offset === 0 : false;
++}
++
++// ASP.NET json date format regex
++var aspNetRegex = /^(\-|\+)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/;
++
++// from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html
++// somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere
++// and further modified to allow for strings containing both week and day
++var isoRegex = /^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/;
++
++function createDuration (input, key) {
++    var duration = input,
++        // matching against regexp is expensive, do it on demand
++        match = null,
++        sign,
++        ret,
++        diffRes;
++
++    if (isDuration(input)) {
++        duration = {
++            ms : input._milliseconds,
++            d  : input._days,
++            M  : input._months
++        };
++    } else if (isNumber(input)) {
++        duration = {};
++        if (key) {
++            duration[key] = input;
++        } else {
++            duration.milliseconds = input;
++        }
++    } else if (!!(match = aspNetRegex.exec(input))) {
++        sign = (match[1] === '-') ? -1 : 1;
++        duration = {
++            y  : 0,
++            d  : toInt(match[DATE])                         * sign,
++            h  : toInt(match[HOUR])                         * sign,
++            m  : toInt(match[MINUTE])                       * sign,
++            s  : toInt(match[SECOND])                       * sign,
++            ms : toInt(absRound(match[MILLISECOND] * 1000)) * sign // the millisecond decimal point is included in the match
++        };
++    } else if (!!(match = isoRegex.exec(input))) {
++        sign = (match[1] === '-') ? -1 : (match[1] === '+') ? 1 : 1;
++        duration = {
++            y : parseIso(match[2], sign),
++            M : parseIso(match[3], sign),
++            w : parseIso(match[4], sign),
++            d : parseIso(match[5], sign),
++            h : parseIso(match[6], sign),
++            m : parseIso(match[7], sign),
++            s : parseIso(match[8], sign)
++        };
++    } else if (duration == null) {// checks for null or undefined
++        duration = {};
++    } else if (typeof duration === 'object' && ('from' in duration || 'to' in duration)) {
++        diffRes = momentsDifference(createLocal(duration.from), createLocal(duration.to));
++
++        duration = {};
++        duration.ms = diffRes.milliseconds;
++        duration.M = diffRes.months;
++    }
++
++    ret = new Duration(duration);
++
++    if (isDuration(input) && hasOwnProp(input, '_locale')) {
++        ret._locale = input._locale;
++    }
++
++    return ret;
++}
++
++createDuration.fn = Duration.prototype;
++createDuration.invalid = createInvalid$1;
++
++function parseIso (inp, sign) {
++    // We'd normally use ~~inp for this, but unfortunately it also
++    // converts floats to ints.
++    // inp may be undefined, so careful calling replace on it.
++    var res = inp && parseFloat(inp.replace(',', '.'));
++    // apply sign while we're at it
++    return (isNaN(res) ? 0 : res) * sign;
++}
++
++function positiveMomentsDifference(base, other) {
++    var res = {milliseconds: 0, months: 0};
++
++    res.months = other.month() - base.month() +
++        (other.year() - base.year()) * 12;
++    if (base.clone().add(res.months, 'M').isAfter(other)) {
++        --res.months;
++    }
++
++    res.milliseconds = +other - +(base.clone().add(res.months, 'M'));
++
++    return res;
++}
++
++function momentsDifference(base, other) {
++    var res;
++    if (!(base.isValid() && other.isValid())) {
++        return {milliseconds: 0, months: 0};
++    }
++
++    other = cloneWithOffset(other, base);
++    if (base.isBefore(other)) {
++        res = positiveMomentsDifference(base, other);
++    } else {
++        res = positiveMomentsDifference(other, base);
++        res.milliseconds = -res.milliseconds;
++        res.months = -res.months;
++    }
++
++    return res;
++}
++
++// TODO: remove 'name' arg after deprecation is removed
++function createAdder(direction, name) {
++    return function (val, period) {
++        var dur, tmp;
++        //invert the arguments, but complain about it
++        if (period !== null && !isNaN(+period)) {
++            deprecateSimple(name, 'moment().' + name  + '(period, number) is deprecated. Please use moment().' + name + '(number, period). ' +
++            'See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.');
++            tmp = val; val = period; period = tmp;
++        }
++
++        val = typeof val === 'string' ? +val : val;
++        dur = createDuration(val, period);
++        addSubtract(this, dur, direction);
++        return this;
++    };
++}
++
++function addSubtract (mom, duration, isAdding, updateOffset) {
++    var milliseconds = duration._milliseconds,
++        days = absRound(duration._days),
++        months = absRound(duration._months);
++
++    if (!mom.isValid()) {
++        // No op
++        return;
++    }
++
++    updateOffset = updateOffset == null ? true : updateOffset;
++
++    if (months) {
++        setMonth(mom, get(mom, 'Month') + months * isAdding);
++    }
++    if (days) {
++        set$1(mom, 'Date', get(mom, 'Date') + days * isAdding);
++    }
++    if (milliseconds) {
++        mom._d.setTime(mom._d.valueOf() + milliseconds * isAdding);
++    }
++    if (updateOffset) {
++        hooks.updateOffset(mom, days || months);
++    }
++}
++
++var add      = createAdder(1, 'add');
++var subtract = createAdder(-1, 'subtract');
++
++function getCalendarFormat(myMoment, now) {
++    var diff = myMoment.diff(now, 'days', true);
++    return diff < -6 ? 'sameElse' :
++            diff < -1 ? 'lastWeek' :
++            diff < 0 ? 'lastDay' :
++            diff < 1 ? 'sameDay' :
++            diff < 2 ? 'nextDay' :
++            diff < 7 ? 'nextWeek' : 'sameElse';
++}
++
++function calendar$1 (time, formats) {
++    // We want to compare the start of today, vs this.
++    // Getting start-of-today depends on whether we're local/utc/offset or not.
++    var now = time || createLocal(),
++        sod = cloneWithOffset(now, this).startOf('day'),
++        format = hooks.calendarFormat(this, sod) || 'sameElse';
++
++    var output = formats && (isFunction(formats[format]) ? formats[format].call(this, now) : formats[format]);
++
++    return this.format(output || this.localeData().calendar(format, this, createLocal(now)));
++}
++
++function clone () {
++    return new Moment(this);
++}
++
++function isAfter (input, units) {
++    var localInput = isMoment(input) ? input : createLocal(input);
++    if (!(this.isValid() && localInput.isValid())) {
++        return false;
++    }
++    units = normalizeUnits(!isUndefined(units) ? units : 'millisecond');
++    if (units === 'millisecond') {
++        return this.valueOf() > localInput.valueOf();
++    } else {
++        return localInput.valueOf() < this.clone().startOf(units).valueOf();
++    }
++}
++
++function isBefore (input, units) {
++    var localInput = isMoment(input) ? input : createLocal(input);
++    if (!(this.isValid() && localInput.isValid())) {
++        return false;
++    }
++    units = normalizeUnits(!isUndefined(units) ? units : 'millisecond');
++    if (units === 'millisecond') {
++        return this.valueOf() < localInput.valueOf();
++    } else {
++        return this.clone().endOf(units).valueOf() < localInput.valueOf();
++    }
++}
++
++function isBetween (from, to, units, inclusivity) {
++    inclusivity = inclusivity || '()';
++    return (inclusivity[0] === '(' ? this.isAfter(from, units) : !this.isBefore(from, units)) &&
++        (inclusivity[1] === ')' ? this.isBefore(to, units) : !this.isAfter(to, units));
++}
++
++function isSame (input, units) {
++    var localInput = isMoment(input) ? input : createLocal(input),
++        inputMs;
++    if (!(this.isValid() && localInput.isValid())) {
++        return false;
++    }
++    units = normalizeUnits(units || 'millisecond');
++    if (units === 'millisecond') {
++        return this.valueOf() === localInput.valueOf();
++    } else {
++        inputMs = localInput.valueOf();
++        return this.clone().startOf(units).valueOf() <= inputMs && inputMs <= this.clone().endOf(units).valueOf();
++    }
++}
++
++function isSameOrAfter (input, units) {
++    return this.isSame(input, units) || this.isAfter(input,units);
++}
++
++function isSameOrBefore (input, units) {
++    return this.isSame(input, units) || this.isBefore(input,units);
++}
++
++function diff (input, units, asFloat) {
++    var that,
++        zoneDelta,
++        delta, output;
++
++    if (!this.isValid()) {
++        return NaN;
++    }
++
++    that = cloneWithOffset(input, this);
++
++    if (!that.isValid()) {
++        return NaN;
++    }
++
++    zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4;
++
++    units = normalizeUnits(units);
++
++    switch (units) {
++        case 'year': output = monthDiff(this, that) / 12; break;
++        case 'month': output = monthDiff(this, that); break;
++        case 'quarter': output = monthDiff(this, that) / 3; break;
++        case 'second': output = (this - that) / 1e3; break; // 1000
++        case 'minute': output = (this - that) / 6e4; break; // 1000 * 60
++        case 'hour': output = (this - that) / 36e5; break; // 1000 * 60 * 60
++        case 'day': output = (this - that - zoneDelta) / 864e5; break; // 1000 * 60 * 60 * 24, negate dst
++        case 'week': output = (this - that - zoneDelta) / 6048e5; break; // 1000 * 60 * 60 * 24 * 7, negate dst
++        default: output = this - that;
++    }
++
++    return asFloat ? output : absFloor(output);
++}
++
++function monthDiff (a, b) {
++    // difference in months
++    var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()),
++        // b is in (anchor - 1 month, anchor + 1 month)
++        anchor = a.clone().add(wholeMonthDiff, 'months'),
++        anchor2, adjust;
++
++    if (b - anchor < 0) {
++        anchor2 = a.clone().add(wholeMonthDiff - 1, 'months');
++        // linear across the month
++        adjust = (b - anchor) / (anchor - anchor2);
++    } else {
++        anchor2 = a.clone().add(wholeMonthDiff + 1, 'months');
++        // linear across the month
++        adjust = (b - anchor) / (anchor2 - anchor);
++    }
++
++    //check for negative zero, return zero if negative zero
++    return -(wholeMonthDiff + adjust) || 0;
++}
++
++hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ';
++hooks.defaultFormatUtc = 'YYYY-MM-DDTHH:mm:ss[Z]';
++
++function toString () {
++    return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ');
++}
++
++function toISOString(keepOffset) {
++    if (!this.isValid()) {
++        return null;
++    }
++    var utc = keepOffset !== true;
++    var m = utc ? this.clone().utc() : this;
++    if (m.year() < 0 || m.year() > 9999) {
++        return formatMoment(m, utc ? 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]' : 'YYYYYY-MM-DD[T]HH:mm:ss.SSSZ');
++    }
++    if (isFunction(Date.prototype.toISOString)) {
++        // native implementation is ~50x faster, use it when we can
++        if (utc) {
++            return this.toDate().toISOString();
++        } else {
++            return new Date(this._d.valueOf()).toISOString().replace('Z', formatMoment(m, 'Z'));
++        }
++    }
++    return formatMoment(m, utc ? 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]' : 'YYYY-MM-DD[T]HH:mm:ss.SSSZ');
++}
++
++/**
++ * Return a human readable representation of a moment that can
++ * also be evaluated to get a new moment which is the same
++ *
++ * @link https://nodejs.org/dist/latest/docs/api/util.html#util_custom_inspect_function_on_objects
++ */
++function inspect () {
++    if (!this.isValid()) {
++        return 'moment.invalid(/* ' + this._i + ' */)';
++    }
++    var func = 'moment';
++    var zone = '';
++    if (!this.isLocal()) {
++        func = this.utcOffset() === 0 ? 'moment.utc' : 'moment.parseZone';
++        zone = 'Z';
++    }
++    var prefix = '[' + func + '("]';
++    var year = (0 <= this.year() && this.year() <= 9999) ? 'YYYY' : 'YYYYYY';
++    var datetime = '-MM-DD[T]HH:mm:ss.SSS';
++    var suffix = zone + '[")]';
++
++    return this.format(prefix + year + datetime + suffix);
++}
++
++function format (inputString) {
++    if (!inputString) {
++        inputString = this.isUtc() ? hooks.defaultFormatUtc : hooks.defaultFormat;
++    }
++    var output = formatMoment(this, inputString);
++    return this.localeData().postformat(output);
++}
++
++function from (time, withoutSuffix) {
++    if (this.isValid() &&
++            ((isMoment(time) && time.isValid()) ||
++             createLocal(time).isValid())) {
++        return createDuration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix);
++    } else {
++        return this.localeData().invalidDate();
++    }
++}
++
++function fromNow (withoutSuffix) {
++    return this.from(createLocal(), withoutSuffix);
++}
++
++function to (time, withoutSuffix) {
++    if (this.isValid() &&
++            ((isMoment(time) && time.isValid()) ||
++             createLocal(time).isValid())) {
++        return createDuration({from: this, to: time}).locale(this.locale()).humanize(!withoutSuffix);
++    } else {
++        return this.localeData().invalidDate();
++    }
++}
++
++function toNow (withoutSuffix) {
++    return this.to(createLocal(), withoutSuffix);
++}
++
++// If passed a locale key, it will set the locale for this
++// instance.  Otherwise, it will return the locale configuration
++// variables for this instance.
++function locale (key) {
++    var newLocaleData;
++
++    if (key === undefined) {
++        return this._locale._abbr;
++    } else {
++        newLocaleData = getLocale(key);
++        if (newLocaleData != null) {
++            this._locale = newLocaleData;
++        }
++        return this;
++    }
++}
++
++var lang = deprecate(
++    'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.',
++    function (key) {
++        if (key === undefined) {
++            return this.localeData();
++        } else {
++            return this.locale(key);
++        }
++    }
++);
++
++function localeData () {
++    return this._locale;
++}
++
++function startOf (units) {
++    units = normalizeUnits(units);
++    // the following switch intentionally omits break keywords
++    // to utilize falling through the cases.
++    switch (units) {
++        case 'year':
++            this.month(0);
++            /* falls through */
++        case 'quarter':
++        case 'month':
++            this.date(1);
++            /* falls through */
++        case 'week':
++        case 'isoWeek':
++        case 'day':
++        case 'date':
++            this.hours(0);
++            /* falls through */
++        case 'hour':
++            this.minutes(0);
++            /* falls through */
++        case 'minute':
++            this.seconds(0);
++            /* falls through */
++        case 'second':
++            this.milliseconds(0);
++    }
++
++    // weeks are a special case
++    if (units === 'week') {
++        this.weekday(0);
++    }
++    if (units === 'isoWeek') {
++        this.isoWeekday(1);
++    }
++
++    // quarters are also special
++    if (units === 'quarter') {
++        this.month(Math.floor(this.month() / 3) * 3);
++    }
++
++    return this;
++}
++
++function endOf (units) {
++    units = normalizeUnits(units);
++    if (units === undefined || units === 'millisecond') {
++        return this;
++    }
++
++    // 'date' is an alias for 'day', so it should be considered as such.
++    if (units === 'date') {
++        units = 'day';
++    }
++
++    return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms');
++}
++
++function valueOf () {
++    return this._d.valueOf() - ((this._offset || 0) * 60000);
++}
++
++function unix () {
++    return Math.floor(this.valueOf() / 1000);
++}
++
++function toDate () {
++    return new Date(this.valueOf());
++}
++
++function toArray () {
++    var m = this;
++    return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()];
++}
++
++function toObject () {
++    var m = this;
++    return {
++        years: m.year(),
++        months: m.month(),
++        date: m.date(),
++        hours: m.hours(),
++        minutes: m.minutes(),
++        seconds: m.seconds(),
++        milliseconds: m.milliseconds()
++    };
++}
++
++function toJSON () {
++    // new Date(NaN).toJSON() === null
++    return this.isValid() ? this.toISOString() : null;
++}
++
++function isValid$2 () {
++    return isValid(this);
++}
++
++function parsingFlags () {
++    return extend({}, getParsingFlags(this));
++}
++
++function invalidAt () {
++    return getParsingFlags(this).overflow;
++}
++
++function creationData() {
++    return {
++        input: this._i,
++        format: this._f,
++        locale: this._locale,
++        isUTC: this._isUTC,
++        strict: this._strict
++    };
++}
++
++// FORMATTING
++
++addFormatToken(0, ['gg', 2], 0, function () {
++    return this.weekYear() % 100;
++});
++
++addFormatToken(0, ['GG', 2], 0, function () {
++    return this.isoWeekYear() % 100;
++});
++
++function addWeekYearFormatToken (token, getter) {
++    addFormatToken(0, [token, token.length], 0, getter);
++}
++
++addWeekYearFormatToken('gggg',     'weekYear');
++addWeekYearFormatToken('ggggg',    'weekYear');
++addWeekYearFormatToken('GGGG',  'isoWeekYear');
++addWeekYearFormatToken('GGGGG', 'isoWeekYear');
++
++// ALIASES
++
++addUnitAlias('weekYear', 'gg');
++addUnitAlias('isoWeekYear', 'GG');
++
++// PRIORITY
++
++addUnitPriority('weekYear', 1);
++addUnitPriority('isoWeekYear', 1);
++
++
++// PARSING
++
++addRegexToken('G',      matchSigned);
++addRegexToken('g',      matchSigned);
++addRegexToken('GG',     match1to2, match2);
++addRegexToken('gg',     match1to2, match2);
++addRegexToken('GGGG',   match1to4, match4);
++addRegexToken('gggg',   match1to4, match4);
++addRegexToken('GGGGG',  match1to6, match6);
++addRegexToken('ggggg',  match1to6, match6);
++
++addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function (input, week, config, token) {
++    week[token.substr(0, 2)] = toInt(input);
++});
++
++addWeekParseToken(['gg', 'GG'], function (input, week, config, token) {
++    week[token] = hooks.parseTwoDigitYear(input);
++});
++
++// MOMENTS
++
++function getSetWeekYear (input) {
++    return getSetWeekYearHelper.call(this,
++            input,
++            this.week(),
++            this.weekday(),
++            this.localeData()._week.dow,
++            this.localeData()._week.doy);
++}
++
++function getSetISOWeekYear (input) {
++    return getSetWeekYearHelper.call(this,
++            input, this.isoWeek(), this.isoWeekday(), 1, 4);
++}
++
++function getISOWeeksInYear () {
++    return weeksInYear(this.year(), 1, 4);
++}
++
++function getWeeksInYear () {
++    var weekInfo = this.localeData()._week;
++    return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy);
++}
++
++function getSetWeekYearHelper(input, week, weekday, dow, doy) {
++    var weeksTarget;
++    if (input == null) {
++        return weekOfYear(this, dow, doy).year;
++    } else {
++        weeksTarget = weeksInYear(input, dow, doy);
++        if (week > weeksTarget) {
++            week = weeksTarget;
++        }
++        return setWeekAll.call(this, input, week, weekday, dow, doy);
++    }
++}
++
++function setWeekAll(weekYear, week, weekday, dow, doy) {
++    var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy),
++        date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear);
++
++    this.year(date.getUTCFullYear());
++    this.month(date.getUTCMonth());
++    this.date(date.getUTCDate());
++    return this;
++}
++
++// FORMATTING
++
++addFormatToken('Q', 0, 'Qo', 'quarter');
++
++// ALIASES
++
++addUnitAlias('quarter', 'Q');
++
++// PRIORITY
++
++addUnitPriority('quarter', 7);
++
++// PARSING
++
++addRegexToken('Q', match1);
++addParseToken('Q', function (input, array) {
++    array[MONTH] = (toInt(input) - 1) * 3;
++});
++
++// MOMENTS
++
++function getSetQuarter (input) {
++    return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3);
++}
++
++// FORMATTING
++
++addFormatToken('D', ['DD', 2], 'Do', 'date');
++
++// ALIASES
++
++addUnitAlias('date', 'D');
++
++// PRIOROITY
++addUnitPriority('date', 9);
++
++// PARSING
++
++addRegexToken('D',  match1to2);
++addRegexToken('DD', match1to2, match2);
++addRegexToken('Do', function (isStrict, locale) {
++    // TODO: Remove "ordinalParse" fallback in next major release.
++    return isStrict ?
++      (locale._dayOfMonthOrdinalParse || locale._ordinalParse) :
++      locale._dayOfMonthOrdinalParseLenient;
++});
++
++addParseToken(['D', 'DD'], DATE);
++addParseToken('Do', function (input, array) {
++    array[DATE] = toInt(input.match(match1to2)[0]);
++});
++
++// MOMENTS
++
++var getSetDayOfMonth = makeGetSet('Date', true);
++
++// FORMATTING
++
++addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear');
++
++// ALIASES
++
++addUnitAlias('dayOfYear', 'DDD');
++
++// PRIORITY
++addUnitPriority('dayOfYear', 4);
++
++// PARSING
++
++addRegexToken('DDD',  match1to3);
++addRegexToken('DDDD', match3);
++addParseToken(['DDD', 'DDDD'], function (input, array, config) {
++    config._dayOfYear = toInt(input);
++});
++
++// HELPERS
++
++// MOMENTS
++
++function getSetDayOfYear (input) {
++    var dayOfYear = Math.round((this.clone().startOf('day') - this.clone().startOf('year')) / 864e5) + 1;
++    return input == null ? dayOfYear : this.add((input - dayOfYear), 'd');
++}
++
++// FORMATTING
++
++addFormatToken('m', ['mm', 2], 0, 'minute');
++
++// ALIASES
++
++addUnitAlias('minute', 'm');
++
++// PRIORITY
++
++addUnitPriority('minute', 14);
++
++// PARSING
++
++addRegexToken('m',  match1to2);
++addRegexToken('mm', match1to2, match2);
++addParseToken(['m', 'mm'], MINUTE);
++
++// MOMENTS
++
++var getSetMinute = makeGetSet('Minutes', false);
++
++// FORMATTING
++
++addFormatToken('s', ['ss', 2], 0, 'second');
++
++// ALIASES
++
++addUnitAlias('second', 's');
++
++// PRIORITY
++
++addUnitPriority('second', 15);
++
++// PARSING
++
++addRegexToken('s',  match1to2);
++addRegexToken('ss', match1to2, match2);
++addParseToken(['s', 'ss'], SECOND);
++
++// MOMENTS
++
++var getSetSecond = makeGetSet('Seconds', false);
++
++// FORMATTING
++
++addFormatToken('S', 0, 0, function () {
++    return ~~(this.millisecond() / 100);
++});
++
++addFormatToken(0, ['SS', 2], 0, function () {
++    return ~~(this.millisecond() / 10);
++});
++
++addFormatToken(0, ['SSS', 3], 0, 'millisecond');
++addFormatToken(0, ['SSSS', 4], 0, function () {
++    return this.millisecond() * 10;
++});
++addFormatToken(0, ['SSSSS', 5], 0, function () {
++    return this.millisecond() * 100;
++});
++addFormatToken(0, ['SSSSSS', 6], 0, function () {
++    return this.millisecond() * 1000;
++});
++addFormatToken(0, ['SSSSSSS', 7], 0, function () {
++    return this.millisecond() * 10000;
++});
++addFormatToken(0, ['SSSSSSSS', 8], 0, function () {
++    return this.millisecond() * 100000;
++});
++addFormatToken(0, ['SSSSSSSSS', 9], 0, function () {
++    return this.millisecond() * 1000000;
++});
++
++
++// ALIASES
++
++addUnitAlias('millisecond', 'ms');
++
++// PRIORITY
++
++addUnitPriority('millisecond', 16);
++
++// PARSING
++
++addRegexToken('S',    match1to3, match1);
++addRegexToken('SS',   match1to3, match2);
++addRegexToken('SSS',  match1to3, match3);
++
++var token;
++for (token = 'SSSS'; token.length <= 9; token += 'S') {
++    addRegexToken(token, matchUnsigned);
++}
++
++function parseMs(input, array) {
++    array[MILLISECOND] = toInt(('0.' + input) * 1000);
++}
++
++for (token = 'S'; token.length <= 9; token += 'S') {
++    addParseToken(token, parseMs);
++}
++// MOMENTS
++
++var getSetMillisecond = makeGetSet('Milliseconds', false);
++
++// FORMATTING
++
++addFormatToken('z',  0, 0, 'zoneAbbr');
++addFormatToken('zz', 0, 0, 'zoneName');
++
++// MOMENTS
++
++function getZoneAbbr () {
++    return this._isUTC ? 'UTC' : '';
++}
++
++function getZoneName () {
++    return this._isUTC ? 'Coordinated Universal Time' : '';
++}
++
++var proto = Moment.prototype;
++
++proto.add               = add;
++proto.calendar          = calendar$1;
++proto.clone             = clone;
++proto.diff              = diff;
++proto.endOf             = endOf;
++proto.format            = format;
++proto.from              = from;
++proto.fromNow           = fromNow;
++proto.to                = to;
++proto.toNow             = toNow;
++proto.get               = stringGet;
++proto.invalidAt         = invalidAt;
++proto.isAfter           = isAfter;
++proto.isBefore          = isBefore;
++proto.isBetween         = isBetween;
++proto.isSame            = isSame;
++proto.isSameOrAfter     = isSameOrAfter;
++proto.isSameOrBefore    = isSameOrBefore;
++proto.isValid           = isValid$2;
++proto.lang              = lang;
++proto.locale            = locale;
++proto.localeData        = localeData;
++proto.max               = prototypeMax;
++proto.min               = prototypeMin;
++proto.parsingFlags      = parsingFlags;
++proto.set               = stringSet;
++proto.startOf           = startOf;
++proto.subtract          = subtract;
++proto.toArray           = toArray;
++proto.toObject          = toObject;
++proto.toDate            = toDate;
++proto.toISOString       = toISOString;
++proto.inspect           = inspect;
++proto.toJSON            = toJSON;
++proto.toString          = toString;
++proto.unix              = unix;
++proto.valueOf           = valueOf;
++proto.creationData      = creationData;
++
++// Year
++proto.year       = getSetYear;
++proto.isLeapYear = getIsLeapYear;
++
++// Week Year
++proto.weekYear    = getSetWeekYear;
++proto.isoWeekYear = getSetISOWeekYear;
++
++// Quarter
++proto.quarter = proto.quarters = getSetQuarter;
++
++// Month
++proto.month       = getSetMonth;
++proto.daysInMonth = getDaysInMonth;
++
++// Week
++proto.week           = proto.weeks        = getSetWeek;
++proto.isoWeek        = proto.isoWeeks     = getSetISOWeek;
++proto.weeksInYear    = getWeeksInYear;
++proto.isoWeeksInYear = getISOWeeksInYear;
++
++// Day
++proto.date       = getSetDayOfMonth;
++proto.day        = proto.days             = getSetDayOfWeek;
++proto.weekday    = getSetLocaleDayOfWeek;
++proto.isoWeekday = getSetISODayOfWeek;
++proto.dayOfYear  = getSetDayOfYear;
++
++// Hour
++proto.hour = proto.hours = getSetHour;
++
++// Minute
++proto.minute = proto.minutes = getSetMinute;
++
++// Second
++proto.second = proto.seconds = getSetSecond;
++
++// Millisecond
++proto.millisecond = proto.milliseconds = getSetMillisecond;
++
++// Offset
++proto.utcOffset            = getSetOffset;
++proto.utc                  = setOffsetToUTC;
++proto.local                = setOffsetToLocal;
++proto.parseZone            = setOffsetToParsedOffset;
++proto.hasAlignedHourOffset = hasAlignedHourOffset;
++proto.isDST                = isDaylightSavingTime;
++proto.isLocal              = isLocal;
++proto.isUtcOffset          = isUtcOffset;
++proto.isUtc                = isUtc;
++proto.isUTC                = isUtc;
++
++// Timezone
++proto.zoneAbbr = getZoneAbbr;
++proto.zoneName = getZoneName;
++
++// Deprecations
++proto.dates  = deprecate('dates accessor is deprecated. Use date instead.', getSetDayOfMonth);
++proto.months = deprecate('months accessor is deprecated. Use month instead', getSetMonth);
++proto.years  = deprecate('years accessor is deprecated. Use year instead', getSetYear);
++proto.zone   = deprecate('moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/', getSetZone);
++proto.isDSTShifted = deprecate('isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information', isDaylightSavingTimeShifted);
++
++function createUnix (input) {
++    return createLocal(input * 1000);
++}
++
++function createInZone () {
++    return createLocal.apply(null, arguments).parseZone();
++}
++
++function preParsePostFormat (string) {
++    return string;
++}
++
++var proto$1 = Locale.prototype;
++
++proto$1.calendar        = calendar;
++proto$1.longDateFormat  = longDateFormat;
++proto$1.invalidDate     = invalidDate;
++proto$1.ordinal         = ordinal;
++proto$1.preparse        = preParsePostFormat;
++proto$1.postformat      = preParsePostFormat;
++proto$1.relativeTime    = relativeTime;
++proto$1.pastFuture      = pastFuture;
++proto$1.set             = set;
++
++// Month
++proto$1.months            =        localeMonths;
++proto$1.monthsShort       =        localeMonthsShort;
++proto$1.monthsParse       =        localeMonthsParse;
++proto$1.monthsRegex       = monthsRegex;
++proto$1.monthsShortRegex  = monthsShortRegex;
++
++// Week
++proto$1.week = localeWeek;
++proto$1.firstDayOfYear = localeFirstDayOfYear;
++proto$1.firstDayOfWeek = localeFirstDayOfWeek;
++
++// Day of Week
++proto$1.weekdays       =        localeWeekdays;
++proto$1.weekdaysMin    =        localeWeekdaysMin;
++proto$1.weekdaysShort  =        localeWeekdaysShort;
++proto$1.weekdaysParse  =        localeWeekdaysParse;
++
++proto$1.weekdaysRegex       =        weekdaysRegex;
++proto$1.weekdaysShortRegex  =        weekdaysShortRegex;
++proto$1.weekdaysMinRegex    =        weekdaysMinRegex;
++
++// Hours
++proto$1.isPM = localeIsPM;
++proto$1.meridiem = localeMeridiem;
++
++function get$1 (format, index, field, setter) {
++    var locale = getLocale();
++    var utc = createUTC().set(setter, index);
++    return locale[field](utc, format);
++}
++
++function listMonthsImpl (format, index, field) {
++    if (isNumber(format)) {
++        index = format;
++        format = undefined;
++    }
++
++    format = format || '';
++
++    if (index != null) {
++        return get$1(format, index, field, 'month');
++    }
++
++    var i;
++    var out = [];
++    for (i = 0; i < 12; i++) {
++        out[i] = get$1(format, i, field, 'month');
++    }
++    return out;
++}
++
++// ()
++// (5)
++// (fmt, 5)
++// (fmt)
++// (true)
++// (true, 5)
++// (true, fmt, 5)
++// (true, fmt)
++function listWeekdaysImpl (localeSorted, format, index, field) {
++    if (typeof localeSorted === 'boolean') {
++        if (isNumber(format)) {
++            index = format;
++            format = undefined;
++        }
++
++        format = format || '';
++    } else {
++        format = localeSorted;
++        index = format;
++        localeSorted = false;
++
++        if (isNumber(format)) {
++            index = format;
++            format = undefined;
++        }
++
++        format = format || '';
++    }
++
++    var locale = getLocale(),
++        shift = localeSorted ? locale._week.dow : 0;
++
++    if (index != null) {
++        return get$1(format, (index + shift) % 7, field, 'day');
++    }
++
++    var i;
++    var out = [];
++    for (i = 0; i < 7; i++) {
++        out[i] = get$1(format, (i + shift) % 7, field, 'day');
++    }
++    return out;
++}
++
++function listMonths (format, index) {
++    return listMonthsImpl(format, index, 'months');
++}
++
++function listMonthsShort (format, index) {
++    return listMonthsImpl(format, index, 'monthsShort');
++}
++
++function listWeekdays (localeSorted, format, index) {
++    return listWeekdaysImpl(localeSorted, format, index, 'weekdays');
++}
++
++function listWeekdaysShort (localeSorted, format, index) {
++    return listWeekdaysImpl(localeSorted, format, index, 'weekdaysShort');
++}
++
++function listWeekdaysMin (localeSorted, format, index) {
++    return listWeekdaysImpl(localeSorted, format, index, 'weekdaysMin');
++}
++
++getSetGlobalLocale('en', {
++    dayOfMonthOrdinalParse: /\d{1,2}(th|st|nd|rd)/,
++    ordinal : function (number) {
++        var b = number % 10,
++            output = (toInt(number % 100 / 10) === 1) ? 'th' :
++            (b === 1) ? 'st' :
++            (b === 2) ? 'nd' :
++            (b === 3) ? 'rd' : 'th';
++        return number + output;
++    }
++});
++
++// Side effect imports
++hooks.lang = deprecate('moment.lang is deprecated. Use moment.locale instead.', getSetGlobalLocale);
++hooks.langData = deprecate('moment.langData is deprecated. Use moment.localeData instead.', getLocale);
++
++var mathAbs = Math.abs;
++
++function abs () {
++    var data           = this._data;
++
++    this._milliseconds = mathAbs(this._milliseconds);
++    this._days         = mathAbs(this._days);
++    this._months       = mathAbs(this._months);
++
++    data.milliseconds  = mathAbs(data.milliseconds);
++    data.seconds       = mathAbs(data.seconds);
++    data.minutes       = mathAbs(data.minutes);
++    data.hours         = mathAbs(data.hours);
++    data.months        = mathAbs(data.months);
++    data.years         = mathAbs(data.years);
++
++    return this;
++}
++
++function addSubtract$1 (duration, input, value, direction) {
++    var other = createDuration(input, value);
++
++    duration._milliseconds += direction * other._milliseconds;
++    duration._days         += direction * other._days;
++    duration._months       += direction * other._months;
++
++    return duration._bubble();
++}
++
++// supports only 2.0-style add(1, 's') or add(duration)
++function add$1 (input, value) {
++    return addSubtract$1(this, input, value, 1);
++}
++
++// supports only 2.0-style subtract(1, 's') or subtract(duration)
++function subtract$1 (input, value) {
++    return addSubtract$1(this, input, value, -1);
++}
++
++function absCeil (number) {
++    if (number < 0) {
++        return Math.floor(number);
++    } else {
++        return Math.ceil(number);
++    }
++}
++
++function bubble () {
++    var milliseconds = this._milliseconds;
++    var days         = this._days;
++    var months       = this._months;
++    var data         = this._data;
++    var seconds, minutes, hours, years, monthsFromDays;
++
++    // if we have a mix of positive and negative values, bubble down first
++    // check: https://github.com/moment/moment/issues/2166
++    if (!((milliseconds >= 0 && days >= 0 && months >= 0) ||
++            (milliseconds <= 0 && days <= 0 && months <= 0))) {
++        milliseconds += absCeil(monthsToDays(months) + days) * 864e5;
++        days = 0;
++        months = 0;
++    }
++
++    // The following code bubbles up values, see the tests for
++    // examples of what that means.
++    data.milliseconds = milliseconds % 1000;
++
++    seconds           = absFloor(milliseconds / 1000);
++    data.seconds      = seconds % 60;
++
++    minutes           = absFloor(seconds / 60);
++    data.minutes      = minutes % 60;
++
++    hours             = absFloor(minutes / 60);
++    data.hours        = hours % 24;
++
++    days += absFloor(hours / 24);
++
++    // convert days to months
++    monthsFromDays = absFloor(daysToMonths(days));
++    months += monthsFromDays;
++    days -= absCeil(monthsToDays(monthsFromDays));
++
++    // 12 months -> 1 year
++    years = absFloor(months / 12);
++    months %= 12;
++
++    data.days   = days;
++    data.months = months;
++    data.years  = years;
++
++    return this;
++}
++
++function daysToMonths (days) {
++    // 400 years have 146097 days (taking into account leap year rules)
++    // 400 years have 12 months === 4800
++    return days * 4800 / 146097;
++}
++
++function monthsToDays (months) {
++    // the reverse of daysToMonths
++    return months * 146097 / 4800;
++}
++
++function as (units) {
++    if (!this.isValid()) {
++        return NaN;
++    }
++    var days;
++    var months;
++    var milliseconds = this._milliseconds;
++
++    units = normalizeUnits(units);
++
++    if (units === 'month' || units === 'year') {
++        days   = this._days   + milliseconds / 864e5;
++        months = this._months + daysToMonths(days);
++        return units === 'month' ? months : months / 12;
++    } else {
++        // handle milliseconds separately because of floating point math errors (issue #1867)
++        days = this._days + Math.round(monthsToDays(this._months));
++        switch (units) {
++            case 'week'   : return days / 7     + milliseconds / 6048e5;
++            case 'day'    : return days         + milliseconds / 864e5;
++            case 'hour'   : return days * 24    + milliseconds / 36e5;
++            case 'minute' : return days * 1440  + milliseconds / 6e4;
++            case 'second' : return days * 86400 + milliseconds / 1000;
++            // Math.floor prevents floating point math errors here
++            case 'millisecond': return Math.floor(days * 864e5) + milliseconds;
++            default: throw new Error('Unknown unit ' + units);
++        }
++    }
++}
++
++// TODO: Use this.as('ms')?
++function valueOf$1 () {
++    if (!this.isValid()) {
++        return NaN;
++    }
++    return (
++        this._milliseconds +
++        this._days * 864e5 +
++        (this._months % 12) * 2592e6 +
++        toInt(this._months / 12) * 31536e6
++    );
++}
++
++function makeAs (alias) {
++    return function () {
++        return this.as(alias);
++    };
++}
++
++var asMilliseconds = makeAs('ms');
++var asSeconds      = makeAs('s');
++var asMinutes      = makeAs('m');
++var asHours        = makeAs('h');
++var asDays         = makeAs('d');
++var asWeeks        = makeAs('w');
++var asMonths       = makeAs('M');
++var asYears        = makeAs('y');
++
++function clone$1 () {
++    return createDuration(this);
++}
++
++function get$2 (units) {
++    units = normalizeUnits(units);
++    return this.isValid() ? this[units + 's']() : NaN;
++}
++
++function makeGetter(name) {
++    return function () {
++        return this.isValid() ? this._data[name] : NaN;
++    };
++}
++
++var milliseconds = makeGetter('milliseconds');
++var seconds      = makeGetter('seconds');
++var minutes      = makeGetter('minutes');
++var hours        = makeGetter('hours');
++var days         = makeGetter('days');
++var months       = makeGetter('months');
++var years        = makeGetter('years');
++
++function weeks () {
++    return absFloor(this.days() / 7);
++}
++
++var round = Math.round;
++var thresholds = {
++    ss: 44,         // a few seconds to seconds
++    s : 45,         // seconds to minute
++    m : 45,         // minutes to hour
++    h : 22,         // hours to day
++    d : 26,         // days to month
++    M : 11          // months to year
++};
++
++// helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
++function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) {
++    return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture);
++}
++
++function relativeTime$1 (posNegDuration, withoutSuffix, locale) {
++    var duration = createDuration(posNegDuration).abs();
++    var seconds  = round(duration.as('s'));
++    var minutes  = round(duration.as('m'));
++    var hours    = round(duration.as('h'));
++    var days     = round(duration.as('d'));
++    var months   = round(duration.as('M'));
++    var years    = round(duration.as('y'));
++
++    var a = seconds <= thresholds.ss && ['s', seconds]  ||
++            seconds < thresholds.s   && ['ss', seconds] ||
++            minutes <= 1             && ['m']           ||
++            minutes < thresholds.m   && ['mm', minutes] ||
++            hours   <= 1             && ['h']           ||
++            hours   < thresholds.h   && ['hh', hours]   ||
++            days    <= 1             && ['d']           ||
++            days    < thresholds.d   && ['dd', days]    ||
++            months  <= 1             && ['M']           ||
++            months  < thresholds.M   && ['MM', months]  ||
++            years   <= 1             && ['y']           || ['yy', years];
++
++    a[2] = withoutSuffix;
++    a[3] = +posNegDuration > 0;
++    a[4] = locale;
++    return substituteTimeAgo.apply(null, a);
++}
++
++// This function allows you to set the rounding function for relative time strings
++function getSetRelativeTimeRounding (roundingFunction) {
++    if (roundingFunction === undefined) {
++        return round;
++    }
++    if (typeof(roundingFunction) === 'function') {
++        round = roundingFunction;
++        return true;
++    }
++    return false;
++}
++
++// This function allows you to set a threshold for relative time strings
++function getSetRelativeTimeThreshold (threshold, limit) {
++    if (thresholds[threshold] === undefined) {
++        return false;
++    }
++    if (limit === undefined) {
++        return thresholds[threshold];
++    }
++    thresholds[threshold] = limit;
++    if (threshold === 's') {
++        thresholds.ss = limit - 1;
++    }
++    return true;
++}
++
++function humanize (withSuffix) {
++    if (!this.isValid()) {
++        return this.localeData().invalidDate();
++    }
++
++    var locale = this.localeData();
++    var output = relativeTime$1(this, !withSuffix, locale);
++
++    if (withSuffix) {
++        output = locale.pastFuture(+this, output);
++    }
++
++    return locale.postformat(output);
++}
++
++var abs$1 = Math.abs;
++
++function sign(x) {
++    return ((x > 0) - (x < 0)) || +x;
++}
++
++function toISOString$1() {
++    // for ISO strings we do not use the normal bubbling rules:
++    //  * milliseconds bubble up until they become hours
++    //  * days do not bubble at all
++    //  * months bubble up until they become years
++    // This is because there is no context-free conversion between hours and days
++    // (think of clock changes)
++    // and also not between days and months (28-31 days per month)
++    if (!this.isValid()) {
++        return this.localeData().invalidDate();
++    }
++
++    var seconds = abs$1(this._milliseconds) / 1000;
++    var days         = abs$1(this._days);
++    var months       = abs$1(this._months);
++    var minutes, hours, years;
++
++    // 3600 seconds -> 60 minutes -> 1 hour
++    minutes           = absFloor(seconds / 60);
++    hours             = absFloor(minutes / 60);
++    seconds %= 60;
++    minutes %= 60;
++
++    // 12 months -> 1 year
++    years  = absFloor(months / 12);
++    months %= 12;
++
++
++    // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js
++    var Y = years;
++    var M = months;
++    var D = days;
++    var h = hours;
++    var m = minutes;
++    var s = seconds ? seconds.toFixed(3).replace(/\.?0+$/, '') : '';
++    var total = this.asSeconds();
++
++    if (!total) {
++        // this is the same as C#'s (Noda) and python (isodate)...
++        // but not other JS (goog.date)
++        return 'P0D';
++    }
++
++    var totalSign = total < 0 ? '-' : '';
++    var ymSign = sign(this._months) !== sign(total) ? '-' : '';
++    var daysSign = sign(this._days) !== sign(total) ? '-' : '';
++    var hmsSign = sign(this._milliseconds) !== sign(total) ? '-' : '';
++
++    return totalSign + 'P' +
++        (Y ? ymSign + Y + 'Y' : '') +
++        (M ? ymSign + M + 'M' : '') +
++        (D ? daysSign + D + 'D' : '') +
++        ((h || m || s) ? 'T' : '') +
++        (h ? hmsSign + h + 'H' : '') +
++        (m ? hmsSign + m + 'M' : '') +
++        (s ? hmsSign + s + 'S' : '');
++}
++
++var proto$2 = Duration.prototype;
++
++proto$2.isValid        = isValid$1;
++proto$2.abs            = abs;
++proto$2.add            = add$1;
++proto$2.subtract       = subtract$1;
++proto$2.as             = as;
++proto$2.asMilliseconds = asMilliseconds;
++proto$2.asSeconds      = asSeconds;
++proto$2.asMinutes      = asMinutes;
++proto$2.asHours        = asHours;
++proto$2.asDays         = asDays;
++proto$2.asWeeks        = asWeeks;
++proto$2.asMonths       = asMonths;
++proto$2.asYears        = asYears;
++proto$2.valueOf        = valueOf$1;
++proto$2._bubble        = bubble;
++proto$2.clone          = clone$1;
++proto$2.get            = get$2;
++proto$2.milliseconds   = milliseconds;
++proto$2.seconds        = seconds;
++proto$2.minutes        = minutes;
++proto$2.hours          = hours;
++proto$2.days           = days;
++proto$2.weeks          = weeks;
++proto$2.months         = months;
++proto$2.years          = years;
++proto$2.humanize       = humanize;
++proto$2.toISOString    = toISOString$1;
++proto$2.toString       = toISOString$1;
++proto$2.toJSON         = toISOString$1;
++proto$2.locale         = locale;
++proto$2.localeData     = localeData;
++
++// Deprecations
++proto$2.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', toISOString$1);
++proto$2.lang = lang;
++
++// Side effect imports
++
++// FORMATTING
++
++addFormatToken('X', 0, 0, 'unix');
++addFormatToken('x', 0, 0, 'valueOf');
++
++// PARSING
++
++addRegexToken('x', matchSigned);
++addRegexToken('X', matchTimestamp);
++addParseToken('X', function (input, array, config) {
++    config._d = new Date(parseFloat(input, 10) * 1000);
++});
++addParseToken('x', function (input, array, config) {
++    config._d = new Date(toInt(input));
++});
++
++// Side effect imports
++
++
++hooks.version = '2.20.1';
++
++setHookCallback(createLocal);
++
++hooks.fn                    = proto;
++hooks.min                   = min;
++hooks.max                   = max;
++hooks.now                   = now;
++hooks.utc                   = createUTC;
++hooks.unix                  = createUnix;
++hooks.months                = listMonths;
++hooks.isDate                = isDate;
++hooks.locale                = getSetGlobalLocale;
++hooks.invalid               = createInvalid;
++hooks.duration              = createDuration;
++hooks.isMoment              = isMoment;
++hooks.weekdays              = listWeekdays;
++hooks.parseZone             = createInZone;
++hooks.localeData            = getLocale;
++hooks.isDuration            = isDuration;
++hooks.monthsShort           = listMonthsShort;
++hooks.weekdaysMin           = listWeekdaysMin;
++hooks.defineLocale          = defineLocale;
++hooks.updateLocale          = updateLocale;
++hooks.locales               = listLocales;
++hooks.weekdaysShort         = listWeekdaysShort;
++hooks.normalizeUnits        = normalizeUnits;
++hooks.relativeTimeRounding  = getSetRelativeTimeRounding;
++hooks.relativeTimeThreshold = getSetRelativeTimeThreshold;
++hooks.calendarFormat        = getCalendarFormat;
++hooks.prototype             = proto;
++
++// currently HTML5 input type only supports 24-hour formats
++hooks.HTML5_FMT = {
++    DATETIME_LOCAL: 'YYYY-MM-DDTHH:mm',             // <input type="datetime-local" />
++    DATETIME_LOCAL_SECONDS: 'YYYY-MM-DDTHH:mm:ss',  // <input type="datetime-local" step="1" />
++    DATETIME_LOCAL_MS: 'YYYY-MM-DDTHH:mm:ss.SSS',   // <input type="datetime-local" step="0.001" />
++    DATE: 'YYYY-MM-DD',                             // <input type="date" />
++    TIME: 'HH:mm',                                  // <input type="time" />
++    TIME_SECONDS: 'HH:mm:ss',                       // <input type="time" step="1" />
++    TIME_MS: 'HH:mm:ss.SSS',                        // <input type="time" step="0.001" />
++    WEEK: 'YYYY-[W]WW',                             // <input type="week" />
++    MONTH: 'YYYY-MM'                                // <input type="month" />
++};
++
++return hooks;
++
++})));
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7e45a6a597d7e23254d6ddf2d6fd9d596df774d4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,212 @@@
++// PatternFly Namespace
++var PatternFly = PatternFly || {};
++
++// Util: PatternFly Sidebar 
++// Set height of sidebar-pf to height of document minus height of navbar-pf if not mobile
++(function($) {
++  sidebar = function() {
++    var documentHeight = 0;
++    var navbarpfHeight = 0;
++    var colHeight = 0;
++    if ( $('.navbar-pf .navbar-toggle').is(':hidden') ) {
++      documentHeight = $(document).height();
++      navbarpfHeight = $('.navbar-pf').outerHeight();
++      colHeight = documentHeight - navbarpfHeight;
++    }
++    $('.sidebar-pf').parent('.row').children('[class*="col-"]').css({ "min-height":colHeight});
++  }
++  $(document).ready(function() {
++    // Call sidebar() on ready if .sidebar-pf exists and .datatable does not exist
++    if ($('.sidebar-pf').length > 0 && $('.datatable').length == 0) {
++      sidebar();
++    }
++  });
++  $(window).resize(function() {
++    // Call sidebar() on resize if .sidebar-pf exists
++    if ($('.sidebar-pf').length > 0) {
++      sidebar();
++    }
++  });
++})(jQuery);
++
++// Util: PatternFly Popovers
++// Add data-close="true" to insert close X icon
++(function($) {
++  PatternFly.popovers = function( selector ) {
++    var allpopovers = $(selector);
++    
++    // Initialize
++    allpopovers.popover();
++    
++    // Add close icons
++    allpopovers.filter('[data-close=true]').each(function(index, element) {
++      var $this = $(element),
++        title = $this.attr('data-original-title') + '<button type="button" class="close" aria-hidden="true"><span class="pficon pficon-close"></span></button>';
++
++      $this.attr('data-original-title', title);
++    });
++    
++    // Bind Close Icon to Toggle Display
++    allpopovers.on('click', function(e) {
++      var $this = $(this);
++        $title = $this.next('.popover').find('.popover-title');
++      
++      // Only if data-close is true add class "x" to title for right padding
++      $title.find('.close').parent('.popover-title').addClass('closable');
++      
++      // Bind x icon to close popover
++      $title.find('.close').on('click', function() {
++        $this.popover('toggle');
++      });
++      
++      // Prevent href="#" page scroll to top
++      e.preventDefault();
++    });
++  };
++})(jQuery);
++
++
++// Util: DataTables Settings
++(function($) {
++  if ($.fn.dataTableExt) {
++    /* Set the defaults for DataTables initialisation */
++    $.extend( true, $.fn.dataTable.defaults, {
++      "bDestroy": true,
++      "bAutoWidth": false,
++      "iDisplayLength": 20,
++      "sDom": 
++        "<'dataTables_header' f i r >" + 
++        "<'table-responsive'  t >" + 
++        "<'dataTables_footer' p >",
++      "oLanguage": {
++        "sInfo": "Showing <b>_START_</b> to <b>_END_</b> of <b>_TOTAL_</b> Items",
++        "sInfoFiltered" : "(of <b>_MAX_</b>)",
++        "sInfoEmpty" : "Showing <b>0</b> Results",
++        "sZeroRecords": 
++          "<p>Suggestions</p>" + 
++          "<ul>" + 
++            "<li>Check the syntax of the search term.</li>" +
++            "<li>Check that the correct menu option is chosen (token ID vs. user ID).</li>" +
++            "<li>Use wildcards (* to match zero or more characters or ? to match a single character).</li>" +
++            "<li>Clear the search field, then click Search to return to the 20 most recent records.</li>" +
++          "</ul>",
++        "sSearch": ""
++      },
++      "sPaginationType": "bootstrap_input"
++    });
++
++    /* Default class modification */
++    $.extend( $.fn.dataTableExt.oStdClasses, {
++      "sWrapper": "dataTables_wrapper"
++    });
++
++    /* API method to get paging information */
++    $.fn.dataTableExt.oApi.fnPagingInfo = function ( oSettings ) {
++      return {
++        "iStart":         oSettings._iDisplayStart,
++        "iEnd":           oSettings.fnDisplayEnd(),
++        "iLength":        oSettings._iDisplayLength,
++        "iTotal":         oSettings.fnRecordsTotal(),
++        "iFilteredTotal": oSettings.fnRecordsDisplay(),
++        "iPage":          oSettings._iDisplayLength === -1 ?
++          0 : Math.ceil( oSettings._iDisplayStart / oSettings._iDisplayLength ),
++        "iTotalPages":    oSettings._iDisplayLength === -1 ?
++          0 : Math.ceil( oSettings.fnRecordsDisplay() / oSettings._iDisplayLength )
++      };
++    };
++
++    /* Combination of Bootstrap + Input Text style pagination control */
++    $.extend( $.fn.dataTableExt.oPagination, {
++      "bootstrap_input": {
++        "fnInit": function( oSettings, nPaging, fnDraw ) {
++          var oLang = oSettings.oLanguage.oPaginate;
++          var fnClickHandler = function ( e ) {
++            e.preventDefault();
++            if ( oSettings.oApi._fnPageChange(oSettings, e.data.action) ) {
++              fnDraw( oSettings );
++            }
++          };
++
++          $(nPaging).append(
++            '<ul class="pagination">'+
++              '<li class="first disabled"><span class="i fa fa-angle-double-left"></span></li>' +
++              '<li class="prev disabled"><span class="i fa fa-angle-left"></span></li>' +
++            '</ul>' + 
++            '<div class="pagination-input">' + 
++              '<input type="text" class="paginate_input">' + 
++              '<span class="paginate_of">of <b>3</b></span>' + 
++            '</div>' + 
++            '<ul class="pagination">'+
++              '<li class="next disabled"><span class="i fa fa-angle-right"></span></li>' +
++              '<li class="last disabled"><span class="i fa fa-angle-double-right"></span></li>' +
++            '</ul>'
++          );
++          
++          var els = $('li', nPaging);
++          $(els[0]).bind( 'click.DT', { action: "first" }, fnClickHandler );
++          $(els[1]).bind( 'click.DT', { action: "previous" }, fnClickHandler );
++          $(els[2]).bind( 'click.DT', { action: "next" }, fnClickHandler );
++          $(els[3]).bind( 'click.DT', { action: "last" }, fnClickHandler );
++          
++          var nInput = $('input', nPaging);
++          $(nInput).keyup( function (e) {
++              if ( e.which == 38 || e.which == 39 ) {
++                this.value++;
++              }
++              else if ( (e.which == 37 || e.which == 40) && this.value > 1 ) {
++                this.value--;
++              }
++                
++              if ( this.value == "" || this.value.match(/[^0-9]/) ) {
++                /* Nothing entered or non-numeric character */
++                return;
++              }
++                
++              var iNewStart = oSettings._iDisplayLength * (this.value - 1);
++              if ( iNewStart > oSettings.fnRecordsDisplay() ) {
++                /* Display overrun */
++                oSettings._iDisplayStart = (Math.ceil((oSettings.fnRecordsDisplay()-1) /
++                  oSettings._iDisplayLength)-1) * oSettings._iDisplayLength;
++                fnDraw( oSettings );
++                return;
++              }
++                
++              oSettings._iDisplayStart = iNewStart;
++              fnDraw( oSettings );
++          });
++        },
++
++        "fnUpdate": function ( oSettings, fnDraw ) {
++          var oPaging = oSettings.oInstance.fnPagingInfo(),
++            an = oSettings.aanFeatures.p,
++            i,
++            ien,
++            iPages = Math.ceil((oSettings.fnRecordsDisplay()) / oSettings._iDisplayLength),
++            iCurrentPage = Math.ceil(oSettings._iDisplayStart / oSettings._iDisplayLength) + 1;
++
++          for ( i=0, ien=an.length ; i<ien ; i++ ) {
++            $('.paginate_input').val(iCurrentPage);
++            $('.paginate_of b').html(iPages);
++            
++            // Add / remove disabled classes from the static elements
++            if ( oPaging.iPage === 0 ) {
++              $('li.first', an[i]).addClass('disabled');
++              $('li.prev', an[i]).addClass('disabled');
++            } else {
++              $('li.first', an[i]).removeClass('disabled');
++              $('li.prev', an[i]).removeClass('disabled');
++            }
++
++            if ( oPaging.iPage === oPaging.iTotalPages-1 || oPaging.iTotalPages === 0 ) {
++              $('li.next', an[i]).addClass('disabled');
++              $('li.last', an[i]).addClass('disabled');
++            } else {
++              $('li.next', an[i]).removeClass('disabled');
++              $('li.last', an[i]).removeClass('disabled');
++            }
++          }
++        }
++      }
++    });
++  }
++})(jQuery);
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3e20471042ad87626526c78d141eb2e1a2349c03
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,247 @@@
++From 872c98cd1b5059a4b76e3707d92f1445663db83d Mon Sep 17 00:00:00 2001
++From: William Brown <firstyear@redhat.com>
++Date: Thu, 18 Jan 2018 11:27:58 +1000
++Subject: [PATCH] Ticket bz1525628 - invalid password migration causes unauth
++ bind
++
++Bug Description:  Slapi_ct_memcmp expects both inputs to be
++at LEAST size n. If they are not, we only compared UP to n.
++
++Invalid migrations of passwords (IE {CRYPT}XX) would create
++a pw which is just salt and no hash. ct_memcmp would then
++only verify the salt bits and would allow the authentication.
++
++This relies on an administrative mistake both of allowing
++password migration (nsslapd-allow-hashed-passwords) and then
++subsequently migrating an INVALID password to the server.
++
++Fix Description:  slapi_ct_memcmp now access n1, n2 size
++and will FAIL if they are not the same, but will still compare
++n bytes, where n is the "longest" memory, to the first byte
++of the other to prevent length disclosure of the shorter
++value (generally the mis-migrated password)
++
++https://bugzilla.redhat.com/show_bug.cgi?id=1525628
++
++Author: wibrown
++
++Review by: ???
++---
++ .../bz1525628_ct_memcmp_invalid_hash_test.py       | 56 ++++++++++++++++++++++
++ ldap/servers/plugins/pwdstorage/clear_pwd.c        |  4 +-
++ ldap/servers/plugins/pwdstorage/crypt_pwd.c        |  4 +-
++ ldap/servers/plugins/pwdstorage/md5_pwd.c          |  4 +-
++ ldap/servers/plugins/pwdstorage/sha_pwd.c          |  4 +-
++ ldap/servers/plugins/pwdstorage/smd5_pwd.c         |  2 +-
++ ldap/servers/slapd/ch_malloc.c                     | 36 ++++++++++++--
++ ldap/servers/slapd/slapi-plugin.h                  |  2 +-
++ 8 files changed, 97 insertions(+), 15 deletions(-)
++ create mode 100644 dirsrvtests/tests/suites/password/bz1525628_ct_memcmp_invalid_hash_test.py
++
++--- /dev/null
+++++ b/dirsrvtests/tests/suites/password/bz1525628_ct_memcmp_invalid_hash_test.py
++@@ -0,0 +1,56 @@
+++# --- BEGIN COPYRIGHT BLOCK ---
+++# Copyright (C) 2018 Red Hat, Inc.
+++# All rights reserved.
+++#
+++# License: GPL (version 3 or any later version).
+++# See LICENSE for details.
+++# --- END COPYRIGHT BLOCK ---
+++#
+++
+++import ldap
+++import pytest
+++import logging
+++from lib389.topologies import topology_st
+++from lib389._constants import PASSWORD, DEFAULT_SUFFIX
+++
+++from lib389.idm.user import UserAccounts, TEST_USER_PROPERTIES
+++
+++logging.getLogger(__name__).setLevel(logging.DEBUG)
+++log = logging.getLogger(__name__)
+++
+++def test_invalid_hash_fails(topology_st):
+++    """When given a malformed hash from userpassword migration
+++    slapi_ct_memcmp would check only to the length of the shorter
+++    field. This affects some values where it would ONLY verify
+++    the salt is valid, and thus would allow any password to bind.
+++
+++    :id: 8131c029-7147-47db-8d03-ec5db2a01cfb
+++    :setup: Standalone Instance
+++    :steps:
+++        1. Create a user
+++        2. Add an invalid password hash (truncated)
+++        3. Attempt to bind
+++    :expectedresults:
+++        1. User is added
+++        2. Invalid pw hash is added
+++        3. Bind fails
+++    """
+++    log.info("Running invalid hash test")
+++
+++    # Allow setting raw password hashes for migration.
+++    topology_st.standalone.config.set('nsslapd-allow-hashed-passwords', 'on')
+++
+++    users = UserAccounts(topology_st.standalone, DEFAULT_SUFFIX)
+++    user = users.create(properties=TEST_USER_PROPERTIES)
+++    user.set('userPassword', '{CRYPT}XX')
+++
+++    # Attempt to bind. This should fail.
+++    with pytest.raises(ldap.INVALID_CREDENTIALS):
+++        user.bind(PASSWORD)
+++    with pytest.raises(ldap.INVALID_CREDENTIALS):
+++        user.bind('XX')
+++    with pytest.raises(ldap.INVALID_CREDENTIALS):
+++        user.bind('{CRYPT}XX')
+++
+++    log.info("PASSED")
+++
++--- a/ldap/servers/plugins/pwdstorage/clear_pwd.c
+++++ b/ldap/servers/plugins/pwdstorage/clear_pwd.c
++@@ -39,7 +39,7 @@ clear_pw_cmp(const char *userpwd, const
++          * However, even if the first part of userpw matches dbpwd, but len !=, we
++          * have already failed anyawy. This prevents substring matching.
++          */
++-        if (slapi_ct_memcmp(userpwd, dbpwd, len_dbp) != 0) {
+++        if (slapi_ct_memcmp(userpwd, dbpwd, len_user, len_dbp) != 0) {
++             result = 1;
++         }
++     } else {
++@@ -51,7 +51,7 @@ clear_pw_cmp(const char *userpwd, const
++          * dbpwd to itself. We have already got result == 1 if we are here, so we are
++          * just trying to take up time!
++          */
++-        if (slapi_ct_memcmp(dbpwd, dbpwd, len_dbp)) {
+++        if (slapi_ct_memcmp(dbpwd, dbpwd, len_dbp, len_dbp)) {
++             /* Do nothing, we have the if to fix a coverity check. */
++         }
++     }
++--- a/ldap/servers/plugins/pwdstorage/crypt_pwd.c
+++++ b/ldap/servers/plugins/pwdstorage/crypt_pwd.c
++@@ -42,7 +42,7 @@ static unsigned char itoa64[] = /* 0 ...
++ int
++ crypt_pw_cmp(const char *userpwd, const char *dbpwd)
++ {
++-    int rc;
+++    int32_t rc;
++     char *cp;
++     struct crypt_data data;
++     data.initialized = 0;
++@@ -50,7 +50,7 @@ crypt_pw_cmp(const char *userpwd, const
++     /* we use salt (first 2 chars) of encoded password in call to crypt_r() */
++     cp = crypt_r(userpwd, dbpwd, &data);
++     if (cp) {
++-        rc = slapi_ct_memcmp(dbpwd, cp, strlen(dbpwd));
+++        rc = slapi_ct_memcmp(dbpwd, cp, strlen(dbpwd), strlen(cp));
++     } else {
++         rc = -1;
++     }
++--- a/ldap/servers/plugins/pwdstorage/md5_pwd.c
+++++ b/ldap/servers/plugins/pwdstorage/md5_pwd.c
++@@ -30,7 +30,7 @@
++ int
++ md5_pw_cmp(const char *userpwd, const char *dbpwd)
++ {
++-    int rc = -1;
+++    int32_t rc = -1;
++     char *bver;
++     PK11Context *ctx = NULL;
++     unsigned int outLen;
++@@ -57,7 +57,7 @@ md5_pw_cmp(const char *userpwd, const ch
++     bver = NSSBase64_EncodeItem(NULL, (char *)b2a_out, sizeof b2a_out, &binary_item);
++     /* bver points to b2a_out upon success */
++     if (bver) {
++-        rc = slapi_ct_memcmp(bver, dbpwd, strlen(dbpwd));
+++        rc = slapi_ct_memcmp(bver, dbpwd, strlen(dbpwd), strlen(bver));
++     } else {
++         slapi_log_err(SLAPI_LOG_PLUGIN, MD5_SUBSYSTEM_NAME,
++                       "Could not base64 encode hashed value for password compare");
++--- a/ldap/servers/plugins/pwdstorage/sha_pwd.c
+++++ b/ldap/servers/plugins/pwdstorage/sha_pwd.c
++@@ -122,9 +122,9 @@ sha_pw_cmp(const char *userpwd, const ch
++ 
++     /* the proof is in the comparison... */
++     if (hash_len >= shaLen) {
++-        result = slapi_ct_memcmp(userhash, dbhash, shaLen);
+++        result = slapi_ct_memcmp(userhash, dbhash, shaLen, shaLen);
++     } else {
++-        result = slapi_ct_memcmp(userhash, dbhash + OLD_SALT_LENGTH, hash_len - OLD_SALT_LENGTH);
+++        result = slapi_ct_memcmp(userhash, dbhash + OLD_SALT_LENGTH, hash_len - OLD_SALT_LENGTH, hash_len - OLD_SALT_LENGTH);
++     }
++ 
++ loser:
++--- a/ldap/servers/plugins/pwdstorage/smd5_pwd.c
+++++ b/ldap/servers/plugins/pwdstorage/smd5_pwd.c
++@@ -82,7 +82,7 @@ smd5_pw_cmp(const char *userpwd, const c
++     PK11_DestroyContext(ctx, 1);
++ 
++     /* Compare everything up to the salt. */
++-    rc = slapi_ct_memcmp(userhash, dbhash, MD5_LENGTH);
+++    rc = slapi_ct_memcmp(userhash, dbhash, MD5_LENGTH, MD5_LENGTH);
++ 
++ loser:
++     if (dbhash && dbhash != quick_dbhash)
++--- a/ldap/servers/slapd/ch_malloc.c
+++++ b/ldap/servers/slapd/ch_malloc.c
++@@ -331,8 +331,8 @@ slapi_ch_smprintf(const char *fmt, ...)
++ 
++ /* Constant time memcmp. Does not shortcircuit on failure! */
++ /* This relies on p1 and p2 both being size at least n! */
++-int
++-slapi_ct_memcmp(const void *p1, const void *p2, size_t n)
+++int32_t
+++slapi_ct_memcmp(const void *p1, const void *p2, size_t n1, size_t n2)
++ {
++     int result = 0;
++     const unsigned char *_p1 = (const unsigned char *)p1;
++@@ -342,9 +342,35 @@ slapi_ct_memcmp(const void *p1, const vo
++         return 2;
++     }
++ 
++-    for (size_t i = 0; i < n; i++) {
++-        if (_p1[i] ^ _p2[i]) {
++-            result = 1;
+++    if (n1 == n2) {
+++        for (size_t i = 0; i < n1; i++) {
+++            if (_p1[i] ^ _p2[i]) {
+++                result = 1;
+++            }
+++        }
+++    } else {
+++        const unsigned char *_pa;
+++        const unsigned char *_pb;
+++        size_t nl;
+++        if (n2 > n1) {
+++            _pa = _p2;
+++            _pb = _p2;
+++            nl = n2;
+++        } else {
+++            _pa = _p1;
+++            _pb = _p1;
+++            nl = n1;
+++        }
+++        /* We already fail as n1 != n2 */
+++        result = 3;
+++        for (size_t i = 0; i < nl; i++) {
+++            if (_pa[i] ^ _pb[i]) {
+++                /*
+++                 * If we don't mutate result here, dead code elimination
+++                 * we remove for loop.
+++                 */
+++                result = 4;
+++            }
++         }
++     }
++     return result;
++--- a/ldap/servers/slapd/slapi-plugin.h
+++++ b/ldap/servers/slapd/slapi-plugin.h
++@@ -5781,7 +5781,7 @@ char *slapi_ch_smprintf(const char *fmt,
++  * \param n length in bytes of the content of p1 AND p2.
++  * \return 0 on match. 1 on non-match. 2 on presence of NULL pointer in p1 or p2.
++  */
++-int slapi_ct_memcmp(const void *p1, const void *p2, size_t n);
+++int32_t slapi_ct_memcmp(const void *p1, const void *p2, size_t n1, size_t n2);
++ 
++ /*
++  * syntax plugin routines
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5103236b7942439630f339c35a120f576879ec97
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++diff --git a/ldap/servers/plugins/sync/sync_persist.c b/ldap/servers/plugins/sync/sync_persist.c
++index 598c6868d..f7f69bd3d 100644
++--- a/ldap/servers/plugins/sync/sync_persist.c
+++++ b/ldap/servers/plugins/sync/sync_persist.c
++@@ -9,7 +9,7 @@
++ #include <config.h>
++ #endif
++ 
++-#include <nspr4/prlog.h>
+++#include <prlog.h>
++ #include <bits/stdint-intn.h>
++ 
++ #include "sync.h"
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fef40b9d1beaa3384ee6e20b3c3ab76e9bb00dab
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,57 @@@
++--- a/ldap/servers/slapd/ldaputil.c
+++++ b/ldap/servers/slapd/ldaputil.c
++@@ -827,10 +827,14 @@ ldaputil_get_saslpath()
++         if (PR_SUCCESS != PR_Access(saslpath, PR_ACCESS_EXISTS)) {
++ #ifdef CPU_arm
++             /* the 64-bit ARMv8 architecture. */
++-            saslpath = "/usr/lib/aarch64-linux-gnu";
+++            saslpath = "/usr/lib/aarch64-linux-gnu/sasl2";
+++#elif defined(CPU_powerpc64le)
+++            saslpath = "/usr/lib/powerpc64le-linux-gnu/sasl2";
+++#elif defined(CPU_s390x)
+++            saslpath = "/usr/lib/s390x-linux-gnu/sasl2";
++ #else
++             /* Try x86_64 gnu triplet */
++-            saslpath = "/usr/lib/x86_64-linux-gnu";
+++            saslpath = "/usr/lib/x86_64-linux-gnu/sasl2";
++ #endif
++         }
++ #else
++@@ -838,14 +842,14 @@ ldaputil_get_saslpath()
++         if (PR_SUCCESS != PR_Access(saslpath, PR_ACCESS_EXISTS)) {
++ #ifdef CPU_arm
++             /* the latest 32 bit ARM architecture using the hard-float version of EABI. */
++-            saslpath = "/usr/lib/arm-linux-gnueabihf";
+++            saslpath = "/usr/lib/arm-linux-gnueabihf/sasl2";
++             if (PR_SUCCESS != PR_Access(saslpath, PR_ACCESS_EXISTS)) {
++                 /* the 32 bit ARM architecture of EABI. */
++-                saslpath = "/usr/lib/arm-linux-gnueabi";
+++                saslpath = "/usr/lib/arm-linux-gnueabi/sasl2";
++             }
++ #else
++             /* Try i386 gnu triplet */
++-            saslpath = "/usr/lib/i386-linux-gnu";
+++            saslpath = "/usr/lib/i386-linux-gnu/sasl2";
++ #endif
++         }
++ #endif
++--- a/configure.ac
+++++ b/configure.ac
++@@ -655,7 +655,8 @@ case $host in
++       arm-*-linux*)
++         AC_DEFINE([CPU_arm], [], [cpu type arm])
++         ;;
++-      ppc64le-*-linux*)
+++      powerpc64le-*-linux*)
+++        AC_DEFINE([CPU_powerpc64le], [], [cpu type powerpc64le])
++         ;;
++       ppc64-*-linux*)
++         ;;
++@@ -664,6 +665,7 @@ case $host in
++       s390-*-linux*)
++         ;;
++       s390x-*-linux*)
+++        AC_DEFINE([CPU_s390x], [], [cpu type s390x])
++         ;;
++     esac
++     # some programs use the native thread library directly
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7bf99089bca9b889c39325a05c830c2adef98013
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3 @@@
++fix-saslpath.diff
++CVE-2017-15135.patch
++fix-prlog-include.diff
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4483e2be7995ddff010206ec7eb57bc9360c0bac
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++usr/lib/python3/dist-packages/lib389-*
++usr/lib/python3/dist-packages/lib389/
++usr/sbin/dsconf
++usr/sbin/dscreate
++usr/sbin/dsctl
++usr/sbin/dsidm
++usr/share/man/man8/dsconf.8
++usr/share/man/man8/dscreate.8
++usr/share/man/man8/dsctl.8
++usr/share/man/man8/dsidm.8
diff --cc debian/rules
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8a1a1fafa81f4f3d633ab80da83d7ed5b1d77ceb
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,76 @@@
++#!/usr/bin/make -f
++# -*- makefile -*-
++
++export DEB_BUILD_MAINT_OPTIONS = hardening=+pie
++
++
++ifneq (,$(filter $(DEB_HOST_ARCH), armel m68k mips mipsel powerpc powerpcspe sh4))
++  export DEB_LDFLAGS_MAINT_APPEND=-latomic
++endif
++
++REALFILE = \
++      bin/ds-logpipe.py \
++      bin/logconv.pl \
++      share/man/man1/ds-logpipe.py.1 \
++      share/man/man1/logconv.pl.1 \
++
++%:
++      dh $@ --with python3 --builddir build/
++
++override_dh_auto_clean:
++      dh_auto_clean
++      rm -f aclocal.m4 config.* ltmain.sh m4/libtool.m4 m4/lt*.m4
++      rm -f ldap/servers/snmp/ldap-agent.conf
++      rm -rf src/lib389/build src/lib389/lib389.egg-info
++      find src/lib389/ -name '__pycache__' -exec rm -rf '{}' ';'
++      rm -f src/lib389/man/*.8
++
++override_dh_auto_configure:
++      dh_auto_configure -- \
++              --with-openldap \
++              --with-systemd \
++              --with-systemdsystemunitdir=/lib/systemd/system \
++              --with-systemdsystemconfdir=/etc/systemd/system \
++              --with-systemdgroupname=dirsrv.target \
++              --with-tmpfiles-d=/etc/tmpfiles.d \
++              --enable-autobind \
++              --enable-cmocka \
++              --enable-icu \
++              --enable-perl
++
++override_dh_auto_build:
++      (cd src/lib389 && python3 setup.py build)
++      dh_auto_build
++
++override_dh_auto_install:
++      (cd src/lib389 && python3 setup.py install --install-layout=deb --root ../../debian/tmp)
++
++      dh_auto_install --max-parallel=1
++
++override_dh_install:
++      # lets do the renaming here afterall, instead of in 389-ds-base.install
++      for file in $(REALFILE); do mv -f $(CURDIR)/debian/tmp/usr/$$file \
++              $(CURDIR)/debian/tmp/usr/`echo $$file | \
++              sed -s 's/\.pl//;s/\.py//'`; \
++              done
++      # purge .la files
++      find $(CURDIR)/debian/tmp -name "*.la" -type f -exec rm -f "{}" \;
++
++      mkdir -p $(CURDIR)/debian/tmp/etc/systemd/system/dirsrv.target.wants
++
++      # fix the manpage section, argparse-manpage hardcodes it as 1
++      sed -i  "1s/\"1\"/\"8\"/" debian/tmp/usr/share/man/man8/dsconf.8
++      sed -i  "1s/\"1\"/\"8\"/" debian/tmp/usr/share/man/man8/dscreate.8
++      sed -i  "1s/\"1\"/\"8\"/" debian/tmp/usr/share/man/man8/dsctl.8
++      sed -i  "1s/\"1\"/\"8\"/" debian/tmp/usr/share/man/man8/dsidm.8
++
++      dh_install
++
++override_dh_missing:
++      dh_missing --fail-missing
++
++override_dh_installsystemd:
++      dh_installsystemd -p389-ds-base --no-enable dirsrv-snmp.service
++
++override_dh_shlibdeps:
++      dh_shlibdeps -l"debian/389-ds-base/usr/lib/$(DEB_HOST_MULTIARCH)/dirsrv" -a
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..163aaf8d82b6c54f23c45f32895dbdfdcc27b047
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++3.0 (quilt)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f0e3ffc5cd8d4aa5103bef4efd473d5072f3deca
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2 @@@
++# it just has long lines
++389-ds-base source: source-is-missing src/cockpit/389-console/cockpit_dist/index.js line length is 312 characters (>256)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b0e8f8e81a9093e7f14e5d463fa0e4cfe7a08d84
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++Tests: setup
++Depends:
++ 389-ds-base,
++Restrictions: needs-root
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0ffa36648b1777b31e8a6bf64e5573ae5ccf9b65
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++#!/bin/sh
++
++# hack for lxc
++IP=`ip route get 1.1.1.1 | sed -n -e's/.*src //; s/ .*//; p; q'`
++echo "IP address is $IP"
++
++HOSTNAME=`cat /etc/hosts| grep '127.0.1.1' | awk '{print $NF; exit}'`
++echo "Hostname was: $HOSTNAME"
++
++if [ -z $HOSTNAME ]; then
++    HOSTNAME=autopkgtest
++    hostname $HOSTNAME
++    echo $HOSTNAME > /etc/hostname
++fi
++
++echo "$IP $HOSTNAME.debci $HOSTNAME" >> /etc/hosts
++
++echo "/etc/hosts now has:"
++cat /etc/hosts
++
++cat << EOF > /tmp/debci.inf
++[general]
++full_machine_name = $HOSTNAME.debci
++strict_host_checking = False
++[slapd]
++group = dirsrv
++instance_name = debci
++port = 1389
++root_dn = cn=Directory Manager
++root_password = Secret123
++user = dirsrv
++[backend-userroot]
++suffix = dc=example,dc=com
++EOF
++
++/usr/sbin/dscreate from-file /tmp/debci.inf 2>&1
diff --cc debian/watch
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..674923d4740212d4d46a34556d09829ec3484db9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3 @@@
++#git=https://github.com/389ds/389-ds-base
++version=3
++https://releases.pagure.org/389-ds-base/389-ds-base-(1\.4\.4\..*).tar.bz2