diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 15:26:01 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 15:26:01 +0000 |
commit | ef03469fec14f1f0358b690934fc173d744f4e7d (patch) | |
tree | 8be439d7b2f1d7c8283b745919b9e66481a950e7 | |
parent | Adding upstream version 5.6.0. (diff) | |
download | knot-resolver-ef03469fec14f1f0358b690934fc173d744f4e7d.tar.xz knot-resolver-ef03469fec14f1f0358b690934fc173d744f4e7d.zip |
Adding debian version 5.6.0-1.debian/5.6.0-1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
129 files changed, 75087 insertions, 0 deletions
diff --git a/debian/NEWS b/debian/NEWS new file mode 100644 index 0000000..5df8f90 --- /dev/null +++ b/debian/NEWS @@ -0,0 +1,40 @@ +knot-resolver (5.1.2-1) unstable; urgency=medium + + Up to Knot Resolver 4.x, network interface bindings and service + start were done by default via systemd sockets. These systemd sockets + are no longer supported, and upgrading from a 3.x version (as in Debian + Buster) requires manual action to (re)enable the service. + + Please refer to kresd.systemd(7) and to the + /usr/share/doc/knot-resolver/upgrading.html file (from + knot-resolver-doc). An online version of the latter is available at + https://knot-resolver.readthedocs.io/en/stable/upgrading.html#x-to-5-x + + For convenience, a suggested networking configuration can be found in + the file /var/lib/knot-resolver/.upgrade-4-to-5/kresd.conf.net + + -- Santiago Ruano Rincón <santiago@debian.org> Tue, 28 Jul 2020 14:48:09 +0200 + +knot-resolver (2.0.0-1) unstable; urgency=medium + + Knot Resolver systemd service units are now templated, so that multiple + processes can run concurrently on multi-core systems. For a full + overview of the status of all the running daemons, use: + + systemctl status system-kresd.slice + + For more information about this setup, please see kresd.systemd(7). + + -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Sat, 03 Feb 2018 22:51:02 -0500 + +knot-resolver (1.1.0~git2016072900-1) unstable; urgency=medium + + Knot Resolver now starts and runs under unprivileged user and uses a + socket activations to bind on the privileged ports. That means that if + you use anything more complicated than that you need to either override + the default service file with `systemd edit kresd.service` and + `systemd edit kresd.socket` to add more IP addresses, or just disable + it with `systemd mask kresd*.socket kresd.service` and provide your + own custom system service file tailored to your needs. + + -- Ondřej Surý <ondrej@debian.org> Thu, 04 Aug 2016 09:04:53 +0200 diff --git a/debian/README.source b/debian/README.source new file mode 100644 index 0000000..657fc28 --- /dev/null +++ b/debian/README.source @@ -0,0 +1,70 @@ +Tracking upstream sources in git +-------------------------------- + +This debian package is tracked in the git "debian/master" branch at +https://salsa.debian.org/dns-team/knot-resolver, which +includes the commits from the upstream git repository at +https://gitlab.labs.nic.cz/knot/knot-resolver.git. When importing +upstream tarballs, make sure you've fetched from the upstream repo +before using "gbp import-orig" the tarball. We rely on +upstream-vcs-tag from debian/gbp.conf to link upstream git history +with new package history. + +See Joey Hess's thoughts about why tracking upstream git history is +useful: https://joeyh.name/blog/entry/upstream_git_repositories/, as +well as DEP-14 for the branch-naming scheme: +http://dep.debian.net/deps/dep14/ + +Rebuilding epoch.js and epoch.css +--------------------------------- + + If you need to rebuild epoch.js and epoch.css using only tools + available in Debian, read gulpfile.js in d/missing-sources/epoch/ and + adjust following script if any files has been changed. You also need + to install ruby-sass, coffeescript and node-uglify + +cat > build.sh << EOF +#!/bin/sh + +coffee -b -c \ + src/epoch.coffee \ + src/core/context.coffee \ + src/core/util.coffee \ + src/core/d3.coffee \ + src/core/format.coffee \ + src/core/chart.coffee \ + src/core/css.coffee \ + src/data.coffee \ + src/model.coffee \ + src/basic.coffee \ + src/basic/*.coffee \ + src/time.coffee \ + src/time/*.coffee \ + src/adapters.coffee \ + src/adapters/*.coffee + +cat \ + src/epoch.js \ + src/core/context.js \ + src/core/util.js \ + src/core/d3.js \ + src/core/format.js \ + src/core/chart.js \ + src/core/css.js \ + src/data.js \ + src/model.js \ + src/basic.js \ + src/basic/*.js \ + src/time.js \ + src/time/*.js \ + src/adapters.js \ + src/adapters/*.js \ + > dist/js/epoch.js + +uglifyjs dist/js/epoch.js > dist/js/epoch.min.js + +sass -t compact sass/epoch.scss > dist/css/epoch.css +sass -t compressed sass/epoch.scss > dist/css/epoch.min.css +EOF + + -- Daniel Kahn Gillmor <dkg@fifthhorseman.net>, Thu, 22 Feb 2018 13:25:27 -0800 diff --git a/debian/TODO b/debian/TODO new file mode 100644 index 0000000..ed3a0cd --- /dev/null +++ b/debian/TODO @@ -0,0 +1,8 @@ +Things to work on for debian packaging for knot-resolver: + +* clean up javascript bundled by upstream, try to regenerate it from + source (starting with https://bugs.debian.org/923991 and + https://bugs.debian.org/924021) + +* normalize kresd.conf for sysvinit use by updating initscript to use + socket-activate + runuser diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..40af4db --- /dev/null +++ b/debian/changelog @@ -0,0 +1,1109 @@ +knot-resolver (5.6.0-1) unstable; urgency=medium + + * New upstream release + * New Build-Depends: libjemalloc-dev + * Remove years from CZ.NIC copyright + + -- Jakub Ružička <jakub.ruzicka@nic.cz> Thu, 26 Jan 2023 21:41:02 +0100 + +knot-resolver (5.5.3-1) unstable; urgency=medium + + * New upstream release + - Resolves CVE-2022-40188 + + -- Jakub Ružička <jakub.ruzicka@nic.cz> Thu, 22 Sep 2022 05:34:20 +0000 + +knot-resolver (5.5.2-1) unstable; urgency=medium + + * New upstream release with support for Knot DNS >= 3.2.0 + * Remove ppc64 arch without luajit + + -- Jakub Ružička <jakub.ruzicka@nic.cz> Fri, 02 Sep 2022 13:05:58 +0000 + +knot-resolver (5.5.1-5) unstable; urgency=medium + + * d/control: remove ppc64el from module-http + + -- Jakub Ružička <jakub.ruzicka@nic.cz> Fri, 05 Aug 2022 16:59:22 +0000 + +knot-resolver (5.5.1-4) unstable; urgency=medium + + * d/control: remove broken ppc64el and s390x arches + + -- Jakub Ružička <jakub.ruzicka@nic.cz> Tue, 02 Aug 2022 10:09:52 +0000 + +knot-resolver (5.5.1-3) unstable; urgency=medium + + * Upload to unstable + + -- Jakub Ružička <jakub.ruzicka@nic.cz> Wed, 27 Jul 2022 08:14:55 +0000 + +knot-resolver (5.5.1-2) experimental; urgency=medium + + * Use experimental to test luajit2 (Closes: #1013810) + * d/control: enable s390x due to luajit2 support + + -- Jakub Ružička <jakub.ruzicka@nic.cz> Sun, 03 Jul 2022 18:19:14 +0000 + +knot-resolver (5.5.1-1) unstable; urgency=medium + + * New upstream release + + -- Jakub Ružička <jakub.ruzicka@nic.cz> Tue, 14 Jun 2022 13:20:04 +0000 + +knot-resolver (5.5.0-1) unstable; urgency=medium + + * New upstream release + * d/upstream/signing-key.asc: update upstream key + * d/control: require Knot DNS 3.0.2 + + -- Jakub Ružička <jakub.ruzicka@nic.cz> Thu, 17 Mar 2022 17:34:34 +0000 + +knot-resolver (5.4.4-1) unstable; urgency=medium + + * New upstream release + + -- Jakub Ružička <jakub.ruzicka@nic.cz> Wed, 05 Jan 2022 17:35:35 +0000 + +knot-resolver (5.4.3-1) unstable; urgency=medium + + * New upstream release + + -- Jakub Ružička <jakub.ruzicka@nic.cz> Wed, 01 Dec 2021 15:34:11 +0000 + +knot-resolver (5.4.2-1) unstable; urgency=medium + + * New upstream release + + -- Jakub Ružička <jakub.ruzicka@nic.cz> Mon, 18 Oct 2021 13:56:09 +0000 + +knot-resolver (5.4.1-2) unstable; urgency=medium + + * Upload to unstable + + -- Jakub Ružička <jakub.ruzicka@nic.cz> Tue, 31 Aug 2021 14:14:14 +0000 + +knot-resolver (5.4.1-1) experimental; urgency=medium + + * New upstream release + - Compatible with Knot DNS 3.1 + - Includes fix for CVE-2021-40083 (Closes: #991463) + * d/control: bump Standards-Version to 4.6.0 + * d/control: add Multi-Arch: same for module-http + * d/tests: replace `which` with `command -v` + + -- Jakub Ružička <jakub.ruzicka@nic.cz> Wed, 25 Aug 2021 12:21:12 +0000 + +knot-resolver (5.3.2-1) unstable; urgency=medium + + [ Jakub Ružička ] + * New upstream release + * d/patches: remove patch included upstream + + [ Santiago Ruano Rincón ] + * Add Spanish debconf template translation (Closes: #987594) + + -- Jakub Ružička <jakub.ruzicka@nic.cz> Thu, 13 May 2021 18:18:18 +0100 + +knot-resolver (5.3.1-1) unstable; urgency=medium + + [ Jakub Ružička ] + * New upstream release + + [ Santiago Ruano Rincón ] + * Fix unaligned access and SIGBUS on armhf, and then the autopkgtest on + armhf at ci.debian.net (Closes: #918248) + + -- Santiago Ruano Rincón <santiago@debian.org> Mon, 12 Apr 2021 07:59:28 +0200 + +knot-resolver (5.3.0-1) unstable; urgency=medium + + [ Jakub Ružička ] + * New upstream release + + [ Santiago Ruano Rincón ] + * Add Portuguese debconf template translation (Closes: #982329) + + -- Jakub Ružička <jakub.ruzicka@nic.cz> Wed, 03 Mar 2021 15:11:51 +0100 + +knot-resolver (5.2.1-1) unstable; urgency=medium + + * New upstream release + * Remove no longer needed upstream patch: + 0001-fix-map-command-on-32-bit-platforms-regressed-in-5.2.patch + Note: it made the build non-reproducible. Thanks to Chris Lamb! + (Closes: #976827) + * Add lintian overrides for no-debconf-config tag and excessive line lengths + in some d/missing-sources/ .js files + * Fix no-symbols-control-file lintian override + * Update d/copyright: remove no longer included entries and reorder File + sections. + * Bump Standards-Version to 4.5.1. No changes required + + -- Santiago Ruano Rincón <santiago@debian.org> Tue, 15 Dec 2020 09:56:25 +0100 + +knot-resolver (5.2.0-2) unstable; urgency=low + + * Team upload. + + [ Jakub Ružička ] + * bugfix release + * debian/patches: fix map() command on 32-bit + + [ Dan Streetman ] + * d/control: Change arch 'any' to specific list + + [ Santiago Ruano Rincón ] + * Upload to unstable + + -- Santiago Ruano Rincón <santiago@debian.org> Mon, 07 Dec 2020 17:29:18 +0100 + +knot-resolver (5.2.0-1) experimental; urgency=medium + + [ Jakub Ružička ] + * new upstream release + * use experimental in order to safely move to knot >= 3.0 + * debian/control: require libknot-dev >= 3.0.1 + * debian/control: add myself to Uploaders + * debian/control: add lua-basexx build dep + * debian/control: add libnghttp2 dep for DoH support + * debian/control: use HTTP instead of HTTP/2 in desc + * debian/not-installed: don't use sysusers + + [ Santiago Ruano Rincón ] + * Set RELEASE to experimental in debian/salsa-ci.yml + + -- Jakub Ružička <jakub.ruzicka@nic.cz> Fri, 20 Nov 2020 21:41:22 +0100 + +knot-resolver (5.1.3-1) unstable; urgency=low + + * New upstream release. + * Add debconf template translations: + - German (Closes: #969234) + - Dutch (Closes: #969341) + - French (Closes: #969478) + * d/control: build again for arm64. See + https://gitlab.nic.cz/knot/knot-resolver/-/merge_requests/1033 + * d/rules: really disable config_test on arm64. enable on other archs + + -- Santiago Ruano Rincón <santiago@debian.org> Mon, 14 Sep 2020 11:24:10 +0200 + +knot-resolver (5.1.2-3) unstable; urgency=medium + + * d/tests/control: add skip-not-installable restriction to avoid failure + state on arm64 + + -- Santiago Ruano Rincón <santiago@debian.org> Wed, 12 Aug 2020 22:24:46 +0200 + +knot-resolver (5.1.2-2) unstable; urgency=low + + * Add myself to Uploaders + * debian/rules: disable config_tests on arm64 + https://gitlab.nic.cz/knot/knot-resolver/-/issues/593 + * knot-resolver-module-http: explicitly set non-arm64 architectures + + -- Santiago Ruano Rincón <santiago@debian.org> Fri, 31 Jul 2020 16:17:50 +0200 + +knot-resolver (5.1.2-1) unstable; urgency=low + + * Team upload. + * New upstream release. (Closes: #966077) + * preinst, postinst maintainer scripts: base upgrade-4-to-5-related code + from upstream distro/deb/. + * Update debian/copyright to include CZ.NIC in debian/ files + * Document changes and manual action required when upgrading from 3.x, in + debian/NEWS and through debconf. (Closes: #952673) + Thanks to Justin B Rye for reviewing the English template. + * Add Build-Dep on po-debconf + * Add debian/po + + -- Santiago Ruano Rincón <santiago@debian.org> Wed, 29 Jul 2020 17:07:51 +0200 + +knot-resolver (5.1.1-1) unstable; urgency=medium + + * Team upload. + * Acknowledge 5.1.1-0.1 NMU. Thanks to Daniel Baumann. + * Mitigates NXNSAttack (CVE-2020-12667) (Closes: #961076) + * d/tests/control: remove Test-Command: make -k installcheck ... It is + broken since meson+ninja is used to build kresd + * d/tests/roundtrip: update to 5.x: change --forks for --noninteractive + option. fix control socket path + * Add debian/salsa-ci.yml + * d/tests/control: set Restrictions: needs-root for roundtrip + * Add source of dygraph.js to debian/missing-sources + * update dygraph.min.js in debian/copyright, remove entry for + dygraph-combined.js + * remove debian/missing-sources/dygraph-combined.js + * Renable PIE. Building seems to be OK now. + * d/control: Bump debhelper-compat to 13 + + -- Santiago Ruano Rincón <santiago@debian.org> Fri, 10 Jul 2020 13:43:19 +0200 + +knot-resolver (5.1.1-0.1) unstable; urgency=medium + + * Non-maintainer upload. + * New upstream release: + - mitigation for NXNSAttack DNS protocol vulnerability + [CVE-2020-12667] (Closes: #961076) + + -- Daniel Baumann <daniel.baumann@progress-linux.org> Sun, 14 Jun 2020 06:24:58 +0200 + +knot-resolver (5.0.1-2) unstable; urgency=medium + + * Merge the indep and arch build, it doesn't really work with meson + * With new layout, the http module has to be converted to arch:any + + -- Ondřej Surý <ondrej@debian.org> Fri, 28 Feb 2020 10:10:07 +0100 + +knot-resolver (5.0.1-1) unstable; urgency=medium + + * Adapt d/rules to meson-build + * Use wrap-and-sort -a to sort the d/control and d/tests/control + * Remove debian/patches, they are no longer applicable + * Enable upstream systemd files, enable config_tests + * Update the list of installed and not-installed files + * Enable the documentation build and build it in dh_auto_build target + * Merge manpages installation to knot-resolver.install + * Don't call dh_installsystemd, remove the extra Debian unit files + * Use deb-systemd-invoke instead of systemctl in the maintainer scripts + * Enable dnstap and add libprotobuf-c-dev to B-D + + -- Ondřej Surý <ondrej@debian.org> Sat, 22 Feb 2020 21:14:47 +0100 + +knot-resolver (3.2.1-3) unstable; urgency=medium + + * knot-resolver-module-http is arch: all, not arch: any + * Explicitly list all non-arm64 architectures + + -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Fri, 08 Mar 2019 00:56:09 -0500 + +knot-resolver (3.2.1-2) unstable; urgency=medium + + * Standards-Version: move to 4.3.0 (no changes needed) + * move to debhelper 12 + * Avoid breakage when built against knot-dns 2.8.0 + * d/tests/control: wrap-and-sort + * Drop libkres9 and libkres-dev packages (Closes: #923970) + * avoid clobbering CXXFLAGS when compiling lua-aho-corasick + * missing-sources: updated dygraph-combined.js to match minified version + * avoid shipping pre-built glyphicons-halflings-regular.woff2 + + -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Thu, 07 Mar 2019 16:23:16 -0500 + +knot-resolver (3.2.1-1) unstable; urgency=medium + + * new upstream release (Closes: #922172) + * Update symbols file for 3.2.1 release + + -- Ondřej Surý <ondrej@debian.org> Thu, 21 Feb 2019 09:42:37 +0000 + +knot-resolver (3.2.0-1) unstable; urgency=medium + + * new upstream release + * bump ABI from libkres8 to libkres9 + * drop reproducibility patch, adopted upstream + * include experimental_dot_auth module (recommend: lua-basexx for it) + * d/copyright: removed reference to ISAAC, removed upstream + + -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Wed, 19 Dec 2018 19:23:37 -0500 + +knot-resolver (3.1.0-1) unstable; urgency=medium + + * new upstream release + * set knot-resolver-doc to Multi-Arch: foreign + * drop upstreamed patch; avoid use of git during installcheck + * make reproducible datestamp in kresd(8) + * increase build-dep on libknot to 2.7.2 + * updated libkres8.symbols + * minimize upstream signing keys + * drop unused lintian-overrides + + -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Tue, 20 Nov 2018 15:47:19 -0500 + +knot-resolver (3.0.0-9) unstable; urgency=medium + + * autopkg: allow stderr on "make installcheck" + + -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Wed, 12 Sep 2018 13:02:45 -0400 + +knot-resolver (3.0.0-8) unstable; urgency=medium + + * avoid stderr during autopkgtest + * refresh patches + + -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Wed, 12 Sep 2018 09:51:40 -0400 + +knot-resolver (3.0.0-7) unstable; urgency=medium + + * try upstream proposed test suite change + + -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Tue, 11 Sep 2018 23:58:14 -0400 + +knot-resolver (3.0.0-6) unstable; urgency=medium + + * stop building knot-resolver on arm64 (Closes: #907729) + + -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Wed, 05 Sep 2018 19:30:10 -0400 + +knot-resolver (3.0.0-5) unstable; urgency=medium + + * roundtrip test: increase verbosity + * roundtrip test: avoid long timeouts if UDP query fails + * autopkgtest: improve call to "make installcheck" + + -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Fri, 31 Aug 2018 21:10:39 -0400 + +knot-resolver (3.0.0-4) unstable; urgency=medium + + * Re-enable tests on all architectures + * autopkgtest: be more verbose about error + * autopkgtest: allow specifying module directory + * point to modules needed to run roundtrip test at build time + + -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Fri, 31 Aug 2018 14:00:24 -0400 + +knot-resolver (3.0.0-3) unstable; urgency=medium + + * improve autopkgtest + * autopkgtest: env vars can select which kdig and kresd to test + * run roundtrip test at compile time too + + -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Fri, 31 Aug 2018 11:36:17 -0400 + +knot-resolver (3.0.0-2) unstable; urgency=medium + + * autopkgtest: added full roundtrip tests + + -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Thu, 30 Aug 2018 14:41:02 -0400 + +knot-resolver (3.0.0-1) unstable; urgency=medium + + * new upstream release + * Standards-Version: bump to 4.2.1 (no changes needed) + * build-depend on libknot 2.7 or later + + -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Mon, 27 Aug 2018 18:42:45 -0400 + +knot-resolver (2.4.1-1) unstable; urgency=medium + + * new upstream release + - resolves CVE-2018-10920 + * k-r.postinst: strip trailing whitespace + * clean up patches already adopted upstream + * ship rebinding lua module + * use upstream-preferred systemd service configuration + * tell lintian not to complain about kresd.target + * tighten up build-deps on libknot-dev + * three symbols (re)introduced into libkres7 + * Standards-Version: bump to 4.2.0 (no changes needed) + + -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Mon, 27 Aug 2018 18:22:48 -0400 + +knot-resolver (2.3.0-4) unstable; urgency=medium + + * try to restart any running kresd daemon on upgrade + * correct systemctl start documentation (Closes: #891838) + * fix FTBFS on kfreebsd (Closes: #900013) + + -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Thu, 24 May 2018 13:10:31 -0400 + +knot-resolver (2.3.0-3) unstable; urgency=medium + + * create knot-resolver user correctly in postinst (Closes: #894580) + * d/changelog: update to acknowledge CVE-2018-1110 + + -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Mon, 14 May 2018 11:58:29 -0400 + +knot-resolver (2.3.0-2) unstable; urgency=medium + + * only build against libsystemd on linux architectures + + -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Thu, 26 Apr 2018 11:56:29 -0400 + +knot-resolver (2.3.0-1) unstable; urgency=medium + + * new upstream release + - resolves CVE-2018-1110 + * drop patches already upstream + * ship prefill module + * d/copyright: adjust years of main stanza + * add trie_* and kr_zonecut_is_empty to symbols + * Standards-Version: bump to 4.1.4 (no changes needed) + * drop unnecessary build-dependencies + + -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Tue, 24 Apr 2018 19:20:45 -0400 + +knot-resolver (2.2.0-1) unstable; urgency=medium + + * new upstream release + - move from libkres6 to libkres7 due to ABI change + * drop patches already merged upstream + * avoid shipping root.hints and icann-ca.pem without a patch + * work around https://gitlab.labs.nic.cz/knot/knot-resolver/issues/338 + * renumbering patches for cleanliness + * use externally-set CPPFLAGS during build + * clean up build of lua-aho-corasick + * document copyright better to drop a lintian override + * clarify rationales behind lintian-overrides + * be clearer about keyfile-ro restarting kresd in kresd.8 + + -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Sun, 08 Apr 2018 23:46:47 -0400 + +knot-resolver (2.1.1-1) unstable; urgency=medium + + * new upstream release + * drop patches already upstream + * refresh remaining patches + + -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Sun, 25 Feb 2018 17:36:46 -0800 + +knot-resolver (2.1.0-2) unstable; urgency=medium + + * clean up README.source + * more bugfix patches from upstream + + -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Thu, 22 Feb 2018 13:27:59 -0800 + +knot-resolver (2.1.0-1) unstable; urgency=medium + + * new upstream release + - move from libkres5 to libkres6 due to an ABI change + - kresd.systemd(7) documents running a public-facing resolver + (Closes: #880682) + * d/upstream/signing-key.asc: add Tomas Krizek's key + * d/gbp.conf: use DEP-14 branch naming + * drop patches already upstream + * d/control: build-depend on libknot-dev (>= 2.6.4) + * handle updates to the DNS root.key sensibly (dpkg triggers) + * systemd: mask kresd.service, following upstream's recommendation + * /etc/default/kresd: indicate that it is only for sysvinit + * d/rules: clean up unnecessary lines + * systemd: ship upstream unit files and tmpfiles config directly + * d/control: Rules-Requires-Root: no + * use python3 during the build. + * ship upstream NEWS file as upstream changelog + * debian/TODO: clean up + + -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Sat, 17 Feb 2018 21:13:45 -0500 + +knot-resolver (2.0.0-3) unstable; urgency=medium + + * documentation: build with sphinx_rtd_theme + + -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Mon, 05 Feb 2018 17:13:04 -0500 + +knot-resolver (2.0.0-2) unstable; urgency=medium + + * Simplify /etc/default/kresd + * drop preinst special case for versions < 1.1.0-2 + * postinst: create the knot-resolver user at configure time + * initscript knows path to /usr/lib/tmpfiles.d/knot-resolver.conf + * Overhaul systemd integration. + * d/copyright: include more contributed subprojects (Closes: #889646) + + -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Mon, 05 Feb 2018 13:46:25 -0500 + +knot-resolver (2.0.0-1) unstable; urgency=medium + + [ Ondřej Surý ] + * Reduce the (non-public) symbols in libkres4 + + [ Daniel Kahn Gillmor ] + * import systemd fixes from Tomas Krizek + * avoid leaving /run/knot-resolver world-executable + * systemd: set up kresd@1 service and sockets + * wrap-and-sort -ast + * Add kresd.service as a symlink to kresd@1.service + * more bugfixes from upstream + * NEWS: document changes for using kresd with systemd + * include new lua modules + * move from libkres4 to libkres5 (cache functions dropped) + + -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Mon, 05 Feb 2018 00:25:51 -0500 + +knot-resolver (1.5.3-1) unstable; urgency=medium + + * New upstream release + + -- Ondřej Surý <ondrej@debian.org> Wed, 24 Jan 2018 12:35:55 +0000 + +knot-resolver (1.5.2-1) unstable; urgency=medium + + * New upstream release + - closes CVE-2018-1000002 + * d/copyright: use https where possible + * move to debhelper 11 (docs to /u/s/doc/knot-resolver/, policy §12.3) + * d/control: move Maintainer: to knot-resolver@packages.debian.org + + -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Mon, 22 Jan 2018 15:00:21 +0100 + +knot-resolver (1.5.1-2) unstable; urgency=medium + + * libkres-dev Depends on libkres4 (Closes: #887572) + * Standards-Version: bump to 4.1.3 (no changes needed) + * move Vcs-* to salsa.debian.org + + -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Wed, 17 Jan 2018 23:23:45 -0500 + +knot-resolver (1.5.1-1) unstable; urgency=medium + + * new upstream release + * move packaging split to unstable + * d/watch: move to version 4, avoid alpha/beta releases + * drop patches for bugs already fixed upstream + * use "make installcheck" as a simple test + * track evolving name of etcd module + * wrap-and-sort -a + * shipping new modules: priming, detect_time_{jump,skew} + * added kr_now to exported symbols + * Standards-Version: bump to 4.1.2 (no changes needed) + + -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Wed, 13 Dec 2017 15:40:10 -0500 + +knot-resolver (1.5.0-5) experimental; urgency=medium + + * packaging cleanup, splitting out library packages + * dbgsym migration no longer needed + knot-resolver-dbg is not shipped anywhere + * clean up installation of knot-resolver-module-http + * move to dh_missing, as dh_install --fail-missing is deprecated + * clean up/simplify knot-resolver.install + * break out libkres library packages + * put manpage installation under control of dh_installman + * knock stuff off the packaging todo list + * register knot-resolver-doc with doc-base + * add libkres4.symbols to track API evolution + * correct debian/copyright paths + + -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Thu, 30 Nov 2017 23:25:54 -0500 + +knot-resolver (1.5.0-4) unstable; urgency=medium + + * Pull upstream patch to increase the CACHE_SIZE in tests_cache for + platforms with page size greater than 4k (Closes: #878976) + * Pull upstream patch from MR!389 to not run check-config at all as + modulepath is hardcoded (Closes: #881492) + + -- Ondřej Surý <ondrej@debian.org> Mon, 13 Nov 2017 19:53:30 +0000 + +knot-resolver (1.5.0-3) unstable; urgency=medium + + * reorder tests + + -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Mon, 13 Nov 2017 03:24:06 +0800 + +knot-resolver (1.5.0-2) unstable; urgency=medium + + * include a list of suggestions for future packaging work + * try some debugging to see failures for #881492 + + -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Mon, 13 Nov 2017 03:19:21 +0800 + +knot-resolver (1.5.0-1) unstable; urgency=medium + + * New upstream version. + * d/copyright: update source homepage (Closes: #879166) + * Standards-Version: bump to 4.1.1 (no changes needed) + * switch to debhelper 10 + * drop dh-systemd build-dep -- no longer needed (may be added back for + backports) + * [lintian] fix debian-news-entry-uses-asterisk + * drop unused lintian overrides + * added myself to uploaders + * update upstream signer key + * patch from upstream for SIGPIPE (Closes: #881462) + * patch to fix upstream test suite + * debian/copyright: cleanup + * move to python3-{sphinx,breathe} for build-dep + * use root.hints from dns-root-data + + -- Daniel Kahn Gillmor <dkg@fifthhorseman.net> Sun, 12 Nov 2017 13:00:50 +0800 + +knot-resolver (1.4.0-2) unstable; urgency=medium + + * Add root.hints into the package + * Bump required libknot version to >= 2.6.0 + + -- Ondřej Surý <ondrej@debian.org> Fri, 29 Sep 2017 19:56:14 +0200 + +knot-resolver (1.4.0-1) unstable; urgency=medium + + * Make libsystemd-dev (>= 227) a hard requirement + * Fix the Knot Resolver homepage + * New upstream version 1.4.0 + * aho-corrasick.lua has been removed from the sources + + -- Ondřej Surý <ondrej@debian.org> Thu, 28 Sep 2017 18:31:54 +0200 + +knot-resolver (1.3.3-1) unstable; urgency=medium + + * New upstream version 1.3.3 + + -- Ondřej Surý <ondrej@debian.org> Wed, 09 Aug 2017 15:28:35 +0200 + +knot-resolver (1.3.2-1) unstable; urgency=medium + + * New upstream version 1.3.2 + + -- Ondřej Surý <ondrej@debian.org> Fri, 28 Jul 2017 14:22:08 +0200 + +knot-resolver (1.3.1-1) unstable; urgency=medium + + * New upstream version 1.3.1 + * Remove all upstream patches merged upstream + + -- Ondřej Surý <ondrej@debian.org> Fri, 23 Jun 2017 14:38:15 +0200 + +knot-resolver (1.3.0-2) unstable; urgency=medium + + * Fix the moduledir in the http2 module + + -- Ondřej Surý <ondrej@debian.org> Fri, 16 Jun 2017 13:20:14 +0200 + +knot-resolver (1.3.0-1) unstable; urgency=medium + + [ Ondřej Surý ] + * New upstream version 1.3.0 + * Export MAKEVARS as environment variables + * Add libedit-dev to B-D to build kresc + + [ Daniel Kahn Gillmor ] + * ship kresc because we use --fail-missing + * clean up things the upstream build scripts leave behind + + -- Ondřej Surý <ondrej@debian.org> Tue, 13 Jun 2017 09:07:54 +0200 + +knot-resolver (1.2.6-1) unstable; urgency=medium + + * New upstream version 1.2.6 + + -- Ondřej Surý <ondrej@debian.org> Mon, 24 Apr 2017 16:31:11 +0200 + +knot-resolver (1.2.5-1) unstable; urgency=medium + + * New upstream version 1.2.5 + + -- Ondřej Surý <ondrej@debian.org> Wed, 05 Apr 2017 16:07:40 +0200 + +knot-resolver (1.2.4-1) unstable; urgency=medium + + * New upstream version 1.2.4 + + -- Ondřej Surý <ondrej@debian.org> Thu, 09 Mar 2017 14:20:24 +0100 + +knot-resolver (1.2.3-1) unstable; urgency=medium + + * New upstream version 1.2.3 + + -- Ondřej Surý <ondrej@debian.org> Thu, 23 Feb 2017 15:49:14 +0100 + +knot-resolver (1.2.2-1) unstable; urgency=medium + + * New upstream version 1.2.2 + + -- Ondřej Surý <ondrej@debian.org> Fri, 10 Feb 2017 13:54:28 +0100 + +knot-resolver (1.2.1-1) unstable; urgency=high + + * New upstream version 1.2.1 + + -- Ondřej Surý <ondrej@debian.org> Wed, 01 Feb 2017 20:56:35 +0100 + +knot-resolver (1.2.0-1) unstable; urgency=medium + + * New upstream version 1.2.0 + * Squash 1.2.0 changelog into a single 1.2.0 entry + * Add d/upstream/signing-key.asc + * Add d/watch to monitor upstream tarballs + + -- Ondřej Surý <ondrej@debian.org> Wed, 25 Jan 2017 14:23:27 +0100 + +knot-resolver (1.1.1-131-g59d5adf-1) unstable; urgency=medium + + * Disable tests on mipsel that started to fail in a mysterious way + (Closes: #847696) + * Imported Upstream version 1.1.1-131-g59d5adf + + -- Ondřej Surý <ondrej@debian.org> Wed, 14 Dec 2016 12:16:36 +0100 + +knot-resolver (1.1.1-122-g940fda9-2) unstable; urgency=medium + + * Move -DNDEBUG just to build target + + -- Ondřej Surý <ondrej@debian.org> Tue, 13 Dec 2016 09:52:19 +0100 + +knot-resolver (1.1.1-122-g940fda9-1) unstable; urgency=medium + + * Imported Upstream version 1.1.1-122-g940fda9 + * Remove static linking patch + * Add $(MAKEVARS) to make check invocation + * Use SOVERSION when compiling to unfail the tests + + -- Ondřej Surý <ondrej@debian.org> Mon, 12 Dec 2016 16:26:27 +0100 + +knot-resolver (1.1.1-41-g8437a7d-2) unstable; urgency=medium + + * Update NEWS with kresd.service (Closes: #843178) + * Enable NDEBUG on production builds + + -- Ondřej Surý <ondrej@debian.org> Wed, 07 Dec 2016 10:02:55 +0100 + +knot-resolver (1.1.1-41-g8437a7d-1) unstable; urgency=medium + + * Imported Upstream version 1.1.1-41-g8437a7d + * Add instructions how to rebuild epoch.js and epoch.css using Debian + tools (Closes: #833309) + * Record SONAMEs of used C libraries as lua literals instead of guessing + (Closes: #841496) + * Rebase patches on top of new git master release + + -- Ondřej Surý <ondrej@debian.org> Fri, 21 Oct 2016 11:27:56 +0200 + +knot-resolver (1.1.1-2) unstable; urgency=medium + + * Add missing sources for epoch.js + * Also allow loading of libknot.so.4 + + -- Ondřej Surý <ondrej@debian.org> Tue, 11 Oct 2016 14:54:20 +0200 + +knot-resolver (1.1.1-1) unstable; urgency=medium + + * Imported Upstream version 1.1.1 + + -- Ondřej Surý <ondrej@debian.org> Tue, 11 Oct 2016 14:52:37 +0200 + +knot-resolver (1.1.0-8) unstable; urgency=medium + + * Use restart instead of try-restart if upgrading from << 1.1.0-7 + + -- Ondřej Surý <ondrej@debian.org> Tue, 16 Aug 2016 11:18:46 +0200 + +knot-resolver (1.1.0-7) unstable; urgency=medium + + * Add the final missing bits from dh_systemd_enable + + -- Ondřej Surý <ondrej@debian.org> Tue, 16 Aug 2016 10:29:22 +0200 + +knot-resolver (1.1.0-6) unstable; urgency=medium + + * Make extra sure that kresd.service is stopped before starting the + sockets again + * Stop old systemd units early before they are removed + + -- Ondřej Surý <ondrej@debian.org> Tue, 16 Aug 2016 08:32:22 +0200 + +knot-resolver (1.1.0-5) unstable; urgency=medium + + * Stop kresd.service in prerm script to allow kresd.socket restarts + + -- Ondřej Surý <ondrej@debian.org> Mon, 15 Aug 2016 15:43:59 +0200 + +knot-resolver (1.1.0-4) unstable; urgency=medium + + * Install kresd-tls.socket into the package + * Unwrap variables in default file as systemd units cannot resolve + nested variables + + -- Ondřej Surý <ondrej@debian.org> Mon, 15 Aug 2016 13:29:32 +0200 + +knot-resolver (1.1.0-3) unstable; urgency=medium + + * Tighten Build-Depends on libcmocka-dev to 1.0.0 + * Cleanup the knot-resolver -> kresd init script rename + + -- Ondřej Surý <ondrej@debian.org> Mon, 15 Aug 2016 13:11:10 +0200 + +knot-resolver (1.1.0-2) unstable; urgency=medium + + * Update systemd units to be named after the daemon (kresd) + + -- Ondřej Surý <ondrej@debian.org> Mon, 15 Aug 2016 10:09:53 +0200 + +knot-resolver (1.1.0-1) unstable; urgency=medium + + * Imported Upstream version 1.1.0 + * Rebase patches on top of Knot Resolver 1.1.0 + * Don't require libsystemd-dev on Ubuntu 14.04 LTS + + -- Ondřej Surý <ondrej@debian.org> Fri, 12 Aug 2016 10:45:09 +0200 + +knot-resolver (1.1.0~git2016072900-2) unstable; urgency=medium + + * Fix minor nits in maintainers scripts (Courtesy of Andreas Henriksson) + * Imported Upstream version 1.1.0~2016080800 + * Bump required version of libknot to 2.3.0 + + -- Ondřej Surý <ondrej@debian.org> Wed, 10 Aug 2016 10:04:01 +0200 + +knot-resolver (1.1.0~git2016072900-1) unstable; urgency=medium + + * Imported Upstream version 1.1.0~git2016072900 + * Knot Resolver is now running as non-privileged socket activated + systemd service. + + -- Ondřej Surý <ondrej@debian.org> Thu, 04 Aug 2016 09:01:15 +0200 + +knot-resolver (1.1.0~git2016072000-1) unstable; urgency=medium + + * Imported Upstream version 1.1.0~git2016072000 + + -- Ondřej Surý <ondrej@debian.org> Wed, 20 Jul 2016 14:31:10 +0200 + +knot-resolver (1.1.0~git2016071900-1) unstable; urgency=medium + + * Use correct lua comments in default kresd.conf + * knot-resolver 1.1.0~git2016071300-3 + * Imported Upstream version 1.1.0~git2016071900 + + -- Ondřej Surý <ondrej@debian.org> Wed, 20 Jul 2016 07:45:42 +0200 + +knot-resolver (1.1.0~git2016071300-3) unstable; urgency=medium + + [ Daniel Kahn Gillmor ] + * Remove static lib/libkres.a during clean + * Put knot-resolver-doc into Section: doc (thanks, Lintian!) + + [ Ondřej Surý ] + * Use correct lua comments in default kresd.conf + + -- Ondřej Surý <ondrej@debian.org> Mon, 18 Jul 2016 10:47:12 +0200 + +knot-resolver (1.1.0~git2016071300-2) unstable; urgency=medium + + * Build and install documentation into knot-resolver-doc package + (Closes: #831461) + * Build with libsystemd-dev to get socket-activation + * Set LD_LIBRARY_PATH to load correct libkresd.so.1 for tests + * Link the tests with static libkres library + + -- Ondřej Surý <ondrej@debian.org> Sat, 16 Jul 2016 15:55:04 +0200 + +knot-resolver (1.1.0~git2016071300-1) unstable; urgency=medium + + * Imported Upstream version 1.1.0~git2016071300 + * knot-resolver-module-all should be arch:all (only lua there) + * Don't build tinyweb module anymore as HTTP/2 module replaces it + + -- Ondřej Surý <ondrej@debian.org> Thu, 14 Jul 2016 07:28:24 +0200 + +knot-resolver (1.1.0~git2016070600-4) unstable; urgency=medium + + * Add missing http.lua to knot-resolver-module-http package + + -- Ondřej Surý <ondrej@debian.org> Wed, 13 Jul 2016 15:35:11 +0200 + +knot-resolver (1.1.0~git2016070600-3) unstable; urgency=medium + + * Add NEW packages lua-http and lua-mmdb to knot-resolver-module-http + Depends + * knot-resolver now recommends knot-resolver-module-http + * Add liblmdb-dev to Build-Depends (Closes: #830934) + + -- Ondřej Surý <ondrej@debian.org> Wed, 13 Jul 2016 14:09:12 +0200 + +knot-resolver (1.1.0~git2016070600-2) unstable; urgency=medium + + * Explicitly declare architectures for tinyweb module + (Closes: #830939) + + -- Ondřej Surý <ondrej@debian.org> Wed, 13 Jul 2016 09:32:30 +0200 + +knot-resolver (1.1.0~git2016070600-1) unstable; urgency=medium + + * Imported Upstream version 1.1.0~git2016070600 + * Adapt to the new fixed config.mk that accepts overrides + * Add new HTTP/2 module that will replace tinyweb module + * Add daf module to knot-resolver + * Bump standards to 3.9.8 (no change) + + -- Ondřej Surý <ondrej@debian.org> Mon, 11 Jul 2016 15:33:21 +0200 + +knot-resolver (1.0.0-16-g1070c49-1) unstable; urgency=medium + + * Imported Upstream version 1.0.0-16-g1070c49 + * Add Breaks/Replaces for knot-resolver-module-tinyweb (Closes: #829051) + + -- Ondřej Surý <ondrej@debian.org> Thu, 30 Jun 2016 11:02:06 +0200 + +knot-resolver (1.0.0-13-g95b243f-1) unstable; urgency=medium + + * Imported Upstream version 1.0.0-13-g95b243f + + -- Ondřej Surý <ondrej@debian.org> Wed, 22 Jun 2016 09:28:44 +0200 + +knot-resolver (1.0.0-12-g86f75f0-1) unstable; urgency=medium + + * Split tinyweb module into separate package + * Imported Upstream version 1.0.0-12-g86f75f0 + * Use simpler method for optional dh_strip parameter + + -- Ondřej Surý <ondrej@debian.org> Tue, 21 Jun 2016 09:23:27 +0200 + +knot-resolver (1.0.0-1) unstable; urgency=medium + + * Imported Upstream version 1.0.0 + + -- Ondřej Surý <ondrej@debian.org> Tue, 31 May 2016 10:12:26 +0200 + +knot-resolver (1.0.0~beta3-95-g550ceca-1) unstable; urgency=medium + + * Imported Upstream version 1.0.0~beta3-95-g550ceca + + -- Ondřej Surý <ondrej@debian.org> Mon, 30 May 2016 09:38:02 +0200 + +knot-resolver (1.0.0~beta3-91-g9259b27-1) unstable; urgency=medium + + * Imported Upstream version 1.0.0~beta3-91-g9259b27 + + -- Ondřej Surý <ondrej@debian.org> Fri, 27 May 2016 14:51:19 +0200 + +knot-resolver (1.0.0~beta3-85-g503adee-1) unstable; urgency=medium + + * Imported Upstream version 1.0.0~beta3-85-g503adee + + -- Ondřej Surý <ondrej@debian.org> Wed, 25 May 2016 09:19:05 +0200 + +knot-resolver (1.0.0~beta3-83-gc416877-1) unstable; urgency=medium + + * Imported Upstream version 1.0.0~beta3-83-gc416877 + + -- Ondřej Surý <ondrej@debian.org> Mon, 23 May 2016 14:24:41 +0200 + +knot-resolver (1.0.0~beta3-78-g3429619-1) unstable; urgency=medium + + * Imported Upstream version 1.0.0~beta3-78-g3429619 + + -- Ondřej Surý <ondrej@debian.org> Wed, 18 May 2016 10:33:19 +0200 + +knot-resolver (1.0.0~beta3-72-g4ffb749-1) unstable; urgency=medium + + * Move lintian overrides for sources to d/source.lintian-overrides + * Add missing wildcard in lintian overrides + * Require golang-go (>= 1.5.0) that's needed + * Fix order of version requirement and arch for golang-go + * Imported Upstream version 1.0.0~beta3-72-g4ffb749 + * Dynamic Go modules are supported on arm*, amd64 and i386 since + golang-go (>= 1.6.0) + + -- Ondřej Surý <ondrej@debian.org> Thu, 12 May 2016 15:32:22 +0200 + +knot-resolver (1.0.0~beta3-71-gb5b0232-1) unstable; urgency=medium + + * Imported Upstream version 1.0.0~beta3-71-gb5b0232 + * Back to master branch + + -- Ondřej Surý <ondrej@debian.org> Wed, 11 May 2016 13:36:37 +0200 + +knot-resolver (1.0.0~beta3-61-gc23edd0-1) unstable; urgency=medium + + * Imported Upstream version 1.0.0~beta3-61-gc23edd0 + * Pull fixes from tcp-ooo branch + + -- Ondřej Surý <ondrej@debian.org> Tue, 03 May 2016 09:55:40 +0200 + +knot-resolver (1.0.0~beta3-60-ge61c48e-3) unstable; urgency=medium + + * Add a versioned Built-Using for golang-github-abh-geoip-dev + (Courtesy of Michael Hudson-Doyle) + + -- Ondřej Surý <ondrej@debian.org> Tue, 26 Apr 2016 13:18:16 +0200 + +knot-resolver (1.0.0~beta3-60-ge61c48e-2) unstable; urgency=medium + + * Don't use dh_golang for computing Built-Using as it doesn't work, + so we can build tinyweb module again + + -- Ondřej Surý <ondrej@debian.org> Tue, 26 Apr 2016 11:40:30 +0200 + +knot-resolver (1.0.0~beta3-60-ge61c48e-1) unstable; urgency=medium + + * Imported Upstream version 1.0.0~beta3-60-ge61c48e + * Remove -dbg package for dbgsym transition + * Don't build tinyweb module as dh_golang is broken right now + (Closes: #822386) + + -- Ondřej Surý <ondrej@debian.org> Tue, 26 Apr 2016 10:35:56 +0200 + +knot-resolver (1.0.0~beta3-41-g5bf6748-1) unstable; urgency=medium + + * Imported Upstream version 1.0.0~beta3-41-g5bf6748 + + -- Ondřej Surý <ondrej@debian.org> Wed, 13 Apr 2016 21:55:07 +0200 + +knot-resolver (1.0.0~beta3-31-g79a8440-1) unstable; urgency=medium + + * Imported Upstream version 1.0.0~beta3-31-g79a8440 + * Add kresd.8 man page to distribution + * Fix WS in d/control + + -- Ondřej Surý <ondrej@debian.org> Thu, 31 Mar 2016 18:46:30 +0200 + +knot-resolver (1.0.0~beta3-1) unstable; urgency=medium + + * Add lua-socket and lua-sec for root TA bootstrapping + * Imported Upstream version 1.0.0~beta3 + + -- Ondřej Surý <ondrej@debian.org> Sat, 30 Jan 2016 16:15:23 +0100 + +knot-resolver (1.0.0~beta2-123-g6134920-1) unstable; urgency=medium + + * Imported Upstream version 1.0.0~beta2-123-g6134920 + * Mangle tinyweb only if it exists (Closes: #812276) + * Mute lintian error about missing tinyweb.js sources and mute lintian + warnings about shared library + + -- Ondřej Surý <ondrej@debian.org> Tue, 26 Jan 2016 15:00:54 +0100 + +knot-resolver (1.0.0~beta2-104-ge110f97-1) unstable; urgency=medium + + * Imported Upstream version 1.0.0~beta2-104-ge110f97 + * Rebase patches on top of 1.0.0~beta2-104-ge110f97 release + * Knot Resolver needs libknot 2.1.0 at least + * Disable PIE hardening as it breaks the build + * Squash debian quirks patch into one + * Disable ASAN sanitized build + * libkresd is now dynamic library + * Install other new files into the package + + -- Ondřej Surý <ondrej@debian.org> Tue, 12 Jan 2016 16:45:42 +0100 + +knot-resolver (1.0.0~beta2-58-gd8762fb-1) unstable; urgency=medium + + * Imported Upstream version 1.0.0~beta2-58-gd8762fb + + -- Ondřej Surý <ondrej@debian.org> Fri, 04 Dec 2015 15:16:43 +0100 + +knot-resolver (1.0.0~beta2-1) unstable; urgency=medium + + * Imported Upstream version 1.0.0~beta2 + * Build the Go modules on amd64 + * Compile the experimental with clang and AddressSanitizer + * Fix missing GPL-2 and s/MIT/Expat/ in d/copyright + * Add missing sources for javascript files + * Add ${misc:Depends} to -dbg package + * Tidy-up the permissions on modules + * Use libjs-jquery and libjs-d3 instead of bundled javascript + * Stop depending on libknot-dev as the code can cope with SOVERSION + + -- Ondřej Surý <ondrej@debian.org> Mon, 23 Nov 2015 11:49:25 +0100 + +knot-resolver (1.0.0~beta1-85-gd94caa6-1) experimental; urgency=medium + + * Imported Upstream version 1.0.0~beta1-85-gd94caa6 + + -- Ondřej Surý <ondrej@debian.org> Thu, 12 Nov 2015 23:14:07 +0100 + +knot-resolver (1.0.0~beta1-75-g0497be2-1) experimental; urgency=medium + + * Imported Upstream version 1.0.0~beta1-75-g0497be2 + * Rebase patches on top of 1.0.0~beta1-75-g0497be2 + + -- Ondřej Surý <ondrej@debian.org> Wed, 11 Nov 2015 09:58:20 +0100 + +knot-resolver (1.0.0~beta1-12-g822d8fe-1) experimental; urgency=medium + + * Initial release + * Update d/copyright with some missing bits + * Imported Upstream version 1.0.0~beta1-12-g822d8fe + + -- Ondřej Surý <ondrej@debian.org> Mon, 12 Oct 2015 11:55:14 +0200 diff --git a/debian/clean b/debian/clean new file mode 100644 index 0000000..ce17de8 --- /dev/null +++ b/debian/clean @@ -0,0 +1,5 @@ +doc/kresd.8 +libkres.pc +lib/libkres.a +lib/libkres.so.* +test-modules/ diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..2031085 --- /dev/null +++ b/debian/control @@ -0,0 +1,113 @@ +Source: knot-resolver +Section: net +Priority: optional +Maintainer: knot-resolver packagers <knot-resolver@packages.debian.org> +Uploaders: Ondřej Surý <ondrej@debian.org>, + Daniel Kahn Gillmor <dkg@fifthhorseman.net>, + Santiago Ruano Rincón <santiago@debian.org>, + Jakub Ružička <jakub.ruzicka@nic.cz> +Build-Depends: debhelper-compat (= 13), + dns-root-data, + doxygen, + gnutls-bin <!nocheck>, + knot-dnsutils <!nocheck>, + libcmocka-dev (>= 1.0.0), + libedit-dev, + libfstrm-dev, + libgnutls28-dev, + libjemalloc-dev, + libknot-dev (>= 3.0.2), + liblmdb-dev, + libluajit-5.1-dev, + libnghttp2-dev, + libprotobuf-c-dev, + libsystemd-dev (>= 227) [linux-any], + libuv1-dev, + lua-basexx, + lua-cqueues, + luajit, + meson, + ninja-build, + po-debconf, + pkg-config, + protobuf-c-compiler, + python3-breathe, + python3-sphinx, + python3-sphinx-rtd-theme, + socat <!nocheck> +Standards-Version: 4.6.0 +Homepage: https://www.knot-resolver.cz/ +Vcs-Browser: https://salsa.debian.org/dns-team/knot-resolver +Vcs-Git: https://salsa.debian.org/dns-team/knot-resolver.git +Rules-Requires-Root: no + +Package: knot-resolver +Architecture: amd64 arm64 armel armhf i386 mips mips64el mipsel +Depends: adduser, + debconf, + dns-root-data, + lua-sec, + lua-socket, + ${misc:Depends}, + ${shlibs:Depends} +Replaces: libkres9 (<< 3.2.1-2) +Breaks: libkres9 (<< 3.2.1-2) +Recommends: knot-resolver-module-http, + lua-basexx, + lua-cqueues +Description: caching, DNSSEC-validating DNS resolver + The Knot Resolver is a caching full resolver implementation + written in C and LuaJIT, including both a resolver library and a + daemon. Modular architecture of the library keeps the core tiny and + efficient, and provides a state-machine like API for + extensions. There are three built-in modules - iterator, cache, + validator, and many external. + . + The Lua modules, switchable and shareable cache, and fast FFI + bindings makes it great to tap into resolution process, or be used + for your recursive DNS service. It's the OpenResty of DNS. + . + The server adopts a different scaling strategy than the rest of the + DNS recursors - no threading, shared-nothing architecture (except + MVCC cache that may be shared). You can start and stop additional + nodes depending on the contention without downtime. + +Package: knot-resolver-module-http +Architecture: amd64 arm64 armel armhf i386 mips mips64el mipsel +Multi-Arch: same +Depends: fonts-glyphicons-halflings, + libjs-bootstrap, + libjs-d3, + libjs-jquery, + lua-http, + lua-mmdb, + ${misc:Depends}, + ${shlibs:Depends} +Breaks: knot-resolver-module-tinyweb (<< 1.1.0~git20160713-1~) +Description: HTTP module for Knot Resolver + The Knot Resolver is a caching full resolver implementation + written in C and LuaJIT, including both a resolver library and a + daemon. Modular architecture of the library keeps the core tiny and + efficient, and provides a state-machine like API for + extensions. There are three built-in modules - iterator, cache, + validator, and many external. + . + This package contains HTTP module for local visualization of the + resolver cache and queries. + +Package: knot-resolver-doc +Architecture: all +Multi-Arch: foreign +Section: doc +Depends: libjs-jquery, + libjs-underscore, + ${misc:Depends} +Description: Documentation for Knot Resolver + The Knot Resolver is a caching full resolver implementation + written in C and LuaJIT, including both a resolver library and a + daemon. Modular architecture of the library keeps the core tiny and + efficient, and provides a state-machine like API for + extensions. There are three built-in modules - iterator, cache, + validator, and many external. + . + This package contains Knot Resolver Documentation. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..dfcde1f --- /dev/null +++ b/debian/copyright @@ -0,0 +1,391 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: knot-resolver +Source: https://www.knot-resolver.cz/ + +Files: * +Copyright: CZ.NIC +License: GPL-3.0+ + +Files: contrib/ccan/asprintf/* +Copyright: Rusty Russell +License: Expat + +Files: contrib/ccan/compiler/* +Copyright: Rusty Russell +License: CC0 + +Files: contrib/ccan/json/* +Copyright: 2011 Joey Adams +License: Expat + +Files: contrib/murmurhash3/* +Copyright: Austin Appleby +License: CC0-1.0 + +Files: contrib/ucw/* +Copyright: 1997-2015 Martin Mares + 2005-2014 Tomas Valla + 2006 Robert Spalek + 2007-2015 Pavel Charvat +License: LGPL-2.1 + +Files: debian/* +Copyright: 2015 Ondřej Surý <ondrej@debian.org> + 2015-2019 CZ.NIC +License: GPL-3.0+ + +Files: debian/missing-sources/dygraph.js + modules/http/static/dygraph.min.js +Copyright: 2006-2017 Dan Vanderkam <danvdk@gmail.com> + 2016 Paul Miller + 2011 Robert Konigsberg <konigsberg@google.com> + 2013 David Eberlein <david.eberlein@ch.sauter-bc.com> +License: MIT +Comment: upstream site: http://dygraphs.com/download.html + +Files: lib/generic/map.c lib/generic/map.h +Copyright: Dan Bernstein + Jonas Gehring + Adam Langley + Marek Vavrusa +License: public-domain + +Files: modules/policy/lua-aho-corasick/* +Copyright: 2013 CloudFlare, Inc. +License: BSD-3-CloudFlare + +Files: modules/http/static/jquery.js +Copyright: 2005-2011 John Resig, Brandon Aaron & Jörn Zaefferer +License: GPL-2 or Expat + +Files: modules/http/static/d3.js + modules/http/static/topojson.js +Copyright: 2010-2015 Michael Bostock +License: BSD-3-clause + +Files: modules/http/static/epoch.* + debian/missing-sources/epoch/* + debian/missing-sources/epoch.* +Copyright: 2014 Fastly, Inc. +License: Expat + +Files: modules/http/static/datamaps.world.min.js +Copyright: 2012 Mark DiMarco +License: Expat + +Files: modules/http/static/bootstrap.min.css + modules/http/static/bootstrap.min.js + modules/http/static/bootstrap-theme.min.css + modules/http/static/glyphicons-halflings-regular.woff2 +Copyright: 2012-2016 Thomas Park + 2011-2015 Twitter, Inc. +License: Expat + +Files: modules/http/static/selectize.bootstrap3.css + modules/http/static/selectize.min.js +Copyright: 2013–2015 Brian Reavis & contributors +License: Apache-2.0 + +Files: tests/config/tapered/* +Copyright: 2012-2017, Peter Aronoff +License: BSD-3-clause + +License: LGPL-2.1 + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + . + This library 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 + Lesser General Public License for more details. + . + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see <https://www.gnu.org/licenses/>. + . + On Debian systems, the complete text of the GNU General + Public License version 3 can be found in "/usr/share/common-licenses/LGPL-2.1". + +License: GPL-3.0+ + 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 package 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 <https://www.gnu.org/licenses/>. + . + On Debian systems, the complete text of the GNU General + Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". + +License: Expat + 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: CC0 + Statement of Purpose + . + The laws of most jurisdictions throughout the world automatically + confer exclusive Copyright and Related Rights (defined below) upon + the creator and subsequent owner(s) (each and all, an "owner") of an + original work of authorship and/or a database (each, a "Work"). + . + Certain owners wish to permanently relinquish those rights to a Work + for the purpose of contributing to a commons of creative, cultural + and scientific works ("Commons") that the public can reliably and + without fear of later claims of infringement build upon, modify, + incorporate in other works, reuse and redistribute as freely as + possible in any form whatsoever and for any purposes, including + without limitation commercial purposes. These owners may contribute + to the Commons to promote the ideal of a free culture and the further + production of creative, cultural and scientific works, or to gain + reputation or greater distribution for their Work in part through the + use and efforts of others. + . + For these and/or other purposes and motivations, and without any + expectation of additional consideration or compensation, the person + associating CC0 with a Work (the "Affirmer"), to the extent that he + or she is an owner of Copyright and Related Rights in the Work, + voluntarily elects to apply CC0 to the Work and publicly distribute + the Work under its terms, with knowledge of his or her Copyright and + Related Rights in the Work and the meaning and intended legal effect + of CC0 on those rights. + . + 1. Copyright and Related Rights. A Work made available under CC0 may + be protected by copyright and related or neighboring rights + ("Copyright and Related Rights"). Copyright and Related Rights + include, but are not limited to, the following: + . + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or + performer(s); + iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a + Work, subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse + of data in a Work; + vi. database rights (such as those arising under Directive 96/9/EC + of the European Parliament and of the Council of 11 March 1996 + on the legal protection of databases, and under any national + implementation thereof, including any amended or successor + version of such directive); and + vii. other similar, equivalent or corresponding rights throughout + the world based on applicable law or treaty, and any national + implementations thereof. + . + 2. Waiver. To the greatest extent permitted by, but not in + contravention of, applicable law, Affirmer hereby overtly, fully, + permanently, irrevocably and unconditionally waives, abandons, and + surrenders all of Affirmer's Copyright and Related Rights and + associated claims and causes of action, whether now known or + unknown (including existing as well as future claims and causes of + action), in the Work (i) in all territories worldwide, (ii) for + the maximum duration provided by applicable law or treaty + (including future time extensions), (iii) in any current or future + medium and for any number of copies, and (iv) for any purpose + whatsoever, including without limitation commercial, advertising + or promotional purposes (the "Waiver"). Affirmer makes the Waiver + for the benefit of each member of the public at large and to the + detriment of Affirmer's heirs and successors, fully intending that + such Waiver shall not be subject to revocation, rescission, + cancellation, termination, or any other legal or equitable action + to disrupt the quiet enjoyment of the Work by the public as + contemplated by Affirmer's express Statement of Purpose. + . + 3. Public License Fallback. Should any part of the Waiver for any + reason be judged legally invalid or ineffective under applicable + law, then the Waiver shall be preserved to the maximum extent + permitted taking into account Affirmer's express Statement of + Purpose. In addition, to the extent the Waiver is so judged + Affirmer hereby grants to each affected person a royalty-free, non + transferable, non sublicensable, non exclusive, irrevocable and + unconditional license to exercise Affirmer's Copyright and Related + Rights in the Work (i) in all territories worldwide, (ii) for the + maximum duration provided by applicable law or treaty (including + future time extensions), (iii) in any current or future medium and + for any number of copies, and (iv) for any purpose whatsoever, + including without limitation commercial, advertising or + promotional purposes (the "License"). The License shall be deemed + effective as of the date CC0 was applied by Affirmer to the + Work. Should any part of the License for any reason be judged + legally invalid or ineffective under applicable law, such partial + invalidity or ineffectiveness shall not invalidate the remainder + of the License, and in such case Affirmer hereby affirms that he + or she will not (i) exercise any of his or her remaining Copyright + and Related Rights in the Work or (ii) assert any associated + claims and causes of action with respect to the Work, in either + case contrary to Affirmer's express Statement of Purpose. + . + 4. Limitations and Disclaimers. + . + a. No trademark or patent rights held by Affirmer are waived, + abandoned, surrendered, licensed or otherwise affected by this + document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties + of title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, + accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under + applicable law. + c. Affirmer disclaims responsibility for clearing rights of other + persons that may apply to the Work or any use thereof, including + without limitation any person's Copyright and Related Rights in + the Work. Further, Affirmer disclaims responsibility for + obtaining any necessary consents, permissions or other rights + required for any use of the Work. + d. Affirmer understands and acknowledges that Creative Commons is + not a party to this document and has no duty or obligation with + respect to this CC0 or use of the Work. + +License: BSD-3-CloudFlare + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + . + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + . + 2. 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. + . + 3. Neither the name of CloudFlare, Inc. 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: BSD-3-clause + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + . + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + . + 2. 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. + . + 3. Neither the name of the author 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 AUTHOR 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 AUTHOR 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 + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + . + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + . + On Debian systems, the complete text of the GNU General Public + License version 2 can be found in "/usr/share/common-licenses/GPL-2". + +License: Apache-2.0 + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + . + https://www.apache.org/licenses/LICENSE-2.0 + . + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + . + On Debian systems, the full text of the Apache Software License version 2 can + be found in the file `/usr/share/common-licenses/Apache-2.0'. + +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: CC0-1.0 + This work is licensed under the "Creative Commons Zero" license. + . + On debian systems, a copy of the Creative Commons Zero license may be + found at /usr/share/common-licenses/CC0-1.0. + +License: public-domain + This work has been released into the public domain. The map + implementation builds off of prior public domain work from Dan + Bernstein (qhasm) and Adam Langley (critbit). diff --git a/debian/docs b/debian/docs new file mode 100644 index 0000000..b43bf86 --- /dev/null +++ b/debian/docs @@ -0,0 +1 @@ +README.md diff --git a/debian/gbp.conf b/debian/gbp.conf new file mode 100644 index 0000000..ff17343 --- /dev/null +++ b/debian/gbp.conf @@ -0,0 +1,10 @@ +[DEFAULT] +debian-branch = debian/master +debian-tag = debian/%(version)s +upstream-branch = upstream +upstream-tag = upstream/%(version)s +upstream-vcs-tag = v%(version)s +pristine-tar = True + +[dch] +meta = 1 diff --git a/debian/init-d-script b/debian/init-d-script new file mode 100755 index 0000000..334dc32 --- /dev/null +++ b/debian/init-d-script @@ -0,0 +1,239 @@ +#!/bin/sh +# See init-d-script(5) for instructions on how to use this library. +#============================================================================= +# Define LSB log_* functions. +# Depend on lsb-base (>= 3.2-14) to ensure that this file is present +# and status_of_proc is working. +. /lib/lsb/init-functions + +# PATH should only include /usr/* if it runs after the mountnfs.sh +# script. Scripts running before mountnfs.sh should remove the /usr/* +# entries. +PATH=/sbin:/usr/sbin:/bin:/usr/bin +export PATH + +is_call_implemented() { + command -V $1 > /dev/null 2>&1 +} + +do_usage() { + if is_call_implemented do_reload ; then + echo "Usage: $SCRIPTNAME {start|stop|status|reload|restart|try-restart|force-reload}" >&2 + else + echo "Usage: $SCRIPTNAME {start|stop|status|restart|try-restart|force-reload}" >&2 + fi +} + +call() { + cmd="$1" + shift + if is_call_implemented ${cmd}_override ; then + ${cmd}_override "$@" + else + ${cmd} "$@" + fi +} + +# +# Function that starts the daemon/service +# + +# Return +# 0 if daemon has been started +# 1 if daemon was already running +# 2 if daemon could not be started +do_start_cmd() { + start-stop-daemon --start --quiet ${PIDFILE:+--pidfile ${PIDFILE}} \ + $START_ARGS \ + --startas $DAEMON --name $NAME --exec $DAEMON --test > /dev/null \ + || return 1 + start-stop-daemon --start --quiet ${PIDFILE:+--pidfile ${PIDFILE}} \ + $START_ARGS \ + --startas $DAEMON --name $NAME --exec $DAEMON -- $DAEMON_ARGS \ + || return 2 + # Add code here, if necessary, that waits for the process to be ready + # to handle requests from services started subsequently which depend + # on this one. As a last resort, sleep for some time. +} + +do_start() +{ + if is_call_implemented do_start_prepare ; then + call do_start_prepare + fi + [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" + call do_start_cmd + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + if is_call_implemented do_start_cleanup ; then + call do_start_cleanup + fi +} + +# +# Function that stops the daemon/service +# + +# Return +# 0 if daemon has been stopped +# 1 if daemon was already stopped +# 2 if daemon could not be stopped +# other if a failure occurred +do_stop_cmd() { + start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 \ + $STOP_ARGS \ + ${PIDFILE:+--pidfile ${PIDFILE}} --name $NAME --exec $DAEMON + RETVAL="$?" + [ "$RETVAL" = 2 ] && return 2 + # Wait for children to finish too if this is a daemon that forks + # and if the daemon is only ever run from this initscript. + # If the above conditions are not satisfied then add some other code + # that waits for the process to drop all resources that could be + # needed by services started subsequently. A last resort is to + # sleep for some time. + start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 \ + $STOP_ARGS \ + --exec $DAEMON + [ "$?" = 2 ] && return 2 + # Many daemons don't delete their pidfiles when they exit. + rm -f $PIDFILE + return $RETVAL +} + +do_stop() +{ + if is_call_implemented do_stop_prepare ; then + call do_stop_prepare + fi + [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" + call do_stop_cmd + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + if is_call_implemented do_stop_cleanup ; then + call do_stop_cleanup + fi +} + +do_restart() { + [ "$VERBOSE" != no ] && log_daemon_msg "Restarting $DESC" "$NAME" + call do_stop_cmd + call do_start_cmd + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac +} + +do_force_reload() { + if is_call_implemented do_reload ; then + call do_reload + else + call do_restart + fi +} + +# Enable this using +# alias do_reload=do_reload_sigusr1 +do_reload_sigusr1() { + log_daemon_msg "Reloading $DESC configuration files" "$NAME" + start-stop-daemon --oknodo --stop --signal 1 --quiet \ + --pidfile "$PIDFILE" --exec "$DAEMON" + log_end_msg $? +} + +do_status() { + status_of_proc "$DAEMON" "$NAME" && return 0 || return $? +} + +if [ "$DEBUG" = "true" ] ; then + set -x +fi + +SCRIPTNAME=$1 +scriptbasename="$(basename $1)" +if [ "$scriptbasename" != "init-d-script" ] ; then + script="$1" + shift + . $script +else + exit 0 +fi + +NAME=${NAME:=$(basename $DAEMON)} +DESC=${DESC:=$NAME} + +# Do not use pid file if $PIDFILE is 'none'. Otherwise, generate from +# $NAME or use the value provided by the init.d script. +if [ none = "$PIDFILE" ] ; then + PIDFILE= +elif [ -z "$PIDFILE" ] ; then + PIDFILE=/var/run/$NAME.pid +fi + +# Exit if the package is not installed +if [ none != "$DAEMON" ] && [ ! -x "$DAEMON" ] ; then + exit 0 +fi + +# Read configuration variable file if it is present +[ -r /etc/default/$NAME ] && . /etc/default/$NAME + +# Load the VERBOSE setting and other rcS variables +. /lib/init/vars.sh +if [ -t 0 ] ; then # Be verbose when called from a terminal + VERBOSE=yes +fi + +case "$1" in + start) + call do_start + ;; + stop) + call do_stop + ;; + status) + call do_status + ;; + reload) + if is_call_implemented do_reload ; then + do_reload + else + call do_usage + exit 3 + fi + ;; + force-reload) + call do_force_reload + ;; + restart) + call do_restart + ;; + try-restart) + log_daemon_msg "Trying to restart $DESC" "$NAME" + if call do_status > /dev/null 2>&1 ; then + call do_restart + log_end_msg $? + else + log_progress_msg "is not running." + log_end_msg 1 + fi + ;; + '') + call do_usage + exit 3 + ;; + *) + if is_call_implemented do_unknown ; then + call do_unknown "$1" + exit 3 + else + call do_usage + exit 3 + fi + ;; +esac +exit 0 diff --git a/debian/knot-resolver-doc.doc-base b/debian/knot-resolver-doc.doc-base new file mode 100644 index 0000000..32b415d --- /dev/null +++ b/debian/knot-resolver-doc.doc-base @@ -0,0 +1,12 @@ +Document: knot-resolver +Title: Knot Resolver documentation +Author: CZ.NIC labs +Abstract: Documentation for the Knot Resolver, + a caching recursive DNS resolver. Documentation + includes building from source, using the library, + and configuration and operation of the daemon. +Section: Network/Communication + +Format: HTML +Index: /usr/share/doc/knot-resolver/index.html +Files: /usr/share/doc/knot-resolver/*.html diff --git a/debian/knot-resolver-doc.docs b/debian/knot-resolver-doc.docs new file mode 100644 index 0000000..ac44141 --- /dev/null +++ b/debian/knot-resolver-doc.docs @@ -0,0 +1 @@ +usr/share/doc/knot-resolver/html/* diff --git a/debian/knot-resolver-doc.links b/debian/knot-resolver-doc.links new file mode 100644 index 0000000..4c7c74f --- /dev/null +++ b/debian/knot-resolver-doc.links @@ -0,0 +1,2 @@ +usr/share/javascript/jquery/jquery.min.js usr/share/doc/knot-resolver/_static/jquery.js +usr/share/javascript/underscore/underscore.min.js usr/share/doc/knot-resolver/_static/underscore.js diff --git a/debian/knot-resolver-module-http.install b/debian/knot-resolver-module-http.install new file mode 100644 index 0000000..0875a3f --- /dev/null +++ b/debian/knot-resolver-module-http.install @@ -0,0 +1,7 @@ +usr/lib/*/knot-resolver/kres_modules/http*.lua +usr/lib/*/knot-resolver/kres_modules/http/*.css +usr/lib/*/knot-resolver/kres_modules/http/*.ico +usr/lib/*/knot-resolver/kres_modules/http/*.js +usr/lib/*/knot-resolver/kres_modules/http/*.tpl +usr/lib/*/knot-resolver/kres_modules/http/*.woff2 +usr/lib/*/knot-resolver/kres_modules/prometheus.lua diff --git a/debian/knot-resolver-module-http.links b/debian/knot-resolver-module-http.links new file mode 100644 index 0000000..d651f6f --- /dev/null +++ b/debian/knot-resolver-module-http.links @@ -0,0 +1,6 @@ +/usr/share/fonts-glyphicons/glyphicons-halflings-regular.woff2 /usr/lib/knot-resolver/http/glyphicons-halflings-regular.woff2 +/usr/share/javascript/bootstrap/css/bootstrap-theme.min.css /usr/lib/knot-resolver/http/bootstrap-theme.min.css +/usr/share/javascript/bootstrap/css/bootstrap.min.css /usr/lib/knot-resolver/http/bootstrap.min.css +/usr/share/javascript/bootstrap/js/bootstrap.min.js /usr/lib/knot-resolver/http/bootstrap.min.js +/usr/share/javascript/d3/d3.min.js /usr/lib/knot-resolver/http/d3.js +/usr/share/javascript/jquery/jquery.min.js /usr/lib/knot-resolver/http/jquery.js diff --git a/debian/knot-resolver-module-tinyweb.install b/debian/knot-resolver-module-tinyweb.install new file mode 100644 index 0000000..905241c --- /dev/null +++ b/debian/knot-resolver-module-tinyweb.install @@ -0,0 +1 @@ +usr/lib/knot-resolver/tinyweb/* diff --git a/debian/knot-resolver.dirs b/debian/knot-resolver.dirs new file mode 100644 index 0000000..472b32a --- /dev/null +++ b/debian/knot-resolver.dirs @@ -0,0 +1,3 @@ +/etc/knot-resolver +/usr/share/doc/knot-resolver/examples/ +/var/lib/knot-resolver diff --git a/debian/knot-resolver.install b/debian/knot-resolver.install new file mode 100644 index 0000000..11c4ca8 --- /dev/null +++ b/debian/knot-resolver.install @@ -0,0 +1,36 @@ +etc/knot-resolver/kresd.conf +usr/lib/*/*.so.* +usr/lib/*/knot-resolver/*.lua +usr/lib/*/knot-resolver/*.so +usr/lib/*/knot-resolver/kres_modules/*.so +usr/lib/*/knot-resolver/kres_modules/daf.lua +usr/lib/*/knot-resolver/kres_modules/daf/* +usr/lib/*/knot-resolver/kres_modules/detect_time_jump.lua +usr/lib/*/knot-resolver/kres_modules/detect_time_skew.lua +usr/lib/*/knot-resolver/kres_modules/dns64.lua +usr/lib/*/knot-resolver/kres_modules/etcd.lua +usr/lib/*/knot-resolver/kres_modules/experimental_dot_auth.lua +usr/lib/*/knot-resolver/kres_modules/graphite.lua +usr/lib/*/knot-resolver/kres_modules/policy.lua +usr/lib/*/knot-resolver/kres_modules/predict.lua +usr/lib/*/knot-resolver/kres_modules/prefill.lua +usr/lib/*/knot-resolver/kres_modules/priming.lua +usr/lib/*/knot-resolver/kres_modules/rebinding.lua +usr/lib/*/knot-resolver/kres_modules/renumber.lua +usr/lib/*/knot-resolver/kres_modules/serve_stale.lua +usr/lib/*/knot-resolver/kres_modules/ta_sentinel.lua +usr/lib/*/knot-resolver/kres_modules/ta_signal_query.lua +usr/lib/*/knot-resolver/kres_modules/ta_update.lua +usr/lib/*/knot-resolver/kres_modules/view.lua +usr/lib/*/knot-resolver/kres_modules/watchdog.lua +usr/lib/*/knot-resolver/kres_modules/workarounds.lua +usr/lib/systemd/system/kres-cache-gc.service lib/systemd/system/ +usr/lib/systemd/system/kresd.target lib/systemd/system/ +usr/lib/systemd/system/kresd@.service lib/systemd/system/ +usr/lib/tmpfiles.d/knot-resolver.conf +usr/sbin/kres-cache-gc +usr/sbin/kresc +usr/sbin/kresd +usr/share/doc/knot-resolver/examples +usr/share/man/man7/kresd.systemd.7 +usr/share/man/man8/kresd.8 diff --git a/debian/knot-resolver.kresd.default b/debian/knot-resolver.kresd.default new file mode 100644 index 0000000..f7c46e5 --- /dev/null +++ b/debian/knot-resolver.kresd.default @@ -0,0 +1,13 @@ +# /etc/default/kresd + +# This file is used only under sysvinit. If you use systemd and you +# want to modify the arguments with which kresd is invoked, you should +# instead use "systemctl edit kresd@.service" to override ExecStart= +# in the [Service] section (see kresd.systemd(7) for more details). + +# For sysvinit users: KRESD_ARGS used to exist for historical reasons, +# but that variable is deprecated and may stop working at some point +# in the future. You are encouraged to merge any local changes into +# DAEMON_ARGS directly. + +DAEMON_ARGS="--config=/etc/knot-resolver/kresd.conf --addr=127.0.0.1#53 --addr=::1#53 $KRESD_ARGS" diff --git a/debian/knot-resolver.kresd.init b/debian/knot-resolver.kresd.init new file mode 100644 index 0000000..b1361fd --- /dev/null +++ b/debian/knot-resolver.kresd.init @@ -0,0 +1,59 @@ +#!/bin/sh +# kFreeBSD do not accept scripts as interpreters, using #!/bin/sh and sourcing. +if [ true != "$INIT_D_SCRIPT_SOURCED" ] ; then + if [ -x /lib/init/init-d-script ]; then + set "$0" "$@"; INIT_D_SCRIPT_SOURCED=true . /lib/init/init-d-script + else + set "$0" "$@"; INIT_D_SCRIPT_SOURCED=true . /usr/lib/knot-resolver/init-d-script + fi +fi +### BEGIN INIT INFO +# Provides: kresd +# Required-Start: $remote_fs $syslog +# Required-Stop: $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Knot Resolver +# Description: Knot Resolver +### END INIT INFO + +# Author: Ondřej Surý <ondrej@debian.org> + +NAME=kresd +DESC="Knot Resolver" +DAEMON=/usr/sbin/kresd +START_ARGS="--background --make-pidfile" + +do_tmpfiles() { + local tmpfile type path mode user group age argument + tmpfile=/usr/lib/tmpfiles.d/$1.conf + if [ -r "$tmpfile" ]; then + if [ -x /bin/systemd-tmpfiles ]; then + /bin/systemd-tmpfiles --create "$tmpfile" + else + while read type path mode user group age argument; do + case "$type" in + d) + mkdir -p "$path"; + chmod "$mode" "$path"; + chown "$user:$group" "$path"; + ;; + L) + if [ ! -e "$path" ]; then ln -s "$argument" "$path"; fi + ;; + \#*) + ;; + *) + log_warning_msg "tmpfile.d type '$type' is not supported yet" + ;; + esac + done < "$tmpfile" + fi + else + log_warning_msg "tmpfiles.d file '$1' doesn't exist or is not readable" + fi +} + +do_start_prepare() { + do_tmpfiles knot-resolver +} diff --git a/debian/knot-resolver.links b/debian/knot-resolver.links new file mode 100644 index 0000000..8196524 --- /dev/null +++ b/debian/knot-resolver.links @@ -0,0 +1,2 @@ +dev/null lib/systemd/system/kresd.service +lib/systemd/system/kresd.target lib/systemd/system/multi-user.target.wants/kresd.target diff --git a/debian/knot-resolver.lintian-overrides b/debian/knot-resolver.lintian-overrides new file mode 100644 index 0000000..5e0d7c3 --- /dev/null +++ b/debian/knot-resolver.lintian-overrides @@ -0,0 +1,9 @@ +# debian uses kresd.target recommended by both knot-resolver upstream and systemd upstream +# see https://gitlab.labs.nic.cz/knot/knot-resolver/merge_requests/597 +# and https://github.com/systemd/systemd/issues/9080#issuecomment-393552613 +knot-resolver: systemd-service-file-refers-to-unusual-wantedby-target lib/systemd/system/kresd@.service kresd.target +# libkres9 is not currently functional independently. see https://bugs.debian.org/923970 +knot-resolver: package-name-doesnt-match-sonames libkres9 +knot-resolver: no-symbols-control-file usr/lib/*/libkres.so.9 +# Current use of debconf no required config - 2020-12-14 +knot-resolver: no-debconf-config diff --git a/debian/knot-resolver.postinst.in b/debian/knot-resolver.postinst.in new file mode 100644 index 0000000..d403ff4 --- /dev/null +++ b/debian/knot-resolver.postinst.in @@ -0,0 +1,43 @@ +#!/bin/sh +set -e + +# Source debconf library. +. /usr/share/debconf/confmodule + +# In Debian, this should be upgrade from 3-to-5, but the following code +# and the upgrade-4-to-5.lua file come from upstream. So I keep it like +# this for uniformity. Also, the issue relies on the lack of support on +# systemd sockets since 5.x. +# upgrade-4-to-5 +export UPG_DIR=/var/lib/knot-resolver/.upgrade-4-to-5 +if [ -f ${UPG_DIR}/.unfinished ] ; then + rm -f ${UPG_DIR}/.unfinished + kresd -c /usr/lib/@DEB_HOST_MULTIARCH@/knot-resolver/upgrade-4-to-5.lua >/dev/null 2>/dev/null + db_input high knot-resolver/upgrade-buster-to-bullseye || true + db_go || true + cat ${UPG_DIR}/kresd.conf.net 2>/dev/null +fi + +if [ "$1" = "configure" ]; then + adduser --quiet --system --group --no-create-home --home /var/cache/knot-resolver knot-resolver +fi + +# Restart any running kresd instances if the root key is updated. +# Note: if knot-resolver upstream watches this file and reloads it +# upon a change, we can and should remove this trigger. +if [ "$1" = "triggered" ]; then + if [ "$2" = "/usr/share/dns/root.key" ]; then + # systemctl of the sub-services is the preferred method to restart + deb-systemd-invoke try-restart 'kresd@*.service' || true + # but if we are running sysvinit, we can try to restart that process anyway + # (kresd.service is masked on systems that use systemd) + invoke-rc.d kresd try-restart || true + fi +fi + +if [ "$1" = "configure" ] || [ "$1" = "abort-upgrade" ] || [ "$1" = "abort-deconfigure" ] || [ "$1" = "abort-remove" ] ; then + deb-systemd-invoke try-restart 'kresd@*.service' || true + invoke-rc.d kresd try-restart || true +fi + +#DEBHELPER# diff --git a/debian/knot-resolver.postrm b/debian/knot-resolver.postrm new file mode 100644 index 0000000..78f3b6f --- /dev/null +++ b/debian/knot-resolver.postrm @@ -0,0 +1,8 @@ +#!/bin/sh +set -e + +if [ "$1" = "remove" ]; then + deb-systemd-invoke stop system-kresd.slice || true +fi + +#DEBHELPER# diff --git a/debian/knot-resolver.preinst b/debian/knot-resolver.preinst new file mode 100644 index 0000000..c6df754 --- /dev/null +++ b/debian/knot-resolver.preinst @@ -0,0 +1,29 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-3.0-or-later +set -e + +# upgrade-4-to-5 +if [ -f /lib/systemd/system/kresd.socket ] ; then + export UPG_DIR=/var/lib/knot-resolver/.upgrade-4-to-5 + mkdir -p ${UPG_DIR} + touch ${UPG_DIR}/.unfinished + + for sock in kresd.socket kresd-tls.socket ; do + if deb-systemd-helper is-enabled ${sock} 2>/dev/null | grep -qv masked ; then + #TODO: handle systems not running systemd + systemctl show ${sock} -p Listen > ${UPG_DIR}/${sock} + case "$(systemctl show ${sock} -p BindIPv6Only)" in + *ipv6-only) + touch ${UPG_DIR}/${sock}.v6only + ;; + *default) + if cat /proc/sys/net/ipv6/bindv6only | grep -q 1 ; then + touch ${UPG_DIR}/${sock}.v6only + fi + ;; + esac + fi + done +fi + +#DEBHELPER# diff --git a/debian/knot-resolver.templates b/debian/knot-resolver.templates new file mode 100644 index 0000000..931934b --- /dev/null +++ b/debian/knot-resolver.templates @@ -0,0 +1,17 @@ +Template: knot-resolver/upgrade-buster-to-bullseye +Type: note +_Description: Upgrading from Knot Resolver < 5.x + Knot Resolver configuration file requires manual upgrade. + . + Up to Knot Resolver 4.x, network interface bindings and service + start were done by default via systemd sockets. These systemd sockets + are no longer supported, and upgrading from a 3.x version (as in Debian + Buster) requires manual action to (re)enable the service. + . + Please refer to kresd.systemd(7) and to the + /usr/share/doc/knot-resolver/upgrading.html file (from + knot-resolver-doc). An online version of the latter is available at + https://knot-resolver.readthedocs.io/en/stable/upgrading.html#x-to-5-x + . + For convenience, a suggested networking configuration can be found in + the file /var/lib/knot-resolver/.upgrade-4-to-5/kresd.conf.net diff --git a/debian/knot-resolver.triggers b/debian/knot-resolver.triggers new file mode 100644 index 0000000..e8d8246 --- /dev/null +++ b/debian/knot-resolver.triggers @@ -0,0 +1 @@ +interest-noawait /usr/share/dns/root.key diff --git a/debian/missing-sources/bootstrap-theme.css b/debian/missing-sources/bootstrap-theme.css new file mode 100644 index 0000000..ebe57fb --- /dev/null +++ b/debian/missing-sources/bootstrap-theme.css @@ -0,0 +1,587 @@ +/*! + * Bootstrap v3.3.6 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +.btn-default, +.btn-primary, +.btn-success, +.btn-info, +.btn-warning, +.btn-danger { + text-shadow: 0 -1px 0 rgba(0, 0, 0, .2); + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); +} +.btn-default:active, +.btn-primary:active, +.btn-success:active, +.btn-info:active, +.btn-warning:active, +.btn-danger:active, +.btn-default.active, +.btn-primary.active, +.btn-success.active, +.btn-info.active, +.btn-warning.active, +.btn-danger.active { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); +} +.btn-default.disabled, +.btn-primary.disabled, +.btn-success.disabled, +.btn-info.disabled, +.btn-warning.disabled, +.btn-danger.disabled, +.btn-default[disabled], +.btn-primary[disabled], +.btn-success[disabled], +.btn-info[disabled], +.btn-warning[disabled], +.btn-danger[disabled], +fieldset[disabled] .btn-default, +fieldset[disabled] .btn-primary, +fieldset[disabled] .btn-success, +fieldset[disabled] .btn-info, +fieldset[disabled] .btn-warning, +fieldset[disabled] .btn-danger { + -webkit-box-shadow: none; + box-shadow: none; +} +.btn-default .badge, +.btn-primary .badge, +.btn-success .badge, +.btn-info .badge, +.btn-warning .badge, +.btn-danger .badge { + text-shadow: none; +} +.btn:active, +.btn.active { + background-image: none; +} +.btn-default { + text-shadow: 0 1px 0 #fff; + background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%); + background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0)); + background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #dbdbdb; + border-color: #ccc; +} +.btn-default:hover, +.btn-default:focus { + background-color: #e0e0e0; + background-position: 0 -15px; +} +.btn-default:active, +.btn-default.active { + background-color: #e0e0e0; + border-color: #dbdbdb; +} +.btn-default.disabled, +.btn-default[disabled], +fieldset[disabled] .btn-default, +.btn-default.disabled:hover, +.btn-default[disabled]:hover, +fieldset[disabled] .btn-default:hover, +.btn-default.disabled:focus, +.btn-default[disabled]:focus, +fieldset[disabled] .btn-default:focus, +.btn-default.disabled.focus, +.btn-default[disabled].focus, +fieldset[disabled] .btn-default.focus, +.btn-default.disabled:active, +.btn-default[disabled]:active, +fieldset[disabled] .btn-default:active, +.btn-default.disabled.active, +.btn-default[disabled].active, +fieldset[disabled] .btn-default.active { + background-color: #e0e0e0; + background-image: none; +} +.btn-primary { + background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88)); + background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #245580; +} +.btn-primary:hover, +.btn-primary:focus { + background-color: #265a88; + background-position: 0 -15px; +} +.btn-primary:active, +.btn-primary.active { + background-color: #265a88; + border-color: #245580; +} +.btn-primary.disabled, +.btn-primary[disabled], +fieldset[disabled] .btn-primary, +.btn-primary.disabled:hover, +.btn-primary[disabled]:hover, +fieldset[disabled] .btn-primary:hover, +.btn-primary.disabled:focus, +.btn-primary[disabled]:focus, +fieldset[disabled] .btn-primary:focus, +.btn-primary.disabled.focus, +.btn-primary[disabled].focus, +fieldset[disabled] .btn-primary.focus, +.btn-primary.disabled:active, +.btn-primary[disabled]:active, +fieldset[disabled] .btn-primary:active, +.btn-primary.disabled.active, +.btn-primary[disabled].active, +fieldset[disabled] .btn-primary.active { + background-color: #265a88; + background-image: none; +} +.btn-success { + background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%); + background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641)); + background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #3e8f3e; +} +.btn-success:hover, +.btn-success:focus { + background-color: #419641; + background-position: 0 -15px; +} +.btn-success:active, +.btn-success.active { + background-color: #419641; + border-color: #3e8f3e; +} +.btn-success.disabled, +.btn-success[disabled], +fieldset[disabled] .btn-success, +.btn-success.disabled:hover, +.btn-success[disabled]:hover, +fieldset[disabled] .btn-success:hover, +.btn-success.disabled:focus, +.btn-success[disabled]:focus, +fieldset[disabled] .btn-success:focus, +.btn-success.disabled.focus, +.btn-success[disabled].focus, +fieldset[disabled] .btn-success.focus, +.btn-success.disabled:active, +.btn-success[disabled]:active, +fieldset[disabled] .btn-success:active, +.btn-success.disabled.active, +.btn-success[disabled].active, +fieldset[disabled] .btn-success.active { + background-color: #419641; + background-image: none; +} +.btn-info { + background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); + background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2)); + background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #28a4c9; +} +.btn-info:hover, +.btn-info:focus { + background-color: #2aabd2; + background-position: 0 -15px; +} +.btn-info:active, +.btn-info.active { + background-color: #2aabd2; + border-color: #28a4c9; +} +.btn-info.disabled, +.btn-info[disabled], +fieldset[disabled] .btn-info, +.btn-info.disabled:hover, +.btn-info[disabled]:hover, +fieldset[disabled] .btn-info:hover, +.btn-info.disabled:focus, +.btn-info[disabled]:focus, +fieldset[disabled] .btn-info:focus, +.btn-info.disabled.focus, +.btn-info[disabled].focus, +fieldset[disabled] .btn-info.focus, +.btn-info.disabled:active, +.btn-info[disabled]:active, +fieldset[disabled] .btn-info:active, +.btn-info.disabled.active, +.btn-info[disabled].active, +fieldset[disabled] .btn-info.active { + background-color: #2aabd2; + background-image: none; +} +.btn-warning { + background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); + background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316)); + background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #e38d13; +} +.btn-warning:hover, +.btn-warning:focus { + background-color: #eb9316; + background-position: 0 -15px; +} +.btn-warning:active, +.btn-warning.active { + background-color: #eb9316; + border-color: #e38d13; +} +.btn-warning.disabled, +.btn-warning[disabled], +fieldset[disabled] .btn-warning, +.btn-warning.disabled:hover, +.btn-warning[disabled]:hover, +fieldset[disabled] .btn-warning:hover, +.btn-warning.disabled:focus, +.btn-warning[disabled]:focus, +fieldset[disabled] .btn-warning:focus, +.btn-warning.disabled.focus, +.btn-warning[disabled].focus, +fieldset[disabled] .btn-warning.focus, +.btn-warning.disabled:active, +.btn-warning[disabled]:active, +fieldset[disabled] .btn-warning:active, +.btn-warning.disabled.active, +.btn-warning[disabled].active, +fieldset[disabled] .btn-warning.active { + background-color: #eb9316; + background-image: none; +} +.btn-danger { + background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%); + background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a)); + background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #b92c28; +} +.btn-danger:hover, +.btn-danger:focus { + background-color: #c12e2a; + background-position: 0 -15px; +} +.btn-danger:active, +.btn-danger.active { + background-color: #c12e2a; + border-color: #b92c28; +} +.btn-danger.disabled, +.btn-danger[disabled], +fieldset[disabled] .btn-danger, +.btn-danger.disabled:hover, +.btn-danger[disabled]:hover, +fieldset[disabled] .btn-danger:hover, +.btn-danger.disabled:focus, +.btn-danger[disabled]:focus, +fieldset[disabled] .btn-danger:focus, +.btn-danger.disabled.focus, +.btn-danger[disabled].focus, +fieldset[disabled] .btn-danger.focus, +.btn-danger.disabled:active, +.btn-danger[disabled]:active, +fieldset[disabled] .btn-danger:active, +.btn-danger.disabled.active, +.btn-danger[disabled].active, +fieldset[disabled] .btn-danger.active { + background-color: #c12e2a; + background-image: none; +} +.thumbnail, +.img-thumbnail { + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); + box-shadow: 0 1px 2px rgba(0, 0, 0, .075); +} +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus { + background-color: #e8e8e8; + background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); + background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); + background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); + background-repeat: repeat-x; +} +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + background-color: #2e6da4; + background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); + background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); + background-repeat: repeat-x; +} +.navbar-default { + background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%); + background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8)); + background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); +} +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .active > a { + background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); + background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2)); + background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0); + background-repeat: repeat-x; + -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); + box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); +} +.navbar-brand, +.navbar-nav > li > a { + text-shadow: 0 1px 0 rgba(255, 255, 255, .25); +} +.navbar-inverse { + background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%); + background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222)); + background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-radius: 4px; +} +.navbar-inverse .navbar-nav > .open > a, +.navbar-inverse .navbar-nav > .active > a { + background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%); + background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f)); + background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0); + background-repeat: repeat-x; + -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); + box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); +} +.navbar-inverse .navbar-brand, +.navbar-inverse .navbar-nav > li > a { + text-shadow: 0 -1px 0 rgba(0, 0, 0, .25); +} +.navbar-static-top, +.navbar-fixed-top, +.navbar-fixed-bottom { + border-radius: 0; +} +@media (max-width: 767px) { + .navbar .navbar-nav .open .dropdown-menu > .active > a, + .navbar .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #fff; + background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); + background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); + background-repeat: repeat-x; + } +} +.alert { + text-shadow: 0 1px 0 rgba(255, 255, 255, .2); + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); +} +.alert-success { + background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); + background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc)); + background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); + background-repeat: repeat-x; + border-color: #b2dba1; +} +.alert-info { + background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%); + background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0)); + background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); + background-repeat: repeat-x; + border-color: #9acfea; +} +.alert-warning { + background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); + background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0)); + background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); + background-repeat: repeat-x; + border-color: #f5e79e; +} +.alert-danger { + background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); + background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3)); + background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); + background-repeat: repeat-x; + border-color: #dca7a7; +} +.progress { + background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); + background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5)); + background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar { + background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090)); + background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar-success { + background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%); + background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44)); + background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar-info { + background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); + background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5)); + background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar-warning { + background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); + background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f)); + background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar-danger { + background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%); + background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c)); + background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar-striped { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.list-group { + border-radius: 4px; + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); + box-shadow: 0 1px 2px rgba(0, 0, 0, .075); +} +.list-group-item.active, +.list-group-item.active:hover, +.list-group-item.active:focus { + text-shadow: 0 -1px 0 #286090; + background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a)); + background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0); + background-repeat: repeat-x; + border-color: #2b669a; +} +.list-group-item.active .badge, +.list-group-item.active:hover .badge, +.list-group-item.active:focus .badge { + text-shadow: none; +} +.panel { + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05); + box-shadow: 0 1px 2px rgba(0, 0, 0, .05); +} +.panel-default > .panel-heading { + background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); + background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); + background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); + background-repeat: repeat-x; +} +.panel-primary > .panel-heading { + background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); + background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); + background-repeat: repeat-x; +} +.panel-success > .panel-heading { + background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); + background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6)); + background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); + background-repeat: repeat-x; +} +.panel-info > .panel-heading { + background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); + background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3)); + background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); + background-repeat: repeat-x; +} +.panel-warning > .panel-heading { + background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); + background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc)); + background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); + background-repeat: repeat-x; +} +.panel-danger > .panel-heading { + background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%); + background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc)); + background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); + background-repeat: repeat-x; +} +.well { + background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); + background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5)); + background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); + background-repeat: repeat-x; + border-color: #dcdcdc; + -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); +} +/*# sourceMappingURL=bootstrap-theme.css.map */ diff --git a/debian/missing-sources/bootstrap.css b/debian/missing-sources/bootstrap.css new file mode 100644 index 0000000..42c79d6 --- /dev/null +++ b/debian/missing-sources/bootstrap.css @@ -0,0 +1,6760 @@ +/*! + * Bootstrap v3.3.6 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ +html { + font-family: sans-serif; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} +body { + margin: 0; +} +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +menu, +nav, +section, +summary { + display: block; +} +audio, +canvas, +progress, +video { + display: inline-block; + vertical-align: baseline; +} +audio:not([controls]) { + display: none; + height: 0; +} +[hidden], +template { + display: none; +} +a { + background-color: transparent; +} +a:active, +a:hover { + outline: 0; +} +abbr[title] { + border-bottom: 1px dotted; +} +b, +strong { + font-weight: bold; +} +dfn { + font-style: italic; +} +h1 { + margin: .67em 0; + font-size: 2em; +} +mark { + color: #000; + background: #ff0; +} +small { + font-size: 80%; +} +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} +sup { + top: -.5em; +} +sub { + bottom: -.25em; +} +img { + border: 0; +} +svg:not(:root) { + overflow: hidden; +} +figure { + margin: 1em 40px; +} +hr { + height: 0; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} +pre { + overflow: auto; +} +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + font-size: 1em; +} +button, +input, +optgroup, +select, +textarea { + margin: 0; + font: inherit; + color: inherit; +} +button { + overflow: visible; +} +button, +select { + text-transform: none; +} +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; + cursor: pointer; +} +button[disabled], +html input[disabled] { + cursor: default; +} +button::-moz-focus-inner, +input::-moz-focus-inner { + padding: 0; + border: 0; +} +input { + line-height: normal; +} +input[type="checkbox"], +input[type="radio"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 0; +} +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; +} +input[type="search"] { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + -webkit-appearance: textfield; +} +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} +fieldset { + padding: .35em .625em .75em; + margin: 0 2px; + border: 1px solid #c0c0c0; +} +legend { + padding: 0; + border: 0; +} +textarea { + overflow: auto; +} +optgroup { + font-weight: bold; +} +table { + border-spacing: 0; + border-collapse: collapse; +} +td, +th { + padding: 0; +} +/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ +@media print { + *, + *:before, + *:after { + color: #000 !important; + text-shadow: none !important; + background: transparent !important; + -webkit-box-shadow: none !important; + box-shadow: none !important; + } + a, + a:visited { + text-decoration: underline; + } + a[href]:after { + content: " (" attr(href) ")"; + } + abbr[title]:after { + content: " (" attr(title) ")"; + } + a[href^="#"]:after, + a[href^="javascript:"]:after { + content: ""; + } + pre, + blockquote { + border: 1px solid #999; + + page-break-inside: avoid; + } + thead { + display: table-header-group; + } + tr, + img { + page-break-inside: avoid; + } + img { + max-width: 100% !important; + } + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + h2, + h3 { + page-break-after: avoid; + } + .navbar { + display: none; + } + .btn > .caret, + .dropup > .btn > .caret { + border-top-color: #000 !important; + } + .label { + border: 1px solid #000; + } + .table { + border-collapse: collapse !important; + } + .table td, + .table th { + background-color: #fff !important; + } + .table-bordered th, + .table-bordered td { + border: 1px solid #ddd !important; + } +} +@font-face { + font-family: 'Glyphicons Halflings'; + + src: url('../fonts/glyphicons-halflings-regular.eot'); + src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); +} +.glyphicon { + position: relative; + top: 1px; + display: inline-block; + font-family: 'Glyphicons Halflings'; + font-style: normal; + font-weight: normal; + line-height: 1; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.glyphicon-asterisk:before { + content: "\002a"; +} +.glyphicon-plus:before { + content: "\002b"; +} +.glyphicon-euro:before, +.glyphicon-eur:before { + content: "\20ac"; +} +.glyphicon-minus:before { + content: "\2212"; +} +.glyphicon-cloud:before { + content: "\2601"; +} +.glyphicon-envelope:before { + content: "\2709"; +} +.glyphicon-pencil:before { + content: "\270f"; +} +.glyphicon-glass:before { + content: "\e001"; +} +.glyphicon-music:before { + content: "\e002"; +} +.glyphicon-search:before { + content: "\e003"; +} +.glyphicon-heart:before { + content: "\e005"; +} +.glyphicon-star:before { + content: "\e006"; +} +.glyphicon-star-empty:before { + content: "\e007"; +} +.glyphicon-user:before { + content: "\e008"; +} +.glyphicon-film:before { + content: "\e009"; +} +.glyphicon-th-large:before { + content: "\e010"; +} +.glyphicon-th:before { + content: "\e011"; +} +.glyphicon-th-list:before { + content: "\e012"; +} +.glyphicon-ok:before { + content: "\e013"; +} +.glyphicon-remove:before { + content: "\e014"; +} +.glyphicon-zoom-in:before { + content: "\e015"; +} +.glyphicon-zoom-out:before { + content: "\e016"; +} +.glyphicon-off:before { + content: "\e017"; +} +.glyphicon-signal:before { + content: "\e018"; +} +.glyphicon-cog:before { + content: "\e019"; +} +.glyphicon-trash:before { + content: "\e020"; +} +.glyphicon-home:before { + content: "\e021"; +} +.glyphicon-file:before { + content: "\e022"; +} +.glyphicon-time:before { + content: "\e023"; +} +.glyphicon-road:before { + content: "\e024"; +} +.glyphicon-download-alt:before { + content: "\e025"; +} +.glyphicon-download:before { + content: "\e026"; +} +.glyphicon-upload:before { + content: "\e027"; +} +.glyphicon-inbox:before { + content: "\e028"; +} +.glyphicon-play-circle:before { + content: "\e029"; +} +.glyphicon-repeat:before { + content: "\e030"; +} +.glyphicon-refresh:before { + content: "\e031"; +} +.glyphicon-list-alt:before { + content: "\e032"; +} +.glyphicon-lock:before { + content: "\e033"; +} +.glyphicon-flag:before { + content: "\e034"; +} +.glyphicon-headphones:before { + content: "\e035"; +} +.glyphicon-volume-off:before { + content: "\e036"; +} +.glyphicon-volume-down:before { + content: "\e037"; +} +.glyphicon-volume-up:before { + content: "\e038"; +} +.glyphicon-qrcode:before { + content: "\e039"; +} +.glyphicon-barcode:before { + content: "\e040"; +} +.glyphicon-tag:before { + content: "\e041"; +} +.glyphicon-tags:before { + content: "\e042"; +} +.glyphicon-book:before { + content: "\e043"; +} +.glyphicon-bookmark:before { + content: "\e044"; +} +.glyphicon-print:before { + content: "\e045"; +} +.glyphicon-camera:before { + content: "\e046"; +} +.glyphicon-font:before { + content: "\e047"; +} +.glyphicon-bold:before { + content: "\e048"; +} +.glyphicon-italic:before { + content: "\e049"; +} +.glyphicon-text-height:before { + content: "\e050"; +} +.glyphicon-text-width:before { + content: "\e051"; +} +.glyphicon-align-left:before { + content: "\e052"; +} +.glyphicon-align-center:before { + content: "\e053"; +} +.glyphicon-align-right:before { + content: "\e054"; +} +.glyphicon-align-justify:before { + content: "\e055"; +} +.glyphicon-list:before { + content: "\e056"; +} +.glyphicon-indent-left:before { + content: "\e057"; +} +.glyphicon-indent-right:before { + content: "\e058"; +} +.glyphicon-facetime-video:before { + content: "\e059"; +} +.glyphicon-picture:before { + content: "\e060"; +} +.glyphicon-map-marker:before { + content: "\e062"; +} +.glyphicon-adjust:before { + content: "\e063"; +} +.glyphicon-tint:before { + content: "\e064"; +} +.glyphicon-edit:before { + content: "\e065"; +} +.glyphicon-share:before { + content: "\e066"; +} +.glyphicon-check:before { + content: "\e067"; +} +.glyphicon-move:before { + content: "\e068"; +} +.glyphicon-step-backward:before { + content: "\e069"; +} +.glyphicon-fast-backward:before { + content: "\e070"; +} +.glyphicon-backward:before { + content: "\e071"; +} +.glyphicon-play:before { + content: "\e072"; +} +.glyphicon-pause:before { + content: "\e073"; +} +.glyphicon-stop:before { + content: "\e074"; +} +.glyphicon-forward:before { + content: "\e075"; +} +.glyphicon-fast-forward:before { + content: "\e076"; +} +.glyphicon-step-forward:before { + content: "\e077"; +} +.glyphicon-eject:before { + content: "\e078"; +} +.glyphicon-chevron-left:before { + content: "\e079"; +} +.glyphicon-chevron-right:before { + content: "\e080"; +} +.glyphicon-plus-sign:before { + content: "\e081"; +} +.glyphicon-minus-sign:before { + content: "\e082"; +} +.glyphicon-remove-sign:before { + content: "\e083"; +} +.glyphicon-ok-sign:before { + content: "\e084"; +} +.glyphicon-question-sign:before { + content: "\e085"; +} +.glyphicon-info-sign:before { + content: "\e086"; +} +.glyphicon-screenshot:before { + content: "\e087"; +} +.glyphicon-remove-circle:before { + content: "\e088"; +} +.glyphicon-ok-circle:before { + content: "\e089"; +} +.glyphicon-ban-circle:before { + content: "\e090"; +} +.glyphicon-arrow-left:before { + content: "\e091"; +} +.glyphicon-arrow-right:before { + content: "\e092"; +} +.glyphicon-arrow-up:before { + content: "\e093"; +} +.glyphicon-arrow-down:before { + content: "\e094"; +} +.glyphicon-share-alt:before { + content: "\e095"; +} +.glyphicon-resize-full:before { + content: "\e096"; +} +.glyphicon-resize-small:before { + content: "\e097"; +} +.glyphicon-exclamation-sign:before { + content: "\e101"; +} +.glyphicon-gift:before { + content: "\e102"; +} +.glyphicon-leaf:before { + content: "\e103"; +} +.glyphicon-fire:before { + content: "\e104"; +} +.glyphicon-eye-open:before { + content: "\e105"; +} +.glyphicon-eye-close:before { + content: "\e106"; +} +.glyphicon-warning-sign:before { + content: "\e107"; +} +.glyphicon-plane:before { + content: "\e108"; +} +.glyphicon-calendar:before { + content: "\e109"; +} +.glyphicon-random:before { + content: "\e110"; +} +.glyphicon-comment:before { + content: "\e111"; +} +.glyphicon-magnet:before { + content: "\e112"; +} +.glyphicon-chevron-up:before { + content: "\e113"; +} +.glyphicon-chevron-down:before { + content: "\e114"; +} +.glyphicon-retweet:before { + content: "\e115"; +} +.glyphicon-shopping-cart:before { + content: "\e116"; +} +.glyphicon-folder-close:before { + content: "\e117"; +} +.glyphicon-folder-open:before { + content: "\e118"; +} +.glyphicon-resize-vertical:before { + content: "\e119"; +} +.glyphicon-resize-horizontal:before { + content: "\e120"; +} +.glyphicon-hdd:before { + content: "\e121"; +} +.glyphicon-bullhorn:before { + content: "\e122"; +} +.glyphicon-bell:before { + content: "\e123"; +} +.glyphicon-certificate:before { + content: "\e124"; +} +.glyphicon-thumbs-up:before { + content: "\e125"; +} +.glyphicon-thumbs-down:before { + content: "\e126"; +} +.glyphicon-hand-right:before { + content: "\e127"; +} +.glyphicon-hand-left:before { + content: "\e128"; +} +.glyphicon-hand-up:before { + content: "\e129"; +} +.glyphicon-hand-down:before { + content: "\e130"; +} +.glyphicon-circle-arrow-right:before { + content: "\e131"; +} +.glyphicon-circle-arrow-left:before { + content: "\e132"; +} +.glyphicon-circle-arrow-up:before { + content: "\e133"; +} +.glyphicon-circle-arrow-down:before { + content: "\e134"; +} +.glyphicon-globe:before { + content: "\e135"; +} +.glyphicon-wrench:before { + content: "\e136"; +} +.glyphicon-tasks:before { + content: "\e137"; +} +.glyphicon-filter:before { + content: "\e138"; +} +.glyphicon-briefcase:before { + content: "\e139"; +} +.glyphicon-fullscreen:before { + content: "\e140"; +} +.glyphicon-dashboard:before { + content: "\e141"; +} +.glyphicon-paperclip:before { + content: "\e142"; +} +.glyphicon-heart-empty:before { + content: "\e143"; +} +.glyphicon-link:before { + content: "\e144"; +} +.glyphicon-phone:before { + content: "\e145"; +} +.glyphicon-pushpin:before { + content: "\e146"; +} +.glyphicon-usd:before { + content: "\e148"; +} +.glyphicon-gbp:before { + content: "\e149"; +} +.glyphicon-sort:before { + content: "\e150"; +} +.glyphicon-sort-by-alphabet:before { + content: "\e151"; +} +.glyphicon-sort-by-alphabet-alt:before { + content: "\e152"; +} +.glyphicon-sort-by-order:before { + content: "\e153"; +} +.glyphicon-sort-by-order-alt:before { + content: "\e154"; +} +.glyphicon-sort-by-attributes:before { + content: "\e155"; +} +.glyphicon-sort-by-attributes-alt:before { + content: "\e156"; +} +.glyphicon-unchecked:before { + content: "\e157"; +} +.glyphicon-expand:before { + content: "\e158"; +} +.glyphicon-collapse-down:before { + content: "\e159"; +} +.glyphicon-collapse-up:before { + content: "\e160"; +} +.glyphicon-log-in:before { + content: "\e161"; +} +.glyphicon-flash:before { + content: "\e162"; +} +.glyphicon-log-out:before { + content: "\e163"; +} +.glyphicon-new-window:before { + content: "\e164"; +} +.glyphicon-record:before { + content: "\e165"; +} +.glyphicon-save:before { + content: "\e166"; +} +.glyphicon-open:before { + content: "\e167"; +} +.glyphicon-saved:before { + content: "\e168"; +} +.glyphicon-import:before { + content: "\e169"; +} +.glyphicon-export:before { + content: "\e170"; +} +.glyphicon-send:before { + content: "\e171"; +} +.glyphicon-floppy-disk:before { + content: "\e172"; +} +.glyphicon-floppy-saved:before { + content: "\e173"; +} +.glyphicon-floppy-remove:before { + content: "\e174"; +} +.glyphicon-floppy-save:before { + content: "\e175"; +} +.glyphicon-floppy-open:before { + content: "\e176"; +} +.glyphicon-credit-card:before { + content: "\e177"; +} +.glyphicon-transfer:before { + content: "\e178"; +} +.glyphicon-cutlery:before { + content: "\e179"; +} +.glyphicon-header:before { + content: "\e180"; +} +.glyphicon-compressed:before { + content: "\e181"; +} +.glyphicon-earphone:before { + content: "\e182"; +} +.glyphicon-phone-alt:before { + content: "\e183"; +} +.glyphicon-tower:before { + content: "\e184"; +} +.glyphicon-stats:before { + content: "\e185"; +} +.glyphicon-sd-video:before { + content: "\e186"; +} +.glyphicon-hd-video:before { + content: "\e187"; +} +.glyphicon-subtitles:before { + content: "\e188"; +} +.glyphicon-sound-stereo:before { + content: "\e189"; +} +.glyphicon-sound-dolby:before { + content: "\e190"; +} +.glyphicon-sound-5-1:before { + content: "\e191"; +} +.glyphicon-sound-6-1:before { + content: "\e192"; +} +.glyphicon-sound-7-1:before { + content: "\e193"; +} +.glyphicon-copyright-mark:before { + content: "\e194"; +} +.glyphicon-registration-mark:before { + content: "\e195"; +} +.glyphicon-cloud-download:before { + content: "\e197"; +} +.glyphicon-cloud-upload:before { + content: "\e198"; +} +.glyphicon-tree-conifer:before { + content: "\e199"; +} +.glyphicon-tree-deciduous:before { + content: "\e200"; +} +.glyphicon-cd:before { + content: "\e201"; +} +.glyphicon-save-file:before { + content: "\e202"; +} +.glyphicon-open-file:before { + content: "\e203"; +} +.glyphicon-level-up:before { + content: "\e204"; +} +.glyphicon-copy:before { + content: "\e205"; +} +.glyphicon-paste:before { + content: "\e206"; +} +.glyphicon-alert:before { + content: "\e209"; +} +.glyphicon-equalizer:before { + content: "\e210"; +} +.glyphicon-king:before { + content: "\e211"; +} +.glyphicon-queen:before { + content: "\e212"; +} +.glyphicon-pawn:before { + content: "\e213"; +} +.glyphicon-bishop:before { + content: "\e214"; +} +.glyphicon-knight:before { + content: "\e215"; +} +.glyphicon-baby-formula:before { + content: "\e216"; +} +.glyphicon-tent:before { + content: "\26fa"; +} +.glyphicon-blackboard:before { + content: "\e218"; +} +.glyphicon-bed:before { + content: "\e219"; +} +.glyphicon-apple:before { + content: "\f8ff"; +} +.glyphicon-erase:before { + content: "\e221"; +} +.glyphicon-hourglass:before { + content: "\231b"; +} +.glyphicon-lamp:before { + content: "\e223"; +} +.glyphicon-duplicate:before { + content: "\e224"; +} +.glyphicon-piggy-bank:before { + content: "\e225"; +} +.glyphicon-scissors:before { + content: "\e226"; +} +.glyphicon-bitcoin:before { + content: "\e227"; +} +.glyphicon-btc:before { + content: "\e227"; +} +.glyphicon-xbt:before { + content: "\e227"; +} +.glyphicon-yen:before { + content: "\00a5"; +} +.glyphicon-jpy:before { + content: "\00a5"; +} +.glyphicon-ruble:before { + content: "\20bd"; +} +.glyphicon-rub:before { + content: "\20bd"; +} +.glyphicon-scale:before { + content: "\e230"; +} +.glyphicon-ice-lolly:before { + content: "\e231"; +} +.glyphicon-ice-lolly-tasted:before { + content: "\e232"; +} +.glyphicon-education:before { + content: "\e233"; +} +.glyphicon-option-horizontal:before { + content: "\e234"; +} +.glyphicon-option-vertical:before { + content: "\e235"; +} +.glyphicon-menu-hamburger:before { + content: "\e236"; +} +.glyphicon-modal-window:before { + content: "\e237"; +} +.glyphicon-oil:before { + content: "\e238"; +} +.glyphicon-grain:before { + content: "\e239"; +} +.glyphicon-sunglasses:before { + content: "\e240"; +} +.glyphicon-text-size:before { + content: "\e241"; +} +.glyphicon-text-color:before { + content: "\e242"; +} +.glyphicon-text-background:before { + content: "\e243"; +} +.glyphicon-object-align-top:before { + content: "\e244"; +} +.glyphicon-object-align-bottom:before { + content: "\e245"; +} +.glyphicon-object-align-horizontal:before { + content: "\e246"; +} +.glyphicon-object-align-left:before { + content: "\e247"; +} +.glyphicon-object-align-vertical:before { + content: "\e248"; +} +.glyphicon-object-align-right:before { + content: "\e249"; +} +.glyphicon-triangle-right:before { + content: "\e250"; +} +.glyphicon-triangle-left:before { + content: "\e251"; +} +.glyphicon-triangle-bottom:before { + content: "\e252"; +} +.glyphicon-triangle-top:before { + content: "\e253"; +} +.glyphicon-console:before { + content: "\e254"; +} +.glyphicon-superscript:before { + content: "\e255"; +} +.glyphicon-subscript:before { + content: "\e256"; +} +.glyphicon-menu-left:before { + content: "\e257"; +} +.glyphicon-menu-right:before { + content: "\e258"; +} +.glyphicon-menu-down:before { + content: "\e259"; +} +.glyphicon-menu-up:before { + content: "\e260"; +} +* { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +*:before, +*:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +html { + font-size: 10px; + + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} +body { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 1.42857143; + color: #333; + background-color: #fff; +} +input, +button, +select, +textarea { + font-family: inherit; + font-size: inherit; + line-height: inherit; +} +a { + color: #337ab7; + text-decoration: none; +} +a:hover, +a:focus { + color: #23527c; + text-decoration: underline; +} +a:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +figure { + margin: 0; +} +img { + vertical-align: middle; +} +.img-responsive, +.thumbnail > img, +.thumbnail a > img, +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + display: block; + max-width: 100%; + height: auto; +} +.img-rounded { + border-radius: 6px; +} +.img-thumbnail { + display: inline-block; + max-width: 100%; + height: auto; + padding: 4px; + line-height: 1.42857143; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -webkit-transition: all .2s ease-in-out; + -o-transition: all .2s ease-in-out; + transition: all .2s ease-in-out; +} +.img-circle { + border-radius: 50%; +} +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #eee; +} +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} +.sr-only-focusable:active, +.sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + margin: 0; + overflow: visible; + clip: auto; +} +[role="button"] { + cursor: pointer; +} +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-family: inherit; + font-weight: 500; + line-height: 1.1; + color: inherit; +} +h1 small, +h2 small, +h3 small, +h4 small, +h5 small, +h6 small, +.h1 small, +.h2 small, +.h3 small, +.h4 small, +.h5 small, +.h6 small, +h1 .small, +h2 .small, +h3 .small, +h4 .small, +h5 .small, +h6 .small, +.h1 .small, +.h2 .small, +.h3 .small, +.h4 .small, +.h5 .small, +.h6 .small { + font-weight: normal; + line-height: 1; + color: #777; +} +h1, +.h1, +h2, +.h2, +h3, +.h3 { + margin-top: 20px; + margin-bottom: 10px; +} +h1 small, +.h1 small, +h2 small, +.h2 small, +h3 small, +.h3 small, +h1 .small, +.h1 .small, +h2 .small, +.h2 .small, +h3 .small, +.h3 .small { + font-size: 65%; +} +h4, +.h4, +h5, +.h5, +h6, +.h6 { + margin-top: 10px; + margin-bottom: 10px; +} +h4 small, +.h4 small, +h5 small, +.h5 small, +h6 small, +.h6 small, +h4 .small, +.h4 .small, +h5 .small, +.h5 .small, +h6 .small, +.h6 .small { + font-size: 75%; +} +h1, +.h1 { + font-size: 36px; +} +h2, +.h2 { + font-size: 30px; +} +h3, +.h3 { + font-size: 24px; +} +h4, +.h4 { + font-size: 18px; +} +h5, +.h5 { + font-size: 14px; +} +h6, +.h6 { + font-size: 12px; +} +p { + margin: 0 0 10px; +} +.lead { + margin-bottom: 20px; + font-size: 16px; + font-weight: 300; + line-height: 1.4; +} +@media (min-width: 768px) { + .lead { + font-size: 21px; + } +} +small, +.small { + font-size: 85%; +} +mark, +.mark { + padding: .2em; + background-color: #fcf8e3; +} +.text-left { + text-align: left; +} +.text-right { + text-align: right; +} +.text-center { + text-align: center; +} +.text-justify { + text-align: justify; +} +.text-nowrap { + white-space: nowrap; +} +.text-lowercase { + text-transform: lowercase; +} +.text-uppercase { + text-transform: uppercase; +} +.text-capitalize { + text-transform: capitalize; +} +.text-muted { + color: #777; +} +.text-primary { + color: #337ab7; +} +a.text-primary:hover, +a.text-primary:focus { + color: #286090; +} +.text-success { + color: #3c763d; +} +a.text-success:hover, +a.text-success:focus { + color: #2b542c; +} +.text-info { + color: #31708f; +} +a.text-info:hover, +a.text-info:focus { + color: #245269; +} +.text-warning { + color: #8a6d3b; +} +a.text-warning:hover, +a.text-warning:focus { + color: #66512c; +} +.text-danger { + color: #a94442; +} +a.text-danger:hover, +a.text-danger:focus { + color: #843534; +} +.bg-primary { + color: #fff; + background-color: #337ab7; +} +a.bg-primary:hover, +a.bg-primary:focus { + background-color: #286090; +} +.bg-success { + background-color: #dff0d8; +} +a.bg-success:hover, +a.bg-success:focus { + background-color: #c1e2b3; +} +.bg-info { + background-color: #d9edf7; +} +a.bg-info:hover, +a.bg-info:focus { + background-color: #afd9ee; +} +.bg-warning { + background-color: #fcf8e3; +} +a.bg-warning:hover, +a.bg-warning:focus { + background-color: #f7ecb5; +} +.bg-danger { + background-color: #f2dede; +} +a.bg-danger:hover, +a.bg-danger:focus { + background-color: #e4b9b9; +} +.page-header { + padding-bottom: 9px; + margin: 40px 0 20px; + border-bottom: 1px solid #eee; +} +ul, +ol { + margin-top: 0; + margin-bottom: 10px; +} +ul ul, +ol ul, +ul ol, +ol ol { + margin-bottom: 0; +} +.list-unstyled { + padding-left: 0; + list-style: none; +} +.list-inline { + padding-left: 0; + margin-left: -5px; + list-style: none; +} +.list-inline > li { + display: inline-block; + padding-right: 5px; + padding-left: 5px; +} +dl { + margin-top: 0; + margin-bottom: 20px; +} +dt, +dd { + line-height: 1.42857143; +} +dt { + font-weight: bold; +} +dd { + margin-left: 0; +} +@media (min-width: 768px) { + .dl-horizontal dt { + float: left; + width: 160px; + overflow: hidden; + clear: left; + text-align: right; + text-overflow: ellipsis; + white-space: nowrap; + } + .dl-horizontal dd { + margin-left: 180px; + } +} +abbr[title], +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted #777; +} +.initialism { + font-size: 90%; + text-transform: uppercase; +} +blockquote { + padding: 10px 20px; + margin: 0 0 20px; + font-size: 17.5px; + border-left: 5px solid #eee; +} +blockquote p:last-child, +blockquote ul:last-child, +blockquote ol:last-child { + margin-bottom: 0; +} +blockquote footer, +blockquote small, +blockquote .small { + display: block; + font-size: 80%; + line-height: 1.42857143; + color: #777; +} +blockquote footer:before, +blockquote small:before, +blockquote .small:before { + content: '\2014 \00A0'; +} +.blockquote-reverse, +blockquote.pull-right { + padding-right: 15px; + padding-left: 0; + text-align: right; + border-right: 5px solid #eee; + border-left: 0; +} +.blockquote-reverse footer:before, +blockquote.pull-right footer:before, +.blockquote-reverse small:before, +blockquote.pull-right small:before, +.blockquote-reverse .small:before, +blockquote.pull-right .small:before { + content: ''; +} +.blockquote-reverse footer:after, +blockquote.pull-right footer:after, +.blockquote-reverse small:after, +blockquote.pull-right small:after, +.blockquote-reverse .small:after, +blockquote.pull-right .small:after { + content: '\00A0 \2014'; +} +address { + margin-bottom: 20px; + font-style: normal; + line-height: 1.42857143; +} +code, +kbd, +pre, +samp { + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; +} +code { + padding: 2px 4px; + font-size: 90%; + color: #c7254e; + background-color: #f9f2f4; + border-radius: 4px; +} +kbd { + padding: 2px 4px; + font-size: 90%; + color: #fff; + background-color: #333; + border-radius: 3px; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); +} +kbd kbd { + padding: 0; + font-size: 100%; + font-weight: bold; + -webkit-box-shadow: none; + box-shadow: none; +} +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.42857143; + color: #333; + word-break: break-all; + word-wrap: break-word; + background-color: #f5f5f5; + border: 1px solid #ccc; + border-radius: 4px; +} +pre code { + padding: 0; + font-size: inherit; + color: inherit; + white-space: pre-wrap; + background-color: transparent; + border-radius: 0; +} +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} +.container { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} +@media (min-width: 768px) { + .container { + width: 750px; + } +} +@media (min-width: 992px) { + .container { + width: 970px; + } +} +@media (min-width: 1200px) { + .container { + width: 1170px; + } +} +.container-fluid { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} +.row { + margin-right: -15px; + margin-left: -15px; +} +.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { + position: relative; + min-height: 1px; + padding-right: 15px; + padding-left: 15px; +} +.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { + float: left; +} +.col-xs-12 { + width: 100%; +} +.col-xs-11 { + width: 91.66666667%; +} +.col-xs-10 { + width: 83.33333333%; +} +.col-xs-9 { + width: 75%; +} +.col-xs-8 { + width: 66.66666667%; +} +.col-xs-7 { + width: 58.33333333%; +} +.col-xs-6 { + width: 50%; +} +.col-xs-5 { + width: 41.66666667%; +} +.col-xs-4 { + width: 33.33333333%; +} +.col-xs-3 { + width: 25%; +} +.col-xs-2 { + width: 16.66666667%; +} +.col-xs-1 { + width: 8.33333333%; +} +.col-xs-pull-12 { + right: 100%; +} +.col-xs-pull-11 { + right: 91.66666667%; +} +.col-xs-pull-10 { + right: 83.33333333%; +} +.col-xs-pull-9 { + right: 75%; +} +.col-xs-pull-8 { + right: 66.66666667%; +} +.col-xs-pull-7 { + right: 58.33333333%; +} +.col-xs-pull-6 { + right: 50%; +} +.col-xs-pull-5 { + right: 41.66666667%; +} +.col-xs-pull-4 { + right: 33.33333333%; +} +.col-xs-pull-3 { + right: 25%; +} +.col-xs-pull-2 { + right: 16.66666667%; +} +.col-xs-pull-1 { + right: 8.33333333%; +} +.col-xs-pull-0 { + right: auto; +} +.col-xs-push-12 { + left: 100%; +} +.col-xs-push-11 { + left: 91.66666667%; +} +.col-xs-push-10 { + left: 83.33333333%; +} +.col-xs-push-9 { + left: 75%; +} +.col-xs-push-8 { + left: 66.66666667%; +} +.col-xs-push-7 { + left: 58.33333333%; +} +.col-xs-push-6 { + left: 50%; +} +.col-xs-push-5 { + left: 41.66666667%; +} +.col-xs-push-4 { + left: 33.33333333%; +} +.col-xs-push-3 { + left: 25%; +} +.col-xs-push-2 { + left: 16.66666667%; +} +.col-xs-push-1 { + left: 8.33333333%; +} +.col-xs-push-0 { + left: auto; +} +.col-xs-offset-12 { + margin-left: 100%; +} +.col-xs-offset-11 { + margin-left: 91.66666667%; +} +.col-xs-offset-10 { + margin-left: 83.33333333%; +} +.col-xs-offset-9 { + margin-left: 75%; +} +.col-xs-offset-8 { + margin-left: 66.66666667%; +} +.col-xs-offset-7 { + margin-left: 58.33333333%; +} +.col-xs-offset-6 { + margin-left: 50%; +} +.col-xs-offset-5 { + margin-left: 41.66666667%; +} +.col-xs-offset-4 { + margin-left: 33.33333333%; +} +.col-xs-offset-3 { + margin-left: 25%; +} +.col-xs-offset-2 { + margin-left: 16.66666667%; +} +.col-xs-offset-1 { + margin-left: 8.33333333%; +} +.col-xs-offset-0 { + margin-left: 0; +} +@media (min-width: 768px) { + .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 { + float: left; + } + .col-sm-12 { + width: 100%; + } + .col-sm-11 { + width: 91.66666667%; + } + .col-sm-10 { + width: 83.33333333%; + } + .col-sm-9 { + width: 75%; + } + .col-sm-8 { + width: 66.66666667%; + } + .col-sm-7 { + width: 58.33333333%; + } + .col-sm-6 { + width: 50%; + } + .col-sm-5 { + width: 41.66666667%; + } + .col-sm-4 { + width: 33.33333333%; + } + .col-sm-3 { + width: 25%; + } + .col-sm-2 { + width: 16.66666667%; + } + .col-sm-1 { + width: 8.33333333%; + } + .col-sm-pull-12 { + right: 100%; + } + .col-sm-pull-11 { + right: 91.66666667%; + } + .col-sm-pull-10 { + right: 83.33333333%; + } + .col-sm-pull-9 { + right: 75%; + } + .col-sm-pull-8 { + right: 66.66666667%; + } + .col-sm-pull-7 { + right: 58.33333333%; + } + .col-sm-pull-6 { + right: 50%; + } + .col-sm-pull-5 { + right: 41.66666667%; + } + .col-sm-pull-4 { + right: 33.33333333%; + } + .col-sm-pull-3 { + right: 25%; + } + .col-sm-pull-2 { + right: 16.66666667%; + } + .col-sm-pull-1 { + right: 8.33333333%; + } + .col-sm-pull-0 { + right: auto; + } + .col-sm-push-12 { + left: 100%; + } + .col-sm-push-11 { + left: 91.66666667%; + } + .col-sm-push-10 { + left: 83.33333333%; + } + .col-sm-push-9 { + left: 75%; + } + .col-sm-push-8 { + left: 66.66666667%; + } + .col-sm-push-7 { + left: 58.33333333%; + } + .col-sm-push-6 { + left: 50%; + } + .col-sm-push-5 { + left: 41.66666667%; + } + .col-sm-push-4 { + left: 33.33333333%; + } + .col-sm-push-3 { + left: 25%; + } + .col-sm-push-2 { + left: 16.66666667%; + } + .col-sm-push-1 { + left: 8.33333333%; + } + .col-sm-push-0 { + left: auto; + } + .col-sm-offset-12 { + margin-left: 100%; + } + .col-sm-offset-11 { + margin-left: 91.66666667%; + } + .col-sm-offset-10 { + margin-left: 83.33333333%; + } + .col-sm-offset-9 { + margin-left: 75%; + } + .col-sm-offset-8 { + margin-left: 66.66666667%; + } + .col-sm-offset-7 { + margin-left: 58.33333333%; + } + .col-sm-offset-6 { + margin-left: 50%; + } + .col-sm-offset-5 { + margin-left: 41.66666667%; + } + .col-sm-offset-4 { + margin-left: 33.33333333%; + } + .col-sm-offset-3 { + margin-left: 25%; + } + .col-sm-offset-2 { + margin-left: 16.66666667%; + } + .col-sm-offset-1 { + margin-left: 8.33333333%; + } + .col-sm-offset-0 { + margin-left: 0; + } +} +@media (min-width: 992px) { + .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 { + float: left; + } + .col-md-12 { + width: 100%; + } + .col-md-11 { + width: 91.66666667%; + } + .col-md-10 { + width: 83.33333333%; + } + .col-md-9 { + width: 75%; + } + .col-md-8 { + width: 66.66666667%; + } + .col-md-7 { + width: 58.33333333%; + } + .col-md-6 { + width: 50%; + } + .col-md-5 { + width: 41.66666667%; + } + .col-md-4 { + width: 33.33333333%; + } + .col-md-3 { + width: 25%; + } + .col-md-2 { + width: 16.66666667%; + } + .col-md-1 { + width: 8.33333333%; + } + .col-md-pull-12 { + right: 100%; + } + .col-md-pull-11 { + right: 91.66666667%; + } + .col-md-pull-10 { + right: 83.33333333%; + } + .col-md-pull-9 { + right: 75%; + } + .col-md-pull-8 { + right: 66.66666667%; + } + .col-md-pull-7 { + right: 58.33333333%; + } + .col-md-pull-6 { + right: 50%; + } + .col-md-pull-5 { + right: 41.66666667%; + } + .col-md-pull-4 { + right: 33.33333333%; + } + .col-md-pull-3 { + right: 25%; + } + .col-md-pull-2 { + right: 16.66666667%; + } + .col-md-pull-1 { + right: 8.33333333%; + } + .col-md-pull-0 { + right: auto; + } + .col-md-push-12 { + left: 100%; + } + .col-md-push-11 { + left: 91.66666667%; + } + .col-md-push-10 { + left: 83.33333333%; + } + .col-md-push-9 { + left: 75%; + } + .col-md-push-8 { + left: 66.66666667%; + } + .col-md-push-7 { + left: 58.33333333%; + } + .col-md-push-6 { + left: 50%; + } + .col-md-push-5 { + left: 41.66666667%; + } + .col-md-push-4 { + left: 33.33333333%; + } + .col-md-push-3 { + left: 25%; + } + .col-md-push-2 { + left: 16.66666667%; + } + .col-md-push-1 { + left: 8.33333333%; + } + .col-md-push-0 { + left: auto; + } + .col-md-offset-12 { + margin-left: 100%; + } + .col-md-offset-11 { + margin-left: 91.66666667%; + } + .col-md-offset-10 { + margin-left: 83.33333333%; + } + .col-md-offset-9 { + margin-left: 75%; + } + .col-md-offset-8 { + margin-left: 66.66666667%; + } + .col-md-offset-7 { + margin-left: 58.33333333%; + } + .col-md-offset-6 { + margin-left: 50%; + } + .col-md-offset-5 { + margin-left: 41.66666667%; + } + .col-md-offset-4 { + margin-left: 33.33333333%; + } + .col-md-offset-3 { + margin-left: 25%; + } + .col-md-offset-2 { + margin-left: 16.66666667%; + } + .col-md-offset-1 { + margin-left: 8.33333333%; + } + .col-md-offset-0 { + margin-left: 0; + } +} +@media (min-width: 1200px) { + .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 { + float: left; + } + .col-lg-12 { + width: 100%; + } + .col-lg-11 { + width: 91.66666667%; + } + .col-lg-10 { + width: 83.33333333%; + } + .col-lg-9 { + width: 75%; + } + .col-lg-8 { + width: 66.66666667%; + } + .col-lg-7 { + width: 58.33333333%; + } + .col-lg-6 { + width: 50%; + } + .col-lg-5 { + width: 41.66666667%; + } + .col-lg-4 { + width: 33.33333333%; + } + .col-lg-3 { + width: 25%; + } + .col-lg-2 { + width: 16.66666667%; + } + .col-lg-1 { + width: 8.33333333%; + } + .col-lg-pull-12 { + right: 100%; + } + .col-lg-pull-11 { + right: 91.66666667%; + } + .col-lg-pull-10 { + right: 83.33333333%; + } + .col-lg-pull-9 { + right: 75%; + } + .col-lg-pull-8 { + right: 66.66666667%; + } + .col-lg-pull-7 { + right: 58.33333333%; + } + .col-lg-pull-6 { + right: 50%; + } + .col-lg-pull-5 { + right: 41.66666667%; + } + .col-lg-pull-4 { + right: 33.33333333%; + } + .col-lg-pull-3 { + right: 25%; + } + .col-lg-pull-2 { + right: 16.66666667%; + } + .col-lg-pull-1 { + right: 8.33333333%; + } + .col-lg-pull-0 { + right: auto; + } + .col-lg-push-12 { + left: 100%; + } + .col-lg-push-11 { + left: 91.66666667%; + } + .col-lg-push-10 { + left: 83.33333333%; + } + .col-lg-push-9 { + left: 75%; + } + .col-lg-push-8 { + left: 66.66666667%; + } + .col-lg-push-7 { + left: 58.33333333%; + } + .col-lg-push-6 { + left: 50%; + } + .col-lg-push-5 { + left: 41.66666667%; + } + .col-lg-push-4 { + left: 33.33333333%; + } + .col-lg-push-3 { + left: 25%; + } + .col-lg-push-2 { + left: 16.66666667%; + } + .col-lg-push-1 { + left: 8.33333333%; + } + .col-lg-push-0 { + left: auto; + } + .col-lg-offset-12 { + margin-left: 100%; + } + .col-lg-offset-11 { + margin-left: 91.66666667%; + } + .col-lg-offset-10 { + margin-left: 83.33333333%; + } + .col-lg-offset-9 { + margin-left: 75%; + } + .col-lg-offset-8 { + margin-left: 66.66666667%; + } + .col-lg-offset-7 { + margin-left: 58.33333333%; + } + .col-lg-offset-6 { + margin-left: 50%; + } + .col-lg-offset-5 { + margin-left: 41.66666667%; + } + .col-lg-offset-4 { + margin-left: 33.33333333%; + } + .col-lg-offset-3 { + margin-left: 25%; + } + .col-lg-offset-2 { + margin-left: 16.66666667%; + } + .col-lg-offset-1 { + margin-left: 8.33333333%; + } + .col-lg-offset-0 { + margin-left: 0; + } +} +table { + background-color: transparent; +} +caption { + padding-top: 8px; + padding-bottom: 8px; + color: #777; + text-align: left; +} +th { + text-align: left; +} +.table { + width: 100%; + max-width: 100%; + margin-bottom: 20px; +} +.table > thead > tr > th, +.table > tbody > tr > th, +.table > tfoot > tr > th, +.table > thead > tr > td, +.table > tbody > tr > td, +.table > tfoot > tr > td { + padding: 8px; + line-height: 1.42857143; + vertical-align: top; + border-top: 1px solid #ddd; +} +.table > thead > tr > th { + vertical-align: bottom; + border-bottom: 2px solid #ddd; +} +.table > caption + thead > tr:first-child > th, +.table > colgroup + thead > tr:first-child > th, +.table > thead:first-child > tr:first-child > th, +.table > caption + thead > tr:first-child > td, +.table > colgroup + thead > tr:first-child > td, +.table > thead:first-child > tr:first-child > td { + border-top: 0; +} +.table > tbody + tbody { + border-top: 2px solid #ddd; +} +.table .table { + background-color: #fff; +} +.table-condensed > thead > tr > th, +.table-condensed > tbody > tr > th, +.table-condensed > tfoot > tr > th, +.table-condensed > thead > tr > td, +.table-condensed > tbody > tr > td, +.table-condensed > tfoot > tr > td { + padding: 5px; +} +.table-bordered { + border: 1px solid #ddd; +} +.table-bordered > thead > tr > th, +.table-bordered > tbody > tr > th, +.table-bordered > tfoot > tr > th, +.table-bordered > thead > tr > td, +.table-bordered > tbody > tr > td, +.table-bordered > tfoot > tr > td { + border: 1px solid #ddd; +} +.table-bordered > thead > tr > th, +.table-bordered > thead > tr > td { + border-bottom-width: 2px; +} +.table-striped > tbody > tr:nth-of-type(odd) { + background-color: #f9f9f9; +} +.table-hover > tbody > tr:hover { + background-color: #f5f5f5; +} +table col[class*="col-"] { + position: static; + display: table-column; + float: none; +} +table td[class*="col-"], +table th[class*="col-"] { + position: static; + display: table-cell; + float: none; +} +.table > thead > tr > td.active, +.table > tbody > tr > td.active, +.table > tfoot > tr > td.active, +.table > thead > tr > th.active, +.table > tbody > tr > th.active, +.table > tfoot > tr > th.active, +.table > thead > tr.active > td, +.table > tbody > tr.active > td, +.table > tfoot > tr.active > td, +.table > thead > tr.active > th, +.table > tbody > tr.active > th, +.table > tfoot > tr.active > th { + background-color: #f5f5f5; +} +.table-hover > tbody > tr > td.active:hover, +.table-hover > tbody > tr > th.active:hover, +.table-hover > tbody > tr.active:hover > td, +.table-hover > tbody > tr:hover > .active, +.table-hover > tbody > tr.active:hover > th { + background-color: #e8e8e8; +} +.table > thead > tr > td.success, +.table > tbody > tr > td.success, +.table > tfoot > tr > td.success, +.table > thead > tr > th.success, +.table > tbody > tr > th.success, +.table > tfoot > tr > th.success, +.table > thead > tr.success > td, +.table > tbody > tr.success > td, +.table > tfoot > tr.success > td, +.table > thead > tr.success > th, +.table > tbody > tr.success > th, +.table > tfoot > tr.success > th { + background-color: #dff0d8; +} +.table-hover > tbody > tr > td.success:hover, +.table-hover > tbody > tr > th.success:hover, +.table-hover > tbody > tr.success:hover > td, +.table-hover > tbody > tr:hover > .success, +.table-hover > tbody > tr.success:hover > th { + background-color: #d0e9c6; +} +.table > thead > tr > td.info, +.table > tbody > tr > td.info, +.table > tfoot > tr > td.info, +.table > thead > tr > th.info, +.table > tbody > tr > th.info, +.table > tfoot > tr > th.info, +.table > thead > tr.info > td, +.table > tbody > tr.info > td, +.table > tfoot > tr.info > td, +.table > thead > tr.info > th, +.table > tbody > tr.info > th, +.table > tfoot > tr.info > th { + background-color: #d9edf7; +} +.table-hover > tbody > tr > td.info:hover, +.table-hover > tbody > tr > th.info:hover, +.table-hover > tbody > tr.info:hover > td, +.table-hover > tbody > tr:hover > .info, +.table-hover > tbody > tr.info:hover > th { + background-color: #c4e3f3; +} +.table > thead > tr > td.warning, +.table > tbody > tr > td.warning, +.table > tfoot > tr > td.warning, +.table > thead > tr > th.warning, +.table > tbody > tr > th.warning, +.table > tfoot > tr > th.warning, +.table > thead > tr.warning > td, +.table > tbody > tr.warning > td, +.table > tfoot > tr.warning > td, +.table > thead > tr.warning > th, +.table > tbody > tr.warning > th, +.table > tfoot > tr.warning > th { + background-color: #fcf8e3; +} +.table-hover > tbody > tr > td.warning:hover, +.table-hover > tbody > tr > th.warning:hover, +.table-hover > tbody > tr.warning:hover > td, +.table-hover > tbody > tr:hover > .warning, +.table-hover > tbody > tr.warning:hover > th { + background-color: #faf2cc; +} +.table > thead > tr > td.danger, +.table > tbody > tr > td.danger, +.table > tfoot > tr > td.danger, +.table > thead > tr > th.danger, +.table > tbody > tr > th.danger, +.table > tfoot > tr > th.danger, +.table > thead > tr.danger > td, +.table > tbody > tr.danger > td, +.table > tfoot > tr.danger > td, +.table > thead > tr.danger > th, +.table > tbody > tr.danger > th, +.table > tfoot > tr.danger > th { + background-color: #f2dede; +} +.table-hover > tbody > tr > td.danger:hover, +.table-hover > tbody > tr > th.danger:hover, +.table-hover > tbody > tr.danger:hover > td, +.table-hover > tbody > tr:hover > .danger, +.table-hover > tbody > tr.danger:hover > th { + background-color: #ebcccc; +} +.table-responsive { + min-height: .01%; + overflow-x: auto; +} +@media screen and (max-width: 767px) { + .table-responsive { + width: 100%; + margin-bottom: 15px; + overflow-y: hidden; + -ms-overflow-style: -ms-autohiding-scrollbar; + border: 1px solid #ddd; + } + .table-responsive > .table { + margin-bottom: 0; + } + .table-responsive > .table > thead > tr > th, + .table-responsive > .table > tbody > tr > th, + .table-responsive > .table > tfoot > tr > th, + .table-responsive > .table > thead > tr > td, + .table-responsive > .table > tbody > tr > td, + .table-responsive > .table > tfoot > tr > td { + white-space: nowrap; + } + .table-responsive > .table-bordered { + border: 0; + } + .table-responsive > .table-bordered > thead > tr > th:first-child, + .table-responsive > .table-bordered > tbody > tr > th:first-child, + .table-responsive > .table-bordered > tfoot > tr > th:first-child, + .table-responsive > .table-bordered > thead > tr > td:first-child, + .table-responsive > .table-bordered > tbody > tr > td:first-child, + .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; + } + .table-responsive > .table-bordered > thead > tr > th:last-child, + .table-responsive > .table-bordered > tbody > tr > th:last-child, + .table-responsive > .table-bordered > tfoot > tr > th:last-child, + .table-responsive > .table-bordered > thead > tr > td:last-child, + .table-responsive > .table-bordered > tbody > tr > td:last-child, + .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; + } + .table-responsive > .table-bordered > tbody > tr:last-child > th, + .table-responsive > .table-bordered > tfoot > tr:last-child > th, + .table-responsive > .table-bordered > tbody > tr:last-child > td, + .table-responsive > .table-bordered > tfoot > tr:last-child > td { + border-bottom: 0; + } +} +fieldset { + min-width: 0; + padding: 0; + margin: 0; + border: 0; +} +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 20px; + font-size: 21px; + line-height: inherit; + color: #333; + border: 0; + border-bottom: 1px solid #e5e5e5; +} +label { + display: inline-block; + max-width: 100%; + margin-bottom: 5px; + font-weight: bold; +} +input[type="search"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + margin-top: 1px \9; + line-height: normal; +} +input[type="file"] { + display: block; +} +input[type="range"] { + display: block; + width: 100%; +} +select[multiple], +select[size] { + height: auto; +} +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +output { + display: block; + padding-top: 7px; + font-size: 14px; + line-height: 1.42857143; + color: #555; +} +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857143; + color: #555; + background-color: #fff; + background-image: none; + border: 1px solid #ccc; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; + -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; +} +.form-control:focus { + border-color: #66afe9; + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); +} +.form-control::-moz-placeholder { + color: #999; + opacity: 1; +} +.form-control:-ms-input-placeholder { + color: #999; +} +.form-control::-webkit-input-placeholder { + color: #999; +} +.form-control::-ms-expand { + background-color: transparent; + border: 0; +} +.form-control[disabled], +.form-control[readonly], +fieldset[disabled] .form-control { + background-color: #eee; + opacity: 1; +} +.form-control[disabled], +fieldset[disabled] .form-control { + cursor: not-allowed; +} +textarea.form-control { + height: auto; +} +input[type="search"] { + -webkit-appearance: none; +} +@media screen and (-webkit-min-device-pixel-ratio: 0) { + input[type="date"].form-control, + input[type="time"].form-control, + input[type="datetime-local"].form-control, + input[type="month"].form-control { + line-height: 34px; + } + input[type="date"].input-sm, + input[type="time"].input-sm, + input[type="datetime-local"].input-sm, + input[type="month"].input-sm, + .input-group-sm input[type="date"], + .input-group-sm input[type="time"], + .input-group-sm input[type="datetime-local"], + .input-group-sm input[type="month"] { + line-height: 30px; + } + input[type="date"].input-lg, + input[type="time"].input-lg, + input[type="datetime-local"].input-lg, + input[type="month"].input-lg, + .input-group-lg input[type="date"], + .input-group-lg input[type="time"], + .input-group-lg input[type="datetime-local"], + .input-group-lg input[type="month"] { + line-height: 46px; + } +} +.form-group { + margin-bottom: 15px; +} +.radio, +.checkbox { + position: relative; + display: block; + margin-top: 10px; + margin-bottom: 10px; +} +.radio label, +.checkbox label { + min-height: 20px; + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + cursor: pointer; +} +.radio input[type="radio"], +.radio-inline input[type="radio"], +.checkbox input[type="checkbox"], +.checkbox-inline input[type="checkbox"] { + position: absolute; + margin-top: 4px \9; + margin-left: -20px; +} +.radio + .radio, +.checkbox + .checkbox { + margin-top: -5px; +} +.radio-inline, +.checkbox-inline { + position: relative; + display: inline-block; + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + vertical-align: middle; + cursor: pointer; +} +.radio-inline + .radio-inline, +.checkbox-inline + .checkbox-inline { + margin-top: 0; + margin-left: 10px; +} +input[type="radio"][disabled], +input[type="checkbox"][disabled], +input[type="radio"].disabled, +input[type="checkbox"].disabled, +fieldset[disabled] input[type="radio"], +fieldset[disabled] input[type="checkbox"] { + cursor: not-allowed; +} +.radio-inline.disabled, +.checkbox-inline.disabled, +fieldset[disabled] .radio-inline, +fieldset[disabled] .checkbox-inline { + cursor: not-allowed; +} +.radio.disabled label, +.checkbox.disabled label, +fieldset[disabled] .radio label, +fieldset[disabled] .checkbox label { + cursor: not-allowed; +} +.form-control-static { + min-height: 34px; + padding-top: 7px; + padding-bottom: 7px; + margin-bottom: 0; +} +.form-control-static.input-lg, +.form-control-static.input-sm { + padding-right: 0; + padding-left: 0; +} +.input-sm { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-sm { + height: 30px; + line-height: 30px; +} +textarea.input-sm, +select[multiple].input-sm { + height: auto; +} +.form-group-sm .form-control { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.form-group-sm select.form-control { + height: 30px; + line-height: 30px; +} +.form-group-sm textarea.form-control, +.form-group-sm select[multiple].form-control { + height: auto; +} +.form-group-sm .form-control-static { + height: 30px; + min-height: 32px; + padding: 6px 10px; + font-size: 12px; + line-height: 1.5; +} +.input-lg { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +select.input-lg { + height: 46px; + line-height: 46px; +} +textarea.input-lg, +select[multiple].input-lg { + height: auto; +} +.form-group-lg .form-control { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +.form-group-lg select.form-control { + height: 46px; + line-height: 46px; +} +.form-group-lg textarea.form-control, +.form-group-lg select[multiple].form-control { + height: auto; +} +.form-group-lg .form-control-static { + height: 46px; + min-height: 38px; + padding: 11px 16px; + font-size: 18px; + line-height: 1.3333333; +} +.has-feedback { + position: relative; +} +.has-feedback .form-control { + padding-right: 42.5px; +} +.form-control-feedback { + position: absolute; + top: 0; + right: 0; + z-index: 2; + display: block; + width: 34px; + height: 34px; + line-height: 34px; + text-align: center; + pointer-events: none; +} +.input-lg + .form-control-feedback, +.input-group-lg + .form-control-feedback, +.form-group-lg .form-control + .form-control-feedback { + width: 46px; + height: 46px; + line-height: 46px; +} +.input-sm + .form-control-feedback, +.input-group-sm + .form-control-feedback, +.form-group-sm .form-control + .form-control-feedback { + width: 30px; + height: 30px; + line-height: 30px; +} +.has-success .help-block, +.has-success .control-label, +.has-success .radio, +.has-success .checkbox, +.has-success .radio-inline, +.has-success .checkbox-inline, +.has-success.radio label, +.has-success.checkbox label, +.has-success.radio-inline label, +.has-success.checkbox-inline label { + color: #3c763d; +} +.has-success .form-control { + border-color: #3c763d; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-success .form-control:focus { + border-color: #2b542c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; +} +.has-success .input-group-addon { + color: #3c763d; + background-color: #dff0d8; + border-color: #3c763d; +} +.has-success .form-control-feedback { + color: #3c763d; +} +.has-warning .help-block, +.has-warning .control-label, +.has-warning .radio, +.has-warning .checkbox, +.has-warning .radio-inline, +.has-warning .checkbox-inline, +.has-warning.radio label, +.has-warning.checkbox label, +.has-warning.radio-inline label, +.has-warning.checkbox-inline label { + color: #8a6d3b; +} +.has-warning .form-control { + border-color: #8a6d3b; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-warning .form-control:focus { + border-color: #66512c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; +} +.has-warning .input-group-addon { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #8a6d3b; +} +.has-warning .form-control-feedback { + color: #8a6d3b; +} +.has-error .help-block, +.has-error .control-label, +.has-error .radio, +.has-error .checkbox, +.has-error .radio-inline, +.has-error .checkbox-inline, +.has-error.radio label, +.has-error.checkbox label, +.has-error.radio-inline label, +.has-error.checkbox-inline label { + color: #a94442; +} +.has-error .form-control { + border-color: #a94442; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-error .form-control:focus { + border-color: #843534; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; +} +.has-error .input-group-addon { + color: #a94442; + background-color: #f2dede; + border-color: #a94442; +} +.has-error .form-control-feedback { + color: #a94442; +} +.has-feedback label ~ .form-control-feedback { + top: 25px; +} +.has-feedback label.sr-only ~ .form-control-feedback { + top: 0; +} +.help-block { + display: block; + margin-top: 5px; + margin-bottom: 10px; + color: #737373; +} +@media (min-width: 768px) { + .form-inline .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .form-inline .form-control-static { + display: inline-block; + } + .form-inline .input-group { + display: inline-table; + vertical-align: middle; + } + .form-inline .input-group .input-group-addon, + .form-inline .input-group .input-group-btn, + .form-inline .input-group .form-control { + width: auto; + } + .form-inline .input-group > .form-control { + width: 100%; + } + .form-inline .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio, + .form-inline .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio label, + .form-inline .checkbox label { + padding-left: 0; + } + .form-inline .radio input[type="radio"], + .form-inline .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0; + } + .form-inline .has-feedback .form-control-feedback { + top: 0; + } +} +.form-horizontal .radio, +.form-horizontal .checkbox, +.form-horizontal .radio-inline, +.form-horizontal .checkbox-inline { + padding-top: 7px; + margin-top: 0; + margin-bottom: 0; +} +.form-horizontal .radio, +.form-horizontal .checkbox { + min-height: 27px; +} +.form-horizontal .form-group { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .form-horizontal .control-label { + padding-top: 7px; + margin-bottom: 0; + text-align: right; + } +} +.form-horizontal .has-feedback .form-control-feedback { + right: 15px; +} +@media (min-width: 768px) { + .form-horizontal .form-group-lg .control-label { + padding-top: 11px; + font-size: 18px; + } +} +@media (min-width: 768px) { + .form-horizontal .form-group-sm .control-label { + padding-top: 6px; + font-size: 12px; + } +} +.btn { + display: inline-block; + padding: 6px 12px; + margin-bottom: 0; + font-size: 14px; + font-weight: normal; + line-height: 1.42857143; + text-align: center; + white-space: nowrap; + vertical-align: middle; + -ms-touch-action: manipulation; + touch-action: manipulation; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; +} +.btn:focus, +.btn:active:focus, +.btn.active:focus, +.btn.focus, +.btn:active.focus, +.btn.active.focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.btn:hover, +.btn:focus, +.btn.focus { + color: #333; + text-decoration: none; +} +.btn:active, +.btn.active { + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); +} +.btn.disabled, +.btn[disabled], +fieldset[disabled] .btn { + cursor: not-allowed; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + box-shadow: none; + opacity: .65; +} +a.btn.disabled, +fieldset[disabled] a.btn { + pointer-events: none; +} +.btn-default { + color: #333; + background-color: #fff; + border-color: #ccc; +} +.btn-default:focus, +.btn-default.focus { + color: #333; + background-color: #e6e6e6; + border-color: #8c8c8c; +} +.btn-default:hover { + color: #333; + background-color: #e6e6e6; + border-color: #adadad; +} +.btn-default:active, +.btn-default.active, +.open > .dropdown-toggle.btn-default { + color: #333; + background-color: #e6e6e6; + border-color: #adadad; +} +.btn-default:active:hover, +.btn-default.active:hover, +.open > .dropdown-toggle.btn-default:hover, +.btn-default:active:focus, +.btn-default.active:focus, +.open > .dropdown-toggle.btn-default:focus, +.btn-default:active.focus, +.btn-default.active.focus, +.open > .dropdown-toggle.btn-default.focus { + color: #333; + background-color: #d4d4d4; + border-color: #8c8c8c; +} +.btn-default:active, +.btn-default.active, +.open > .dropdown-toggle.btn-default { + background-image: none; +} +.btn-default.disabled:hover, +.btn-default[disabled]:hover, +fieldset[disabled] .btn-default:hover, +.btn-default.disabled:focus, +.btn-default[disabled]:focus, +fieldset[disabled] .btn-default:focus, +.btn-default.disabled.focus, +.btn-default[disabled].focus, +fieldset[disabled] .btn-default.focus { + background-color: #fff; + border-color: #ccc; +} +.btn-default .badge { + color: #fff; + background-color: #333; +} +.btn-primary { + color: #fff; + background-color: #337ab7; + border-color: #2e6da4; +} +.btn-primary:focus, +.btn-primary.focus { + color: #fff; + background-color: #286090; + border-color: #122b40; +} +.btn-primary:hover { + color: #fff; + background-color: #286090; + border-color: #204d74; +} +.btn-primary:active, +.btn-primary.active, +.open > .dropdown-toggle.btn-primary { + color: #fff; + background-color: #286090; + border-color: #204d74; +} +.btn-primary:active:hover, +.btn-primary.active:hover, +.open > .dropdown-toggle.btn-primary:hover, +.btn-primary:active:focus, +.btn-primary.active:focus, +.open > .dropdown-toggle.btn-primary:focus, +.btn-primary:active.focus, +.btn-primary.active.focus, +.open > .dropdown-toggle.btn-primary.focus { + color: #fff; + background-color: #204d74; + border-color: #122b40; +} +.btn-primary:active, +.btn-primary.active, +.open > .dropdown-toggle.btn-primary { + background-image: none; +} +.btn-primary.disabled:hover, +.btn-primary[disabled]:hover, +fieldset[disabled] .btn-primary:hover, +.btn-primary.disabled:focus, +.btn-primary[disabled]:focus, +fieldset[disabled] .btn-primary:focus, +.btn-primary.disabled.focus, +.btn-primary[disabled].focus, +fieldset[disabled] .btn-primary.focus { + background-color: #337ab7; + border-color: #2e6da4; +} +.btn-primary .badge { + color: #337ab7; + background-color: #fff; +} +.btn-success { + color: #fff; + background-color: #5cb85c; + border-color: #4cae4c; +} +.btn-success:focus, +.btn-success.focus { + color: #fff; + background-color: #449d44; + border-color: #255625; +} +.btn-success:hover { + color: #fff; + background-color: #449d44; + border-color: #398439; +} +.btn-success:active, +.btn-success.active, +.open > .dropdown-toggle.btn-success { + color: #fff; + background-color: #449d44; + border-color: #398439; +} +.btn-success:active:hover, +.btn-success.active:hover, +.open > .dropdown-toggle.btn-success:hover, +.btn-success:active:focus, +.btn-success.active:focus, +.open > .dropdown-toggle.btn-success:focus, +.btn-success:active.focus, +.btn-success.active.focus, +.open > .dropdown-toggle.btn-success.focus { + color: #fff; + background-color: #398439; + border-color: #255625; +} +.btn-success:active, +.btn-success.active, +.open > .dropdown-toggle.btn-success { + background-image: none; +} +.btn-success.disabled:hover, +.btn-success[disabled]:hover, +fieldset[disabled] .btn-success:hover, +.btn-success.disabled:focus, +.btn-success[disabled]:focus, +fieldset[disabled] .btn-success:focus, +.btn-success.disabled.focus, +.btn-success[disabled].focus, +fieldset[disabled] .btn-success.focus { + background-color: #5cb85c; + border-color: #4cae4c; +} +.btn-success .badge { + color: #5cb85c; + background-color: #fff; +} +.btn-info { + color: #fff; + background-color: #5bc0de; + border-color: #46b8da; +} +.btn-info:focus, +.btn-info.focus { + color: #fff; + background-color: #31b0d5; + border-color: #1b6d85; +} +.btn-info:hover { + color: #fff; + background-color: #31b0d5; + border-color: #269abc; +} +.btn-info:active, +.btn-info.active, +.open > .dropdown-toggle.btn-info { + color: #fff; + background-color: #31b0d5; + border-color: #269abc; +} +.btn-info:active:hover, +.btn-info.active:hover, +.open > .dropdown-toggle.btn-info:hover, +.btn-info:active:focus, +.btn-info.active:focus, +.open > .dropdown-toggle.btn-info:focus, +.btn-info:active.focus, +.btn-info.active.focus, +.open > .dropdown-toggle.btn-info.focus { + color: #fff; + background-color: #269abc; + border-color: #1b6d85; +} +.btn-info:active, +.btn-info.active, +.open > .dropdown-toggle.btn-info { + background-image: none; +} +.btn-info.disabled:hover, +.btn-info[disabled]:hover, +fieldset[disabled] .btn-info:hover, +.btn-info.disabled:focus, +.btn-info[disabled]:focus, +fieldset[disabled] .btn-info:focus, +.btn-info.disabled.focus, +.btn-info[disabled].focus, +fieldset[disabled] .btn-info.focus { + background-color: #5bc0de; + border-color: #46b8da; +} +.btn-info .badge { + color: #5bc0de; + background-color: #fff; +} +.btn-warning { + color: #fff; + background-color: #f0ad4e; + border-color: #eea236; +} +.btn-warning:focus, +.btn-warning.focus { + color: #fff; + background-color: #ec971f; + border-color: #985f0d; +} +.btn-warning:hover { + color: #fff; + background-color: #ec971f; + border-color: #d58512; +} +.btn-warning:active, +.btn-warning.active, +.open > .dropdown-toggle.btn-warning { + color: #fff; + background-color: #ec971f; + border-color: #d58512; +} +.btn-warning:active:hover, +.btn-warning.active:hover, +.open > .dropdown-toggle.btn-warning:hover, +.btn-warning:active:focus, +.btn-warning.active:focus, +.open > .dropdown-toggle.btn-warning:focus, +.btn-warning:active.focus, +.btn-warning.active.focus, +.open > .dropdown-toggle.btn-warning.focus { + color: #fff; + background-color: #d58512; + border-color: #985f0d; +} +.btn-warning:active, +.btn-warning.active, +.open > .dropdown-toggle.btn-warning { + background-image: none; +} +.btn-warning.disabled:hover, +.btn-warning[disabled]:hover, +fieldset[disabled] .btn-warning:hover, +.btn-warning.disabled:focus, +.btn-warning[disabled]:focus, +fieldset[disabled] .btn-warning:focus, +.btn-warning.disabled.focus, +.btn-warning[disabled].focus, +fieldset[disabled] .btn-warning.focus { + background-color: #f0ad4e; + border-color: #eea236; +} +.btn-warning .badge { + color: #f0ad4e; + background-color: #fff; +} +.btn-danger { + color: #fff; + background-color: #d9534f; + border-color: #d43f3a; +} +.btn-danger:focus, +.btn-danger.focus { + color: #fff; + background-color: #c9302c; + border-color: #761c19; +} +.btn-danger:hover { + color: #fff; + background-color: #c9302c; + border-color: #ac2925; +} +.btn-danger:active, +.btn-danger.active, +.open > .dropdown-toggle.btn-danger { + color: #fff; + background-color: #c9302c; + border-color: #ac2925; +} +.btn-danger:active:hover, +.btn-danger.active:hover, +.open > .dropdown-toggle.btn-danger:hover, +.btn-danger:active:focus, +.btn-danger.active:focus, +.open > .dropdown-toggle.btn-danger:focus, +.btn-danger:active.focus, +.btn-danger.active.focus, +.open > .dropdown-toggle.btn-danger.focus { + color: #fff; + background-color: #ac2925; + border-color: #761c19; +} +.btn-danger:active, +.btn-danger.active, +.open > .dropdown-toggle.btn-danger { + background-image: none; +} +.btn-danger.disabled:hover, +.btn-danger[disabled]:hover, +fieldset[disabled] .btn-danger:hover, +.btn-danger.disabled:focus, +.btn-danger[disabled]:focus, +fieldset[disabled] .btn-danger:focus, +.btn-danger.disabled.focus, +.btn-danger[disabled].focus, +fieldset[disabled] .btn-danger.focus { + background-color: #d9534f; + border-color: #d43f3a; +} +.btn-danger .badge { + color: #d9534f; + background-color: #fff; +} +.btn-link { + font-weight: normal; + color: #337ab7; + border-radius: 0; +} +.btn-link, +.btn-link:active, +.btn-link.active, +.btn-link[disabled], +fieldset[disabled] .btn-link { + background-color: transparent; + -webkit-box-shadow: none; + box-shadow: none; +} +.btn-link, +.btn-link:hover, +.btn-link:focus, +.btn-link:active { + border-color: transparent; +} +.btn-link:hover, +.btn-link:focus { + color: #23527c; + text-decoration: underline; + background-color: transparent; +} +.btn-link[disabled]:hover, +fieldset[disabled] .btn-link:hover, +.btn-link[disabled]:focus, +fieldset[disabled] .btn-link:focus { + color: #777; + text-decoration: none; +} +.btn-lg, +.btn-group-lg > .btn { + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +.btn-sm, +.btn-group-sm > .btn { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-xs, +.btn-group-xs > .btn { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-block { + display: block; + width: 100%; +} +.btn-block + .btn-block { + margin-top: 5px; +} +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} +.fade { + opacity: 0; + -webkit-transition: opacity .15s linear; + -o-transition: opacity .15s linear; + transition: opacity .15s linear; +} +.fade.in { + opacity: 1; +} +.collapse { + display: none; +} +.collapse.in { + display: block; +} +tr.collapse.in { + display: table-row; +} +tbody.collapse.in { + display: table-row-group; +} +.collapsing { + position: relative; + height: 0; + overflow: hidden; + -webkit-transition-timing-function: ease; + -o-transition-timing-function: ease; + transition-timing-function: ease; + -webkit-transition-duration: .35s; + -o-transition-duration: .35s; + transition-duration: .35s; + -webkit-transition-property: height, visibility; + -o-transition-property: height, visibility; + transition-property: height, visibility; +} +.caret { + display: inline-block; + width: 0; + height: 0; + margin-left: 2px; + vertical-align: middle; + border-top: 4px dashed; + border-top: 4px solid \9; + border-right: 4px solid transparent; + border-left: 4px solid transparent; +} +.dropup, +.dropdown { + position: relative; +} +.dropdown-toggle:focus { + outline: 0; +} +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + font-size: 14px; + text-align: left; + list-style: none; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .15); + border-radius: 4px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); + box-shadow: 0 6px 12px rgba(0, 0, 0, .175); +} +.dropdown-menu.pull-right { + right: 0; + left: auto; +} +.dropdown-menu .divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.dropdown-menu > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 1.42857143; + color: #333; + white-space: nowrap; +} +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus { + color: #262626; + text-decoration: none; + background-color: #f5f5f5; +} +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + color: #fff; + text-decoration: none; + background-color: #337ab7; + outline: 0; +} +.dropdown-menu > .disabled > a, +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + color: #777; +} +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + text-decoration: none; + cursor: not-allowed; + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.open > .dropdown-menu { + display: block; +} +.open > a { + outline: 0; +} +.dropdown-menu-right { + right: 0; + left: auto; +} +.dropdown-menu-left { + right: auto; + left: 0; +} +.dropdown-header { + display: block; + padding: 3px 20px; + font-size: 12px; + line-height: 1.42857143; + color: #777; + white-space: nowrap; +} +.dropdown-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 990; +} +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} +.dropup .caret, +.navbar-fixed-bottom .dropdown .caret { + content: ""; + border-top: 0; + border-bottom: 4px dashed; + border-bottom: 4px solid \9; +} +.dropup .dropdown-menu, +.navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 2px; +} +@media (min-width: 768px) { + .navbar-right .dropdown-menu { + right: 0; + left: auto; + } + .navbar-right .dropdown-menu-left { + right: auto; + left: 0; + } +} +.btn-group, +.btn-group-vertical { + position: relative; + display: inline-block; + vertical-align: middle; +} +.btn-group > .btn, +.btn-group-vertical > .btn { + position: relative; + float: left; +} +.btn-group > .btn:hover, +.btn-group-vertical > .btn:hover, +.btn-group > .btn:focus, +.btn-group-vertical > .btn:focus, +.btn-group > .btn:active, +.btn-group-vertical > .btn:active, +.btn-group > .btn.active, +.btn-group-vertical > .btn.active { + z-index: 2; +} +.btn-group .btn + .btn, +.btn-group .btn + .btn-group, +.btn-group .btn-group + .btn, +.btn-group .btn-group + .btn-group { + margin-left: -1px; +} +.btn-toolbar { + margin-left: -5px; +} +.btn-toolbar .btn, +.btn-toolbar .btn-group, +.btn-toolbar .input-group { + float: left; +} +.btn-toolbar > .btn, +.btn-toolbar > .btn-group, +.btn-toolbar > .input-group { + margin-left: 5px; +} +.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { + border-radius: 0; +} +.btn-group > .btn:first-child { + margin-left: 0; +} +.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.btn-group > .btn:last-child:not(:first-child), +.btn-group > .dropdown-toggle:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group > .btn-group { + float: left; +} +.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child, +.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} +.btn-group > .btn + .dropdown-toggle { + padding-right: 8px; + padding-left: 8px; +} +.btn-group > .btn-lg + .dropdown-toggle { + padding-right: 12px; + padding-left: 12px; +} +.btn-group.open .dropdown-toggle { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); +} +.btn-group.open .dropdown-toggle.btn-link { + -webkit-box-shadow: none; + box-shadow: none; +} +.btn .caret { + margin-left: 0; +} +.btn-lg .caret { + border-width: 5px 5px 0; + border-bottom-width: 0; +} +.dropup .btn-lg .caret { + border-width: 0 5px 5px; +} +.btn-group-vertical > .btn, +.btn-group-vertical > .btn-group, +.btn-group-vertical > .btn-group > .btn { + display: block; + float: none; + width: 100%; + max-width: 100%; +} +.btn-group-vertical > .btn-group > .btn { + float: none; +} +.btn-group-vertical > .btn + .btn, +.btn-group-vertical > .btn + .btn-group, +.btn-group-vertical > .btn-group + .btn, +.btn-group-vertical > .btn-group + .btn-group { + margin-top: -1px; + margin-left: 0; +} +.btn-group-vertical > .btn:not(:first-child):not(:last-child) { + border-radius: 0; +} +.btn-group-vertical > .btn:first-child:not(:last-child) { + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn:last-child:not(:first-child) { + border-top-left-radius: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.btn-group-justified { + display: table; + width: 100%; + table-layout: fixed; + border-collapse: separate; +} +.btn-group-justified > .btn, +.btn-group-justified > .btn-group { + display: table-cell; + float: none; + width: 1%; +} +.btn-group-justified > .btn-group .btn { + width: 100%; +} +.btn-group-justified > .btn-group .dropdown-menu { + left: auto; +} +[data-toggle="buttons"] > .btn input[type="radio"], +[data-toggle="buttons"] > .btn-group > .btn input[type="radio"], +[data-toggle="buttons"] > .btn input[type="checkbox"], +[data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] { + position: absolute; + clip: rect(0, 0, 0, 0); + pointer-events: none; +} +.input-group { + position: relative; + display: table; + border-collapse: separate; +} +.input-group[class*="col-"] { + float: none; + padding-right: 0; + padding-left: 0; +} +.input-group .form-control { + position: relative; + z-index: 2; + float: left; + width: 100%; + margin-bottom: 0; +} +.input-group .form-control:focus { + z-index: 3; +} +.input-group-lg > .form-control, +.input-group-lg > .input-group-addon, +.input-group-lg > .input-group-btn > .btn { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; +} +select.input-group-lg > .form-control, +select.input-group-lg > .input-group-addon, +select.input-group-lg > .input-group-btn > .btn { + height: 46px; + line-height: 46px; +} +textarea.input-group-lg > .form-control, +textarea.input-group-lg > .input-group-addon, +textarea.input-group-lg > .input-group-btn > .btn, +select[multiple].input-group-lg > .form-control, +select[multiple].input-group-lg > .input-group-addon, +select[multiple].input-group-lg > .input-group-btn > .btn { + height: auto; +} +.input-group-sm > .form-control, +.input-group-sm > .input-group-addon, +.input-group-sm > .input-group-btn > .btn { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-group-sm > .form-control, +select.input-group-sm > .input-group-addon, +select.input-group-sm > .input-group-btn > .btn { + height: 30px; + line-height: 30px; +} +textarea.input-group-sm > .form-control, +textarea.input-group-sm > .input-group-addon, +textarea.input-group-sm > .input-group-btn > .btn, +select[multiple].input-group-sm > .form-control, +select[multiple].input-group-sm > .input-group-addon, +select[multiple].input-group-sm > .input-group-btn > .btn { + height: auto; +} +.input-group-addon, +.input-group-btn, +.input-group .form-control { + display: table-cell; +} +.input-group-addon:not(:first-child):not(:last-child), +.input-group-btn:not(:first-child):not(:last-child), +.input-group .form-control:not(:first-child):not(:last-child) { + border-radius: 0; +} +.input-group-addon, +.input-group-btn { + width: 1%; + white-space: nowrap; + vertical-align: middle; +} +.input-group-addon { + padding: 6px 12px; + font-size: 14px; + font-weight: normal; + line-height: 1; + color: #555; + text-align: center; + background-color: #eee; + border: 1px solid #ccc; + border-radius: 4px; +} +.input-group-addon.input-sm { + padding: 5px 10px; + font-size: 12px; + border-radius: 3px; +} +.input-group-addon.input-lg { + padding: 10px 16px; + font-size: 18px; + border-radius: 6px; +} +.input-group-addon input[type="radio"], +.input-group-addon input[type="checkbox"] { + margin-top: 0; +} +.input-group .form-control:first-child, +.input-group-addon:first-child, +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group > .btn, +.input-group-btn:first-child > .dropdown-toggle, +.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), +.input-group-btn:last-child > .btn-group:not(:last-child) > .btn { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.input-group-addon:first-child { + border-right: 0; +} +.input-group .form-control:last-child, +.input-group-addon:last-child, +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group > .btn, +.input-group-btn:last-child > .dropdown-toggle, +.input-group-btn:first-child > .btn:not(:first-child), +.input-group-btn:first-child > .btn-group:not(:first-child) > .btn { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.input-group-addon:last-child { + border-left: 0; +} +.input-group-btn { + position: relative; + font-size: 0; + white-space: nowrap; +} +.input-group-btn > .btn { + position: relative; +} +.input-group-btn > .btn + .btn { + margin-left: -1px; +} +.input-group-btn > .btn:hover, +.input-group-btn > .btn:focus, +.input-group-btn > .btn:active { + z-index: 2; +} +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group { + margin-right: -1px; +} +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group { + z-index: 2; + margin-left: -1px; +} +.nav { + padding-left: 0; + margin-bottom: 0; + list-style: none; +} +.nav > li { + position: relative; + display: block; +} +.nav > li > a { + position: relative; + display: block; + padding: 10px 15px; +} +.nav > li > a:hover, +.nav > li > a:focus { + text-decoration: none; + background-color: #eee; +} +.nav > li.disabled > a { + color: #777; +} +.nav > li.disabled > a:hover, +.nav > li.disabled > a:focus { + color: #777; + text-decoration: none; + cursor: not-allowed; + background-color: transparent; +} +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + background-color: #eee; + border-color: #337ab7; +} +.nav .nav-divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.nav > li > a > img { + max-width: none; +} +.nav-tabs { + border-bottom: 1px solid #ddd; +} +.nav-tabs > li { + float: left; + margin-bottom: -1px; +} +.nav-tabs > li > a { + margin-right: 2px; + line-height: 1.42857143; + border: 1px solid transparent; + border-radius: 4px 4px 0 0; +} +.nav-tabs > li > a:hover { + border-color: #eee #eee #ddd; +} +.nav-tabs > li.active > a, +.nav-tabs > li.active > a:hover, +.nav-tabs > li.active > a:focus { + color: #555; + cursor: default; + background-color: #fff; + border: 1px solid #ddd; + border-bottom-color: transparent; +} +.nav-tabs.nav-justified { + width: 100%; + border-bottom: 0; +} +.nav-tabs.nav-justified > li { + float: none; +} +.nav-tabs.nav-justified > li > a { + margin-bottom: 5px; + text-align: center; +} +.nav-tabs.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-tabs.nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs.nav-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs.nav-justified > .active > a, +.nav-tabs.nav-justified > .active > a:hover, +.nav-tabs.nav-justified > .active > a:focus { + border: 1px solid #ddd; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li > a { + border-bottom: 1px solid #ddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs.nav-justified > .active > a, + .nav-tabs.nav-justified > .active > a:hover, + .nav-tabs.nav-justified > .active > a:focus { + border-bottom-color: #fff; + } +} +.nav-pills > li { + float: left; +} +.nav-pills > li > a { + border-radius: 4px; +} +.nav-pills > li + li { + margin-left: 2px; +} +.nav-pills > li.active > a, +.nav-pills > li.active > a:hover, +.nav-pills > li.active > a:focus { + color: #fff; + background-color: #337ab7; +} +.nav-stacked > li { + float: none; +} +.nav-stacked > li + li { + margin-top: 2px; + margin-left: 0; +} +.nav-justified { + width: 100%; +} +.nav-justified > li { + float: none; +} +.nav-justified > li > a { + margin-bottom: 5px; + text-align: center; +} +.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs-justified { + border-bottom: 0; +} +.nav-tabs-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs-justified > .active > a, +.nav-tabs-justified > .active > a:hover, +.nav-tabs-justified > .active > a:focus { + border: 1px solid #ddd; +} +@media (min-width: 768px) { + .nav-tabs-justified > li > a { + border-bottom: 1px solid #ddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs-justified > .active > a, + .nav-tabs-justified > .active > a:hover, + .nav-tabs-justified > .active > a:focus { + border-bottom-color: #fff; + } +} +.tab-content > .tab-pane { + display: none; +} +.tab-content > .active { + display: block; +} +.nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.navbar { + position: relative; + min-height: 50px; + margin-bottom: 20px; + border: 1px solid transparent; +} +@media (min-width: 768px) { + .navbar { + border-radius: 4px; + } +} +@media (min-width: 768px) { + .navbar-header { + float: left; + } +} +.navbar-collapse { + padding-right: 15px; + padding-left: 15px; + overflow-x: visible; + -webkit-overflow-scrolling: touch; + border-top: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); +} +.navbar-collapse.in { + overflow-y: auto; +} +@media (min-width: 768px) { + .navbar-collapse { + width: auto; + border-top: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-collapse.collapse { + display: block !important; + height: auto !important; + padding-bottom: 0; + overflow: visible !important; + } + .navbar-collapse.in { + overflow-y: visible; + } + .navbar-fixed-top .navbar-collapse, + .navbar-static-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + padding-right: 0; + padding-left: 0; + } +} +.navbar-fixed-top .navbar-collapse, +.navbar-fixed-bottom .navbar-collapse { + max-height: 340px; +} +@media (max-device-width: 480px) and (orientation: landscape) { + .navbar-fixed-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + max-height: 200px; + } +} +.container > .navbar-header, +.container-fluid > .navbar-header, +.container > .navbar-collapse, +.container-fluid > .navbar-collapse { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .container > .navbar-header, + .container-fluid > .navbar-header, + .container > .navbar-collapse, + .container-fluid > .navbar-collapse { + margin-right: 0; + margin-left: 0; + } +} +.navbar-static-top { + z-index: 1000; + border-width: 0 0 1px; +} +@media (min-width: 768px) { + .navbar-static-top { + border-radius: 0; + } +} +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; +} +@media (min-width: 768px) { + .navbar-fixed-top, + .navbar-fixed-bottom { + border-radius: 0; + } +} +.navbar-fixed-top { + top: 0; + border-width: 0 0 1px; +} +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; + border-width: 1px 0 0; +} +.navbar-brand { + float: left; + height: 50px; + padding: 15px 15px; + font-size: 18px; + line-height: 20px; +} +.navbar-brand:hover, +.navbar-brand:focus { + text-decoration: none; +} +.navbar-brand > img { + display: block; +} +@media (min-width: 768px) { + .navbar > .container .navbar-brand, + .navbar > .container-fluid .navbar-brand { + margin-left: -15px; + } +} +.navbar-toggle { + position: relative; + float: right; + padding: 9px 10px; + margin-top: 8px; + margin-right: 15px; + margin-bottom: 8px; + background-color: transparent; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; +} +.navbar-toggle:focus { + outline: 0; +} +.navbar-toggle .icon-bar { + display: block; + width: 22px; + height: 2px; + border-radius: 1px; +} +.navbar-toggle .icon-bar + .icon-bar { + margin-top: 4px; +} +@media (min-width: 768px) { + .navbar-toggle { + display: none; + } +} +.navbar-nav { + margin: 7.5px -15px; +} +.navbar-nav > li > a { + padding-top: 10px; + padding-bottom: 10px; + line-height: 20px; +} +@media (max-width: 767px) { + .navbar-nav .open .dropdown-menu { + position: static; + float: none; + width: auto; + margin-top: 0; + background-color: transparent; + border: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-nav .open .dropdown-menu > li > a, + .navbar-nav .open .dropdown-menu .dropdown-header { + padding: 5px 15px 5px 25px; + } + .navbar-nav .open .dropdown-menu > li > a { + line-height: 20px; + } + .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-nav .open .dropdown-menu > li > a:focus { + background-image: none; + } +} +@media (min-width: 768px) { + .navbar-nav { + float: left; + margin: 0; + } + .navbar-nav > li { + float: left; + } + .navbar-nav > li > a { + padding-top: 15px; + padding-bottom: 15px; + } +} +.navbar-form { + padding: 10px 15px; + margin-top: 8px; + margin-right: -15px; + margin-bottom: 8px; + margin-left: -15px; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); +} +@media (min-width: 768px) { + .navbar-form .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .navbar-form .form-control-static { + display: inline-block; + } + .navbar-form .input-group { + display: inline-table; + vertical-align: middle; + } + .navbar-form .input-group .input-group-addon, + .navbar-form .input-group .input-group-btn, + .navbar-form .input-group .form-control { + width: auto; + } + .navbar-form .input-group > .form-control { + width: 100%; + } + .navbar-form .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio, + .navbar-form .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio label, + .navbar-form .checkbox label { + padding-left: 0; + } + .navbar-form .radio input[type="radio"], + .navbar-form .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0; + } + .navbar-form .has-feedback .form-control-feedback { + top: 0; + } +} +@media (max-width: 767px) { + .navbar-form .form-group { + margin-bottom: 5px; + } + .navbar-form .form-group:last-child { + margin-bottom: 0; + } +} +@media (min-width: 768px) { + .navbar-form { + width: auto; + padding-top: 0; + padding-bottom: 0; + margin-right: 0; + margin-left: 0; + border: 0; + -webkit-box-shadow: none; + box-shadow: none; + } +} +.navbar-nav > li > .dropdown-menu { + margin-top: 0; + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + margin-bottom: 0; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.navbar-btn { + margin-top: 8px; + margin-bottom: 8px; +} +.navbar-btn.btn-sm { + margin-top: 10px; + margin-bottom: 10px; +} +.navbar-btn.btn-xs { + margin-top: 14px; + margin-bottom: 14px; +} +.navbar-text { + margin-top: 15px; + margin-bottom: 15px; +} +@media (min-width: 768px) { + .navbar-text { + float: left; + margin-right: 15px; + margin-left: 15px; + } +} +@media (min-width: 768px) { + .navbar-left { + float: left !important; + } + .navbar-right { + float: right !important; + margin-right: -15px; + } + .navbar-right ~ .navbar-right { + margin-right: 0; + } +} +.navbar-default { + background-color: #f8f8f8; + border-color: #e7e7e7; +} +.navbar-default .navbar-brand { + color: #777; +} +.navbar-default .navbar-brand:hover, +.navbar-default .navbar-brand:focus { + color: #5e5e5e; + background-color: transparent; +} +.navbar-default .navbar-text { + color: #777; +} +.navbar-default .navbar-nav > li > a { + color: #777; +} +.navbar-default .navbar-nav > li > a:hover, +.navbar-default .navbar-nav > li > a:focus { + color: #333; + background-color: transparent; +} +.navbar-default .navbar-nav > .active > a, +.navbar-default .navbar-nav > .active > a:hover, +.navbar-default .navbar-nav > .active > a:focus { + color: #555; + background-color: #e7e7e7; +} +.navbar-default .navbar-nav > .disabled > a, +.navbar-default .navbar-nav > .disabled > a:hover, +.navbar-default .navbar-nav > .disabled > a:focus { + color: #ccc; + background-color: transparent; +} +.navbar-default .navbar-toggle { + border-color: #ddd; +} +.navbar-default .navbar-toggle:hover, +.navbar-default .navbar-toggle:focus { + background-color: #ddd; +} +.navbar-default .navbar-toggle .icon-bar { + background-color: #888; +} +.navbar-default .navbar-collapse, +.navbar-default .navbar-form { + border-color: #e7e7e7; +} +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .open > a:hover, +.navbar-default .navbar-nav > .open > a:focus { + color: #555; + background-color: #e7e7e7; +} +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #777; + } + .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { + color: #333; + background-color: transparent; + } + .navbar-default .navbar-nav .open .dropdown-menu > .active > a, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #555; + background-color: #e7e7e7; + } + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #ccc; + background-color: transparent; + } +} +.navbar-default .navbar-link { + color: #777; +} +.navbar-default .navbar-link:hover { + color: #333; +} +.navbar-default .btn-link { + color: #777; +} +.navbar-default .btn-link:hover, +.navbar-default .btn-link:focus { + color: #333; +} +.navbar-default .btn-link[disabled]:hover, +fieldset[disabled] .navbar-default .btn-link:hover, +.navbar-default .btn-link[disabled]:focus, +fieldset[disabled] .navbar-default .btn-link:focus { + color: #ccc; +} +.navbar-inverse { + background-color: #222; + border-color: #080808; +} +.navbar-inverse .navbar-brand { + color: #9d9d9d; +} +.navbar-inverse .navbar-brand:hover, +.navbar-inverse .navbar-brand:focus { + color: #fff; + background-color: transparent; +} +.navbar-inverse .navbar-text { + color: #9d9d9d; +} +.navbar-inverse .navbar-nav > li > a { + color: #9d9d9d; +} +.navbar-inverse .navbar-nav > li > a:hover, +.navbar-inverse .navbar-nav > li > a:focus { + color: #fff; + background-color: transparent; +} +.navbar-inverse .navbar-nav > .active > a, +.navbar-inverse .navbar-nav > .active > a:hover, +.navbar-inverse .navbar-nav > .active > a:focus { + color: #fff; + background-color: #080808; +} +.navbar-inverse .navbar-nav > .disabled > a, +.navbar-inverse .navbar-nav > .disabled > a:hover, +.navbar-inverse .navbar-nav > .disabled > a:focus { + color: #444; + background-color: transparent; +} +.navbar-inverse .navbar-toggle { + border-color: #333; +} +.navbar-inverse .navbar-toggle:hover, +.navbar-inverse .navbar-toggle:focus { + background-color: #333; +} +.navbar-inverse .navbar-toggle .icon-bar { + background-color: #fff; +} +.navbar-inverse .navbar-collapse, +.navbar-inverse .navbar-form { + border-color: #101010; +} +.navbar-inverse .navbar-nav > .open > a, +.navbar-inverse .navbar-nav > .open > a:hover, +.navbar-inverse .navbar-nav > .open > a:focus { + color: #fff; + background-color: #080808; +} +@media (max-width: 767px) { + .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { + border-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu .divider { + background-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { + color: #9d9d9d; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { + color: #fff; + background-color: transparent; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #fff; + background-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #444; + background-color: transparent; + } +} +.navbar-inverse .navbar-link { + color: #9d9d9d; +} +.navbar-inverse .navbar-link:hover { + color: #fff; +} +.navbar-inverse .btn-link { + color: #9d9d9d; +} +.navbar-inverse .btn-link:hover, +.navbar-inverse .btn-link:focus { + color: #fff; +} +.navbar-inverse .btn-link[disabled]:hover, +fieldset[disabled] .navbar-inverse .btn-link:hover, +.navbar-inverse .btn-link[disabled]:focus, +fieldset[disabled] .navbar-inverse .btn-link:focus { + color: #444; +} +.breadcrumb { + padding: 8px 15px; + margin-bottom: 20px; + list-style: none; + background-color: #f5f5f5; + border-radius: 4px; +} +.breadcrumb > li { + display: inline-block; +} +.breadcrumb > li + li:before { + padding: 0 5px; + color: #ccc; + content: "/\00a0"; +} +.breadcrumb > .active { + color: #777; +} +.pagination { + display: inline-block; + padding-left: 0; + margin: 20px 0; + border-radius: 4px; +} +.pagination > li { + display: inline; +} +.pagination > li > a, +.pagination > li > span { + position: relative; + float: left; + padding: 6px 12px; + margin-left: -1px; + line-height: 1.42857143; + color: #337ab7; + text-decoration: none; + background-color: #fff; + border: 1px solid #ddd; +} +.pagination > li:first-child > a, +.pagination > li:first-child > span { + margin-left: 0; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; +} +.pagination > li:last-child > a, +.pagination > li:last-child > span { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} +.pagination > li > a:hover, +.pagination > li > span:hover, +.pagination > li > a:focus, +.pagination > li > span:focus { + z-index: 2; + color: #23527c; + background-color: #eee; + border-color: #ddd; +} +.pagination > .active > a, +.pagination > .active > span, +.pagination > .active > a:hover, +.pagination > .active > span:hover, +.pagination > .active > a:focus, +.pagination > .active > span:focus { + z-index: 3; + color: #fff; + cursor: default; + background-color: #337ab7; + border-color: #337ab7; +} +.pagination > .disabled > span, +.pagination > .disabled > span:hover, +.pagination > .disabled > span:focus, +.pagination > .disabled > a, +.pagination > .disabled > a:hover, +.pagination > .disabled > a:focus { + color: #777; + cursor: not-allowed; + background-color: #fff; + border-color: #ddd; +} +.pagination-lg > li > a, +.pagination-lg > li > span { + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; +} +.pagination-lg > li:first-child > a, +.pagination-lg > li:first-child > span { + border-top-left-radius: 6px; + border-bottom-left-radius: 6px; +} +.pagination-lg > li:last-child > a, +.pagination-lg > li:last-child > span { + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; +} +.pagination-sm > li > a, +.pagination-sm > li > span { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; +} +.pagination-sm > li:first-child > a, +.pagination-sm > li:first-child > span { + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; +} +.pagination-sm > li:last-child > a, +.pagination-sm > li:last-child > span { + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; +} +.pager { + padding-left: 0; + margin: 20px 0; + text-align: center; + list-style: none; +} +.pager li { + display: inline; +} +.pager li > a, +.pager li > span { + display: inline-block; + padding: 5px 14px; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 15px; +} +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + background-color: #eee; +} +.pager .next > a, +.pager .next > span { + float: right; +} +.pager .previous > a, +.pager .previous > span { + float: left; +} +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: #777; + cursor: not-allowed; + background-color: #fff; +} +.label { + display: inline; + padding: .2em .6em .3em; + font-size: 75%; + font-weight: bold; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25em; +} +a.label:hover, +a.label:focus { + color: #fff; + text-decoration: none; + cursor: pointer; +} +.label:empty { + display: none; +} +.btn .label { + position: relative; + top: -1px; +} +.label-default { + background-color: #777; +} +.label-default[href]:hover, +.label-default[href]:focus { + background-color: #5e5e5e; +} +.label-primary { + background-color: #337ab7; +} +.label-primary[href]:hover, +.label-primary[href]:focus { + background-color: #286090; +} +.label-success { + background-color: #5cb85c; +} +.label-success[href]:hover, +.label-success[href]:focus { + background-color: #449d44; +} +.label-info { + background-color: #5bc0de; +} +.label-info[href]:hover, +.label-info[href]:focus { + background-color: #31b0d5; +} +.label-warning { + background-color: #f0ad4e; +} +.label-warning[href]:hover, +.label-warning[href]:focus { + background-color: #ec971f; +} +.label-danger { + background-color: #d9534f; +} +.label-danger[href]:hover, +.label-danger[href]:focus { + background-color: #c9302c; +} +.badge { + display: inline-block; + min-width: 10px; + padding: 3px 7px; + font-size: 12px; + font-weight: bold; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: middle; + background-color: #777; + border-radius: 10px; +} +.badge:empty { + display: none; +} +.btn .badge { + position: relative; + top: -1px; +} +.btn-xs .badge, +.btn-group-xs > .btn .badge { + top: 0; + padding: 1px 5px; +} +a.badge:hover, +a.badge:focus { + color: #fff; + text-decoration: none; + cursor: pointer; +} +.list-group-item.active > .badge, +.nav-pills > .active > a > .badge { + color: #337ab7; + background-color: #fff; +} +.list-group-item > .badge { + float: right; +} +.list-group-item > .badge + .badge { + margin-right: 5px; +} +.nav-pills > li > a > .badge { + margin-left: 3px; +} +.jumbotron { + padding-top: 30px; + padding-bottom: 30px; + margin-bottom: 30px; + color: inherit; + background-color: #eee; +} +.jumbotron h1, +.jumbotron .h1 { + color: inherit; +} +.jumbotron p { + margin-bottom: 15px; + font-size: 21px; + font-weight: 200; +} +.jumbotron > hr { + border-top-color: #d5d5d5; +} +.container .jumbotron, +.container-fluid .jumbotron { + padding-right: 15px; + padding-left: 15px; + border-radius: 6px; +} +.jumbotron .container { + max-width: 100%; +} +@media screen and (min-width: 768px) { + .jumbotron { + padding-top: 48px; + padding-bottom: 48px; + } + .container .jumbotron, + .container-fluid .jumbotron { + padding-right: 60px; + padding-left: 60px; + } + .jumbotron h1, + .jumbotron .h1 { + font-size: 63px; + } +} +.thumbnail { + display: block; + padding: 4px; + margin-bottom: 20px; + line-height: 1.42857143; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -webkit-transition: border .2s ease-in-out; + -o-transition: border .2s ease-in-out; + transition: border .2s ease-in-out; +} +.thumbnail > img, +.thumbnail a > img { + margin-right: auto; + margin-left: auto; +} +a.thumbnail:hover, +a.thumbnail:focus, +a.thumbnail.active { + border-color: #337ab7; +} +.thumbnail .caption { + padding: 9px; + color: #333; +} +.alert { + padding: 15px; + margin-bottom: 20px; + border: 1px solid transparent; + border-radius: 4px; +} +.alert h4 { + margin-top: 0; + color: inherit; +} +.alert .alert-link { + font-weight: bold; +} +.alert > p, +.alert > ul { + margin-bottom: 0; +} +.alert > p + p { + margin-top: 5px; +} +.alert-dismissable, +.alert-dismissible { + padding-right: 35px; +} +.alert-dismissable .close, +.alert-dismissible .close { + position: relative; + top: -2px; + right: -21px; + color: inherit; +} +.alert-success { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6; +} +.alert-success hr { + border-top-color: #c9e2b3; +} +.alert-success .alert-link { + color: #2b542c; +} +.alert-info { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} +.alert-info hr { + border-top-color: #a6e1ec; +} +.alert-info .alert-link { + color: #245269; +} +.alert-warning { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} +.alert-warning hr { + border-top-color: #f7e1b5; +} +.alert-warning .alert-link { + color: #66512c; +} +.alert-danger { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} +.alert-danger hr { + border-top-color: #e4b9c0; +} +.alert-danger .alert-link { + color: #843534; +} +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@-o-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +.progress { + height: 20px; + margin-bottom: 20px; + overflow: hidden; + background-color: #f5f5f5; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); +} +.progress-bar { + float: left; + width: 0; + height: 100%; + font-size: 12px; + line-height: 20px; + color: #fff; + text-align: center; + background-color: #337ab7; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + -webkit-transition: width .6s ease; + -o-transition: width .6s ease; + transition: width .6s ease; +} +.progress-striped .progress-bar, +.progress-bar-striped { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + -webkit-background-size: 40px 40px; + background-size: 40px 40px; +} +.progress.active .progress-bar, +.progress-bar.active { + -webkit-animation: progress-bar-stripes 2s linear infinite; + -o-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} +.progress-bar-success { + background-color: #5cb85c; +} +.progress-striped .progress-bar-success { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-info { + background-color: #5bc0de; +} +.progress-striped .progress-bar-info { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-warning { + background-color: #f0ad4e; +} +.progress-striped .progress-bar-warning { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-danger { + background-color: #d9534f; +} +.progress-striped .progress-bar-danger { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.media { + margin-top: 15px; +} +.media:first-child { + margin-top: 0; +} +.media, +.media-body { + overflow: hidden; + zoom: 1; +} +.media-body { + width: 10000px; +} +.media-object { + display: block; +} +.media-object.img-thumbnail { + max-width: none; +} +.media-right, +.media > .pull-right { + padding-left: 10px; +} +.media-left, +.media > .pull-left { + padding-right: 10px; +} +.media-left, +.media-right, +.media-body { + display: table-cell; + vertical-align: top; +} +.media-middle { + vertical-align: middle; +} +.media-bottom { + vertical-align: bottom; +} +.media-heading { + margin-top: 0; + margin-bottom: 5px; +} +.media-list { + padding-left: 0; + list-style: none; +} +.list-group { + padding-left: 0; + margin-bottom: 20px; +} +.list-group-item { + position: relative; + display: block; + padding: 10px 15px; + margin-bottom: -1px; + background-color: #fff; + border: 1px solid #ddd; +} +.list-group-item:first-child { + border-top-left-radius: 4px; + border-top-right-radius: 4px; +} +.list-group-item:last-child { + margin-bottom: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +a.list-group-item, +button.list-group-item { + color: #555; +} +a.list-group-item .list-group-item-heading, +button.list-group-item .list-group-item-heading { + color: #333; +} +a.list-group-item:hover, +button.list-group-item:hover, +a.list-group-item:focus, +button.list-group-item:focus { + color: #555; + text-decoration: none; + background-color: #f5f5f5; +} +button.list-group-item { + width: 100%; + text-align: left; +} +.list-group-item.disabled, +.list-group-item.disabled:hover, +.list-group-item.disabled:focus { + color: #777; + cursor: not-allowed; + background-color: #eee; +} +.list-group-item.disabled .list-group-item-heading, +.list-group-item.disabled:hover .list-group-item-heading, +.list-group-item.disabled:focus .list-group-item-heading { + color: inherit; +} +.list-group-item.disabled .list-group-item-text, +.list-group-item.disabled:hover .list-group-item-text, +.list-group-item.disabled:focus .list-group-item-text { + color: #777; +} +.list-group-item.active, +.list-group-item.active:hover, +.list-group-item.active:focus { + z-index: 2; + color: #fff; + background-color: #337ab7; + border-color: #337ab7; +} +.list-group-item.active .list-group-item-heading, +.list-group-item.active:hover .list-group-item-heading, +.list-group-item.active:focus .list-group-item-heading, +.list-group-item.active .list-group-item-heading > small, +.list-group-item.active:hover .list-group-item-heading > small, +.list-group-item.active:focus .list-group-item-heading > small, +.list-group-item.active .list-group-item-heading > .small, +.list-group-item.active:hover .list-group-item-heading > .small, +.list-group-item.active:focus .list-group-item-heading > .small { + color: inherit; +} +.list-group-item.active .list-group-item-text, +.list-group-item.active:hover .list-group-item-text, +.list-group-item.active:focus .list-group-item-text { + color: #c7ddef; +} +.list-group-item-success { + color: #3c763d; + background-color: #dff0d8; +} +a.list-group-item-success, +button.list-group-item-success { + color: #3c763d; +} +a.list-group-item-success .list-group-item-heading, +button.list-group-item-success .list-group-item-heading { + color: inherit; +} +a.list-group-item-success:hover, +button.list-group-item-success:hover, +a.list-group-item-success:focus, +button.list-group-item-success:focus { + color: #3c763d; + background-color: #d0e9c6; +} +a.list-group-item-success.active, +button.list-group-item-success.active, +a.list-group-item-success.active:hover, +button.list-group-item-success.active:hover, +a.list-group-item-success.active:focus, +button.list-group-item-success.active:focus { + color: #fff; + background-color: #3c763d; + border-color: #3c763d; +} +.list-group-item-info { + color: #31708f; + background-color: #d9edf7; +} +a.list-group-item-info, +button.list-group-item-info { + color: #31708f; +} +a.list-group-item-info .list-group-item-heading, +button.list-group-item-info .list-group-item-heading { + color: inherit; +} +a.list-group-item-info:hover, +button.list-group-item-info:hover, +a.list-group-item-info:focus, +button.list-group-item-info:focus { + color: #31708f; + background-color: #c4e3f3; +} +a.list-group-item-info.active, +button.list-group-item-info.active, +a.list-group-item-info.active:hover, +button.list-group-item-info.active:hover, +a.list-group-item-info.active:focus, +button.list-group-item-info.active:focus { + color: #fff; + background-color: #31708f; + border-color: #31708f; +} +.list-group-item-warning { + color: #8a6d3b; + background-color: #fcf8e3; +} +a.list-group-item-warning, +button.list-group-item-warning { + color: #8a6d3b; +} +a.list-group-item-warning .list-group-item-heading, +button.list-group-item-warning .list-group-item-heading { + color: inherit; +} +a.list-group-item-warning:hover, +button.list-group-item-warning:hover, +a.list-group-item-warning:focus, +button.list-group-item-warning:focus { + color: #8a6d3b; + background-color: #faf2cc; +} +a.list-group-item-warning.active, +button.list-group-item-warning.active, +a.list-group-item-warning.active:hover, +button.list-group-item-warning.active:hover, +a.list-group-item-warning.active:focus, +button.list-group-item-warning.active:focus { + color: #fff; + background-color: #8a6d3b; + border-color: #8a6d3b; +} +.list-group-item-danger { + color: #a94442; + background-color: #f2dede; +} +a.list-group-item-danger, +button.list-group-item-danger { + color: #a94442; +} +a.list-group-item-danger .list-group-item-heading, +button.list-group-item-danger .list-group-item-heading { + color: inherit; +} +a.list-group-item-danger:hover, +button.list-group-item-danger:hover, +a.list-group-item-danger:focus, +button.list-group-item-danger:focus { + color: #a94442; + background-color: #ebcccc; +} +a.list-group-item-danger.active, +button.list-group-item-danger.active, +a.list-group-item-danger.active:hover, +button.list-group-item-danger.active:hover, +a.list-group-item-danger.active:focus, +button.list-group-item-danger.active:focus { + color: #fff; + background-color: #a94442; + border-color: #a94442; +} +.list-group-item-heading { + margin-top: 0; + margin-bottom: 5px; +} +.list-group-item-text { + margin-bottom: 0; + line-height: 1.3; +} +.panel { + margin-bottom: 20px; + background-color: #fff; + border: 1px solid transparent; + border-radius: 4px; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05); + box-shadow: 0 1px 1px rgba(0, 0, 0, .05); +} +.panel-body { + padding: 15px; +} +.panel-heading { + padding: 10px 15px; + border-bottom: 1px solid transparent; + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel-heading > .dropdown .dropdown-toggle { + color: inherit; +} +.panel-title { + margin-top: 0; + margin-bottom: 0; + font-size: 16px; + color: inherit; +} +.panel-title > a, +.panel-title > small, +.panel-title > .small, +.panel-title > small > a, +.panel-title > .small > a { + color: inherit; +} +.panel-footer { + padding: 10px 15px; + background-color: #f5f5f5; + border-top: 1px solid #ddd; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .list-group, +.panel > .panel-collapse > .list-group { + margin-bottom: 0; +} +.panel > .list-group .list-group-item, +.panel > .panel-collapse > .list-group .list-group-item { + border-width: 1px 0; + border-radius: 0; +} +.panel > .list-group:first-child .list-group-item:first-child, +.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child { + border-top: 0; + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .list-group:last-child .list-group-item:last-child, +.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child { + border-bottom: 0; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child { + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.panel-heading + .list-group .list-group-item:first-child { + border-top-width: 0; +} +.list-group + .panel-footer { + border-top-width: 0; +} +.panel > .table, +.panel > .table-responsive > .table, +.panel > .panel-collapse > .table { + margin-bottom: 0; +} +.panel > .table caption, +.panel > .table-responsive > .table caption, +.panel > .panel-collapse > .table caption { + padding-right: 15px; + padding-left: 15px; +} +.panel > .table:first-child, +.panel > .table-responsive:first-child > .table:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { + border-top-left-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { + border-top-right-radius: 3px; +} +.panel > .table:last-child, +.panel > .table-responsive:last-child > .table:last-child { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { + border-bottom-right-radius: 3px; +} +.panel > .panel-body + .table, +.panel > .panel-body + .table-responsive, +.panel > .table + .panel-body, +.panel > .table-responsive + .panel-body { + border-top: 1px solid #ddd; +} +.panel > .table > tbody:first-child > tr:first-child th, +.panel > .table > tbody:first-child > tr:first-child td { + border-top: 0; +} +.panel > .table-bordered, +.panel > .table-responsive > .table-bordered { + border: 0; +} +.panel > .table-bordered > thead > tr > th:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:first-child, +.panel > .table-bordered > tbody > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, +.panel > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-bordered > thead > tr > td:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:first-child, +.panel > .table-bordered > tbody > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, +.panel > .table-bordered > tfoot > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; +} +.panel > .table-bordered > thead > tr > th:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:last-child, +.panel > .table-bordered > tbody > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, +.panel > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-bordered > thead > tr > td:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:last-child, +.panel > .table-bordered > tbody > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, +.panel > .table-bordered > tfoot > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; +} +.panel > .table-bordered > thead > tr:first-child > td, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > td, +.panel > .table-bordered > tbody > tr:first-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, +.panel > .table-bordered > thead > tr:first-child > th, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > th, +.panel > .table-bordered > tbody > tr:first-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { + border-bottom: 0; +} +.panel > .table-bordered > tbody > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, +.panel > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-bordered > tbody > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, +.panel > .table-bordered > tfoot > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { + border-bottom: 0; +} +.panel > .table-responsive { + margin-bottom: 0; + border: 0; +} +.panel-group { + margin-bottom: 20px; +} +.panel-group .panel { + margin-bottom: 0; + border-radius: 4px; +} +.panel-group .panel + .panel { + margin-top: 5px; +} +.panel-group .panel-heading { + border-bottom: 0; +} +.panel-group .panel-heading + .panel-collapse > .panel-body, +.panel-group .panel-heading + .panel-collapse > .list-group { + border-top: 1px solid #ddd; +} +.panel-group .panel-footer { + border-top: 0; +} +.panel-group .panel-footer + .panel-collapse .panel-body { + border-bottom: 1px solid #ddd; +} +.panel-default { + border-color: #ddd; +} +.panel-default > .panel-heading { + color: #333; + background-color: #f5f5f5; + border-color: #ddd; +} +.panel-default > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #ddd; +} +.panel-default > .panel-heading .badge { + color: #f5f5f5; + background-color: #333; +} +.panel-default > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #ddd; +} +.panel-primary { + border-color: #337ab7; +} +.panel-primary > .panel-heading { + color: #fff; + background-color: #337ab7; + border-color: #337ab7; +} +.panel-primary > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #337ab7; +} +.panel-primary > .panel-heading .badge { + color: #337ab7; + background-color: #fff; +} +.panel-primary > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #337ab7; +} +.panel-success { + border-color: #d6e9c6; +} +.panel-success > .panel-heading { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6; +} +.panel-success > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #d6e9c6; +} +.panel-success > .panel-heading .badge { + color: #dff0d8; + background-color: #3c763d; +} +.panel-success > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #d6e9c6; +} +.panel-info { + border-color: #bce8f1; +} +.panel-info > .panel-heading { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} +.panel-info > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #bce8f1; +} +.panel-info > .panel-heading .badge { + color: #d9edf7; + background-color: #31708f; +} +.panel-info > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #bce8f1; +} +.panel-warning { + border-color: #faebcc; +} +.panel-warning > .panel-heading { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} +.panel-warning > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #faebcc; +} +.panel-warning > .panel-heading .badge { + color: #fcf8e3; + background-color: #8a6d3b; +} +.panel-warning > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #faebcc; +} +.panel-danger { + border-color: #ebccd1; +} +.panel-danger > .panel-heading { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} +.panel-danger > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #ebccd1; +} +.panel-danger > .panel-heading .badge { + color: #f2dede; + background-color: #a94442; +} +.panel-danger > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #ebccd1; +} +.embed-responsive { + position: relative; + display: block; + height: 0; + padding: 0; + overflow: hidden; +} +.embed-responsive .embed-responsive-item, +.embed-responsive iframe, +.embed-responsive embed, +.embed-responsive object, +.embed-responsive video { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + border: 0; +} +.embed-responsive-16by9 { + padding-bottom: 56.25%; +} +.embed-responsive-4by3 { + padding-bottom: 75%; +} +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); +} +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, .15); +} +.well-lg { + padding: 24px; + border-radius: 6px; +} +.well-sm { + padding: 9px; + border-radius: 3px; +} +.close { + float: right; + font-size: 21px; + font-weight: bold; + line-height: 1; + color: #000; + text-shadow: 0 1px 0 #fff; + filter: alpha(opacity=20); + opacity: .2; +} +.close:hover, +.close:focus { + color: #000; + text-decoration: none; + cursor: pointer; + filter: alpha(opacity=50); + opacity: .5; +} +button.close { + -webkit-appearance: none; + padding: 0; + cursor: pointer; + background: transparent; + border: 0; +} +.modal-open { + overflow: hidden; +} +.modal { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1050; + display: none; + overflow: hidden; + -webkit-overflow-scrolling: touch; + outline: 0; +} +.modal.fade .modal-dialog { + -webkit-transition: -webkit-transform .3s ease-out; + -o-transition: -o-transform .3s ease-out; + transition: transform .3s ease-out; + -webkit-transform: translate(0, -25%); + -ms-transform: translate(0, -25%); + -o-transform: translate(0, -25%); + transform: translate(0, -25%); +} +.modal.in .modal-dialog { + -webkit-transform: translate(0, 0); + -ms-transform: translate(0, 0); + -o-transform: translate(0, 0); + transform: translate(0, 0); +} +.modal-open .modal { + overflow-x: hidden; + overflow-y: auto; +} +.modal-dialog { + position: relative; + width: auto; + margin: 10px; +} +.modal-content { + position: relative; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #999; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: 6px; + outline: 0; + -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5); + box-shadow: 0 3px 9px rgba(0, 0, 0, .5); +} +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + background-color: #000; +} +.modal-backdrop.fade { + filter: alpha(opacity=0); + opacity: 0; +} +.modal-backdrop.in { + filter: alpha(opacity=50); + opacity: .5; +} +.modal-header { + padding: 15px; + border-bottom: 1px solid #e5e5e5; +} +.modal-header .close { + margin-top: -2px; +} +.modal-title { + margin: 0; + line-height: 1.42857143; +} +.modal-body { + position: relative; + padding: 15px; +} +.modal-footer { + padding: 15px; + text-align: right; + border-top: 1px solid #e5e5e5; +} +.modal-footer .btn + .btn { + margin-bottom: 0; + margin-left: 5px; +} +.modal-footer .btn-group .btn + .btn { + margin-left: -1px; +} +.modal-footer .btn-block + .btn-block { + margin-left: 0; +} +.modal-scrollbar-measure { + position: absolute; + top: -9999px; + width: 50px; + height: 50px; + overflow: scroll; +} +@media (min-width: 768px) { + .modal-dialog { + width: 600px; + margin: 30px auto; + } + .modal-content { + -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5); + box-shadow: 0 5px 15px rgba(0, 0, 0, .5); + } + .modal-sm { + width: 300px; + } +} +@media (min-width: 992px) { + .modal-lg { + width: 900px; + } +} +.tooltip { + position: absolute; + z-index: 1070; + display: block; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 12px; + font-style: normal; + font-weight: normal; + line-height: 1.42857143; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + word-wrap: normal; + white-space: normal; + filter: alpha(opacity=0); + opacity: 0; + + line-break: auto; +} +.tooltip.in { + filter: alpha(opacity=90); + opacity: .9; +} +.tooltip.top { + padding: 5px 0; + margin-top: -3px; +} +.tooltip.right { + padding: 0 5px; + margin-left: 3px; +} +.tooltip.bottom { + padding: 5px 0; + margin-top: 3px; +} +.tooltip.left { + padding: 0 5px; + margin-left: -3px; +} +.tooltip-inner { + max-width: 200px; + padding: 3px 8px; + color: #fff; + text-align: center; + background-color: #000; + border-radius: 4px; +} +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.top-left .tooltip-arrow { + right: 5px; + bottom: 0; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.top-right .tooltip-arrow { + bottom: 0; + left: 5px; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-width: 5px 5px 5px 0; + border-right-color: #000; +} +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-width: 5px 0 5px 5px; + border-left-color: #000; +} +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.tooltip.bottom-left .tooltip-arrow { + top: 0; + right: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.tooltip.bottom-right .tooltip-arrow { + top: 0; + left: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1060; + display: none; + max-width: 276px; + padding: 1px; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + font-style: normal; + font-weight: normal; + line-height: 1.42857143; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + word-wrap: normal; + white-space: normal; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2); + box-shadow: 0 5px 10px rgba(0, 0, 0, .2); + + line-break: auto; +} +.popover.top { + margin-top: -10px; +} +.popover.right { + margin-left: 10px; +} +.popover.bottom { + margin-top: 10px; +} +.popover.left { + margin-left: -10px; +} +.popover-title { + padding: 8px 14px; + margin: 0; + font-size: 14px; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + border-radius: 5px 5px 0 0; +} +.popover-content { + padding: 9px 14px; +} +.popover > .arrow, +.popover > .arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.popover > .arrow { + border-width: 11px; +} +.popover > .arrow:after { + content: ""; + border-width: 10px; +} +.popover.top > .arrow { + bottom: -11px; + left: 50%; + margin-left: -11px; + border-top-color: #999; + border-top-color: rgba(0, 0, 0, .25); + border-bottom-width: 0; +} +.popover.top > .arrow:after { + bottom: 1px; + margin-left: -10px; + content: " "; + border-top-color: #fff; + border-bottom-width: 0; +} +.popover.right > .arrow { + top: 50%; + left: -11px; + margin-top: -11px; + border-right-color: #999; + border-right-color: rgba(0, 0, 0, .25); + border-left-width: 0; +} +.popover.right > .arrow:after { + bottom: -10px; + left: 1px; + content: " "; + border-right-color: #fff; + border-left-width: 0; +} +.popover.bottom > .arrow { + top: -11px; + left: 50%; + margin-left: -11px; + border-top-width: 0; + border-bottom-color: #999; + border-bottom-color: rgba(0, 0, 0, .25); +} +.popover.bottom > .arrow:after { + top: 1px; + margin-left: -10px; + content: " "; + border-top-width: 0; + border-bottom-color: #fff; +} +.popover.left > .arrow { + top: 50%; + right: -11px; + margin-top: -11px; + border-right-width: 0; + border-left-color: #999; + border-left-color: rgba(0, 0, 0, .25); +} +.popover.left > .arrow:after { + right: 1px; + bottom: -10px; + content: " "; + border-right-width: 0; + border-left-color: #fff; +} +.carousel { + position: relative; +} +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden; +} +.carousel-inner > .item { + position: relative; + display: none; + -webkit-transition: .6s ease-in-out left; + -o-transition: .6s ease-in-out left; + transition: .6s ease-in-out left; +} +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + line-height: 1; +} +@media all and (transform-3d), (-webkit-transform-3d) { + .carousel-inner > .item { + -webkit-transition: -webkit-transform .6s ease-in-out; + -o-transition: -o-transform .6s ease-in-out; + transition: transform .6s ease-in-out; + + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + -webkit-perspective: 1000px; + perspective: 1000px; + } + .carousel-inner > .item.next, + .carousel-inner > .item.active.right { + left: 0; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + } + .carousel-inner > .item.prev, + .carousel-inner > .item.active.left { + left: 0; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + } + .carousel-inner > .item.next.left, + .carousel-inner > .item.prev.right, + .carousel-inner > .item.active { + left: 0; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} +.carousel-inner > .active, +.carousel-inner > .next, +.carousel-inner > .prev { + display: block; +} +.carousel-inner > .active { + left: 0; +} +.carousel-inner > .next, +.carousel-inner > .prev { + position: absolute; + top: 0; + width: 100%; +} +.carousel-inner > .next { + left: 100%; +} +.carousel-inner > .prev { + left: -100%; +} +.carousel-inner > .next.left, +.carousel-inner > .prev.right { + left: 0; +} +.carousel-inner > .active.left { + left: -100%; +} +.carousel-inner > .active.right { + left: 100%; +} +.carousel-control { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 15%; + font-size: 20px; + color: #fff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, .6); + background-color: rgba(0, 0, 0, 0); + filter: alpha(opacity=50); + opacity: .5; +} +.carousel-control.left { + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + background-image: -o-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, .0001))); + background-image: linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); + background-repeat: repeat-x; +} +.carousel-control.right { + right: 0; + left: auto; + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + background-image: -o-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), to(rgba(0, 0, 0, .5))); + background-image: linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); + background-repeat: repeat-x; +} +.carousel-control:hover, +.carousel-control:focus { + color: #fff; + text-decoration: none; + filter: alpha(opacity=90); + outline: 0; + opacity: .9; +} +.carousel-control .icon-prev, +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-left, +.carousel-control .glyphicon-chevron-right { + position: absolute; + top: 50%; + z-index: 5; + display: inline-block; + margin-top: -10px; +} +.carousel-control .icon-prev, +.carousel-control .glyphicon-chevron-left { + left: 50%; + margin-left: -10px; +} +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-right { + right: 50%; + margin-right: -10px; +} +.carousel-control .icon-prev, +.carousel-control .icon-next { + width: 20px; + height: 20px; + font-family: serif; + line-height: 1; +} +.carousel-control .icon-prev:before { + content: '\2039'; +} +.carousel-control .icon-next:before { + content: '\203a'; +} +.carousel-indicators { + position: absolute; + bottom: 10px; + left: 50%; + z-index: 15; + width: 60%; + padding-left: 0; + margin-left: -30%; + text-align: center; + list-style: none; +} +.carousel-indicators li { + display: inline-block; + width: 10px; + height: 10px; + margin: 1px; + text-indent: -999px; + cursor: pointer; + background-color: #000 \9; + background-color: rgba(0, 0, 0, 0); + border: 1px solid #fff; + border-radius: 10px; +} +.carousel-indicators .active { + width: 12px; + height: 12px; + margin: 0; + background-color: #fff; +} +.carousel-caption { + position: absolute; + right: 15%; + bottom: 20px; + left: 15%; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: #fff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, .6); +} +.carousel-caption .btn { + text-shadow: none; +} +@media screen and (min-width: 768px) { + .carousel-control .glyphicon-chevron-left, + .carousel-control .glyphicon-chevron-right, + .carousel-control .icon-prev, + .carousel-control .icon-next { + width: 30px; + height: 30px; + margin-top: -10px; + font-size: 30px; + } + .carousel-control .glyphicon-chevron-left, + .carousel-control .icon-prev { + margin-left: -10px; + } + .carousel-control .glyphicon-chevron-right, + .carousel-control .icon-next { + margin-right: -10px; + } + .carousel-caption { + right: 20%; + left: 20%; + padding-bottom: 30px; + } + .carousel-indicators { + bottom: 20px; + } +} +.clearfix:before, +.clearfix:after, +.dl-horizontal dd:before, +.dl-horizontal dd:after, +.container:before, +.container:after, +.container-fluid:before, +.container-fluid:after, +.row:before, +.row:after, +.form-horizontal .form-group:before, +.form-horizontal .form-group:after, +.btn-toolbar:before, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:before, +.btn-group-vertical > .btn-group:after, +.nav:before, +.nav:after, +.navbar:before, +.navbar:after, +.navbar-header:before, +.navbar-header:after, +.navbar-collapse:before, +.navbar-collapse:after, +.pager:before, +.pager:after, +.panel-body:before, +.panel-body:after, +.modal-header:before, +.modal-header:after, +.modal-footer:before, +.modal-footer:after { + display: table; + content: " "; +} +.clearfix:after, +.dl-horizontal dd:after, +.container:after, +.container-fluid:after, +.row:after, +.form-horizontal .form-group:after, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:after, +.nav:after, +.navbar:after, +.navbar-header:after, +.navbar-collapse:after, +.pager:after, +.panel-body:after, +.modal-header:after, +.modal-footer:after { + clear: both; +} +.center-block { + display: block; + margin-right: auto; + margin-left: auto; +} +.pull-right { + float: right !important; +} +.pull-left { + float: left !important; +} +.hide { + display: none !important; +} +.show { + display: block !important; +} +.invisible { + visibility: hidden; +} +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.hidden { + display: none !important; +} +.affix { + position: fixed; +} +@-ms-viewport { + width: device-width; +} +.visible-xs, +.visible-sm, +.visible-md, +.visible-lg { + display: none !important; +} +.visible-xs-block, +.visible-xs-inline, +.visible-xs-inline-block, +.visible-sm-block, +.visible-sm-inline, +.visible-sm-inline-block, +.visible-md-block, +.visible-md-inline, +.visible-md-inline-block, +.visible-lg-block, +.visible-lg-inline, +.visible-lg-inline-block { + display: none !important; +} +@media (max-width: 767px) { + .visible-xs { + display: block !important; + } + table.visible-xs { + display: table !important; + } + tr.visible-xs { + display: table-row !important; + } + th.visible-xs, + td.visible-xs { + display: table-cell !important; + } +} +@media (max-width: 767px) { + .visible-xs-block { + display: block !important; + } +} +@media (max-width: 767px) { + .visible-xs-inline { + display: inline !important; + } +} +@media (max-width: 767px) { + .visible-xs-inline-block { + display: inline-block !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm { + display: block !important; + } + table.visible-sm { + display: table !important; + } + tr.visible-sm { + display: table-row !important; + } + th.visible-sm, + td.visible-sm { + display: table-cell !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-block { + display: block !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline { + display: inline !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline-block { + display: inline-block !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md { + display: block !important; + } + table.visible-md { + display: table !important; + } + tr.visible-md { + display: table-row !important; + } + th.visible-md, + td.visible-md { + display: table-cell !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-block { + display: block !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline { + display: inline !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline-block { + display: inline-block !important; + } +} +@media (min-width: 1200px) { + .visible-lg { + display: block !important; + } + table.visible-lg { + display: table !important; + } + tr.visible-lg { + display: table-row !important; + } + th.visible-lg, + td.visible-lg { + display: table-cell !important; + } +} +@media (min-width: 1200px) { + .visible-lg-block { + display: block !important; + } +} +@media (min-width: 1200px) { + .visible-lg-inline { + display: inline !important; + } +} +@media (min-width: 1200px) { + .visible-lg-inline-block { + display: inline-block !important; + } +} +@media (max-width: 767px) { + .hidden-xs { + display: none !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .hidden-sm { + display: none !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .hidden-md { + display: none !important; + } +} +@media (min-width: 1200px) { + .hidden-lg { + display: none !important; + } +} +.visible-print { + display: none !important; +} +@media print { + .visible-print { + display: block !important; + } + table.visible-print { + display: table !important; + } + tr.visible-print { + display: table-row !important; + } + th.visible-print, + td.visible-print { + display: table-cell !important; + } +} +.visible-print-block { + display: none !important; +} +@media print { + .visible-print-block { + display: block !important; + } +} +.visible-print-inline { + display: none !important; +} +@media print { + .visible-print-inline { + display: inline !important; + } +} +.visible-print-inline-block { + display: none !important; +} +@media print { + .visible-print-inline-block { + display: inline-block !important; + } +} +@media print { + .hidden-print { + display: none !important; + } +} +/*# sourceMappingURL=bootstrap.css.map */ diff --git a/debian/missing-sources/bootstrap.js b/debian/missing-sources/bootstrap.js new file mode 100644 index 0000000..01fbbcb --- /dev/null +++ b/debian/missing-sources/bootstrap.js @@ -0,0 +1,2363 @@ +/*! + * Bootstrap v3.3.6 (http://getbootstrap.com) + * Copyright 2011-2015 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] > 2)) { + throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher, but lower than version 3') + } +}(jQuery); + +/* ======================================================================== + * Bootstrap: transition.js v3.3.6 + * http://getbootstrap.com/javascript/#transitions + * ======================================================================== + * Copyright 2011-2015 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.6 + * http://getbootstrap.com/javascript/#alerts + * ======================================================================== + * Copyright 2011-2015 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.6' + + 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) + + 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.6 + * http://getbootstrap.com/javascript/#buttons + * ======================================================================== + * Copyright 2011-2015 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.6' + + 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) + } else if (this.isLoading) { + this.isLoading = false + $el.removeClass(d).removeAttr(d) + } + }, 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) + if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') + Plugin.call($btn, 'toggle') + if (!($(e.target).is('input[type="radio"]') || $(e.target).is('input[type="checkbox"]'))) e.preventDefault() + }) + .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.6 + * http://getbootstrap.com/javascript/#carousel + * ======================================================================== + * Copyright 2011-2015 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.6' + + 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.6 + * http://getbootstrap.com/javascript/#collapse + * ======================================================================== + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++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.6' + + 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.6 + * http://getbootstrap.com/javascript/#dropdowns + * ======================================================================== + * Copyright 2011-2015 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.6' + + 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.6 + * http://getbootstrap.com/javascript/#modals + * ======================================================================== + * Copyright 2011-2015 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.6' + + 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 (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.6 + * http://getbootstrap.com/javascript/#tooltip + * Inspired by the original jQuery.tipsy by Jason Frame + * ======================================================================== + * Copyright 2011-2015 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.6' + + 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() + 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 elOffset = isBody ? { top: 0, left: 0 } : $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 + }) + } + + + // 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.6 + * http://getbootstrap.com/javascript/#popovers + * ======================================================================== + * Copyright 2011-2015 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.6' + + 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.6 + * http://getbootstrap.com/javascript/#scrollspy + * ======================================================================== + * Copyright 2011-2015 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.6' + + 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.6 + * http://getbootstrap.com/javascript/#tabs + * ======================================================================== + * Copyright 2011-2015 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.6' + + 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.6 + * http://getbootstrap.com/javascript/#affix + * ======================================================================== + * Copyright 2011-2015 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.6' + + 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); diff --git a/debian/missing-sources/d3.js b/debian/missing-sources/d3.js new file mode 100644 index 0000000..8873e0a --- /dev/null +++ b/debian/missing-sources/d3.js @@ -0,0 +1,9550 @@ +!function() { + var d3 = { + version: "3.5.9" + }; + var d3_arraySlice = [].slice, d3_array = function(list) { + return d3_arraySlice.call(list); + }; + var d3_document = this.document; + function d3_documentElement(node) { + return node && (node.ownerDocument || node.document || node).documentElement; + } + function d3_window(node) { + return node && (node.ownerDocument && node.ownerDocument.defaultView || node.document && node || node.defaultView); + } + if (d3_document) { + try { + d3_array(d3_document.documentElement.childNodes)[0].nodeType; + } catch (e) { + d3_array = function(list) { + var i = list.length, array = new Array(i); + while (i--) array[i] = list[i]; + return array; + }; + } + } + if (!Date.now) Date.now = function() { + return +new Date(); + }; + if (d3_document) { + try { + d3_document.createElement("DIV").style.setProperty("opacity", 0, ""); + } catch (error) { + var d3_element_prototype = this.Element.prototype, d3_element_setAttribute = d3_element_prototype.setAttribute, d3_element_setAttributeNS = d3_element_prototype.setAttributeNS, d3_style_prototype = this.CSSStyleDeclaration.prototype, d3_style_setProperty = d3_style_prototype.setProperty; + d3_element_prototype.setAttribute = function(name, value) { + d3_element_setAttribute.call(this, name, value + ""); + }; + d3_element_prototype.setAttributeNS = function(space, local, value) { + d3_element_setAttributeNS.call(this, space, local, value + ""); + }; + d3_style_prototype.setProperty = function(name, value, priority) { + d3_style_setProperty.call(this, name, value + "", priority); + }; + } + } + d3.ascending = d3_ascending; + function d3_ascending(a, b) { + return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; + } + d3.descending = function(a, b) { + return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN; + }; + d3.min = function(array, f) { + var i = -1, n = array.length, a, b; + if (arguments.length === 1) { + while (++i < n) if ((b = array[i]) != null && b >= b) { + a = b; + break; + } + while (++i < n) if ((b = array[i]) != null && a > b) a = b; + } else { + while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) { + a = b; + break; + } + while (++i < n) if ((b = f.call(array, array[i], i)) != null && a > b) a = b; + } + return a; + }; + d3.max = function(array, f) { + var i = -1, n = array.length, a, b; + if (arguments.length === 1) { + while (++i < n) if ((b = array[i]) != null && b >= b) { + a = b; + break; + } + while (++i < n) if ((b = array[i]) != null && b > a) a = b; + } else { + while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) { + a = b; + break; + } + while (++i < n) if ((b = f.call(array, array[i], i)) != null && b > a) a = b; + } + return a; + }; + d3.extent = function(array, f) { + var i = -1, n = array.length, a, b, c; + if (arguments.length === 1) { + while (++i < n) if ((b = array[i]) != null && b >= b) { + a = c = b; + break; + } + while (++i < n) if ((b = array[i]) != null) { + if (a > b) a = b; + if (c < b) c = b; + } + } else { + while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) { + a = c = b; + break; + } + while (++i < n) if ((b = f.call(array, array[i], i)) != null) { + if (a > b) a = b; + if (c < b) c = b; + } + } + return [ a, c ]; + }; + function d3_number(x) { + return x === null ? NaN : +x; + } + function d3_numeric(x) { + return !isNaN(x); + } + d3.sum = function(array, f) { + var s = 0, n = array.length, a, i = -1; + if (arguments.length === 1) { + while (++i < n) if (d3_numeric(a = +array[i])) s += a; + } else { + while (++i < n) if (d3_numeric(a = +f.call(array, array[i], i))) s += a; + } + return s; + }; + d3.mean = function(array, f) { + var s = 0, n = array.length, a, i = -1, j = n; + if (arguments.length === 1) { + while (++i < n) if (d3_numeric(a = d3_number(array[i]))) s += a; else --j; + } else { + while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) s += a; else --j; + } + if (j) return s / j; + }; + d3.quantile = function(values, p) { + var H = (values.length - 1) * p + 1, h = Math.floor(H), v = +values[h - 1], e = H - h; + return e ? v + e * (values[h] - v) : v; + }; + d3.median = function(array, f) { + var numbers = [], n = array.length, a, i = -1; + if (arguments.length === 1) { + while (++i < n) if (d3_numeric(a = d3_number(array[i]))) numbers.push(a); + } else { + while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) numbers.push(a); + } + if (numbers.length) return d3.quantile(numbers.sort(d3_ascending), .5); + }; + d3.variance = function(array, f) { + var n = array.length, m = 0, a, d, s = 0, i = -1, j = 0; + if (arguments.length === 1) { + while (++i < n) { + if (d3_numeric(a = d3_number(array[i]))) { + d = a - m; + m += d / ++j; + s += d * (a - m); + } + } + } else { + while (++i < n) { + if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) { + d = a - m; + m += d / ++j; + s += d * (a - m); + } + } + } + if (j > 1) return s / (j - 1); + }; + d3.deviation = function() { + var v = d3.variance.apply(this, arguments); + return v ? Math.sqrt(v) : v; + }; + function d3_bisector(compare) { + return { + left: function(a, x, lo, hi) { + if (arguments.length < 3) lo = 0; + if (arguments.length < 4) 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 (arguments.length < 3) lo = 0; + if (arguments.length < 4) 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; + } + }; + } + var d3_bisect = d3_bisector(d3_ascending); + d3.bisectLeft = d3_bisect.left; + d3.bisect = d3.bisectRight = d3_bisect.right; + d3.bisector = function(f) { + return d3_bisector(f.length === 1 ? function(d, x) { + return d3_ascending(f(d), x); + } : f); + }; + d3.shuffle = function(array, i0, i1) { + if ((m = arguments.length) < 3) { + i1 = array.length; + if (m < 2) i0 = 0; + } + var m = i1 - 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; + }; + d3.permute = function(array, indexes) { + var i = indexes.length, permutes = new Array(i); + while (i--) permutes[i] = array[indexes[i]]; + return permutes; + }; + d3.pairs = function(array) { + var i = 0, n = array.length - 1, p0, p1 = array[0], pairs = new Array(n < 0 ? 0 : n); + while (i < n) pairs[i] = [ p0 = p1, p1 = array[++i] ]; + return pairs; + }; + d3.zip = function() { + if (!(n = arguments.length)) return []; + for (var i = -1, m = d3.min(arguments, d3_zipLength), zips = new Array(m); ++i < m; ) { + for (var j = -1, n, zip = zips[i] = new Array(n); ++j < n; ) { + zip[j] = arguments[j][i]; + } + } + return zips; + }; + function d3_zipLength(d) { + return d.length; + } + d3.transpose = function(matrix) { + return d3.zip.apply(d3, matrix); + }; + d3.keys = function(map) { + var keys = []; + for (var key in map) keys.push(key); + return keys; + }; + d3.values = function(map) { + var values = []; + for (var key in map) values.push(map[key]); + return values; + }; + d3.entries = function(map) { + var entries = []; + for (var key in map) entries.push({ + key: key, + value: map[key] + }); + return entries; + }; + d3.merge = function(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; + }; + var abs = Math.abs; + d3.range = function(start, stop, step) { + if (arguments.length < 3) { + step = 1; + if (arguments.length < 2) { + stop = start; + start = 0; + } + } + if ((stop - start) / step === Infinity) throw new Error("infinite range"); + var range = [], k = d3_range_integerScale(abs(step)), i = -1, j; + start *= k, stop *= k, step *= k; + if (step < 0) while ((j = start + step * ++i) > stop) range.push(j / k); else while ((j = start + step * ++i) < stop) range.push(j / k); + return range; + }; + function d3_range_integerScale(x) { + var k = 1; + while (x * k % 1) k *= 10; + return k; + } + function d3_class(ctor, properties) { + for (var key in properties) { + Object.defineProperty(ctor.prototype, key, { + value: properties[key], + enumerable: false + }); + } + } + d3.map = function(object, f) { + var map = new d3_Map(); + if (object instanceof d3_Map) { + object.forEach(function(key, value) { + map.set(key, value); + }); + } else if (Array.isArray(object)) { + var i = -1, n = object.length, o; + if (arguments.length === 1) while (++i < n) map.set(i, object[i]); else while (++i < n) map.set(f.call(object, o = object[i], i), o); + } else { + for (var key in object) map.set(key, object[key]); + } + return map; + }; + function d3_Map() { + this._ = Object.create(null); + } + var d3_map_proto = "__proto__", d3_map_zero = "\x00"; + d3_class(d3_Map, { + has: d3_map_has, + get: function(key) { + return this._[d3_map_escape(key)]; + }, + set: function(key, value) { + return this._[d3_map_escape(key)] = value; + }, + remove: d3_map_remove, + keys: d3_map_keys, + values: function() { + var values = []; + for (var key in this._) values.push(this._[key]); + return values; + }, + entries: function() { + var entries = []; + for (var key in this._) entries.push({ + key: d3_map_unescape(key), + value: this._[key] + }); + return entries; + }, + size: d3_map_size, + empty: d3_map_empty, + forEach: function(f) { + for (var key in this._) f.call(this, d3_map_unescape(key), this._[key]); + } + }); + function d3_map_escape(key) { + return (key += "") === d3_map_proto || key[0] === d3_map_zero ? d3_map_zero + key : key; + } + function d3_map_unescape(key) { + return (key += "")[0] === d3_map_zero ? key.slice(1) : key; + } + function d3_map_has(key) { + return d3_map_escape(key) in this._; + } + function d3_map_remove(key) { + return (key = d3_map_escape(key)) in this._ && delete this._[key]; + } + function d3_map_keys() { + var keys = []; + for (var key in this._) keys.push(d3_map_unescape(key)); + return keys; + } + function d3_map_size() { + var size = 0; + for (var key in this._) ++size; + return size; + } + function d3_map_empty() { + for (var key in this._) return false; + return true; + } + d3.nest = function() { + var nest = {}, keys = [], sortKeys = [], sortValues, rollup; + function map(mapType, array, depth) { + if (depth >= keys.length) return rollup ? rollup.call(nest, array) : sortValues ? array.sort(sortValues) : array; + var i = -1, n = array.length, key = keys[depth++], keyValue, object, setter, valuesByKey = new d3_Map(), values; + while (++i < n) { + if (values = valuesByKey.get(keyValue = key(object = array[i]))) { + values.push(object); + } else { + valuesByKey.set(keyValue, [ object ]); + } + } + if (mapType) { + object = mapType(); + setter = function(keyValue, values) { + object.set(keyValue, map(mapType, values, depth)); + }; + } else { + object = {}; + setter = function(keyValue, values) { + object[keyValue] = map(mapType, values, depth); + }; + } + valuesByKey.forEach(setter); + return object; + } + function entries(map, depth) { + if (depth >= keys.length) return map; + var array = [], sortKey = sortKeys[depth++]; + map.forEach(function(key, keyMap) { + array.push({ + key: key, + values: entries(keyMap, depth) + }); + }); + return sortKey ? array.sort(function(a, b) { + return sortKey(a.key, b.key); + }) : array; + } + nest.map = function(array, mapType) { + return map(mapType, array, 0); + }; + nest.entries = function(array) { + return entries(map(d3.map, array, 0), 0); + }; + nest.key = function(d) { + keys.push(d); + return nest; + }; + nest.sortKeys = function(order) { + sortKeys[keys.length - 1] = order; + return nest; + }; + nest.sortValues = function(order) { + sortValues = order; + return nest; + }; + nest.rollup = function(f) { + rollup = f; + return nest; + }; + return nest; + }; + d3.set = function(array) { + var set = new d3_Set(); + if (array) for (var i = 0, n = array.length; i < n; ++i) set.add(array[i]); + return set; + }; + function d3_Set() { + this._ = Object.create(null); + } + d3_class(d3_Set, { + has: d3_map_has, + add: function(key) { + this._[d3_map_escape(key += "")] = true; + return key; + }, + remove: d3_map_remove, + values: d3_map_keys, + size: d3_map_size, + empty: d3_map_empty, + forEach: function(f) { + for (var key in this._) f.call(this, d3_map_unescape(key)); + } + }); + d3.behavior = {}; + function d3_identity(d) { + return d; + } + d3.rebind = function(target, source) { + var i = 1, n = arguments.length, method; + while (++i < n) target[method = arguments[i]] = d3_rebind(target, source, source[method]); + return target; + }; + function d3_rebind(target, source, method) { + return function() { + var value = method.apply(source, arguments); + return value === source ? target : value; + }; + } + function d3_vendorSymbol(object, name) { + if (name in object) return name; + name = name.charAt(0).toUpperCase() + name.slice(1); + for (var i = 0, n = d3_vendorPrefixes.length; i < n; ++i) { + var prefixName = d3_vendorPrefixes[i] + name; + if (prefixName in object) return prefixName; + } + } + var d3_vendorPrefixes = [ "webkit", "ms", "moz", "Moz", "o", "O" ]; + function d3_noop() {} + d3.dispatch = function() { + var dispatch = new d3_dispatch(), i = -1, n = arguments.length; + while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch); + return dispatch; + }; + function d3_dispatch() {} + d3_dispatch.prototype.on = function(type, listener) { + var i = type.indexOf("."), name = ""; + if (i >= 0) { + name = type.slice(i + 1); + type = type.slice(0, i); + } + if (type) return arguments.length < 2 ? this[type].on(name) : this[type].on(name, listener); + if (arguments.length === 2) { + if (listener == null) for (type in this) { + if (this.hasOwnProperty(type)) this[type].on(name, null); + } + return this; + } + }; + function d3_dispatch_event(dispatch) { + var listeners = [], listenerByName = new d3_Map(); + function event() { + var z = listeners, i = -1, n = z.length, l; + while (++i < n) if (l = z[i].on) l.apply(this, arguments); + return dispatch; + } + event.on = function(name, listener) { + var l = listenerByName.get(name), i; + if (arguments.length < 2) return l && l.on; + if (l) { + l.on = null; + listeners = listeners.slice(0, i = listeners.indexOf(l)).concat(listeners.slice(i + 1)); + listenerByName.remove(name); + } + if (listener) listeners.push(listenerByName.set(name, { + on: listener + })); + return dispatch; + }; + return event; + } + d3.event = null; + function d3_eventPreventDefault() { + d3.event.preventDefault(); + } + function d3_eventSource() { + var e = d3.event, s; + while (s = e.sourceEvent) e = s; + return e; + } + function d3_eventDispatch(target) { + var dispatch = new d3_dispatch(), i = 0, n = arguments.length; + while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch); + dispatch.of = function(thiz, argumentz) { + return function(e1) { + try { + var e0 = e1.sourceEvent = d3.event; + e1.target = target; + d3.event = e1; + dispatch[e1.type].apply(thiz, argumentz); + } finally { + d3.event = e0; + } + }; + }; + return dispatch; + } + d3.requote = function(s) { + return s.replace(d3_requote_re, "\\$&"); + }; + var d3_requote_re = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g; + var d3_subclass = {}.__proto__ ? function(object, prototype) { + object.__proto__ = prototype; + } : function(object, prototype) { + for (var property in prototype) object[property] = prototype[property]; + }; + function d3_selection(groups) { + d3_subclass(groups, d3_selectionPrototype); + return groups; + } + var d3_select = function(s, n) { + return n.querySelector(s); + }, d3_selectAll = function(s, n) { + return n.querySelectorAll(s); + }, d3_selectMatches = function(n, s) { + var d3_selectMatcher = n.matches || n[d3_vendorSymbol(n, "matchesSelector")]; + d3_selectMatches = function(n, s) { + return d3_selectMatcher.call(n, s); + }; + return d3_selectMatches(n, s); + }; + if (typeof Sizzle === "function") { + d3_select = function(s, n) { + return Sizzle(s, n)[0] || null; + }; + d3_selectAll = Sizzle; + d3_selectMatches = Sizzle.matchesSelector; + } + d3.selection = function() { + return d3.select(d3_document.documentElement); + }; + var d3_selectionPrototype = d3.selection.prototype = []; + d3_selectionPrototype.select = function(selector) { + var subgroups = [], subgroup, subnode, group, node; + selector = d3_selection_selector(selector); + for (var j = -1, m = this.length; ++j < m; ) { + subgroups.push(subgroup = []); + subgroup.parentNode = (group = this[j]).parentNode; + for (var i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) { + subgroup.push(subnode = selector.call(node, node.__data__, i, j)); + if (subnode && "__data__" in node) subnode.__data__ = node.__data__; + } else { + subgroup.push(null); + } + } + } + return d3_selection(subgroups); + }; + function d3_selection_selector(selector) { + return typeof selector === "function" ? selector : function() { + return d3_select(selector, this); + }; + } + d3_selectionPrototype.selectAll = function(selector) { + var subgroups = [], subgroup, node; + selector = d3_selection_selectorAll(selector); + for (var j = -1, m = this.length; ++j < m; ) { + for (var group = this[j], i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) { + subgroups.push(subgroup = d3_array(selector.call(node, node.__data__, i, j))); + subgroup.parentNode = node; + } + } + } + return d3_selection(subgroups); + }; + function d3_selection_selectorAll(selector) { + return typeof selector === "function" ? selector : function() { + return d3_selectAll(selector, this); + }; + } + var d3_nsPrefix = { + svg: "http://www.w3.org/2000/svg", + xhtml: "http://www.w3.org/1999/xhtml", + xlink: "http://www.w3.org/1999/xlink", + xml: "http://www.w3.org/XML/1998/namespace", + xmlns: "http://www.w3.org/2000/xmlns/" + }; + d3.ns = { + prefix: d3_nsPrefix, + qualify: function(name) { + var i = name.indexOf(":"), prefix = name; + if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1); + return d3_nsPrefix.hasOwnProperty(prefix) ? { + space: d3_nsPrefix[prefix], + local: name + } : name; + } + }; + d3_selectionPrototype.attr = function(name, value) { + if (arguments.length < 2) { + if (typeof name === "string") { + var node = this.node(); + name = d3.ns.qualify(name); + return name.local ? node.getAttributeNS(name.space, name.local) : node.getAttribute(name); + } + for (value in name) this.each(d3_selection_attr(value, name[value])); + return this; + } + return this.each(d3_selection_attr(name, value)); + }; + function d3_selection_attr(name, value) { + name = d3.ns.qualify(name); + function attrNull() { + this.removeAttribute(name); + } + function attrNullNS() { + this.removeAttributeNS(name.space, name.local); + } + function attrConstant() { + this.setAttribute(name, value); + } + function attrConstantNS() { + this.setAttributeNS(name.space, name.local, value); + } + function attrFunction() { + var x = value.apply(this, arguments); + if (x == null) this.removeAttribute(name); else this.setAttribute(name, x); + } + function attrFunctionNS() { + var x = value.apply(this, arguments); + if (x == null) this.removeAttributeNS(name.space, name.local); else this.setAttributeNS(name.space, name.local, x); + } + return value == null ? name.local ? attrNullNS : attrNull : typeof value === "function" ? name.local ? attrFunctionNS : attrFunction : name.local ? attrConstantNS : attrConstant; + } + function d3_collapse(s) { + return s.trim().replace(/\s+/g, " "); + } + d3_selectionPrototype.classed = function(name, value) { + if (arguments.length < 2) { + if (typeof name === "string") { + var node = this.node(), n = (name = d3_selection_classes(name)).length, i = -1; + if (value = node.classList) { + while (++i < n) if (!value.contains(name[i])) return false; + } else { + value = node.getAttribute("class"); + while (++i < n) if (!d3_selection_classedRe(name[i]).test(value)) return false; + } + return true; + } + for (value in name) this.each(d3_selection_classed(value, name[value])); + return this; + } + return this.each(d3_selection_classed(name, value)); + }; + function d3_selection_classedRe(name) { + return new RegExp("(?:^|\\s+)" + d3.requote(name) + "(?:\\s+|$)", "g"); + } + function d3_selection_classes(name) { + return (name + "").trim().split(/^|\s+/); + } + function d3_selection_classed(name, value) { + name = d3_selection_classes(name).map(d3_selection_classedName); + var n = name.length; + function classedConstant() { + var i = -1; + while (++i < n) name[i](this, value); + } + function classedFunction() { + var i = -1, x = value.apply(this, arguments); + while (++i < n) name[i](this, x); + } + return typeof value === "function" ? classedFunction : classedConstant; + } + function d3_selection_classedName(name) { + var re = d3_selection_classedRe(name); + return function(node, value) { + if (c = node.classList) return value ? c.add(name) : c.remove(name); + var c = node.getAttribute("class") || ""; + if (value) { + re.lastIndex = 0; + if (!re.test(c)) node.setAttribute("class", d3_collapse(c + " " + name)); + } else { + node.setAttribute("class", d3_collapse(c.replace(re, " "))); + } + }; + } + d3_selectionPrototype.style = function(name, value, priority) { + var n = arguments.length; + if (n < 3) { + if (typeof name !== "string") { + if (n < 2) value = ""; + for (priority in name) this.each(d3_selection_style(priority, name[priority], value)); + return this; + } + if (n < 2) { + var node = this.node(); + return d3_window(node).getComputedStyle(node, null).getPropertyValue(name); + } + priority = ""; + } + return this.each(d3_selection_style(name, value, priority)); + }; + function d3_selection_style(name, value, priority) { + function styleNull() { + this.style.removeProperty(name); + } + function styleConstant() { + this.style.setProperty(name, value, priority); + } + function styleFunction() { + var x = value.apply(this, arguments); + if (x == null) this.style.removeProperty(name); else this.style.setProperty(name, x, priority); + } + return value == null ? styleNull : typeof value === "function" ? styleFunction : styleConstant; + } + d3_selectionPrototype.property = function(name, value) { + if (arguments.length < 2) { + if (typeof name === "string") return this.node()[name]; + for (value in name) this.each(d3_selection_property(value, name[value])); + return this; + } + return this.each(d3_selection_property(name, value)); + }; + function d3_selection_property(name, value) { + function propertyNull() { + delete this[name]; + } + function propertyConstant() { + this[name] = value; + } + function propertyFunction() { + var x = value.apply(this, arguments); + if (x == null) delete this[name]; else this[name] = x; + } + return value == null ? propertyNull : typeof value === "function" ? propertyFunction : propertyConstant; + } + d3_selectionPrototype.text = function(value) { + return arguments.length ? this.each(typeof value === "function" ? function() { + var v = value.apply(this, arguments); + this.textContent = v == null ? "" : v; + } : value == null ? function() { + this.textContent = ""; + } : function() { + this.textContent = value; + }) : this.node().textContent; + }; + d3_selectionPrototype.html = function(value) { + return arguments.length ? this.each(typeof value === "function" ? function() { + var v = value.apply(this, arguments); + this.innerHTML = v == null ? "" : v; + } : value == null ? function() { + this.innerHTML = ""; + } : function() { + this.innerHTML = value; + }) : this.node().innerHTML; + }; + d3_selectionPrototype.append = function(name) { + name = d3_selection_creator(name); + return this.select(function() { + return this.appendChild(name.apply(this, arguments)); + }); + }; + function d3_selection_creator(name) { + function create() { + var document = this.ownerDocument, namespace = this.namespaceURI; + return namespace ? document.createElementNS(namespace, name) : document.createElement(name); + } + function createNS() { + return this.ownerDocument.createElementNS(name.space, name.local); + } + return typeof name === "function" ? name : (name = d3.ns.qualify(name)).local ? createNS : create; + } + d3_selectionPrototype.insert = function(name, before) { + name = d3_selection_creator(name); + before = d3_selection_selector(before); + return this.select(function() { + return this.insertBefore(name.apply(this, arguments), before.apply(this, arguments) || null); + }); + }; + d3_selectionPrototype.remove = function() { + return this.each(d3_selectionRemove); + }; + function d3_selectionRemove() { + var parent = this.parentNode; + if (parent) parent.removeChild(this); + } + d3_selectionPrototype.data = function(value, key) { + var i = -1, n = this.length, group, node; + if (!arguments.length) { + value = new Array(n = (group = this[0]).length); + while (++i < n) { + if (node = group[i]) { + value[i] = node.__data__; + } + } + return value; + } + function bind(group, groupData) { + var i, n = group.length, m = groupData.length, n0 = Math.min(n, m), updateNodes = new Array(m), enterNodes = new Array(m), exitNodes = new Array(n), node, nodeData; + if (key) { + var nodeByKeyValue = new d3_Map(), keyValues = new Array(n), keyValue; + for (i = -1; ++i < n; ) { + if (node = group[i]) { + if (nodeByKeyValue.has(keyValue = key.call(node, node.__data__, i))) { + exitNodes[i] = node; + } else { + nodeByKeyValue.set(keyValue, node); + } + keyValues[i] = keyValue; + } + } + for (i = -1; ++i < m; ) { + if (!(node = nodeByKeyValue.get(keyValue = key.call(groupData, nodeData = groupData[i], i)))) { + enterNodes[i] = d3_selection_dataNode(nodeData); + } else if (node !== true) { + updateNodes[i] = node; + node.__data__ = nodeData; + } + nodeByKeyValue.set(keyValue, true); + } + for (i = -1; ++i < n; ) { + if (i in keyValues && nodeByKeyValue.get(keyValues[i]) !== true) { + exitNodes[i] = group[i]; + } + } + } else { + for (i = -1; ++i < n0; ) { + node = group[i]; + nodeData = groupData[i]; + if (node) { + node.__data__ = nodeData; + updateNodes[i] = node; + } else { + enterNodes[i] = d3_selection_dataNode(nodeData); + } + } + for (;i < m; ++i) { + enterNodes[i] = d3_selection_dataNode(groupData[i]); + } + for (;i < n; ++i) { + exitNodes[i] = group[i]; + } + } + enterNodes.update = updateNodes; + enterNodes.parentNode = updateNodes.parentNode = exitNodes.parentNode = group.parentNode; + enter.push(enterNodes); + update.push(updateNodes); + exit.push(exitNodes); + } + var enter = d3_selection_enter([]), update = d3_selection([]), exit = d3_selection([]); + if (typeof value === "function") { + while (++i < n) { + bind(group = this[i], value.call(group, group.parentNode.__data__, i)); + } + } else { + while (++i < n) { + bind(group = this[i], value); + } + } + update.enter = function() { + return enter; + }; + update.exit = function() { + return exit; + }; + return update; + }; + function d3_selection_dataNode(data) { + return { + __data__: data + }; + } + d3_selectionPrototype.datum = function(value) { + return arguments.length ? this.property("__data__", value) : this.property("__data__"); + }; + d3_selectionPrototype.filter = function(filter) { + var subgroups = [], subgroup, group, node; + if (typeof filter !== "function") filter = d3_selection_filter(filter); + for (var j = 0, m = this.length; j < m; j++) { + subgroups.push(subgroup = []); + subgroup.parentNode = (group = this[j]).parentNode; + for (var i = 0, n = group.length; i < n; i++) { + if ((node = group[i]) && filter.call(node, node.__data__, i, j)) { + subgroup.push(node); + } + } + } + return d3_selection(subgroups); + }; + function d3_selection_filter(selector) { + return function() { + return d3_selectMatches(this, selector); + }; + } + d3_selectionPrototype.order = function() { + for (var j = -1, m = this.length; ++j < m; ) { + for (var group = this[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; + }; + d3_selectionPrototype.sort = function(comparator) { + comparator = d3_selection_sortComparator.apply(this, arguments); + for (var j = -1, m = this.length; ++j < m; ) this[j].sort(comparator); + return this.order(); + }; + function d3_selection_sortComparator(comparator) { + if (!arguments.length) comparator = d3_ascending; + return function(a, b) { + return a && b ? comparator(a.__data__, b.__data__) : !a - !b; + }; + } + d3_selectionPrototype.each = function(callback) { + return d3_selection_each(this, function(node, i, j) { + callback.call(node, node.__data__, i, j); + }); + }; + function d3_selection_each(groups, callback) { + for (var 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(node, i, j); + } + } + return groups; + } + d3_selectionPrototype.call = function(callback) { + var args = d3_array(arguments); + callback.apply(args[0] = this, args); + return this; + }; + d3_selectionPrototype.empty = function() { + return !this.node(); + }; + d3_selectionPrototype.node = function() { + for (var j = 0, m = this.length; j < m; j++) { + for (var group = this[j], i = 0, n = group.length; i < n; i++) { + var node = group[i]; + if (node) return node; + } + } + return null; + }; + d3_selectionPrototype.size = function() { + var n = 0; + d3_selection_each(this, function() { + ++n; + }); + return n; + }; + function d3_selection_enter(selection) { + d3_subclass(selection, d3_selection_enterPrototype); + return selection; + } + var d3_selection_enterPrototype = []; + d3.selection.enter = d3_selection_enter; + d3.selection.enter.prototype = d3_selection_enterPrototype; + d3_selection_enterPrototype.append = d3_selectionPrototype.append; + d3_selection_enterPrototype.empty = d3_selectionPrototype.empty; + d3_selection_enterPrototype.node = d3_selectionPrototype.node; + d3_selection_enterPrototype.call = d3_selectionPrototype.call; + d3_selection_enterPrototype.size = d3_selectionPrototype.size; + d3_selection_enterPrototype.select = function(selector) { + var subgroups = [], subgroup, subnode, upgroup, group, node; + for (var j = -1, m = this.length; ++j < m; ) { + upgroup = (group = this[j]).update; + subgroups.push(subgroup = []); + subgroup.parentNode = group.parentNode; + for (var i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) { + subgroup.push(upgroup[i] = subnode = selector.call(group.parentNode, node.__data__, i, j)); + subnode.__data__ = node.__data__; + } else { + subgroup.push(null); + } + } + } + return d3_selection(subgroups); + }; + d3_selection_enterPrototype.insert = function(name, before) { + if (arguments.length < 2) before = d3_selection_enterInsertBefore(this); + return d3_selectionPrototype.insert.call(this, name, before); + }; + function d3_selection_enterInsertBefore(enter) { + var i0, j0; + return function(d, i, j) { + var group = enter[j].update, n = group.length, node; + if (j != j0) j0 = j, i0 = 0; + if (i >= i0) i0 = i + 1; + while (!(node = group[i0]) && ++i0 < n) ; + return node; + }; + } + d3.select = function(node) { + var group; + if (typeof node === "string") { + group = [ d3_select(node, d3_document) ]; + group.parentNode = d3_document.documentElement; + } else { + group = [ node ]; + group.parentNode = d3_documentElement(node); + } + return d3_selection([ group ]); + }; + d3.selectAll = function(nodes) { + var group; + if (typeof nodes === "string") { + group = d3_array(d3_selectAll(nodes, d3_document)); + group.parentNode = d3_document.documentElement; + } else { + group = d3_array(nodes); + group.parentNode = null; + } + return d3_selection([ group ]); + }; + d3_selectionPrototype.on = function(type, listener, capture) { + var n = arguments.length; + if (n < 3) { + if (typeof type !== "string") { + if (n < 2) listener = false; + for (capture in type) this.each(d3_selection_on(capture, type[capture], listener)); + return this; + } + if (n < 2) return (n = this.node()["__on" + type]) && n._; + capture = false; + } + return this.each(d3_selection_on(type, listener, capture)); + }; + function d3_selection_on(type, listener, capture) { + var name = "__on" + type, i = type.indexOf("."), wrap = d3_selection_onListener; + if (i > 0) type = type.slice(0, i); + var filter = d3_selection_onFilters.get(type); + if (filter) type = filter, wrap = d3_selection_onFilter; + function onRemove() { + var l = this[name]; + if (l) { + this.removeEventListener(type, l, l.$); + delete this[name]; + } + } + function onAdd() { + var l = wrap(listener, d3_array(arguments)); + onRemove.call(this); + this.addEventListener(type, this[name] = l, l.$ = capture); + l._ = listener; + } + function removeAll() { + var re = new RegExp("^__on([^.]+)" + d3.requote(type) + "$"), match; + for (var name in this) { + if (match = name.match(re)) { + var l = this[name]; + this.removeEventListener(match[1], l, l.$); + delete this[name]; + } + } + } + return i ? listener ? onAdd : onRemove : listener ? d3_noop : removeAll; + } + var d3_selection_onFilters = d3.map({ + mouseenter: "mouseover", + mouseleave: "mouseout" + }); + if (d3_document) { + d3_selection_onFilters.forEach(function(k) { + if ("on" + k in d3_document) d3_selection_onFilters.remove(k); + }); + } + function d3_selection_onListener(listener, argumentz) { + return function(e) { + var o = d3.event; + d3.event = e; + argumentz[0] = this.__data__; + try { + listener.apply(this, argumentz); + } finally { + d3.event = o; + } + }; + } + function d3_selection_onFilter(listener, argumentz) { + var l = d3_selection_onListener(listener, argumentz); + return function(e) { + var target = this, related = e.relatedTarget; + if (!related || related !== target && !(related.compareDocumentPosition(target) & 8)) { + l.call(target, e); + } + }; + } + var d3_event_dragSelect, d3_event_dragId = 0; + function d3_event_dragSuppress(node) { + var name = ".dragsuppress-" + ++d3_event_dragId, click = "click" + name, w = d3.select(d3_window(node)).on("touchmove" + name, d3_eventPreventDefault).on("dragstart" + name, d3_eventPreventDefault).on("selectstart" + name, d3_eventPreventDefault); + if (d3_event_dragSelect == null) { + d3_event_dragSelect = "onselectstart" in node ? false : d3_vendorSymbol(node.style, "userSelect"); + } + if (d3_event_dragSelect) { + var style = d3_documentElement(node).style, select = style[d3_event_dragSelect]; + style[d3_event_dragSelect] = "none"; + } + return function(suppressClick) { + w.on(name, null); + if (d3_event_dragSelect) style[d3_event_dragSelect] = select; + if (suppressClick) { + var off = function() { + w.on(click, null); + }; + w.on(click, function() { + d3_eventPreventDefault(); + off(); + }, true); + setTimeout(off, 0); + } + }; + } + d3.mouse = function(container) { + return d3_mousePoint(container, d3_eventSource()); + }; + var d3_mouse_bug44083 = this.navigator && /WebKit/.test(this.navigator.userAgent) ? -1 : 0; + function d3_mousePoint(container, e) { + if (e.changedTouches) e = e.changedTouches[0]; + var svg = container.ownerSVGElement || container; + if (svg.createSVGPoint) { + var point = svg.createSVGPoint(); + if (d3_mouse_bug44083 < 0) { + var window = d3_window(container); + if (window.scrollX || window.scrollY) { + svg = d3.select("body").append("svg").style({ + position: "absolute", + top: 0, + left: 0, + margin: 0, + padding: 0, + border: "none" + }, "important"); + var ctm = svg[0][0].getScreenCTM(); + d3_mouse_bug44083 = !(ctm.f || ctm.e); + svg.remove(); + } + } + if (d3_mouse_bug44083) point.x = e.pageX, point.y = e.pageY; else point.x = e.clientX, + point.y = e.clientY; + point = point.matrixTransform(container.getScreenCTM().inverse()); + return [ point.x, point.y ]; + } + var rect = container.getBoundingClientRect(); + return [ e.clientX - rect.left - container.clientLeft, e.clientY - rect.top - container.clientTop ]; + } + d3.touch = function(container, touches, identifier) { + if (arguments.length < 3) identifier = touches, touches = d3_eventSource().changedTouches; + if (touches) for (var i = 0, n = touches.length, touch; i < n; ++i) { + if ((touch = touches[i]).identifier === identifier) { + return d3_mousePoint(container, touch); + } + } + }; + d3.behavior.drag = function() { + var event = d3_eventDispatch(drag, "drag", "dragstart", "dragend"), origin = null, mousedown = dragstart(d3_noop, d3.mouse, d3_window, "mousemove", "mouseup"), touchstart = dragstart(d3_behavior_dragTouchId, d3.touch, d3_identity, "touchmove", "touchend"); + function drag() { + this.on("mousedown.drag", mousedown).on("touchstart.drag", touchstart); + } + function dragstart(id, position, subject, move, end) { + return function() { + var that = this, target = d3.event.target, parent = that.parentNode, dispatch = event.of(that, arguments), dragged = 0, dragId = id(), dragName = ".drag" + (dragId == null ? "" : "-" + dragId), dragOffset, dragSubject = d3.select(subject(target)).on(move + dragName, moved).on(end + dragName, ended), dragRestore = d3_event_dragSuppress(target), position0 = position(parent, dragId); + if (origin) { + dragOffset = origin.apply(that, arguments); + dragOffset = [ dragOffset.x - position0[0], dragOffset.y - position0[1] ]; + } else { + dragOffset = [ 0, 0 ]; + } + dispatch({ + type: "dragstart" + }); + function moved() { + var position1 = position(parent, dragId), dx, dy; + if (!position1) return; + dx = position1[0] - position0[0]; + dy = position1[1] - position0[1]; + dragged |= dx | dy; + position0 = position1; + dispatch({ + type: "drag", + x: position1[0] + dragOffset[0], + y: position1[1] + dragOffset[1], + dx: dx, + dy: dy + }); + } + function ended() { + if (!position(parent, dragId)) return; + dragSubject.on(move + dragName, null).on(end + dragName, null); + dragRestore(dragged); + dispatch({ + type: "dragend" + }); + } + }; + } + drag.origin = function(x) { + if (!arguments.length) return origin; + origin = x; + return drag; + }; + return d3.rebind(drag, event, "on"); + }; + function d3_behavior_dragTouchId() { + return d3.event.changedTouches[0].identifier; + } + d3.touches = function(container, touches) { + if (arguments.length < 2) touches = d3_eventSource().touches; + return touches ? d3_array(touches).map(function(touch) { + var point = d3_mousePoint(container, touch); + point.identifier = touch.identifier; + return point; + }) : []; + }; + var ε = 1e-6, ε2 = ε * ε, π = Math.PI, τ = 2 * π, τε = τ - ε, halfπ = π / 2, d3_radians = π / 180, d3_degrees = 180 / π; + function d3_sgn(x) { + return x > 0 ? 1 : x < 0 ? -1 : 0; + } + function d3_cross2d(a, b, c) { + return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]); + } + function d3_acos(x) { + return x > 1 ? 0 : x < -1 ? π : Math.acos(x); + } + function d3_asin(x) { + return x > 1 ? halfπ : x < -1 ? -halfπ : Math.asin(x); + } + function d3_sinh(x) { + return ((x = Math.exp(x)) - 1 / x) / 2; + } + function d3_cosh(x) { + return ((x = Math.exp(x)) + 1 / x) / 2; + } + function d3_tanh(x) { + return ((x = Math.exp(2 * x)) - 1) / (x + 1); + } + function d3_haversin(x) { + return (x = Math.sin(x / 2)) * x; + } + var ρ = Math.SQRT2, ρ2 = 2, ρ4 = 4; + d3.interpolateZoom = function(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; + if (d2 < ε2) { + S = Math.log(w1 / w0) / ρ; + i = function(t) { + return [ ux0 + t * dx, uy0 + t * dy, w0 * Math.exp(ρ * t * S) ]; + }; + } else { + var d1 = Math.sqrt(d2), b0 = (w1 * w1 - w0 * w0 + ρ4 * d2) / (2 * w0 * ρ2 * d1), b1 = (w1 * w1 - w0 * w0 - ρ4 * d2) / (2 * w1 * ρ2 * d1), r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0), r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1); + S = (r1 - r0) / ρ; + i = function(t) { + var s = t * S, coshr0 = d3_cosh(r0), u = w0 / (ρ2 * d1) * (coshr0 * d3_tanh(ρ * s + r0) - d3_sinh(r0)); + return [ ux0 + u * dx, uy0 + u * dy, w0 * coshr0 / d3_cosh(ρ * s + r0) ]; + }; + } + i.duration = S * 1e3; + return i; + }; + d3.behavior.zoom = function() { + var view = { + x: 0, + y: 0, + k: 1 + }, translate0, center0, center, size = [ 960, 500 ], scaleExtent = d3_behavior_zoomInfinity, duration = 250, zooming = 0, mousedown = "mousedown.zoom", mousemove = "mousemove.zoom", mouseup = "mouseup.zoom", mousewheelTimer, touchstart = "touchstart.zoom", touchtime, event = d3_eventDispatch(zoom, "zoomstart", "zoom", "zoomend"), x0, x1, y0, y1; + if (!d3_behavior_zoomWheel) { + d3_behavior_zoomWheel = "onwheel" in d3_document ? (d3_behavior_zoomDelta = function() { + return -d3.event.deltaY * (d3.event.deltaMode ? 120 : 1); + }, "wheel") : "onmousewheel" in d3_document ? (d3_behavior_zoomDelta = function() { + return d3.event.wheelDelta; + }, "mousewheel") : (d3_behavior_zoomDelta = function() { + return -d3.event.detail; + }, "MozMousePixelScroll"); + } + function zoom(g) { + g.on(mousedown, mousedowned).on(d3_behavior_zoomWheel + ".zoom", mousewheeled).on("dblclick.zoom", dblclicked).on(touchstart, touchstarted); + } + zoom.event = function(g) { + g.each(function() { + var dispatch = event.of(this, arguments), view1 = view; + if (d3_transitionInheritId) { + d3.select(this).transition().each("start.zoom", function() { + view = this.__chart__ || { + x: 0, + y: 0, + k: 1 + }; + zoomstarted(dispatch); + }).tween("zoom:zoom", function() { + var dx = size[0], dy = size[1], cx = center0 ? center0[0] : dx / 2, cy = center0 ? center0[1] : dy / 2, i = d3.interpolateZoom([ (cx - view.x) / view.k, (cy - view.y) / view.k, dx / view.k ], [ (cx - view1.x) / view1.k, (cy - view1.y) / view1.k, dx / view1.k ]); + return function(t) { + var l = i(t), k = dx / l[2]; + this.__chart__ = view = { + x: cx - l[0] * k, + y: cy - l[1] * k, + k: k + }; + zoomed(dispatch); + }; + }).each("interrupt.zoom", function() { + zoomended(dispatch); + }).each("end.zoom", function() { + zoomended(dispatch); + }); + } else { + this.__chart__ = view; + zoomstarted(dispatch); + zoomed(dispatch); + zoomended(dispatch); + } + }); + }; + zoom.translate = function(_) { + if (!arguments.length) return [ view.x, view.y ]; + view = { + x: +_[0], + y: +_[1], + k: view.k + }; + rescale(); + return zoom; + }; + zoom.scale = function(_) { + if (!arguments.length) return view.k; + view = { + x: view.x, + y: view.y, + k: null + }; + scaleTo(+_); + rescale(); + return zoom; + }; + zoom.scaleExtent = function(_) { + if (!arguments.length) return scaleExtent; + scaleExtent = _ == null ? d3_behavior_zoomInfinity : [ +_[0], +_[1] ]; + return zoom; + }; + zoom.center = function(_) { + if (!arguments.length) return center; + center = _ && [ +_[0], +_[1] ]; + return zoom; + }; + zoom.size = function(_) { + if (!arguments.length) return size; + size = _ && [ +_[0], +_[1] ]; + return zoom; + }; + zoom.duration = function(_) { + if (!arguments.length) return duration; + duration = +_; + return zoom; + }; + zoom.x = function(z) { + if (!arguments.length) return x1; + x1 = z; + x0 = z.copy(); + view = { + x: 0, + y: 0, + k: 1 + }; + return zoom; + }; + zoom.y = function(z) { + if (!arguments.length) return y1; + y1 = z; + y0 = z.copy(); + view = { + x: 0, + y: 0, + k: 1 + }; + return zoom; + }; + function location(p) { + return [ (p[0] - view.x) / view.k, (p[1] - view.y) / view.k ]; + } + function point(l) { + return [ l[0] * view.k + view.x, l[1] * view.k + view.y ]; + } + function scaleTo(s) { + view.k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], s)); + } + function translateTo(p, l) { + l = point(l); + view.x += p[0] - l[0]; + view.y += p[1] - l[1]; + } + function zoomTo(that, p, l, k) { + that.__chart__ = { + x: view.x, + y: view.y, + k: view.k + }; + scaleTo(Math.pow(2, k)); + translateTo(center0 = p, l); + that = d3.select(that); + if (duration > 0) that = that.transition().duration(duration); + that.call(zoom.event); + } + function rescale() { + if (x1) x1.domain(x0.range().map(function(x) { + return (x - view.x) / view.k; + }).map(x0.invert)); + if (y1) y1.domain(y0.range().map(function(y) { + return (y - view.y) / view.k; + }).map(y0.invert)); + } + function zoomstarted(dispatch) { + if (!zooming++) dispatch({ + type: "zoomstart" + }); + } + function zoomed(dispatch) { + rescale(); + dispatch({ + type: "zoom", + scale: view.k, + translate: [ view.x, view.y ] + }); + } + function zoomended(dispatch) { + if (!--zooming) dispatch({ + type: "zoomend" + }), center0 = null; + } + function mousedowned() { + var that = this, dispatch = event.of(that, arguments), dragged = 0, subject = d3.select(d3_window(that)).on(mousemove, moved).on(mouseup, ended), location0 = location(d3.mouse(that)), dragRestore = d3_event_dragSuppress(that); + d3_selection_interrupt.call(that); + zoomstarted(dispatch); + function moved() { + dragged = 1; + translateTo(d3.mouse(that), location0); + zoomed(dispatch); + } + function ended() { + subject.on(mousemove, null).on(mouseup, null); + dragRestore(dragged); + zoomended(dispatch); + } + } + function touchstarted() { + var that = this, dispatch = event.of(that, arguments), locations0 = {}, distance0 = 0, scale0, zoomName = ".zoom-" + d3.event.changedTouches[0].identifier, touchmove = "touchmove" + zoomName, touchend = "touchend" + zoomName, targets = [], subject = d3.select(that), dragRestore = d3_event_dragSuppress(that); + started(); + zoomstarted(dispatch); + subject.on(mousedown, null).on(touchstart, started); + function relocate() { + var touches = d3.touches(that); + scale0 = view.k; + touches.forEach(function(t) { + if (t.identifier in locations0) locations0[t.identifier] = location(t); + }); + return touches; + } + function started() { + var target = d3.event.target; + d3.select(target).on(touchmove, moved).on(touchend, ended); + targets.push(target); + var changed = d3.event.changedTouches; + for (var i = 0, n = changed.length; i < n; ++i) { + locations0[changed[i].identifier] = null; + } + var touches = relocate(), now = Date.now(); + if (touches.length === 1) { + if (now - touchtime < 500) { + var p = touches[0]; + zoomTo(that, p, locations0[p.identifier], Math.floor(Math.log(view.k) / Math.LN2) + 1); + d3_eventPreventDefault(); + } + touchtime = now; + } else if (touches.length > 1) { + var p = touches[0], q = touches[1], dx = p[0] - q[0], dy = p[1] - q[1]; + distance0 = dx * dx + dy * dy; + } + } + function moved() { + var touches = d3.touches(that), p0, l0, p1, l1; + d3_selection_interrupt.call(that); + for (var i = 0, n = touches.length; i < n; ++i, l1 = null) { + p1 = touches[i]; + if (l1 = locations0[p1.identifier]) { + if (l0) break; + p0 = p1, l0 = l1; + } + } + if (l1) { + var distance1 = (distance1 = p1[0] - p0[0]) * distance1 + (distance1 = p1[1] - p0[1]) * distance1, scale1 = distance0 && Math.sqrt(distance1 / distance0); + p0 = [ (p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2 ]; + l0 = [ (l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2 ]; + scaleTo(scale1 * scale0); + } + touchtime = null; + translateTo(p0, l0); + zoomed(dispatch); + } + function ended() { + if (d3.event.touches.length) { + var changed = d3.event.changedTouches; + for (var i = 0, n = changed.length; i < n; ++i) { + delete locations0[changed[i].identifier]; + } + for (var identifier in locations0) { + return void relocate(); + } + } + d3.selectAll(targets).on(zoomName, null); + subject.on(mousedown, mousedowned).on(touchstart, touchstarted); + dragRestore(); + zoomended(dispatch); + } + } + function mousewheeled() { + var dispatch = event.of(this, arguments); + if (mousewheelTimer) clearTimeout(mousewheelTimer); else d3_selection_interrupt.call(this), + translate0 = location(center0 = center || d3.mouse(this)), zoomstarted(dispatch); + mousewheelTimer = setTimeout(function() { + mousewheelTimer = null; + zoomended(dispatch); + }, 50); + d3_eventPreventDefault(); + scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * view.k); + translateTo(center0, translate0); + zoomed(dispatch); + } + function dblclicked() { + var p = d3.mouse(this), k = Math.log(view.k) / Math.LN2; + zoomTo(this, p, location(p), d3.event.shiftKey ? Math.ceil(k) - 1 : Math.floor(k) + 1); + } + return d3.rebind(zoom, event, "on"); + }; + var d3_behavior_zoomInfinity = [ 0, Infinity ], d3_behavior_zoomDelta, d3_behavior_zoomWheel; + d3.color = d3_color; + function d3_color() {} + d3_color.prototype.toString = function() { + return this.rgb() + ""; + }; + d3.hsl = d3_hsl; + function d3_hsl(h, s, l) { + return this instanceof d3_hsl ? void (this.h = +h, this.s = +s, this.l = +l) : arguments.length < 2 ? h instanceof d3_hsl ? new d3_hsl(h.h, h.s, h.l) : d3_rgb_parse("" + h, d3_rgb_hsl, d3_hsl) : new d3_hsl(h, s, l); + } + var d3_hslPrototype = d3_hsl.prototype = new d3_color(); + d3_hslPrototype.brighter = function(k) { + k = Math.pow(.7, arguments.length ? k : 1); + return new d3_hsl(this.h, this.s, this.l / k); + }; + d3_hslPrototype.darker = function(k) { + k = Math.pow(.7, arguments.length ? k : 1); + return new d3_hsl(this.h, this.s, k * this.l); + }; + d3_hslPrototype.rgb = function() { + return d3_hsl_rgb(this.h, this.s, this.l); + }; + function d3_hsl_rgb(h, s, l) { + var m1, m2; + h = isNaN(h) ? 0 : (h %= 360) < 0 ? h + 360 : h; + s = isNaN(s) ? 0 : s < 0 ? 0 : s > 1 ? 1 : s; + l = l < 0 ? 0 : l > 1 ? 1 : l; + m2 = l <= .5 ? l * (1 + s) : l + s - l * s; + m1 = 2 * l - m2; + function v(h) { + if (h > 360) h -= 360; else if (h < 0) h += 360; + if (h < 60) return m1 + (m2 - m1) * h / 60; + if (h < 180) return m2; + if (h < 240) return m1 + (m2 - m1) * (240 - h) / 60; + return m1; + } + function vv(h) { + return Math.round(v(h) * 255); + } + return new d3_rgb(vv(h + 120), vv(h), vv(h - 120)); + } + d3.hcl = d3_hcl; + function d3_hcl(h, c, l) { + return this instanceof d3_hcl ? void (this.h = +h, this.c = +c, this.l = +l) : arguments.length < 2 ? h instanceof d3_hcl ? new d3_hcl(h.h, h.c, h.l) : h instanceof d3_lab ? d3_lab_hcl(h.l, h.a, h.b) : d3_lab_hcl((h = d3_rgb_lab((h = d3.rgb(h)).r, h.g, h.b)).l, h.a, h.b) : new d3_hcl(h, c, l); + } + var d3_hclPrototype = d3_hcl.prototype = new d3_color(); + d3_hclPrototype.brighter = function(k) { + return new d3_hcl(this.h, this.c, Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1))); + }; + d3_hclPrototype.darker = function(k) { + return new d3_hcl(this.h, this.c, Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1))); + }; + d3_hclPrototype.rgb = function() { + return d3_hcl_lab(this.h, this.c, this.l).rgb(); + }; + function d3_hcl_lab(h, c, l) { + if (isNaN(h)) h = 0; + if (isNaN(c)) c = 0; + return new d3_lab(l, Math.cos(h *= d3_radians) * c, Math.sin(h) * c); + } + d3.lab = d3_lab; + function d3_lab(l, a, b) { + return this instanceof d3_lab ? void (this.l = +l, this.a = +a, this.b = +b) : arguments.length < 2 ? l instanceof d3_lab ? new d3_lab(l.l, l.a, l.b) : l instanceof d3_hcl ? d3_hcl_lab(l.h, l.c, l.l) : d3_rgb_lab((l = d3_rgb(l)).r, l.g, l.b) : new d3_lab(l, a, b); + } + var d3_lab_K = 18; + var d3_lab_X = .95047, d3_lab_Y = 1, d3_lab_Z = 1.08883; + var d3_labPrototype = d3_lab.prototype = new d3_color(); + d3_labPrototype.brighter = function(k) { + return new d3_lab(Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)), this.a, this.b); + }; + d3_labPrototype.darker = function(k) { + return new d3_lab(Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)), this.a, this.b); + }; + d3_labPrototype.rgb = function() { + return d3_lab_rgb(this.l, this.a, this.b); + }; + function d3_lab_rgb(l, a, b) { + var y = (l + 16) / 116, x = y + a / 500, z = y - b / 200; + x = d3_lab_xyz(x) * d3_lab_X; + y = d3_lab_xyz(y) * d3_lab_Y; + z = d3_lab_xyz(z) * d3_lab_Z; + return new d3_rgb(d3_xyz_rgb(3.2404542 * x - 1.5371385 * y - .4985314 * z), d3_xyz_rgb(-.969266 * x + 1.8760108 * y + .041556 * z), d3_xyz_rgb(.0556434 * x - .2040259 * y + 1.0572252 * z)); + } + function d3_lab_hcl(l, a, b) { + return l > 0 ? new d3_hcl(Math.atan2(b, a) * d3_degrees, Math.sqrt(a * a + b * b), l) : new d3_hcl(NaN, NaN, l); + } + function d3_lab_xyz(x) { + return x > .206893034 ? x * x * x : (x - 4 / 29) / 7.787037; + } + function d3_xyz_lab(x) { + return x > .008856 ? Math.pow(x, 1 / 3) : 7.787037 * x + 4 / 29; + } + function d3_xyz_rgb(r) { + return Math.round(255 * (r <= .00304 ? 12.92 * r : 1.055 * Math.pow(r, 1 / 2.4) - .055)); + } + d3.rgb = d3_rgb; + function d3_rgb(r, g, b) { + return this instanceof d3_rgb ? void (this.r = ~~r, this.g = ~~g, this.b = ~~b) : arguments.length < 2 ? r instanceof d3_rgb ? new d3_rgb(r.r, r.g, r.b) : d3_rgb_parse("" + r, d3_rgb, d3_hsl_rgb) : new d3_rgb(r, g, b); + } + function d3_rgbNumber(value) { + return new d3_rgb(value >> 16, value >> 8 & 255, value & 255); + } + function d3_rgbString(value) { + return d3_rgbNumber(value) + ""; + } + var d3_rgbPrototype = d3_rgb.prototype = new d3_color(); + d3_rgbPrototype.brighter = function(k) { + k = Math.pow(.7, arguments.length ? k : 1); + var r = this.r, g = this.g, b = this.b, i = 30; + if (!r && !g && !b) return new d3_rgb(i, i, i); + if (r && r < i) r = i; + if (g && g < i) g = i; + if (b && b < i) b = i; + return new d3_rgb(Math.min(255, r / k), Math.min(255, g / k), Math.min(255, b / k)); + }; + d3_rgbPrototype.darker = function(k) { + k = Math.pow(.7, arguments.length ? k : 1); + return new d3_rgb(k * this.r, k * this.g, k * this.b); + }; + d3_rgbPrototype.hsl = function() { + return d3_rgb_hsl(this.r, this.g, this.b); + }; + d3_rgbPrototype.toString = function() { + return "#" + d3_rgb_hex(this.r) + d3_rgb_hex(this.g) + d3_rgb_hex(this.b); + }; + function d3_rgb_hex(v) { + return v < 16 ? "0" + Math.max(0, v).toString(16) : Math.min(255, v).toString(16); + } + function d3_rgb_parse(format, rgb, hsl) { + var r = 0, g = 0, b = 0, m1, m2, color; + m1 = /([a-z]+)\((.*)\)/.exec(format = format.toLowerCase()); + if (m1) { + m2 = m1[2].split(","); + switch (m1[1]) { + case "hsl": + { + return hsl(parseFloat(m2[0]), parseFloat(m2[1]) / 100, parseFloat(m2[2]) / 100); + } + + case "rgb": + { + return rgb(d3_rgb_parseNumber(m2[0]), d3_rgb_parseNumber(m2[1]), d3_rgb_parseNumber(m2[2])); + } + } + } + if (color = d3_rgb_names.get(format)) { + return rgb(color.r, color.g, color.b); + } + if (format != null && format.charAt(0) === "#" && !isNaN(color = parseInt(format.slice(1), 16))) { + if (format.length === 4) { + r = (color & 3840) >> 4; + r = r >> 4 | r; + g = color & 240; + g = g >> 4 | g; + b = color & 15; + b = b << 4 | b; + } else if (format.length === 7) { + r = (color & 16711680) >> 16; + g = (color & 65280) >> 8; + b = color & 255; + } + } + return rgb(r, g, b); + } + function d3_rgb_hsl(r, g, b) { + var min = Math.min(r /= 255, g /= 255, b /= 255), max = Math.max(r, g, b), d = max - min, h, s, l = (max + min) / 2; + if (d) { + s = l < .5 ? d / (max + min) : d / (2 - max - min); + if (r == max) h = (g - b) / d + (g < b ? 6 : 0); else if (g == max) h = (b - r) / d + 2; else h = (r - g) / d + 4; + h *= 60; + } else { + h = NaN; + s = l > 0 && l < 1 ? 0 : h; + } + return new d3_hsl(h, s, l); + } + function d3_rgb_lab(r, g, b) { + r = d3_rgb_xyz(r); + g = d3_rgb_xyz(g); + b = d3_rgb_xyz(b); + var x = d3_xyz_lab((.4124564 * r + .3575761 * g + .1804375 * b) / d3_lab_X), y = d3_xyz_lab((.2126729 * r + .7151522 * g + .072175 * b) / d3_lab_Y), z = d3_xyz_lab((.0193339 * r + .119192 * g + .9503041 * b) / d3_lab_Z); + return d3_lab(116 * y - 16, 500 * (x - y), 200 * (y - z)); + } + function d3_rgb_xyz(r) { + return (r /= 255) <= .04045 ? r / 12.92 : Math.pow((r + .055) / 1.055, 2.4); + } + function d3_rgb_parseNumber(c) { + var f = parseFloat(c); + return c.charAt(c.length - 1) === "%" ? Math.round(f * 2.55) : f; + } + var d3_rgb_names = d3.map({ + aliceblue: 15792383, + antiquewhite: 16444375, + aqua: 65535, + aquamarine: 8388564, + azure: 15794175, + beige: 16119260, + bisque: 16770244, + black: 0, + blanchedalmond: 16772045, + blue: 255, + blueviolet: 9055202, + brown: 10824234, + burlywood: 14596231, + cadetblue: 6266528, + chartreuse: 8388352, + chocolate: 13789470, + coral: 16744272, + cornflowerblue: 6591981, + cornsilk: 16775388, + crimson: 14423100, + cyan: 65535, + darkblue: 139, + darkcyan: 35723, + darkgoldenrod: 12092939, + darkgray: 11119017, + darkgreen: 25600, + darkgrey: 11119017, + darkkhaki: 12433259, + darkmagenta: 9109643, + darkolivegreen: 5597999, + darkorange: 16747520, + darkorchid: 10040012, + darkred: 9109504, + darksalmon: 15308410, + darkseagreen: 9419919, + darkslateblue: 4734347, + darkslategray: 3100495, + darkslategrey: 3100495, + darkturquoise: 52945, + darkviolet: 9699539, + deeppink: 16716947, + deepskyblue: 49151, + dimgray: 6908265, + dimgrey: 6908265, + dodgerblue: 2003199, + firebrick: 11674146, + floralwhite: 16775920, + forestgreen: 2263842, + fuchsia: 16711935, + gainsboro: 14474460, + ghostwhite: 16316671, + gold: 16766720, + goldenrod: 14329120, + gray: 8421504, + green: 32768, + greenyellow: 11403055, + grey: 8421504, + honeydew: 15794160, + hotpink: 16738740, + indianred: 13458524, + indigo: 4915330, + ivory: 16777200, + khaki: 15787660, + lavender: 15132410, + lavenderblush: 16773365, + lawngreen: 8190976, + lemonchiffon: 16775885, + lightblue: 11393254, + lightcoral: 15761536, + lightcyan: 14745599, + lightgoldenrodyellow: 16448210, + lightgray: 13882323, + lightgreen: 9498256, + lightgrey: 13882323, + lightpink: 16758465, + lightsalmon: 16752762, + lightseagreen: 2142890, + lightskyblue: 8900346, + lightslategray: 7833753, + lightslategrey: 7833753, + lightsteelblue: 11584734, + lightyellow: 16777184, + lime: 65280, + limegreen: 3329330, + linen: 16445670, + magenta: 16711935, + maroon: 8388608, + mediumaquamarine: 6737322, + mediumblue: 205, + mediumorchid: 12211667, + mediumpurple: 9662683, + mediumseagreen: 3978097, + mediumslateblue: 8087790, + mediumspringgreen: 64154, + mediumturquoise: 4772300, + mediumvioletred: 13047173, + midnightblue: 1644912, + mintcream: 16121850, + mistyrose: 16770273, + moccasin: 16770229, + navajowhite: 16768685, + navy: 128, + oldlace: 16643558, + olive: 8421376, + olivedrab: 7048739, + orange: 16753920, + orangered: 16729344, + orchid: 14315734, + palegoldenrod: 15657130, + palegreen: 10025880, + paleturquoise: 11529966, + palevioletred: 14381203, + papayawhip: 16773077, + peachpuff: 16767673, + peru: 13468991, + pink: 16761035, + plum: 14524637, + powderblue: 11591910, + purple: 8388736, + rebeccapurple: 6697881, + red: 16711680, + rosybrown: 12357519, + royalblue: 4286945, + saddlebrown: 9127187, + salmon: 16416882, + sandybrown: 16032864, + seagreen: 3050327, + seashell: 16774638, + sienna: 10506797, + silver: 12632256, + skyblue: 8900331, + slateblue: 6970061, + slategray: 7372944, + slategrey: 7372944, + snow: 16775930, + springgreen: 65407, + steelblue: 4620980, + tan: 13808780, + teal: 32896, + thistle: 14204888, + tomato: 16737095, + turquoise: 4251856, + violet: 15631086, + wheat: 16113331, + white: 16777215, + whitesmoke: 16119285, + yellow: 16776960, + yellowgreen: 10145074 + }); + d3_rgb_names.forEach(function(key, value) { + d3_rgb_names.set(key, d3_rgbNumber(value)); + }); + function d3_functor(v) { + return typeof v === "function" ? v : function() { + return v; + }; + } + d3.functor = d3_functor; + d3.xhr = d3_xhrType(d3_identity); + function d3_xhrType(response) { + return function(url, mimeType, callback) { + if (arguments.length === 2 && typeof mimeType === "function") callback = mimeType, + mimeType = null; + return d3_xhr(url, mimeType, response, callback); + }; + } + function d3_xhr(url, mimeType, response, callback) { + var xhr = {}, dispatch = d3.dispatch("beforesend", "progress", "load", "error"), headers = {}, request = new XMLHttpRequest(), responseType = null; + if (this.XDomainRequest && !("withCredentials" in request) && /^(http(s)?:)?\/\//.test(url)) request = new XDomainRequest(); + "onload" in request ? request.onload = request.onerror = respond : request.onreadystatechange = function() { + request.readyState > 3 && respond(); + }; + function respond() { + var status = request.status, result; + if (!status && d3_xhrHasResponse(request) || status >= 200 && status < 300 || status === 304) { + try { + result = response.call(xhr, request); + } catch (e) { + dispatch.error.call(xhr, e); + return; + } + dispatch.load.call(xhr, result); + } else { + dispatch.error.call(xhr, request); + } + } + request.onprogress = function(event) { + var o = d3.event; + d3.event = event; + try { + dispatch.progress.call(xhr, request); + } finally { + d3.event = o; + } + }; + xhr.header = function(name, value) { + name = (name + "").toLowerCase(); + if (arguments.length < 2) return headers[name]; + if (value == null) delete headers[name]; else headers[name] = value + ""; + return xhr; + }; + xhr.mimeType = function(value) { + if (!arguments.length) return mimeType; + mimeType = value == null ? null : value + ""; + return xhr; + }; + xhr.responseType = function(value) { + if (!arguments.length) return responseType; + responseType = value; + return xhr; + }; + xhr.response = function(value) { + response = value; + return xhr; + }; + [ "get", "post" ].forEach(function(method) { + xhr[method] = function() { + return xhr.send.apply(xhr, [ method ].concat(d3_array(arguments))); + }; + }); + xhr.send = function(method, data, callback) { + if (arguments.length === 2 && typeof data === "function") callback = data, data = null; + request.open(method, url, true); + if (mimeType != null && !("accept" in headers)) headers["accept"] = mimeType + ",*/*"; + if (request.setRequestHeader) for (var name in headers) request.setRequestHeader(name, headers[name]); + if (mimeType != null && request.overrideMimeType) request.overrideMimeType(mimeType); + if (responseType != null) request.responseType = responseType; + if (callback != null) xhr.on("error", callback).on("load", function(request) { + callback(null, request); + }); + dispatch.beforesend.call(xhr, request); + request.send(data == null ? null : data); + return xhr; + }; + xhr.abort = function() { + request.abort(); + return xhr; + }; + d3.rebind(xhr, dispatch, "on"); + return callback == null ? xhr : xhr.get(d3_xhr_fixCallback(callback)); + } + function d3_xhr_fixCallback(callback) { + return callback.length === 1 ? function(error, request) { + callback(error == null ? request : null); + } : callback; + } + function d3_xhrHasResponse(request) { + var type = request.responseType; + return type && type !== "text" ? request.response : request.responseText; + } + d3.dsv = function(delimiter, mimeType) { + var reFormat = new RegExp('["' + delimiter + "\n]"), delimiterCode = delimiter.charCodeAt(0); + function dsv(url, row, callback) { + if (arguments.length < 3) callback = row, row = null; + var xhr = d3_xhr(url, mimeType, row == null ? response : typedResponse(row), callback); + xhr.row = function(_) { + return arguments.length ? xhr.response((row = _) == null ? response : typedResponse(_)) : row; + }; + return xhr; + } + function response(request) { + return dsv.parse(request.responseText); + } + function typedResponse(f) { + return function(request) { + return dsv.parse(request.responseText, f); + }; + } + dsv.parse = function(text, f) { + var o; + return dsv.parseRows(text, function(row, i) { + if (o) return o(row, i - 1); + var a = new Function("d", "return {" + row.map(function(name, i) { + return JSON.stringify(name) + ": d[" + i + "]"; + }).join(",") + "}"); + o = f ? function(row, i) { + return f(a(row), i); + } : a; + }); + }; + dsv.parseRows = function(text, f) { + var EOL = {}, EOF = {}, rows = [], N = text.length, I = 0, n = 0, t, eol; + function token() { + if (I >= N) return EOF; + if (eol) return eol = false, EOL; + var j = I; + if (text.charCodeAt(j) === 34) { + var i = j; + while (i++ < N) { + if (text.charCodeAt(i) === 34) { + if (text.charCodeAt(i + 1) !== 34) break; + ++i; + } + } + I = i + 2; + var c = text.charCodeAt(i + 1); + if (c === 13) { + eol = true; + if (text.charCodeAt(i + 2) === 10) ++I; + } else if (c === 10) { + eol = true; + } + return text.slice(j + 1, i).replace(/""/g, '"'); + } + while (I < N) { + var c = text.charCodeAt(I++), k = 1; + if (c === 10) eol = true; else if (c === 13) { + eol = true; + if (text.charCodeAt(I) === 10) ++I, ++k; + } else if (c !== delimiterCode) continue; + return text.slice(j, I - k); + } + return text.slice(j); + } + while ((t = token()) !== EOF) { + var a = []; + while (t !== EOL && t !== EOF) { + a.push(t); + t = token(); + } + if (f && (a = f(a, n++)) == null) continue; + rows.push(a); + } + return rows; + }; + dsv.format = function(rows) { + if (Array.isArray(rows[0])) return dsv.formatRows(rows); + var fieldSet = new d3_Set(), fields = []; + rows.forEach(function(row) { + for (var field in row) { + if (!fieldSet.has(field)) { + fields.push(fieldSet.add(field)); + } + } + }); + return [ fields.map(formatValue).join(delimiter) ].concat(rows.map(function(row) { + return fields.map(function(field) { + return formatValue(row[field]); + }).join(delimiter); + })).join("\n"); + }; + dsv.formatRows = function(rows) { + return rows.map(formatRow).join("\n"); + }; + function formatRow(row) { + return row.map(formatValue).join(delimiter); + } + function formatValue(text) { + return reFormat.test(text) ? '"' + text.replace(/\"/g, '""') + '"' : text; + } + return dsv; + }; + d3.csv = d3.dsv(",", "text/csv"); + d3.tsv = d3.dsv(" ", "text/tab-separated-values"); + var d3_timer_queueHead, d3_timer_queueTail, d3_timer_interval, d3_timer_timeout, d3_timer_frame = this[d3_vendorSymbol(this, "requestAnimationFrame")] || function(callback) { + setTimeout(callback, 17); + }; + d3.timer = function() { + d3_timer.apply(this, arguments); + }; + function d3_timer(callback, delay, then) { + var n = arguments.length; + if (n < 2) delay = 0; + if (n < 3) then = Date.now(); + var time = then + delay, timer = { + c: callback, + t: time, + n: null + }; + if (d3_timer_queueTail) d3_timer_queueTail.n = timer; else d3_timer_queueHead = timer; + d3_timer_queueTail = timer; + if (!d3_timer_interval) { + d3_timer_timeout = clearTimeout(d3_timer_timeout); + d3_timer_interval = 1; + d3_timer_frame(d3_timer_step); + } + return timer; + } + function d3_timer_step() { + var now = d3_timer_mark(), delay = d3_timer_sweep() - now; + if (delay > 24) { + if (isFinite(delay)) { + clearTimeout(d3_timer_timeout); + d3_timer_timeout = setTimeout(d3_timer_step, delay); + } + d3_timer_interval = 0; + } else { + d3_timer_interval = 1; + d3_timer_frame(d3_timer_step); + } + } + d3.timer.flush = function() { + d3_timer_mark(); + d3_timer_sweep(); + }; + function d3_timer_mark() { + var now = Date.now(), timer = d3_timer_queueHead; + while (timer) { + if (now >= timer.t && timer.c(now - timer.t)) timer.c = null; + timer = timer.n; + } + return now; + } + function d3_timer_sweep() { + var t0, t1 = d3_timer_queueHead, time = Infinity; + while (t1) { + if (t1.c) { + if (t1.t < time) time = t1.t; + t1 = (t0 = t1).n; + } else { + t1 = t0 ? t0.n = t1.n : d3_timer_queueHead = t1.n; + } + } + d3_timer_queueTail = t0; + return time; + } + function d3_format_precision(x, p) { + return p - (x ? Math.ceil(Math.log(x) / Math.LN10) : 1); + } + d3.round = function(x, n) { + return n ? Math.round(x * (n = Math.pow(10, n))) / n : Math.round(x); + }; + var d3_formatPrefixes = [ "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y" ].map(d3_formatPrefix); + d3.formatPrefix = function(value, precision) { + var i = 0; + if (value = +value) { + if (value < 0) value *= -1; + if (precision) value = d3.round(value, d3_format_precision(value, precision)); + i = 1 + Math.floor(1e-12 + Math.log(value) / Math.LN10); + i = Math.max(-24, Math.min(24, Math.floor((i - 1) / 3) * 3)); + } + return d3_formatPrefixes[8 + i / 3]; + }; + function d3_formatPrefix(d, i) { + var k = Math.pow(10, abs(8 - i) * 3); + return { + scale: i > 8 ? function(d) { + return d / k; + } : function(d) { + return d * k; + }, + symbol: d + }; + } + function d3_locale_numberFormat(locale) { + var locale_decimal = locale.decimal, locale_thousands = locale.thousands, locale_grouping = locale.grouping, locale_currency = locale.currency, formatGroup = locale_grouping && locale_thousands ? function(value, width) { + var i = value.length, t = [], j = 0, g = locale_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 = locale_grouping[j = (j + 1) % locale_grouping.length]; + } + return t.reverse().join(locale_thousands); + } : d3_identity; + return function(specifier) { + var match = d3_format_re.exec(specifier), fill = match[1] || " ", align = match[2] || ">", sign = match[3] || "-", symbol = match[4] || "", zfill = match[5], width = +match[6], comma = match[7], precision = match[8], type = match[9], scale = 1, prefix = "", suffix = "", integer = false, exponent = true; + if (precision) precision = +precision.substring(1); + if (zfill || fill === "0" && align === "=") { + zfill = fill = "0"; + align = "="; + } + switch (type) { + case "n": + comma = true; + type = "g"; + break; + + case "%": + scale = 100; + suffix = "%"; + type = "f"; + break; + + case "p": + scale = 100; + suffix = "%"; + type = "r"; + break; + + case "b": + case "o": + case "x": + case "X": + if (symbol === "#") prefix = "0" + type.toLowerCase(); + + case "c": + exponent = false; + + case "d": + integer = true; + precision = 0; + break; + + case "s": + scale = -1; + type = "r"; + break; + } + if (symbol === "$") prefix = locale_currency[0], suffix = locale_currency[1]; + if (type == "r" && !precision) type = "g"; + if (precision != null) { + if (type == "g") precision = Math.max(1, Math.min(21, precision)); else if (type == "e" || type == "f") precision = Math.max(0, Math.min(20, precision)); + } + type = d3_format_types.get(type) || d3_format_typeDefault; + var zcomma = zfill && comma; + return function(value) { + var fullSuffix = suffix; + if (integer && value % 1) return ""; + var negative = value < 0 || value === 0 && 1 / value < 0 ? (value = -value, "-") : sign === "-" ? "" : sign; + if (scale < 0) { + var unit = d3.formatPrefix(value, precision); + value = unit.scale(value); + fullSuffix = unit.symbol + suffix; + } else { + value *= scale; + } + value = type(value, precision); + var i = value.lastIndexOf("."), before, after; + if (i < 0) { + var j = exponent ? value.lastIndexOf("e") : -1; + if (j < 0) before = value, after = ""; else before = value.substring(0, j), after = value.substring(j); + } else { + before = value.substring(0, i); + after = locale_decimal + value.substring(i + 1); + } + if (!zfill && comma) before = formatGroup(before, Infinity); + var length = prefix.length + before.length + after.length + (zcomma ? 0 : negative.length), padding = length < width ? new Array(length = width - length + 1).join(fill) : ""; + if (zcomma) before = formatGroup(padding + before, padding.length ? width - after.length : Infinity); + negative += prefix; + value = before + after; + return (align === "<" ? negative + value + padding : align === ">" ? padding + negative + value : align === "^" ? padding.substring(0, length >>= 1) + negative + value + padding.substring(length) : negative + (zcomma ? value : padding + value)) + fullSuffix; + }; + }; + } + var d3_format_re = /(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i; + var d3_format_types = d3.map({ + b: function(x) { + return x.toString(2); + }, + c: function(x) { + return String.fromCharCode(x); + }, + o: function(x) { + return x.toString(8); + }, + x: function(x) { + return x.toString(16); + }, + X: function(x) { + return x.toString(16).toUpperCase(); + }, + g: function(x, p) { + return x.toPrecision(p); + }, + e: function(x, p) { + return x.toExponential(p); + }, + f: function(x, p) { + return x.toFixed(p); + }, + r: function(x, p) { + return (x = d3.round(x, d3_format_precision(x, p))).toFixed(Math.max(0, Math.min(20, d3_format_precision(x * (1 + 1e-15), p)))); + } + }); + function d3_format_typeDefault(x) { + return x + ""; + } + var d3_time = d3.time = {}, d3_date = Date; + function d3_date_utc() { + this._ = new Date(arguments.length > 1 ? Date.UTC.apply(this, arguments) : arguments[0]); + } + d3_date_utc.prototype = { + getDate: function() { + return this._.getUTCDate(); + }, + getDay: function() { + return this._.getUTCDay(); + }, + getFullYear: function() { + return this._.getUTCFullYear(); + }, + getHours: function() { + return this._.getUTCHours(); + }, + getMilliseconds: function() { + return this._.getUTCMilliseconds(); + }, + getMinutes: function() { + return this._.getUTCMinutes(); + }, + getMonth: function() { + return this._.getUTCMonth(); + }, + getSeconds: function() { + return this._.getUTCSeconds(); + }, + getTime: function() { + return this._.getTime(); + }, + getTimezoneOffset: function() { + return 0; + }, + valueOf: function() { + return this._.valueOf(); + }, + setDate: function() { + d3_time_prototype.setUTCDate.apply(this._, arguments); + }, + setDay: function() { + d3_time_prototype.setUTCDay.apply(this._, arguments); + }, + setFullYear: function() { + d3_time_prototype.setUTCFullYear.apply(this._, arguments); + }, + setHours: function() { + d3_time_prototype.setUTCHours.apply(this._, arguments); + }, + setMilliseconds: function() { + d3_time_prototype.setUTCMilliseconds.apply(this._, arguments); + }, + setMinutes: function() { + d3_time_prototype.setUTCMinutes.apply(this._, arguments); + }, + setMonth: function() { + d3_time_prototype.setUTCMonth.apply(this._, arguments); + }, + setSeconds: function() { + d3_time_prototype.setUTCSeconds.apply(this._, arguments); + }, + setTime: function() { + d3_time_prototype.setTime.apply(this._, arguments); + } + }; + var d3_time_prototype = Date.prototype; + function d3_time_interval(local, step, number) { + function round(date) { + var d0 = local(date), d1 = offset(d0, 1); + return date - d0 < d1 - date ? d0 : d1; + } + function ceil(date) { + step(date = local(new d3_date(date - 1)), 1); + return date; + } + function offset(date, k) { + step(date = new d3_date(+date), k); + return date; + } + function range(t0, t1, dt) { + var time = ceil(t0), times = []; + if (dt > 1) { + while (time < t1) { + if (!(number(time) % dt)) times.push(new Date(+time)); + step(time, 1); + } + } else { + while (time < t1) times.push(new Date(+time)), step(time, 1); + } + return times; + } + function range_utc(t0, t1, dt) { + try { + d3_date = d3_date_utc; + var utc = new d3_date_utc(); + utc._ = t0; + return range(utc, t1, dt); + } finally { + d3_date = Date; + } + } + local.floor = local; + local.round = round; + local.ceil = ceil; + local.offset = offset; + local.range = range; + var utc = local.utc = d3_time_interval_utc(local); + utc.floor = utc; + utc.round = d3_time_interval_utc(round); + utc.ceil = d3_time_interval_utc(ceil); + utc.offset = d3_time_interval_utc(offset); + utc.range = range_utc; + return local; + } + function d3_time_interval_utc(method) { + return function(date, k) { + try { + d3_date = d3_date_utc; + var utc = new d3_date_utc(); + utc._ = date; + return method(utc, k)._; + } finally { + d3_date = Date; + } + }; + } + d3_time.year = d3_time_interval(function(date) { + date = d3_time.day(date); + date.setMonth(0, 1); + return date; + }, function(date, offset) { + date.setFullYear(date.getFullYear() + offset); + }, function(date) { + return date.getFullYear(); + }); + d3_time.years = d3_time.year.range; + d3_time.years.utc = d3_time.year.utc.range; + d3_time.day = d3_time_interval(function(date) { + var day = new d3_date(2e3, 0); + day.setFullYear(date.getFullYear(), date.getMonth(), date.getDate()); + return day; + }, function(date, offset) { + date.setDate(date.getDate() + offset); + }, function(date) { + return date.getDate() - 1; + }); + d3_time.days = d3_time.day.range; + d3_time.days.utc = d3_time.day.utc.range; + d3_time.dayOfYear = function(date) { + var year = d3_time.year(date); + return Math.floor((date - year - (date.getTimezoneOffset() - year.getTimezoneOffset()) * 6e4) / 864e5); + }; + [ "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday" ].forEach(function(day, i) { + i = 7 - i; + var interval = d3_time[day] = d3_time_interval(function(date) { + (date = d3_time.day(date)).setDate(date.getDate() - (date.getDay() + i) % 7); + return date; + }, function(date, offset) { + date.setDate(date.getDate() + Math.floor(offset) * 7); + }, function(date) { + var day = d3_time.year(date).getDay(); + return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7) - (day !== i); + }); + d3_time[day + "s"] = interval.range; + d3_time[day + "s"].utc = interval.utc.range; + d3_time[day + "OfYear"] = function(date) { + var day = d3_time.year(date).getDay(); + return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7); + }; + }); + d3_time.week = d3_time.sunday; + d3_time.weeks = d3_time.sunday.range; + d3_time.weeks.utc = d3_time.sunday.utc.range; + d3_time.weekOfYear = d3_time.sundayOfYear; + function d3_locale_timeFormat(locale) { + var locale_dateTime = locale.dateTime, locale_date = locale.date, locale_time = locale.time, locale_periods = locale.periods, locale_days = locale.days, locale_shortDays = locale.shortDays, locale_months = locale.months, locale_shortMonths = locale.shortMonths; + function d3_time_format(template) { + var n = template.length; + function format(date) { + var string = [], i = -1, j = 0, c, p, f; + while (++i < n) { + if (template.charCodeAt(i) === 37) { + string.push(template.slice(j, i)); + if ((p = d3_time_formatPads[c = template.charAt(++i)]) != null) c = template.charAt(++i); + if (f = d3_time_formats[c]) c = f(date, p == null ? c === "e" ? " " : "0" : p); + string.push(c); + j = i + 1; + } + } + string.push(template.slice(j, i)); + return string.join(""); + } + format.parse = function(string) { + var d = { + y: 1900, + m: 0, + d: 1, + H: 0, + M: 0, + S: 0, + L: 0, + Z: null + }, i = d3_time_parse(d, template, string, 0); + if (i != string.length) return null; + if ("p" in d) d.H = d.H % 12 + d.p * 12; + var localZ = d.Z != null && d3_date !== d3_date_utc, date = new (localZ ? d3_date_utc : d3_date)(); + if ("j" in d) date.setFullYear(d.y, 0, d.j); else if ("W" in d || "U" in d) { + if (!("w" in d)) d.w = "W" in d ? 1 : 0; + date.setFullYear(d.y, 0, 1); + date.setFullYear(d.y, 0, "W" in d ? (d.w + 6) % 7 + d.W * 7 - (date.getDay() + 5) % 7 : d.w + d.U * 7 - (date.getDay() + 6) % 7); + } else date.setFullYear(d.y, d.m, d.d); + date.setHours(d.H + (d.Z / 100 | 0), d.M + d.Z % 100, d.S, d.L); + return localZ ? date._ : date; + }; + format.toString = function() { + return template; + }; + return format; + } + function d3_time_parse(date, template, string, j) { + var c, p, t, i = 0, n = template.length, m = string.length; + while (i < n) { + if (j >= m) return -1; + c = template.charCodeAt(i++); + if (c === 37) { + t = template.charAt(i++); + p = d3_time_parsers[t in d3_time_formatPads ? template.charAt(i++) : t]; + if (!p || (j = p(date, string, j)) < 0) return -1; + } else if (c != string.charCodeAt(j++)) { + return -1; + } + } + return j; + } + d3_time_format.utc = function(template) { + var local = d3_time_format(template); + function format(date) { + try { + d3_date = d3_date_utc; + var utc = new d3_date(); + utc._ = date; + return local(utc); + } finally { + d3_date = Date; + } + } + format.parse = function(string) { + try { + d3_date = d3_date_utc; + var date = local.parse(string); + return date && date._; + } finally { + d3_date = Date; + } + }; + format.toString = local.toString; + return format; + }; + d3_time_format.multi = d3_time_format.utc.multi = d3_time_formatMulti; + var d3_time_periodLookup = d3.map(), d3_time_dayRe = d3_time_formatRe(locale_days), d3_time_dayLookup = d3_time_formatLookup(locale_days), d3_time_dayAbbrevRe = d3_time_formatRe(locale_shortDays), d3_time_dayAbbrevLookup = d3_time_formatLookup(locale_shortDays), d3_time_monthRe = d3_time_formatRe(locale_months), d3_time_monthLookup = d3_time_formatLookup(locale_months), d3_time_monthAbbrevRe = d3_time_formatRe(locale_shortMonths), d3_time_monthAbbrevLookup = d3_time_formatLookup(locale_shortMonths); + locale_periods.forEach(function(p, i) { + d3_time_periodLookup.set(p.toLowerCase(), i); + }); + var d3_time_formats = { + a: function(d) { + return locale_shortDays[d.getDay()]; + }, + A: function(d) { + return locale_days[d.getDay()]; + }, + b: function(d) { + return locale_shortMonths[d.getMonth()]; + }, + B: function(d) { + return locale_months[d.getMonth()]; + }, + c: d3_time_format(locale_dateTime), + d: function(d, p) { + return d3_time_formatPad(d.getDate(), p, 2); + }, + e: function(d, p) { + return d3_time_formatPad(d.getDate(), p, 2); + }, + H: function(d, p) { + return d3_time_formatPad(d.getHours(), p, 2); + }, + I: function(d, p) { + return d3_time_formatPad(d.getHours() % 12 || 12, p, 2); + }, + j: function(d, p) { + return d3_time_formatPad(1 + d3_time.dayOfYear(d), p, 3); + }, + L: function(d, p) { + return d3_time_formatPad(d.getMilliseconds(), p, 3); + }, + m: function(d, p) { + return d3_time_formatPad(d.getMonth() + 1, p, 2); + }, + M: function(d, p) { + return d3_time_formatPad(d.getMinutes(), p, 2); + }, + p: function(d) { + return locale_periods[+(d.getHours() >= 12)]; + }, + S: function(d, p) { + return d3_time_formatPad(d.getSeconds(), p, 2); + }, + U: function(d, p) { + return d3_time_formatPad(d3_time.sundayOfYear(d), p, 2); + }, + w: function(d) { + return d.getDay(); + }, + W: function(d, p) { + return d3_time_formatPad(d3_time.mondayOfYear(d), p, 2); + }, + x: d3_time_format(locale_date), + X: d3_time_format(locale_time), + y: function(d, p) { + return d3_time_formatPad(d.getFullYear() % 100, p, 2); + }, + Y: function(d, p) { + return d3_time_formatPad(d.getFullYear() % 1e4, p, 4); + }, + Z: d3_time_zone, + "%": function() { + return "%"; + } + }; + var d3_time_parsers = { + a: d3_time_parseWeekdayAbbrev, + A: d3_time_parseWeekday, + b: d3_time_parseMonthAbbrev, + B: d3_time_parseMonth, + c: d3_time_parseLocaleFull, + d: d3_time_parseDay, + e: d3_time_parseDay, + H: d3_time_parseHour24, + I: d3_time_parseHour24, + j: d3_time_parseDayOfYear, + L: d3_time_parseMilliseconds, + m: d3_time_parseMonthNumber, + M: d3_time_parseMinutes, + p: d3_time_parseAmPm, + S: d3_time_parseSeconds, + U: d3_time_parseWeekNumberSunday, + w: d3_time_parseWeekdayNumber, + W: d3_time_parseWeekNumberMonday, + x: d3_time_parseLocaleDate, + X: d3_time_parseLocaleTime, + y: d3_time_parseYear, + Y: d3_time_parseFullYear, + Z: d3_time_parseZone, + "%": d3_time_parseLiteralPercent + }; + function d3_time_parseWeekdayAbbrev(date, string, i) { + d3_time_dayAbbrevRe.lastIndex = 0; + var n = d3_time_dayAbbrevRe.exec(string.slice(i)); + return n ? (date.w = d3_time_dayAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; + } + function d3_time_parseWeekday(date, string, i) { + d3_time_dayRe.lastIndex = 0; + var n = d3_time_dayRe.exec(string.slice(i)); + return n ? (date.w = d3_time_dayLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; + } + function d3_time_parseMonthAbbrev(date, string, i) { + d3_time_monthAbbrevRe.lastIndex = 0; + var n = d3_time_monthAbbrevRe.exec(string.slice(i)); + return n ? (date.m = d3_time_monthAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; + } + function d3_time_parseMonth(date, string, i) { + d3_time_monthRe.lastIndex = 0; + var n = d3_time_monthRe.exec(string.slice(i)); + return n ? (date.m = d3_time_monthLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; + } + function d3_time_parseLocaleFull(date, string, i) { + return d3_time_parse(date, d3_time_formats.c.toString(), string, i); + } + function d3_time_parseLocaleDate(date, string, i) { + return d3_time_parse(date, d3_time_formats.x.toString(), string, i); + } + function d3_time_parseLocaleTime(date, string, i) { + return d3_time_parse(date, d3_time_formats.X.toString(), string, i); + } + function d3_time_parseAmPm(date, string, i) { + var n = d3_time_periodLookup.get(string.slice(i, i += 2).toLowerCase()); + return n == null ? -1 : (date.p = n, i); + } + return d3_time_format; + } + var d3_time_formatPads = { + "-": "", + _: " ", + "0": "0" + }, d3_time_numberRe = /^\s*\d+/, d3_time_percentRe = /^%/; + function d3_time_formatPad(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 d3_time_formatRe(names) { + return new RegExp("^(?:" + names.map(d3.requote).join("|") + ")", "i"); + } + function d3_time_formatLookup(names) { + var map = new d3_Map(), i = -1, n = names.length; + while (++i < n) map.set(names[i].toLowerCase(), i); + return map; + } + function d3_time_parseWeekdayNumber(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 1)); + return n ? (date.w = +n[0], i + n[0].length) : -1; + } + function d3_time_parseWeekNumberSunday(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i)); + return n ? (date.U = +n[0], i + n[0].length) : -1; + } + function d3_time_parseWeekNumberMonday(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i)); + return n ? (date.W = +n[0], i + n[0].length) : -1; + } + function d3_time_parseFullYear(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 4)); + return n ? (date.y = +n[0], i + n[0].length) : -1; + } + function d3_time_parseYear(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 2)); + return n ? (date.y = d3_time_expandYear(+n[0]), i + n[0].length) : -1; + } + function d3_time_parseZone(date, string, i) { + return /^[+-]\d{4}$/.test(string = string.slice(i, i + 5)) ? (date.Z = -string, + i + 5) : -1; + } + function d3_time_expandYear(d) { + return d + (d > 68 ? 1900 : 2e3); + } + function d3_time_parseMonthNumber(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 2)); + return n ? (date.m = n[0] - 1, i + n[0].length) : -1; + } + function d3_time_parseDay(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 2)); + return n ? (date.d = +n[0], i + n[0].length) : -1; + } + function d3_time_parseDayOfYear(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 3)); + return n ? (date.j = +n[0], i + n[0].length) : -1; + } + function d3_time_parseHour24(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 2)); + return n ? (date.H = +n[0], i + n[0].length) : -1; + } + function d3_time_parseMinutes(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 2)); + return n ? (date.M = +n[0], i + n[0].length) : -1; + } + function d3_time_parseSeconds(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 2)); + return n ? (date.S = +n[0], i + n[0].length) : -1; + } + function d3_time_parseMilliseconds(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.slice(i, i + 3)); + return n ? (date.L = +n[0], i + n[0].length) : -1; + } + function d3_time_zone(d) { + var z = d.getTimezoneOffset(), zs = z > 0 ? "-" : "+", zh = abs(z) / 60 | 0, zm = abs(z) % 60; + return zs + d3_time_formatPad(zh, "0", 2) + d3_time_formatPad(zm, "0", 2); + } + function d3_time_parseLiteralPercent(date, string, i) { + d3_time_percentRe.lastIndex = 0; + var n = d3_time_percentRe.exec(string.slice(i, i + 1)); + return n ? i + n[0].length : -1; + } + function d3_time_formatMulti(formats) { + var n = formats.length, i = -1; + while (++i < n) formats[i][0] = this(formats[i][0]); + return function(date) { + var i = 0, f = formats[i]; + while (!f[1](date)) f = formats[++i]; + return f[0](date); + }; + } + d3.locale = function(locale) { + return { + numberFormat: d3_locale_numberFormat(locale), + timeFormat: d3_locale_timeFormat(locale) + }; + }; + var d3_locale_enUS = d3.locale({ + decimal: ".", + thousands: ",", + grouping: [ 3 ], + currency: [ "$", "" ], + dateTime: "%a %b %e %X %Y", + date: "%m/%d/%Y", + time: "%H:%M:%S", + 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" ] + }); + d3.format = d3_locale_enUS.numberFormat; + d3.geo = {}; + function d3_adder() {} + d3_adder.prototype = { + s: 0, + t: 0, + add: function(y) { + d3_adderSum(y, this.t, d3_adderTemp); + d3_adderSum(d3_adderTemp.s, this.s, this); + if (this.s) this.t += d3_adderTemp.t; else this.s = d3_adderTemp.t; + }, + reset: function() { + this.s = this.t = 0; + }, + valueOf: function() { + return this.s; + } + }; + var d3_adderTemp = new d3_adder(); + function d3_adderSum(a, b, o) { + var x = o.s = a + b, bv = x - a, av = x - bv; + o.t = a - av + (b - bv); + } + d3.geo.stream = function(object, listener) { + if (object && d3_geo_streamObjectType.hasOwnProperty(object.type)) { + d3_geo_streamObjectType[object.type](object, listener); + } else { + d3_geo_streamGeometry(object, listener); + } + }; + function d3_geo_streamGeometry(geometry, listener) { + if (geometry && d3_geo_streamGeometryType.hasOwnProperty(geometry.type)) { + d3_geo_streamGeometryType[geometry.type](geometry, listener); + } + } + var d3_geo_streamObjectType = { + Feature: function(feature, listener) { + d3_geo_streamGeometry(feature.geometry, listener); + }, + FeatureCollection: function(object, listener) { + var features = object.features, i = -1, n = features.length; + while (++i < n) d3_geo_streamGeometry(features[i].geometry, listener); + } + }; + var d3_geo_streamGeometryType = { + Sphere: function(object, listener) { + listener.sphere(); + }, + Point: function(object, listener) { + object = object.coordinates; + listener.point(object[0], object[1], object[2]); + }, + MultiPoint: function(object, listener) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) object = coordinates[i], listener.point(object[0], object[1], object[2]); + }, + LineString: function(object, listener) { + d3_geo_streamLine(object.coordinates, listener, 0); + }, + MultiLineString: function(object, listener) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) d3_geo_streamLine(coordinates[i], listener, 0); + }, + Polygon: function(object, listener) { + d3_geo_streamPolygon(object.coordinates, listener); + }, + MultiPolygon: function(object, listener) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) d3_geo_streamPolygon(coordinates[i], listener); + }, + GeometryCollection: function(object, listener) { + var geometries = object.geometries, i = -1, n = geometries.length; + while (++i < n) d3_geo_streamGeometry(geometries[i], listener); + } + }; + function d3_geo_streamLine(coordinates, listener, closed) { + var i = -1, n = coordinates.length - closed, coordinate; + listener.lineStart(); + while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1], coordinate[2]); + listener.lineEnd(); + } + function d3_geo_streamPolygon(coordinates, listener) { + var i = -1, n = coordinates.length; + listener.polygonStart(); + while (++i < n) d3_geo_streamLine(coordinates[i], listener, 1); + listener.polygonEnd(); + } + d3.geo.area = function(object) { + d3_geo_areaSum = 0; + d3.geo.stream(object, d3_geo_area); + return d3_geo_areaSum; + }; + var d3_geo_areaSum, d3_geo_areaRingSum = new d3_adder(); + var d3_geo_area = { + sphere: function() { + d3_geo_areaSum += 4 * π; + }, + point: d3_noop, + lineStart: d3_noop, + lineEnd: d3_noop, + polygonStart: function() { + d3_geo_areaRingSum.reset(); + d3_geo_area.lineStart = d3_geo_areaRingStart; + }, + polygonEnd: function() { + var area = 2 * d3_geo_areaRingSum; + d3_geo_areaSum += area < 0 ? 4 * π + area : area; + d3_geo_area.lineStart = d3_geo_area.lineEnd = d3_geo_area.point = d3_noop; + } + }; + function d3_geo_areaRingStart() { + var λ00, φ00, λ0, cosφ0, sinφ0; + d3_geo_area.point = function(λ, φ) { + d3_geo_area.point = nextPoint; + λ0 = (λ00 = λ) * d3_radians, cosφ0 = Math.cos(φ = (φ00 = φ) * d3_radians / 2 + π / 4), + sinφ0 = Math.sin(φ); + }; + function nextPoint(λ, φ) { + λ *= d3_radians; + φ = φ * d3_radians / 2 + π / 4; + var dλ = λ - λ0, sdλ = dλ >= 0 ? 1 : -1, adλ = sdλ * dλ, cosφ = Math.cos(φ), sinφ = Math.sin(φ), k = sinφ0 * sinφ, u = cosφ0 * cosφ + k * Math.cos(adλ), v = k * sdλ * Math.sin(adλ); + d3_geo_areaRingSum.add(Math.atan2(v, u)); + λ0 = λ, cosφ0 = cosφ, sinφ0 = sinφ; + } + d3_geo_area.lineEnd = function() { + nextPoint(λ00, φ00); + }; + } + function d3_geo_cartesian(spherical) { + var λ = spherical[0], φ = spherical[1], cosφ = Math.cos(φ); + return [ cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ) ]; + } + function d3_geo_cartesianDot(a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; + } + function d3_geo_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] ]; + } + function d3_geo_cartesianAdd(a, b) { + a[0] += b[0]; + a[1] += b[1]; + a[2] += b[2]; + } + function d3_geo_cartesianScale(vector, k) { + return [ vector[0] * k, vector[1] * k, vector[2] * k ]; + } + function d3_geo_cartesianNormalize(d) { + var l = Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]); + d[0] /= l; + d[1] /= l; + d[2] /= l; + } + function d3_geo_spherical(cartesian) { + return [ Math.atan2(cartesian[1], cartesian[0]), d3_asin(cartesian[2]) ]; + } + function d3_geo_sphericalEqual(a, b) { + return abs(a[0] - b[0]) < ε && abs(a[1] - b[1]) < ε; + } + d3.geo.bounds = function() { + var λ0, φ0, λ1, φ1, λ_, λ__, φ__, p0, dλSum, ranges, range; + var bound = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: function() { + bound.point = ringPoint; + bound.lineStart = ringStart; + bound.lineEnd = ringEnd; + dλSum = 0; + d3_geo_area.polygonStart(); + }, + polygonEnd: function() { + d3_geo_area.polygonEnd(); + bound.point = point; + bound.lineStart = lineStart; + bound.lineEnd = lineEnd; + if (d3_geo_areaRingSum < 0) λ0 = -(λ1 = 180), φ0 = -(φ1 = 90); else if (dλSum > ε) φ1 = 90; else if (dλSum < -ε) φ0 = -90; + range[0] = λ0, range[1] = λ1; + } + }; + function point(λ, φ) { + ranges.push(range = [ λ0 = λ, λ1 = λ ]); + if (φ < φ0) φ0 = φ; + if (φ > φ1) φ1 = φ; + } + function linePoint(λ, φ) { + var p = d3_geo_cartesian([ λ * d3_radians, φ * d3_radians ]); + if (p0) { + var normal = d3_geo_cartesianCross(p0, p), equatorial = [ normal[1], -normal[0], 0 ], inflection = d3_geo_cartesianCross(equatorial, normal); + d3_geo_cartesianNormalize(inflection); + inflection = d3_geo_spherical(inflection); + var dλ = λ - λ_, s = dλ > 0 ? 1 : -1, λi = inflection[0] * d3_degrees * s, antimeridian = abs(dλ) > 180; + if (antimeridian ^ (s * λ_ < λi && λi < s * λ)) { + var φi = inflection[1] * d3_degrees; + if (φi > φ1) φ1 = φi; + } else if (λi = (λi + 360) % 360 - 180, antimeridian ^ (s * λ_ < λi && λi < s * λ)) { + var φi = -inflection[1] * d3_degrees; + if (φi < φ0) φ0 = φi; + } else { + if (φ < φ0) φ0 = φ; + if (φ > φ1) φ1 = φ; + } + if (antimeridian) { + if (λ < λ_) { + if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ; + } else { + if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ; + } + } else { + if (λ1 >= λ0) { + if (λ < λ0) λ0 = λ; + if (λ > λ1) λ1 = λ; + } else { + if (λ > λ_) { + if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ; + } else { + if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ; + } + } + } + } else { + point(λ, φ); + } + p0 = p, λ_ = λ; + } + function lineStart() { + bound.point = linePoint; + } + function lineEnd() { + range[0] = λ0, range[1] = λ1; + bound.point = point; + p0 = null; + } + function ringPoint(λ, φ) { + if (p0) { + var dλ = λ - λ_; + dλSum += abs(dλ) > 180 ? dλ + (dλ > 0 ? 360 : -360) : dλ; + } else λ__ = λ, φ__ = φ; + d3_geo_area.point(λ, φ); + linePoint(λ, φ); + } + function ringStart() { + d3_geo_area.lineStart(); + } + function ringEnd() { + ringPoint(λ__, φ__); + d3_geo_area.lineEnd(); + if (abs(dλSum) > ε) λ0 = -(λ1 = 180); + range[0] = λ0, range[1] = λ1; + p0 = null; + } + function angle(λ0, λ1) { + return (λ1 -= λ0) < 0 ? λ1 + 360 : λ1; + } + function compareRanges(a, b) { + return a[0] - b[0]; + } + function withinRange(x, range) { + return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x; + } + return function(feature) { + φ1 = λ1 = -(λ0 = φ0 = Infinity); + ranges = []; + d3.geo.stream(feature, bound); + var n = ranges.length; + if (n) { + ranges.sort(compareRanges); + for (var i = 1, a = ranges[0], b, merged = [ a ]; i < n; ++i) { + b = ranges[i]; + if (withinRange(b[0], a) || withinRange(b[1], a)) { + 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); + } + } + var best = -Infinity, dλ; + for (var n = merged.length - 1, i = 0, a = merged[n], b; i <= n; a = b, ++i) { + b = merged[i]; + if ((dλ = angle(a[1], b[0])) > best) best = dλ, λ0 = b[0], λ1 = a[1]; + } + } + ranges = range = null; + return λ0 === Infinity || φ0 === Infinity ? [ [ NaN, NaN ], [ NaN, NaN ] ] : [ [ λ0, φ0 ], [ λ1, φ1 ] ]; + }; + }(); + d3.geo.centroid = function(object) { + d3_geo_centroidW0 = d3_geo_centroidW1 = d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0; + d3.geo.stream(object, d3_geo_centroid); + var x = d3_geo_centroidX2, y = d3_geo_centroidY2, z = d3_geo_centroidZ2, m = x * x + y * y + z * z; + if (m < ε2) { + x = d3_geo_centroidX1, y = d3_geo_centroidY1, z = d3_geo_centroidZ1; + if (d3_geo_centroidW1 < ε) x = d3_geo_centroidX0, y = d3_geo_centroidY0, z = d3_geo_centroidZ0; + m = x * x + y * y + z * z; + if (m < ε2) return [ NaN, NaN ]; + } + return [ Math.atan2(y, x) * d3_degrees, d3_asin(z / Math.sqrt(m)) * d3_degrees ]; + }; + var d3_geo_centroidW0, d3_geo_centroidW1, d3_geo_centroidX0, d3_geo_centroidY0, d3_geo_centroidZ0, d3_geo_centroidX1, d3_geo_centroidY1, d3_geo_centroidZ1, d3_geo_centroidX2, d3_geo_centroidY2, d3_geo_centroidZ2; + var d3_geo_centroid = { + sphere: d3_noop, + point: d3_geo_centroidPoint, + lineStart: d3_geo_centroidLineStart, + lineEnd: d3_geo_centroidLineEnd, + polygonStart: function() { + d3_geo_centroid.lineStart = d3_geo_centroidRingStart; + }, + polygonEnd: function() { + d3_geo_centroid.lineStart = d3_geo_centroidLineStart; + } + }; + function d3_geo_centroidPoint(λ, φ) { + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians); + d3_geo_centroidPointXYZ(cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ)); + } + function d3_geo_centroidPointXYZ(x, y, z) { + ++d3_geo_centroidW0; + d3_geo_centroidX0 += (x - d3_geo_centroidX0) / d3_geo_centroidW0; + d3_geo_centroidY0 += (y - d3_geo_centroidY0) / d3_geo_centroidW0; + d3_geo_centroidZ0 += (z - d3_geo_centroidZ0) / d3_geo_centroidW0; + } + function d3_geo_centroidLineStart() { + var x0, y0, z0; + d3_geo_centroid.point = function(λ, φ) { + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians); + x0 = cosφ * Math.cos(λ); + y0 = cosφ * Math.sin(λ); + z0 = Math.sin(φ); + d3_geo_centroid.point = nextPoint; + d3_geo_centroidPointXYZ(x0, y0, z0); + }; + function nextPoint(λ, φ) { + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), w = Math.atan2(Math.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); + d3_geo_centroidW1 += w; + d3_geo_centroidX1 += w * (x0 + (x0 = x)); + d3_geo_centroidY1 += w * (y0 + (y0 = y)); + d3_geo_centroidZ1 += w * (z0 + (z0 = z)); + d3_geo_centroidPointXYZ(x0, y0, z0); + } + } + function d3_geo_centroidLineEnd() { + d3_geo_centroid.point = d3_geo_centroidPoint; + } + function d3_geo_centroidRingStart() { + var λ00, φ00, x0, y0, z0; + d3_geo_centroid.point = function(λ, φ) { + λ00 = λ, φ00 = φ; + d3_geo_centroid.point = nextPoint; + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians); + x0 = cosφ * Math.cos(λ); + y0 = cosφ * Math.sin(λ); + z0 = Math.sin(φ); + d3_geo_centroidPointXYZ(x0, y0, z0); + }; + d3_geo_centroid.lineEnd = function() { + nextPoint(λ00, φ00); + d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd; + d3_geo_centroid.point = d3_geo_centroidPoint; + }; + function nextPoint(λ, φ) { + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), cx = y0 * z - z0 * y, cy = z0 * x - x0 * z, cz = x0 * y - y0 * x, m = Math.sqrt(cx * cx + cy * cy + cz * cz), u = x0 * x + y0 * y + z0 * z, v = m && -d3_acos(u) / m, w = Math.atan2(m, u); + d3_geo_centroidX2 += v * cx; + d3_geo_centroidY2 += v * cy; + d3_geo_centroidZ2 += v * cz; + d3_geo_centroidW1 += w; + d3_geo_centroidX1 += w * (x0 + (x0 = x)); + d3_geo_centroidY1 += w * (y0 + (y0 = y)); + d3_geo_centroidZ1 += w * (z0 + (z0 = z)); + d3_geo_centroidPointXYZ(x0, y0, z0); + } + } + function d3_geo_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 d3_true() { + return true; + } + function d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener) { + var subject = [], clip = []; + segments.forEach(function(segment) { + if ((n = segment.length - 1) <= 0) return; + var n, p0 = segment[0], p1 = segment[n]; + if (d3_geo_sphericalEqual(p0, p1)) { + listener.lineStart(); + for (var i = 0; i < n; ++i) listener.point((p0 = segment[i])[0], p0[1]); + listener.lineEnd(); + return; + } + var a = new d3_geo_clipPolygonIntersection(p0, segment, null, true), b = new d3_geo_clipPolygonIntersection(p0, null, a, false); + a.o = b; + subject.push(a); + clip.push(b); + a = new d3_geo_clipPolygonIntersection(p1, segment, null, false); + b = new d3_geo_clipPolygonIntersection(p1, null, a, true); + a.o = b; + subject.push(a); + clip.push(b); + }); + clip.sort(compare); + d3_geo_clipPolygonLinkCircular(subject); + d3_geo_clipPolygonLinkCircular(clip); + if (!subject.length) return; + for (var i = 0, entry = clipStartInside, n = clip.length; i < n; ++i) { + clip[i].e = entry = !entry; + } + var start = subject[0], points, point; + while (1) { + var current = start, isSubject = true; + while (current.v) if ((current = current.n) === start) return; + points = current.z; + listener.lineStart(); + do { + current.v = current.o.v = true; + if (current.e) { + if (isSubject) { + for (var i = 0, n = points.length; i < n; ++i) listener.point((point = points[i])[0], point[1]); + } else { + interpolate(current.x, current.n.x, 1, listener); + } + current = current.n; + } else { + if (isSubject) { + points = current.p.z; + for (var i = points.length - 1; i >= 0; --i) listener.point((point = points[i])[0], point[1]); + } else { + interpolate(current.x, current.p.x, -1, listener); + } + current = current.p; + } + current = current.o; + points = current.z; + isSubject = !isSubject; + } while (!current.v); + listener.lineEnd(); + } + } + function d3_geo_clipPolygonLinkCircular(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; + } + function d3_geo_clipPolygonIntersection(point, points, other, entry) { + this.x = point; + this.z = points; + this.o = other; + this.e = entry; + this.v = false; + this.n = this.p = null; + } + function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart) { + return function(rotate, listener) { + var line = clipLine(listener), rotatedClipStart = rotate.invert(clipStart[0], clipStart[1]); + 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 = d3.merge(segments); + var clipStartInside = d3_geo_pointInPolygon(rotatedClipStart, polygon); + if (segments.length) { + if (!polygonStarted) listener.polygonStart(), polygonStarted = true; + d3_geo_clipPolygon(segments, d3_geo_clipSort, clipStartInside, interpolate, listener); + } else if (clipStartInside) { + if (!polygonStarted) listener.polygonStart(), polygonStarted = true; + listener.lineStart(); + interpolate(null, null, 1, listener); + listener.lineEnd(); + } + if (polygonStarted) listener.polygonEnd(), polygonStarted = false; + segments = polygon = null; + }, + sphere: function() { + listener.polygonStart(); + listener.lineStart(); + interpolate(null, null, 1, listener); + listener.lineEnd(); + listener.polygonEnd(); + } + }; + function point(λ, φ) { + var point = rotate(λ, φ); + if (pointVisible(λ = point[0], φ = point[1])) listener.point(λ, φ); + } + function pointLine(λ, φ) { + var point = rotate(λ, φ); + line.point(point[0], point[1]); + } + function lineStart() { + clip.point = pointLine; + line.lineStart(); + } + function lineEnd() { + clip.point = point; + line.lineEnd(); + } + var segments; + var buffer = d3_geo_clipBufferListener(), ringListener = clipLine(buffer), polygonStarted = false, polygon, ring; + function pointRing(λ, φ) { + ring.push([ λ, φ ]); + var point = rotate(λ, φ); + ringListener.point(point[0], point[1]); + } + function ringStart() { + ringListener.lineStart(); + ring = []; + } + function ringEnd() { + pointRing(ring[0][0], ring[0][1]); + ringListener.lineEnd(); + var clean = ringListener.clean(), ringSegments = buffer.buffer(), segment, n = ringSegments.length; + ring.pop(); + polygon.push(ring); + ring = null; + if (!n) return; + if (clean & 1) { + segment = ringSegments[0]; + var n = segment.length - 1, i = -1, point; + if (n > 0) { + if (!polygonStarted) listener.polygonStart(), polygonStarted = true; + listener.lineStart(); + while (++i < n) listener.point((point = segment[i])[0], point[1]); + listener.lineEnd(); + } + return; + } + if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift())); + segments.push(ringSegments.filter(d3_geo_clipSegmentLength1)); + } + return clip; + }; + } + function d3_geo_clipSegmentLength1(segment) { + return segment.length > 1; + } + function d3_geo_clipBufferListener() { + var lines = [], line; + return { + lineStart: function() { + lines.push(line = []); + }, + point: function(λ, φ) { + line.push([ λ, φ ]); + }, + lineEnd: d3_noop, + buffer: function() { + var buffer = lines; + lines = []; + line = null; + return buffer; + }, + rejoin: function() { + if (lines.length > 1) lines.push(lines.pop().concat(lines.shift())); + } + }; + } + function d3_geo_clipSort(a, b) { + return ((a = a.x)[0] < 0 ? a[1] - halfπ - ε : halfπ - a[1]) - ((b = b.x)[0] < 0 ? b[1] - halfπ - ε : halfπ - b[1]); + } + var d3_geo_clipAntimeridian = d3_geo_clip(d3_true, d3_geo_clipAntimeridianLine, d3_geo_clipAntimeridianInterpolate, [ -π, -π / 2 ]); + function d3_geo_clipAntimeridianLine(listener) { + var λ0 = NaN, φ0 = NaN, sλ0 = NaN, clean; + return { + lineStart: function() { + listener.lineStart(); + clean = 1; + }, + point: function(λ1, φ1) { + var sλ1 = λ1 > 0 ? π : -π, dλ = abs(λ1 - λ0); + if (abs(dλ - π) < ε) { + listener.point(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? halfπ : -halfπ); + listener.point(sλ0, φ0); + listener.lineEnd(); + listener.lineStart(); + listener.point(sλ1, φ0); + listener.point(λ1, φ0); + clean = 0; + } else if (sλ0 !== sλ1 && dλ >= π) { + if (abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε; + if (abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε; + φ0 = d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1); + listener.point(sλ0, φ0); + listener.lineEnd(); + listener.lineStart(); + listener.point(sλ1, φ0); + clean = 0; + } + listener.point(λ0 = λ1, φ0 = φ1); + sλ0 = sλ1; + }, + lineEnd: function() { + listener.lineEnd(); + λ0 = φ0 = NaN; + }, + clean: function() { + return 2 - clean; + } + }; + } + function d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1) { + var cosφ0, cosφ1, sinλ0_λ1 = Math.sin(λ0 - λ1); + return abs(sinλ0_λ1) > ε ? Math.atan((Math.sin(φ0) * (cosφ1 = Math.cos(φ1)) * Math.sin(λ1) - Math.sin(φ1) * (cosφ0 = Math.cos(φ0)) * Math.sin(λ0)) / (cosφ0 * cosφ1 * sinλ0_λ1)) : (φ0 + φ1) / 2; + } + function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) { + var φ; + if (from == null) { + φ = direction * halfπ; + listener.point(-π, φ); + listener.point(0, φ); + listener.point(π, φ); + listener.point(π, 0); + listener.point(π, -φ); + listener.point(0, -φ); + listener.point(-π, -φ); + listener.point(-π, 0); + listener.point(-π, φ); + } else if (abs(from[0] - to[0]) > ε) { + var s = from[0] < to[0] ? π : -π; + φ = direction * s / 2; + listener.point(-s, φ); + listener.point(0, φ); + listener.point(s, φ); + } else { + listener.point(to[0], to[1]); + } + } + function d3_geo_pointInPolygon(point, polygon) { + var meridian = point[0], parallel = point[1], meridianNormal = [ Math.sin(meridian), -Math.cos(meridian), 0 ], polarAngle = 0, winding = 0; + d3_geo_areaRingSum.reset(); + for (var i = 0, n = polygon.length; i < n; ++i) { + var ring = polygon[i], m = ring.length; + if (!m) continue; + var point0 = ring[0], λ0 = point0[0], φ0 = point0[1] / 2 + π / 4, sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), j = 1; + while (true) { + if (j === m) j = 0; + point = ring[j]; + var λ = point[0], φ = point[1] / 2 + π / 4, sinφ = Math.sin(φ), cosφ = Math.cos(φ), dλ = λ - λ0, sdλ = dλ >= 0 ? 1 : -1, adλ = sdλ * dλ, antimeridian = adλ > π, k = sinφ0 * sinφ; + d3_geo_areaRingSum.add(Math.atan2(k * sdλ * Math.sin(adλ), cosφ0 * cosφ + k * Math.cos(adλ))); + polarAngle += antimeridian ? dλ + sdλ * τ : dλ; + if (antimeridian ^ λ0 >= meridian ^ λ >= meridian) { + var arc = d3_geo_cartesianCross(d3_geo_cartesian(point0), d3_geo_cartesian(point)); + d3_geo_cartesianNormalize(arc); + var intersection = d3_geo_cartesianCross(meridianNormal, arc); + d3_geo_cartesianNormalize(intersection); + var φarc = (antimeridian ^ dλ >= 0 ? -1 : 1) * d3_asin(intersection[2]); + if (parallel > φarc || parallel === φarc && (arc[0] || arc[1])) { + winding += antimeridian ^ dλ >= 0 ? 1 : -1; + } + } + if (!j++) break; + λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ, point0 = point; + } + } + return (polarAngle < -ε || polarAngle < ε && d3_geo_areaRingSum < 0) ^ winding & 1; + } + function d3_geo_clipCircle(radius) { + var cr = Math.cos(radius), smallRadius = cr > 0, notHemisphere = abs(cr) > ε, interpolate = d3_geo_circleInterpolate(radius, 6 * d3_radians); + return d3_geo_clip(visible, clipLine, interpolate, smallRadius ? [ 0, -radius ] : [ -π, radius - π ]); + function visible(λ, φ) { + return Math.cos(λ) * Math.cos(φ) > cr; + } + function clipLine(listener) { + var point0, c0, v0, v00, clean; + return { + lineStart: function() { + v00 = v0 = false; + clean = 1; + }, + point: function(λ, φ) { + var point1 = [ λ, φ ], point2, v = visible(λ, φ), c = smallRadius ? v ? 0 : code(λ, φ) : v ? code(λ + (λ < 0 ? π : -π), φ) : 0; + if (!point0 && (v00 = v0 = v)) listener.lineStart(); + if (v !== v0) { + point2 = intersect(point0, point1); + if (d3_geo_sphericalEqual(point0, point2) || d3_geo_sphericalEqual(point1, point2)) { + point1[0] += ε; + point1[1] += ε; + v = visible(point1[0], point1[1]); + } + } + if (v !== v0) { + clean = 0; + if (v) { + listener.lineStart(); + point2 = intersect(point1, point0); + listener.point(point2[0], point2[1]); + } else { + point2 = intersect(point0, point1); + listener.point(point2[0], point2[1]); + listener.lineEnd(); + } + point0 = point2; + } else if (notHemisphere && point0 && smallRadius ^ v) { + var t; + if (!(c & c0) && (t = intersect(point1, point0, true))) { + clean = 0; + if (smallRadius) { + listener.lineStart(); + listener.point(t[0][0], t[0][1]); + listener.point(t[1][0], t[1][1]); + listener.lineEnd(); + } else { + listener.point(t[1][0], t[1][1]); + listener.lineEnd(); + listener.lineStart(); + listener.point(t[0][0], t[0][1]); + } + } + } + if (v && (!point0 || !d3_geo_sphericalEqual(point0, point1))) { + listener.point(point1[0], point1[1]); + } + point0 = point1, v0 = v, c0 = c; + }, + lineEnd: function() { + if (v0) listener.lineEnd(); + point0 = null; + }, + clean: function() { + return clean | (v00 && v0) << 1; + } + }; + } + function intersect(a, b, two) { + var pa = d3_geo_cartesian(a), pb = d3_geo_cartesian(b); + var n1 = [ 1, 0, 0 ], n2 = d3_geo_cartesianCross(pa, pb), n2n2 = d3_geo_cartesianDot(n2, n2), n1n2 = n2[0], determinant = n2n2 - n1n2 * n1n2; + if (!determinant) return !two && a; + var c1 = cr * n2n2 / determinant, c2 = -cr * n1n2 / determinant, n1xn2 = d3_geo_cartesianCross(n1, n2), A = d3_geo_cartesianScale(n1, c1), B = d3_geo_cartesianScale(n2, c2); + d3_geo_cartesianAdd(A, B); + var u = n1xn2, w = d3_geo_cartesianDot(A, u), uu = d3_geo_cartesianDot(u, u), t2 = w * w - uu * (d3_geo_cartesianDot(A, A) - 1); + if (t2 < 0) return; + var t = Math.sqrt(t2), q = d3_geo_cartesianScale(u, (-w - t) / uu); + d3_geo_cartesianAdd(q, A); + q = d3_geo_spherical(q); + if (!two) return q; + var λ0 = a[0], λ1 = b[0], φ0 = a[1], φ1 = b[1], z; + if (λ1 < λ0) z = λ0, λ0 = λ1, λ1 = z; + var δλ = λ1 - λ0, polar = abs(δλ - π) < ε, meridian = polar || δλ < ε; + if (!polar && φ1 < φ0) z = φ0, φ0 = φ1, φ1 = z; + if (meridian ? polar ? φ0 + φ1 > 0 ^ q[1] < (abs(q[0] - λ0) < ε ? φ0 : φ1) : φ0 <= q[1] && q[1] <= φ1 : δλ > π ^ (λ0 <= q[0] && q[0] <= λ1)) { + var q1 = d3_geo_cartesianScale(u, (-w + t) / uu); + d3_geo_cartesianAdd(q1, A); + return [ q, d3_geo_spherical(q1) ]; + } + } + function code(λ, φ) { + var r = smallRadius ? radius : π - radius, code = 0; + if (λ < -r) code |= 1; else if (λ > r) code |= 2; + if (φ < -r) code |= 4; else if (φ > r) code |= 8; + return code; + } + } + function d3_geom_clipLine(x0, y0, x1, y1) { + return function(line) { + var a = line.a, b = line.b, ax = a.x, ay = a.y, bx = b.x, by = b.y, 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) line.a = { + x: ax + t0 * dx, + y: ay + t0 * dy + }; + if (t1 < 1) line.b = { + x: ax + t1 * dx, + y: ay + t1 * dy + }; + return line; + }; + } + var d3_geo_clipExtentMAX = 1e9; + d3.geo.clipExtent = function() { + var x0, y0, x1, y1, stream, clip, clipExtent = { + stream: function(output) { + if (stream) stream.valid = false; + stream = clip(output); + stream.valid = true; + return stream; + }, + extent: function(_) { + if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ]; + clip = d3_geo_clipExtent(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]); + if (stream) stream.valid = false, stream = null; + return clipExtent; + } + }; + return clipExtent.extent([ [ 0, 0 ], [ 960, 500 ] ]); + }; + function d3_geo_clipExtent(x0, y0, x1, y1) { + return function(listener) { + var listener_ = listener, bufferListener = d3_geo_clipBufferListener(), clipLine = d3_geom_clipLine(x0, y0, x1, y1), segments, polygon, ring; + var clip = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: function() { + listener = bufferListener; + segments = []; + polygon = []; + clean = true; + }, + polygonEnd: function() { + listener = listener_; + segments = d3.merge(segments); + var clipStartInside = insidePolygon([ x0, y1 ]), inside = clean && clipStartInside, visible = segments.length; + if (inside || visible) { + listener.polygonStart(); + if (inside) { + listener.lineStart(); + interpolate(null, null, 1, listener); + listener.lineEnd(); + } + if (visible) { + d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener); + } + listener.polygonEnd(); + } + segments = polygon = ring = null; + } + }; + function insidePolygon(p) { + var wn = 0, n = polygon.length, y = p[1]; + for (var i = 0; i < n; ++i) { + for (var j = 1, v = polygon[i], m = v.length, a = v[0], b; j < m; ++j) { + b = v[j]; + if (a[1] <= y) { + if (b[1] > y && d3_cross2d(a, b, p) > 0) ++wn; + } else { + if (b[1] <= y && d3_cross2d(a, b, p) < 0) --wn; + } + a = b; + } + } + return wn !== 0; + } + function interpolate(from, to, direction, listener) { + var a = 0, a1 = 0; + if (from == null || (a = corner(from, direction)) !== (a1 = corner(to, direction)) || comparePoints(from, to) < 0 ^ direction > 0) { + do { + listener.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0); + } while ((a = (a + direction + 4) % 4) !== a1); + } else { + listener.point(to[0], to[1]); + } + } + function pointVisible(x, y) { + return x0 <= x && x <= x1 && y0 <= y && y <= y1; + } + function point(x, y) { + if (pointVisible(x, y)) listener.point(x, y); + } + var x__, y__, v__, x_, y_, v_, first, clean; + function lineStart() { + clip.point = linePoint; + if (polygon) polygon.push(ring = []); + first = true; + v_ = false; + x_ = y_ = NaN; + } + function lineEnd() { + if (segments) { + linePoint(x__, y__); + if (v__ && v_) bufferListener.rejoin(); + segments.push(bufferListener.buffer()); + } + clip.point = point; + if (v_) listener.lineEnd(); + } + function linePoint(x, y) { + x = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, x)); + y = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, y)); + var v = pointVisible(x, y); + if (polygon) ring.push([ x, y ]); + if (first) { + x__ = x, y__ = y, v__ = v; + first = false; + if (v) { + listener.lineStart(); + listener.point(x, y); + } + } else { + if (v && v_) listener.point(x, y); else { + var l = { + a: { + x: x_, + y: y_ + }, + b: { + x: x, + y: y + } + }; + if (clipLine(l)) { + if (!v_) { + listener.lineStart(); + listener.point(l.a.x, l.a.y); + } + listener.point(l.b.x, l.b.y); + if (!v) listener.lineEnd(); + clean = false; + } else if (v) { + listener.lineStart(); + listener.point(x, y); + clean = false; + } + } + } + x_ = x, y_ = y, v_ = v; + } + return clip; + }; + function corner(p, direction) { + return abs(p[0] - x0) < ε ? direction > 0 ? 0 : 3 : abs(p[0] - x1) < ε ? direction > 0 ? 2 : 1 : abs(p[1] - y0) < ε ? direction > 0 ? 1 : 0 : direction > 0 ? 3 : 2; + } + function compare(a, b) { + return comparePoints(a.x, b.x); + } + function comparePoints(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]; + } + } + function d3_geo_conic(projectAt) { + var φ0 = 0, φ1 = π / 3, m = d3_geo_projectionMutator(projectAt), p = m(φ0, φ1); + p.parallels = function(_) { + if (!arguments.length) return [ φ0 / π * 180, φ1 / π * 180 ]; + return m(φ0 = _[0] * π / 180, φ1 = _[1] * π / 180); + }; + return p; + } + function d3_geo_conicEqualArea(φ0, φ1) { + var sinφ0 = Math.sin(φ0), n = (sinφ0 + Math.sin(φ1)) / 2, C = 1 + sinφ0 * (2 * n - sinφ0), ρ0 = Math.sqrt(C) / n; + function forward(λ, φ) { + var ρ = Math.sqrt(C - 2 * n * Math.sin(φ)) / n; + return [ ρ * Math.sin(λ *= n), ρ0 - ρ * Math.cos(λ) ]; + } + forward.invert = function(x, y) { + var ρ0_y = ρ0 - y; + return [ Math.atan2(x, ρ0_y) / n, d3_asin((C - (x * x + ρ0_y * ρ0_y) * n * n) / (2 * n)) ]; + }; + return forward; + } + (d3.geo.conicEqualArea = function() { + return d3_geo_conic(d3_geo_conicEqualArea); + }).raw = d3_geo_conicEqualArea; + d3.geo.albers = function() { + return d3.geo.conicEqualArea().rotate([ 96, 0 ]).center([ -.6, 38.7 ]).parallels([ 29.5, 45.5 ]).scale(1070); + }; + d3.geo.albersUsa = function() { + var lower48 = d3.geo.albers(); + var alaska = d3.geo.conicEqualArea().rotate([ 154, 0 ]).center([ -2, 58.5 ]).parallels([ 55, 65 ]); + var hawaii = d3.geo.conicEqualArea().rotate([ 157, 0 ]).center([ -3, 19.9 ]).parallels([ 8, 18 ]); + var point, pointStream = { + point: function(x, y) { + point = [ x, y ]; + } + }, lower48Point, alaskaPoint, hawaiiPoint; + function albersUsa(coordinates) { + var x = coordinates[0], y = coordinates[1]; + point = null; + (lower48Point(x, y), point) || (alaskaPoint(x, y), point) || hawaiiPoint(x, y); + return 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 >= .12 && y < .234 && x >= -.425 && x < -.214 ? alaska : y >= .166 && y < .234 && x >= -.214 && x < -.115 ? hawaii : lower48).invert(coordinates); + }; + albersUsa.stream = function(stream) { + var lower48Stream = lower48.stream(stream), alaskaStream = alaska.stream(stream), hawaiiStream = hawaii.stream(stream); + return { + point: function(x, y) { + lower48Stream.point(x, y); + alaskaStream.point(x, y); + hawaiiStream.point(x, y); + }, + sphere: function() { + lower48Stream.sphere(); + alaskaStream.sphere(); + hawaiiStream.sphere(); + }, + lineStart: function() { + lower48Stream.lineStart(); + alaskaStream.lineStart(); + hawaiiStream.lineStart(); + }, + lineEnd: function() { + lower48Stream.lineEnd(); + alaskaStream.lineEnd(); + hawaiiStream.lineEnd(); + }, + polygonStart: function() { + lower48Stream.polygonStart(); + alaskaStream.polygonStart(); + hawaiiStream.polygonStart(); + }, + polygonEnd: function() { + lower48Stream.polygonEnd(); + alaskaStream.polygonEnd(); + hawaiiStream.polygonEnd(); + } + }; + }; + albersUsa.precision = function(_) { + if (!arguments.length) return lower48.precision(); + lower48.precision(_); + alaska.precision(_); + hawaii.precision(_); + return albersUsa; + }; + albersUsa.scale = function(_) { + if (!arguments.length) return lower48.scale(); + lower48.scale(_); + alaska.scale(_ * .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 - .455 * k, y - .238 * k ], [ x + .455 * k, y + .238 * k ] ]).stream(pointStream).point; + alaskaPoint = alaska.translate([ x - .307 * k, y + .201 * k ]).clipExtent([ [ x - .425 * k + ε, y + .12 * k + ε ], [ x - .214 * k - ε, y + .234 * k - ε ] ]).stream(pointStream).point; + hawaiiPoint = hawaii.translate([ x - .205 * k, y + .212 * k ]).clipExtent([ [ x - .214 * k + ε, y + .166 * k + ε ], [ x - .115 * k - ε, y + .234 * k - ε ] ]).stream(pointStream).point; + return albersUsa; + }; + return albersUsa.scale(1070); + }; + var d3_geo_pathAreaSum, d3_geo_pathAreaPolygon, d3_geo_pathArea = { + point: d3_noop, + lineStart: d3_noop, + lineEnd: d3_noop, + polygonStart: function() { + d3_geo_pathAreaPolygon = 0; + d3_geo_pathArea.lineStart = d3_geo_pathAreaRingStart; + }, + polygonEnd: function() { + d3_geo_pathArea.lineStart = d3_geo_pathArea.lineEnd = d3_geo_pathArea.point = d3_noop; + d3_geo_pathAreaSum += abs(d3_geo_pathAreaPolygon / 2); + } + }; + function d3_geo_pathAreaRingStart() { + var x00, y00, x0, y0; + d3_geo_pathArea.point = function(x, y) { + d3_geo_pathArea.point = nextPoint; + x00 = x0 = x, y00 = y0 = y; + }; + function nextPoint(x, y) { + d3_geo_pathAreaPolygon += y0 * x - x0 * y; + x0 = x, y0 = y; + } + d3_geo_pathArea.lineEnd = function() { + nextPoint(x00, y00); + }; + } + var d3_geo_pathBoundsX0, d3_geo_pathBoundsY0, d3_geo_pathBoundsX1, d3_geo_pathBoundsY1; + var d3_geo_pathBounds = { + point: d3_geo_pathBoundsPoint, + lineStart: d3_noop, + lineEnd: d3_noop, + polygonStart: d3_noop, + polygonEnd: d3_noop + }; + function d3_geo_pathBoundsPoint(x, y) { + if (x < d3_geo_pathBoundsX0) d3_geo_pathBoundsX0 = x; + if (x > d3_geo_pathBoundsX1) d3_geo_pathBoundsX1 = x; + if (y < d3_geo_pathBoundsY0) d3_geo_pathBoundsY0 = y; + if (y > d3_geo_pathBoundsY1) d3_geo_pathBoundsY1 = y; + } + function d3_geo_pathBuffer() { + var pointCircle = d3_geo_pathBufferCircle(4.5), buffer = []; + var stream = { + point: point, + lineStart: function() { + stream.point = pointLineStart; + }, + lineEnd: lineEnd, + polygonStart: function() { + stream.lineEnd = lineEndPolygon; + }, + polygonEnd: function() { + stream.lineEnd = lineEnd; + stream.point = point; + }, + pointRadius: function(_) { + pointCircle = d3_geo_pathBufferCircle(_); + return stream; + }, + result: function() { + if (buffer.length) { + var result = buffer.join(""); + buffer = []; + return result; + } + } + }; + function point(x, y) { + buffer.push("M", x, ",", y, pointCircle); + } + function pointLineStart(x, y) { + buffer.push("M", x, ",", y); + stream.point = pointLine; + } + function pointLine(x, y) { + buffer.push("L", x, ",", y); + } + function lineEnd() { + stream.point = point; + } + function lineEndPolygon() { + buffer.push("Z"); + } + return stream; + } + function d3_geo_pathBufferCircle(radius) { + return "m0," + radius + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius + "z"; + } + var d3_geo_pathCentroid = { + point: d3_geo_pathCentroidPoint, + lineStart: d3_geo_pathCentroidLineStart, + lineEnd: d3_geo_pathCentroidLineEnd, + polygonStart: function() { + d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidRingStart; + }, + polygonEnd: function() { + d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint; + d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidLineStart; + d3_geo_pathCentroid.lineEnd = d3_geo_pathCentroidLineEnd; + } + }; + function d3_geo_pathCentroidPoint(x, y) { + d3_geo_centroidX0 += x; + d3_geo_centroidY0 += y; + ++d3_geo_centroidZ0; + } + function d3_geo_pathCentroidLineStart() { + var x0, y0; + d3_geo_pathCentroid.point = function(x, y) { + d3_geo_pathCentroid.point = nextPoint; + d3_geo_pathCentroidPoint(x0 = x, y0 = y); + }; + function nextPoint(x, y) { + var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy); + d3_geo_centroidX1 += z * (x0 + x) / 2; + d3_geo_centroidY1 += z * (y0 + y) / 2; + d3_geo_centroidZ1 += z; + d3_geo_pathCentroidPoint(x0 = x, y0 = y); + } + } + function d3_geo_pathCentroidLineEnd() { + d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint; + } + function d3_geo_pathCentroidRingStart() { + var x00, y00, x0, y0; + d3_geo_pathCentroid.point = function(x, y) { + d3_geo_pathCentroid.point = nextPoint; + d3_geo_pathCentroidPoint(x00 = x0 = x, y00 = y0 = y); + }; + function nextPoint(x, y) { + var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy); + d3_geo_centroidX1 += z * (x0 + x) / 2; + d3_geo_centroidY1 += z * (y0 + y) / 2; + d3_geo_centroidZ1 += z; + z = y0 * x - x0 * y; + d3_geo_centroidX2 += z * (x0 + x); + d3_geo_centroidY2 += z * (y0 + y); + d3_geo_centroidZ2 += z * 3; + d3_geo_pathCentroidPoint(x0 = x, y0 = y); + } + d3_geo_pathCentroid.lineEnd = function() { + nextPoint(x00, y00); + }; + } + function d3_geo_pathContext(context) { + var pointRadius = 4.5; + var stream = { + point: point, + lineStart: function() { + stream.point = pointLineStart; + }, + lineEnd: lineEnd, + polygonStart: function() { + stream.lineEnd = lineEndPolygon; + }, + polygonEnd: function() { + stream.lineEnd = lineEnd; + stream.point = point; + }, + pointRadius: function(_) { + pointRadius = _; + return stream; + }, + result: d3_noop + }; + function point(x, y) { + context.moveTo(x + pointRadius, y); + context.arc(x, y, pointRadius, 0, τ); + } + function pointLineStart(x, y) { + context.moveTo(x, y); + stream.point = pointLine; + } + function pointLine(x, y) { + context.lineTo(x, y); + } + function lineEnd() { + stream.point = point; + } + function lineEndPolygon() { + context.closePath(); + } + return stream; + } + function d3_geo_resample(project) { + var δ2 = .5, cosMinDistance = Math.cos(30 * d3_radians), maxDepth = 16; + function resample(stream) { + return (maxDepth ? resampleRecursive : resampleNone)(stream); + } + function resampleNone(stream) { + return d3_geo_transformPoint(stream, function(x, y) { + x = project(x, y); + stream.point(x[0], x[1]); + }); + } + function resampleRecursive(stream) { + var λ00, φ00, x00, y00, a00, b00, c00, λ0, x0, y0, a0, b0, c0; + var resample = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: function() { + stream.polygonStart(); + resample.lineStart = ringStart; + }, + polygonEnd: function() { + stream.polygonEnd(); + resample.lineStart = lineStart; + } + }; + function point(x, y) { + x = project(x, y); + stream.point(x[0], x[1]); + } + function lineStart() { + x0 = NaN; + resample.point = linePoint; + stream.lineStart(); + } + function linePoint(λ, φ) { + var c = d3_geo_cartesian([ λ, φ ]), p = project(λ, φ); + resampleLineTo(x0, y0, λ0, a0, b0, c0, x0 = p[0], y0 = p[1], λ0 = λ, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream); + stream.point(x0, y0); + } + function lineEnd() { + resample.point = point; + stream.lineEnd(); + } + function ringStart() { + lineStart(); + resample.point = ringPoint; + resample.lineEnd = ringEnd; + } + function ringPoint(λ, φ) { + linePoint(λ00 = λ, φ00 = φ), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0; + resample.point = linePoint; + } + function ringEnd() { + resampleLineTo(x0, y0, λ0, a0, b0, c0, x00, y00, λ00, a00, b00, c00, maxDepth, stream); + resample.lineEnd = lineEnd; + lineEnd(); + } + return resample; + } + function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, stream) { + var dx = x1 - x0, dy = y1 - y0, d2 = dx * dx + dy * dy; + if (d2 > 4 * δ2 && depth--) { + var a = a0 + a1, b = b0 + b1, c = c0 + c1, m = Math.sqrt(a * a + b * b + c * c), φ2 = Math.asin(c /= m), λ2 = abs(abs(c) - 1) < ε || abs(λ0 - λ1) < ε ? (λ0 + λ1) / 2 : Math.atan2(b, a), p = project(λ2, φ2), x2 = p[0], y2 = p[1], dx2 = x2 - x0, dy2 = y2 - y0, dz = dy * dx2 - dx * dy2; + if (dz * dz / d2 > δ2 || abs((dx * dx2 + dy * dy2) / d2 - .5) > .3 || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) { + resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, stream); + stream.point(x2, y2); + resampleLineTo(x2, y2, λ2, a, b, c, x1, y1, λ1, a1, b1, c1, depth, stream); + } + } + } + resample.precision = function(_) { + if (!arguments.length) return Math.sqrt(δ2); + maxDepth = (δ2 = _ * _) > 0 && 16; + return resample; + }; + return resample; + } + d3.geo.path = function() { + var pointRadius = 4.5, projection, context, projectStream, contextStream, cacheStream; + function path(object) { + if (object) { + if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments)); + if (!cacheStream || !cacheStream.valid) cacheStream = projectStream(contextStream); + d3.geo.stream(object, cacheStream); + } + return contextStream.result(); + } + path.area = function(object) { + d3_geo_pathAreaSum = 0; + d3.geo.stream(object, projectStream(d3_geo_pathArea)); + return d3_geo_pathAreaSum; + }; + path.centroid = function(object) { + d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0; + d3.geo.stream(object, projectStream(d3_geo_pathCentroid)); + return d3_geo_centroidZ2 ? [ d3_geo_centroidX2 / d3_geo_centroidZ2, d3_geo_centroidY2 / d3_geo_centroidZ2 ] : d3_geo_centroidZ1 ? [ d3_geo_centroidX1 / d3_geo_centroidZ1, d3_geo_centroidY1 / d3_geo_centroidZ1 ] : d3_geo_centroidZ0 ? [ d3_geo_centroidX0 / d3_geo_centroidZ0, d3_geo_centroidY0 / d3_geo_centroidZ0 ] : [ NaN, NaN ]; + }; + path.bounds = function(object) { + d3_geo_pathBoundsX1 = d3_geo_pathBoundsY1 = -(d3_geo_pathBoundsX0 = d3_geo_pathBoundsY0 = Infinity); + d3.geo.stream(object, projectStream(d3_geo_pathBounds)); + return [ [ d3_geo_pathBoundsX0, d3_geo_pathBoundsY0 ], [ d3_geo_pathBoundsX1, d3_geo_pathBoundsY1 ] ]; + }; + path.projection = function(_) { + if (!arguments.length) return projection; + projectStream = (projection = _) ? _.stream || d3_geo_pathProjectStream(_) : d3_identity; + return reset(); + }; + path.context = function(_) { + if (!arguments.length) return context; + contextStream = (context = _) == null ? new d3_geo_pathBuffer() : new d3_geo_pathContext(_); + if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius); + return reset(); + }; + path.pointRadius = function(_) { + if (!arguments.length) return pointRadius; + pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_); + return path; + }; + function reset() { + cacheStream = null; + return path; + } + return path.projection(d3.geo.albersUsa()).context(null); + }; + function d3_geo_pathProjectStream(project) { + var resample = d3_geo_resample(function(x, y) { + return project([ x * d3_degrees, y * d3_degrees ]); + }); + return function(stream) { + return d3_geo_projectionRadians(resample(stream)); + }; + } + d3.geo.transform = function(methods) { + return { + stream: function(stream) { + var transform = new d3_geo_transform(stream); + for (var k in methods) transform[k] = methods[k]; + return transform; + } + }; + }; + function d3_geo_transform(stream) { + this.stream = stream; + } + d3_geo_transform.prototype = { + 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 d3_geo_transformPoint(stream, point) { + return { + point: point, + sphere: function() { + stream.sphere(); + }, + lineStart: function() { + stream.lineStart(); + }, + lineEnd: function() { + stream.lineEnd(); + }, + polygonStart: function() { + stream.polygonStart(); + }, + polygonEnd: function() { + stream.polygonEnd(); + } + }; + } + d3.geo.projection = d3_geo_projection; + d3.geo.projectionMutator = d3_geo_projectionMutator; + function d3_geo_projection(project) { + return d3_geo_projectionMutator(function() { + return project; + })(); + } + function d3_geo_projectionMutator(projectAt) { + var project, rotate, projectRotate, projectResample = d3_geo_resample(function(x, y) { + x = project(x, y); + return [ x[0] * k + δx, δy - x[1] * k ]; + }), k = 150, x = 480, y = 250, λ = 0, φ = 0, δλ = 0, δφ = 0, δγ = 0, δx, δy, preclip = d3_geo_clipAntimeridian, postclip = d3_identity, clipAngle = null, clipExtent = null, stream; + function projection(point) { + point = projectRotate(point[0] * d3_radians, point[1] * d3_radians); + return [ point[0] * k + δx, δy - point[1] * k ]; + } + function invert(point) { + point = projectRotate.invert((point[0] - δx) / k, (δy - point[1]) / k); + return point && [ point[0] * d3_degrees, point[1] * d3_degrees ]; + } + projection.stream = function(output) { + if (stream) stream.valid = false; + stream = d3_geo_projectionRadians(preclip(rotate, projectResample(postclip(output)))); + stream.valid = true; + return stream; + }; + projection.clipAngle = function(_) { + if (!arguments.length) return clipAngle; + preclip = _ == null ? (clipAngle = _, d3_geo_clipAntimeridian) : d3_geo_clipCircle((clipAngle = +_) * d3_radians); + return invalidate(); + }; + projection.clipExtent = function(_) { + if (!arguments.length) return clipExtent; + clipExtent = _; + postclip = _ ? d3_geo_clipExtent(_[0][0], _[0][1], _[1][0], _[1][1]) : d3_identity; + return invalidate(); + }; + projection.scale = function(_) { + if (!arguments.length) return k; + k = +_; + return reset(); + }; + projection.translate = function(_) { + if (!arguments.length) return [ x, y ]; + x = +_[0]; + y = +_[1]; + return reset(); + }; + projection.center = function(_) { + if (!arguments.length) return [ λ * d3_degrees, φ * d3_degrees ]; + λ = _[0] % 360 * d3_radians; + φ = _[1] % 360 * d3_radians; + return reset(); + }; + projection.rotate = function(_) { + if (!arguments.length) return [ δλ * d3_degrees, δφ * d3_degrees, δγ * d3_degrees ]; + δλ = _[0] % 360 * d3_radians; + δφ = _[1] % 360 * d3_radians; + δγ = _.length > 2 ? _[2] % 360 * d3_radians : 0; + return reset(); + }; + d3.rebind(projection, projectResample, "precision"); + function reset() { + projectRotate = d3_geo_compose(rotate = d3_geo_rotation(δλ, δφ, δγ), project); + var center = project(λ, φ); + δx = x - center[0] * k; + δy = y + center[1] * k; + return invalidate(); + } + function invalidate() { + if (stream) stream.valid = false, stream = null; + return projection; + } + return function() { + project = projectAt.apply(this, arguments); + projection.invert = project.invert && invert; + return reset(); + }; + } + function d3_geo_projectionRadians(stream) { + return d3_geo_transformPoint(stream, function(x, y) { + stream.point(x * d3_radians, y * d3_radians); + }); + } + function d3_geo_equirectangular(λ, φ) { + return [ λ, φ ]; + } + (d3.geo.equirectangular = function() { + return d3_geo_projection(d3_geo_equirectangular); + }).raw = d3_geo_equirectangular.invert = d3_geo_equirectangular; + d3.geo.rotation = function(rotate) { + rotate = d3_geo_rotation(rotate[0] % 360 * d3_radians, rotate[1] * d3_radians, rotate.length > 2 ? rotate[2] * d3_radians : 0); + function forward(coordinates) { + coordinates = rotate(coordinates[0] * d3_radians, coordinates[1] * d3_radians); + return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates; + } + forward.invert = function(coordinates) { + coordinates = rotate.invert(coordinates[0] * d3_radians, coordinates[1] * d3_radians); + return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates; + }; + return forward; + }; + function d3_geo_identityRotation(λ, φ) { + return [ λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ ]; + } + d3_geo_identityRotation.invert = d3_geo_equirectangular; + function d3_geo_rotation(δλ, δφ, δγ) { + return δλ ? δφ || δγ ? d3_geo_compose(d3_geo_rotationλ(δλ), d3_geo_rotationφγ(δφ, δγ)) : d3_geo_rotationλ(δλ) : δφ || δγ ? d3_geo_rotationφγ(δφ, δγ) : d3_geo_identityRotation; + } + function d3_geo_forwardRotationλ(δλ) { + return function(λ, φ) { + return λ += δλ, [ λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ ]; + }; + } + function d3_geo_rotationλ(δλ) { + var rotation = d3_geo_forwardRotationλ(δλ); + rotation.invert = d3_geo_forwardRotationλ(-δλ); + return rotation; + } + function d3_geo_rotationφγ(δφ, δγ) { + var cosδφ = Math.cos(δφ), sinδφ = Math.sin(δφ), cosδγ = Math.cos(δγ), sinδγ = Math.sin(δγ); + function rotation(λ, φ) { + var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδφ + x * sinδφ; + return [ Math.atan2(y * cosδγ - k * sinδγ, x * cosδφ - z * sinδφ), d3_asin(k * cosδγ + y * sinδγ) ]; + } + rotation.invert = function(λ, φ) { + var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδγ - y * sinδγ; + return [ Math.atan2(y * cosδγ + z * sinδγ, x * cosδφ + k * sinδφ), d3_asin(k * cosδφ - x * sinδφ) ]; + }; + return rotation; + } + d3.geo.circle = function() { + var origin = [ 0, 0 ], angle, precision = 6, interpolate; + function circle() { + var center = typeof origin === "function" ? origin.apply(this, arguments) : origin, rotate = d3_geo_rotation(-center[0] * d3_radians, -center[1] * d3_radians, 0).invert, ring = []; + interpolate(null, null, 1, { + point: function(x, y) { + ring.push(x = rotate(x, y)); + x[0] *= d3_degrees, x[1] *= d3_degrees; + } + }); + return { + type: "Polygon", + coordinates: [ ring ] + }; + } + circle.origin = function(x) { + if (!arguments.length) return origin; + origin = x; + return circle; + }; + circle.angle = function(x) { + if (!arguments.length) return angle; + interpolate = d3_geo_circleInterpolate((angle = +x) * d3_radians, precision * d3_radians); + return circle; + }; + circle.precision = function(_) { + if (!arguments.length) return precision; + interpolate = d3_geo_circleInterpolate(angle * d3_radians, (precision = +_) * d3_radians); + return circle; + }; + return circle.angle(90); + }; + function d3_geo_circleInterpolate(radius, precision) { + var cr = Math.cos(radius), sr = Math.sin(radius); + return function(from, to, direction, listener) { + var step = direction * precision; + if (from != null) { + from = d3_geo_circleAngle(cr, from); + to = d3_geo_circleAngle(cr, to); + if (direction > 0 ? from < to : from > to) from += direction * τ; + } else { + from = radius + direction * τ; + to = radius - .5 * step; + } + for (var point, t = from; direction > 0 ? t > to : t < to; t -= step) { + listener.point((point = d3_geo_spherical([ cr, -sr * Math.cos(t), -sr * Math.sin(t) ]))[0], point[1]); + } + }; + } + function d3_geo_circleAngle(cr, point) { + var a = d3_geo_cartesian(point); + a[0] -= cr; + d3_geo_cartesianNormalize(a); + var angle = d3_acos(-a[1]); + return ((-a[2] < 0 ? -angle : angle) + 2 * Math.PI - ε) % (2 * Math.PI); + } + d3.geo.distance = function(a, b) { + var Δλ = (b[0] - a[0]) * d3_radians, φ0 = a[1] * d3_radians, φ1 = b[1] * d3_radians, sinΔλ = Math.sin(Δλ), cosΔλ = Math.cos(Δλ), sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), sinφ1 = Math.sin(φ1), cosφ1 = Math.cos(φ1), t; + return Math.atan2(Math.sqrt((t = cosφ1 * sinΔλ) * t + (t = cosφ0 * sinφ1 - sinφ0 * cosφ1 * cosΔλ) * t), sinφ0 * sinφ1 + cosφ0 * cosφ1 * cosΔλ); + }; + d3.geo.graticule = function() { + 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 d3.range(Math.ceil(X0 / DX) * DX, X1, DX).map(X).concat(d3.range(Math.ceil(Y0 / DY) * DY, Y1, DY).map(Y)).concat(d3.range(Math.ceil(x0 / dx) * dx, x1, dx).filter(function(x) { + return abs(x % DX) > ε; + }).map(x)).concat(d3.range(Math.ceil(y0 / dy) * dy, y1, dy).filter(function(y) { + return abs(y % DY) > ε; + }).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.minorExtent(); + return graticule.majorExtent(_).minorExtent(_); + }; + graticule.majorExtent = 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.minorExtent = 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.minorStep(); + return graticule.majorStep(_).minorStep(_); + }; + graticule.majorStep = function(_) { + if (!arguments.length) return [ DX, DY ]; + DX = +_[0], DY = +_[1]; + return graticule; + }; + graticule.minorStep = function(_) { + if (!arguments.length) return [ dx, dy ]; + dx = +_[0], dy = +_[1]; + return graticule; + }; + graticule.precision = function(_) { + if (!arguments.length) return precision; + precision = +_; + x = d3_geo_graticuleX(y0, y1, 90); + y = d3_geo_graticuleY(x0, x1, precision); + X = d3_geo_graticuleX(Y0, Y1, 90); + Y = d3_geo_graticuleY(X0, X1, precision); + return graticule; + }; + return graticule.majorExtent([ [ -180, -90 + ε ], [ 180, 90 - ε ] ]).minorExtent([ [ -180, -80 - ε ], [ 180, 80 + ε ] ]); + }; + function d3_geo_graticuleX(y0, y1, dy) { + var y = d3.range(y0, y1 - ε, dy).concat(y1); + return function(x) { + return y.map(function(y) { + return [ x, y ]; + }); + }; + } + function d3_geo_graticuleY(x0, x1, dx) { + var x = d3.range(x0, x1 - ε, dx).concat(x1); + return function(y) { + return x.map(function(x) { + return [ x, y ]; + }); + }; + } + function d3_source(d) { + return d.source; + } + function d3_target(d) { + return d.target; + } + d3.geo.greatArc = function() { + var source = d3_source, source_, target = d3_target, target_; + function greatArc() { + return { + type: "LineString", + coordinates: [ source_ || source.apply(this, arguments), target_ || target.apply(this, arguments) ] + }; + } + greatArc.distance = function() { + return d3.geo.distance(source_ || source.apply(this, arguments), target_ || target.apply(this, arguments)); + }; + greatArc.source = function(_) { + if (!arguments.length) return source; + source = _, source_ = typeof _ === "function" ? null : _; + return greatArc; + }; + greatArc.target = function(_) { + if (!arguments.length) return target; + target = _, target_ = typeof _ === "function" ? null : _; + return greatArc; + }; + greatArc.precision = function() { + return arguments.length ? greatArc : 0; + }; + return greatArc; + }; + d3.geo.interpolate = function(source, target) { + return d3_geo_interpolate(source[0] * d3_radians, source[1] * d3_radians, target[0] * d3_radians, target[1] * d3_radians); + }; + function d3_geo_interpolate(x0, y0, x1, y1) { + var cy0 = Math.cos(y0), sy0 = Math.sin(y0), cy1 = Math.cos(y1), sy1 = Math.sin(y1), kx0 = cy0 * Math.cos(x0), ky0 = cy0 * Math.sin(x0), kx1 = cy1 * Math.cos(x1), ky1 = cy1 * Math.sin(x1), d = 2 * Math.asin(Math.sqrt(d3_haversin(y1 - y0) + cy0 * cy1 * d3_haversin(x1 - x0))), k = 1 / Math.sin(d); + var interpolate = d ? function(t) { + var B = Math.sin(t *= d) * k, A = Math.sin(d - t) * k, x = A * kx0 + B * kx1, y = A * ky0 + B * ky1, z = A * sy0 + B * sy1; + return [ Math.atan2(y, x) * d3_degrees, Math.atan2(z, Math.sqrt(x * x + y * y)) * d3_degrees ]; + } : function() { + return [ x0 * d3_degrees, y0 * d3_degrees ]; + }; + interpolate.distance = d; + return interpolate; + } + d3.geo.length = function(object) { + d3_geo_lengthSum = 0; + d3.geo.stream(object, d3_geo_length); + return d3_geo_lengthSum; + }; + var d3_geo_lengthSum; + var d3_geo_length = { + sphere: d3_noop, + point: d3_noop, + lineStart: d3_geo_lengthLineStart, + lineEnd: d3_noop, + polygonStart: d3_noop, + polygonEnd: d3_noop + }; + function d3_geo_lengthLineStart() { + var λ0, sinφ0, cosφ0; + d3_geo_length.point = function(λ, φ) { + λ0 = λ * d3_radians, sinφ0 = Math.sin(φ *= d3_radians), cosφ0 = Math.cos(φ); + d3_geo_length.point = nextPoint; + }; + d3_geo_length.lineEnd = function() { + d3_geo_length.point = d3_geo_length.lineEnd = d3_noop; + }; + function nextPoint(λ, φ) { + var sinφ = Math.sin(φ *= d3_radians), cosφ = Math.cos(φ), t = abs((λ *= d3_radians) - λ0), cosΔλ = Math.cos(t); + d3_geo_lengthSum += Math.atan2(Math.sqrt((t = cosφ * Math.sin(t)) * t + (t = cosφ0 * sinφ - sinφ0 * cosφ * cosΔλ) * t), sinφ0 * sinφ + cosφ0 * cosφ * cosΔλ); + λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ; + } + } + function d3_geo_azimuthal(scale, angle) { + function azimuthal(λ, φ) { + var cosλ = Math.cos(λ), cosφ = Math.cos(φ), k = scale(cosλ * cosφ); + return [ k * cosφ * Math.sin(λ), k * Math.sin(φ) ]; + } + azimuthal.invert = function(x, y) { + var ρ = Math.sqrt(x * x + y * y), c = angle(ρ), sinc = Math.sin(c), cosc = Math.cos(c); + return [ Math.atan2(x * sinc, ρ * cosc), Math.asin(ρ && y * sinc / ρ) ]; + }; + return azimuthal; + } + var d3_geo_azimuthalEqualArea = d3_geo_azimuthal(function(cosλcosφ) { + return Math.sqrt(2 / (1 + cosλcosφ)); + }, function(ρ) { + return 2 * Math.asin(ρ / 2); + }); + (d3.geo.azimuthalEqualArea = function() { + return d3_geo_projection(d3_geo_azimuthalEqualArea); + }).raw = d3_geo_azimuthalEqualArea; + var d3_geo_azimuthalEquidistant = d3_geo_azimuthal(function(cosλcosφ) { + var c = Math.acos(cosλcosφ); + return c && c / Math.sin(c); + }, d3_identity); + (d3.geo.azimuthalEquidistant = function() { + return d3_geo_projection(d3_geo_azimuthalEquidistant); + }).raw = d3_geo_azimuthalEquidistant; + function d3_geo_conicConformal(φ0, φ1) { + var cosφ0 = Math.cos(φ0), t = function(φ) { + return Math.tan(π / 4 + φ / 2); + }, n = φ0 === φ1 ? Math.sin(φ0) : Math.log(cosφ0 / Math.cos(φ1)) / Math.log(t(φ1) / t(φ0)), F = cosφ0 * Math.pow(t(φ0), n) / n; + if (!n) return d3_geo_mercator; + function forward(λ, φ) { + if (F > 0) { + if (φ < -halfπ + ε) φ = -halfπ + ε; + } else { + if (φ > halfπ - ε) φ = halfπ - ε; + } + var ρ = F / Math.pow(t(φ), n); + return [ ρ * Math.sin(n * λ), F - ρ * Math.cos(n * λ) ]; + } + forward.invert = function(x, y) { + var ρ0_y = F - y, ρ = d3_sgn(n) * Math.sqrt(x * x + ρ0_y * ρ0_y); + return [ Math.atan2(x, ρ0_y) / n, 2 * Math.atan(Math.pow(F / ρ, 1 / n)) - halfπ ]; + }; + return forward; + } + (d3.geo.conicConformal = function() { + return d3_geo_conic(d3_geo_conicConformal); + }).raw = d3_geo_conicConformal; + function d3_geo_conicEquidistant(φ0, φ1) { + var cosφ0 = Math.cos(φ0), n = φ0 === φ1 ? Math.sin(φ0) : (cosφ0 - Math.cos(φ1)) / (φ1 - φ0), G = cosφ0 / n + φ0; + if (abs(n) < ε) return d3_geo_equirectangular; + function forward(λ, φ) { + var ρ = G - φ; + return [ ρ * Math.sin(n * λ), G - ρ * Math.cos(n * λ) ]; + } + forward.invert = function(x, y) { + var ρ0_y = G - y; + return [ Math.atan2(x, ρ0_y) / n, G - d3_sgn(n) * Math.sqrt(x * x + ρ0_y * ρ0_y) ]; + }; + return forward; + } + (d3.geo.conicEquidistant = function() { + return d3_geo_conic(d3_geo_conicEquidistant); + }).raw = d3_geo_conicEquidistant; + var d3_geo_gnomonic = d3_geo_azimuthal(function(cosλcosφ) { + return 1 / cosλcosφ; + }, Math.atan); + (d3.geo.gnomonic = function() { + return d3_geo_projection(d3_geo_gnomonic); + }).raw = d3_geo_gnomonic; + function d3_geo_mercator(λ, φ) { + return [ λ, Math.log(Math.tan(π / 4 + φ / 2)) ]; + } + d3_geo_mercator.invert = function(x, y) { + return [ x, 2 * Math.atan(Math.exp(y)) - halfπ ]; + }; + function d3_geo_mercatorProjection(project) { + var m = d3_geo_projection(project), scale = m.scale, translate = m.translate, clipExtent = m.clipExtent, clipAuto; + m.scale = function() { + var v = scale.apply(m, arguments); + return v === m ? clipAuto ? m.clipExtent(null) : m : v; + }; + m.translate = function() { + var v = translate.apply(m, arguments); + return v === m ? clipAuto ? m.clipExtent(null) : m : v; + }; + m.clipExtent = function(_) { + var v = clipExtent.apply(m, arguments); + if (v === m) { + if (clipAuto = _ == null) { + var k = π * scale(), t = translate(); + clipExtent([ [ t[0] - k, t[1] - k ], [ t[0] + k, t[1] + k ] ]); + } + } else if (clipAuto) { + v = null; + } + return v; + }; + return m.clipExtent(null); + } + (d3.geo.mercator = function() { + return d3_geo_mercatorProjection(d3_geo_mercator); + }).raw = d3_geo_mercator; + var d3_geo_orthographic = d3_geo_azimuthal(function() { + return 1; + }, Math.asin); + (d3.geo.orthographic = function() { + return d3_geo_projection(d3_geo_orthographic); + }).raw = d3_geo_orthographic; + var d3_geo_stereographic = d3_geo_azimuthal(function(cosλcosφ) { + return 1 / (1 + cosλcosφ); + }, function(ρ) { + return 2 * Math.atan(ρ); + }); + (d3.geo.stereographic = function() { + return d3_geo_projection(d3_geo_stereographic); + }).raw = d3_geo_stereographic; + function d3_geo_transverseMercator(λ, φ) { + return [ Math.log(Math.tan(π / 4 + φ / 2)), -λ ]; + } + d3_geo_transverseMercator.invert = function(x, y) { + return [ -y, 2 * Math.atan(Math.exp(x)) - halfπ ]; + }; + (d3.geo.transverseMercator = function() { + var projection = d3_geo_mercatorProjection(d3_geo_transverseMercator), center = projection.center, rotate = projection.rotate; + projection.center = function(_) { + return _ ? center([ -_[1], _[0] ]) : (_ = center(), [ _[1], -_[0] ]); + }; + projection.rotate = function(_) { + return _ ? rotate([ _[0], _[1], _.length > 2 ? _[2] + 90 : 90 ]) : (_ = rotate(), + [ _[0], _[1], _[2] - 90 ]); + }; + return rotate([ 0, 0, 90 ]); + }).raw = d3_geo_transverseMercator; + d3.geom = {}; + function d3_geom_pointX(d) { + return d[0]; + } + function d3_geom_pointY(d) { + return d[1]; + } + d3.geom.hull = function(vertices) { + var x = d3_geom_pointX, y = d3_geom_pointY; + if (arguments.length) return hull(vertices); + function hull(data) { + if (data.length < 3) return []; + var fx = d3_functor(x), fy = d3_functor(y), i, n = data.length, points = [], flippedPoints = []; + for (i = 0; i < n; i++) { + points.push([ +fx.call(this, data[i], i), +fy.call(this, data[i], i), i ]); + } + points.sort(d3_geom_hullOrder); + for (i = 0; i < n; i++) flippedPoints.push([ points[i][0], -points[i][1] ]); + var upper = d3_geom_hullUpper(points), lower = d3_geom_hullUpper(flippedPoints); + var skipLeft = lower[0] === upper[0], skipRight = lower[lower.length - 1] === upper[upper.length - 1], polygon = []; + for (i = upper.length - 1; i >= 0; --i) polygon.push(data[points[upper[i]][2]]); + for (i = +skipLeft; i < lower.length - skipRight; ++i) polygon.push(data[points[lower[i]][2]]); + return polygon; + } + hull.x = function(_) { + return arguments.length ? (x = _, hull) : x; + }; + hull.y = function(_) { + return arguments.length ? (y = _, hull) : y; + }; + return hull; + }; + function d3_geom_hullUpper(points) { + var n = points.length, hull = [ 0, 1 ], hs = 2; + for (var i = 2; i < n; i++) { + while (hs > 1 && d3_cross2d(points[hull[hs - 2]], points[hull[hs - 1]], points[i]) <= 0) --hs; + hull[hs++] = i; + } + return hull.slice(0, hs); + } + function d3_geom_hullOrder(a, b) { + return a[0] - b[0] || a[1] - b[1]; + } + d3.geom.polygon = function(coordinates) { + d3_subclass(coordinates, d3_geom_polygonPrototype); + return coordinates; + }; + var d3_geom_polygonPrototype = d3.geom.polygon.prototype = []; + d3_geom_polygonPrototype.area = function() { + var i = -1, n = this.length, a, b = this[n - 1], area = 0; + while (++i < n) { + a = b; + b = this[i]; + area += a[1] * b[0] - a[0] * b[1]; + } + return area * .5; + }; + d3_geom_polygonPrototype.centroid = function(k) { + var i = -1, n = this.length, x = 0, y = 0, a, b = this[n - 1], c; + if (!arguments.length) k = -1 / (6 * this.area()); + while (++i < n) { + a = b; + b = this[i]; + c = a[0] * b[1] - b[0] * a[1]; + x += (a[0] + b[0]) * c; + y += (a[1] + b[1]) * c; + } + return [ x * k, y * k ]; + }; + d3_geom_polygonPrototype.clip = function(subject) { + var input, closed = d3_geom_polygonClosed(subject), i = -1, n = this.length - d3_geom_polygonClosed(this), j, m, a = this[n - 1], b, c, d; + while (++i < n) { + input = subject.slice(); + subject.length = 0; + b = this[i]; + c = input[(m = input.length - closed) - 1]; + j = -1; + while (++j < m) { + d = input[j]; + if (d3_geom_polygonInside(d, a, b)) { + if (!d3_geom_polygonInside(c, a, b)) { + subject.push(d3_geom_polygonIntersect(c, d, a, b)); + } + subject.push(d); + } else if (d3_geom_polygonInside(c, a, b)) { + subject.push(d3_geom_polygonIntersect(c, d, a, b)); + } + c = d; + } + if (closed) subject.push(subject[0]); + a = b; + } + return subject; + }; + function d3_geom_polygonInside(p, a, b) { + return (b[0] - a[0]) * (p[1] - a[1]) < (b[1] - a[1]) * (p[0] - a[0]); + } + function d3_geom_polygonIntersect(c, d, a, b) { + var x1 = c[0], x3 = a[0], x21 = d[0] - x1, x43 = b[0] - x3, y1 = c[1], y3 = a[1], y21 = d[1] - y1, y43 = b[1] - y3, ua = (x43 * (y1 - y3) - y43 * (x1 - x3)) / (y43 * x21 - x43 * y21); + return [ x1 + ua * x21, y1 + ua * y21 ]; + } + function d3_geom_polygonClosed(coordinates) { + var a = coordinates[0], b = coordinates[coordinates.length - 1]; + return !(a[0] - b[0] || a[1] - b[1]); + } + var d3_geom_voronoiEdges, d3_geom_voronoiCells, d3_geom_voronoiBeaches, d3_geom_voronoiBeachPool = [], d3_geom_voronoiFirstCircle, d3_geom_voronoiCircles, d3_geom_voronoiCirclePool = []; + function d3_geom_voronoiBeach() { + d3_geom_voronoiRedBlackNode(this); + this.edge = this.site = this.circle = null; + } + function d3_geom_voronoiCreateBeach(site) { + var beach = d3_geom_voronoiBeachPool.pop() || new d3_geom_voronoiBeach(); + beach.site = site; + return beach; + } + function d3_geom_voronoiDetachBeach(beach) { + d3_geom_voronoiDetachCircle(beach); + d3_geom_voronoiBeaches.remove(beach); + d3_geom_voronoiBeachPool.push(beach); + d3_geom_voronoiRedBlackNode(beach); + } + function d3_geom_voronoiRemoveBeach(beach) { + var circle = beach.circle, x = circle.x, y = circle.cy, vertex = { + x: x, + y: y + }, previous = beach.P, next = beach.N, disappearing = [ beach ]; + d3_geom_voronoiDetachBeach(beach); + var lArc = previous; + while (lArc.circle && abs(x - lArc.circle.x) < ε && abs(y - lArc.circle.cy) < ε) { + previous = lArc.P; + disappearing.unshift(lArc); + d3_geom_voronoiDetachBeach(lArc); + lArc = previous; + } + disappearing.unshift(lArc); + d3_geom_voronoiDetachCircle(lArc); + var rArc = next; + while (rArc.circle && abs(x - rArc.circle.x) < ε && abs(y - rArc.circle.cy) < ε) { + next = rArc.N; + disappearing.push(rArc); + d3_geom_voronoiDetachBeach(rArc); + rArc = next; + } + disappearing.push(rArc); + d3_geom_voronoiDetachCircle(rArc); + var nArcs = disappearing.length, iArc; + for (iArc = 1; iArc < nArcs; ++iArc) { + rArc = disappearing[iArc]; + lArc = disappearing[iArc - 1]; + d3_geom_voronoiSetEdgeEnd(rArc.edge, lArc.site, rArc.site, vertex); + } + lArc = disappearing[0]; + rArc = disappearing[nArcs - 1]; + rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, rArc.site, null, vertex); + d3_geom_voronoiAttachCircle(lArc); + d3_geom_voronoiAttachCircle(rArc); + } + function d3_geom_voronoiAddBeach(site) { + var x = site.x, directrix = site.y, lArc, rArc, dxl, dxr, node = d3_geom_voronoiBeaches._; + while (node) { + dxl = d3_geom_voronoiLeftBreakPoint(node, directrix) - x; + if (dxl > ε) node = node.L; else { + dxr = x - d3_geom_voronoiRightBreakPoint(node, directrix); + if (dxr > ε) { + if (!node.R) { + lArc = node; + break; + } + node = node.R; + } else { + if (dxl > -ε) { + lArc = node.P; + rArc = node; + } else if (dxr > -ε) { + lArc = node; + rArc = node.N; + } else { + lArc = rArc = node; + } + break; + } + } + } + var newArc = d3_geom_voronoiCreateBeach(site); + d3_geom_voronoiBeaches.insert(lArc, newArc); + if (!lArc && !rArc) return; + if (lArc === rArc) { + d3_geom_voronoiDetachCircle(lArc); + rArc = d3_geom_voronoiCreateBeach(lArc.site); + d3_geom_voronoiBeaches.insert(newArc, rArc); + newArc.edge = rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site); + d3_geom_voronoiAttachCircle(lArc); + d3_geom_voronoiAttachCircle(rArc); + return; + } + if (!rArc) { + newArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site); + return; + } + d3_geom_voronoiDetachCircle(lArc); + d3_geom_voronoiDetachCircle(rArc); + var lSite = lArc.site, ax = lSite.x, ay = lSite.y, bx = site.x - ax, by = site.y - ay, rSite = rArc.site, cx = rSite.x - ax, cy = rSite.y - ay, d = 2 * (bx * cy - by * cx), hb = bx * bx + by * by, hc = cx * cx + cy * cy, vertex = { + x: (cy * hb - by * hc) / d + ax, + y: (bx * hc - cx * hb) / d + ay + }; + d3_geom_voronoiSetEdgeEnd(rArc.edge, lSite, rSite, vertex); + newArc.edge = d3_geom_voronoiCreateEdge(lSite, site, null, vertex); + rArc.edge = d3_geom_voronoiCreateEdge(site, rSite, null, vertex); + d3_geom_voronoiAttachCircle(lArc); + d3_geom_voronoiAttachCircle(rArc); + } + function d3_geom_voronoiLeftBreakPoint(arc, directrix) { + var site = arc.site, rfocx = site.x, rfocy = site.y, pby2 = rfocy - directrix; + if (!pby2) return rfocx; + var lArc = arc.P; + if (!lArc) return -Infinity; + site = lArc.site; + var lfocx = site.x, lfocy = site.y, 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 d3_geom_voronoiRightBreakPoint(arc, directrix) { + var rArc = arc.N; + if (rArc) return d3_geom_voronoiLeftBreakPoint(rArc, directrix); + var site = arc.site; + return site.y === directrix ? site.x : Infinity; + } + function d3_geom_voronoiCell(site) { + this.site = site; + this.edges = []; + } + d3_geom_voronoiCell.prototype.prepare = function() { + var halfEdges = this.edges, iHalfEdge = halfEdges.length, edge; + while (iHalfEdge--) { + edge = halfEdges[iHalfEdge].edge; + if (!edge.b || !edge.a) halfEdges.splice(iHalfEdge, 1); + } + halfEdges.sort(d3_geom_voronoiHalfEdgeOrder); + return halfEdges.length; + }; + function d3_geom_voronoiCloseCells(extent) { + var x0 = extent[0][0], x1 = extent[1][0], y0 = extent[0][1], y1 = extent[1][1], x2, y2, x3, y3, cells = d3_geom_voronoiCells, iCell = cells.length, cell, iHalfEdge, halfEdges, nHalfEdges, start, end; + while (iCell--) { + cell = cells[iCell]; + if (!cell || !cell.prepare()) continue; + halfEdges = cell.edges; + nHalfEdges = halfEdges.length; + iHalfEdge = 0; + while (iHalfEdge < nHalfEdges) { + end = halfEdges[iHalfEdge].end(), x3 = end.x, y3 = end.y; + start = halfEdges[++iHalfEdge % nHalfEdges].start(), x2 = start.x, y2 = start.y; + if (abs(x3 - x2) > ε || abs(y3 - y2) > ε) { + halfEdges.splice(iHalfEdge, 0, new d3_geom_voronoiHalfEdge(d3_geom_voronoiCreateBorderEdge(cell.site, end, abs(x3 - x0) < ε && y1 - y3 > ε ? { + x: x0, + y: abs(x2 - x0) < ε ? y2 : y1 + } : abs(y3 - y1) < ε && x1 - x3 > ε ? { + x: abs(y2 - y1) < ε ? x2 : x1, + y: y1 + } : abs(x3 - x1) < ε && y3 - y0 > ε ? { + x: x1, + y: abs(x2 - x1) < ε ? y2 : y0 + } : abs(y3 - y0) < ε && x3 - x0 > ε ? { + x: abs(y2 - y0) < ε ? x2 : x0, + y: y0 + } : null), cell.site, null)); + ++nHalfEdges; + } + } + } + } + function d3_geom_voronoiHalfEdgeOrder(a, b) { + return b.angle - a.angle; + } + function d3_geom_voronoiCircle() { + d3_geom_voronoiRedBlackNode(this); + this.x = this.y = this.arc = this.site = this.cy = null; + } + function d3_geom_voronoiAttachCircle(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.x, by = cSite.y, ax = lSite.x - bx, ay = lSite.y - by, cx = rSite.x - bx, cy = rSite.y - by; + var d = 2 * (ax * cy - ay * cx); + if (d >= -ε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, cy = y + by; + var circle = d3_geom_voronoiCirclePool.pop() || new d3_geom_voronoiCircle(); + circle.arc = arc; + circle.site = cSite; + circle.x = x + bx; + circle.y = cy + Math.sqrt(x * x + y * y); + circle.cy = cy; + arc.circle = circle; + var before = null, node = d3_geom_voronoiCircles._; + 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; + } + } + } + d3_geom_voronoiCircles.insert(before, circle); + if (!before) d3_geom_voronoiFirstCircle = circle; + } + function d3_geom_voronoiDetachCircle(arc) { + var circle = arc.circle; + if (circle) { + if (!circle.P) d3_geom_voronoiFirstCircle = circle.N; + d3_geom_voronoiCircles.remove(circle); + d3_geom_voronoiCirclePool.push(circle); + d3_geom_voronoiRedBlackNode(circle); + arc.circle = null; + } + } + function d3_geom_voronoiClipEdges(extent) { + var edges = d3_geom_voronoiEdges, clip = d3_geom_clipLine(extent[0][0], extent[0][1], extent[1][0], extent[1][1]), i = edges.length, e; + while (i--) { + e = edges[i]; + if (!d3_geom_voronoiConnectEdge(e, extent) || !clip(e) || abs(e.a.x - e.b.x) < ε && abs(e.a.y - e.b.y) < ε) { + e.a = e.b = null; + edges.splice(i, 1); + } + } + } + function d3_geom_voronoiConnectEdge(edge, extent) { + var vb = edge.b; + if (vb) return true; + var va = edge.a, x0 = extent[0][0], x1 = extent[1][0], y0 = extent[0][1], y1 = extent[1][1], lSite = edge.l, rSite = edge.r, lx = lSite.x, ly = lSite.y, rx = rSite.x, ry = rSite.y, fx = (lx + rx) / 2, fy = (ly + ry) / 2, fm, fb; + if (ry === ly) { + if (fx < x0 || fx >= x1) return; + if (lx > rx) { + if (!va) va = { + x: fx, + y: y0 + }; else if (va.y >= y1) return; + vb = { + x: fx, + y: y1 + }; + } else { + if (!va) va = { + x: fx, + y: y1 + }; else if (va.y < y0) return; + vb = { + x: fx, + y: y0 + }; + } + } else { + fm = (lx - rx) / (ry - ly); + fb = fy - fm * fx; + if (fm < -1 || fm > 1) { + if (lx > rx) { + if (!va) va = { + x: (y0 - fb) / fm, + y: y0 + }; else if (va.y >= y1) return; + vb = { + x: (y1 - fb) / fm, + y: y1 + }; + } else { + if (!va) va = { + x: (y1 - fb) / fm, + y: y1 + }; else if (va.y < y0) return; + vb = { + x: (y0 - fb) / fm, + y: y0 + }; + } + } else { + if (ly < ry) { + if (!va) va = { + x: x0, + y: fm * x0 + fb + }; else if (va.x >= x1) return; + vb = { + x: x1, + y: fm * x1 + fb + }; + } else { + if (!va) va = { + x: x1, + y: fm * x1 + fb + }; else if (va.x < x0) return; + vb = { + x: x0, + y: fm * x0 + fb + }; + } + } + } + edge.a = va; + edge.b = vb; + return true; + } + function d3_geom_voronoiEdge(lSite, rSite) { + this.l = lSite; + this.r = rSite; + this.a = this.b = null; + } + function d3_geom_voronoiCreateEdge(lSite, rSite, va, vb) { + var edge = new d3_geom_voronoiEdge(lSite, rSite); + d3_geom_voronoiEdges.push(edge); + if (va) d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, va); + if (vb) d3_geom_voronoiSetEdgeEnd(edge, rSite, lSite, vb); + d3_geom_voronoiCells[lSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, lSite, rSite)); + d3_geom_voronoiCells[rSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, rSite, lSite)); + return edge; + } + function d3_geom_voronoiCreateBorderEdge(lSite, va, vb) { + var edge = new d3_geom_voronoiEdge(lSite, null); + edge.a = va; + edge.b = vb; + d3_geom_voronoiEdges.push(edge); + return edge; + } + function d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, vertex) { + if (!edge.a && !edge.b) { + edge.a = vertex; + edge.l = lSite; + edge.r = rSite; + } else if (edge.l === rSite) { + edge.b = vertex; + } else { + edge.a = vertex; + } + } + function d3_geom_voronoiHalfEdge(edge, lSite, rSite) { + var va = edge.a, vb = edge.b; + this.edge = edge; + this.site = lSite; + this.angle = rSite ? Math.atan2(rSite.y - lSite.y, rSite.x - lSite.x) : edge.l === lSite ? Math.atan2(vb.x - va.x, va.y - vb.y) : Math.atan2(va.x - vb.x, vb.y - va.y); + } + d3_geom_voronoiHalfEdge.prototype = { + start: function() { + return this.edge.l === this.site ? this.edge.a : this.edge.b; + }, + end: function() { + return this.edge.l === this.site ? this.edge.b : this.edge.a; + } + }; + function d3_geom_voronoiRedBlackTree() { + this._ = null; + } + function d3_geom_voronoiRedBlackNode(node) { + node.U = node.C = node.L = node.R = node.P = node.N = null; + } + d3_geom_voronoiRedBlackTree.prototype = { + 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 = d3_geom_voronoiRedBlackFirst(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) { + d3_geom_voronoiRedBlackRotateLeft(this, parent); + after = parent; + parent = after.U; + } + parent.C = false; + grandpa.C = true; + d3_geom_voronoiRedBlackRotateRight(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) { + d3_geom_voronoiRedBlackRotateRight(this, parent); + after = parent; + parent = after.U; + } + parent.C = false; + grandpa.C = true; + d3_geom_voronoiRedBlackRotateLeft(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 = d3_geom_voronoiRedBlackFirst(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; + d3_geom_voronoiRedBlackRotateLeft(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; + d3_geom_voronoiRedBlackRotateRight(this, sibling); + sibling = parent.R; + } + sibling.C = parent.C; + parent.C = sibling.R.C = false; + d3_geom_voronoiRedBlackRotateLeft(this, parent); + node = this._; + break; + } + } else { + sibling = parent.L; + if (sibling.C) { + sibling.C = false; + parent.C = true; + d3_geom_voronoiRedBlackRotateRight(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; + d3_geom_voronoiRedBlackRotateLeft(this, sibling); + sibling = parent.L; + } + sibling.C = parent.C; + parent.C = sibling.L.C = false; + d3_geom_voronoiRedBlackRotateRight(this, parent); + node = this._; + break; + } + } + sibling.C = true; + node = parent; + parent = parent.U; + } while (!node.C); + if (node) node.C = false; + } + }; + function d3_geom_voronoiRedBlackRotateLeft(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 d3_geom_voronoiRedBlackRotateRight(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 d3_geom_voronoiRedBlackFirst(node) { + while (node.L) node = node.L; + return node; + } + function d3_geom_voronoi(sites, bbox) { + var site = sites.sort(d3_geom_voronoiVertexOrder).pop(), x0, y0, circle; + d3_geom_voronoiEdges = []; + d3_geom_voronoiCells = new Array(sites.length); + d3_geom_voronoiBeaches = new d3_geom_voronoiRedBlackTree(); + d3_geom_voronoiCircles = new d3_geom_voronoiRedBlackTree(); + while (true) { + circle = d3_geom_voronoiFirstCircle; + if (site && (!circle || site.y < circle.y || site.y === circle.y && site.x < circle.x)) { + if (site.x !== x0 || site.y !== y0) { + d3_geom_voronoiCells[site.i] = new d3_geom_voronoiCell(site); + d3_geom_voronoiAddBeach(site); + x0 = site.x, y0 = site.y; + } + site = sites.pop(); + } else if (circle) { + d3_geom_voronoiRemoveBeach(circle.arc); + } else { + break; + } + } + if (bbox) d3_geom_voronoiClipEdges(bbox), d3_geom_voronoiCloseCells(bbox); + var diagram = { + cells: d3_geom_voronoiCells, + edges: d3_geom_voronoiEdges + }; + d3_geom_voronoiBeaches = d3_geom_voronoiCircles = d3_geom_voronoiEdges = d3_geom_voronoiCells = null; + return diagram; + } + function d3_geom_voronoiVertexOrder(a, b) { + return b.y - a.y || b.x - a.x; + } + d3.geom.voronoi = function(points) { + var x = d3_geom_pointX, y = d3_geom_pointY, fx = x, fy = y, clipExtent = d3_geom_voronoiClipExtent; + if (points) return voronoi(points); + function voronoi(data) { + var polygons = new Array(data.length), x0 = clipExtent[0][0], y0 = clipExtent[0][1], x1 = clipExtent[1][0], y1 = clipExtent[1][1]; + d3_geom_voronoi(sites(data), clipExtent).cells.forEach(function(cell, i) { + var edges = cell.edges, site = cell.site, polygon = polygons[i] = edges.length ? edges.map(function(e) { + var s = e.start(); + return [ s.x, s.y ]; + }) : site.x >= x0 && site.x <= x1 && site.y >= y0 && site.y <= y1 ? [ [ x0, y1 ], [ x1, y1 ], [ x1, y0 ], [ x0, y0 ] ] : []; + polygon.point = data[i]; + }); + return polygons; + } + function sites(data) { + return data.map(function(d, i) { + return { + x: Math.round(fx(d, i) / ε) * ε, + y: Math.round(fy(d, i) / ε) * ε, + i: i + }; + }); + } + voronoi.links = function(data) { + return d3_geom_voronoi(sites(data)).edges.filter(function(edge) { + return edge.l && edge.r; + }).map(function(edge) { + return { + source: data[edge.l.i], + target: data[edge.r.i] + }; + }); + }; + voronoi.triangles = function(data) { + var triangles = []; + d3_geom_voronoi(sites(data)).cells.forEach(function(cell, i) { + var site = cell.site, edges = cell.edges.sort(d3_geom_voronoiHalfEdgeOrder), j = -1, m = edges.length, e0, s0, e1 = edges[m - 1].edge, s1 = e1.l === site ? e1.r : e1.l; + while (++j < m) { + e0 = e1; + s0 = s1; + e1 = edges[j].edge; + s1 = e1.l === site ? e1.r : e1.l; + if (i < s0.i && i < s1.i && d3_geom_voronoiTriangleArea(site, s0, s1) < 0) { + triangles.push([ data[i], data[s0.i], data[s1.i] ]); + } + } + }); + return triangles; + }; + voronoi.x = function(_) { + return arguments.length ? (fx = d3_functor(x = _), voronoi) : x; + }; + voronoi.y = function(_) { + return arguments.length ? (fy = d3_functor(y = _), voronoi) : y; + }; + voronoi.clipExtent = function(_) { + if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent; + clipExtent = _ == null ? d3_geom_voronoiClipExtent : _; + return voronoi; + }; + voronoi.size = function(_) { + if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent && clipExtent[1]; + return voronoi.clipExtent(_ && [ [ 0, 0 ], _ ]); + }; + return voronoi; + }; + var d3_geom_voronoiClipExtent = [ [ -1e6, -1e6 ], [ 1e6, 1e6 ] ]; + function d3_geom_voronoiTriangleArea(a, b, c) { + return (a.x - c.x) * (b.y - a.y) - (a.x - b.x) * (c.y - a.y); + } + d3.geom.delaunay = function(vertices) { + return d3.geom.voronoi().triangles(vertices); + }; + d3.geom.quadtree = function(points, x1, y1, x2, y2) { + var x = d3_geom_pointX, y = d3_geom_pointY, compat; + if (compat = arguments.length) { + x = d3_geom_quadtreeCompatX; + y = d3_geom_quadtreeCompatY; + if (compat === 3) { + y2 = y1; + x2 = x1; + y1 = x1 = 0; + } + return quadtree(points); + } + function quadtree(data) { + var d, fx = d3_functor(x), fy = d3_functor(y), xs, ys, i, n, x1_, y1_, x2_, y2_; + if (x1 != null) { + x1_ = x1, y1_ = y1, x2_ = x2, y2_ = y2; + } else { + x2_ = y2_ = -(x1_ = y1_ = Infinity); + xs = [], ys = []; + n = data.length; + if (compat) for (i = 0; i < n; ++i) { + d = data[i]; + if (d.x < x1_) x1_ = d.x; + if (d.y < y1_) y1_ = d.y; + if (d.x > x2_) x2_ = d.x; + if (d.y > y2_) y2_ = d.y; + xs.push(d.x); + ys.push(d.y); + } else for (i = 0; i < n; ++i) { + var x_ = +fx(d = data[i], i), y_ = +fy(d, i); + if (x_ < x1_) x1_ = x_; + if (y_ < y1_) y1_ = y_; + if (x_ > x2_) x2_ = x_; + if (y_ > y2_) y2_ = y_; + xs.push(x_); + ys.push(y_); + } + } + var dx = x2_ - x1_, dy = y2_ - y1_; + if (dx > dy) y2_ = y1_ + dx; else x2_ = x1_ + dy; + function insert(n, d, x, y, x1, y1, x2, y2) { + if (isNaN(x) || isNaN(y)) return; + if (n.leaf) { + var nx = n.x, ny = n.y; + if (nx != null) { + if (abs(nx - x) + abs(ny - y) < .01) { + insertChild(n, d, x, y, x1, y1, x2, y2); + } else { + var nPoint = n.point; + n.x = n.y = n.point = null; + insertChild(n, nPoint, nx, ny, x1, y1, x2, y2); + insertChild(n, d, x, y, x1, y1, x2, y2); + } + } else { + n.x = x, n.y = y, n.point = d; + } + } else { + insertChild(n, d, x, y, x1, y1, x2, y2); + } + } + function insertChild(n, d, x, y, x1, y1, x2, y2) { + var xm = (x1 + x2) * .5, ym = (y1 + y2) * .5, right = x >= xm, below = y >= ym, i = below << 1 | right; + n.leaf = false; + n = n.nodes[i] || (n.nodes[i] = d3_geom_quadtreeNode()); + if (right) x1 = xm; else x2 = xm; + if (below) y1 = ym; else y2 = ym; + insert(n, d, x, y, x1, y1, x2, y2); + } + var root = d3_geom_quadtreeNode(); + root.add = function(d) { + insert(root, d, +fx(d, ++i), +fy(d, i), x1_, y1_, x2_, y2_); + }; + root.visit = function(f) { + d3_geom_quadtreeVisit(f, root, x1_, y1_, x2_, y2_); + }; + root.find = function(point) { + return d3_geom_quadtreeFind(root, point[0], point[1], x1_, y1_, x2_, y2_); + }; + i = -1; + if (x1 == null) { + while (++i < n) { + insert(root, data[i], xs[i], ys[i], x1_, y1_, x2_, y2_); + } + --i; + } else data.forEach(root.add); + xs = ys = data = d = null; + return root; + } + quadtree.x = function(_) { + return arguments.length ? (x = _, quadtree) : x; + }; + quadtree.y = function(_) { + return arguments.length ? (y = _, quadtree) : y; + }; + quadtree.extent = function(_) { + if (!arguments.length) return x1 == null ? null : [ [ x1, y1 ], [ x2, y2 ] ]; + if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = +_[0][0], y1 = +_[0][1], x2 = +_[1][0], + y2 = +_[1][1]; + return quadtree; + }; + quadtree.size = function(_) { + if (!arguments.length) return x1 == null ? null : [ x2 - x1, y2 - y1 ]; + if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = y1 = 0, x2 = +_[0], y2 = +_[1]; + return quadtree; + }; + return quadtree; + }; + function d3_geom_quadtreeCompatX(d) { + return d.x; + } + function d3_geom_quadtreeCompatY(d) { + return d.y; + } + function d3_geom_quadtreeNode() { + return { + leaf: true, + nodes: [], + point: null, + x: null, + y: null + }; + } + function d3_geom_quadtreeVisit(f, node, x1, y1, x2, y2) { + if (!f(node, x1, y1, x2, y2)) { + var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5, children = node.nodes; + if (children[0]) d3_geom_quadtreeVisit(f, children[0], x1, y1, sx, sy); + if (children[1]) d3_geom_quadtreeVisit(f, children[1], sx, y1, x2, sy); + if (children[2]) d3_geom_quadtreeVisit(f, children[2], x1, sy, sx, y2); + if (children[3]) d3_geom_quadtreeVisit(f, children[3], sx, sy, x2, y2); + } + } + function d3_geom_quadtreeFind(root, x, y, x0, y0, x3, y3) { + var minDistance2 = Infinity, closestPoint; + (function find(node, x1, y1, x2, y2) { + if (x1 > x3 || y1 > y3 || x2 < x0 || y2 < y0) return; + if (point = node.point) { + var point, dx = x - node.x, dy = y - node.y, distance2 = dx * dx + dy * dy; + if (distance2 < minDistance2) { + var distance = Math.sqrt(minDistance2 = distance2); + x0 = x - distance, y0 = y - distance; + x3 = x + distance, y3 = y + distance; + closestPoint = point; + } + } + var children = node.nodes, xm = (x1 + x2) * .5, ym = (y1 + y2) * .5, right = x >= xm, below = y >= ym; + for (var i = below << 1 | right, j = i + 4; i < j; ++i) { + if (node = children[i & 3]) switch (i & 3) { + case 0: + find(node, x1, y1, xm, ym); + break; + + case 1: + find(node, xm, y1, x2, ym); + break; + + case 2: + find(node, x1, ym, xm, y2); + break; + + case 3: + find(node, xm, ym, x2, y2); + break; + } + } + })(root, x0, y0, x3, y3); + return closestPoint; + } + d3.interpolateRgb = d3_interpolateRgb; + function d3_interpolateRgb(a, b) { + a = d3.rgb(a); + b = d3.rgb(b); + var ar = a.r, ag = a.g, ab = a.b, br = b.r - ar, bg = b.g - ag, bb = b.b - ab; + return function(t) { + return "#" + d3_rgb_hex(Math.round(ar + br * t)) + d3_rgb_hex(Math.round(ag + bg * t)) + d3_rgb_hex(Math.round(ab + bb * t)); + }; + } + d3.interpolateObject = d3_interpolateObject; + function d3_interpolateObject(a, b) { + var i = {}, c = {}, k; + for (k in a) { + if (k in b) { + i[k] = d3_interpolate(a[k], b[k]); + } else { + c[k] = a[k]; + } + } + for (k in b) { + if (!(k in a)) { + c[k] = b[k]; + } + } + return function(t) { + for (k in i) c[k] = i[k](t); + return c; + }; + } + d3.interpolateNumber = d3_interpolateNumber; + function d3_interpolateNumber(a, b) { + a = +a, b = +b; + return function(t) { + return a * (1 - t) + b * t; + }; + } + d3.interpolateString = d3_interpolateString; + function d3_interpolateString(a, b) { + var bi = d3_interpolate_numberA.lastIndex = d3_interpolate_numberB.lastIndex = 0, am, bm, bs, i = -1, s = [], q = []; + a = a + "", b = b + ""; + while ((am = d3_interpolate_numberA.exec(a)) && (bm = d3_interpolate_numberB.exec(b))) { + if ((bs = bm.index) > bi) { + bs = b.slice(bi, bs); + if (s[i]) s[i] += bs; else s[++i] = bs; + } + if ((am = am[0]) === (bm = bm[0])) { + if (s[i]) s[i] += bm; else s[++i] = bm; + } else { + s[++i] = null; + q.push({ + i: i, + x: d3_interpolateNumber(am, bm) + }); + } + bi = d3_interpolate_numberB.lastIndex; + } + if (bi < b.length) { + bs = b.slice(bi); + if (s[i]) s[i] += bs; else s[++i] = bs; + } + return s.length < 2 ? q[0] ? (b = q[0].x, function(t) { + return b(t) + ""; + }) : function() { + return 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(""); + }); + } + var d3_interpolate_numberA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g, d3_interpolate_numberB = new RegExp(d3_interpolate_numberA.source, "g"); + d3.interpolate = d3_interpolate; + function d3_interpolate(a, b) { + var i = d3.interpolators.length, f; + while (--i >= 0 && !(f = d3.interpolators[i](a, b))) ; + return f; + } + d3.interpolators = [ function(a, b) { + var t = typeof b; + return (t === "string" ? d3_rgb_names.has(b.toLowerCase()) || /^(#|rgb\(|hsl\()/i.test(b) ? d3_interpolateRgb : d3_interpolateString : b instanceof d3_color ? d3_interpolateRgb : Array.isArray(b) ? d3_interpolateArray : t === "object" && isNaN(b) ? d3_interpolateObject : d3_interpolateNumber)(a, b); + } ]; + d3.interpolateArray = d3_interpolateArray; + function d3_interpolateArray(a, b) { + var x = [], c = [], na = a.length, nb = b.length, n0 = Math.min(a.length, b.length), i; + for (i = 0; i < n0; ++i) x.push(d3_interpolate(a[i], b[i])); + for (;i < na; ++i) c[i] = a[i]; + for (;i < nb; ++i) c[i] = b[i]; + return function(t) { + for (i = 0; i < n0; ++i) c[i] = x[i](t); + return c; + }; + } + var d3_ease_default = function() { + return d3_identity; + }; + var d3_ease = d3.map({ + linear: d3_ease_default, + poly: d3_ease_poly, + quad: function() { + return d3_ease_quad; + }, + cubic: function() { + return d3_ease_cubic; + }, + sin: function() { + return d3_ease_sin; + }, + exp: function() { + return d3_ease_exp; + }, + circle: function() { + return d3_ease_circle; + }, + elastic: d3_ease_elastic, + back: d3_ease_back, + bounce: function() { + return d3_ease_bounce; + } + }); + var d3_ease_mode = d3.map({ + "in": d3_identity, + out: d3_ease_reverse, + "in-out": d3_ease_reflect, + "out-in": function(f) { + return d3_ease_reflect(d3_ease_reverse(f)); + } + }); + d3.ease = function(name) { + var i = name.indexOf("-"), t = i >= 0 ? name.slice(0, i) : name, m = i >= 0 ? name.slice(i + 1) : "in"; + t = d3_ease.get(t) || d3_ease_default; + m = d3_ease_mode.get(m) || d3_identity; + return d3_ease_clamp(m(t.apply(null, d3_arraySlice.call(arguments, 1)))); + }; + function d3_ease_clamp(f) { + return function(t) { + return t <= 0 ? 0 : t >= 1 ? 1 : f(t); + }; + } + function d3_ease_reverse(f) { + return function(t) { + return 1 - f(1 - t); + }; + } + function d3_ease_reflect(f) { + return function(t) { + return .5 * (t < .5 ? f(2 * t) : 2 - f(2 - 2 * t)); + }; + } + function d3_ease_quad(t) { + return t * t; + } + function d3_ease_cubic(t) { + return t * t * t; + } + function d3_ease_cubicInOut(t) { + if (t <= 0) return 0; + if (t >= 1) return 1; + var t2 = t * t, t3 = t2 * t; + return 4 * (t < .5 ? t3 : 3 * (t - t2) + t3 - .75); + } + function d3_ease_poly(e) { + return function(t) { + return Math.pow(t, e); + }; + } + function d3_ease_sin(t) { + return 1 - Math.cos(t * halfπ); + } + function d3_ease_exp(t) { + return Math.pow(2, 10 * (t - 1)); + } + function d3_ease_circle(t) { + return 1 - Math.sqrt(1 - t * t); + } + function d3_ease_elastic(a, p) { + var s; + if (arguments.length < 2) p = .45; + if (arguments.length) s = p / τ * Math.asin(1 / a); else a = 1, s = p / 4; + return function(t) { + return 1 + a * Math.pow(2, -10 * t) * Math.sin((t - s) * τ / p); + }; + } + function d3_ease_back(s) { + if (!s) s = 1.70158; + return function(t) { + return t * t * ((s + 1) * t - s); + }; + } + function d3_ease_bounce(t) { + return t < 1 / 2.75 ? 7.5625 * t * t : t < 2 / 2.75 ? 7.5625 * (t -= 1.5 / 2.75) * t + .75 : t < 2.5 / 2.75 ? 7.5625 * (t -= 2.25 / 2.75) * t + .9375 : 7.5625 * (t -= 2.625 / 2.75) * t + .984375; + } + d3.interpolateHcl = d3_interpolateHcl; + function d3_interpolateHcl(a, b) { + a = d3.hcl(a); + b = d3.hcl(b); + var ah = a.h, ac = a.c, al = a.l, bh = b.h - ah, bc = b.c - ac, bl = b.l - al; + if (isNaN(bc)) bc = 0, ac = isNaN(ac) ? b.c : ac; + if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360; + return function(t) { + return d3_hcl_lab(ah + bh * t, ac + bc * t, al + bl * t) + ""; + }; + } + d3.interpolateHsl = d3_interpolateHsl; + function d3_interpolateHsl(a, b) { + a = d3.hsl(a); + b = d3.hsl(b); + var ah = a.h, as = a.s, al = a.l, bh = b.h - ah, bs = b.s - as, bl = b.l - al; + if (isNaN(bs)) bs = 0, as = isNaN(as) ? b.s : as; + if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360; + return function(t) { + return d3_hsl_rgb(ah + bh * t, as + bs * t, al + bl * t) + ""; + }; + } + d3.interpolateLab = d3_interpolateLab; + function d3_interpolateLab(a, b) { + a = d3.lab(a); + b = d3.lab(b); + var al = a.l, aa = a.a, ab = a.b, bl = b.l - al, ba = b.a - aa, bb = b.b - ab; + return function(t) { + return d3_lab_rgb(al + bl * t, aa + ba * t, ab + bb * t) + ""; + }; + } + d3.interpolateRound = d3_interpolateRound; + function d3_interpolateRound(a, b) { + b -= a; + return function(t) { + return Math.round(a + b * t); + }; + } + d3.transform = function(string) { + var g = d3_document.createElementNS(d3.ns.prefix.svg, "g"); + return (d3.transform = function(string) { + if (string != null) { + g.setAttribute("transform", string); + var t = g.transform.baseVal.consolidate(); + } + return new d3_transform(t ? t.matrix : d3_transformIdentity); + })(string); + }; + function d3_transform(m) { + var r0 = [ m.a, m.b ], r1 = [ m.c, m.d ], kx = d3_transformNormalize(r0), kz = d3_transformDot(r0, r1), ky = d3_transformNormalize(d3_transformCombine(r1, r0, -kz)) || 0; + if (r0[0] * r1[1] < r1[0] * r0[1]) { + r0[0] *= -1; + r0[1] *= -1; + kx *= -1; + kz *= -1; + } + this.rotate = (kx ? Math.atan2(r0[1], r0[0]) : Math.atan2(-r1[0], r1[1])) * d3_degrees; + this.translate = [ m.e, m.f ]; + this.scale = [ kx, ky ]; + this.skew = ky ? Math.atan2(kz, ky) * d3_degrees : 0; + } + d3_transform.prototype.toString = function() { + return "translate(" + this.translate + ")rotate(" + this.rotate + ")skewX(" + this.skew + ")scale(" + this.scale + ")"; + }; + function d3_transformDot(a, b) { + return a[0] * b[0] + a[1] * b[1]; + } + function d3_transformNormalize(a) { + var k = Math.sqrt(d3_transformDot(a, a)); + if (k) { + a[0] /= k; + a[1] /= k; + } + return k; + } + function d3_transformCombine(a, b, k) { + a[0] += k * b[0]; + a[1] += k * b[1]; + return a; + } + var d3_transformIdentity = { + a: 1, + b: 0, + c: 0, + d: 1, + e: 0, + f: 0 + }; + d3.interpolateTransform = d3_interpolateTransform; + function d3_interpolateTransformPop(s) { + return s.length ? s.pop() + "," : ""; + } + function d3_interpolateTranslate(ta, tb, s, q) { + if (ta[0] !== tb[0] || ta[1] !== tb[1]) { + var i = s.push("translate(", null, ",", null, ")"); + q.push({ + i: i - 4, + x: d3_interpolateNumber(ta[0], tb[0]) + }, { + i: i - 2, + x: d3_interpolateNumber(ta[1], tb[1]) + }); + } else if (tb[0] || tb[1]) { + s.push("translate(" + tb + ")"); + } + } + function d3_interpolateRotate(ra, rb, s, q) { + if (ra !== rb) { + if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360; + q.push({ + i: s.push(d3_interpolateTransformPop(s) + "rotate(", null, ")") - 2, + x: d3_interpolateNumber(ra, rb) + }); + } else if (rb) { + s.push(d3_interpolateTransformPop(s) + "rotate(" + rb + ")"); + } + } + function d3_interpolateSkew(wa, wb, s, q) { + if (wa !== wb) { + q.push({ + i: s.push(d3_interpolateTransformPop(s) + "skewX(", null, ")") - 2, + x: d3_interpolateNumber(wa, wb) + }); + } else if (wb) { + s.push(d3_interpolateTransformPop(s) + "skewX(" + wb + ")"); + } + } + function d3_interpolateScale(ka, kb, s, q) { + if (ka[0] !== kb[0] || ka[1] !== kb[1]) { + var i = s.push(d3_interpolateTransformPop(s) + "scale(", null, ",", null, ")"); + q.push({ + i: i - 4, + x: d3_interpolateNumber(ka[0], kb[0]) + }, { + i: i - 2, + x: d3_interpolateNumber(ka[1], kb[1]) + }); + } else if (kb[0] !== 1 || kb[1] !== 1) { + s.push(d3_interpolateTransformPop(s) + "scale(" + kb + ")"); + } + } + function d3_interpolateTransform(a, b) { + var s = [], q = []; + a = d3.transform(a), b = d3.transform(b); + d3_interpolateTranslate(a.translate, b.translate, s, q); + d3_interpolateRotate(a.rotate, b.rotate, s, q); + d3_interpolateSkew(a.skew, b.skew, s, q); + d3_interpolateScale(a.scale, b.scale, s, q); + a = b = null; + return function(t) { + var i = -1, n = q.length, o; + while (++i < n) s[(o = q[i]).i] = o.x(t); + return s.join(""); + }; + } + function d3_uninterpolateNumber(a, b) { + b = (b -= a = +a) || 1 / b; + return function(x) { + return (x - a) / b; + }; + } + function d3_uninterpolateClamp(a, b) { + b = (b -= a = +a) || 1 / b; + return function(x) { + return Math.max(0, Math.min(1, (x - a) / b)); + }; + } + d3.layout = {}; + d3.layout.bundle = function() { + return function(links) { + var paths = [], i = -1, n = links.length; + while (++i < n) paths.push(d3_layout_bundlePath(links[i])); + return paths; + }; + }; + function d3_layout_bundlePath(link) { + var start = link.source, end = link.target, lca = d3_layout_bundleLeastCommonAncestor(start, end), points = [ start ]; + while (start !== lca) { + start = start.parent; + points.push(start); + } + var k = points.length; + while (end !== lca) { + points.splice(k, 0, end); + end = end.parent; + } + return points; + } + function d3_layout_bundleAncestors(node) { + var ancestors = [], parent = node.parent; + while (parent != null) { + ancestors.push(node); + node = parent; + parent = parent.parent; + } + ancestors.push(node); + return ancestors; + } + function d3_layout_bundleLeastCommonAncestor(a, b) { + if (a === b) return a; + var aNodes = d3_layout_bundleAncestors(a), bNodes = d3_layout_bundleAncestors(b), aNode = aNodes.pop(), bNode = bNodes.pop(), sharedNode = null; + while (aNode === bNode) { + sharedNode = aNode; + aNode = aNodes.pop(); + bNode = bNodes.pop(); + } + return sharedNode; + } + d3.layout.chord = function() { + var chord = {}, chords, groups, matrix, n, padding = 0, sortGroups, sortSubgroups, sortChords; + function relayout() { + var subgroups = {}, groupSums = [], groupIndex = d3.range(n), subgroupIndex = [], k, x, x0, i, j; + chords = []; + groups = []; + k = 0, i = -1; + while (++i < n) { + x = 0, j = -1; + while (++j < n) { + x += matrix[i][j]; + } + groupSums.push(x); + subgroupIndex.push(d3.range(n)); + k += x; + } + if (sortGroups) { + groupIndex.sort(function(a, b) { + return sortGroups(groupSums[a], groupSums[b]); + }); + } + if (sortSubgroups) { + subgroupIndex.forEach(function(d, i) { + d.sort(function(a, b) { + return sortSubgroups(matrix[i][a], matrix[i][b]); + }); + }); + } + k = (τ - padding * n) / k; + 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[di + "-" + dj] = { + index: di, + subindex: dj, + startAngle: a0, + endAngle: a1, + value: v + }; + } + groups[di] = { + index: di, + startAngle: x0, + endAngle: x, + value: (x - x0) / k + }; + x += padding; + } + i = -1; + while (++i < n) { + j = i - 1; + while (++j < n) { + var source = subgroups[i + "-" + j], target = subgroups[j + "-" + i]; + if (source.value || target.value) { + chords.push(source.value < target.value ? { + source: target, + target: source + } : { + source: source, + target: target + }); + } + } + } + if (sortChords) resort(); + } + function resort() { + chords.sort(function(a, b) { + return sortChords((a.source.value + a.target.value) / 2, (b.source.value + b.target.value) / 2); + }); + } + chord.matrix = function(x) { + if (!arguments.length) return matrix; + n = (matrix = x) && matrix.length; + chords = groups = null; + return chord; + }; + chord.padding = function(x) { + if (!arguments.length) return padding; + padding = x; + chords = groups = null; + return chord; + }; + chord.sortGroups = function(x) { + if (!arguments.length) return sortGroups; + sortGroups = x; + chords = groups = null; + return chord; + }; + chord.sortSubgroups = function(x) { + if (!arguments.length) return sortSubgroups; + sortSubgroups = x; + chords = null; + return chord; + }; + chord.sortChords = function(x) { + if (!arguments.length) return sortChords; + sortChords = x; + if (chords) resort(); + return chord; + }; + chord.chords = function() { + if (!chords) relayout(); + return chords; + }; + chord.groups = function() { + if (!groups) relayout(); + return groups; + }; + return chord; + }; + d3.layout.force = function() { + var force = {}, event = d3.dispatch("start", "tick", "end"), timer, size = [ 1, 1 ], drag, alpha, friction = .9, linkDistance = d3_layout_forceLinkDistance, linkStrength = d3_layout_forceLinkStrength, charge = -30, chargeDistance2 = d3_layout_forceChargeDistance2, gravity = .1, theta2 = .64, nodes = [], links = [], distances, strengths, charges; + function repulse(node) { + return function(quad, x1, _, x2) { + if (quad.point !== node) { + var dx = quad.cx - node.x, dy = quad.cy - node.y, dw = x2 - x1, dn = dx * dx + dy * dy; + if (dw * dw / theta2 < dn) { + if (dn < chargeDistance2) { + var k = quad.charge / dn; + node.px -= dx * k; + node.py -= dy * k; + } + return true; + } + if (quad.point && dn && dn < chargeDistance2) { + var k = quad.pointCharge / dn; + node.px -= dx * k; + node.py -= dy * k; + } + } + return !quad.charge; + }; + } + force.tick = function() { + if ((alpha *= .99) < .005) { + timer = null; + event.end({ + type: "end", + alpha: alpha = 0 + }); + return true; + } + var n = nodes.length, m = links.length, q, i, o, s, t, l, k, x, y; + for (i = 0; i < m; ++i) { + o = links[i]; + s = o.source; + t = o.target; + x = t.x - s.x; + y = t.y - s.y; + if (l = x * x + y * y) { + l = alpha * strengths[i] * ((l = Math.sqrt(l)) - distances[i]) / l; + x *= l; + y *= l; + t.x -= x * (k = s.weight + t.weight ? s.weight / (s.weight + t.weight) : .5); + t.y -= y * k; + s.x += x * (k = 1 - k); + s.y += y * k; + } + } + if (k = alpha * gravity) { + x = size[0] / 2; + y = size[1] / 2; + i = -1; + if (k) while (++i < n) { + o = nodes[i]; + o.x += (x - o.x) * k; + o.y += (y - o.y) * k; + } + } + if (charge) { + d3_layout_forceAccumulate(q = d3.geom.quadtree(nodes), alpha, charges); + i = -1; + while (++i < n) { + if (!(o = nodes[i]).fixed) { + q.visit(repulse(o)); + } + } + } + i = -1; + while (++i < n) { + o = nodes[i]; + if (o.fixed) { + o.x = o.px; + o.y = o.py; + } else { + o.x -= (o.px - (o.px = o.x)) * friction; + o.y -= (o.py - (o.py = o.y)) * friction; + } + } + event.tick({ + type: "tick", + alpha: alpha + }); + }; + force.nodes = function(x) { + if (!arguments.length) return nodes; + nodes = x; + return force; + }; + force.links = function(x) { + if (!arguments.length) return links; + links = x; + return force; + }; + force.size = function(x) { + if (!arguments.length) return size; + size = x; + return force; + }; + force.linkDistance = function(x) { + if (!arguments.length) return linkDistance; + linkDistance = typeof x === "function" ? x : +x; + return force; + }; + force.distance = force.linkDistance; + force.linkStrength = function(x) { + if (!arguments.length) return linkStrength; + linkStrength = typeof x === "function" ? x : +x; + return force; + }; + force.friction = function(x) { + if (!arguments.length) return friction; + friction = +x; + return force; + }; + force.charge = function(x) { + if (!arguments.length) return charge; + charge = typeof x === "function" ? x : +x; + return force; + }; + force.chargeDistance = function(x) { + if (!arguments.length) return Math.sqrt(chargeDistance2); + chargeDistance2 = x * x; + return force; + }; + force.gravity = function(x) { + if (!arguments.length) return gravity; + gravity = +x; + return force; + }; + force.theta = function(x) { + if (!arguments.length) return Math.sqrt(theta2); + theta2 = x * x; + return force; + }; + force.alpha = function(x) { + if (!arguments.length) return alpha; + x = +x; + if (alpha) { + if (x > 0) { + alpha = x; + } else { + timer.c = null, timer.t = NaN, timer = null; + event.start({ + type: "end", + alpha: alpha = 0 + }); + } + } else if (x > 0) { + event.start({ + type: "start", + alpha: alpha = x + }); + timer = d3_timer(force.tick); + } + return force; + }; + force.start = function() { + var i, n = nodes.length, m = links.length, w = size[0], h = size[1], neighbors, o; + for (i = 0; i < n; ++i) { + (o = nodes[i]).index = i; + o.weight = 0; + } + for (i = 0; i < m; ++i) { + o = links[i]; + if (typeof o.source == "number") o.source = nodes[o.source]; + if (typeof o.target == "number") o.target = nodes[o.target]; + ++o.source.weight; + ++o.target.weight; + } + for (i = 0; i < n; ++i) { + o = nodes[i]; + if (isNaN(o.x)) o.x = position("x", w); + if (isNaN(o.y)) o.y = position("y", h); + if (isNaN(o.px)) o.px = o.x; + if (isNaN(o.py)) o.py = o.y; + } + distances = []; + if (typeof linkDistance === "function") for (i = 0; i < m; ++i) distances[i] = +linkDistance.call(this, links[i], i); else for (i = 0; i < m; ++i) distances[i] = linkDistance; + strengths = []; + if (typeof linkStrength === "function") for (i = 0; i < m; ++i) strengths[i] = +linkStrength.call(this, links[i], i); else for (i = 0; i < m; ++i) strengths[i] = linkStrength; + charges = []; + if (typeof charge === "function") for (i = 0; i < n; ++i) charges[i] = +charge.call(this, nodes[i], i); else for (i = 0; i < n; ++i) charges[i] = charge; + function position(dimension, size) { + if (!neighbors) { + neighbors = new Array(n); + for (j = 0; j < n; ++j) { + neighbors[j] = []; + } + for (j = 0; j < m; ++j) { + var o = links[j]; + neighbors[o.source.index].push(o.target); + neighbors[o.target.index].push(o.source); + } + } + var candidates = neighbors[i], j = -1, l = candidates.length, x; + while (++j < l) if (!isNaN(x = candidates[j][dimension])) return x; + return Math.random() * size; + } + return force.resume(); + }; + force.resume = function() { + return force.alpha(.1); + }; + force.stop = function() { + return force.alpha(0); + }; + force.drag = function() { + if (!drag) drag = d3.behavior.drag().origin(d3_identity).on("dragstart.force", d3_layout_forceDragstart).on("drag.force", dragmove).on("dragend.force", d3_layout_forceDragend); + if (!arguments.length) return drag; + this.on("mouseover.force", d3_layout_forceMouseover).on("mouseout.force", d3_layout_forceMouseout).call(drag); + }; + function dragmove(d) { + d.px = d3.event.x, d.py = d3.event.y; + force.resume(); + } + return d3.rebind(force, event, "on"); + }; + function d3_layout_forceDragstart(d) { + d.fixed |= 2; + } + function d3_layout_forceDragend(d) { + d.fixed &= ~6; + } + function d3_layout_forceMouseover(d) { + d.fixed |= 4; + d.px = d.x, d.py = d.y; + } + function d3_layout_forceMouseout(d) { + d.fixed &= ~4; + } + function d3_layout_forceAccumulate(quad, alpha, charges) { + var cx = 0, cy = 0; + quad.charge = 0; + if (!quad.leaf) { + var nodes = quad.nodes, n = nodes.length, i = -1, c; + while (++i < n) { + c = nodes[i]; + if (c == null) continue; + d3_layout_forceAccumulate(c, alpha, charges); + quad.charge += c.charge; + cx += c.charge * c.cx; + cy += c.charge * c.cy; + } + } + if (quad.point) { + if (!quad.leaf) { + quad.point.x += Math.random() - .5; + quad.point.y += Math.random() - .5; + } + var k = alpha * charges[quad.point.index]; + quad.charge += quad.pointCharge = k; + cx += k * quad.point.x; + cy += k * quad.point.y; + } + quad.cx = cx / quad.charge; + quad.cy = cy / quad.charge; + } + var d3_layout_forceLinkDistance = 20, d3_layout_forceLinkStrength = 1, d3_layout_forceChargeDistance2 = Infinity; + d3.layout.hierarchy = function() { + var sort = d3_layout_hierarchySort, children = d3_layout_hierarchyChildren, value = d3_layout_hierarchyValue; + function hierarchy(root) { + var stack = [ root ], nodes = [], node; + root.depth = 0; + while ((node = stack.pop()) != null) { + nodes.push(node); + if ((childs = children.call(hierarchy, node, node.depth)) && (n = childs.length)) { + var n, childs, child; + while (--n >= 0) { + stack.push(child = childs[n]); + child.parent = node; + child.depth = node.depth + 1; + } + if (value) node.value = 0; + node.children = childs; + } else { + if (value) node.value = +value.call(hierarchy, node, node.depth) || 0; + delete node.children; + } + } + d3_layout_hierarchyVisitAfter(root, function(node) { + var childs, parent; + if (sort && (childs = node.children)) childs.sort(sort); + if (value && (parent = node.parent)) parent.value += node.value; + }); + return nodes; + } + hierarchy.sort = function(x) { + if (!arguments.length) return sort; + sort = x; + return hierarchy; + }; + hierarchy.children = function(x) { + if (!arguments.length) return children; + children = x; + return hierarchy; + }; + hierarchy.value = function(x) { + if (!arguments.length) return value; + value = x; + return hierarchy; + }; + hierarchy.revalue = function(root) { + if (value) { + d3_layout_hierarchyVisitBefore(root, function(node) { + if (node.children) node.value = 0; + }); + d3_layout_hierarchyVisitAfter(root, function(node) { + var parent; + if (!node.children) node.value = +value.call(hierarchy, node, node.depth) || 0; + if (parent = node.parent) parent.value += node.value; + }); + } + return root; + }; + return hierarchy; + }; + function d3_layout_hierarchyRebind(object, hierarchy) { + d3.rebind(object, hierarchy, "sort", "children", "value"); + object.nodes = object; + object.links = d3_layout_hierarchyLinks; + return object; + } + function d3_layout_hierarchyVisitBefore(node, callback) { + var nodes = [ node ]; + while ((node = nodes.pop()) != null) { + callback(node); + if ((children = node.children) && (n = children.length)) { + var n, children; + while (--n >= 0) nodes.push(children[n]); + } + } + } + function d3_layout_hierarchyVisitAfter(node, callback) { + var nodes = [ node ], nodes2 = []; + while ((node = nodes.pop()) != null) { + nodes2.push(node); + if ((children = node.children) && (n = children.length)) { + var i = -1, n, children; + while (++i < n) nodes.push(children[i]); + } + } + while ((node = nodes2.pop()) != null) { + callback(node); + } + } + function d3_layout_hierarchyChildren(d) { + return d.children; + } + function d3_layout_hierarchyValue(d) { + return d.value; + } + function d3_layout_hierarchySort(a, b) { + return b.value - a.value; + } + function d3_layout_hierarchyLinks(nodes) { + return d3.merge(nodes.map(function(parent) { + return (parent.children || []).map(function(child) { + return { + source: parent, + target: child + }; + }); + })); + } + d3.layout.partition = function() { + var hierarchy = d3.layout.hierarchy(), size = [ 1, 1 ]; + function position(node, x, dx, dy) { + var children = node.children; + node.x = x; + node.y = node.depth * dy; + node.dx = dx; + node.dy = dy; + if (children && (n = children.length)) { + var i = -1, n, c, d; + dx = node.value ? dx / node.value : 0; + while (++i < n) { + position(c = children[i], x, d = c.value * dx, dy); + x += d; + } + } + } + function depth(node) { + var children = node.children, d = 0; + if (children && (n = children.length)) { + var i = -1, n; + while (++i < n) d = Math.max(d, depth(children[i])); + } + return 1 + d; + } + function partition(d, i) { + var nodes = hierarchy.call(this, d, i); + position(nodes[0], 0, size[0], size[1] / depth(nodes[0])); + return nodes; + } + partition.size = function(x) { + if (!arguments.length) return size; + size = x; + return partition; + }; + return d3_layout_hierarchyRebind(partition, hierarchy); + }; + d3.layout.pie = function() { + var value = Number, sort = d3_layout_pieSortByValue, startAngle = 0, endAngle = τ, padAngle = 0; + function pie(data) { + var n = data.length, values = data.map(function(d, i) { + return +value.call(pie, d, i); + }), a = +(typeof startAngle === "function" ? startAngle.apply(this, arguments) : startAngle), da = (typeof endAngle === "function" ? endAngle.apply(this, arguments) : endAngle) - a, p = Math.min(Math.abs(da) / n, +(typeof padAngle === "function" ? padAngle.apply(this, arguments) : padAngle)), pa = p * (da < 0 ? -1 : 1), sum = d3.sum(values), k = sum ? (da - n * pa) / sum : 0, index = d3.range(n), arcs = [], v; + if (sort != null) index.sort(sort === d3_layout_pieSortByValue ? function(i, j) { + return values[j] - values[i]; + } : function(i, j) { + return sort(data[i], data[j]); + }); + index.forEach(function(i) { + arcs[i] = { + data: data[i], + value: v = values[i], + startAngle: a, + endAngle: a += v * k + pa, + padAngle: p + }; + }); + return arcs; + } + pie.value = function(_) { + if (!arguments.length) return value; + value = _; + return pie; + }; + pie.sort = function(_) { + if (!arguments.length) return sort; + sort = _; + return pie; + }; + pie.startAngle = function(_) { + if (!arguments.length) return startAngle; + startAngle = _; + return pie; + }; + pie.endAngle = function(_) { + if (!arguments.length) return endAngle; + endAngle = _; + return pie; + }; + pie.padAngle = function(_) { + if (!arguments.length) return padAngle; + padAngle = _; + return pie; + }; + return pie; + }; + var d3_layout_pieSortByValue = {}; + d3.layout.stack = function() { + var values = d3_identity, order = d3_layout_stackOrderDefault, offset = d3_layout_stackOffsetZero, out = d3_layout_stackOut, x = d3_layout_stackX, y = d3_layout_stackY; + function stack(data, index) { + if (!(n = data.length)) return data; + var series = data.map(function(d, i) { + return values.call(stack, d, i); + }); + var points = series.map(function(d) { + return d.map(function(v, i) { + return [ x.call(stack, v, i), y.call(stack, v, i) ]; + }); + }); + var orders = order.call(stack, points, index); + series = d3.permute(series, orders); + points = d3.permute(points, orders); + var offsets = offset.call(stack, points, index); + var m = series[0].length, n, i, j, o; + for (j = 0; j < m; ++j) { + out.call(stack, series[0][j], o = offsets[j], points[0][j][1]); + for (i = 1; i < n; ++i) { + out.call(stack, series[i][j], o += points[i - 1][j][1], points[i][j][1]); + } + } + return data; + } + stack.values = function(x) { + if (!arguments.length) return values; + values = x; + return stack; + }; + stack.order = function(x) { + if (!arguments.length) return order; + order = typeof x === "function" ? x : d3_layout_stackOrders.get(x) || d3_layout_stackOrderDefault; + return stack; + }; + stack.offset = function(x) { + if (!arguments.length) return offset; + offset = typeof x === "function" ? x : d3_layout_stackOffsets.get(x) || d3_layout_stackOffsetZero; + return stack; + }; + stack.x = function(z) { + if (!arguments.length) return x; + x = z; + return stack; + }; + stack.y = function(z) { + if (!arguments.length) return y; + y = z; + return stack; + }; + stack.out = function(z) { + if (!arguments.length) return out; + out = z; + return stack; + }; + return stack; + }; + function d3_layout_stackX(d) { + return d.x; + } + function d3_layout_stackY(d) { + return d.y; + } + function d3_layout_stackOut(d, y0, y) { + d.y0 = y0; + d.y = y; + } + var d3_layout_stackOrders = d3.map({ + "inside-out": function(data) { + var n = data.length, i, j, max = data.map(d3_layout_stackMaxIndex), sums = data.map(d3_layout_stackReduceSum), index = d3.range(n).sort(function(a, b) { + return max[a] - max[b]; + }), top = 0, bottom = 0, tops = [], bottoms = []; + for (i = 0; i < n; ++i) { + j = index[i]; + if (top < bottom) { + top += sums[j]; + tops.push(j); + } else { + bottom += sums[j]; + bottoms.push(j); + } + } + return bottoms.reverse().concat(tops); + }, + reverse: function(data) { + return d3.range(data.length).reverse(); + }, + "default": d3_layout_stackOrderDefault + }); + var d3_layout_stackOffsets = d3.map({ + silhouette: function(data) { + var n = data.length, m = data[0].length, sums = [], max = 0, i, j, o, y0 = []; + for (j = 0; j < m; ++j) { + for (i = 0, o = 0; i < n; i++) o += data[i][j][1]; + if (o > max) max = o; + sums.push(o); + } + for (j = 0; j < m; ++j) { + y0[j] = (max - sums[j]) / 2; + } + return y0; + }, + wiggle: function(data) { + var n = data.length, x = data[0], m = x.length, i, j, k, s1, s2, s3, dx, o, o0, y0 = []; + y0[0] = o = o0 = 0; + for (j = 1; j < m; ++j) { + for (i = 0, s1 = 0; i < n; ++i) s1 += data[i][j][1]; + for (i = 0, s2 = 0, dx = x[j][0] - x[j - 1][0]; i < n; ++i) { + for (k = 0, s3 = (data[i][j][1] - data[i][j - 1][1]) / (2 * dx); k < i; ++k) { + s3 += (data[k][j][1] - data[k][j - 1][1]) / dx; + } + s2 += s3 * data[i][j][1]; + } + y0[j] = o -= s1 ? s2 / s1 * dx : 0; + if (o < o0) o0 = o; + } + for (j = 0; j < m; ++j) y0[j] -= o0; + return y0; + }, + expand: function(data) { + var n = data.length, m = data[0].length, k = 1 / n, i, j, o, y0 = []; + for (j = 0; j < m; ++j) { + for (i = 0, o = 0; i < n; i++) o += data[i][j][1]; + if (o) for (i = 0; i < n; i++) data[i][j][1] /= o; else for (i = 0; i < n; i++) data[i][j][1] = k; + } + for (j = 0; j < m; ++j) y0[j] = 0; + return y0; + }, + zero: d3_layout_stackOffsetZero + }); + function d3_layout_stackOrderDefault(data) { + return d3.range(data.length); + } + function d3_layout_stackOffsetZero(data) { + var j = -1, m = data[0].length, y0 = []; + while (++j < m) y0[j] = 0; + return y0; + } + function d3_layout_stackMaxIndex(array) { + var i = 1, j = 0, v = array[0][1], k, n = array.length; + for (;i < n; ++i) { + if ((k = array[i][1]) > v) { + j = i; + v = k; + } + } + return j; + } + function d3_layout_stackReduceSum(d) { + return d.reduce(d3_layout_stackSum, 0); + } + function d3_layout_stackSum(p, d) { + return p + d[1]; + } + d3.layout.histogram = function() { + var frequency = true, valuer = Number, ranger = d3_layout_histogramRange, binner = d3_layout_histogramBinSturges; + function histogram(data, i) { + var bins = [], values = data.map(valuer, this), range = ranger.call(this, values, i), thresholds = binner.call(this, range, values, i), bin, i = -1, n = values.length, m = thresholds.length - 1, k = frequency ? 1 : 1 / n, x; + while (++i < m) { + bin = bins[i] = []; + bin.dx = thresholds[i + 1] - (bin.x = thresholds[i]); + bin.y = 0; + } + if (m > 0) { + i = -1; + while (++i < n) { + x = values[i]; + if (x >= range[0] && x <= range[1]) { + bin = bins[d3.bisect(thresholds, x, 1, m) - 1]; + bin.y += k; + bin.push(data[i]); + } + } + } + return bins; + } + histogram.value = function(x) { + if (!arguments.length) return valuer; + valuer = x; + return histogram; + }; + histogram.range = function(x) { + if (!arguments.length) return ranger; + ranger = d3_functor(x); + return histogram; + }; + histogram.bins = function(x) { + if (!arguments.length) return binner; + binner = typeof x === "number" ? function(range) { + return d3_layout_histogramBinFixed(range, x); + } : d3_functor(x); + return histogram; + }; + histogram.frequency = function(x) { + if (!arguments.length) return frequency; + frequency = !!x; + return histogram; + }; + return histogram; + }; + function d3_layout_histogramBinSturges(range, values) { + return d3_layout_histogramBinFixed(range, Math.ceil(Math.log(values.length) / Math.LN2 + 1)); + } + function d3_layout_histogramBinFixed(range, n) { + var x = -1, b = +range[0], m = (range[1] - b) / n, f = []; + while (++x <= n) f[x] = m * x + b; + return f; + } + function d3_layout_histogramRange(values) { + return [ d3.min(values), d3.max(values) ]; + } + d3.layout.pack = function() { + var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort), padding = 0, size = [ 1, 1 ], radius; + function pack(d, i) { + var nodes = hierarchy.call(this, d, i), root = nodes[0], w = size[0], h = size[1], r = radius == null ? Math.sqrt : typeof radius === "function" ? radius : function() { + return radius; + }; + root.x = root.y = 0; + d3_layout_hierarchyVisitAfter(root, function(d) { + d.r = +r(d.value); + }); + d3_layout_hierarchyVisitAfter(root, d3_layout_packSiblings); + if (padding) { + var dr = padding * (radius ? 1 : Math.max(2 * root.r / w, 2 * root.r / h)) / 2; + d3_layout_hierarchyVisitAfter(root, function(d) { + d.r += dr; + }); + d3_layout_hierarchyVisitAfter(root, d3_layout_packSiblings); + d3_layout_hierarchyVisitAfter(root, function(d) { + d.r -= dr; + }); + } + d3_layout_packTransform(root, w / 2, h / 2, radius ? 1 : 1 / Math.max(2 * root.r / w, 2 * root.r / h)); + return nodes; + } + pack.size = function(_) { + if (!arguments.length) return size; + size = _; + return pack; + }; + pack.radius = function(_) { + if (!arguments.length) return radius; + radius = _ == null || typeof _ === "function" ? _ : +_; + return pack; + }; + pack.padding = function(_) { + if (!arguments.length) return padding; + padding = +_; + return pack; + }; + return d3_layout_hierarchyRebind(pack, hierarchy); + }; + function d3_layout_packSort(a, b) { + return a.value - b.value; + } + function d3_layout_packInsert(a, b) { + var c = a._pack_next; + a._pack_next = b; + b._pack_prev = a; + b._pack_next = c; + c._pack_prev = b; + } + function d3_layout_packSplice(a, b) { + a._pack_next = b; + b._pack_prev = a; + } + function d3_layout_packIntersects(a, b) { + var dx = b.x - a.x, dy = b.y - a.y, dr = a.r + b.r; + return .999 * dr * dr > dx * dx + dy * dy; + } + function d3_layout_packSiblings(node) { + if (!(nodes = node.children) || !(n = nodes.length)) return; + var nodes, xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity, a, b, c, i, j, k, n; + function bound(node) { + xMin = Math.min(node.x - node.r, xMin); + xMax = Math.max(node.x + node.r, xMax); + yMin = Math.min(node.y - node.r, yMin); + yMax = Math.max(node.y + node.r, yMax); + } + nodes.forEach(d3_layout_packLink); + a = nodes[0]; + a.x = -a.r; + a.y = 0; + bound(a); + if (n > 1) { + b = nodes[1]; + b.x = b.r; + b.y = 0; + bound(b); + if (n > 2) { + c = nodes[2]; + d3_layout_packPlace(a, b, c); + bound(c); + d3_layout_packInsert(a, c); + a._pack_prev = c; + d3_layout_packInsert(c, b); + b = a._pack_next; + for (i = 3; i < n; i++) { + d3_layout_packPlace(a, b, c = nodes[i]); + var isect = 0, s1 = 1, s2 = 1; + for (j = b._pack_next; j !== b; j = j._pack_next, s1++) { + if (d3_layout_packIntersects(j, c)) { + isect = 1; + break; + } + } + if (isect == 1) { + for (k = a._pack_prev; k !== j._pack_prev; k = k._pack_prev, s2++) { + if (d3_layout_packIntersects(k, c)) { + break; + } + } + } + if (isect) { + if (s1 < s2 || s1 == s2 && b.r < a.r) d3_layout_packSplice(a, b = j); else d3_layout_packSplice(a = k, b); + i--; + } else { + d3_layout_packInsert(a, c); + b = c; + bound(c); + } + } + } + } + var cx = (xMin + xMax) / 2, cy = (yMin + yMax) / 2, cr = 0; + for (i = 0; i < n; i++) { + c = nodes[i]; + c.x -= cx; + c.y -= cy; + cr = Math.max(cr, c.r + Math.sqrt(c.x * c.x + c.y * c.y)); + } + node.r = cr; + nodes.forEach(d3_layout_packUnlink); + } + function d3_layout_packLink(node) { + node._pack_next = node._pack_prev = node; + } + function d3_layout_packUnlink(node) { + delete node._pack_next; + delete node._pack_prev; + } + function d3_layout_packTransform(node, x, y, k) { + var children = node.children; + node.x = x += k * node.x; + node.y = y += k * node.y; + node.r *= k; + if (children) { + var i = -1, n = children.length; + while (++i < n) d3_layout_packTransform(children[i], x, y, k); + } + } + function d3_layout_packPlace(a, b, c) { + var db = a.r + c.r, dx = b.x - a.x, dy = b.y - a.y; + if (db && (dx || dy)) { + var da = b.r + c.r, dc = dx * dx + dy * dy; + da *= da; + db *= db; + var x = .5 + (db - da) / (2 * dc), y = Math.sqrt(Math.max(0, 2 * da * (db + dc) - (db -= dc) * db - da * da)) / (2 * dc); + c.x = a.x + x * dx + y * dy; + c.y = a.y + x * dy - y * dx; + } else { + c.x = a.x + db; + c.y = a.y; + } + } + d3.layout.tree = function() { + var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = null; + function tree(d, i) { + var nodes = hierarchy.call(this, d, i), root0 = nodes[0], root1 = wrapTree(root0); + d3_layout_hierarchyVisitAfter(root1, firstWalk), root1.parent.m = -root1.z; + d3_layout_hierarchyVisitBefore(root1, secondWalk); + if (nodeSize) d3_layout_hierarchyVisitBefore(root0, sizeNode); else { + var left = root0, right = root0, bottom = root0; + d3_layout_hierarchyVisitBefore(root0, function(node) { + if (node.x < left.x) left = node; + if (node.x > right.x) right = node; + if (node.depth > bottom.depth) bottom = node; + }); + var tx = separation(left, right) / 2 - left.x, kx = size[0] / (right.x + separation(right, left) / 2 + tx), ky = size[1] / (bottom.depth || 1); + d3_layout_hierarchyVisitBefore(root0, function(node) { + node.x = (node.x + tx) * kx; + node.y = node.depth * ky; + }); + } + return nodes; + } + function wrapTree(root0) { + var root1 = { + A: null, + children: [ root0 ] + }, queue = [ root1 ], node1; + while ((node1 = queue.pop()) != null) { + for (var children = node1.children, child, i = 0, n = children.length; i < n; ++i) { + queue.push((children[i] = child = { + _: children[i], + parent: node1, + children: (child = children[i].children) && child.slice() || [], + A: null, + a: null, + z: 0, + m: 0, + c: 0, + s: 0, + t: null, + i: i + }).a = child); + } + } + return root1.children[0]; + } + function firstWalk(v) { + var children = v.children, siblings = v.parent.children, w = v.i ? siblings[v.i - 1] : null; + if (children.length) { + d3_layout_treeShift(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]); + } + function secondWalk(v) { + v._.x = v.z + v.parent.m; + v.m += v.parent.m; + } + 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 = d3_layout_treeRight(vim), vip = d3_layout_treeLeft(vip), vim && vip) { + vom = d3_layout_treeLeft(vom); + vop = d3_layout_treeRight(vop); + vop.a = v; + shift = vim.z + sim - vip.z - sip + separation(vim._, vip._); + if (shift > 0) { + d3_layout_treeMove(d3_layout_treeAncestor(vim, v, ancestor), v, shift); + sip += shift; + sop += shift; + } + sim += vim.m; + sip += vip.m; + som += vom.m; + sop += vop.m; + } + if (vim && !d3_layout_treeRight(vop)) { + vop.t = vim; + vop.m += sim - sop; + } + if (vip && !d3_layout_treeLeft(vom)) { + vom.t = vip; + vom.m += sip - som; + ancestor = v; + } + } + return ancestor; + } + function sizeNode(node) { + node.x *= size[0]; + node.y = node.depth * size[1]; + } + tree.separation = function(x) { + if (!arguments.length) return separation; + separation = x; + return tree; + }; + tree.size = function(x) { + if (!arguments.length) return nodeSize ? null : size; + nodeSize = (size = x) == null ? sizeNode : null; + return tree; + }; + tree.nodeSize = function(x) { + if (!arguments.length) return nodeSize ? size : null; + nodeSize = (size = x) == null ? null : sizeNode; + return tree; + }; + return d3_layout_hierarchyRebind(tree, hierarchy); + }; + function d3_layout_treeSeparation(a, b) { + return a.parent == b.parent ? 1 : 2; + } + function d3_layout_treeLeft(v) { + var children = v.children; + return children.length ? children[0] : v.t; + } + function d3_layout_treeRight(v) { + var children = v.children, n; + return (n = children.length) ? children[n - 1] : v.t; + } + function d3_layout_treeMove(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; + } + function d3_layout_treeShift(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); + } + } + function d3_layout_treeAncestor(vim, v, ancestor) { + return vim.a.parent === v.parent ? vim.a : ancestor; + } + d3.layout.cluster = function() { + var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = false; + function cluster(d, i) { + var nodes = hierarchy.call(this, d, i), root = nodes[0], previousNode, x = 0; + d3_layout_hierarchyVisitAfter(root, function(node) { + var children = node.children; + if (children && children.length) { + node.x = d3_layout_clusterX(children); + node.y = d3_layout_clusterY(children); + } else { + node.x = previousNode ? x += separation(node, previousNode) : 0; + node.y = 0; + previousNode = node; + } + }); + var left = d3_layout_clusterLeft(root), right = d3_layout_clusterRight(root), x0 = left.x - separation(left, right) / 2, x1 = right.x + separation(right, left) / 2; + d3_layout_hierarchyVisitAfter(root, nodeSize ? function(node) { + node.x = (node.x - root.x) * size[0]; + node.y = (root.y - node.y) * size[1]; + } : function(node) { + node.x = (node.x - x0) / (x1 - x0) * size[0]; + node.y = (1 - (root.y ? node.y / root.y : 1)) * size[1]; + }); + return nodes; + } + cluster.separation = function(x) { + if (!arguments.length) return separation; + separation = x; + return cluster; + }; + cluster.size = function(x) { + if (!arguments.length) return nodeSize ? null : size; + nodeSize = (size = x) == null; + return cluster; + }; + cluster.nodeSize = function(x) { + if (!arguments.length) return nodeSize ? size : null; + nodeSize = (size = x) != null; + return cluster; + }; + return d3_layout_hierarchyRebind(cluster, hierarchy); + }; + function d3_layout_clusterY(children) { + return 1 + d3.max(children, function(child) { + return child.y; + }); + } + function d3_layout_clusterX(children) { + return children.reduce(function(x, child) { + return x + child.x; + }, 0) / children.length; + } + function d3_layout_clusterLeft(node) { + var children = node.children; + return children && children.length ? d3_layout_clusterLeft(children[0]) : node; + } + function d3_layout_clusterRight(node) { + var children = node.children, n; + return children && (n = children.length) ? d3_layout_clusterRight(children[n - 1]) : node; + } + d3.layout.treemap = function() { + var hierarchy = d3.layout.hierarchy(), round = Math.round, size = [ 1, 1 ], padding = null, pad = d3_layout_treemapPadNull, sticky = false, stickies, mode = "squarify", ratio = .5 * (1 + Math.sqrt(5)); + function scale(children, k) { + var i = -1, n = children.length, child, area; + while (++i < n) { + area = (child = children[i]).value * (k < 0 ? 0 : k); + child.area = isNaN(area) || area <= 0 ? 0 : area; + } + } + function squarify(node) { + var children = node.children; + if (children && children.length) { + var rect = pad(node), row = [], remaining = children.slice(), child, best = Infinity, score, u = mode === "slice" ? rect.dx : mode === "dice" ? rect.dy : mode === "slice-dice" ? node.depth & 1 ? rect.dy : rect.dx : Math.min(rect.dx, rect.dy), n; + scale(remaining, rect.dx * rect.dy / node.value); + row.area = 0; + while ((n = remaining.length) > 0) { + row.push(child = remaining[n - 1]); + row.area += child.area; + if (mode !== "squarify" || (score = worst(row, u)) <= best) { + remaining.pop(); + best = score; + } else { + row.area -= row.pop().area; + position(row, u, rect, false); + u = Math.min(rect.dx, rect.dy); + row.length = row.area = 0; + best = Infinity; + } + } + if (row.length) { + position(row, u, rect, true); + row.length = row.area = 0; + } + children.forEach(squarify); + } + } + function stickify(node) { + var children = node.children; + if (children && children.length) { + var rect = pad(node), remaining = children.slice(), child, row = []; + scale(remaining, rect.dx * rect.dy / node.value); + row.area = 0; + while (child = remaining.pop()) { + row.push(child); + row.area += child.area; + if (child.z != null) { + position(row, child.z ? rect.dx : rect.dy, rect, !remaining.length); + row.length = row.area = 0; + } + } + children.forEach(stickify); + } + } + function worst(row, u) { + var s = row.area, r, rmax = 0, rmin = Infinity, i = -1, n = row.length; + while (++i < n) { + if (!(r = row[i].area)) continue; + if (r < rmin) rmin = r; + if (r > rmax) rmax = r; + } + s *= s; + u *= u; + return s ? Math.max(u * rmax * ratio / s, s / (u * rmin * ratio)) : Infinity; + } + function position(row, u, rect, flush) { + var i = -1, n = row.length, x = rect.x, y = rect.y, v = u ? round(row.area / u) : 0, o; + if (u == rect.dx) { + if (flush || v > rect.dy) v = rect.dy; + while (++i < n) { + o = row[i]; + o.x = x; + o.y = y; + o.dy = v; + x += o.dx = Math.min(rect.x + rect.dx - x, v ? round(o.area / v) : 0); + } + o.z = true; + o.dx += rect.x + rect.dx - x; + rect.y += v; + rect.dy -= v; + } else { + if (flush || v > rect.dx) v = rect.dx; + while (++i < n) { + o = row[i]; + o.x = x; + o.y = y; + o.dx = v; + y += o.dy = Math.min(rect.y + rect.dy - y, v ? round(o.area / v) : 0); + } + o.z = false; + o.dy += rect.y + rect.dy - y; + rect.x += v; + rect.dx -= v; + } + } + function treemap(d) { + var nodes = stickies || hierarchy(d), root = nodes[0]; + root.x = root.y = 0; + if (root.value) root.dx = size[0], root.dy = size[1]; else root.dx = root.dy = 0; + if (stickies) hierarchy.revalue(root); + scale([ root ], root.dx * root.dy / root.value); + (stickies ? stickify : squarify)(root); + if (sticky) stickies = nodes; + return nodes; + } + treemap.size = function(x) { + if (!arguments.length) return size; + size = x; + return treemap; + }; + treemap.padding = function(x) { + if (!arguments.length) return padding; + function padFunction(node) { + var p = x.call(treemap, node, node.depth); + return p == null ? d3_layout_treemapPadNull(node) : d3_layout_treemapPad(node, typeof p === "number" ? [ p, p, p, p ] : p); + } + function padConstant(node) { + return d3_layout_treemapPad(node, x); + } + var type; + pad = (padding = x) == null ? d3_layout_treemapPadNull : (type = typeof x) === "function" ? padFunction : type === "number" ? (x = [ x, x, x, x ], + padConstant) : padConstant; + return treemap; + }; + treemap.round = function(x) { + if (!arguments.length) return round != Number; + round = x ? Math.round : Number; + return treemap; + }; + treemap.sticky = function(x) { + if (!arguments.length) return sticky; + sticky = x; + stickies = null; + return treemap; + }; + treemap.ratio = function(x) { + if (!arguments.length) return ratio; + ratio = x; + return treemap; + }; + treemap.mode = function(x) { + if (!arguments.length) return mode; + mode = x + ""; + return treemap; + }; + return d3_layout_hierarchyRebind(treemap, hierarchy); + }; + function d3_layout_treemapPadNull(node) { + return { + x: node.x, + y: node.y, + dx: node.dx, + dy: node.dy + }; + } + function d3_layout_treemapPad(node, padding) { + var x = node.x + padding[3], y = node.y + padding[0], dx = node.dx - padding[1] - padding[3], dy = node.dy - padding[0] - padding[2]; + if (dx < 0) { + x += dx / 2; + dx = 0; + } + if (dy < 0) { + y += dy / 2; + dy = 0; + } + return { + x: x, + y: y, + dx: dx, + dy: dy + }; + } + d3.random = { + normal: function(µ, σ) { + var n = arguments.length; + if (n < 2) σ = 1; + if (n < 1) µ = 0; + return function() { + var x, y, r; + do { + x = Math.random() * 2 - 1; + y = Math.random() * 2 - 1; + r = x * x + y * y; + } while (!r || r > 1); + return µ + σ * x * Math.sqrt(-2 * Math.log(r) / r); + }; + }, + logNormal: function() { + var random = d3.random.normal.apply(d3, arguments); + return function() { + return Math.exp(random()); + }; + }, + bates: function(m) { + var random = d3.random.irwinHall(m); + return function() { + return random() / m; + }; + }, + irwinHall: function(m) { + return function() { + for (var s = 0, j = 0; j < m; j++) s += Math.random(); + return s; + }; + } + }; + d3.scale = {}; + function d3_scaleExtent(domain) { + var start = domain[0], stop = domain[domain.length - 1]; + return start < stop ? [ start, stop ] : [ stop, start ]; + } + function d3_scaleRange(scale) { + return scale.rangeExtent ? scale.rangeExtent() : d3_scaleExtent(scale.range()); + } + function d3_scale_bilinear(domain, range, uninterpolate, interpolate) { + var u = uninterpolate(domain[0], domain[1]), i = interpolate(range[0], range[1]); + return function(x) { + return i(u(x)); + }; + } + function d3_scale_nice(domain, nice) { + var i0 = 0, i1 = domain.length - 1, x0 = domain[i0], x1 = domain[i1], dx; + if (x1 < x0) { + dx = i0, i0 = i1, i1 = dx; + dx = x0, x0 = x1, x1 = dx; + } + domain[i0] = nice.floor(x0); + domain[i1] = nice.ceil(x1); + return domain; + } + function d3_scale_niceStep(step) { + return step ? { + floor: function(x) { + return Math.floor(x / step) * step; + }, + ceil: function(x) { + return Math.ceil(x / step) * step; + } + } : d3_scale_niceIdentity; + } + var d3_scale_niceIdentity = { + floor: d3_identity, + ceil: d3_identity + }; + function d3_scale_polylinear(domain, range, uninterpolate, interpolate) { + var u = [], i = [], j = 0, k = Math.min(domain.length, range.length) - 1; + if (domain[k] < domain[0]) { + domain = domain.slice().reverse(); + range = range.slice().reverse(); + } + while (++j <= k) { + u.push(uninterpolate(domain[j - 1], domain[j])); + i.push(interpolate(range[j - 1], range[j])); + } + return function(x) { + var j = d3.bisect(domain, x, 1, k) - 1; + return i[j](u[j](x)); + }; + } + d3.scale.linear = function() { + return d3_scale_linear([ 0, 1 ], [ 0, 1 ], d3_interpolate, false); + }; + function d3_scale_linear(domain, range, interpolate, clamp) { + var output, input; + function rescale() { + var linear = Math.min(domain.length, range.length) > 2 ? d3_scale_polylinear : d3_scale_bilinear, uninterpolate = clamp ? d3_uninterpolateClamp : d3_uninterpolateNumber; + output = linear(domain, range, uninterpolate, interpolate); + input = linear(range, domain, uninterpolate, d3_interpolate); + return scale; + } + function scale(x) { + return output(x); + } + scale.invert = function(y) { + return input(y); + }; + scale.domain = function(x) { + if (!arguments.length) return domain; + domain = x.map(Number); + return rescale(); + }; + scale.range = function(x) { + if (!arguments.length) return range; + range = x; + return rescale(); + }; + scale.rangeRound = function(x) { + return scale.range(x).interpolate(d3_interpolateRound); + }; + scale.clamp = function(x) { + if (!arguments.length) return clamp; + clamp = x; + return rescale(); + }; + scale.interpolate = function(x) { + if (!arguments.length) return interpolate; + interpolate = x; + return rescale(); + }; + scale.ticks = function(m) { + return d3_scale_linearTicks(domain, m); + }; + scale.tickFormat = function(m, format) { + return d3_scale_linearTickFormat(domain, m, format); + }; + scale.nice = function(m) { + d3_scale_linearNice(domain, m); + return rescale(); + }; + scale.copy = function() { + return d3_scale_linear(domain, range, interpolate, clamp); + }; + return rescale(); + } + function d3_scale_linearRebind(scale, linear) { + return d3.rebind(scale, linear, "range", "rangeRound", "interpolate", "clamp"); + } + function d3_scale_linearNice(domain, m) { + return d3_scale_nice(domain, d3_scale_niceStep(d3_scale_linearTickRange(domain, m)[2])); + } + function d3_scale_linearTickRange(domain, m) { + if (m == null) m = 10; + var extent = d3_scaleExtent(domain), span = extent[1] - extent[0], step = Math.pow(10, Math.floor(Math.log(span / m) / Math.LN10)), err = m / span * step; + if (err <= .15) step *= 10; else if (err <= .35) step *= 5; else if (err <= .75) step *= 2; + extent[0] = Math.ceil(extent[0] / step) * step; + extent[1] = Math.floor(extent[1] / step) * step + step * .5; + extent[2] = step; + return extent; + } + function d3_scale_linearTicks(domain, m) { + return d3.range.apply(d3, d3_scale_linearTickRange(domain, m)); + } + function d3_scale_linearTickFormat(domain, m, format) { + var range = d3_scale_linearTickRange(domain, m); + if (format) { + var match = d3_format_re.exec(format); + match.shift(); + if (match[8] === "s") { + var prefix = d3.formatPrefix(Math.max(abs(range[0]), abs(range[1]))); + if (!match[7]) match[7] = "." + d3_scale_linearPrecision(prefix.scale(range[2])); + match[8] = "f"; + format = d3.format(match.join("")); + return function(d) { + return format(prefix.scale(d)) + prefix.symbol; + }; + } + if (!match[7]) match[7] = "." + d3_scale_linearFormatPrecision(match[8], range); + format = match.join(""); + } else { + format = ",." + d3_scale_linearPrecision(range[2]) + "f"; + } + return d3.format(format); + } + var d3_scale_linearFormatSignificant = { + s: 1, + g: 1, + p: 1, + r: 1, + e: 1 + }; + function d3_scale_linearPrecision(value) { + return -Math.floor(Math.log(value) / Math.LN10 + .01); + } + function d3_scale_linearFormatPrecision(type, range) { + var p = d3_scale_linearPrecision(range[2]); + return type in d3_scale_linearFormatSignificant ? Math.abs(p - d3_scale_linearPrecision(Math.max(abs(range[0]), abs(range[1])))) + +(type !== "e") : p - (type === "%") * 2; + } + d3.scale.log = function() { + return d3_scale_log(d3.scale.linear().domain([ 0, 1 ]), 10, true, [ 1, 10 ]); + }; + function d3_scale_log(linear, base, positive, domain) { + function log(x) { + return (positive ? Math.log(x < 0 ? 0 : x) : -Math.log(x > 0 ? 0 : -x)) / Math.log(base); + } + function pow(x) { + return positive ? Math.pow(base, x) : -Math.pow(base, -x); + } + function scale(x) { + return linear(log(x)); + } + scale.invert = function(x) { + return pow(linear.invert(x)); + }; + scale.domain = function(x) { + if (!arguments.length) return domain; + positive = x[0] >= 0; + linear.domain((domain = x.map(Number)).map(log)); + return scale; + }; + scale.base = function(_) { + if (!arguments.length) return base; + base = +_; + linear.domain(domain.map(log)); + return scale; + }; + scale.nice = function() { + var niced = d3_scale_nice(domain.map(log), positive ? Math : d3_scale_logNiceNegative); + linear.domain(niced); + domain = niced.map(pow); + return scale; + }; + scale.ticks = function() { + var extent = d3_scaleExtent(domain), ticks = [], u = extent[0], v = extent[1], i = Math.floor(log(u)), j = Math.ceil(log(v)), n = base % 1 ? 2 : base; + if (isFinite(j - i)) { + if (positive) { + for (;i < j; i++) for (var k = 1; k < n; k++) ticks.push(pow(i) * k); + ticks.push(pow(i)); + } else { + ticks.push(pow(i)); + for (;i++ < j; ) for (var k = n - 1; k > 0; k--) ticks.push(pow(i) * k); + } + for (i = 0; ticks[i] < u; i++) {} + for (j = ticks.length; ticks[j - 1] > v; j--) {} + ticks = ticks.slice(i, j); + } + return ticks; + }; + scale.tickFormat = function(n, format) { + if (!arguments.length) return d3_scale_logFormat; + if (arguments.length < 2) format = d3_scale_logFormat; else if (typeof format !== "function") format = d3.format(format); + var k = Math.max(.1, n / scale.ticks().length), f = positive ? (e = 1e-12, Math.ceil) : (e = -1e-12, + Math.floor), e; + return function(d) { + return d / pow(f(log(d) + e)) <= k ? format(d) : ""; + }; + }; + scale.copy = function() { + return d3_scale_log(linear.copy(), base, positive, domain); + }; + return d3_scale_linearRebind(scale, linear); + } + var d3_scale_logFormat = d3.format(".0e"), d3_scale_logNiceNegative = { + floor: function(x) { + return -Math.ceil(-x); + }, + ceil: function(x) { + return -Math.floor(-x); + } + }; + d3.scale.pow = function() { + return d3_scale_pow(d3.scale.linear(), 1, [ 0, 1 ]); + }; + function d3_scale_pow(linear, exponent, domain) { + var powp = d3_scale_powPow(exponent), powb = d3_scale_powPow(1 / exponent); + function scale(x) { + return linear(powp(x)); + } + scale.invert = function(x) { + return powb(linear.invert(x)); + }; + scale.domain = function(x) { + if (!arguments.length) return domain; + linear.domain((domain = x.map(Number)).map(powp)); + return scale; + }; + scale.ticks = function(m) { + return d3_scale_linearTicks(domain, m); + }; + scale.tickFormat = function(m, format) { + return d3_scale_linearTickFormat(domain, m, format); + }; + scale.nice = function(m) { + return scale.domain(d3_scale_linearNice(domain, m)); + }; + scale.exponent = function(x) { + if (!arguments.length) return exponent; + powp = d3_scale_powPow(exponent = x); + powb = d3_scale_powPow(1 / exponent); + linear.domain(domain.map(powp)); + return scale; + }; + scale.copy = function() { + return d3_scale_pow(linear.copy(), exponent, domain); + }; + return d3_scale_linearRebind(scale, linear); + } + function d3_scale_powPow(e) { + return function(x) { + return x < 0 ? -Math.pow(-x, e) : Math.pow(x, e); + }; + } + d3.scale.sqrt = function() { + return d3.scale.pow().exponent(.5); + }; + d3.scale.ordinal = function() { + return d3_scale_ordinal([], { + t: "range", + a: [ [] ] + }); + }; + function d3_scale_ordinal(domain, ranger) { + var index, range, rangeBand; + function scale(x) { + return range[((index.get(x) || (ranger.t === "range" ? index.set(x, domain.push(x)) : NaN)) - 1) % range.length]; + } + function steps(start, step) { + return d3.range(domain.length).map(function(i) { + return start + step * i; + }); + } + scale.domain = function(x) { + if (!arguments.length) return domain; + domain = []; + index = new d3_Map(); + var i = -1, n = x.length, xi; + while (++i < n) if (!index.has(xi = x[i])) index.set(xi, domain.push(xi)); + return scale[ranger.t].apply(scale, ranger.a); + }; + scale.range = function(x) { + if (!arguments.length) return range; + range = x; + rangeBand = 0; + ranger = { + t: "range", + a: arguments + }; + return scale; + }; + scale.rangePoints = function(x, padding) { + if (arguments.length < 2) padding = 0; + var start = x[0], stop = x[1], step = domain.length < 2 ? (start = (start + stop) / 2, + 0) : (stop - start) / (domain.length - 1 + padding); + range = steps(start + step * padding / 2, step); + rangeBand = 0; + ranger = { + t: "rangePoints", + a: arguments + }; + return scale; + }; + scale.rangeRoundPoints = function(x, padding) { + if (arguments.length < 2) padding = 0; + var start = x[0], stop = x[1], step = domain.length < 2 ? (start = stop = Math.round((start + stop) / 2), + 0) : (stop - start) / (domain.length - 1 + padding) | 0; + range = steps(start + Math.round(step * padding / 2 + (stop - start - (domain.length - 1 + padding) * step) / 2), step); + rangeBand = 0; + ranger = { + t: "rangeRoundPoints", + a: arguments + }; + return scale; + }; + scale.rangeBands = function(x, padding, outerPadding) { + if (arguments.length < 2) padding = 0; + if (arguments.length < 3) outerPadding = padding; + var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = (stop - start) / (domain.length - padding + 2 * outerPadding); + range = steps(start + step * outerPadding, step); + if (reverse) range.reverse(); + rangeBand = step * (1 - padding); + ranger = { + t: "rangeBands", + a: arguments + }; + return scale; + }; + scale.rangeRoundBands = function(x, padding, outerPadding) { + if (arguments.length < 2) padding = 0; + if (arguments.length < 3) outerPadding = padding; + var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = Math.floor((stop - start) / (domain.length - padding + 2 * outerPadding)); + range = steps(start + Math.round((stop - start - (domain.length - padding) * step) / 2), step); + if (reverse) range.reverse(); + rangeBand = Math.round(step * (1 - padding)); + ranger = { + t: "rangeRoundBands", + a: arguments + }; + return scale; + }; + scale.rangeBand = function() { + return rangeBand; + }; + scale.rangeExtent = function() { + return d3_scaleExtent(ranger.a[0]); + }; + scale.copy = function() { + return d3_scale_ordinal(domain, ranger); + }; + return scale.domain(domain); + } + d3.scale.category10 = function() { + return d3.scale.ordinal().range(d3_category10); + }; + d3.scale.category20 = function() { + return d3.scale.ordinal().range(d3_category20); + }; + d3.scale.category20b = function() { + return d3.scale.ordinal().range(d3_category20b); + }; + d3.scale.category20c = function() { + return d3.scale.ordinal().range(d3_category20c); + }; + var d3_category10 = [ 2062260, 16744206, 2924588, 14034728, 9725885, 9197131, 14907330, 8355711, 12369186, 1556175 ].map(d3_rgbString); + var d3_category20 = [ 2062260, 11454440, 16744206, 16759672, 2924588, 10018698, 14034728, 16750742, 9725885, 12955861, 9197131, 12885140, 14907330, 16234194, 8355711, 13092807, 12369186, 14408589, 1556175, 10410725 ].map(d3_rgbString); + var d3_category20b = [ 3750777, 5395619, 7040719, 10264286, 6519097, 9216594, 11915115, 13556636, 9202993, 12426809, 15186514, 15190932, 8666169, 11356490, 14049643, 15177372, 8077683, 10834324, 13528509, 14589654 ].map(d3_rgbString); + var d3_category20c = [ 3244733, 7057110, 10406625, 13032431, 15095053, 16616764, 16625259, 16634018, 3253076, 7652470, 10607003, 13101504, 7695281, 10394312, 12369372, 14342891, 6513507, 9868950, 12434877, 14277081 ].map(d3_rgbString); + d3.scale.quantile = function() { + return d3_scale_quantile([], []); + }; + function d3_scale_quantile(domain, range) { + var thresholds; + function rescale() { + var k = 0, q = range.length; + thresholds = []; + while (++k < q) thresholds[k - 1] = d3.quantile(domain, k / q); + return scale; + } + function scale(x) { + if (!isNaN(x = +x)) return range[d3.bisect(thresholds, x)]; + } + scale.domain = function(x) { + if (!arguments.length) return domain; + domain = x.map(d3_number).filter(d3_numeric).sort(d3_ascending); + return rescale(); + }; + scale.range = function(x) { + if (!arguments.length) return range; + range = x; + return rescale(); + }; + scale.quantiles = function() { + return thresholds; + }; + scale.invertExtent = function(y) { + y = range.indexOf(y); + return y < 0 ? [ NaN, NaN ] : [ y > 0 ? thresholds[y - 1] : domain[0], y < thresholds.length ? thresholds[y] : domain[domain.length - 1] ]; + }; + scale.copy = function() { + return d3_scale_quantile(domain, range); + }; + return rescale(); + } + d3.scale.quantize = function() { + return d3_scale_quantize(0, 1, [ 0, 1 ]); + }; + function d3_scale_quantize(x0, x1, range) { + var kx, i; + function scale(x) { + return range[Math.max(0, Math.min(i, Math.floor(kx * (x - x0))))]; + } + function rescale() { + kx = range.length / (x1 - x0); + i = range.length - 1; + return scale; + } + scale.domain = function(x) { + if (!arguments.length) return [ x0, x1 ]; + x0 = +x[0]; + x1 = +x[x.length - 1]; + return rescale(); + }; + scale.range = function(x) { + if (!arguments.length) return range; + range = x; + return rescale(); + }; + scale.invertExtent = function(y) { + y = range.indexOf(y); + y = y < 0 ? NaN : y / kx + x0; + return [ y, y + 1 / kx ]; + }; + scale.copy = function() { + return d3_scale_quantize(x0, x1, range); + }; + return rescale(); + } + d3.scale.threshold = function() { + return d3_scale_threshold([ .5 ], [ 0, 1 ]); + }; + function d3_scale_threshold(domain, range) { + function scale(x) { + if (x <= x) return range[d3.bisect(domain, x)]; + } + scale.domain = function(_) { + if (!arguments.length) return domain; + domain = _; + return scale; + }; + scale.range = function(_) { + if (!arguments.length) return range; + range = _; + return scale; + }; + scale.invertExtent = function(y) { + y = range.indexOf(y); + return [ domain[y - 1], domain[y] ]; + }; + scale.copy = function() { + return d3_scale_threshold(domain, range); + }; + return scale; + } + d3.scale.identity = function() { + return d3_scale_identity([ 0, 1 ]); + }; + function d3_scale_identity(domain) { + function identity(x) { + return +x; + } + identity.invert = identity; + identity.domain = identity.range = function(x) { + if (!arguments.length) return domain; + domain = x.map(identity); + return identity; + }; + identity.ticks = function(m) { + return d3_scale_linearTicks(domain, m); + }; + identity.tickFormat = function(m, format) { + return d3_scale_linearTickFormat(domain, m, format); + }; + identity.copy = function() { + return d3_scale_identity(domain); + }; + return identity; + } + d3.svg = {}; + function d3_zero() { + return 0; + } + d3.svg.arc = function() { + var innerRadius = d3_svg_arcInnerRadius, outerRadius = d3_svg_arcOuterRadius, cornerRadius = d3_zero, padRadius = d3_svg_arcAuto, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle, padAngle = d3_svg_arcPadAngle; + function arc() { + var r0 = Math.max(0, +innerRadius.apply(this, arguments)), r1 = Math.max(0, +outerRadius.apply(this, arguments)), a0 = startAngle.apply(this, arguments) - halfπ, a1 = endAngle.apply(this, arguments) - halfπ, da = Math.abs(a1 - a0), cw = a0 > a1 ? 0 : 1; + if (r1 < r0) rc = r1, r1 = r0, r0 = rc; + if (da >= τε) return circleSegment(r1, cw) + (r0 ? circleSegment(r0, 1 - cw) : "") + "Z"; + var rc, cr, rp, ap, p0 = 0, p1 = 0, x0, y0, x1, y1, x2, y2, x3, y3, path = []; + if (ap = (+padAngle.apply(this, arguments) || 0) / 2) { + rp = padRadius === d3_svg_arcAuto ? Math.sqrt(r0 * r0 + r1 * r1) : +padRadius.apply(this, arguments); + if (!cw) p1 *= -1; + if (r1) p1 = d3_asin(rp / r1 * Math.sin(ap)); + if (r0) p0 = d3_asin(rp / r0 * Math.sin(ap)); + } + if (r1) { + x0 = r1 * Math.cos(a0 + p1); + y0 = r1 * Math.sin(a0 + p1); + x1 = r1 * Math.cos(a1 - p1); + y1 = r1 * Math.sin(a1 - p1); + var l1 = Math.abs(a1 - a0 - 2 * p1) <= π ? 0 : 1; + if (p1 && d3_svg_arcSweep(x0, y0, x1, y1) === cw ^ l1) { + var h1 = (a0 + a1) / 2; + x0 = r1 * Math.cos(h1); + y0 = r1 * Math.sin(h1); + x1 = y1 = null; + } + } else { + x0 = y0 = 0; + } + if (r0) { + x2 = r0 * Math.cos(a1 - p0); + y2 = r0 * Math.sin(a1 - p0); + x3 = r0 * Math.cos(a0 + p0); + y3 = r0 * Math.sin(a0 + p0); + var l0 = Math.abs(a0 - a1 + 2 * p0) <= π ? 0 : 1; + if (p0 && d3_svg_arcSweep(x2, y2, x3, y3) === 1 - cw ^ l0) { + var h0 = (a0 + a1) / 2; + x2 = r0 * Math.cos(h0); + y2 = r0 * Math.sin(h0); + x3 = y3 = null; + } + } else { + x2 = y2 = 0; + } + if (da > ε && (rc = Math.min(Math.abs(r1 - r0) / 2, +cornerRadius.apply(this, arguments))) > .001) { + cr = r0 < r1 ^ cw ? 0 : 1; + var rc1 = rc, rc0 = rc; + if (da < π) { + var oc = x3 == null ? [ x2, y2 ] : x1 == null ? [ x0, y0 ] : d3_geom_polygonIntersect([ x0, y0 ], [ x3, y3 ], [ x1, y1 ], [ x2, y2 ]), ax = x0 - oc[0], ay = y0 - oc[1], bx = x1 - oc[0], by = y1 - oc[1], kc = 1 / Math.sin(Math.acos((ax * bx + ay * by) / (Math.sqrt(ax * ax + ay * ay) * Math.sqrt(bx * bx + by * by))) / 2), lc = Math.sqrt(oc[0] * oc[0] + oc[1] * oc[1]); + rc0 = Math.min(rc, (r0 - lc) / (kc - 1)); + rc1 = Math.min(rc, (r1 - lc) / (kc + 1)); + } + if (x1 != null) { + var t30 = d3_svg_arcCornerTangents(x3 == null ? [ x2, y2 ] : [ x3, y3 ], [ x0, y0 ], r1, rc1, cw), t12 = d3_svg_arcCornerTangents([ x1, y1 ], [ x2, y2 ], r1, rc1, cw); + if (rc === rc1) { + path.push("M", t30[0], "A", rc1, ",", rc1, " 0 0,", cr, " ", t30[1], "A", r1, ",", r1, " 0 ", 1 - cw ^ d3_svg_arcSweep(t30[1][0], t30[1][1], t12[1][0], t12[1][1]), ",", cw, " ", t12[1], "A", rc1, ",", rc1, " 0 0,", cr, " ", t12[0]); + } else { + path.push("M", t30[0], "A", rc1, ",", rc1, " 0 1,", cr, " ", t12[0]); + } + } else { + path.push("M", x0, ",", y0); + } + if (x3 != null) { + var t03 = d3_svg_arcCornerTangents([ x0, y0 ], [ x3, y3 ], r0, -rc0, cw), t21 = d3_svg_arcCornerTangents([ x2, y2 ], x1 == null ? [ x0, y0 ] : [ x1, y1 ], r0, -rc0, cw); + if (rc === rc0) { + path.push("L", t21[0], "A", rc0, ",", rc0, " 0 0,", cr, " ", t21[1], "A", r0, ",", r0, " 0 ", cw ^ d3_svg_arcSweep(t21[1][0], t21[1][1], t03[1][0], t03[1][1]), ",", 1 - cw, " ", t03[1], "A", rc0, ",", rc0, " 0 0,", cr, " ", t03[0]); + } else { + path.push("L", t21[0], "A", rc0, ",", rc0, " 0 0,", cr, " ", t03[0]); + } + } else { + path.push("L", x2, ",", y2); + } + } else { + path.push("M", x0, ",", y0); + if (x1 != null) path.push("A", r1, ",", r1, " 0 ", l1, ",", cw, " ", x1, ",", y1); + path.push("L", x2, ",", y2); + if (x3 != null) path.push("A", r0, ",", r0, " 0 ", l0, ",", 1 - cw, " ", x3, ",", y3); + } + path.push("Z"); + return path.join(""); + } + function circleSegment(r1, cw) { + return "M0," + r1 + "A" + r1 + "," + r1 + " 0 1," + cw + " 0," + -r1 + "A" + r1 + "," + r1 + " 0 1," + cw + " 0," + r1; + } + arc.innerRadius = function(v) { + if (!arguments.length) return innerRadius; + innerRadius = d3_functor(v); + return arc; + }; + arc.outerRadius = function(v) { + if (!arguments.length) return outerRadius; + outerRadius = d3_functor(v); + return arc; + }; + arc.cornerRadius = function(v) { + if (!arguments.length) return cornerRadius; + cornerRadius = d3_functor(v); + return arc; + }; + arc.padRadius = function(v) { + if (!arguments.length) return padRadius; + padRadius = v == d3_svg_arcAuto ? d3_svg_arcAuto : d3_functor(v); + return arc; + }; + arc.startAngle = function(v) { + if (!arguments.length) return startAngle; + startAngle = d3_functor(v); + return arc; + }; + arc.endAngle = function(v) { + if (!arguments.length) return endAngle; + endAngle = d3_functor(v); + return arc; + }; + arc.padAngle = function(v) { + if (!arguments.length) return padAngle; + padAngle = d3_functor(v); + return arc; + }; + arc.centroid = function() { + var r = (+innerRadius.apply(this, arguments) + +outerRadius.apply(this, arguments)) / 2, a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - halfπ; + return [ Math.cos(a) * r, Math.sin(a) * r ]; + }; + return arc; + }; + var d3_svg_arcAuto = "auto"; + function d3_svg_arcInnerRadius(d) { + return d.innerRadius; + } + function d3_svg_arcOuterRadius(d) { + return d.outerRadius; + } + function d3_svg_arcStartAngle(d) { + return d.startAngle; + } + function d3_svg_arcEndAngle(d) { + return d.endAngle; + } + function d3_svg_arcPadAngle(d) { + return d && d.padAngle; + } + function d3_svg_arcSweep(x0, y0, x1, y1) { + return (x0 - x1) * y0 - (y0 - y1) * x0 > 0 ? 0 : 1; + } + function d3_svg_arcCornerTangents(p0, p1, r1, rc, cw) { + var x01 = p0[0] - p1[0], y01 = p0[1] - p1[1], lo = (cw ? rc : -rc) / Math.sqrt(x01 * x01 + y01 * y01), ox = lo * y01, oy = -lo * x01, x1 = p0[0] + ox, y1 = p0[1] + oy, x2 = p1[0] + ox, y2 = p1[1] + oy, x3 = (x1 + x2) / 2, y3 = (y1 + y2) / 2, dx = x2 - x1, dy = y2 - y1, d2 = dx * dx + dy * dy, r = r1 - rc, D = x1 * y2 - x2 * y1, d = (dy < 0 ? -1 : 1) * Math.sqrt(Math.max(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 - x3, dy0 = cy0 - y3, dx1 = cx1 - x3, dy1 = cy1 - y3; + if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) cx0 = cx1, cy0 = cy1; + return [ [ cx0 - ox, cy0 - oy ], [ cx0 * r1 / r, cy0 * r1 / r ] ]; + } + function d3_svg_line(projection) { + var x = d3_geom_pointX, y = d3_geom_pointY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, tension = .7; + function line(data) { + var segments = [], points = [], i = -1, n = data.length, d, fx = d3_functor(x), fy = d3_functor(y); + function segment() { + segments.push("M", interpolate(projection(points), tension)); + } + while (++i < n) { + if (defined.call(this, d = data[i], i)) { + points.push([ +fx.call(this, d, i), +fy.call(this, d, i) ]); + } else if (points.length) { + segment(); + points = []; + } + } + if (points.length) segment(); + return segments.length ? segments.join("") : null; + } + line.x = function(_) { + if (!arguments.length) return x; + x = _; + return line; + }; + line.y = function(_) { + if (!arguments.length) return y; + y = _; + return line; + }; + line.defined = function(_) { + if (!arguments.length) return defined; + defined = _; + return line; + }; + line.interpolate = function(_) { + if (!arguments.length) return interpolateKey; + if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key; + return line; + }; + line.tension = function(_) { + if (!arguments.length) return tension; + tension = _; + return line; + }; + return line; + } + d3.svg.line = function() { + return d3_svg_line(d3_identity); + }; + var d3_svg_lineInterpolators = d3.map({ + linear: d3_svg_lineLinear, + "linear-closed": d3_svg_lineLinearClosed, + step: d3_svg_lineStep, + "step-before": d3_svg_lineStepBefore, + "step-after": d3_svg_lineStepAfter, + basis: d3_svg_lineBasis, + "basis-open": d3_svg_lineBasisOpen, + "basis-closed": d3_svg_lineBasisClosed, + bundle: d3_svg_lineBundle, + cardinal: d3_svg_lineCardinal, + "cardinal-open": d3_svg_lineCardinalOpen, + "cardinal-closed": d3_svg_lineCardinalClosed, + monotone: d3_svg_lineMonotone + }); + d3_svg_lineInterpolators.forEach(function(key, value) { + value.key = key; + value.closed = /-closed$/.test(key); + }); + function d3_svg_lineLinear(points) { + return points.length > 1 ? points.join("L") : points + "Z"; + } + function d3_svg_lineLinearClosed(points) { + return points.join("L") + "Z"; + } + function d3_svg_lineStep(points) { + var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ]; + while (++i < n) path.push("H", (p[0] + (p = points[i])[0]) / 2, "V", p[1]); + if (n > 1) path.push("H", p[0]); + return path.join(""); + } + function d3_svg_lineStepBefore(points) { + var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ]; + while (++i < n) path.push("V", (p = points[i])[1], "H", p[0]); + return path.join(""); + } + function d3_svg_lineStepAfter(points) { + var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ]; + while (++i < n) path.push("H", (p = points[i])[0], "V", p[1]); + return path.join(""); + } + function d3_svg_lineCardinalOpen(points, tension) { + return points.length < 4 ? d3_svg_lineLinear(points) : points[1] + d3_svg_lineHermite(points.slice(1, -1), d3_svg_lineCardinalTangents(points, tension)); + } + function d3_svg_lineCardinalClosed(points, tension) { + return points.length < 3 ? d3_svg_lineLinearClosed(points) : points[0] + d3_svg_lineHermite((points.push(points[0]), + points), d3_svg_lineCardinalTangents([ points[points.length - 2] ].concat(points, [ points[1] ]), tension)); + } + function d3_svg_lineCardinal(points, tension) { + return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineCardinalTangents(points, tension)); + } + function d3_svg_lineHermite(points, tangents) { + if (tangents.length < 1 || points.length != tangents.length && points.length != tangents.length + 2) { + return d3_svg_lineLinear(points); + } + var quad = points.length != tangents.length, path = "", p0 = points[0], p = points[1], t0 = tangents[0], t = t0, pi = 1; + if (quad) { + path += "Q" + (p[0] - t0[0] * 2 / 3) + "," + (p[1] - t0[1] * 2 / 3) + "," + p[0] + "," + p[1]; + p0 = points[1]; + pi = 2; + } + if (tangents.length > 1) { + t = tangents[1]; + p = points[pi]; + pi++; + path += "C" + (p0[0] + t0[0]) + "," + (p0[1] + t0[1]) + "," + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1]; + for (var i = 2; i < tangents.length; i++, pi++) { + p = points[pi]; + t = tangents[i]; + path += "S" + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1]; + } + } + if (quad) { + var lp = points[pi]; + path += "Q" + (p[0] + t[0] * 2 / 3) + "," + (p[1] + t[1] * 2 / 3) + "," + lp[0] + "," + lp[1]; + } + return path; + } + function d3_svg_lineCardinalTangents(points, tension) { + var tangents = [], a = (1 - tension) / 2, p0, p1 = points[0], p2 = points[1], i = 1, n = points.length; + while (++i < n) { + p0 = p1; + p1 = p2; + p2 = points[i]; + tangents.push([ a * (p2[0] - p0[0]), a * (p2[1] - p0[1]) ]); + } + return tangents; + } + function d3_svg_lineBasis(points) { + if (points.length < 3) return d3_svg_lineLinear(points); + var i = 1, n = points.length, pi = points[0], x0 = pi[0], y0 = pi[1], px = [ x0, x0, x0, (pi = points[1])[0] ], py = [ y0, y0, y0, pi[1] ], path = [ x0, ",", y0, "L", d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ]; + points.push(points[n - 1]); + while (++i <= n) { + pi = points[i]; + px.shift(); + px.push(pi[0]); + py.shift(); + py.push(pi[1]); + d3_svg_lineBasisBezier(path, px, py); + } + points.pop(); + path.push("L", pi); + return path.join(""); + } + function d3_svg_lineBasisOpen(points) { + if (points.length < 4) return d3_svg_lineLinear(points); + var path = [], i = -1, n = points.length, pi, px = [ 0 ], py = [ 0 ]; + while (++i < 3) { + pi = points[i]; + px.push(pi[0]); + py.push(pi[1]); + } + path.push(d3_svg_lineDot4(d3_svg_lineBasisBezier3, px) + "," + d3_svg_lineDot4(d3_svg_lineBasisBezier3, py)); + --i; + while (++i < n) { + pi = points[i]; + px.shift(); + px.push(pi[0]); + py.shift(); + py.push(pi[1]); + d3_svg_lineBasisBezier(path, px, py); + } + return path.join(""); + } + function d3_svg_lineBasisClosed(points) { + var path, i = -1, n = points.length, m = n + 4, pi, px = [], py = []; + while (++i < 4) { + pi = points[i % n]; + px.push(pi[0]); + py.push(pi[1]); + } + path = [ d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ]; + --i; + while (++i < m) { + pi = points[i % n]; + px.shift(); + px.push(pi[0]); + py.shift(); + py.push(pi[1]); + d3_svg_lineBasisBezier(path, px, py); + } + return path.join(""); + } + function d3_svg_lineBundle(points, tension) { + var n = points.length - 1; + if (n) { + var x0 = points[0][0], y0 = points[0][1], dx = points[n][0] - x0, dy = points[n][1] - y0, i = -1, p, t; + while (++i <= n) { + p = points[i]; + t = i / n; + p[0] = tension * p[0] + (1 - tension) * (x0 + t * dx); + p[1] = tension * p[1] + (1 - tension) * (y0 + t * dy); + } + } + return d3_svg_lineBasis(points); + } + function d3_svg_lineDot4(a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]; + } + var d3_svg_lineBasisBezier1 = [ 0, 2 / 3, 1 / 3, 0 ], d3_svg_lineBasisBezier2 = [ 0, 1 / 3, 2 / 3, 0 ], d3_svg_lineBasisBezier3 = [ 0, 1 / 6, 2 / 3, 1 / 6 ]; + function d3_svg_lineBasisBezier(path, x, y) { + path.push("C", d3_svg_lineDot4(d3_svg_lineBasisBezier1, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier1, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, y)); + } + function d3_svg_lineSlope(p0, p1) { + return (p1[1] - p0[1]) / (p1[0] - p0[0]); + } + function d3_svg_lineFiniteDifferences(points) { + var i = 0, j = points.length - 1, m = [], p0 = points[0], p1 = points[1], d = m[0] = d3_svg_lineSlope(p0, p1); + while (++i < j) { + m[i] = (d + (d = d3_svg_lineSlope(p0 = p1, p1 = points[i + 1]))) / 2; + } + m[i] = d; + return m; + } + function d3_svg_lineMonotoneTangents(points) { + var tangents = [], d, a, b, s, m = d3_svg_lineFiniteDifferences(points), i = -1, j = points.length - 1; + while (++i < j) { + d = d3_svg_lineSlope(points[i], points[i + 1]); + if (abs(d) < ε) { + m[i] = m[i + 1] = 0; + } else { + a = m[i] / d; + b = m[i + 1] / d; + s = a * a + b * b; + if (s > 9) { + s = d * 3 / Math.sqrt(s); + m[i] = s * a; + m[i + 1] = s * b; + } + } + } + i = -1; + while (++i <= j) { + s = (points[Math.min(j, i + 1)][0] - points[Math.max(0, i - 1)][0]) / (6 * (1 + m[i] * m[i])); + tangents.push([ s || 0, m[i] * s || 0 ]); + } + return tangents; + } + function d3_svg_lineMonotone(points) { + return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineMonotoneTangents(points)); + } + d3.svg.line.radial = function() { + var line = d3_svg_line(d3_svg_lineRadial); + line.radius = line.x, delete line.x; + line.angle = line.y, delete line.y; + return line; + }; + function d3_svg_lineRadial(points) { + var point, i = -1, n = points.length, r, a; + while (++i < n) { + point = points[i]; + r = point[0]; + a = point[1] - halfπ; + point[0] = r * Math.cos(a); + point[1] = r * Math.sin(a); + } + return points; + } + function d3_svg_area(projection) { + var x0 = d3_geom_pointX, x1 = d3_geom_pointX, y0 = 0, y1 = d3_geom_pointY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, interpolateReverse = interpolate, L = "L", tension = .7; + function area(data) { + var segments = [], points0 = [], points1 = [], i = -1, n = data.length, d, fx0 = d3_functor(x0), fy0 = d3_functor(y0), fx1 = x0 === x1 ? function() { + return x; + } : d3_functor(x1), fy1 = y0 === y1 ? function() { + return y; + } : d3_functor(y1), x, y; + function segment() { + segments.push("M", interpolate(projection(points1), tension), L, interpolateReverse(projection(points0.reverse()), tension), "Z"); + } + while (++i < n) { + if (defined.call(this, d = data[i], i)) { + points0.push([ x = +fx0.call(this, d, i), y = +fy0.call(this, d, i) ]); + points1.push([ +fx1.call(this, d, i), +fy1.call(this, d, i) ]); + } else if (points0.length) { + segment(); + points0 = []; + points1 = []; + } + } + if (points0.length) segment(); + return segments.length ? segments.join("") : null; + } + area.x = function(_) { + if (!arguments.length) return x1; + x0 = x1 = _; + return area; + }; + area.x0 = function(_) { + if (!arguments.length) return x0; + x0 = _; + return area; + }; + area.x1 = function(_) { + if (!arguments.length) return x1; + x1 = _; + return area; + }; + area.y = function(_) { + if (!arguments.length) return y1; + y0 = y1 = _; + return area; + }; + area.y0 = function(_) { + if (!arguments.length) return y0; + y0 = _; + return area; + }; + area.y1 = function(_) { + if (!arguments.length) return y1; + y1 = _; + return area; + }; + area.defined = function(_) { + if (!arguments.length) return defined; + defined = _; + return area; + }; + area.interpolate = function(_) { + if (!arguments.length) return interpolateKey; + if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key; + interpolateReverse = interpolate.reverse || interpolate; + L = interpolate.closed ? "M" : "L"; + return area; + }; + area.tension = function(_) { + if (!arguments.length) return tension; + tension = _; + return area; + }; + return area; + } + d3_svg_lineStepBefore.reverse = d3_svg_lineStepAfter; + d3_svg_lineStepAfter.reverse = d3_svg_lineStepBefore; + d3.svg.area = function() { + return d3_svg_area(d3_identity); + }; + d3.svg.area.radial = function() { + var area = d3_svg_area(d3_svg_lineRadial); + area.radius = area.x, delete area.x; + area.innerRadius = area.x0, delete area.x0; + area.outerRadius = area.x1, delete area.x1; + area.angle = area.y, delete area.y; + area.startAngle = area.y0, delete area.y0; + area.endAngle = area.y1, delete area.y1; + return area; + }; + d3.svg.chord = function() { + var source = d3_source, target = d3_target, radius = d3_svg_chordRadius, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle; + function chord(d, i) { + var s = subgroup(this, source, d, i), t = subgroup(this, target, d, i); + return "M" + s.p0 + arc(s.r, s.p1, s.a1 - s.a0) + (equals(s, t) ? curve(s.r, s.p1, s.r, s.p0) : curve(s.r, s.p1, t.r, t.p0) + arc(t.r, t.p1, t.a1 - t.a0) + curve(t.r, t.p1, s.r, s.p0)) + "Z"; + } + function subgroup(self, f, d, i) { + var subgroup = f.call(self, d, i), r = radius.call(self, subgroup, i), a0 = startAngle.call(self, subgroup, i) - halfπ, a1 = endAngle.call(self, subgroup, i) - halfπ; + return { + r: r, + a0: a0, + a1: a1, + p0: [ r * Math.cos(a0), r * Math.sin(a0) ], + p1: [ r * Math.cos(a1), r * Math.sin(a1) ] + }; + } + function equals(a, b) { + return a.a0 == b.a0 && a.a1 == b.a1; + } + function arc(r, p, a) { + return "A" + r + "," + r + " 0 " + +(a > π) + ",1 " + p; + } + function curve(r0, p0, r1, p1) { + return "Q 0,0 " + p1; + } + chord.radius = function(v) { + if (!arguments.length) return radius; + radius = d3_functor(v); + return chord; + }; + chord.source = function(v) { + if (!arguments.length) return source; + source = d3_functor(v); + return chord; + }; + chord.target = function(v) { + if (!arguments.length) return target; + target = d3_functor(v); + return chord; + }; + chord.startAngle = function(v) { + if (!arguments.length) return startAngle; + startAngle = d3_functor(v); + return chord; + }; + chord.endAngle = function(v) { + if (!arguments.length) return endAngle; + endAngle = d3_functor(v); + return chord; + }; + return chord; + }; + function d3_svg_chordRadius(d) { + return d.radius; + } + d3.svg.diagonal = function() { + var source = d3_source, target = d3_target, projection = d3_svg_diagonalProjection; + function diagonal(d, i) { + var p0 = source.call(this, d, i), p3 = target.call(this, d, i), m = (p0.y + p3.y) / 2, p = [ p0, { + x: p0.x, + y: m + }, { + x: p3.x, + y: m + }, p3 ]; + p = p.map(projection); + return "M" + p[0] + "C" + p[1] + " " + p[2] + " " + p[3]; + } + diagonal.source = function(x) { + if (!arguments.length) return source; + source = d3_functor(x); + return diagonal; + }; + diagonal.target = function(x) { + if (!arguments.length) return target; + target = d3_functor(x); + return diagonal; + }; + diagonal.projection = function(x) { + if (!arguments.length) return projection; + projection = x; + return diagonal; + }; + return diagonal; + }; + function d3_svg_diagonalProjection(d) { + return [ d.x, d.y ]; + } + d3.svg.diagonal.radial = function() { + var diagonal = d3.svg.diagonal(), projection = d3_svg_diagonalProjection, projection_ = diagonal.projection; + diagonal.projection = function(x) { + return arguments.length ? projection_(d3_svg_diagonalRadialProjection(projection = x)) : projection; + }; + return diagonal; + }; + function d3_svg_diagonalRadialProjection(projection) { + return function() { + var d = projection.apply(this, arguments), r = d[0], a = d[1] - halfπ; + return [ r * Math.cos(a), r * Math.sin(a) ]; + }; + } + d3.svg.symbol = function() { + var type = d3_svg_symbolType, size = d3_svg_symbolSize; + function symbol(d, i) { + return (d3_svg_symbols.get(type.call(this, d, i)) || d3_svg_symbolCircle)(size.call(this, d, i)); + } + symbol.type = function(x) { + if (!arguments.length) return type; + type = d3_functor(x); + return symbol; + }; + symbol.size = function(x) { + if (!arguments.length) return size; + size = d3_functor(x); + return symbol; + }; + return symbol; + }; + function d3_svg_symbolSize() { + return 64; + } + function d3_svg_symbolType() { + return "circle"; + } + function d3_svg_symbolCircle(size) { + var r = Math.sqrt(size / π); + return "M0," + r + "A" + r + "," + r + " 0 1,1 0," + -r + "A" + r + "," + r + " 0 1,1 0," + r + "Z"; + } + var d3_svg_symbols = d3.map({ + circle: d3_svg_symbolCircle, + cross: function(size) { + var r = Math.sqrt(size / 5) / 2; + return "M" + -3 * r + "," + -r + "H" + -r + "V" + -3 * r + "H" + r + "V" + -r + "H" + 3 * r + "V" + r + "H" + r + "V" + 3 * r + "H" + -r + "V" + r + "H" + -3 * r + "Z"; + }, + diamond: function(size) { + var ry = Math.sqrt(size / (2 * d3_svg_symbolTan30)), rx = ry * d3_svg_symbolTan30; + return "M0," + -ry + "L" + rx + ",0" + " 0," + ry + " " + -rx + ",0" + "Z"; + }, + square: function(size) { + var r = Math.sqrt(size) / 2; + return "M" + -r + "," + -r + "L" + r + "," + -r + " " + r + "," + r + " " + -r + "," + r + "Z"; + }, + "triangle-down": function(size) { + var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2; + return "M0," + ry + "L" + rx + "," + -ry + " " + -rx + "," + -ry + "Z"; + }, + "triangle-up": function(size) { + var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2; + return "M0," + -ry + "L" + rx + "," + ry + " " + -rx + "," + ry + "Z"; + } + }); + d3.svg.symbolTypes = d3_svg_symbols.keys(); + var d3_svg_symbolSqrt3 = Math.sqrt(3), d3_svg_symbolTan30 = Math.tan(30 * d3_radians); + d3_selectionPrototype.transition = function(name) { + var id = d3_transitionInheritId || ++d3_transitionId, ns = d3_transitionNamespace(name), subgroups = [], subgroup, node, transition = d3_transitionInherit || { + time: Date.now(), + ease: d3_ease_cubicInOut, + delay: 0, + duration: 250 + }; + for (var j = -1, m = this.length; ++j < m; ) { + subgroups.push(subgroup = []); + for (var group = this[j], i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) d3_transitionNode(node, i, ns, id, transition); + subgroup.push(node); + } + } + return d3_transition(subgroups, ns, id); + }; + d3_selectionPrototype.interrupt = function(name) { + return this.each(name == null ? d3_selection_interrupt : d3_selection_interruptNS(d3_transitionNamespace(name))); + }; + var d3_selection_interrupt = d3_selection_interruptNS(d3_transitionNamespace()); + function d3_selection_interruptNS(ns) { + return function() { + var lock, activeId, active; + if ((lock = this[ns]) && (active = lock[activeId = lock.active])) { + active.timer.c = null; + active.timer.t = NaN; + if (--lock.count) delete lock[activeId]; else delete this[ns]; + lock.active += .5; + active.event && active.event.interrupt.call(this, this.__data__, active.index); + } + }; + } + function d3_transition(groups, ns, id) { + d3_subclass(groups, d3_transitionPrototype); + groups.namespace = ns; + groups.id = id; + return groups; + } + var d3_transitionPrototype = [], d3_transitionId = 0, d3_transitionInheritId, d3_transitionInherit; + d3_transitionPrototype.call = d3_selectionPrototype.call; + d3_transitionPrototype.empty = d3_selectionPrototype.empty; + d3_transitionPrototype.node = d3_selectionPrototype.node; + d3_transitionPrototype.size = d3_selectionPrototype.size; + d3.transition = function(selection, name) { + return selection && selection.transition ? d3_transitionInheritId ? selection.transition(name) : selection : d3.selection().transition(selection); + }; + d3.transition.prototype = d3_transitionPrototype; + d3_transitionPrototype.select = function(selector) { + var id = this.id, ns = this.namespace, subgroups = [], subgroup, subnode, node; + selector = d3_selection_selector(selector); + for (var j = -1, m = this.length; ++j < m; ) { + subgroups.push(subgroup = []); + for (var group = this[j], i = -1, n = group.length; ++i < n; ) { + if ((node = group[i]) && (subnode = selector.call(node, node.__data__, i, j))) { + if ("__data__" in node) subnode.__data__ = node.__data__; + d3_transitionNode(subnode, i, ns, id, node[ns][id]); + subgroup.push(subnode); + } else { + subgroup.push(null); + } + } + } + return d3_transition(subgroups, ns, id); + }; + d3_transitionPrototype.selectAll = function(selector) { + var id = this.id, ns = this.namespace, subgroups = [], subgroup, subnodes, node, subnode, transition; + selector = d3_selection_selectorAll(selector); + for (var j = -1, m = this.length; ++j < m; ) { + for (var group = this[j], i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) { + transition = node[ns][id]; + subnodes = selector.call(node, node.__data__, i, j); + subgroups.push(subgroup = []); + for (var k = -1, o = subnodes.length; ++k < o; ) { + if (subnode = subnodes[k]) d3_transitionNode(subnode, k, ns, id, transition); + subgroup.push(subnode); + } + } + } + } + return d3_transition(subgroups, ns, id); + }; + d3_transitionPrototype.filter = function(filter) { + var subgroups = [], subgroup, group, node; + if (typeof filter !== "function") filter = d3_selection_filter(filter); + for (var j = 0, m = this.length; j < m; j++) { + subgroups.push(subgroup = []); + for (var group = this[j], i = 0, n = group.length; i < n; i++) { + if ((node = group[i]) && filter.call(node, node.__data__, i, j)) { + subgroup.push(node); + } + } + } + return d3_transition(subgroups, this.namespace, this.id); + }; + d3_transitionPrototype.tween = function(name, tween) { + var id = this.id, ns = this.namespace; + if (arguments.length < 2) return this.node()[ns][id].tween.get(name); + return d3_selection_each(this, tween == null ? function(node) { + node[ns][id].tween.remove(name); + } : function(node) { + node[ns][id].tween.set(name, tween); + }); + }; + function d3_transition_tween(groups, name, value, tween) { + var id = groups.id, ns = groups.namespace; + return d3_selection_each(groups, typeof value === "function" ? function(node, i, j) { + node[ns][id].tween.set(name, tween(value.call(node, node.__data__, i, j))); + } : (value = tween(value), function(node) { + node[ns][id].tween.set(name, value); + })); + } + d3_transitionPrototype.attr = function(nameNS, value) { + if (arguments.length < 2) { + for (value in nameNS) this.attr(value, nameNS[value]); + return this; + } + var interpolate = nameNS == "transform" ? d3_interpolateTransform : d3_interpolate, name = d3.ns.qualify(nameNS); + function attrNull() { + this.removeAttribute(name); + } + function attrNullNS() { + this.removeAttributeNS(name.space, name.local); + } + function attrTween(b) { + return b == null ? attrNull : (b += "", function() { + var a = this.getAttribute(name), i; + return a !== b && (i = interpolate(a, b), function(t) { + this.setAttribute(name, i(t)); + }); + }); + } + function attrTweenNS(b) { + return b == null ? attrNullNS : (b += "", function() { + var a = this.getAttributeNS(name.space, name.local), i; + return a !== b && (i = interpolate(a, b), function(t) { + this.setAttributeNS(name.space, name.local, i(t)); + }); + }); + } + return d3_transition_tween(this, "attr." + nameNS, value, name.local ? attrTweenNS : attrTween); + }; + d3_transitionPrototype.attrTween = function(nameNS, tween) { + var name = d3.ns.qualify(nameNS); + function attrTween(d, i) { + var f = tween.call(this, d, i, this.getAttribute(name)); + return f && function(t) { + this.setAttribute(name, f(t)); + }; + } + function attrTweenNS(d, i) { + var f = tween.call(this, d, i, this.getAttributeNS(name.space, name.local)); + return f && function(t) { + this.setAttributeNS(name.space, name.local, f(t)); + }; + } + return this.tween("attr." + nameNS, name.local ? attrTweenNS : attrTween); + }; + d3_transitionPrototype.style = function(name, value, priority) { + var n = arguments.length; + if (n < 3) { + if (typeof name !== "string") { + if (n < 2) value = ""; + for (priority in name) this.style(priority, name[priority], value); + return this; + } + priority = ""; + } + function styleNull() { + this.style.removeProperty(name); + } + function styleString(b) { + return b == null ? styleNull : (b += "", function() { + var a = d3_window(this).getComputedStyle(this, null).getPropertyValue(name), i; + return a !== b && (i = d3_interpolate(a, b), function(t) { + this.style.setProperty(name, i(t), priority); + }); + }); + } + return d3_transition_tween(this, "style." + name, value, styleString); + }; + d3_transitionPrototype.styleTween = function(name, tween, priority) { + if (arguments.length < 3) priority = ""; + function styleTween(d, i) { + var f = tween.call(this, d, i, d3_window(this).getComputedStyle(this, null).getPropertyValue(name)); + return f && function(t) { + this.style.setProperty(name, f(t), priority); + }; + } + return this.tween("style." + name, styleTween); + }; + d3_transitionPrototype.text = function(value) { + return d3_transition_tween(this, "text", value, d3_transition_text); + }; + function d3_transition_text(b) { + if (b == null) b = ""; + return function() { + this.textContent = b; + }; + } + d3_transitionPrototype.remove = function() { + var ns = this.namespace; + return this.each("end.transition", function() { + var p; + if (this[ns].count < 2 && (p = this.parentNode)) p.removeChild(this); + }); + }; + d3_transitionPrototype.ease = function(value) { + var id = this.id, ns = this.namespace; + if (arguments.length < 1) return this.node()[ns][id].ease; + if (typeof value !== "function") value = d3.ease.apply(d3, arguments); + return d3_selection_each(this, function(node) { + node[ns][id].ease = value; + }); + }; + d3_transitionPrototype.delay = function(value) { + var id = this.id, ns = this.namespace; + if (arguments.length < 1) return this.node()[ns][id].delay; + return d3_selection_each(this, typeof value === "function" ? function(node, i, j) { + node[ns][id].delay = +value.call(node, node.__data__, i, j); + } : (value = +value, function(node) { + node[ns][id].delay = value; + })); + }; + d3_transitionPrototype.duration = function(value) { + var id = this.id, ns = this.namespace; + if (arguments.length < 1) return this.node()[ns][id].duration; + return d3_selection_each(this, typeof value === "function" ? function(node, i, j) { + node[ns][id].duration = Math.max(1, value.call(node, node.__data__, i, j)); + } : (value = Math.max(1, value), function(node) { + node[ns][id].duration = value; + })); + }; + d3_transitionPrototype.each = function(type, listener) { + var id = this.id, ns = this.namespace; + if (arguments.length < 2) { + var inherit = d3_transitionInherit, inheritId = d3_transitionInheritId; + try { + d3_transitionInheritId = id; + d3_selection_each(this, function(node, i, j) { + d3_transitionInherit = node[ns][id]; + type.call(node, node.__data__, i, j); + }); + } finally { + d3_transitionInherit = inherit; + d3_transitionInheritId = inheritId; + } + } else { + d3_selection_each(this, function(node) { + var transition = node[ns][id]; + (transition.event || (transition.event = d3.dispatch("start", "end", "interrupt"))).on(type, listener); + }); + } + return this; + }; + d3_transitionPrototype.transition = function() { + var id0 = this.id, id1 = ++d3_transitionId, ns = this.namespace, subgroups = [], subgroup, group, node, transition; + for (var j = 0, m = this.length; j < m; j++) { + subgroups.push(subgroup = []); + for (var group = this[j], i = 0, n = group.length; i < n; i++) { + if (node = group[i]) { + transition = node[ns][id0]; + d3_transitionNode(node, i, ns, id1, { + time: transition.time, + ease: transition.ease, + delay: transition.delay + transition.duration, + duration: transition.duration + }); + } + subgroup.push(node); + } + } + return d3_transition(subgroups, ns, id1); + }; + function d3_transitionNamespace(name) { + return name == null ? "__transition__" : "__transition_" + name + "__"; + } + function d3_transitionNode(node, i, ns, id, inherit) { + var lock = node[ns] || (node[ns] = { + active: 0, + count: 0 + }), transition = lock[id], time, timer, duration, ease, tweens; + function schedule(elapsed) { + var delay = transition.delay; + timer.t = delay + time; + if (delay <= elapsed) return start(elapsed - delay); + timer.c = start; + } + function start(elapsed) { + var activeId = lock.active, active = lock[activeId]; + if (active) { + active.timer.c = null; + active.timer.t = NaN; + --lock.count; + delete lock[activeId]; + active.event && active.event.interrupt.call(node, node.__data__, active.index); + } + for (var cancelId in lock) { + if (+cancelId < id) { + var cancel = lock[cancelId]; + cancel.timer.c = null; + cancel.timer.t = NaN; + --lock.count; + delete lock[cancelId]; + } + } + timer.c = tick; + d3_timer(function() { + if (timer.c && tick(elapsed || 1)) { + timer.c = null; + timer.t = NaN; + } + return 1; + }, 0, time); + lock.active = id; + transition.event && transition.event.start.call(node, node.__data__, i); + tweens = []; + transition.tween.forEach(function(key, value) { + if (value = value.call(node, node.__data__, i)) { + tweens.push(value); + } + }); + ease = transition.ease; + duration = transition.duration; + } + function tick(elapsed) { + var t = elapsed / duration, e = ease(t), n = tweens.length; + while (n > 0) { + tweens[--n].call(node, e); + } + if (t >= 1) { + transition.event && transition.event.end.call(node, node.__data__, i); + if (--lock.count) delete lock[id]; else delete node[ns]; + return 1; + } + } + if (!transition) { + time = inherit.time; + timer = d3_timer(schedule, 0, time); + transition = lock[id] = { + tween: new d3_Map(), + time: time, + timer: timer, + delay: inherit.delay, + duration: inherit.duration, + ease: inherit.ease, + index: i + }; + inherit = null; + ++lock.count; + } + } + d3.svg.axis = function() { + var scale = d3.scale.linear(), orient = d3_svg_axisDefaultOrient, innerTickSize = 6, outerTickSize = 6, tickPadding = 3, tickArguments_ = [ 10 ], tickValues = null, tickFormat_; + function axis(g) { + g.each(function() { + var g = d3.select(this); + var scale0 = this.__chart__ || scale, scale1 = this.__chart__ = scale.copy(); + var ticks = tickValues == null ? scale1.ticks ? scale1.ticks.apply(scale1, tickArguments_) : scale1.domain() : tickValues, tickFormat = tickFormat_ == null ? scale1.tickFormat ? scale1.tickFormat.apply(scale1, tickArguments_) : d3_identity : tickFormat_, tick = g.selectAll(".tick").data(ticks, scale1), tickEnter = tick.enter().insert("g", ".domain").attr("class", "tick").style("opacity", ε), tickExit = d3.transition(tick.exit()).style("opacity", ε).remove(), tickUpdate = d3.transition(tick.order()).style("opacity", 1), tickSpacing = Math.max(innerTickSize, 0) + tickPadding, tickTransform; + var range = d3_scaleRange(scale1), path = g.selectAll(".domain").data([ 0 ]), pathUpdate = (path.enter().append("path").attr("class", "domain"), + d3.transition(path)); + tickEnter.append("line"); + tickEnter.append("text"); + var lineEnter = tickEnter.select("line"), lineUpdate = tickUpdate.select("line"), text = tick.select("text").text(tickFormat), textEnter = tickEnter.select("text"), textUpdate = tickUpdate.select("text"), sign = orient === "top" || orient === "left" ? -1 : 1, x1, x2, y1, y2; + if (orient === "bottom" || orient === "top") { + tickTransform = d3_svg_axisX, x1 = "x", y1 = "y", x2 = "x2", y2 = "y2"; + text.attr("dy", sign < 0 ? "0em" : ".71em").style("text-anchor", "middle"); + pathUpdate.attr("d", "M" + range[0] + "," + sign * outerTickSize + "V0H" + range[1] + "V" + sign * outerTickSize); + } else { + tickTransform = d3_svg_axisY, x1 = "y", y1 = "x", x2 = "y2", y2 = "x2"; + text.attr("dy", ".32em").style("text-anchor", sign < 0 ? "end" : "start"); + pathUpdate.attr("d", "M" + sign * outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + sign * outerTickSize); + } + lineEnter.attr(y2, sign * innerTickSize); + textEnter.attr(y1, sign * tickSpacing); + lineUpdate.attr(x2, 0).attr(y2, sign * innerTickSize); + textUpdate.attr(x1, 0).attr(y1, sign * tickSpacing); + if (scale1.rangeBand) { + var x = scale1, dx = x.rangeBand() / 2; + scale0 = scale1 = function(d) { + return x(d) + dx; + }; + } else if (scale0.rangeBand) { + scale0 = scale1; + } else { + tickExit.call(tickTransform, scale1, scale0); + } + tickEnter.call(tickTransform, scale0, scale1); + tickUpdate.call(tickTransform, scale1, scale1); + }); + } + axis.scale = function(x) { + if (!arguments.length) return scale; + scale = x; + return axis; + }; + axis.orient = function(x) { + if (!arguments.length) return orient; + orient = x in d3_svg_axisOrients ? x + "" : d3_svg_axisDefaultOrient; + return axis; + }; + axis.ticks = function() { + if (!arguments.length) return tickArguments_; + tickArguments_ = d3_array(arguments); + return axis; + }; + axis.tickValues = function(x) { + if (!arguments.length) return tickValues; + tickValues = x; + return axis; + }; + axis.tickFormat = function(x) { + if (!arguments.length) return tickFormat_; + tickFormat_ = x; + return axis; + }; + axis.tickSize = function(x) { + var n = arguments.length; + if (!n) return innerTickSize; + innerTickSize = +x; + outerTickSize = +arguments[n - 1]; + return axis; + }; + axis.innerTickSize = function(x) { + if (!arguments.length) return innerTickSize; + innerTickSize = +x; + return axis; + }; + axis.outerTickSize = function(x) { + if (!arguments.length) return outerTickSize; + outerTickSize = +x; + return axis; + }; + axis.tickPadding = function(x) { + if (!arguments.length) return tickPadding; + tickPadding = +x; + return axis; + }; + axis.tickSubdivide = function() { + return arguments.length && axis; + }; + return axis; + }; + var d3_svg_axisDefaultOrient = "bottom", d3_svg_axisOrients = { + top: 1, + right: 1, + bottom: 1, + left: 1 + }; + function d3_svg_axisX(selection, x0, x1) { + selection.attr("transform", function(d) { + var v0 = x0(d); + return "translate(" + (isFinite(v0) ? v0 : x1(d)) + ",0)"; + }); + } + function d3_svg_axisY(selection, y0, y1) { + selection.attr("transform", function(d) { + var v0 = y0(d); + return "translate(0," + (isFinite(v0) ? v0 : y1(d)) + ")"; + }); + } + d3.svg.brush = function() { + var event = d3_eventDispatch(brush, "brushstart", "brush", "brushend"), x = null, y = null, xExtent = [ 0, 0 ], yExtent = [ 0, 0 ], xExtentDomain, yExtentDomain, xClamp = true, yClamp = true, resizes = d3_svg_brushResizes[0]; + function brush(g) { + g.each(function() { + var g = d3.select(this).style("pointer-events", "all").style("-webkit-tap-highlight-color", "rgba(0,0,0,0)").on("mousedown.brush", brushstart).on("touchstart.brush", brushstart); + var background = g.selectAll(".background").data([ 0 ]); + background.enter().append("rect").attr("class", "background").style("visibility", "hidden").style("cursor", "crosshair"); + g.selectAll(".extent").data([ 0 ]).enter().append("rect").attr("class", "extent").style("cursor", "move"); + var resize = g.selectAll(".resize").data(resizes, d3_identity); + resize.exit().remove(); + resize.enter().append("g").attr("class", function(d) { + return "resize " + d; + }).style("cursor", function(d) { + return d3_svg_brushCursor[d]; + }).append("rect").attr("x", function(d) { + return /[ew]$/.test(d) ? -3 : null; + }).attr("y", function(d) { + return /^[ns]/.test(d) ? -3 : null; + }).attr("width", 6).attr("height", 6).style("visibility", "hidden"); + resize.style("display", brush.empty() ? "none" : null); + var gUpdate = d3.transition(g), backgroundUpdate = d3.transition(background), range; + if (x) { + range = d3_scaleRange(x); + backgroundUpdate.attr("x", range[0]).attr("width", range[1] - range[0]); + redrawX(gUpdate); + } + if (y) { + range = d3_scaleRange(y); + backgroundUpdate.attr("y", range[0]).attr("height", range[1] - range[0]); + redrawY(gUpdate); + } + redraw(gUpdate); + }); + } + brush.event = function(g) { + g.each(function() { + var event_ = event.of(this, arguments), extent1 = { + x: xExtent, + y: yExtent, + i: xExtentDomain, + j: yExtentDomain + }, extent0 = this.__chart__ || extent1; + this.__chart__ = extent1; + if (d3_transitionInheritId) { + d3.select(this).transition().each("start.brush", function() { + xExtentDomain = extent0.i; + yExtentDomain = extent0.j; + xExtent = extent0.x; + yExtent = extent0.y; + event_({ + type: "brushstart" + }); + }).tween("brush:brush", function() { + var xi = d3_interpolateArray(xExtent, extent1.x), yi = d3_interpolateArray(yExtent, extent1.y); + xExtentDomain = yExtentDomain = null; + return function(t) { + xExtent = extent1.x = xi(t); + yExtent = extent1.y = yi(t); + event_({ + type: "brush", + mode: "resize" + }); + }; + }).each("end.brush", function() { + xExtentDomain = extent1.i; + yExtentDomain = extent1.j; + event_({ + type: "brush", + mode: "resize" + }); + event_({ + type: "brushend" + }); + }); + } else { + event_({ + type: "brushstart" + }); + event_({ + type: "brush", + mode: "resize" + }); + event_({ + type: "brushend" + }); + } + }); + }; + function redraw(g) { + g.selectAll(".resize").attr("transform", function(d) { + return "translate(" + xExtent[+/e$/.test(d)] + "," + yExtent[+/^s/.test(d)] + ")"; + }); + } + function redrawX(g) { + g.select(".extent").attr("x", xExtent[0]); + g.selectAll(".extent,.n>rect,.s>rect").attr("width", xExtent[1] - xExtent[0]); + } + function redrawY(g) { + g.select(".extent").attr("y", yExtent[0]); + g.selectAll(".extent,.e>rect,.w>rect").attr("height", yExtent[1] - yExtent[0]); + } + function brushstart() { + var target = this, eventTarget = d3.select(d3.event.target), event_ = event.of(target, arguments), g = d3.select(target), resizing = eventTarget.datum(), resizingX = !/^(n|s)$/.test(resizing) && x, resizingY = !/^(e|w)$/.test(resizing) && y, dragging = eventTarget.classed("extent"), dragRestore = d3_event_dragSuppress(target), center, origin = d3.mouse(target), offset; + var w = d3.select(d3_window(target)).on("keydown.brush", keydown).on("keyup.brush", keyup); + if (d3.event.changedTouches) { + w.on("touchmove.brush", brushmove).on("touchend.brush", brushend); + } else { + w.on("mousemove.brush", brushmove).on("mouseup.brush", brushend); + } + g.interrupt().selectAll("*").interrupt(); + if (dragging) { + origin[0] = xExtent[0] - origin[0]; + origin[1] = yExtent[0] - origin[1]; + } else if (resizing) { + var ex = +/w$/.test(resizing), ey = +/^n/.test(resizing); + offset = [ xExtent[1 - ex] - origin[0], yExtent[1 - ey] - origin[1] ]; + origin[0] = xExtent[ex]; + origin[1] = yExtent[ey]; + } else if (d3.event.altKey) center = origin.slice(); + g.style("pointer-events", "none").selectAll(".resize").style("display", null); + d3.select("body").style("cursor", eventTarget.style("cursor")); + event_({ + type: "brushstart" + }); + brushmove(); + function keydown() { + if (d3.event.keyCode == 32) { + if (!dragging) { + center = null; + origin[0] -= xExtent[1]; + origin[1] -= yExtent[1]; + dragging = 2; + } + d3_eventPreventDefault(); + } + } + function keyup() { + if (d3.event.keyCode == 32 && dragging == 2) { + origin[0] += xExtent[1]; + origin[1] += yExtent[1]; + dragging = 0; + d3_eventPreventDefault(); + } + } + function brushmove() { + var point = d3.mouse(target), moved = false; + if (offset) { + point[0] += offset[0]; + point[1] += offset[1]; + } + if (!dragging) { + if (d3.event.altKey) { + if (!center) center = [ (xExtent[0] + xExtent[1]) / 2, (yExtent[0] + yExtent[1]) / 2 ]; + origin[0] = xExtent[+(point[0] < center[0])]; + origin[1] = yExtent[+(point[1] < center[1])]; + } else center = null; + } + if (resizingX && move1(point, x, 0)) { + redrawX(g); + moved = true; + } + if (resizingY && move1(point, y, 1)) { + redrawY(g); + moved = true; + } + if (moved) { + redraw(g); + event_({ + type: "brush", + mode: dragging ? "move" : "resize" + }); + } + } + function move1(point, scale, i) { + var range = d3_scaleRange(scale), r0 = range[0], r1 = range[1], position = origin[i], extent = i ? yExtent : xExtent, size = extent[1] - extent[0], min, max; + if (dragging) { + r0 -= position; + r1 -= size + position; + } + min = (i ? yClamp : xClamp) ? Math.max(r0, Math.min(r1, point[i])) : point[i]; + if (dragging) { + max = (min += position) + size; + } else { + if (center) position = Math.max(r0, Math.min(r1, 2 * center[i] - min)); + if (position < min) { + max = min; + min = position; + } else { + max = position; + } + } + if (extent[0] != min || extent[1] != max) { + if (i) yExtentDomain = null; else xExtentDomain = null; + extent[0] = min; + extent[1] = max; + return true; + } + } + function brushend() { + brushmove(); + g.style("pointer-events", "all").selectAll(".resize").style("display", brush.empty() ? "none" : null); + d3.select("body").style("cursor", null); + w.on("mousemove.brush", null).on("mouseup.brush", null).on("touchmove.brush", null).on("touchend.brush", null).on("keydown.brush", null).on("keyup.brush", null); + dragRestore(); + event_({ + type: "brushend" + }); + } + } + brush.x = function(z) { + if (!arguments.length) return x; + x = z; + resizes = d3_svg_brushResizes[!x << 1 | !y]; + return brush; + }; + brush.y = function(z) { + if (!arguments.length) return y; + y = z; + resizes = d3_svg_brushResizes[!x << 1 | !y]; + return brush; + }; + brush.clamp = function(z) { + if (!arguments.length) return x && y ? [ xClamp, yClamp ] : x ? xClamp : y ? yClamp : null; + if (x && y) xClamp = !!z[0], yClamp = !!z[1]; else if (x) xClamp = !!z; else if (y) yClamp = !!z; + return brush; + }; + brush.extent = function(z) { + var x0, x1, y0, y1, t; + if (!arguments.length) { + if (x) { + if (xExtentDomain) { + x0 = xExtentDomain[0], x1 = xExtentDomain[1]; + } else { + x0 = xExtent[0], x1 = xExtent[1]; + if (x.invert) x0 = x.invert(x0), x1 = x.invert(x1); + if (x1 < x0) t = x0, x0 = x1, x1 = t; + } + } + if (y) { + if (yExtentDomain) { + y0 = yExtentDomain[0], y1 = yExtentDomain[1]; + } else { + y0 = yExtent[0], y1 = yExtent[1]; + if (y.invert) y0 = y.invert(y0), y1 = y.invert(y1); + if (y1 < y0) t = y0, y0 = y1, y1 = t; + } + } + return x && y ? [ [ x0, y0 ], [ x1, y1 ] ] : x ? [ x0, x1 ] : y && [ y0, y1 ]; + } + if (x) { + x0 = z[0], x1 = z[1]; + if (y) x0 = x0[0], x1 = x1[0]; + xExtentDomain = [ x0, x1 ]; + if (x.invert) x0 = x(x0), x1 = x(x1); + if (x1 < x0) t = x0, x0 = x1, x1 = t; + if (x0 != xExtent[0] || x1 != xExtent[1]) xExtent = [ x0, x1 ]; + } + if (y) { + y0 = z[0], y1 = z[1]; + if (x) y0 = y0[1], y1 = y1[1]; + yExtentDomain = [ y0, y1 ]; + if (y.invert) y0 = y(y0), y1 = y(y1); + if (y1 < y0) t = y0, y0 = y1, y1 = t; + if (y0 != yExtent[0] || y1 != yExtent[1]) yExtent = [ y0, y1 ]; + } + return brush; + }; + brush.clear = function() { + if (!brush.empty()) { + xExtent = [ 0, 0 ], yExtent = [ 0, 0 ]; + xExtentDomain = yExtentDomain = null; + } + return brush; + }; + brush.empty = function() { + return !!x && xExtent[0] == xExtent[1] || !!y && yExtent[0] == yExtent[1]; + }; + return d3.rebind(brush, event, "on"); + }; + var d3_svg_brushCursor = { + 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 d3_svg_brushResizes = [ [ "n", "e", "s", "w", "nw", "ne", "se", "sw" ], [ "e", "w" ], [ "n", "s" ], [] ]; + var d3_time_format = d3_time.format = d3_locale_enUS.timeFormat; + var d3_time_formatUtc = d3_time_format.utc; + var d3_time_formatIso = d3_time_formatUtc("%Y-%m-%dT%H:%M:%S.%LZ"); + d3_time_format.iso = Date.prototype.toISOString && +new Date("2000-01-01T00:00:00.000Z") ? d3_time_formatIsoNative : d3_time_formatIso; + function d3_time_formatIsoNative(date) { + return date.toISOString(); + } + d3_time_formatIsoNative.parse = function(string) { + var date = new Date(string); + return isNaN(date) ? null : date; + }; + d3_time_formatIsoNative.toString = d3_time_formatIso.toString; + d3_time.second = d3_time_interval(function(date) { + return new d3_date(Math.floor(date / 1e3) * 1e3); + }, function(date, offset) { + date.setTime(date.getTime() + Math.floor(offset) * 1e3); + }, function(date) { + return date.getSeconds(); + }); + d3_time.seconds = d3_time.second.range; + d3_time.seconds.utc = d3_time.second.utc.range; + d3_time.minute = d3_time_interval(function(date) { + return new d3_date(Math.floor(date / 6e4) * 6e4); + }, function(date, offset) { + date.setTime(date.getTime() + Math.floor(offset) * 6e4); + }, function(date) { + return date.getMinutes(); + }); + d3_time.minutes = d3_time.minute.range; + d3_time.minutes.utc = d3_time.minute.utc.range; + d3_time.hour = d3_time_interval(function(date) { + var timezone = date.getTimezoneOffset() / 60; + return new d3_date((Math.floor(date / 36e5 - timezone) + timezone) * 36e5); + }, function(date, offset) { + date.setTime(date.getTime() + Math.floor(offset) * 36e5); + }, function(date) { + return date.getHours(); + }); + d3_time.hours = d3_time.hour.range; + d3_time.hours.utc = d3_time.hour.utc.range; + d3_time.month = d3_time_interval(function(date) { + date = d3_time.day(date); + date.setDate(1); + return date; + }, function(date, offset) { + date.setMonth(date.getMonth() + offset); + }, function(date) { + return date.getMonth(); + }); + d3_time.months = d3_time.month.range; + d3_time.months.utc = d3_time.month.utc.range; + function d3_time_scale(linear, methods, format) { + function scale(x) { + return linear(x); + } + scale.invert = function(x) { + return d3_time_scaleDate(linear.invert(x)); + }; + scale.domain = function(x) { + if (!arguments.length) return linear.domain().map(d3_time_scaleDate); + linear.domain(x); + return scale; + }; + function tickMethod(extent, count) { + var span = extent[1] - extent[0], target = span / count, i = d3.bisect(d3_time_scaleSteps, target); + return i == d3_time_scaleSteps.length ? [ methods.year, d3_scale_linearTickRange(extent.map(function(d) { + return d / 31536e6; + }), count)[2] ] : !i ? [ d3_time_scaleMilliseconds, d3_scale_linearTickRange(extent, count)[2] ] : methods[target / d3_time_scaleSteps[i - 1] < d3_time_scaleSteps[i] / target ? i - 1 : i]; + } + scale.nice = function(interval, skip) { + var domain = scale.domain(), extent = d3_scaleExtent(domain), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" && tickMethod(extent, interval); + if (method) interval = method[0], skip = method[1]; + function skipped(date) { + return !isNaN(date) && !interval.range(date, d3_time_scaleDate(+date + 1), skip).length; + } + return scale.domain(d3_scale_nice(domain, skip > 1 ? { + floor: function(date) { + while (skipped(date = interval.floor(date))) date = d3_time_scaleDate(date - 1); + return date; + }, + ceil: function(date) { + while (skipped(date = interval.ceil(date))) date = d3_time_scaleDate(+date + 1); + return date; + } + } : interval)); + }; + scale.ticks = function(interval, skip) { + var extent = d3_scaleExtent(scale.domain()), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" ? tickMethod(extent, interval) : !interval.range && [ { + range: interval + }, skip ]; + if (method) interval = method[0], skip = method[1]; + return interval.range(extent[0], d3_time_scaleDate(+extent[1] + 1), skip < 1 ? 1 : skip); + }; + scale.tickFormat = function() { + return format; + }; + scale.copy = function() { + return d3_time_scale(linear.copy(), methods, format); + }; + return d3_scale_linearRebind(scale, linear); + } + function d3_time_scaleDate(t) { + return new Date(t); + } + var d3_time_scaleSteps = [ 1e3, 5e3, 15e3, 3e4, 6e4, 3e5, 9e5, 18e5, 36e5, 108e5, 216e5, 432e5, 864e5, 1728e5, 6048e5, 2592e6, 7776e6, 31536e6 ]; + var d3_time_scaleLocalMethods = [ [ d3_time.second, 1 ], [ d3_time.second, 5 ], [ d3_time.second, 15 ], [ d3_time.second, 30 ], [ d3_time.minute, 1 ], [ d3_time.minute, 5 ], [ d3_time.minute, 15 ], [ d3_time.minute, 30 ], [ d3_time.hour, 1 ], [ d3_time.hour, 3 ], [ d3_time.hour, 6 ], [ d3_time.hour, 12 ], [ d3_time.day, 1 ], [ d3_time.day, 2 ], [ d3_time.week, 1 ], [ d3_time.month, 1 ], [ d3_time.month, 3 ], [ d3_time.year, 1 ] ]; + var d3_time_scaleLocalFormat = d3_time_format.multi([ [ ".%L", function(d) { + return d.getMilliseconds(); + } ], [ ":%S", function(d) { + return d.getSeconds(); + } ], [ "%I:%M", function(d) { + return d.getMinutes(); + } ], [ "%I %p", function(d) { + return d.getHours(); + } ], [ "%a %d", function(d) { + return d.getDay() && d.getDate() != 1; + } ], [ "%b %d", function(d) { + return d.getDate() != 1; + } ], [ "%B", function(d) { + return d.getMonth(); + } ], [ "%Y", d3_true ] ]); + var d3_time_scaleMilliseconds = { + range: function(start, stop, step) { + return d3.range(Math.ceil(start / step) * step, +stop, step).map(d3_time_scaleDate); + }, + floor: d3_identity, + ceil: d3_identity + }; + d3_time_scaleLocalMethods.year = d3_time.year; + d3_time.scale = function() { + return d3_time_scale(d3.scale.linear(), d3_time_scaleLocalMethods, d3_time_scaleLocalFormat); + }; + var d3_time_scaleUtcMethods = d3_time_scaleLocalMethods.map(function(m) { + return [ m[0].utc, m[1] ]; + }); + var d3_time_scaleUtcFormat = d3_time_formatUtc.multi([ [ ".%L", function(d) { + return d.getUTCMilliseconds(); + } ], [ ":%S", function(d) { + return d.getUTCSeconds(); + } ], [ "%I:%M", function(d) { + return d.getUTCMinutes(); + } ], [ "%I %p", function(d) { + return d.getUTCHours(); + } ], [ "%a %d", function(d) { + return d.getUTCDay() && d.getUTCDate() != 1; + } ], [ "%b %d", function(d) { + return d.getUTCDate() != 1; + } ], [ "%B", function(d) { + return d.getUTCMonth(); + } ], [ "%Y", d3_true ] ]); + d3_time_scaleUtcMethods.year = d3_time.year.utc; + d3_time.scale.utc = function() { + return d3_time_scale(d3.scale.linear(), d3_time_scaleUtcMethods, d3_time_scaleUtcFormat); + }; + d3.text = d3_xhrType(function(request) { + return request.responseText; + }); + d3.json = function(url, callback) { + return d3_xhr(url, "application/json", d3_json, callback); + }; + function d3_json(request) { + return JSON.parse(request.responseText); + } + d3.html = function(url, callback) { + return d3_xhr(url, "text/html", d3_html, callback); + }; + function d3_html(request) { + var range = d3_document.createRange(); + range.selectNode(d3_document.body); + return range.createContextualFragment(request.responseText); + } + d3.xml = d3_xhrType(function(request) { + return request.responseXML; + }); + if (typeof define === "function" && define.amd) this.d3 = d3, define(d3); else if (typeof module === "object" && module.exports) module.exports = d3; else this.d3 = d3; +}();
\ No newline at end of file diff --git a/debian/missing-sources/datamaps.world.js b/debian/missing-sources/datamaps.world.js new file mode 100644 index 0000000..492eaeb --- /dev/null +++ b/debian/missing-sources/datamaps.world.js @@ -0,0 +1,12241 @@ +(function() { + var svg; + + //save off default references + var d3 = window.d3, topojson = window.topojson; + + var defaultOptions = { + scope: 'world', + responsive: false, + setProjection: setProjection, + projection: 'equirectangular', + dataType: 'json', + data: {}, + done: function() {}, + fills: { + defaultFill: '#ABDDA4' + }, + geographyConfig: { + dataUrl: null, + hideAntarctica: true, + borderWidth: 1, + borderColor: '#FDFDFD', + popupTemplate: function(geography, data) { + return '<div class="hoverinfo"><strong>' + geography.properties.name + '</strong></div>'; + }, + popupOnHover: true, + highlightOnHover: true, + highlightFillColor: '#FC8D59', + highlightBorderColor: 'rgba(250, 15, 160, 0.2)', + highlightBorderWidth: 2 + }, + projectionConfig: { + rotation: [97, 0] + }, + bubblesConfig: { + borderWidth: 2, + borderColor: '#FFFFFF', + popupOnHover: true, + radius: null, + popupTemplate: function(geography, data) { + return '<div class="hoverinfo"><strong>' + data.name + '</strong></div>'; + }, + fillOpacity: 0.75, + animate: true, + highlightOnHover: true, + highlightFillColor: '#FC8D59', + highlightBorderColor: 'rgba(250, 15, 160, 0.2)', + highlightBorderWidth: 2, + highlightFillOpacity: 0.85, + exitDelay: 100 + }, + arcConfig: { + strokeColor: '#DD1C77', + strokeWidth: 1, + arcSharpness: 1, + animationSpeed: 600 + } + }; + + /* + Getter for value. If not declared on datumValue, look up the chain into optionsValue + */ + function val( datumValue, optionsValue, context ) { + if ( typeof context === 'undefined' ) { + context = optionsValue; + optionsValues = undefined; + } + var value = typeof datumValue !== 'undefined' ? datumValue : optionsValue; + + if (typeof value === 'undefined') { + return null; + } + + if ( typeof value === 'function' ) { + var fnContext = [context]; + if ( context.geography ) { + fnContext = [context.geography, context.data]; + } + return value.apply(null, fnContext); + } + else { + return value; + } + } + + function addContainer( element, height, width ) { + this.svg = d3.select( element ).append('svg') + .attr('width', width || element.offsetWidth) + .attr('data-width', width || element.offsetWidth) + .attr('class', 'datamap') + .attr('height', height || element.offsetHeight) + .style('overflow', 'hidden'); // IE10+ doesn't respect height/width when map is zoomed in + + if (this.options.responsive) { + d3.select(this.options.element).style({'position': 'relative', 'padding-bottom': '60%'}); + d3.select(this.options.element).select('svg').style({'position': 'absolute', 'width': '100%', 'height': '100%'}); + d3.select(this.options.element).select('svg').select('g').selectAll('path').style('vector-effect', 'non-scaling-stroke'); + + } + + return this.svg; + } + + // setProjection takes the svg element and options + function setProjection( element, options ) { + var width = options.width || element.offsetWidth; + var height = options.height || element.offsetHeight; + var projection, path; + var svg = this.svg; + + if ( options && typeof options.scope === 'undefined') { + options.scope = 'world'; + } + + if ( options.scope === 'usa' ) { + projection = d3.geo.albersUsa() + .scale(width) + .translate([width / 2, height / 2]); + } + else if ( options.scope === 'world' ) { + projection = d3.geo[options.projection]() + .scale((width + 1) / 2 / Math.PI) + .translate([width / 2, height / (options.projection === "mercator" ? 1.45 : 1.8)]); + } + + if ( options.projection === 'orthographic' ) { + + svg.append("defs").append("path") + .datum({type: "Sphere"}) + .attr("id", "sphere") + .attr("d", path); + + svg.append("use") + .attr("class", "stroke") + .attr("xlink:href", "#sphere"); + + svg.append("use") + .attr("class", "fill") + .attr("xlink:href", "#sphere"); + projection.scale(250).clipAngle(90).rotate(options.projectionConfig.rotation) + } + + path = d3.geo.path() + .projection( projection ); + + return {path: path, projection: projection}; + } + + function addStyleBlock() { + if ( d3.select('.datamaps-style-block').empty() ) { + d3.select('head').append('style').attr('class', 'datamaps-style-block') + .html('.datamap path.datamaps-graticule { fill: none; stroke: #777; stroke-width: 0.5px; stroke-opacity: .5; pointer-events: none; } .datamap .labels {pointer-events: none;} .datamap path {stroke: #FFFFFF; stroke-width: 1px;} .datamaps-legend dt, .datamaps-legend dd { float: left; margin: 0 3px 0 0;} .datamaps-legend dd {width: 20px; margin-right: 6px; border-radius: 3px;} .datamaps-legend {padding-bottom: 20px; z-index: 1001; position: absolute; left: 4px; font-size: 12px; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;} .datamaps-hoverover {display: none; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; } .hoverinfo {padding: 4px; border-radius: 1px; background-color: #FFF; box-shadow: 1px 1px 5px #CCC; font-size: 12px; border: 1px solid #CCC; } .hoverinfo hr {border:1px dotted #CCC; }'); + } + } + + function drawSubunits( data ) { + var fillData = this.options.fills, + colorCodeData = this.options.data || {}, + geoConfig = this.options.geographyConfig; + + + var subunits = this.svg.select('g.datamaps-subunits'); + if ( subunits.empty() ) { + subunits = this.addLayer('datamaps-subunits', null, true); + } + + var geoData = topojson.feature( data, data.objects[ this.options.scope ] ).features; + if ( geoConfig.hideAntarctica ) { + geoData = geoData.filter(function(feature) { + return feature.id !== "ATA"; + }); + } + + var geo = subunits.selectAll('path.datamaps-subunit').data( geoData ); + + geo.enter() + .append('path') + .attr('d', this.path) + .attr('class', function(d) { + return 'datamaps-subunit ' + d.id; + }) + .attr('data-info', function(d) { + return JSON.stringify( colorCodeData[d.id]); + }) + .style('fill', function(d) { + //if fillKey - use that + //otherwise check 'fill' + //otherwise check 'defaultFill' + var fillColor; + + var datum = colorCodeData[d.id]; + if ( datum && datum.fillKey ) { + fillColor = fillData[ val(datum.fillKey, {data: colorCodeData[d.id], geography: d}) ]; + } + + if ( typeof fillColor === 'undefined' ) { + fillColor = val(datum && datum.fillColor, fillData.defaultFill, {data: colorCodeData[d.id], geography: d}); + } + + return fillColor; + }) + .style('stroke-width', geoConfig.borderWidth) + .style('stroke', geoConfig.borderColor); + } + + function handleGeographyConfig () { + var hoverover; + var svg = this.svg; + var self = this; + var options = this.options.geographyConfig; + + if ( options.highlightOnHover || options.popupOnHover ) { + svg.selectAll('.datamaps-subunit') + .on('mouseover', function(d) { + var $this = d3.select(this); + var datum = self.options.data[d.id] || {}; + if ( options.highlightOnHover ) { + var previousAttributes = { + 'fill': $this.style('fill'), + 'stroke': $this.style('stroke'), + 'stroke-width': $this.style('stroke-width'), + 'fill-opacity': $this.style('fill-opacity') + }; + + $this + .style('fill', val(datum.highlightFillColor, options.highlightFillColor, datum)) + .style('stroke', val(datum.highlightBorderColor, options.highlightBorderColor, datum)) + .style('stroke-width', val(datum.highlightBorderWidth, options.highlightBorderWidth, datum)) + .style('fill-opacity', val(datum.highlightFillOpacity, options.highlightFillOpacity, datum)) + .attr('data-previousAttributes', JSON.stringify(previousAttributes)); + + //as per discussion on https://github.com/markmarkoh/datamaps/issues/19 + if ( ! /((MSIE)|(Trident))/.test ) { + moveToFront.call(this); + } + } + + if ( options.popupOnHover ) { + self.updatePopup($this, d, options, svg); + } + }) + .on('mouseout', function() { + var $this = d3.select(this); + + if (options.highlightOnHover) { + //reapply previous attributes + var previousAttributes = JSON.parse( $this.attr('data-previousAttributes') ); + for ( var attr in previousAttributes ) { + $this.style(attr, previousAttributes[attr]); + } + } + $this.on('mousemove', null); + d3.selectAll('.datamaps-hoverover').style('display', 'none'); + }); + } + + function moveToFront() { + this.parentNode.appendChild(this); + } + } + + //plugin to add a simple map legend + function addLegend(layer, data, options) { + data = data || {}; + if ( !this.options.fills ) { + return; + } + + var html = '<dl>'; + var label = ''; + if ( data.legendTitle ) { + html = '<h2>' + data.legendTitle + '</h2>' + html; + } + for ( var fillKey in this.options.fills ) { + + if ( fillKey === 'defaultFill') { + if (! data.defaultFillName ) { + continue; + } + label = data.defaultFillName; + } else { + if (data.labels && data.labels[fillKey]) { + label = data.labels[fillKey]; + } else { + label= fillKey + ': '; + } + } + html += '<dt>' + label + '</dt>'; + html += '<dd style="background-color:' + this.options.fills[fillKey] + '"> </dd>'; + } + html += '</dl>'; + + var hoverover = d3.select( this.options.element ).append('div') + .attr('class', 'datamaps-legend') + .html(html); + } + + function addGraticule ( layer, options ) { + var graticule = d3.geo.graticule(); + this.svg.insert("path", '.datamaps-subunits') + .datum(graticule) + .attr("class", "datamaps-graticule") + .attr("d", this.path); + } + + function handleArcs (layer, data, options) { + var self = this, + svg = this.svg; + + if ( !data || (data && !data.slice) ) { + throw "Datamaps Error - arcs must be an array"; + } + + // For some reason arc options were put in an `options` object instead of the parent arc + // I don't like this, so to match bubbles and other plugins I'm moving it + // This is to keep backwards compatability + for ( var i = 0; i < data.length; i++ ) { + data[i] = defaults(data[i], data[i].options); + delete data[i].options; + } + + if ( typeof options === "undefined" ) { + options = defaultOptions.arcConfig; + } + + var arcs = layer.selectAll('path.datamaps-arc').data( data, JSON.stringify ); + + var path = d3.geo.path() + .projection(self.projection); + + arcs + .enter() + .append('svg:path') + .attr('class', 'datamaps-arc') + .style('stroke-linecap', 'round') + .style('stroke', function(datum) { + return val(datum.strokeColor, options.strokeColor, datum); + }) + .style('fill', 'none') + .style('stroke-width', function(datum) { + return val(datum.strokeWidth, options.strokeWidth, datum); + }) + .attr('d', function(datum) { + var originXY = self.latLngToXY(val(datum.origin.latitude, datum), val(datum.origin.longitude, datum)) + var destXY = self.latLngToXY(val(datum.destination.latitude, datum), val(datum.destination.longitude, datum)); + var midXY = [ (originXY[0] + destXY[0]) / 2, (originXY[1] + destXY[1]) / 2]; + if (options.greatArc) { + // TODO: Move this to inside `if` clause when setting attr `d` + var greatArc = d3.geo.greatArc() + .source(function(d) { return [val(d.origin.longitude, d), val(d.origin.latitude, d)]; }) + .target(function(d) { return [val(d.destination.longitude, d), val(d.destination.latitude, d)]; }); + + return path(greatArc(datum)) + } + var sharpness = val(datum.arcSharpness, options.arcSharpness, datum); + return "M" + originXY[0] + ',' + originXY[1] + "S" + (midXY[0] + (50 * sharpness)) + "," + (midXY[1] - (75 * sharpness)) + "," + destXY[0] + "," + destXY[1]; + }) + .transition() + .delay(100) + .style('fill', function(datum) { + /* + Thank you Jake Archibald, this is awesome. + Source: http://jakearchibald.com/2013/animated-line-drawing-svg/ + */ + var length = this.getTotalLength(); + this.style.transition = this.style.WebkitTransition = 'none'; + this.style.strokeDasharray = length + ' ' + length; + this.style.strokeDashoffset = length; + this.getBoundingClientRect(); + this.style.transition = this.style.WebkitTransition = 'stroke-dashoffset ' + val(datum.animationSpeed, options.animationSpeed, datum) + 'ms ease-out'; + this.style.strokeDashoffset = '0'; + return 'none'; + }) + + arcs.exit() + .transition() + .style('opacity', 0) + .remove(); + } + + function handleLabels ( layer, options ) { + var self = this; + options = options || {}; + var labelStartCoodinates = this.projection([-67.707617, 42.722131]); + this.svg.selectAll(".datamaps-subunit") + .attr("data-foo", function(d) { + var center = self.path.centroid(d); + var xOffset = 7.5, yOffset = 5; + + if ( ["FL", "KY", "MI"].indexOf(d.id) > -1 ) xOffset = -2.5; + if ( d.id === "NY" ) xOffset = -1; + if ( d.id === "MI" ) yOffset = 18; + if ( d.id === "LA" ) xOffset = 13; + + var x,y; + + x = center[0] - xOffset; + y = center[1] + yOffset; + + var smallStateIndex = ["VT", "NH", "MA", "RI", "CT", "NJ", "DE", "MD", "DC"].indexOf(d.id); + if ( smallStateIndex > -1) { + var yStart = labelStartCoodinates[1]; + x = labelStartCoodinates[0]; + y = yStart + (smallStateIndex * (2+ (options.fontSize || 12))); + layer.append("line") + .attr("x1", x - 3) + .attr("y1", y - 5) + .attr("x2", center[0]) + .attr("y2", center[1]) + .style("stroke", options.labelColor || "#000") + .style("stroke-width", options.lineWidth || 1) + } + + layer.append("text") + .attr("x", x) + .attr("y", y) + .style("font-size", (options.fontSize || 10) + 'px') + .style("font-family", options.fontFamily || "Verdana") + .style("fill", options.labelColor || "#000") + .text( d.id ); + return "bar"; + }); + } + + + function handleBubbles (layer, data, options ) { + var self = this, + fillData = this.options.fills, + svg = this.svg; + + if ( !data || (data && !data.slice) ) { + throw "Datamaps Error - bubbles must be an array"; + } + + var bubbles = layer.selectAll('circle.datamaps-bubble').data( data, JSON.stringify ); + + bubbles + .enter() + .append('svg:circle') + .attr('class', 'datamaps-bubble') + .attr('cx', function ( datum ) { + var latLng; + if ( datumHasCoords(datum) ) { + latLng = self.latLngToXY(datum.latitude, datum.longitude); + } + else if ( datum.centered ) { + latLng = self.path.centroid(svg.select('path.' + datum.centered).data()[0]); + } + if ( latLng ) return latLng[0]; + }) + .attr('cy', function ( datum ) { + var latLng; + if ( datumHasCoords(datum) ) { + latLng = self.latLngToXY(datum.latitude, datum.longitude); + } + else if ( datum.centered ) { + latLng = self.path.centroid(svg.select('path.' + datum.centered).data()[0]); + } + if ( latLng ) return latLng[1];; + }) + .attr('r', 0) //for animation purposes + .attr('data-info', function(d) { + return JSON.stringify(d); + }) + .style('stroke', function ( datum ) { + return val(datum.borderColor, options.borderColor, datum); + }) + .style('stroke-width', function ( datum ) { + return val(datum.borderWidth, options.borderWidth, datum); + }) + .style('fill-opacity', function ( datum ) { + return val(datum.fillOpacity, options.fillOpacity, datum); + }) + .style('fill', function ( datum ) { + var fillColor = fillData[ val(datum.fillKey, options.fillKey, datum) ]; + return fillColor || fillData.defaultFill; + }) + .on('mouseover', function ( datum ) { + var $this = d3.select(this); + + if (options.highlightOnHover) { + //save all previous attributes for mouseout + var previousAttributes = { + 'fill': $this.style('fill'), + 'stroke': $this.style('stroke'), + 'stroke-width': $this.style('stroke-width'), + 'fill-opacity': $this.style('fill-opacity') + }; + + $this + .style('fill', val(datum.highlightFillColor, options.highlightFillColor, datum)) + .style('stroke', val(datum.highlightBorderColor, options.highlightBorderColor, datum)) + .style('stroke-width', val(datum.highlightBorderWidth, options.highlightBorderWidth, datum)) + .style('fill-opacity', val(datum.highlightFillOpacity, options.highlightFillOpacity, datum)) + .attr('data-previousAttributes', JSON.stringify(previousAttributes)); + } + + if (options.popupOnHover) { + self.updatePopup($this, datum, options, svg); + } + }) + .on('mouseout', function ( datum ) { + var $this = d3.select(this); + + if (options.highlightOnHover) { + //reapply previous attributes + var previousAttributes = JSON.parse( $this.attr('data-previousAttributes') ); + for ( var attr in previousAttributes ) { + $this.style(attr, previousAttributes[attr]); + } + } + + d3.selectAll('.datamaps-hoverover').style('display', 'none'); + }) + .transition().duration(400) + .attr('r', function ( datum ) { + return val(datum.radius, options.radius, datum); + }); + + bubbles.exit() + .transition() + .delay(options.exitDelay) + .attr("r", 0) + .remove(); + + function datumHasCoords (datum) { + return typeof datum !== 'undefined' && typeof datum.latitude !== 'undefined' && typeof datum.longitude !== 'undefined'; + } + + } + + //stolen from underscore.js + function defaults(obj) { + Array.prototype.slice.call(arguments, 1).forEach(function(source) { + if (source) { + for (var prop in source) { + if (obj[prop] == null) obj[prop] = source[prop]; + } + } + }); + return obj; + } + /************************************** + Public Functions + ***************************************/ + + function Datamap( options ) { + + if ( typeof d3 === 'undefined' || typeof topojson === 'undefined' ) { + throw new Error('Include d3.js (v3.0.3 or greater) and topojson on this page before creating a new map'); + } + //set options for global use + this.options = defaults(options, defaultOptions); + this.options.geographyConfig = defaults(options.geographyConfig, defaultOptions.geographyConfig); + this.options.projectionConfig = defaults(options.projectionConfig, defaultOptions.projectionConfig); + this.options.bubblesConfig = defaults(options.bubblesConfig, defaultOptions.bubblesConfig); + this.options.arcConfig = defaults(options.arcConfig, defaultOptions.arcConfig); + + //add the SVG container + if ( d3.select( this.options.element ).select('svg').length > 0 ) { + addContainer.call(this, this.options.element, this.options.height, this.options.width ); + } + + /* Add core plugins to this instance */ + this.addPlugin('bubbles', handleBubbles); + this.addPlugin('legend', addLegend); + this.addPlugin('arc', handleArcs); + this.addPlugin('labels', handleLabels); + this.addPlugin('graticule', addGraticule); + + //append style block with basic hoverover styles + if ( ! this.options.disableDefaultStyles ) { + addStyleBlock(); + } + + return this.draw(); + } + + // resize map + Datamap.prototype.resize = function () { + + var self = this; + var options = self.options; + + if (options.responsive) { + var prefix = '-webkit-transform' in document.body.style ? '-webkit-' : '-moz-transform' in document.body.style ? '-moz-' : '-ms-transform' in document.body.style ? '-ms-' : '', + newsize = options.element.clientWidth, + oldsize = d3.select( options.element).select('svg').attr('data-width'); + + d3.select(options.element).select('svg').selectAll('g').style(prefix + 'transform', 'scale(' + (newsize / oldsize) + ')'); + } + } + + // actually draw the features(states & countries) + Datamap.prototype.draw = function() { + //save off in a closure + var self = this; + var options = self.options; + + //set projections and paths based on scope + var pathAndProjection = options.setProjection.apply(self, [options.element, options] ); + + this.path = pathAndProjection.path; + this.projection = pathAndProjection.projection; + + //if custom URL for topojson data, retrieve it and render + if ( options.geographyConfig.dataUrl ) { + d3.json( options.geographyConfig.dataUrl, function(error, results) { + if ( error ) throw new Error(error); + self.customTopo = results; + draw( results ); + }); + } + else { + draw( this[options.scope + 'Topo'] || options.geographyConfig.dataJson); + } + + return this; + + function draw (data) { + // if fetching remote data, draw the map first then call `updateChoropleth` + if ( self.options.dataUrl ) { + //allow for csv or json data types + d3[self.options.dataType](self.options.dataUrl, function(data) { + //in the case of csv, transform data to object + if ( self.options.dataType === 'csv' && (data && data.slice) ) { + var tmpData = {}; + for(var i = 0; i < data.length; i++) { + tmpData[data[i].id] = data[i]; + } + data = tmpData; + } + Datamaps.prototype.updateChoropleth.call(self, data); + }); + } + drawSubunits.call(self, data); + handleGeographyConfig.call(self); + + if ( self.options.geographyConfig.popupOnHover || self.options.bubblesConfig.popupOnHover) { + hoverover = d3.select( self.options.element ).append('div') + .attr('class', 'datamaps-hoverover') + .style('z-index', 10001) + .style('position', 'absolute'); + } + + //fire off finished callback + self.options.done(self); + } + }; + /************************************** + TopoJSON + ***************************************/ + Datamap.prototype.worldTopo = { + "type": "Topology", + "objects": { + "world": { + "type": "GeometryCollection", + "geometries": [{ + "type": "Polygon", + "properties": { + "name": "Afghanistan" + }, + "id": "AFG", + "arcs": [ + [0, 1, 2, 3, 4, 5] + ] + }, { + "type": "MultiPolygon", + "properties": { + "name": "Angola" + }, + "id": "AGO", + "arcs": [ + [ + [6, 7, 8, 9] + ], + [ + [10, 11, 12] + ] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Albania" + }, + "id": "ALB", + "arcs": [ + [13, 14, 15, 16, 17] + ] + }, { + "type": "Polygon", + "properties": { + "name": "United Arab Emirates" + }, + "id": "ARE", + "arcs": [ + [18, 19, 20, 21, 22] + ] + }, { + "type": "MultiPolygon", + "properties": { + "name": "Argentina" + }, + "id": "ARG", + "arcs": [ + [ + [23, 24] + ], + [ + [25, 26, 27, 28, 29, 30] + ] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Armenia" + }, + "id": "ARM", + "arcs": [ + [31, 32, 33, 34, 35] + ] + }, { + "type": "MultiPolygon", + "properties": { + "name": "Antarctica" + }, + "id": "ATA", + "arcs": [ + [ + [36] + ], + [ + [37] + ], + [ + [38] + ], + [ + [39] + ], + [ + [40] + ], + [ + [41] + ], + [ + [42] + ], + [ + [43] + ] + ] + }, { + "type": "Polygon", + "properties": { + "name": "French Southern and Antarctic Lands" + }, + "id": "ATF", + "arcs": [ + [44] + ] + }, { + "type": "MultiPolygon", + "properties": { + "name": "Australia" + }, + "id": "AUS", + "arcs": [ + [ + [45] + ], + [ + [46] + ] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Austria" + }, + "id": "AUT", + "arcs": [ + [47, 48, 49, 50, 51, 52, 53] + ] + }, { + "type": "MultiPolygon", + "properties": { + "name": "Azerbaijan" + }, + "id": "AZE", + "arcs": [ + [ + [54, -35] + ], + [ + [55, 56, -33, 57, 58] + ] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Burundi" + }, + "id": "BDI", + "arcs": [ + [59, 60, 61] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Belgium" + }, + "id": "BEL", + "arcs": [ + [62, 63, 64, 65, 66] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Benin" + }, + "id": "BEN", + "arcs": [ + [67, 68, 69, 70, 71] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Burkina Faso" + }, + "id": "BFA", + "arcs": [ + [72, 73, 74, -70, 75, 76] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Bangladesh" + }, + "id": "BGD", + "arcs": [ + [77, 78, 79] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Bulgaria" + }, + "id": "BGR", + "arcs": [ + [80, 81, 82, 83, 84, 85] + ] + }, { + "type": "MultiPolygon", + "properties": { + "name": "The Bahamas" + }, + "id": "BHS", + "arcs": [ + [ + [86] + ], + [ + [87] + ], + [ + [88] + ] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Bosnia and Herzegovina" + }, + "id": "BIH", + "arcs": [ + [89, 90, 91] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Belarus" + }, + "id": "BLR", + "arcs": [ + [92, 93, 94, 95, 96] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Belize" + }, + "id": "BLZ", + "arcs": [ + [97, 98, 99] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Bolivia" + }, + "id": "BOL", + "arcs": [ + [100, 101, 102, 103, -31] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Brazil" + }, + "id": "BRA", + "arcs": [ + [-27, 104, -103, 105, 106, 107, 108, 109, 110, 111, 112] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Brunei" + }, + "id": "BRN", + "arcs": [ + [113, 114] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Bhutan" + }, + "id": "BTN", + "arcs": [ + [115, 116] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Botswana" + }, + "id": "BWA", + "arcs": [ + [117, 118, 119, 120] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Central African Republic" + }, + "id": "CAF", + "arcs": [ + [121, 122, 123, 124, 125, 126, 127] + ] + }, { + "type": "MultiPolygon", + "properties": { + "name": "Canada" + }, + "id": "CAN", + "arcs": [ + [ + [128] + ], + [ + [129] + ], + [ + [130] + ], + [ + [131] + ], + [ + [132] + ], + [ + [133] + ], + [ + [134] + ], + [ + [135] + ], + [ + [136] + ], + [ + [137] + ], + [ + [138, 139, 140, 141] + ], + [ + [142] + ], + [ + [143] + ], + [ + [144] + ], + [ + [145] + ], + [ + [146] + ], + [ + [147] + ], + [ + [148] + ], + [ + [149] + ], + [ + [150] + ], + [ + [151] + ], + [ + [152] + ], + [ + [153] + ], + [ + [154] + ], + [ + [155] + ], + [ + [156] + ], + [ + [157] + ], + [ + [158] + ], + [ + [159] + ], + [ + [160] + ] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Switzerland" + }, + "id": "CHE", + "arcs": [ + [-51, 161, 162, 163] + ] + }, { + "type": "MultiPolygon", + "properties": { + "name": "Chile" + }, + "id": "CHL", + "arcs": [ + [ + [-24, 164] + ], + [ + [-30, 165, 166, -101] + ] + ] + }, { + "type": "MultiPolygon", + "properties": { + "name": "China" + }, + "id": "CHN", + "arcs": [ + [ + [167] + ], + [ + [168, 169, 170, 171, 172, 173, -117, 174, 175, 176, 177, -4, 178, 179, 180, 181, 182, 183] + ] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Ivory Coast" + }, + "id": "CIV", + "arcs": [ + [184, 185, 186, 187, -73, 188] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Cameroon" + }, + "id": "CMR", + "arcs": [ + [189, 190, 191, 192, 193, 194, -128, 195] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Democratic Republic of the Congo" + }, + "id": "COD", + "arcs": [ + [196, 197, -60, 198, 199, -10, 200, -13, 201, -126, 202] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Republic of the Congo" + }, + "id": "COG", + "arcs": [ + [-12, 203, 204, -196, -127, -202] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Colombia" + }, + "id": "COL", + "arcs": [ + [205, 206, 207, 208, 209, -107, 210] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Costa Rica" + }, + "id": "CRI", + "arcs": [ + [211, 212, 213, 214] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Cuba" + }, + "id": "CUB", + "arcs": [ + [215] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Northern Cyprus" + }, + "id": "-99", + "arcs": [ + [216, 217] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Cyprus" + }, + "id": "CYP", + "arcs": [ + [218, -218] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Czech Republic" + }, + "id": "CZE", + "arcs": [ + [-53, 219, 220, 221] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Germany" + }, + "id": "DEU", + "arcs": [ + [222, 223, -220, -52, -164, 224, 225, -64, 226, 227, 228] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Djibouti" + }, + "id": "DJI", + "arcs": [ + [229, 230, 231, 232] + ] + }, { + "type": "MultiPolygon", + "properties": { + "name": "Denmark" + }, + "id": "DNK", + "arcs": [ + [ + [233] + ], + [ + [-229, 234] + ] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Dominican Republic" + }, + "id": "DOM", + "arcs": [ + [235, 236] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Algeria" + }, + "id": "DZA", + "arcs": [ + [237, 238, 239, 240, 241, 242, 243, 244] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Ecuador" + }, + "id": "ECU", + "arcs": [ + [245, -206, 246] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Egypt" + }, + "id": "EGY", + "arcs": [ + [247, 248, 249, 250, 251] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Eritrea" + }, + "id": "ERI", + "arcs": [ + [252, 253, 254, -233] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Spain" + }, + "id": "ESP", + "arcs": [ + [255, 256, 257, 258] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Estonia" + }, + "id": "EST", + "arcs": [ + [259, 260, 261] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Ethiopia" + }, + "id": "ETH", + "arcs": [ + [-232, 262, 263, 264, 265, 266, 267, -253] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Finland" + }, + "id": "FIN", + "arcs": [ + [268, 269, 270, 271] + ] + }, { + "type": "MultiPolygon", + "properties": { + "name": "Fiji" + }, + "id": "FJI", + "arcs": [ + [ + [272] + ], + [ + [273, 274] + ], + [ + [275, -275] + ] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Falkland Islands" + }, + "id": "FLK", + "arcs": [ + [276] + ] + }, { + "type": "MultiPolygon", + "properties": { + "name": "France" + }, + "id": "FRA", + "arcs": [ + [ + [277] + ], + [ + [278, -225, -163, 279, 280, -257, 281, -66] + ] + ] + }, { + "type": "Polygon", + "properties": { + "name": "French Guiana" + }, + "id": "GUF", + "arcs": [ + [282, 283, 284, 285, -111] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Gabon" + }, + "id": "GAB", + "arcs": [ + [286, 287, -190, -205] + ] + }, { + "type": "MultiPolygon", + "properties": { + "name": "United Kingdom" + }, + "id": "GBR", + "arcs": [ + [ + [288, 289] + ], + [ + [290] + ] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Georgia" + }, + "id": "GEO", + "arcs": [ + [291, 292, -58, -32, 293] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Ghana" + }, + "id": "GHA", + "arcs": [ + [294, -189, -77, 295] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Guinea" + }, + "id": "GIN", + "arcs": [ + [296, 297, 298, 299, 300, 301, -187] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Gambia" + }, + "id": "GMB", + "arcs": [ + [302, 303] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Guinea Bissau" + }, + "id": "GNB", + "arcs": [ + [304, 305, -300] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Equatorial Guinea" + }, + "id": "GNQ", + "arcs": [ + [306, -191, -288] + ] + }, { + "type": "MultiPolygon", + "properties": { + "name": "Greece" + }, + "id": "GRC", + "arcs": [ + [ + [307] + ], + [ + [308, -15, 309, -84, 310] + ] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Greenland" + }, + "id": "GRL", + "arcs": [ + [311] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Guatemala" + }, + "id": "GTM", + "arcs": [ + [312, 313, -100, 314, 315, 316] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Guyana" + }, + "id": "GUY", + "arcs": [ + [317, 318, -109, 319] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Honduras" + }, + "id": "HND", + "arcs": [ + [320, 321, -316, 322, 323] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Croatia" + }, + "id": "HRV", + "arcs": [ + [324, -92, 325, 326, 327, 328] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Haiti" + }, + "id": "HTI", + "arcs": [ + [-237, 329] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Hungary" + }, + "id": "HUN", + "arcs": [ + [-48, 330, 331, 332, 333, -329, 334] + ] + }, { + "type": "MultiPolygon", + "properties": { + "name": "Indonesia" + }, + "id": "IDN", + "arcs": [ + [ + [335] + ], + [ + [336, 337] + ], + [ + [338] + ], + [ + [339] + ], + [ + [340] + ], + [ + [341] + ], + [ + [342] + ], + [ + [343] + ], + [ + [344, 345] + ], + [ + [346] + ], + [ + [347] + ], + [ + [348, 349] + ], + [ + [350] + ] + ] + }, { + "type": "Polygon", + "properties": { + "name": "India" + }, + "id": "IND", + "arcs": [ + [-177, 351, -175, -116, -174, 352, -80, 353, 354] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Ireland" + }, + "id": "IRL", + "arcs": [ + [355, -289] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Iran" + }, + "id": "IRN", + "arcs": [ + [356, -6, 357, 358, 359, 360, -55, -34, -57, 361] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Iraq" + }, + "id": "IRQ", + "arcs": [ + [362, 363, 364, 365, 366, 367, -360] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Iceland" + }, + "id": "ISL", + "arcs": [ + [368] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Israel" + }, + "id": "ISR", + "arcs": [ + [369, 370, 371, -252, 372, 373, 374] + ] + }, { + "type": "MultiPolygon", + "properties": { + "name": "Italy" + }, + "id": "ITA", + "arcs": [ + [ + [375] + ], + [ + [376] + ], + [ + [377, 378, -280, -162, -50] + ] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Jamaica" + }, + "id": "JAM", + "arcs": [ + [379] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Jordan" + }, + "id": "JOR", + "arcs": [ + [-370, 380, -366, 381, 382, -372, 383] + ] + }, { + "type": "MultiPolygon", + "properties": { + "name": "Japan" + }, + "id": "JPN", + "arcs": [ + [ + [384] + ], + [ + [385] + ], + [ + [386] + ] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Kazakhstan" + }, + "id": "KAZ", + "arcs": [ + [387, 388, 389, 390, -181, 391] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Kenya" + }, + "id": "KEN", + "arcs": [ + [392, 393, 394, 395, -265, 396] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Kyrgyzstan" + }, + "id": "KGZ", + "arcs": [ + [-392, -180, 397, 398] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Cambodia" + }, + "id": "KHM", + "arcs": [ + [399, 400, 401, 402] + ] + }, { + "type": "Polygon", + "properties": { + "name": "South Korea" + }, + "id": "KOR", + "arcs": [ + [403, 404] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Kosovo" + }, + "id": "-99", + "arcs": [ + [-18, 405, 406, 407] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Kuwait" + }, + "id": "KWT", + "arcs": [ + [408, 409, -364] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Laos" + }, + "id": "LAO", + "arcs": [ + [410, 411, -172, 412, -401] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Lebanon" + }, + "id": "LBN", + "arcs": [ + [-374, 413, 414] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Liberia" + }, + "id": "LBR", + "arcs": [ + [415, 416, -297, -186] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Libya" + }, + "id": "LBY", + "arcs": [ + [417, -245, 418, 419, -250, 420, 421] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Sri Lanka" + }, + "id": "LKA", + "arcs": [ + [422] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Lesotho" + }, + "id": "LSO", + "arcs": [ + [423] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Lithuania" + }, + "id": "LTU", + "arcs": [ + [424, 425, 426, -93, 427] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Luxembourg" + }, + "id": "LUX", + "arcs": [ + [-226, -279, -65] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Latvia" + }, + "id": "LVA", + "arcs": [ + [428, -262, 429, -94, -427] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Morocco" + }, + "id": "MAR", + "arcs": [ + [-242, 430, 431] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Moldova" + }, + "id": "MDA", + "arcs": [ + [432, 433] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Madagascar" + }, + "id": "MDG", + "arcs": [ + [434] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Mexico" + }, + "id": "MEX", + "arcs": [ + [435, -98, -314, 436, 437] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Macedonia" + }, + "id": "MKD", + "arcs": [ + [-408, 438, -85, -310, -14] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Mali" + }, + "id": "MLI", + "arcs": [ + [439, -239, 440, -74, -188, -302, 441] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Myanmar" + }, + "id": "MMR", + "arcs": [ + [442, -78, -353, -173, -412, 443] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Montenegro" + }, + "id": "MNE", + "arcs": [ + [444, -326, -91, 445, -406, -17] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Mongolia" + }, + "id": "MNG", + "arcs": [ + [446, -183] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Mozambique" + }, + "id": "MOZ", + "arcs": [ + [447, 448, 449, 450, 451, 452, 453, 454] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Mauritania" + }, + "id": "MRT", + "arcs": [ + [455, 456, 457, -240, -440] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Malawi" + }, + "id": "MWI", + "arcs": [ + [-455, 458, 459] + ] + }, { + "type": "MultiPolygon", + "properties": { + "name": "Malaysia" + }, + "id": "MYS", + "arcs": [ + [ + [460, 461] + ], + [ + [-349, 462, -115, 463] + ] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Namibia" + }, + "id": "NAM", + "arcs": [ + [464, -8, 465, -119, 466] + ] + }, { + "type": "Polygon", + "properties": { + "name": "New Caledonia" + }, + "id": "NCL", + "arcs": [ + [467] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Niger" + }, + "id": "NER", + "arcs": [ + [-75, -441, -238, -418, 468, -194, 469, -71] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Nigeria" + }, + "id": "NGA", + "arcs": [ + [470, -72, -470, -193] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Nicaragua" + }, + "id": "NIC", + "arcs": [ + [471, -324, 472, -213] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Netherlands" + }, + "id": "NLD", + "arcs": [ + [-227, -63, 473] + ] + }, { + "type": "MultiPolygon", + "properties": { + "name": "Norway" + }, + "id": "NOR", + "arcs": [ + [ + [474, -272, 475, 476] + ], + [ + [477] + ], + [ + [478] + ], + [ + [479] + ] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Nepal" + }, + "id": "NPL", + "arcs": [ + [-352, -176] + ] + }, { + "type": "MultiPolygon", + "properties": { + "name": "New Zealand" + }, + "id": "NZL", + "arcs": [ + [ + [480] + ], + [ + [481] + ] + ] + }, { + "type": "MultiPolygon", + "properties": { + "name": "Oman" + }, + "id": "OMN", + "arcs": [ + [ + [482, 483, -22, 484] + ], + [ + [-20, 485] + ] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Pakistan" + }, + "id": "PAK", + "arcs": [ + [-178, -355, 486, -358, -5] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Panama" + }, + "id": "PAN", + "arcs": [ + [487, -215, 488, -208] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Peru" + }, + "id": "PER", + "arcs": [ + [-167, 489, -247, -211, -106, -102] + ] + }, { + "type": "MultiPolygon", + "properties": { + "name": "Philippines" + }, + "id": "PHL", + "arcs": [ + [ + [490] + ], + [ + [491] + ], + [ + [492] + ], + [ + [493] + ], + [ + [494] + ], + [ + [495] + ], + [ + [496] + ] + ] + }, { + "type": "MultiPolygon", + "properties": { + "name": "Papua New Guinea" + }, + "id": "PNG", + "arcs": [ + [ + [497] + ], + [ + [498] + ], + [ + [-345, 499] + ], + [ + [500] + ] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Poland" + }, + "id": "POL", + "arcs": [ + [-224, 501, 502, -428, -97, 503, 504, -221] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Puerto Rico" + }, + "id": "PRI", + "arcs": [ + [505] + ] + }, { + "type": "Polygon", + "properties": { + "name": "North Korea" + }, + "id": "PRK", + "arcs": [ + [506, 507, -405, 508, -169] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Portugal" + }, + "id": "PRT", + "arcs": [ + [-259, 509] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Paraguay" + }, + "id": "PRY", + "arcs": [ + [-104, -105, -26] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Qatar" + }, + "id": "QAT", + "arcs": [ + [510, 511] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Romania" + }, + "id": "ROU", + "arcs": [ + [512, -434, 513, 514, -81, 515, -333] + ] + }, { + "type": "MultiPolygon", + "properties": { + "name": "Russia" + }, + "id": "RUS", + "arcs": [ + [ + [516] + ], + [ + [-503, 517, -425] + ], + [ + [518, 519] + ], + [ + [520] + ], + [ + [521] + ], + [ + [522] + ], + [ + [523] + ], + [ + [524] + ], + [ + [525] + ], + [ + [526, -507, -184, -447, -182, -391, 527, -59, -293, 528, 529, -95, -430, -261, 530, -269, -475, 531, -520] + ], + [ + [532] + ], + [ + [533] + ], + [ + [534] + ] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Rwanda" + }, + "id": "RWA", + "arcs": [ + [535, -61, -198, 536] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Western Sahara" + }, + "id": "ESH", + "arcs": [ + [-241, -458, 537, -431] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Saudi Arabia" + }, + "id": "SAU", + "arcs": [ + [538, -382, -365, -410, 539, -512, 540, -23, -484, 541] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Sudan" + }, + "id": "SDN", + "arcs": [ + [542, 543, -123, 544, -421, -249, 545, -254, -268, 546] + ] + }, { + "type": "Polygon", + "properties": { + "name": "South Sudan" + }, + "id": "SSD", + "arcs": [ + [547, -266, -396, 548, -203, -125, 549, -543] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Senegal" + }, + "id": "SEN", + "arcs": [ + [550, -456, -442, -301, -306, 551, -304] + ] + }, { + "type": "MultiPolygon", + "properties": { + "name": "Solomon Islands" + }, + "id": "SLB", + "arcs": [ + [ + [552] + ], + [ + [553] + ], + [ + [554] + ], + [ + [555] + ], + [ + [556] + ] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Sierra Leone" + }, + "id": "SLE", + "arcs": [ + [557, -298, -417] + ] + }, { + "type": "Polygon", + "properties": { + "name": "El Salvador" + }, + "id": "SLV", + "arcs": [ + [558, -317, -322] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Somaliland" + }, + "id": "-99", + "arcs": [ + [-263, -231, 559, 560] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Somalia" + }, + "id": "SOM", + "arcs": [ + [-397, -264, -561, 561] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Republic of Serbia" + }, + "id": "SRB", + "arcs": [ + [-86, -439, -407, -446, -90, -325, -334, -516] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Suriname" + }, + "id": "SUR", + "arcs": [ + [562, -285, 563, -283, -110, -319] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Slovakia" + }, + "id": "SVK", + "arcs": [ + [-505, 564, -331, -54, -222] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Slovenia" + }, + "id": "SVN", + "arcs": [ + [-49, -335, -328, 565, -378] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Sweden" + }, + "id": "SWE", + "arcs": [ + [-476, -271, 566] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Swaziland" + }, + "id": "SWZ", + "arcs": [ + [567, -451] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Syria" + }, + "id": "SYR", + "arcs": [ + [-381, -375, -415, 568, 569, -367] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Chad" + }, + "id": "TCD", + "arcs": [ + [-469, -422, -545, -122, -195] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Togo" + }, + "id": "TGO", + "arcs": [ + [570, -296, -76, -69] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Thailand" + }, + "id": "THA", + "arcs": [ + [571, -462, 572, -444, -411, -400] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Tajikistan" + }, + "id": "TJK", + "arcs": [ + [-398, -179, -3, 573] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Turkmenistan" + }, + "id": "TKM", + "arcs": [ + [-357, 574, -389, 575, -1] + ] + }, { + "type": "Polygon", + "properties": { + "name": "East Timor" + }, + "id": "TLS", + "arcs": [ + [576, -337] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Trinidad and Tobago" + }, + "id": "TTO", + "arcs": [ + [577] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Tunisia" + }, + "id": "TUN", + "arcs": [ + [-244, 578, -419] + ] + }, { + "type": "MultiPolygon", + "properties": { + "name": "Turkey" + }, + "id": "TUR", + "arcs": [ + [ + [-294, -36, -361, -368, -570, 579] + ], + [ + [-311, -83, 580] + ] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Taiwan" + }, + "id": "TWN", + "arcs": [ + [581] + ] + }, { + "type": "Polygon", + "properties": { + "name": "United Republic of Tanzania" + }, + "id": "TZA", + "arcs": [ + [-394, 582, -448, -460, 583, -199, -62, -536, 584] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Uganda" + }, + "id": "UGA", + "arcs": [ + [-537, -197, -549, -395, -585] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Ukraine" + }, + "id": "UKR", + "arcs": [ + [-530, 585, -514, -433, -513, -332, -565, -504, -96] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Uruguay" + }, + "id": "URY", + "arcs": [ + [-113, 586, -28] + ] + }, { + "type": "MultiPolygon", + "properties": { + "name": "United States of America" + }, + "id": "USA", + "arcs": [ + [ + [587] + ], + [ + [588] + ], + [ + [589] + ], + [ + [590] + ], + [ + [591] + ], + [ + [592, -438, 593, -139] + ], + [ + [594] + ], + [ + [595] + ], + [ + [596] + ], + [ + [-141, 597] + ] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Uzbekistan" + }, + "id": "UZB", + "arcs": [ + [-576, -388, -399, -574, -2] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Venezuela" + }, + "id": "VEN", + "arcs": [ + [598, -320, -108, -210] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Vietnam" + }, + "id": "VNM", + "arcs": [ + [599, -402, -413, -171] + ] + }, { + "type": "MultiPolygon", + "properties": { + "name": "Vanuatu" + }, + "id": "VUT", + "arcs": [ + [ + [600] + ], + [ + [601] + ] + ] + }, { + "type": "Polygon", + "properties": { + "name": "West Bank" + }, + "id": "PSE", + "arcs": [ + [-384, -371] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Yemen" + }, + "id": "YEM", + "arcs": [ + [602, -542, -483] + ] + }, { + "type": "Polygon", + "properties": { + "name": "South Africa" + }, + "id": "ZAF", + "arcs": [ + [-467, -118, 603, -452, -568, -450, 604], + [-424] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Zambia" + }, + "id": "ZMB", + "arcs": [ + [-459, -454, 605, -120, -466, -7, -200, -584] + ] + }, { + "type": "Polygon", + "properties": { + "name": "Zimbabwe" + }, + "id": "ZWE", + "arcs": [ + [-604, -121, -606, -453] + ] + }] + } + }, + "arcs": [ + [ + [6700, 7164], + [28, -23], + [21, 8], + [6, 27], + [22, 9], + [15, 18], + [6, 47], + [23, 11], + [5, 21], + [13, -15], + [8, -2] + ], + [ + [6847, 7265], + [16, -1], + [20, -12] + ], + [ + [6883, 7252], + [9, -7], + [20, 19], + [9, -12], + [9, 27], + [17, -1], + [4, 9], + [3, 24], + [12, 20], + [15, -13], + [-3, -18], + [9, -3], + [-3, -50], + [11, -19], + [10, 12], + [12, 6], + [17, 27], + [19, -5], + [29, 0] + ], + [ + [7082, 7268], + [5, -17] + ], + [ + [7087, 7251], + [-16, -6], + [-14, -11], + [-32, -7], + [-30, -13], + [-16, -25], + [6, -25], + [4, -30], + [-14, -25], + [1, -22], + [-8, -22], + [-26, 2], + [11, -39], + [-18, -15], + [-12, -35], + [2, -36], + [-11, -16], + [-10, 5], + [-22, -8], + [-3, -16], + [-20, 0], + [-16, -34], + [-1, -50], + [-36, -24], + [-19, 5], + [-6, -13], + [-16, 7], + [-28, -8], + [-47, 30] + ], + [ + [6690, 6820], + [25, 53], + [-2, 38], + [-21, 10], + [-2, 38], + [-9, 47], + [12, 32], + [-12, 9], + [7, 43], + [12, 74] + ], + [ + [5664, 4412], + [3, -18], + [-4, -29], + [5, -28], + [-4, -22], + [3, -20], + [-58, 1], + [-2, -188], + [19, -49], + [18, -37] + ], + [ + [5644, 4022], + [-51, -24], + [-67, 9], + [-19, 28], + [-113, -3], + [-4, -4], + [-17, 27], + [-18, 2], + [-16, -10], + [-14, -12] + ], + [ + [5325, 4035], + [-2, 38], + [4, 51], + [9, 55], + [2, 25], + [9, 53], + [6, 24], + [16, 39], + [9, 26], + [3, 44], + [-1, 34], + [-9, 21], + [-7, 36], + [-7, 35], + [2, 12], + [8, 24], + [-8, 57], + [-6, 39], + [-14, 38], + [3, 11] + ], + [ + [5342, 4697], + [11, 8], + [8, -1], + [10, 7], + [82, -1], + [7, -44], + [8, -35], + [6, -19], + [11, -31], + [18, 5], + [9, 8], + [16, -8], + [4, 14], + [7, 35], + [17, 2], + [2, 10], + [14, 1], + [-3, -22], + [34, 1], + [1, -37], + [5, -23], + [-4, -36], + [2, -36], + [9, -22], + [-1, -70], + [7, 5], + [12, -1], + [17, 8], + [13, -3] + ], + [ + [5338, 4715], + [-8, 45] + ], + [ + [5330, 4760], + [12, 25], + [8, 10], + [10, -20] + ], + [ + [5360, 4775], + [-10, -12], + [-4, -16], + [-1, -25], + [-7, -7] + ], + [ + [5571, 7530], + [-3, -20], + [4, -25], + [11, -15] + ], + [ + [5583, 7470], + [0, -15], + [-9, -9], + [-2, -19], + [-13, -29] + ], + [ + [5559, 7398], + [-5, 5], + [0, 13], + [-15, 19], + [-3, 29], + [2, 40], + [4, 18], + [-4, 10] + ], + [ + [5538, 7532], + [-2, 18], + [12, 29], + [1, -11], + [8, 6] + ], + [ + [5557, 7574], + [6, -16], + [7, -6], + [1, -22] + ], + [ + [6432, 6490], + [5, 3], + [1, -16], + [22, 9], + [23, -2], + [17, -1], + [19, 39], + [20, 38], + [18, 37] + ], + [ + [6557, 6597], + [5, -20] + ], + [ + [6562, 6577], + [4, -47] + ], + [ + [6566, 6530], + [-14, 0], + [-3, -39], + [5, -8], + [-12, -12], + [0, -24], + [-8, -24], + [-1, -24] + ], + [ + [6533, 6399], + [-6, -12], + [-83, 29], + [-11, 60], + [-1, 14] + ], + [ + [3140, 1814], + [-17, 2], + [-30, 0], + [0, 132] + ], + [ + [3093, 1948], + [11, -27], + [14, -45], + [36, -35], + [39, -15], + [-13, -30], + [-26, -2], + [-14, 20] + ], + [ + [3258, 3743], + [51, -96], + [23, -9], + [34, -44], + [29, -23], + [4, -26], + [-28, -90], + [28, -16], + [32, -9], + [22, 10], + [25, 45], + [4, 52] + ], + [ + [3482, 3537], + [14, 11], + [14, -34], + [-1, -47], + [-23, -33], + [-19, -24], + [-31, -57], + [-37, -81] + ], + [ + [3399, 3272], + [-7, -47], + [-7, -61], + [0, -58], + [-6, -14], + [-2, -38] + ], + [ + [3377, 3054], + [-2, -31], + [35, -50], + [-4, -41], + [18, -26], + [-2, -29], + [-26, -75], + [-42, -32], + [-55, -12], + [-31, 6], + [6, -36], + [-6, -44], + [5, -30], + [-16, -20], + [-29, -8], + [-26, 21], + [-11, -15], + [4, -59], + [18, -18], + [16, 19], + [8, -31], + [-26, -18], + [-22, -37], + [-4, -59], + [-7, -32], + [-26, 0], + [-22, -31], + [-8, -44], + [28, -43], + [26, -12], + [-9, -53], + [-33, -33], + [-18, -70], + [-25, -23], + [-12, -28], + [9, -61], + [19, -34], + [-12, 3] + ], + [ + [3095, 1968], + [-26, 9], + [-67, 8], + [-11, 34], + [0, 45], + [-18, -4], + [-10, 21], + [-3, 63], + [22, 26], + [9, 37], + [-4, 30], + [15, 51], + [10, 78], + [-3, 35], + [12, 11], + [-3, 22], + [-13, 12], + [10, 25], + [-13, 22], + [-6, 68], + [11, 12], + [-5, 72], + [7, 61], + [7, 52], + [17, 22], + [-9, 58], + [0, 54], + [21, 38], + [-1, 50], + [16, 57], + [0, 55], + [-7, 11], + [-13, 102], + [17, 60], + [-2, 58], + [10, 53], + [18, 56], + [20, 36], + [-9, 24], + [6, 19], + [-1, 98], + [30, 29], + [10, 62], + [-3, 14] + ], + [ + [3136, 3714], + [23, 54], + [36, -15], + [16, -42], + [11, 47], + [32, -2], + [4, -13] + ], + [ + [6210, 7485], + [39, 9] + ], + [ + [6249, 7494], + [5, -15], + [11, -10], + [-6, -15], + [15, -21], + [-8, -18], + [12, -16], + [13, -10], + [0, -41] + ], + [ + [6291, 7348], + [-10, -2] + ], + [ + [6281, 7346], + [-11, 34], + [0, 10], + [-12, -1], + [-9, 16], + [-5, -1] + ], + [ + [6244, 7404], + [-11, 17], + [-21, 15], + [3, 28], + [-5, 21] + ], + [ + [3345, 329], + [-8, -30], + [-8, -27], + [-59, 8], + [-62, -3], + [-34, 20], + [0, 2], + [-16, 17], + [63, -2], + [60, -6], + [20, 24], + [15, 21], + [29, -24] + ], + [ + [577, 361], + [-53, -8], + [-36, 21], + [-17, 21], + [-1, 3], + [-18, 16], + [17, 22], + [52, -9], + [28, -18], + [21, -21], + [7, -27] + ], + [ + [3745, 447], + [35, -26], + [12, -36], + [3, -25], + [1, -30], + [-43, -19], + [-45, -15], + [-52, -14], + [-59, -11], + [-65, 3], + [-37, 20], + [5, 24], + [59, 16], + [24, 20], + [18, 26], + [12, 22], + [17, 20], + [18, 25], + [14, 0], + [41, 12], + [42, -12] + ], + [ + [1633, 715], + [36, -9], + [33, 10], + [-16, -20], + [-26, -15], + [-39, 4], + [-27, 21], + [6, 20], + [33, -11] + ], + [ + [1512, 716], + [43, -23], + [-17, 3], + [-36, 5], + [-38, 17], + [20, 12], + [28, -14] + ], + [ + [2250, 808], + [31, -8], + [30, 7], + [17, -34], + [-22, 5], + [-34, -2], + [-34, 2], + [-38, -4], + [-28, 12], + [-15, 24], + [18, 11], + [35, -8], + [40, -5] + ], + [ + [3098, 866], + [4, -27], + [-5, -23], + [-8, -22], + [-33, -8], + [-31, -12], + [-36, 1], + [14, 24], + [-33, -9], + [-31, -8], + [-21, 18], + [-2, 24], + [30, 23], + [20, 7], + [32, -2], + [8, 30], + [1, 22], + [0, 47], + [16, 28], + [25, 9], + [15, -22], + [6, -22], + [12, -26], + [10, -26], + [7, -26] + ], + [ + [3371, 1268], + [-11, -13], + [-21, 9], + [-23, -6], + [-19, -14], + [-20, -15], + [-14, -17], + [-4, -23], + [2, -22], + [13, -20], + [-19, -14], + [-26, -4], + [-15, -20], + [-17, -19], + [-17, -25], + [-4, -22], + [9, -24], + [15, -19], + [23, -14], + [21, -18], + [12, -23], + [6, -22], + [8, -24], + [13, -19], + [8, -22], + [4, -55], + [8, -22], + [2, -23], + [9, -23], + [-4, -31], + [-15, -24], + [-17, -20], + [-37, -8], + [-12, -21], + [-17, -20], + [-42, -22], + [-37, -9], + [-35, -13], + [-37, -13], + [-22, -24], + [-45, -2], + [-49, 2], + [-44, -4], + [-47, 0], + [9, -24], + [42, -10], + [31, -16], + [18, -21], + [-31, -19], + [-48, 6], + [-40, -15], + [-2, -24], + [-1, -23], + [33, -20], + [6, -22], + [35, -22], + [59, -9], + [50, -16], + [40, -19], + [50, -18], + [70, -10], + [68, -16], + [47, -17], + [52, -20], + [27, -28], + [13, -22], + [34, 21], + [46, 17], + [48, 19], + [58, 15], + [49, 16], + [69, 1], + [68, -8], + [56, -14], + [18, 26], + [39, 17], + [70, 1], + [55, 13], + [52, 13], + [58, 8], + [62, 10], + [43, 15], + [-20, 21], + [-12, 21], + [0, 22], + [-54, -2], + [-57, -10], + [-54, 0], + [-8, 22], + [4, 44], + [12, 13], + [40, 14], + [47, 14], + [34, 17], + [33, 18], + [25, 23], + [38, 10], + [38, 8], + [19, 5], + [43, 2], + [41, 8], + [34, 12], + [34, 14], + [30, 14], + [39, 18], + [24, 20], + [26, 17], + [9, 24], + [-30, 13], + [10, 25], + [18, 18], + [29, 12], + [31, 14], + [28, 18], + [22, 23], + [13, 28], + [21, 16], + [33, -3], + [13, -20], + [34, -2], + [1, 22], + [14, 23], + [30, -6], + [7, -22], + [33, -3], + [36, 10], + [35, 7], + [31, -3], + [12, -25], + [31, 20], + [28, 10], + [31, 9], + [31, 8], + [29, 14], + [31, 9], + [24, 13], + [17, 20], + [20, -15], + [29, 8], + [20, -27], + [16, -21], + [32, 11], + [12, 24], + [28, 16], + [37, -4], + [11, -22], + [22, 22], + [30, 7], + [33, 3], + [29, -2], + [31, -7], + [30, -3], + [13, -20], + [18, -17], + [31, 10], + [32, 3], + [32, 0], + [31, 1], + [28, 8], + [29, 7], + [25, 16], + [26, 11], + [28, 5], + [21, 17], + [15, 32], + [16, 20], + [29, -10], + [11, -21], + [24, -13], + [29, 4], + [19, -21], + [21, -15], + [28, 14], + [10, 26], + [25, 10], + [29, 20], + [27, 8], + [33, 11], + [22, 13], + [22, 14], + [22, 13], + [26, -7], + [25, 21], + [18, 16], + [26, -1], + [23, 14], + [6, 21], + [23, 16], + [23, 11], + [28, 10], + [25, 4], + [25, -3], + [26, -6], + [22, -16], + [3, -26], + [24, -19], + [17, -17], + [33, -7], + [19, -16], + [23, -16], + [26, -3], + [23, 11], + [24, 24], + [26, -12], + [27, -7], + [26, -7], + [27, -5], + [28, 0], + [23, -61], + [-1, -15], + [-4, -27], + [-26, -15], + [-22, -22], + [4, -23], + [31, 1], + [-4, -23], + [-14, -22], + [-13, -24], + [21, -19], + [32, -6], + [32, 11], + [15, 23], + [10, 22], + [15, 18], + [17, 18], + [7, 21], + [15, 29], + [18, 5], + [31, 3], + [28, 7], + [28, 9], + [14, 23], + [8, 22], + [19, 22], + [27, 15], + [23, 12], + [16, 19], + [15, 11], + [21, 9], + [27, -6], + [25, 6], + [28, 7], + [30, -4], + [20, 17], + [14, 39], + [11, -16], + [13, -28], + [23, -12], + [27, -4], + [26, 7], + [29, -5], + [26, -1], + [17, 6], + [24, -4], + [21, -12], + [25, 8], + [30, 0], + [25, 8], + [29, -8], + [19, 19], + [14, 20], + [19, 16], + [35, 44], + [18, -8], + [21, -16], + [18, -21], + [36, -36], + [27, -1], + [25, 0], + [30, 7], + [30, 8], + [23, 16], + [19, 18], + [31, 2], + [21, 13], + [22, -12], + [14, -18], + [19, -19], + [31, 2], + [19, -15], + [33, -15], + [35, -5], + [29, 4], + [21, 19], + [19, 18], + [25, 5], + [25, -8], + [29, -6], + [26, 9], + [25, 0], + [24, -6], + [26, -5], + [25, 10], + [30, 9], + [28, 3], + [32, 0], + [25, 5], + [25, 5], + [8, 29], + [1, 24], + [17, -16], + [5, -27], + [10, -24], + [11, -20], + [23, -10], + [32, 4], + [36, 1], + [25, 3], + [37, 0], + [26, 1], + [36, -2], + [31, -5], + [20, -18], + [-5, -22], + [18, -18], + [30, -13], + [31, -15], + [35, -11], + [38, -9], + [28, -9], + [32, -2], + [18, 20], + [24, -16], + [21, -19], + [25, -13], + [34, -6], + [32, -7], + [13, -23], + [32, -14], + [21, -21], + [31, -9], + [32, 1], + [30, -4], + [33, 1], + [34, -4], + [31, -8], + [28, -14], + [29, -12], + [20, -17], + [-3, -23], + [-15, -21], + [-13, -27], + [-9, -21], + [-14, -24], + [-36, -9], + [-16, -21], + [-36, -13], + [-13, -23], + [-19, -22], + [-20, -18], + [-11, -25], + [-7, -22], + [-3, -26], + [0, -22], + [16, -23], + [6, -22], + [13, -21], + [52, -8], + [11, -26], + [-50, -9], + [-43, -13], + [-52, -2], + [-24, -34], + [-5, -27], + [-12, -22], + [-14, -22], + [37, -20], + [14, -24], + [24, -22], + [33, -20], + [39, -19], + [42, -18], + [64, -19], + [14, -29], + [80, -12], + [5, -5], + [21, -17], + [77, 15], + [63, -19], + [48, -14], + [-9997, -1], + [24, 35], + [50, -19], + [3, 2], + [30, 19], + [4, 0], + [3, -1], + [40, -25], + [35, 25], + [7, 3], + [81, 11], + [27, -14], + [13, -7], + [41, -20], + [79, -15], + [63, -18], + [107, -14], + [80, 16], + [118, -11], + [67, -19], + [73, 17], + [78, 17], + [6, 27], + [-110, 3], + [-89, 14], + [-24, 23], + [-74, 12], + [5, 27], + [10, 24], + [10, 22], + [-5, 25], + [-46, 16], + [-22, 21], + [-43, 18], + [68, -3], + [64, 9], + [40, -20], + [50, 18], + [45, 22], + [23, 19], + [-10, 25], + [-36, 16], + [-41, 17], + [-57, 4], + [-50, 8], + [-54, 6], + [-18, 22], + [-36, 18], + [-21, 21], + [-9, 67], + [14, -6], + [25, -18], + [45, 6], + [44, 8], + [23, -26], + [44, 6], + [37, 13], + [35, 16], + [32, 20], + [41, 5], + [-1, 22], + [-9, 22], + [8, 21], + [36, 11], + [16, -20], + [42, 12], + [32, 15], + [40, 1], + [38, 6], + [37, 13], + [30, 13], + [34, 13], + [22, -4], + [19, -4], + [41, 8], + [37, -10], + [38, 1], + [37, 8], + [37, -6], + [41, -6], + [39, 3], + [40, -2], + [42, -1], + [38, 3], + [28, 17], + [34, 9], + [35, -13], + [33, 11], + [30, 21], + [18, -19], + [9, -21], + [18, -19], + [29, 17], + [33, -22], + [38, -7], + [32, -16], + [39, 3], + [36, 11], + [41, -3], + [38, -8], + [38, -10], + [15, 25], + [-18, 20], + [-14, 21], + [-36, 5], + [-15, 22], + [-6, 22], + [-10, 43], + [21, -8], + [36, -3], + [36, 3], + [33, -9], + [28, -17], + [12, -21], + [38, -4], + [36, 9], + [38, 11], + [34, 7], + [28, -14], + [37, 5], + [24, 45], + [23, -27], + [32, -10], + [34, 6], + [23, -23], + [37, -3], + [33, -7], + [34, -12], + [21, 22], + [11, 20], + [28, -23], + [38, 6], + [28, -13], + [19, -19], + [37, 5], + [29, 13], + [29, 15], + [33, 8], + [39, 7], + [36, 8], + [27, 13], + [16, 19], + [7, 25], + [-3, 24], + [-9, 24], + [-10, 23], + [-9, 23], + [-7, 21], + [-1, 23], + [2, 23], + [13, 22], + [11, 24], + [5, 23], + [-6, 26], + [-3, 23], + [14, 27], + [15, 17], + [18, 22], + [19, 19], + [22, 17], + [11, 25], + [15, 17], + [18, 15], + [26, 3], + [18, 19], + [19, 11], + [23, 7], + [20, 15], + [16, 19], + [22, 7], + [16, -15], + [-10, -20], + [-29, -17] + ], + [ + [6914, 2185], + [18, -19], + [26, -7], + [1, -11], + [-7, -27], + [-43, -4], + [-1, 31], + [4, 25], + [2, 12] + ], + [ + [9038, 2648], + [27, -21], + [15, 8], + [22, 12], + [16, -4], + [2, -70], + [-9, -21], + [-3, -47], + [-10, 16], + [-19, -41], + [-6, 3], + [-17, 2], + [-17, 50], + [-4, 39], + [-16, 52], + [1, 27], + [18, -5] + ], + [ + [8987, 4244], + [10, -46], + [18, 22], + [9, -25], + [13, -23], + [-3, -26], + [6, -51], + [5, -29], + [7, -7], + [7, -51], + [-3, -30], + [9, -40], + [31, -31], + [19, -28], + [19, -26], + [-4, -14], + [16, -37], + [11, -64], + [11, 13], + [11, -26], + [7, 9], + [5, -63], + [19, -36], + [13, -22], + [22, -48], + [8, -48], + [1, -33], + [-2, -37], + [13, -50], + [-2, -52], + [-5, -28], + [-7, -52], + [1, -34], + [-6, -43], + [-12, -53], + [-21, -29], + [-10, -46], + [-9, -29], + [-8, -51], + [-11, -30], + [-7, -44], + [-4, -41], + [2, -18], + [-16, -21], + [-31, -2], + [-26, -24], + [-13, -23], + [-17, -26], + [-23, 27], + [-17, 10], + [5, 31], + [-15, -11], + [-25, -43], + [-24, 16], + [-15, 9], + [-16, 4], + [-27, 17], + [-18, 37], + [-5, 45], + [-7, 30], + [-13, 24], + [-27, 7], + [9, 28], + [-7, 44], + [-13, -41], + [-25, -11], + [14, 33], + [5, 34], + [10, 29], + [-2, 44], + [-22, -50], + [-18, -21], + [-10, -47], + [-22, 25], + [1, 31], + [-18, 43], + [-14, 22], + [5, 14], + [-36, 35], + [-19, 2], + [-27, 29], + [-50, -6], + [-36, -21], + [-31, -20], + [-27, 4], + [-29, -30], + [-24, -14], + [-6, -31], + [-10, -24], + [-23, -1], + [-18, -5], + [-24, 10], + [-20, -6], + [-19, -3], + [-17, -31], + [-8, 2], + [-14, -16], + [-13, -19], + [-21, 2], + [-18, 0], + [-30, 38], + [-15, 11], + [1, 34], + [14, 8], + [4, 14], + [-1, 21], + [4, 41], + [-3, 35], + [-15, 60], + [-4, 33], + [1, 34], + [-11, 38], + [-1, 18], + [-12, 23], + [-4, 47], + [-16, 46], + [-4, 26], + [13, -26], + [-10, 55], + [14, -17], + [8, -23], + [0, 30], + [-14, 47], + [-3, 18], + [-6, 18], + [3, 34], + [6, 15], + [4, 29], + [-3, 35], + [11, 42], + [2, -45], + [12, 41], + [22, 20], + [14, 25], + [21, 22], + [13, 4], + [7, -7], + [22, 22], + [17, 6], + [4, 13], + [8, 6], + [15, -2], + [29, 18], + [15, 26], + [7, 31], + [17, 30], + [1, 24], + [1, 32], + [19, 50], + [12, -51], + [12, 12], + [-10, 28], + [9, 29], + [12, -13], + [3, 45], + [15, 29], + [7, 23], + [14, 10], + [0, 17], + [13, -7], + [0, 15], + [12, 8], + [14, 8], + [20, -27], + [16, -35], + [17, 0], + [18, -6], + [-6, 33], + [13, 47], + [13, 15], + [-5, 15], + [12, 34], + [17, 21], + [14, -7], + [24, 11], + [-1, 30], + [-20, 19], + [15, 9], + [18, -15], + [15, -24], + [23, -15], + [8, 6], + [17, -18], + [17, 17], + [10, -5], + [7, 11], + [12, -29], + [-7, -32], + [-11, -24], + [-9, -2], + [3, -23], + [-8, -30], + [-10, -29], + [2, -17], + [22, -32], + [21, -19], + [15, -20], + [20, -35], + [8, 0], + [14, -15], + [4, -19], + [27, -20], + [18, 20], + [6, 32], + [5, 26], + [4, 33], + [8, 47], + [-4, 28], + [2, 17], + [-3, 34], + [4, 45], + [5, 12], + [-4, 20], + [7, 31], + [5, 32], + [1, 17], + [10, 22], + [8, -29], + [2, -37], + [7, -7], + [1, -25], + [10, -30], + [2, -33], + [-1, -22] + ], + [ + [5471, 7900], + [-2, -24], + [-16, 0], + [6, -13], + [-9, -38] + ], + [ + [5450, 7825], + [-6, -10], + [-24, -1], + [-14, -13], + [-23, 4] + ], + [ + [5383, 7805], + [-40, 15], + [-6, 21], + [-27, -10], + [-4, -12], + [-16, 9] + ], + [ + [5290, 7828], + [-15, 1], + [-12, 11], + [4, 15], + [-1, 10] + ], + [ + [5266, 7865], + [8, 3], + [14, -16], + [4, 16], + [25, -3], + [20, 11], + [13, -2], + [9, -12], + [2, 10], + [-4, 38], + [10, 8], + [10, 27] + ], + [ + [5377, 7945], + [21, -19], + [15, 24], + [10, 5], + [22, -18], + [13, 3], + [13, -12] + ], + [ + [5471, 7928], + [-3, -7], + [3, -21] + ], + [ + [6281, 7346], + [-19, 8], + [-14, 27], + [-4, 23] + ], + [ + [6349, 7527], + [15, -31], + [14, -42], + [13, -2], + [8, -16], + [-23, -5], + [-5, -46], + [-4, -21], + [-11, -13], + [1, -30] + ], + [ + [6357, 7321], + [-7, -3], + [-17, 31], + [10, 30], + [-9, 17], + [-10, -4], + [-33, -44] + ], + [ + [6249, 7494], + [6, 10], + [21, -17], + [15, -4], + [4, 7], + [-14, 32], + [7, 9] + ], + [ + [6288, 7531], + [8, -2], + [19, -36], + [13, -4], + [4, 15], + [17, 23] + ], + [ + [5814, 4792], + [-1, 71], + [-7, 27] + ], + [ + [5806, 4890], + [17, -5], + [8, 34], + [15, -4] + ], + [ + [5846, 4915], + [1, -23], + [6, -14], + [1, -19], + [-7, -12], + [-11, -31], + [-10, -22], + [-12, -2] + ], + [ + [5092, 8091], + [20, -5], + [26, 12], + [17, -25], + [16, -14] + ], + [ + [5171, 8059], + [-4, -40] + ], + [ + [5167, 8019], + [-7, -2], + [-3, -33] + ], + [ + [5157, 7984], + [-24, 26], + [-14, -4], + [-20, 28], + [-13, 23], + [-13, 1], + [-4, 21] + ], + [ + [5069, 8079], + [23, 12] + ], + [ + [5074, 5427], + [-23, -7] + ], + [ + [5051, 5420], + [-7, 41], + [2, 136], + [-6, 12], + [-1, 29], + [-10, 21], + [-8, 17], + [3, 31] + ], + [ + [5024, 5707], + [10, 7], + [6, 26], + [13, 5], + [6, 18] + ], + [ + [5059, 5763], + [10, 17], + [10, 0], + [21, -34] + ], + [ + [5100, 5746], + [-1, -19], + [6, -35], + [-6, -24], + [3, -16], + [-13, -37], + [-9, -18], + [-5, -37], + [1, -38], + [-2, -95] + ], + [ + [4921, 5627], + [-19, 15], + [-13, -2], + [-10, -15], + [-12, 13], + [-5, 19], + [-13, 13] + ], + [ + [4849, 5670], + [-1, 34], + [7, 26], + [-1, 20], + [23, 48], + [4, 41], + [7, 14], + [14, -8], + [11, 12], + [4, 16], + [22, 26], + [5, 19], + [26, 24], + [15, 9], + [7, -12], + [18, 0] + ], + [ + [5010, 5939], + [-2, -28], + [3, -27], + [16, -39], + [1, -28], + [32, -14], + [-1, -40] + ], + [ + [5024, 5707], + [-24, 1] + ], + [ + [5000, 5708], + [-13, 5], + [-9, -9], + [-12, 4], + [-48, -3], + [-1, -33], + [4, -45] + ], + [ + [7573, 6360], + [0, -43], + [-10, 9], + [2, -47] + ], + [ + [7565, 6279], + [-8, 30], + [-1, 31], + [-6, 28], + [-11, 34], + [-26, 3], + [3, -25], + [-9, -32], + [-12, 12], + [-4, -11], + [-8, 6], + [-11, 5] + ], + [ + [7472, 6360], + [-4, 49], + [-10, 45], + [5, 35], + [-17, 16], + [6, 22], + [18, 22], + [-20, 31], + [9, 40], + [22, -26], + [14, -3], + [2, -41], + [26, -8], + [26, 1], + [16, -10], + [-13, -50], + [-12, -3], + [-9, -34], + [16, -31], + [4, 38], + [8, 0], + [14, -93] + ], + [ + [5629, 7671], + [8, -25], + [11, 5], + [21, -9], + [41, -4], + [13, 16], + [33, 13], + [20, -21], + [17, -6] + ], + [ + [5793, 7640], + [-15, -25], + [-10, -42], + [9, -34] + ], + [ + [5777, 7539], + [-24, 8], + [-28, -18] + ], + [ + [5725, 7529], + [0, -30], + [-26, -5], + [-19, 20], + [-22, -16], + [-21, 2] + ], + [ + [5637, 7500], + [-2, 39], + [-14, 19] + ], + [ + [5621, 7558], + [5, 8], + [-3, 7], + [4, 19], + [11, 18], + [-14, 26], + [-2, 21], + [7, 14] + ], + [ + [2846, 6461], + [-7, -3], + [-7, 34], + [-10, 17], + [6, 38], + [8, -3], + [10, -49], + [0, -34] + ], + [ + [2838, 6628], + [-30, -10], + [-2, 22], + [13, 5], + [18, -2], + [1, -15] + ], + [ + [2861, 6628], + [-5, -42], + [-5, 8], + [0, 31], + [-12, 23], + [0, 7], + [22, -27] + ], + [ + [5527, 7708], + [10, 0], + [-7, -26], + [14, -23], + [-4, -28], + [-7, -2] + ], + [ + [5533, 7629], + [-5, -6], + [-9, -13], + [-4, -33] + ], + [ + [5515, 7577], + [-25, 23], + [-10, 24], + [-11, 13], + [-12, 22], + [-6, 19], + [-14, 27], + [6, 25], + [10, -14], + [6, 12], + [13, 2], + [24, -10], + [19, 1], + [12, -13] + ], + [ + [5652, 8242], + [27, 0], + [30, 22], + [6, 34], + [23, 19], + [-3, 26] + ], + [ + [5735, 8343], + [17, 10], + [30, 23] + ], + [ + [5782, 8376], + [29, -15], + [4, -15], + [15, 7], + [27, -14], + [3, -27], + [-6, -16], + [17, -39], + [12, -11], + [-2, -11], + [19, -10], + [8, -16], + [-11, -13], + [-23, 2], + [-5, -5], + [7, -20], + [6, -37] + ], + [ + [5882, 8136], + [-23, -4], + [-9, -13], + [-2, -30], + [-11, 6], + [-25, -3], + [-7, 14], + [-11, -10], + [-10, 8], + [-22, 1], + [-31, 15], + [-28, 4], + [-22, -1], + [-15, -16], + [-13, -2] + ], + [ + [5653, 8105], + [-1, 26], + [-8, 27], + [17, 12], + [0, 24], + [-8, 22], + [-1, 26] + ], + [ + [2524, 6110], + [-1, 8], + [4, 3], + [5, -7], + [10, 36], + [5, 0] + ], + [ + [2547, 6150], + [0, -8], + [5, -1], + [0, -16], + [-5, -25], + [3, -9], + [-3, -21], + [2, -6], + [-4, -30], + [-5, -16], + [-5, -1], + [-6, -21] + ], + [ + [2529, 5996], + [-8, 0], + [2, 67], + [1, 47] + ], + [ + [3136, 3714], + [-20, -8], + [-11, 82], + [-15, 66], + [9, 57], + [-15, 25], + [-4, 43], + [-13, 40] + ], + [ + [3067, 4019], + [17, 64], + [-12, 49], + [7, 20], + [-5, 22], + [10, 30], + [1, 50], + [1, 41], + [6, 20], + [-24, 96] + ], + [ + [3068, 4411], + [21, -5], + [14, 1], + [6, 18], + [25, 24], + [14, 22], + [37, 10], + [-3, -44], + [3, -23], + [-2, -40], + [30, -53], + [31, -9], + [11, -23], + [19, -11], + [11, -17], + [18, 0], + [16, -17], + [1, -34], + [6, -18], + [0, -25], + [-8, -1], + [11, -69], + [53, -2], + [-4, -35], + [3, -23], + [15, -16], + [6, -37], + [-4, -47], + [-8, -26], + [3, -33], + [-9, -12] + ], + [ + [3384, 3866], + [-1, 18], + [-25, 30], + [-26, 1], + [-49, -17], + [-13, -52], + [-1, -32], + [-11, -71] + ], + [ + [3482, 3537], + [6, 34], + [3, 35], + [1, 32], + [-10, 11], + [-11, -9], + [-10, 2], + [-4, 23], + [-2, 54], + [-5, 18], + [-19, 16], + [-11, -12], + [-30, 11], + [2, 81], + [-8, 33] + ], + [ + [3068, 4411], + [-15, -11], + [-13, 7], + [2, 90], + [-23, -35], + [-24, 2], + [-11, 31], + [-18, 4], + [5, 25], + [-15, 36], + [-11, 53], + [7, 11], + [0, 25], + [17, 17], + [-3, 32], + [7, 20], + [2, 28], + [32, 40], + [22, 11], + [4, 9], + [25, -2] + ], + [ + [3058, 4804], + [13, 162], + [0, 25], + [-4, 34], + [-12, 22], + [0, 42], + [15, 10], + [6, -6], + [1, 23], + [-16, 6], + [-1, 37], + [54, -2], + [10, 21], + [7, -19], + [6, -35], + [5, 8] + ], + [ + [3142, 5132], + [15, -32], + [22, 4], + [5, 18], + [21, 14], + [11, 10], + [4, 25], + [19, 17], + [-1, 12], + [-24, 5], + [-3, 37], + [1, 40], + [-13, 15], + [5, 6], + [21, -8], + [22, -15], + [8, 14], + [20, 9], + [31, 23], + [10, 22], + [-3, 17] + ], + [ + [3313, 5365], + [14, 2], + [7, -13], + [-4, -26], + [9, -9], + [7, -28], + [-8, -20], + [-4, -51], + [7, -30], + [2, -27], + [17, -28], + [14, -3], + [3, 12], + [8, 3], + [13, 10], + [9, 16], + [15, -5], + [7, 2] + ], + [ + [3429, 5170], + [15, -5], + [3, 12], + [-5, 12], + [3, 17], + [11, -5], + [13, 6], + [16, -13] + ], + [ + [3485, 5194], + [12, -12], + [9, 16], + [6, -3], + [4, -16], + [13, 4], + [11, 22], + [8, 44], + [17, 54] + ], + [ + [3565, 5303], + [9, 3], + [7, -33], + [16, -103], + [14, -10], + [1, -41], + [-21, -48], + [9, -18], + [49, -9], + [1, -60], + [21, 39], + [35, -21], + [46, -36], + [14, -35], + [-5, -32], + [33, 18], + [54, -32], + [41, 3], + [41, -49], + [36, -66], + [21, -17], + [24, -3], + [10, -18], + [9, -76], + [5, -35], + [-11, -98], + [-14, -39], + [-39, -82], + [-18, -67], + [-21, -51], + [-7, -1], + [-7, -43], + [2, -111], + [-8, -91], + [-3, -39], + [-9, -23], + [-5, -79], + [-28, -77], + [-5, -61], + [-22, -26], + [-7, -35], + [-30, 0], + [-44, -23], + [-19, -26], + [-31, -18], + [-33, -47], + [-23, -58], + [-5, -44], + [5, -33], + [-5, -60], + [-6, -28], + [-20, -33], + [-31, -104], + [-24, -47], + [-19, -27], + [-13, -57], + [-18, -33] + ], + [ + [3517, 3063], + [-8, 33], + [13, 28], + [-16, 40], + [-22, 33], + [-29, 38], + [-10, -2], + [-28, 46], + [-18, -7] + ], + [ + [8172, 5325], + [11, 22], + [23, 32] + ], + [ + [8206, 5379], + [-1, -29], + [-2, -37], + [-13, 1], + [-6, -20], + [-12, 31] + ], + [ + [7546, 6698], + [12, -19], + [-2, -36], + [-23, -2], + [-23, 4], + [-18, -9], + [-25, 22], + [-1, 12] + ], + [ + [7466, 6670], + [19, 44], + [15, 15], + [20, -14], + [14, -1], + [12, -16] + ], + [ + [5817, 3752], + [-39, -43], + [-25, -44], + [-10, -40], + [-8, -22], + [-15, -4], + [-5, -29], + [-3, -18], + [-17, -14], + [-23, 3], + [-13, 17], + [-12, 7], + [-14, -14], + [-6, -28], + [-14, -18], + [-13, -26], + [-20, -6], + [-6, 20], + [2, 36], + [-16, 56], + [-8, 9] + ], + [ + [5552, 3594], + [0, 173], + [27, 2], + [1, 210], + [21, 2], + [43, 21], + [10, -24], + [18, 23], + [9, 0], + [15, 13] + ], + [ + [5696, 4014], + [5, -4] + ], + [ + [5701, 4010], + [11, -48], + [5, -10], + [9, -34], + [32, -65], + [12, -7], + [0, -20], + [8, -38], + [21, -9], + [18, -27] + ], + [ + [5424, 5496], + [23, 4], + [5, 16], + [5, -2], + [7, -13], + [34, 23], + [12, 23], + [15, 20], + [-3, 21], + [8, 6], + [27, -4], + [26, 27], + [20, 65], + [14, 24], + [18, 10] + ], + [ + [5635, 5716], + [3, -26], + [16, -36], + [0, -25], + [-5, -24], + [2, -18], + [10, -18] + ], + [ + [5661, 5569], + [21, -25] + ], + [ + [5682, 5544], + [15, -24], + [0, -19], + [19, -31], + [12, -26], + [7, -35], + [20, -24], + [5, -18] + ], + [ + [5760, 5367], + [-9, -7], + [-18, 2], + [-21, 6], + [-10, -5], + [-5, -14], + [-9, -2], + [-10, 12], + [-31, -29], + [-13, 6], + [-4, -5], + [-8, -35], + [-21, 11], + [-20, 6], + [-18, 22], + [-23, 20], + [-15, -19], + [-10, -30], + [-3, -41] + ], + [ + [5512, 5265], + [-18, 3], + [-19, 10], + [-16, -32], + [-15, -55] + ], + [ + [5444, 5191], + [-3, 18], + [-1, 27], + [-13, 19], + [-10, 30], + [-2, 21], + [-13, 31], + [2, 18], + [-3, 25], + [2, 45], + [7, 11], + [14, 60] + ], + [ + [3231, 7808], + [20, -8], + [26, 1], + [-14, -24], + [-10, -4], + [-35, 25], + [-7, 20], + [10, 18], + [10, -28] + ], + [ + [3283, 7958], + [-14, -1], + [-36, 19], + [-26, 28], + [10, 5], + [37, -15], + [28, -25], + [1, -11] + ], + [ + [1569, 7923], + [-14, -8], + [-46, 27], + [-8, 21], + [-25, 21], + [-5, 16], + [-28, 11], + [-11, 32], + [2, 14], + [30, -13], + [17, -9], + [26, -6], + [9, -21], + [14, -28], + [28, -24], + [11, -33] + ], + [ + [3440, 8052], + [-18, -52], + [18, 20], + [19, -12], + [-10, -21], + [25, -16], + [12, 14], + [28, -18], + [-8, -43], + [19, 10], + [4, -32], + [8, -36], + [-11, -52], + [-13, -2], + [-18, 11], + [6, 48], + [-8, 8], + [-32, -52], + [-17, 2], + [20, 28], + [-27, 14], + [-30, -3], + [-54, 2], + [-4, 17], + [17, 21], + [-12, 16], + [24, 36], + [28, 94], + [18, 33], + [24, 21], + [13, -3], + [-6, -16], + [-15, -37] + ], + [ + [1313, 8250], + [27, 5], + [-8, -67], + [24, -48], + [-11, 0], + [-17, 27], + [-10, 27], + [-14, 19], + [-5, 26], + [1, 19], + [13, -8] + ], + [ + [2798, 8730], + [-11, -31], + [-12, 5], + [-8, 17], + [2, 4], + [10, 18], + [12, -1], + [7, -12] + ], + [ + [2725, 8762], + [-33, -32], + [-19, 1], + [-6, 16], + [20, 27], + [38, 0], + [0, -12] + ], + [ + [2634, 8936], + [5, -26], + [15, 9], + [16, -15], + [30, -20], + [32, -19], + [2, -28], + [21, 5], + [20, -20], + [-25, -18], + [-43, 14], + [-16, 26], + [-27, -31], + [-40, -31], + [-9, 35], + [-38, -6], + [24, 30], + [4, 46], + [9, 54], + [20, -5] + ], + [ + [2892, 9024], + [-31, -3], + [-7, 29], + [12, 34], + [26, 8], + [21, -17], + [1, -25], + [-4, -8], + [-18, -18] + ], + [ + [2343, 9140], + [-17, -21], + [-38, 18], + [-22, -6], + [-38, 26], + [24, 19], + [19, 25], + [30, -16], + [17, -11], + [8, -11], + [17, -23] + ], + [ + [3135, 7724], + [-18, 33], + [0, 81], + [-13, 17], + [-18, -10], + [-10, 16], + [-21, -45], + [-8, -46], + [-10, -27], + [-12, -9], + [-9, -3], + [-3, -15], + [-51, 0], + [-42, 0], + [-12, -11], + [-30, -42], + [-3, -5], + [-9, -23], + [-26, 0], + [-27, 0], + [-12, -10], + [4, -11], + [2, -18], + [0, -6], + [-36, -30], + [-29, -9], + [-32, -31], + [-7, 0], + [-10, 9], + [-3, 8], + [1, 6], + [6, 21], + [13, 33], + [8, 35], + [-5, 51], + [-6, 53], + [-29, 28], + [3, 11], + [-4, 7], + [-8, 0], + [-5, 9], + [-2, 14], + [-5, -6], + [-7, 2], + [1, 6], + [-6, 6], + [-3, 15], + [-21, 19], + [-23, 20], + [-27, 23], + [-26, 21], + [-25, -17], + [-9, 0], + [-34, 15], + [-23, -8], + [-27, 19], + [-28, 9], + [-19, 4], + [-9, 10], + [-5, 32], + [-9, 0], + [-1, -23], + [-57, 0], + [-95, 0], + [-94, 0], + [-84, 0], + [-83, 0], + [-82, 0], + [-85, 0], + [-27, 0], + [-82, 0], + [-79, 0] + ], + [ + [1588, 7952], + [-4, 0], + [-54, 58], + [-20, 26], + [-50, 24], + [-15, 53], + [3, 36], + [-35, 25], + [-5, 48], + [-34, 43], + [0, 30] + ], + [ + [1374, 8295], + [15, 29], + [0, 37], + [-48, 37], + [-28, 68], + [-17, 42], + [-26, 27], + [-19, 24], + [-14, 31], + [-28, -20], + [-27, -33], + [-25, 39], + [-19, 26], + [-27, 16], + [-28, 2], + [0, 337], + [1, 219] + ], + [ + [1084, 9176], + [51, -14], + [44, -29], + [29, -5], + [24, 24], + [34, 19], + [41, -7], + [42, 26], + [45, 14], + [20, -24], + [20, 14], + [6, 27], + [20, -6], + [47, -53], + [37, 40], + [3, -45], + [34, 10], + [11, 17], + [34, -3], + [42, -25], + [65, -22], + [38, -10], + [28, 4], + [37, -30], + [-39, -29], + [50, -13], + [75, 7], + [24, 11], + [29, -36], + [31, 30], + [-29, 25], + [18, 20], + [34, 3], + [22, 6], + [23, -14], + [28, -32], + [31, 5], + [49, -27], + [43, 9], + [40, -1], + [-3, 37], + [25, 10], + [43, -20], + [0, -56], + [17, 47], + [23, -1], + [12, 59], + [-30, 36], + [-32, 24], + [2, 65], + [33, 43], + [37, -9], + [28, -26], + [38, -67], + [-25, -29], + [52, -12], + [-1, -60], + [38, 46], + [33, -38], + [-9, -44], + [27, -40], + [29, 43], + [21, 51], + [1, 65], + [40, -5], + [41, -8], + [37, -30], + [2, -29], + [-21, -31], + [20, -32], + [-4, -29], + [-54, -41], + [-39, -9], + [-29, 18], + [-8, -30], + [-27, -50], + [-8, -26], + [-32, -40], + [-40, -4], + [-22, -25], + [-2, -38], + [-32, -7], + [-34, -48], + [-30, -67], + [-11, -46], + [-1, -69], + [40, -10], + [13, -55], + [13, -45], + [39, 12], + [51, -26], + [28, -22], + [20, -28], + [35, -17], + [29, -24], + [46, -4], + [30, -6], + [-4, -51], + [8, -59], + [21, -66], + [41, -56], + [21, 19], + [15, 61], + [-14, 93], + [-20, 31], + [45, 28], + [31, 41], + [16, 41], + [-3, 40], + [-19, 50], + [-33, 44], + [32, 62], + [-12, 54], + [-9, 92], + [19, 14], + [48, -16], + [29, -6], + [23, 15], + [25, -20], + [35, -34], + [8, -23], + [50, -4], + [-1, -50], + [9, -74], + [25, -10], + [21, -35], + [40, 33], + [26, 65], + [19, 28], + [21, -53], + [36, -75], + [31, -71], + [-11, -37], + [37, -33], + [25, -34], + [44, -15], + [18, -19], + [11, -50], + [22, -8], + [11, -22], + [2, -67], + [-20, -22], + [-20, -21], + [-46, -21], + [-35, -48], + [-47, -10], + [-59, 13], + [-42, 0], + [-29, -4], + [-23, -43], + [-35, -26], + [-40, -78], + [-32, -54], + [23, 9], + [45, 78], + [58, 49], + [42, 6], + [24, -29], + [-26, -40], + [9, -63], + [9, -45], + [36, -29], + [46, 8], + [28, 67], + [2, -43], + [17, -22], + [-34, -38], + [-61, -36], + [-28, -23], + [-31, -43], + [-21, 4], + [-1, 50], + [48, 49], + [-44, -2], + [-31, -7] + ], + [ + [1829, 9377], + [-14, -27], + [61, 17], + [39, -29], + [31, 30], + [26, -20], + [23, -58], + [14, 25], + [-20, 60], + [24, 9], + [28, -9], + [31, -24], + [17, -58], + [9, -41], + [47, -30], + [50, -28], + [-3, -26], + [-46, -4], + [18, -23], + [-9, -22], + [-51, 9], + [-48, 16], + [-32, -3], + [-52, -20], + [-70, -9], + [-50, -6], + [-15, 28], + [-38, 16], + [-24, -6], + [-35, 47], + [19, 6], + [43, 10], + [39, -3], + [36, 11], + [-54, 13], + [-59, -4], + [-39, 1], + [-15, 22], + [64, 23], + [-42, -1], + [-49, 16], + [23, 44], + [20, 24], + [74, 36], + [29, -12] + ], + [ + [2097, 9395], + [-24, -39], + [-44, 41], + [10, 9], + [37, 2], + [21, -13] + ], + [ + [2879, 9376], + [3, -16], + [-30, 2], + [-30, 1], + [-30, -8], + [-8, 3], + [-31, 32], + [1, 21], + [14, 4], + [63, -6], + [48, -33] + ], + [ + [2595, 9379], + [22, -36], + [26, 47], + [70, 24], + [48, -61], + [-4, -38], + [55, 17], + [26, 23], + [62, -30], + [38, -28], + [3, -25], + [52, 13], + [29, -38], + [67, -23], + [24, -24], + [26, -55], + [-51, -28], + [66, -38], + [44, -13], + [40, -55], + [44, -3], + [-9, -42], + [-49, -69], + [-34, 26], + [-44, 57], + [-36, -8], + [-3, -34], + [29, -34], + [38, -27], + [11, -16], + [18, -58], + [-9, -43], + [-35, 16], + [-70, 47], + [39, -51], + [29, -35], + [5, -21], + [-76, 24], + [-59, 34], + [-34, 29], + [10, 17], + [-42, 30], + [-40, 29], + [0, -18], + [-80, -9], + [-23, 20], + [18, 44], + [52, 1], + [57, 7], + [-9, 21], + [10, 30], + [36, 57], + [-8, 27], + [-11, 20], + [-42, 29], + [-57, 20], + [18, 15], + [-29, 36], + [-25, 4], + [-22, 20], + [-14, -18], + [-51, -7], + [-101, 13], + [-59, 17], + [-45, 9], + [-23, 21], + [29, 27], + [-39, 0], + [-9, 60], + [21, 53], + [29, 24], + [72, 16], + [-21, -39] + ], + [ + [2212, 9420], + [33, -12], + [50, 7], + [7, -17], + [-26, -28], + [42, -26], + [-5, -53], + [-45, -23], + [-27, 5], + [-19, 23], + [-69, 45], + [0, 19], + [57, -7], + [-31, 38], + [33, 29] + ], + [ + [2411, 9357], + [-30, -45], + [-32, 3], + [-17, 52], + [1, 29], + [14, 25], + [28, 16], + [58, -2], + [53, -14], + [-42, -53], + [-33, -11] + ], + [ + [1654, 9275], + [-73, -29], + [-15, 26], + [-64, 31], + [12, 25], + [19, 43], + [24, 39], + [-27, 36], + [94, 10], + [39, -13], + [71, -3], + [27, -17], + [30, -25], + [-35, -15], + [-68, -41], + [-34, -42], + [0, -25] + ], + [ + [2399, 9487], + [-15, -23], + [-40, 5], + [-34, 15], + [15, 27], + [40, 16], + [24, -21], + [10, -19] + ], + [ + [2264, 9590], + [21, -27], + [1, -31], + [-13, -44], + [-46, -6], + [-30, 10], + [1, 34], + [-45, -4], + [-2, 45], + [30, -2], + [41, 21], + [40, -4], + [2, 8] + ], + [ + [1994, 9559], + [11, -21], + [25, 10], + [29, -2], + [5, -29], + [-17, -28], + [-94, -10], + [-70, -25], + [-43, -2], + [-3, 20], + [57, 26], + [-125, -7], + [-39, 10], + [38, 58], + [26, 17], + [78, -20], + [50, -35], + [48, -5], + [-40, 57], + [26, 21], + [29, -7], + [9, -28] + ], + [ + [2370, 9612], + [30, -19], + [55, 0], + [24, -19], + [-6, -22], + [32, -14], + [17, -14], + [38, -2], + [40, -5], + [44, 13], + [57, 5], + [45, -5], + [30, -22], + [6, -24], + [-17, -16], + [-42, -13], + [-35, 8], + [-80, -10], + [-57, -1], + [-45, 8], + [-74, 19], + [-9, 32], + [-4, 29], + [-27, 26], + [-58, 7], + [-32, 19], + [10, 24], + [58, -4] + ], + [ + [1772, 9645], + [-4, -46], + [-21, -20], + [-26, -3], + [-52, -26], + [-44, -9], + [-38, 13], + [47, 44], + [57, 39], + [43, -1], + [38, 9] + ], + [ + [2393, 9637], + [-13, -2], + [-52, 4], + [-7, 17], + [56, -1], + [19, -11], + [-3, -7] + ], + [ + [1939, 9648], + [-52, -17], + [-41, 19], + [23, 19], + [40, 6], + [39, -10], + [-9, -17] + ], + [ + [1954, 9701], + [-34, -11], + [-46, 0], + [0, 8], + [29, 18], + [14, -3], + [37, -12] + ], + [ + [2338, 9669], + [-41, -12], + [-23, 13], + [-12, 23], + [-2, 24], + [36, -2], + [16, -4], + [33, -21], + [-7, -21] + ], + [ + [2220, 9685], + [11, -25], + [-45, 7], + [-46, 19], + [-62, 2], + [27, 18], + [-34, 14], + [-2, 22], + [55, -8], + [75, -21], + [21, -28] + ], + [ + [2583, 9764], + [33, -20], + [-38, -17], + [-51, -45], + [-50, -4], + [-57, 8], + [-30, 24], + [0, 21], + [22, 16], + [-50, 0], + [-31, 19], + [-18, 27], + [20, 26], + [19, 18], + [28, 4], + [-12, 14], + [65, 3], + [35, -32], + [47, -12], + [46, -11], + [22, -39] + ], + [ + [3097, 9967], + [74, -4], + [60, -8], + [51, -16], + [-2, -16], + [-67, -25], + [-68, -12], + [-25, -14], + [61, 1], + [-66, -36], + [-45, -17], + [-48, -48], + [-57, -10], + [-18, -12], + [-84, -6], + [39, -8], + [-20, -10], + [23, -29], + [-26, -21], + [-43, -16], + [-13, -24], + [-39, -17], + [4, -14], + [48, 3], + [0, -15], + [-74, -35], + [-73, 16], + [-81, -9], + [-42, 7], + [-52, 3], + [-4, 29], + [52, 13], + [-14, 43], + [17, 4], + [74, -26], + [-38, 38], + [-45, 11], + [23, 23], + [49, 14], + [8, 21], + [-39, 23], + [-12, 31], + [76, -3], + [22, -6], + [43, 21], + [-62, 7], + [-98, -4], + [-49, 20], + [-23, 24], + [-32, 17], + [-6, 21], + [41, 11], + [32, 2], + [55, 9], + [41, 22], + [34, -3], + [30, -16], + [21, 32], + [37, 9], + [50, 7], + [85, 2], + [14, -6], + [81, 10], + [60, -4], + [60, -4] + ], + [ + [5290, 7828], + [-3, -24], + [-12, -10], + [-20, 7], + [-6, -24], + [-14, -2], + [-5, 10], + [-15, -20], + [-13, -3], + [-12, 13] + ], + [ + [5190, 7775], + [-10, 25], + [-13, -9], + [0, 27], + [21, 33], + [-1, 15], + [12, -5], + [8, 10] + ], + [ + [5207, 7871], + [24, -1], + [5, 13], + [30, -18] + ], + [ + [3140, 1814], + [-10, -24], + [-23, -18], + [-14, 2], + [-16, 5], + [-21, 18], + [-29, 8], + [-35, 33], + [-28, 32], + [-38, 66], + [23, -12], + [39, -40], + [36, -21], + [15, 27], + [9, 41], + [25, 24], + [20, -7] + ], + [ + [3095, 1968], + [-25, 0], + [-13, -14], + [-25, -22], + [-5, -55], + [-11, -1], + [-32, 19], + [-32, 41], + [-34, 34], + [-9, 37], + [8, 35], + [-14, 39], + [-4, 101], + [12, 57], + [30, 45], + [-43, 18], + [27, 52], + [9, 98], + [31, -21], + [15, 123], + [-19, 15], + [-9, -73], + [-17, 8], + [9, 84], + [9, 110], + [13, 40], + [-8, 58], + [-2, 66], + [11, 2], + [17, 96], + [20, 94], + [11, 88], + [-6, 89], + [8, 49], + [-3, 72], + [16, 73], + [5, 114], + [9, 123], + [9, 132], + [-2, 96], + [-6, 84] + ], + [ + [3045, 3974], + [14, 15], + [8, 30] + ], + [ + [8064, 6161], + [-24, -28], + [-23, 18], + [0, 51], + [13, 26], + [31, 17], + [16, -1], + [6, -23], + [-12, -26], + [-7, -34] + ], + [ + [8628, 7562], + [-18, 35], + [-11, -33], + [-43, -26], + [4, -31], + [-24, 2], + [-13, 19], + [-19, -42], + [-30, -32], + [-23, -38] + ], + [ + [8451, 7416], + [-39, -17], + [-20, -27], + [-30, -17], + [15, 28], + [-6, 23], + [22, 40], + [-15, 30], + [-24, -20], + [-32, -41], + [-17, -39], + [-27, -2], + [-14, -28], + [15, -40], + [22, -10], + [1, -26], + [22, -17], + [31, 42], + [25, -23], + [18, -2], + [4, -31], + [-39, -16], + [-13, -32], + [-27, -30], + [-14, -41], + [30, -33], + [11, -58], + [17, -54], + [18, -45], + [0, -44], + [-17, -16], + [6, -32], + [17, -18], + [-5, -48], + [-7, -47], + [-15, -5], + [-21, -64], + [-22, -78], + [-26, -70], + [-38, -55], + [-39, -50], + [-31, -6], + [-17, -27], + [-10, 20], + [-15, -30], + [-39, -29], + [-29, -9], + [-10, -63], + [-15, -3], + [-8, 43], + [7, 22], + [-37, 19], + [-13, -9] + ], + [ + [8001, 6331], + [-28, 15], + [-14, 24], + [5, 34], + [-26, 11], + [-13, 22], + [-24, -31], + [-27, -7], + [-22, 0], + [-15, -14] + ], + [ + [7837, 6385], + [-14, -9], + [4, -68], + [-15, 2], + [-2, 14] + ], + [ + [7810, 6324], + [-1, 24], + [-20, -17], + [-12, 11], + [-21, 22], + [8, 49], + [-18, 12], + [-6, 54], + [-30, -10], + [4, 70], + [26, 50], + [1, 48], + [-1, 46], + [-12, 14], + [-9, 35], + [-16, -5] + ], + [ + [7703, 6727], + [-30, 9], + [9, 25], + [-13, 36], + [-20, -24], + [-23, 14], + [-32, -37], + [-25, -44], + [-23, -8] + ], + [ + [7466, 6670], + [-2, 47], + [-17, -13] + ], + [ + [7447, 6704], + [-32, 6], + [-32, 14], + [-22, 26], + [-22, 11], + [-9, 29], + [-16, 8], + [-28, 39], + [-22, 18], + [-12, -14] + ], + [ + [7252, 6841], + [-38, 41], + [-28, 37], + [-7, 65], + [20, -7], + [1, 30], + [-12, 30], + [3, 48], + [-30, 69] + ], + [ + [7161, 7154], + [-45, 24], + [-8, 46], + [-21, 27] + ], + [ + [7082, 7268], + [-4, 34], + [1, 23], + [-17, 13], + [-9, -6], + [-7, 55] + ], + [ + [7046, 7387], + [8, 13], + [-4, 14], + [26, 28], + [20, 12], + [29, -8], + [11, 38], + [35, 7], + [10, 23], + [44, 32], + [4, 13] + ], + [ + [7229, 7559], + [-2, 34], + [19, 15], + [-25, 103], + [55, 24], + [14, 13], + [20, 106], + [55, -20], + [15, 27], + [2, 59], + [23, 6], + [21, 39] + ], + [ + [7426, 7965], + [11, 5] + ], + [ + [7437, 7970], + [7, -41], + [23, -32], + [40, -22], + [19, -47], + [-10, -70], + [10, -25], + [33, -10], + [37, -8], + [33, -37], + [18, -7], + [12, -54], + [17, -35], + [30, 1], + [58, -13], + [36, 8], + [28, -9], + [41, -36], + [34, 0], + [12, -18], + [32, 32], + [45, 20], + [42, 2], + [32, 21], + [20, 32], + [20, 20], + [-5, 19], + [-9, 23], + [15, 38], + [15, -5], + [29, -12], + [28, 31], + [42, 23], + [20, 39], + [20, 17], + [40, 8], + [22, -7], + [3, 21], + [-25, 41], + [-22, 19], + [-22, -22], + [-27, 10], + [-16, -8], + [-7, 24], + [20, 59], + [13, 45] + ], + [ + [8240, 8005], + [34, -23], + [39, 38], + [-1, 26], + [26, 62], + [15, 19], + [0, 33], + [-16, 14], + [23, 29], + [35, 11], + [37, 2], + [41, -18], + [25, -22], + [17, -59], + [10, -26], + [10, -36], + [10, -58], + [49, -19], + [32, -42], + [12, -55], + [42, 0], + [24, 23], + [46, 17], + [-15, -53], + [-11, -21], + [-9, -65], + [-19, -58], + [-33, 11], + [-24, -21], + [7, -51], + [-4, -69], + [-14, -2], + [0, -30] + ], + [ + [4920, 5353], + [-12, -1], + [-20, 12], + [-18, -1], + [-33, -10], + [-19, -18], + [-27, -21], + [-6, 1] + ], + [ + [4785, 5315], + [2, 49], + [3, 7], + [-1, 24], + [-12, 24], + [-8, 4], + [-8, 17], + [6, 26], + [-3, 28], + [1, 18] + ], + [ + [4765, 5512], + [5, 0], + [1, 25], + [-2, 12], + [3, 8], + [10, 7], + [-7, 47], + [-6, 25], + [2, 20], + [5, 4] + ], + [ + [4776, 5660], + [4, 6], + [8, -9], + [21, -1], + [5, 18], + [5, -1], + [8, 6], + [4, -25], + [7, 7], + [11, 9] + ], + [ + [4921, 5627], + [7, -84], + [-11, -50], + [-8, -66], + [12, -51], + [-1, -23] + ], + [ + [5363, 5191], + [-4, 4], + [-16, -8], + [-17, 8], + [-13, -4] + ], + [ + [5313, 5191], + [-45, 1] + ], + [ + [5268, 5192], + [4, 47], + [-11, 39], + [-13, 10], + [-6, 27], + [-7, 8], + [1, 16] + ], + [ + [5236, 5339], + [7, 42], + [13, 57], + [8, 1], + [17, 34], + [10, 1], + [16, -24], + [19, 20], + [2, 25], + [7, 23], + [4, 30], + [15, 25], + [5, 41], + [6, 13], + [4, 31], + [7, 37], + [24, 46], + [1, 20], + [3, 10], + [-11, 24] + ], + [ + [5393, 5795], + [1, 19], + [8, 3] + ], + [ + [5402, 5817], + [11, -38], + [2, -39], + [-1, -39], + [15, -54], + [-15, 1], + [-8, -4], + [-13, 6], + [-6, -28], + [16, -35], + [13, -10], + [3, -24], + [9, -41], + [-4, -16] + ], + [ + [5444, 5191], + [-2, -31], + [-22, 14], + [-22, 15], + [-35, 2] + ], + [ + [5856, 5265], + [-2, -69], + [11, -8], + [-9, -21], + [-10, -16], + [-11, -31], + [-6, -27], + [-1, -48], + [-7, -22], + [0, -45] + ], + [ + [5821, 4978], + [-8, -16], + [-1, -35], + [-4, -5], + [-2, -32] + ], + [ + [5814, 4792], + [5, -55], + [-2, -30], + [5, -35], + [16, -33], + [15, -74] + ], + [ + [5853, 4565], + [-11, 6], + [-37, -10], + [-7, -7], + [-8, -38], + [6, -26], + [-5, -70], + [-3, -59], + [7, -11], + [19, -23], + [8, 11], + [2, -64], + [-21, 1], + [-11, 32], + [-10, 25], + [-22, 9], + [-6, 31], + [-17, -19], + [-22, 8], + [-10, 27], + [-17, 6], + [-13, -2], + [-2, 19], + [-9, 1] + ], + [ + [5342, 4697], + [-4, 18] + ], + [ + [5360, 4775], + [8, -6], + [9, 23], + [15, -1], + [2, -17], + [11, -10], + [16, 37], + [16, 29], + [7, 19], + [-1, 48], + [12, 58], + [13, 30], + [18, 29], + [3, 18], + [1, 22], + [5, 21], + [-2, 33], + [4, 52], + [5, 37], + [8, 32], + [2, 36] + ], + [ + [5760, 5367], + [17, -49], + [12, -7], + [8, 10], + [12, -4], + [16, 12], + [6, -25], + [25, -39] + ], + [ + [5330, 4760], + [-22, 62] + ], + [ + [5308, 4822], + [21, 33], + [-11, 39], + [10, 15], + [19, 7], + [2, 26], + [15, -28], + [24, -2], + [9, 27], + [3, 40], + [-3, 46], + [-13, 35], + [12, 68], + [-7, 12], + [-21, -5], + [-7, 31], + [2, 25] + ], + [ + [2906, 5049], + [-12, 14], + [-14, 19], + [-7, -9], + [-24, 8], + [-7, 25], + [-5, -1], + [-28, 34] + ], + [ + [2809, 5139], + [-3, 18], + [10, 5], + [-1, 29], + [6, 22], + [14, 4], + [12, 37], + [10, 31], + [-10, 14], + [5, 34], + [-6, 54], + [6, 16], + [-4, 50], + [-12, 31] + ], + [ + [2836, 5484], + [4, 29], + [9, -4], + [5, 17], + [-6, 35], + [3, 9] + ], + [ + [2851, 5570], + [14, -2], + [21, 41], + [12, 6], + [0, 20], + [5, 50], + [16, 27], + [17, 1], + [3, 13], + [21, -5], + [22, 30], + [11, 13], + [14, 28], + [9, -3], + [8, -16], + [-6, -20] + ], + [ + [3018, 5753], + [-18, -10], + [-7, -29], + [-10, -17], + [-8, -22], + [-4, -42], + [-8, -35], + [15, -4], + [3, -27], + [6, -13], + [3, -24], + [-4, -22], + [1, -12], + [7, -5], + [7, -20], + [36, 5], + [16, -7], + [19, -51], + [11, 6], + [20, -3], + [16, 7], + [10, -10], + [-5, -32], + [-6, -20], + [-2, -42], + [5, -40], + [8, -17], + [1, -13], + [-14, -30], + [10, -13], + [8, -21], + [8, -58] + ], + [ + [3058, 4804], + [-14, 31], + [-8, 1], + [18, 61], + [-21, 27], + [-17, -5], + [-10, 10], + [-15, -15], + [-21, 7], + [-16, 62], + [-13, 15], + [-9, 28], + [-19, 28], + [-7, -5] + ], + [ + [2695, 5543], + [-15, 14], + [-6, 12], + [4, 10], + [-1, 13], + [-8, 14], + [-11, 12], + [-10, 8], + [-1, 17], + [-8, 10], + [2, -17], + [-5, -14], + [-7, 17], + [-9, 5], + [-4, 12], + [1, 18], + [3, 19], + [-8, 8], + [7, 12] + ], + [ + [2619, 5713], + [4, 7], + [18, -15], + [7, 7], + [9, -5], + [4, -12], + [8, -4], + [7, 13] + ], + [ + [2676, 5704], + [7, -32], + [11, -24], + [13, -25] + ], + [ + [2707, 5623], + [-11, -6], + [0, -23], + [6, -9], + [-4, -7], + [1, -11], + [-2, -12], + [-2, -12] + ], + [ + [2715, 6427], + [23, -4], + [22, 0], + [26, -21], + [11, -21], + [26, 6], + [10, -13], + [24, -37], + [17, -27], + [9, 1], + [17, -12], + [-2, -17], + [20, -2], + [21, -24], + [-3, -14], + [-19, -7], + [-18, -3], + [-19, 4], + [-40, -5], + [18, 32], + [-11, 16], + [-18, 4], + [-9, 17], + [-7, 33], + [-16, -2], + [-26, 16], + [-8, 12], + [-36, 10], + [-10, 11], + [11, 15], + [-28, 3], + [-20, -31], + [-11, -1], + [-4, -14], + [-14, -7], + [-12, 6], + [15, 18], + [6, 22], + [13, 13], + [14, 11], + [21, 6], + [7, 6] + ], + [ + [5909, 7133], + [2, 1], + [4, 14], + [20, -1], + [25, 18], + [-19, -25], + [2, -11] + ], + [ + [5943, 7129], + [-3, 2], + [-5, -5], + [-4, 1], + [-2, -2], + [0, 6], + [-2, 4], + [-6, 0], + [-7, -5], + [-5, 3] + ], + [ + [5943, 7129], + [1, -5], + [-28, -24], + [-14, 8], + [-7, 23], + [14, 2] + ], + [ + [5377, 7945], + [-16, 25], + [-14, 15], + [-3, 25], + [-5, 17], + [21, 13], + [10, 15], + [20, 11], + [7, 11], + [7, -6], + [13, 6] + ], + [ + [5417, 8077], + [13, -19], + [21, -5], + [-2, -17], + [15, -12], + [4, 15], + [19, -6], + [3, -19], + [20, -3], + [13, -29] + ], + [ + [5523, 7982], + [-8, 0], + [-4, -11], + [-7, -3], + [-2, -13], + [-5, -3], + [-1, -5], + [-9, -7], + [-12, 1], + [-4, -13] + ], + [ + [5275, 8306], + [1, -23], + [28, -14], + [-1, -21], + [29, 11], + [15, 16], + [32, -23], + [13, -19] + ], + [ + [5392, 8233], + [6, -30], + [-8, -16], + [11, -21], + [6, -31], + [-2, -21], + [12, -37] + ], + [ + [5207, 7871], + [3, 42], + [14, 40], + [-40, 11], + [-13, 16] + ], + [ + [5171, 7980], + [2, 26], + [-6, 13] + ], + [ + [5171, 8059], + [-5, 62], + [17, 0], + [7, 22], + [6, 54], + [-5, 20] + ], + [ + [5191, 8217], + [6, 13], + [23, 3], + [5, -13], + [19, 29], + [-6, 22], + [-2, 34] + ], + [ + [5236, 8305], + [21, -8], + [18, 9] + ], + [ + [6196, 5808], + [7, -19], + [-1, -24], + [-16, -14], + [12, -16] + ], + [ + [6198, 5735], + [-10, -32] + ], + [ + [6188, 5703], + [-7, 11], + [-6, -5], + [-16, 1], + [0, 18], + [-2, 17], + [9, 27], + [10, 26] + ], + [ + [6176, 5798], + [12, -5], + [8, 15] + ], + [ + [5352, 8343], + [-17, -48], + [-29, 33], + [-4, 25], + [41, 19], + [9, -29] + ], + [ + [5236, 8305], + [-11, 32], + [-1, 61], + [5, 16], + [8, 17], + [24, 4], + [10, 16], + [22, 17], + [-1, -30], + [-8, -20], + [4, -16], + [15, -9], + [-7, -22], + [-8, 6], + [-20, -42], + [7, -29] + ], + [ + [3008, 6222], + [3, 10], + [22, 0], + [16, -15], + [8, 1], + [5, -21], + [15, 1], + [-1, -17], + [12, -2], + [14, -22], + [-10, -24], + [-14, 13], + [-12, -3], + [-9, 3], + [-5, -11], + [-11, -3], + [-4, 14], + [-10, -8], + [-11, -41], + [-7, 10], + [-1, 17] + ], + [ + [3008, 6124], + [0, 16], + [-7, 17], + [7, 10], + [2, 23], + [-2, 32] + ], + [ + [5333, 6444], + [-95, -112], + [-81, -117], + [-39, -26] + ], + [ + [5118, 6189], + [-31, -6], + [0, 38], + [-13, 10], + [-17, 16], + [-7, 28], + [-94, 129], + [-93, 129] + ], + [ + [4863, 6533], + [-105, 143] + ], + [ + [4758, 6676], + [1, 11], + [0, 4] + ], + [ + [4759, 6691], + [0, 70], + [44, 44], + [28, 9], + [23, 16], + [11, 29], + [32, 24], + [1, 44], + [16, 5], + [13, 22], + [36, 9], + [5, 23], + [-7, 13], + [-10, 62], + [-1, 36], + [-11, 38] + ], + [ + [4939, 7135], + [27, 32], + [30, 11], + [17, 24], + [27, 18], + [47, 11], + [46, 4], + [14, -8], + [26, 23], + [30, 0], + [11, -13], + [19, 3] + ], + [ + [5233, 7240], + [-5, -30], + [4, -56], + [-6, -49], + [-18, -33], + [3, -45], + [23, -35], + [0, -14], + [17, -24], + [12, -106] + ], + [ + [5263, 6848], + [9, -52], + [1, -28], + [-5, -48], + [2, -27], + [-3, -32], + [2, -37], + [-11, -25], + [17, -43], + [1, -25], + [10, -33], + [13, 11], + [22, -28], + [12, -37] + ], + [ + [2769, 4856], + [15, 45], + [-6, 25], + [-11, -27], + [-16, 26], + [5, 16], + [-4, 54], + [9, 9], + [5, 37], + [11, 38], + [-2, 24], + [15, 13], + [19, 23] + ], + [ + [2906, 5049], + [4, -45], + [-9, -39], + [-30, -62], + [-33, -23], + [-17, -51], + [-6, -40], + [-15, -24], + [-12, 29], + [-11, 7], + [-12, -5], + [-1, 22], + [8, 14], + [-3, 24] + ], + [ + [5969, 6800], + [-7, -23], + [-6, -45], + [-8, -31], + [-6, -10], + [-10, 19], + [-12, 26], + [-20, 85], + [-3, -5], + [12, -63], + [17, -59], + [21, -92], + [10, -32], + [9, -34], + [25, -65], + [-6, -10], + [1, -39], + [33, -53], + [4, -12] + ], + [ + [6023, 6357], + [-110, 0], + [-107, 0], + [-112, 0] + ], + [ + [5694, 6357], + [0, 218], + [0, 210], + [-8, 47], + [7, 37], + [-5, 25], + [10, 29] + ], + [ + [5698, 6923], + [37, 0], + [27, -15], + [28, -18], + [13, -9], + [21, 19], + [11, 17], + [25, 5], + [20, -8], + [7, -29], + [7, 19], + [22, -14], + [22, -3], + [13, 15] + ], + [ + [5951, 6902], + [18, -102] + ], + [ + [6176, 5798], + [-10, 20], + [-11, 34], + [-12, 19], + [-8, 21], + [-24, 23], + [-19, 1], + [-7, 12], + [-16, -14], + [-17, 27], + [-8, -44], + [-33, 13] + ], + [ + [6011, 5910], + [-3, 23], + [12, 87], + [3, 39], + [9, 18], + [20, 10], + [14, 34] + ], + [ + [6066, 6121], + [16, -69], + [8, -54], + [15, -29], + [38, -55], + [16, -34], + [15, -34], + [8, -20], + [14, -18] + ], + [ + [4749, 7532], + [1, 42], + [-11, 25], + [39, 43], + [34, -11], + [37, 1], + [30, -10], + [23, 3], + [45, -2] + ], + [ + [4947, 7623], + [11, -23], + [51, -27], + [10, 13], + [31, -27], + [32, 8] + ], + [ + [5082, 7567], + [2, -35], + [-26, -39], + [-36, -12], + [-2, -20], + [-18, -33], + [-10, -48], + [11, -34], + [-16, -26], + [-6, -39], + [-21, -11], + [-20, -46], + [-35, -1], + [-27, 1], + [-17, -21], + [-11, -22], + [-13, 5], + [-11, 20], + [-8, 34], + [-26, 9] + ], + [ + [4792, 7249], + [-2, 20], + [10, 22], + [4, 16], + [-9, 17], + [7, 39], + [-11, 36], + [12, 5], + [1, 27], + [5, 9], + [0, 46], + [13, 16], + [-8, 30], + [-16, 2], + [-5, -8], + [-16, 0], + [-7, 29], + [-11, -8], + [-10, -15] + ], + [ + [5675, 8472], + [3, 35], + [-10, -8], + [-18, 21], + [-2, 34], + [35, 17], + [35, 8], + [30, -10], + [29, 2] + ], + [ + [5777, 8571], + [4, -10], + [-20, -34], + [8, -55], + [-12, -19] + ], + [ + [5757, 8453], + [-22, 0], + [-24, 22], + [-13, 7], + [-23, -10] + ], + [ + [6188, 5703], + [-6, -21], + [10, -32], + [10, -29], + [11, -21], + [90, -70], + [24, 0] + ], + [ + [6327, 5530], + [-79, -177], + [-36, -3], + [-25, -41], + [-17, -1], + [-8, -19] + ], + [ + [6162, 5289], + [-19, 0], + [-11, 20], + [-26, -25], + [-8, -24], + [-18, 4], + [-6, 7], + [-7, -1], + [-9, 0], + [-35, 50], + [-19, 0], + [-10, 20], + [0, 33], + [-14, 10] + ], + [ + [5980, 5383], + [-17, 64], + [-12, 14], + [-5, 23], + [-14, 29], + [-17, 4], + [9, 34], + [15, 2], + [4, 18] + ], + [ + [5943, 5571], + [0, 53] + ], + [ + [5943, 5624], + [8, 62], + [13, 16], + [3, 24], + [12, 45], + [17, 30], + [11, 58], + [4, 51] + ], + [ + [5794, 9138], + [-4, -42], + [42, -39], + [-26, -45], + [33, -67], + [-19, -51], + [25, -43], + [-11, -39], + [41, -40], + [-11, -31], + [-25, -34], + [-60, -75] + ], + [ + [5779, 8632], + [-50, -5], + [-49, -21], + [-45, -13], + [-16, 32], + [-27, 20], + [6, 58], + [-14, 53], + [14, 35], + [25, 37], + [63, 64], + [19, 12], + [-3, 25], + [-39, 28] + ], + [ + [5663, 8957], + [-9, 23], + [-1, 91], + [-43, 40], + [-37, 29] + ], + [ + [5573, 9140], + [17, 16], + [30, -32], + [37, 3], + [30, -14], + [26, 26], + [14, 44], + [43, 20], + [35, -24], + [-11, -41] + ], + [ + [9954, 4033], + [9, -17], + [-4, -31], + [-17, -8], + [-16, 7], + [-2, 26], + [10, 21], + [13, -8], + [7, 10] + ], + [ + [0, 4079], + [9981, -14], + [-17, -13], + [-4, 23], + [14, 12], + [9, 3], + [-9983, 18] + ], + [ + [0, 4108], + [0, -29] + ], + [ + [0, 4108], + [6, 3], + [-4, -28], + [-2, -4] + ], + [ + [3300, 1994], + [33, 36], + [24, -15], + [16, 24], + [22, -27], + [-8, -21], + [-37, -17], + [-13, 20], + [-23, -26], + [-14, 26] + ], + [ + [5265, 7548], + [-9, -46], + [-13, 12], + [-6, 40], + [5, 22], + [18, 22], + [5, -50] + ], + [ + [5157, 7984], + [6, -6], + [8, 2] + ], + [ + [5190, 7775], + [-2, -17], + [9, -22], + [-10, -18], + [7, -46], + [15, -8], + [-3, -25] + ], + [ + [5206, 7639], + [-25, -34], + [-55, 16], + [-40, -19], + [-4, -35] + ], + [ + [4947, 7623], + [14, 35], + [5, 118], + [-28, 62], + [-21, 30], + [-42, 23], + [-3, 43], + [36, 12], + [47, -15], + [-9, 67], + [26, -25], + [65, 46], + [8, 48], + [24, 12] + ], + [ + [3485, 5194], + [7, 25], + [3, 27] + ], + [ + [3495, 5246], + [4, 26], + [-10, 34] + ], + [ + [3489, 5306], + [-3, 41], + [15, 51] + ], + [ + [3501, 5398], + [9, -7], + [21, -14], + [29, -50], + [5, -24] + ], + [ + [5308, 4822], + [-29, 60], + [-18, 49], + [-17, 61], + [1, 19], + [6, 19], + [7, 43], + [5, 44] + ], + [ + [5263, 5117], + [10, 4], + [40, -1], + [0, 71] + ], + [ + [4827, 8240], + [-21, 12], + [-17, -1], + [6, 32], + [-6, 32] + ], + [ + [4789, 8315], + [23, 2], + [30, -37], + [-15, -40] + ], + [ + [4916, 8521], + [-30, -63], + [29, 8], + [30, -1], + [-7, -48], + [-25, -53], + [29, -4], + [2, -6], + [25, -69], + [19, -10], + [17, -67], + [8, -24], + [33, -11], + [-3, -38], + [-14, -17], + [11, -30], + [-25, -31], + [-37, 0], + [-48, -16], + [-13, 12], + [-18, -28], + [-26, 7], + [-19, -23], + [-15, 12], + [41, 62], + [25, 13], + [-1, 0], + [-43, 9], + [-8, 24], + [29, 18], + [-15, 32], + [5, 39], + [42, -6], + [4, 35], + [-19, 36], + [0, 1], + [-34, 10], + [-7, 16], + [10, 27], + [-9, 16], + [-15, -28], + [-1, 57], + [-14, 30], + [10, 61], + [21, 48], + [23, -4], + [33, 4] + ], + [ + [6154, 7511], + [4, 26], + [-7, 40], + [-16, 22], + [-16, 6], + [-10, 19] + ], + [ + [6109, 7624], + [4, 6], + [23, -10], + [41, -9], + [38, -28], + [5, -11], + [17, 9], + [25, -13], + [9, -24], + [17, -13] + ], + [ + [6210, 7485], + [-27, 29], + [-29, -3] + ], + [ + [5029, 5408], + [-44, -35], + [-15, -20], + [-25, -17], + [-25, 17] + ], + [ + [5000, 5708], + [-2, -18], + [12, -30], + [0, -43], + [2, -47], + [7, -21], + [-6, -54], + [2, -29], + [8, -37], + [6, -21] + ], + [ + [4765, 5512], + [-8, 1], + [-5, -24], + [-8, 1], + [-6, 12], + [2, 24], + [-11, 36], + [-8, -7], + [-6, -1] + ], + [ + [4715, 5554], + [-7, -3], + [0, 21], + [-4, 16], + [0, 17], + [-6, 25], + [-7, 21], + [-23, 0], + [-6, -11], + [-8, -1], + [-4, -13], + [-4, -17], + [-14, -26] + ], + [ + [4632, 5583], + [-13, 35], + [-10, 24], + [-8, 7], + [-6, 12], + [-4, 26], + [-4, 13], + [-8, 10] + ], + [ + [4579, 5710], + [13, 29], + [8, -2], + [7, 10], + [6, 0], + [5, 8], + [-3, 20], + [3, 6], + [1, 20] + ], + [ + [4619, 5801], + [13, -1], + [20, -14], + [6, 1], + [3, 7], + [15, -5], + [4, 4] + ], + [ + [4680, 5793], + [1, -22], + [5, 0], + [7, 8], + [5, -2], + [7, -15], + [12, -5], + [8, 13], + [9, 8], + [6, 8], + [6, -1], + [6, -13], + [3, -17], + [12, -24], + [-6, -16], + [-1, -19], + [6, 6], + [3, -7], + [-1, -17], + [8, -18] + ], + [ + [4532, 5834], + [3, 27] + ], + [ + [4535, 5861], + [31, 1], + [6, 14], + [9, 1], + [11, -14], + [8, -1], + [9, 10], + [6, -17], + [-12, -13], + [-12, 1], + [-12, 13], + [-10, -14], + [-5, -1], + [-7, -8], + [-25, 1] + ], + [ + [4579, 5710], + [-15, 24], + [-11, 4], + [-7, 17], + [1, 9], + [-9, 13], + [-2, 12] + ], + [ + [4536, 5789], + [15, 10], + [9, -2], + [8, 7], + [51, -3] + ], + [ + [5263, 5117], + [-5, 9], + [10, 66] + ], + [ + [5658, 7167], + [15, -20], + [22, 3], + [20, -4], + [0, -10], + [15, 7], + [-4, -18], + [-40, -5], + [1, 10], + [-34, 12], + [5, 25] + ], + [ + [5723, 7469], + [-17, 2], + [-14, 6], + [-34, -16], + [19, -33], + [-14, -10], + [-15, 0], + [-15, 31], + [-5, -13], + [6, -36], + [14, -27], + [-10, -13], + [15, -27], + [14, -18], + [0, -33], + [-25, 16], + [8, -30], + [-18, -7], + [11, -52], + [-19, -1], + [-23, 26], + [-10, 47], + [-5, 40], + [-11, 27], + [-14, 34], + [-2, 16] + ], + [ + [5583, 7470], + [18, 6], + [11, 13], + [15, -2], + [5, 11], + [5, 2] + ], + [ + [5725, 7529], + [13, -16], + [-8, -37], + [-7, -7] + ], + [ + [3701, 9939], + [93, 35], + [97, -2], + [36, 21], + [98, 6], + [222, -7], + [174, -47], + [-52, -23], + [-106, -3], + [-150, -5], + [14, -11], + [99, 7], + [83, -21], + [54, 18], + [23, -21], + [-30, -34], + [71, 22], + [135, 23], + [83, -12], + [15, -25], + [-113, -42], + [-16, -14], + [-88, -10], + [64, -3], + [-32, -43], + [-23, -38], + [1, -66], + [33, -38], + [-43, -3], + [-46, -19], + [52, -31], + [6, -50], + [-30, -6], + [36, -50], + [-61, -5], + [32, -24], + [-9, -20], + [-39, -10], + [-39, 0], + [35, -40], + [0, -26], + [-55, 24], + [-14, -15], + [37, -15], + [37, -36], + [10, -48], + [-49, -11], + [-22, 22], + [-34, 34], + [10, -40], + [-33, -31], + [73, -2], + [39, -3], + [-75, -52], + [-75, -46], + [-81, -21], + [-31, 0], + [-29, -23], + [-38, -62], + [-60, -42], + [-19, -2], + [-37, -15], + [-40, -13], + [-24, -37], + [0, -41], + [-15, -39], + [-45, -47], + [11, -47], + [-12, -48], + [-14, -58], + [-39, -4], + [-41, 49], + [-56, 0], + [-27, 32], + [-18, 58], + [-49, 73], + [-14, 39], + [-3, 53], + [-39, 54], + [10, 44], + [-18, 21], + [27, 69], + [42, 22], + [11, 25], + [6, 46], + [-32, -21], + [-15, -9], + [-25, -8], + [-34, 19], + [-2, 40], + [11, 31], + [25, 1], + [57, -15], + [-48, 37], + [-24, 20], + [-28, -8], + [-23, 15], + [31, 55], + [-17, 22], + [-22, 41], + [-34, 62], + [-35, 23], + [0, 25], + [-74, 34], + [-59, 5], + [-74, -3], + [-68, -4], + [-32, 19], + [-49, 37], + [73, 19], + [56, 3], + [-119, 15], + [-62, 24], + [3, 23], + [106, 28], + [101, 29], + [11, 21], + [-75, 22], + [24, 23], + [97, 41], + [40, 7], + [-12, 26], + [66, 16], + [86, 9], + [85, 1], + [30, -19], + [74, 33], + [66, -22], + [39, -5], + [58, -19], + [-66, 32], + [4, 25] + ], + [ + [2497, 5869], + [-14, 10], + [-17, 1], + [-13, 12], + [-15, 24] + ], + [ + [2438, 5916], + [1, 18], + [3, 13], + [-4, 12], + [13, 48], + [36, 0], + [1, 20], + [-5, 4], + [-3, 12], + [-10, 14], + [-11, 20], + [13, 0], + [0, 33], + [26, 0], + [26, 0] + ], + [ + [2529, 5996], + [10, -11], + [2, 9], + [8, -7] + ], + [ + [2549, 5987], + [-13, -23], + [-13, -16], + [-2, -12], + [2, -11], + [-5, -15] + ], + [ + [2518, 5910], + [-7, -4], + [2, -7], + [-6, -6], + [-9, -15], + [-1, -9] + ], + [ + [3340, 5552], + [18, -22], + [17, -38], + [1, -31], + [10, -1], + [15, -29], + [11, -21] + ], + [ + [3412, 5410], + [-4, -53], + [-17, -15], + [1, -14], + [-5, -31], + [13, -42], + [9, -1], + [3, -33], + [17, -51] + ], + [ + [3313, 5365], + [-19, 45], + [7, 16], + [0, 27], + [17, 10], + [7, 11], + [-10, 22], + [3, 21], + [22, 35] + ], + [ + [2574, 5825], + [-5, 18], + [-8, 5] + ], + [ + [2561, 5848], + [2, 24], + [-4, 6], + [-6, 4], + [-12, -7], + [-1, 8], + [-8, 10], + [-6, 12], + [-8, 5] + ], + [ + [2549, 5987], + [3, -3], + [6, 11], + [8, 1], + [3, -5], + [4, 3], + [13, -6], + [13, 2], + [9, 6], + [3, 7], + [9, -3], + [6, -4], + [8, 1], + [5, 5], + [13, -8], + [4, -1], + [9, -11], + [8, -13], + [10, -9], + [7, -17] + ], + [ + [2690, 5943], + [-9, 2], + [-4, -8], + [-10, -8], + [-7, 0], + [-6, -8], + [-6, 3], + [-4, 9], + [-3, -2], + [-4, -14], + [-3, 1], + [0, -12], + [-10, -17], + [-5, -7], + [-3, -7], + [-8, 12], + [-6, -16], + [-6, 1], + [-6, -2], + [0, -29], + [-4, 0], + [-3, -14], + [-9, -2] + ], + [ + [5522, 7770], + [7, -23], + [9, -17], + [-11, -22] + ], + [ + [5515, 7577], + [-3, -10] + ], + [ + [5512, 7567], + [-26, 22], + [-16, 21], + [-26, 18], + [-23, 43], + [6, 5], + [-13, 25], + [-1, 19], + [-17, 10], + [-9, -26], + [-8, 20], + [0, 21], + [1, 1] + ], + [ + [5380, 7746], + [20, -2], + [5, 9], + [9, -9], + [11, -1], + [0, 16], + [10, 6], + [2, 24], + [23, 16] + ], + [ + [5460, 7805], + [8, -7], + [21, -26], + [23, -11], + [10, 9] + ], + [ + [3008, 6124], + [-19, 10], + [-13, -5], + [-17, 5], + [-13, -11], + [-15, 18], + [3, 19], + [25, -8], + [21, -5], + [10, 13], + [-12, 26], + [0, 23], + [-18, 9], + [7, 16], + [17, -3], + [24, -9] + ], + [ + [5471, 7900], + [14, -15], + [10, -6], + [24, 7], + [2, 12], + [11, 2], + [14, 9], + [3, -4], + [13, 8], + [6, 13], + [9, 4], + [30, -18], + [6, 6] + ], + [ + [5613, 7918], + [15, -16], + [2, -16] + ], + [ + [5630, 7886], + [-17, -12], + [-13, -40], + [-17, -40], + [-22, -11] + ], + [ + [5561, 7783], + [-17, 2], + [-22, -15] + ], + [ + [5460, 7805], + [-6, 20], + [-4, 0] + ], + [ + [8352, 4453], + [-11, -2], + [-37, 42], + [26, 11], + [14, -18], + [10, -17], + [-2, -16] + ], + [ + [8471, 4532], + [2, -11], + [1, -18] + ], + [ + [8474, 4503], + [-18, -45], + [-24, -13], + [-3, 8], + [2, 20], + [12, 36], + [28, 23] + ], + [ + [8274, 4579], + [10, -16], + [17, 5], + [7, -25], + [-32, -12], + [-19, -8], + [-15, 1], + [10, 34], + [15, 0], + [7, 21] + ], + [ + [8413, 4579], + [-4, -32], + [-42, -17], + [-37, 7], + [0, 22], + [22, 12], + [18, -18], + [18, 5], + [25, 21] + ], + [ + [8017, 4657], + [53, -6], + [6, 25], + [51, -29], + [10, -38], + [42, -11], + [34, -35], + [-31, -23], + [-31, 24], + [-25, -1], + [-29, 4], + [-26, 11], + [-32, 22], + [-21, 6], + [-11, -7], + [-51, 24], + [-5, 25], + [-25, 5], + [19, 56], + [34, -3], + [22, -23], + [12, -5], + [4, -21] + ], + [ + [8741, 4690], + [-14, -40], + [-3, 45], + [5, 21], + [6, 20], + [7, -17], + [-1, -29] + ], + [ + [8534, 4853], + [-11, -19], + [-19, 10], + [-5, 26], + [28, 3], + [7, -20] + ], + [ + [8623, 4875], + [10, -45], + [-23, 24], + [-23, 5], + [-16, -4], + [-19, 2], + [6, 33], + [35, 2], + [30, -17] + ], + [ + [8916, 4904], + [0, -193], + [1, -192] + ], + [ + [8917, 4519], + [-25, 48], + [-28, 12], + [-7, -17], + [-35, -1], + [12, 48], + [17, 16], + [-7, 64], + [-14, 50], + [-53, 50], + [-23, 5], + [-42, 54], + [-8, -28], + [-11, -5], + [-6, 21], + [0, 26], + [-21, 29], + [29, 21], + [20, -1], + [-2, 16], + [-41, 0], + [-11, 35], + [-25, 11], + [-11, 29], + [37, 14], + [14, 20], + [45, -25], + [4, -22], + [8, -95], + [29, -35], + [23, 62], + [32, 36], + [25, 0], + [23, -21], + [21, -21], + [30, -11] + ], + [ + [8478, 5141], + [-22, -58], + [-21, -12], + [-27, 12], + [-46, -3], + [-24, -8], + [-4, -45], + [24, -53], + [15, 27], + [52, 20], + [-2, -27], + [-12, 9], + [-12, -35], + [-25, -23], + [27, -76], + [-5, -20], + [25, -68], + [-1, -39], + [-14, -17], + [-11, 20], + [13, 49], + [-27, -23], + [-7, 16], + [3, 23], + [-20, 35], + [3, 57], + [-19, -18], + [2, -69], + [1, -84], + [-17, -9], + [-12, 18], + [8, 54], + [-4, 57], + [-12, 1], + [-9, 40], + [12, 39], + [4, 47], + [14, 89], + [5, 24], + [24, 44], + [22, -18], + [35, -8], + [32, 3], + [27, 43], + [5, -14] + ], + [ + [8574, 5124], + [-2, -51], + [-14, 6], + [-4, -36], + [11, -32], + [-8, -7], + [-11, 38], + [-8, 75], + [6, 47], + [9, 22], + [2, -32], + [16, -5], + [3, -25] + ], + [ + [8045, 5176], + [5, -39], + [19, -34], + [18, 12], + [18, -4], + [16, 30], + [13, 5], + [26, -17], + [23, 13], + [14, 82], + [11, 21], + [10, 67], + [32, 0], + [24, -10] + ], + [ + [8274, 5302], + [-16, -53], + [20, -56], + [-5, -28], + [32, -54], + [-33, -7], + [-10, -40], + [2, -54], + [-27, -40], + [-1, -59], + [-10, -91], + [-5, 21], + [-31, -26], + [-11, 36], + [-20, 3], + [-14, 19], + [-33, -21], + [-10, 29], + [-18, -4], + [-23, 7], + [-4, 79], + [-14, 17], + [-13, 50], + [-4, 52], + [3, 55], + [16, 39] + ], + [ + [7939, 4712], + [-31, -1], + [-24, 49], + [-35, 48], + [-12, 36], + [-21, 48], + [-14, 44], + [-21, 83], + [-24, 49], + [-9, 51], + [-10, 46], + [-25, 37], + [-14, 51], + [-21, 33], + [-29, 65], + [-3, 30], + [18, -2], + [43, -12], + [25, -57], + [21, -40], + [16, -25], + [26, -63], + [28, -1], + [23, -41], + [16, -49], + [22, -27], + [-12, -49], + [16, -20], + [10, -2], + [5, -41], + [10, -33], + [20, -5], + [14, -37], + [-7, -74], + [-1, -91] + ], + [ + [7252, 6841], + [-17, -27], + [-11, -55], + [27, -23], + [26, -29], + [36, -33], + [38, -8], + [16, -30], + [22, -5], + [33, -14], + [23, 1], + [4, 23], + [-4, 38], + [2, 25] + ], + [ + [7703, 6727], + [2, -22], + [-10, -11], + [2, -36], + [-19, 10], + [-36, -41], + [0, -33], + [-15, -50], + [-1, -29], + [-13, -48], + [-21, 13], + [-1, -61], + [-7, -20], + [3, -25], + [-14, -14] + ], + [ + [7472, 6360], + [-4, -21], + [-19, 1], + [-34, -13], + [2, -44], + [-15, -35], + [-40, -40], + [-31, -69], + [-21, -38], + [-28, -38], + [0, -27], + [-13, -15], + [-26, -21], + [-12, -3], + [-9, -45], + [6, -77], + [1, -49], + [-11, -56], + [0, -101], + [-15, -2], + [-12, -46], + [8, -19], + [-25, -17], + [-10, -40], + [-11, -17], + [-26, 55], + [-13, 83], + [-11, 60], + [-9, 28], + [-15, 56], + [-7, 74], + [-5, 37], + [-25, 81], + [-12, 115], + [-8, 75], + [0, 72], + [-5, 55], + [-41, -35], + [-19, 7], + [-36, 71], + [13, 22], + [-8, 23], + [-33, 50] + ], + [ + [6893, 6457], + [19, 40], + [61, -1], + [-6, 51], + [-15, 30], + [-4, 46], + [-18, 26], + [31, 62], + [32, -4], + [29, 61], + [18, 60], + [27, 60], + [-1, 42], + [24, 34], + [-23, 29], + [-9, 40], + [-10, 52], + [14, 25], + [42, -14], + [31, 9], + [26, 49] + ], + [ + [4827, 8240], + [5, -42], + [-21, -53], + [-49, -35], + [-40, 9], + [23, 62], + [-15, 60], + [38, 46], + [21, 28] + ], + [ + [6497, 7255], + [25, 12], + [19, 33], + [19, -1], + [12, 11], + [20, -6], + [31, -30], + [22, -6], + [31, -53], + [21, -2], + [3, -49] + ], + [ + [6690, 6820], + [14, -31], + [11, -36], + [27, -26], + [1, -52], + [13, -10], + [2, -27], + [-40, -30], + [-10, -69] + ], + [ + [6708, 6539], + [-53, 18], + [-30, 13], + [-31, 8], + [-12, 73], + [-13, 10], + [-22, -11], + [-28, -28], + [-34, 20], + [-28, 45], + [-27, 17], + [-18, 56], + [-21, 79], + [-15, -10], + [-17, 20], + [-11, -24] + ], + [ + [6348, 6825], + [-15, 32], + [0, 31], + [-9, 0], + [5, 43], + [-15, 45], + [-34, 32], + [-19, 56], + [6, 46], + [14, 21], + [-2, 34], + [-18, 18], + [-18, 70] + ], + [ + [6243, 7253], + [-15, 48], + [5, 18], + [-8, 68], + [19, 17] + ], + [ + [6357, 7321], + [9, -43], + [26, -13], + [20, -29], + [39, -10], + [44, 15], + [2, 14] + ], + [ + [6348, 6825], + [-16, 3] + ], + [ + [6332, 6828], + [-19, 5], + [-20, -56] + ], + [ + [6293, 6777], + [-52, 4], + [-78, 119], + [-41, 41], + [-34, 16] + ], + [ + [6088, 6957], + [-11, 72] + ], + [ + [6077, 7029], + [61, 62], + [11, 71], + [-3, 43], + [16, 15], + [14, 37] + ], + [ + [6176, 7257], + [12, 9], + [32, -8], + [10, -15], + [13, 10] + ], + [ + [4597, 8984], + [-7, -39], + [31, -40], + [-36, -45], + [-80, -41], + [-24, -10], + [-36, 8], + [-78, 19], + [28, 26], + [-61, 29], + [49, 12], + [-1, 17], + [-58, 14], + [19, 38], + [42, 9], + [43, -40], + [42, 32], + [35, -17], + [45, 32], + [47, -4] + ], + [ + [5992, 6990], + [-5, -19] + ], + [ + [5987, 6971], + [-10, 8], + [-6, -39], + [7, -7], + [-7, -8], + [-1, -15], + [13, 8] + ], + [ + [5983, 6918], + [0, -23], + [-14, -95] + ], + [ + [5951, 6902], + [8, 19], + [-2, 4], + [8, 27], + [5, 45], + [4, 15], + [1, 0] + ], + [ + [5975, 7012], + [9, 0], + [3, 11], + [7, 0] + ], + [ + [5994, 7023], + [1, -24], + [-4, -9], + [1, 0] + ], + [ + [5431, 7316], + [-10, -46], + [4, -19], + [-6, -30], + [-21, 22], + [-14, 7], + [-39, 30], + [4, 30], + [32, -6], + [28, 7], + [22, 5] + ], + [ + [5255, 7492], + [17, -42], + [-4, -78], + [-13, 4], + [-11, -20], + [-10, 16], + [-2, 71], + [-6, 34], + [15, -3], + [14, 18] + ], + [ + [5383, 7805], + [-3, -29], + [7, -25] + ], + [ + [5387, 7751], + [-22, 8], + [-23, -20], + [1, -30], + [-3, -17], + [9, -30], + [26, -29], + [14, -49], + [31, -48], + [22, 0], + [7, -13], + [-8, -11], + [25, -22], + [20, -18], + [24, -30], + [3, -11], + [-5, -22], + [-16, 28], + [-24, 10], + [-12, -39], + [20, -21], + [-3, -31], + [-11, -4], + [-15, -50], + [-12, -5], + [0, 18], + [6, 32], + [6, 12], + [-11, 35], + [-8, 29], + [-12, 8], + [-8, 25], + [-18, 11], + [-12, 24], + [-21, 4], + [-21, 26], + [-26, 39], + [-19, 34], + [-8, 58], + [-14, 7], + [-23, 20], + [-12, -8], + [-16, -28], + [-12, -4] + ], + [ + [2845, 6150], + [19, -5], + [14, -15], + [5, -16], + [-19, -1], + [-9, -10], + [-15, 10], + [-16, 21], + [3, 14], + [12, 4], + [6, -2] + ], + [ + [5992, 6990], + [31, -24], + [54, 63] + ], + [ + [6088, 6957], + [-5, -8], + [-56, -30], + [28, -59], + [-9, -10], + [-5, -20], + [-21, -8], + [-7, -21], + [-12, -19], + [-31, 10] + ], + [ + [5970, 6792], + [-1, 8] + ], + [ + [5983, 6918], + [4, 17], + [0, 36] + ], + [ + [8739, 7075], + [4, -20], + [-16, -36], + [-11, 19], + [-15, -14], + [-7, -34], + [-18, 16], + [0, 28], + [15, 36], + [16, -7], + [12, 25], + [20, -13] + ], + [ + [8915, 7252], + [-10, -47], + [4, -30], + [-14, -42], + [-35, -27], + [-49, -4], + [-40, -67], + [-19, 22], + [-1, 44], + [-48, -13], + [-33, -27], + [-32, -2], + [28, -43], + [-19, -101], + [-18, -24], + [-13, 23], + [7, 53], + [-18, 17], + [-11, 41], + [26, 18], + [15, 37], + [28, 30], + [20, 41], + [55, 17], + [30, -12], + [29, 105], + [19, -28], + [40, 59], + [16, 23], + [18, 72], + [-5, 67], + [11, 37], + [30, 11], + [15, -82], + [-1, -48], + [-25, -59], + [0, -61] + ], + [ + [8997, 7667], + [19, -12], + [20, 25], + [6, -67], + [-41, -16], + [-25, -59], + [-43, 41], + [-15, -65], + [-31, -1], + [-4, 59], + [14, 46], + [29, 3], + [8, 82], + [9, 46], + [32, -62], + [22, -20] + ], + [ + [6970, 7554], + [-15, -10], + [-37, -42], + [-12, -42], + [-11, 0], + [-7, 28], + [-36, 2], + [-5, 48], + [-14, 0], + [2, 60], + [-33, 43], + [-48, -5], + [-32, -8], + [-27, 53], + [-22, 22], + [-43, 43], + [-6, 5], + [-71, -35], + [1, -218] + ], + [ + [6554, 7498], + [-14, -3], + [-20, 46], + [-18, 17], + [-32, -12], + [-12, -20] + ], + [ + [6458, 7526], + [-2, 14], + [7, 25], + [-5, 21], + [-32, 20], + [-13, 53], + [-15, 15], + [-1, 19], + [27, -6], + [1, 44], + [23, 9], + [25, -9], + [5, 58], + [-5, 36], + [-28, -2], + [-24, 14], + [-32, -26], + [-26, -12] + ], + [ + [6363, 7799], + [-14, 9], + [3, 31], + [-18, 39], + [-20, -2], + [-24, 40], + [16, 45], + [-8, 12], + [22, 65], + [29, -34], + [3, 43], + [58, 64], + [43, 2], + [61, -41], + [33, -24], + [30, 25], + [44, 1], + [35, -30], + [8, 17], + [39, -2], + [7, 28], + [-45, 40], + [27, 29], + [-5, 16], + [26, 15], + [-20, 41], + [13, 20], + [104, 21], + [13, 14], + [70, 22], + [25, 24], + [50, -12], + [9, -61], + [29, 14], + [35, -20], + [-2, -32], + [27, 3], + [69, 56], + [-10, -19], + [35, -46], + [62, -150], + [15, 31], + [39, -34], + [39, 16], + [16, -11], + [13, -34], + [20, -12], + [11, -25], + [36, 8], + [15, -36] + ], + [ + [7229, 7559], + [-17, 9], + [-14, 21], + [-42, 6], + [-46, 2], + [-10, -6], + [-39, 24], + [-16, -12], + [-4, -35], + [-46, 21], + [-18, -9], + [-7, -26] + ], + [ + [6155, 4958], + [-20, -24], + [-7, -24], + [-10, -4], + [-4, -42], + [-9, -24], + [-5, -39], + [-12, -20] + ], + [ + [6088, 4781], + [-40, 59], + [-1, 35], + [-101, 120], + [-5, 6] + ], + [ + [5941, 5001], + [0, 63], + [8, 24], + [14, 39], + [10, 43], + [-13, 68], + [-3, 30], + [-13, 41] + ], + [ + [5944, 5309], + [17, 35], + [19, 39] + ], + [ + [6162, 5289], + [-24, -67], + [0, -215], + [17, -49] + ], + [ + [7046, 7387], + [-53, -9], + [-34, 19], + [-30, -4], + [3, 34], + [30, -10], + [10, 18] + ], + [ + [6972, 7435], + [21, -6], + [36, 43], + [-33, 31], + [-20, -15], + [-21, 22], + [24, 39], + [-9, 5] + ], + [ + [7849, 5777], + [-7, 72], + [18, 49], + [36, 11], + [26, -8] + ], + [ + [7922, 5901], + [23, -23], + [12, 40], + [25, -21] + ], + [ + [7982, 5897], + [6, -40], + [-3, -71], + [-47, -45], + [13, -36], + [-30, -4], + [-24, -24] + ], + [ + [7897, 5677], + [-23, 9], + [-11, 30], + [-14, 61] + ], + [ + [8564, 7339], + [24, -70], + [7, -38], + [0, -68], + [-10, -33], + [-25, -11], + [-22, -25], + [-25, -5], + [-3, 32], + [5, 45], + [-13, 61], + [21, 10], + [-19, 51] + ], + [ + [8504, 7288], + [2, 5], + [12, -2], + [11, 27], + [20, 2], + [11, 4], + [4, 15] + ], + [ + [5557, 7574], + [5, 13] + ], + [ + [5562, 7587], + [7, 4], + [4, 20], + [5, 3], + [4, -8], + [5, -4], + [3, -10], + [5, -2], + [5, -11], + [4, 0], + [-3, -14], + [-3, -7], + [1, -5] + ], + [ + [5599, 7553], + [-6, -2], + [-17, -9], + [-1, -12], + [-4, 0] + ], + [ + [6332, 6828], + [6, -26], + [-3, -13], + [9, -45] + ], + [ + [6344, 6744], + [-19, -1], + [-7, 28], + [-25, 6] + ], + [ + [7922, 5901], + [9, 26], + [1, 50], + [-22, 52], + [-2, 58], + [-21, 48], + [-21, 4], + [-6, -20], + [-16, -2], + [-8, 10], + [-30, -35], + [0, 53], + [7, 62], + [-19, 3], + [-2, 36], + [-12, 18] + ], + [ + [7780, 6264], + [6, 21], + [24, 39] + ], + [ + [7837, 6385], + [17, -47], + [12, -54], + [34, 0], + [11, -52], + [-18, -15], + [-8, -21], + [34, -36], + [23, -70], + [17, -52], + [21, -41], + [7, -41], + [-5, -59] + ], + [ + [5975, 7012], + [10, 49], + [14, 41], + [0, 2] + ], + [ + [5999, 7104], + [13, -3], + [4, -23], + [-15, -22], + [-7, -33] + ], + [ + [4785, 5315], + [-7, 0], + [-29, 28], + [-25, 45], + [-24, 32], + [-18, 38] + ], + [ + [4682, 5458], + [6, 19], + [2, 17], + [12, 33], + [13, 27] + ], + [ + [5412, 6408], + [-20, -22], + [-15, 33], + [-44, 25] + ], + [ + [5263, 6848], + [13, 14], + [3, 25], + [-3, 24], + [19, 23], + [8, 19], + [14, 17], + [2, 45] + ], + [ + [5319, 7015], + [32, -20], + [12, 5], + [23, -10], + [37, -26], + [13, -53], + [25, -11], + [39, -25], + [30, -29], + [13, 15], + [13, 27], + [-6, 45], + [9, 29], + [20, 28], + [19, 8], + [37, -12], + [10, -27], + [10, 0], + [9, -10], + [28, -7], + [6, -19] + ], + [ + [5694, 6357], + [0, -118], + [-32, 0], + [0, -25] + ], + [ + [5662, 6214], + [-111, 113], + [-111, 113], + [-28, -32] + ], + [ + [7271, 5502], + [-4, -62], + [-12, -16], + [-24, -14], + [-13, 47], + [-5, 85], + [13, 96], + [19, -33], + [13, -42], + [13, -61] + ], + [ + [5804, 3347], + [10, -18], + [-9, -29], + [-4, -19], + [-16, -9], + [-5, -19], + [-10, -6], + [-21, 46], + [15, 37], + [15, 23], + [13, 12], + [12, -18] + ], + [ + [5631, 8267], + [-2, 15], + [3, 16], + [-13, 10], + [-29, 10] + ], + [ + [5590, 8318], + [-6, 50] + ], + [ + [5584, 8368], + [32, 18], + [47, -4], + [27, 6], + [4, -12], + [15, -4], + [26, -29] + ], + [ + [5652, 8242], + [-7, 19], + [-14, 6] + ], + [ + [5584, 8368], + [1, 44], + [14, 37], + [26, 20], + [22, -44], + [22, 1], + [6, 46] + ], + [ + [5757, 8453], + [14, -14], + [2, -28], + [9, -35] + ], + [ + [4759, 6691], + [-4, 0], + [0, -31], + [-17, -2], + [-9, -14], + [-13, 0], + [-10, 8], + [-23, -6], + [-9, -46], + [-9, -5], + [-13, -74], + [-38, -64], + [-9, -81], + [-12, -27], + [-3, -21], + [-63, -5] + ], + [ + [4527, 6323], + [1, 27], + [11, 17], + [9, 30], + [-2, 20], + [10, 42], + [15, 38], + [9, 9], + [8, 35], + [0, 31], + [10, 37], + [19, 21], + [18, 60], + [0, 1], + [14, 23], + [26, 6], + [22, 41], + [14, 16], + [23, 49], + [-7, 73], + [10, 51], + [4, 31], + [18, 40], + [28, 27], + [21, 25], + [18, 61], + [9, 36], + [20, 0], + [17, -25], + [26, 4], + [29, -13], + [12, -1] + ], + [ + [5739, 7906], + [6, 9], + [19, 6], + [20, -19], + [12, -2], + [12, -16], + [-2, -20], + [11, -9], + [4, -25], + [9, -15], + [-2, -9], + [5, -6], + [-7, -4], + [-16, 1], + [-3, 9], + [-6, -5], + [2, -11], + [-7, -19], + [-5, -20], + [-7, -6] + ], + [ + [5784, 7745], + [-5, 27], + [3, 25], + [-1, 26], + [-16, 35], + [-9, 25], + [-9, 17], + [-8, 6] + ], + [ + [6376, 4321], + [7, -25], + [7, -39], + [4, -71], + [7, -28], + [-2, -28], + [-5, -18], + [-10, 35], + [-5, -18], + [5, -43], + [-2, -25], + [-8, -14], + [-1, -50], + [-11, -69], + [-14, -81], + [-17, -112], + [-11, -82], + [-12, -69], + [-23, -14], + [-24, -25], + [-16, 15], + [-22, 21], + [-8, 31], + [-2, 53], + [-10, 47], + [-2, 42], + [5, 43], + [13, 10], + [0, 20], + [13, 45], + [2, 37], + [-6, 28], + [-5, 38], + [-2, 54], + [9, 33], + [4, 38], + [14, 2], + [15, 12], + [11, 10], + [12, 1], + [16, 34], + [23, 36], + [8, 30], + [-4, 25], + [12, -7], + [15, 41], + [1, 36], + [9, 26], + [10, -25] + ], + [ + [2301, 6586], + [-10, -52], + [-5, -43], + [-2, -79], + [-3, -29], + [5, -32], + [9, -29], + [5, -45], + [19, -44], + [6, -34], + [11, -29], + [29, -16], + [12, -25], + [24, 17], + [21, 6], + [21, 11], + [18, 10], + [17, 24], + [7, 34], + [2, 50], + [5, 17], + [19, 16], + [29, 13], + [25, -2], + [17, 5], + [6, -12], + [-1, -29], + [-15, -35], + [-6, -36], + [5, -10], + [-4, -26], + [-7, -46], + [-7, 15], + [-6, -1] + ], + [ + [2438, 5916], + [-32, 64], + [-14, 19], + [-23, 16], + [-15, -5], + [-22, -22], + [-14, -6], + [-20, 16], + [-21, 11], + [-26, 27], + [-21, 8], + [-31, 28], + [-23, 28], + [-7, 16], + [-16, 3], + [-28, 19], + [-12, 27], + [-30, 34], + [-14, 37], + [-6, 29], + [9, 5], + [-3, 17], + [7, 16], + [0, 20], + [-10, 27], + [-2, 23], + [-9, 30], + [-25, 59], + [-28, 46], + [-13, 37], + [-24, 24], + [-5, 14], + [4, 37], + [-14, 13], + [-17, 29], + [-7, 41], + [-14, 5], + [-17, 31], + [-13, 29], + [-1, 19], + [-15, 44], + [-10, 45], + [1, 23], + [-20, 23], + [-10, -2], + [-15, 16], + [-5, -24], + [5, -28], + [2, -45], + [10, -24], + [21, -41], + [4, -14], + [4, -4], + [4, -20], + [5, 1], + [6, -38], + [8, -15], + [6, -21], + [17, -30], + [10, -55], + [8, -26], + [8, -28], + [1, -31], + [13, -2], + [12, -27], + [10, -26], + [-1, -11], + [-12, -21], + [-5, 0], + [-7, 36], + [-18, 33], + [-20, 29], + [-14, 15], + [1, 43], + [-5, 32], + [-13, 19], + [-19, 26], + [-4, -8], + [-7, 16], + [-17, 14], + [-16, 34], + [2, 5], + [11, -4], + [11, 22], + [1, 27], + [-22, 42], + [-16, 17], + [-10, 36], + [-11, 39], + [-12, 47], + [-12, 54] + ], + [ + [1746, 6980], + [32, 4], + [35, 7], + [-2, -12], + [41, -29], + [64, -41], + [55, 0], + [22, 0], + [0, 24], + [48, 0], + [10, -20], + [15, -19], + [16, -26], + [9, -31], + [7, -32], + [15, -18], + [23, -18], + [17, 47], + [23, 1], + [19, -24], + [14, -40], + [10, -35], + [16, -34], + [6, -41], + [8, -28], + [22, -18], + [20, -13], + [10, 2] + ], + [ + [5599, 7553], + [9, 4], + [13, 1] + ], + [ + [4661, 5921], + [10, 11], + [4, 35], + [9, 1], + [20, -16], + [15, 11], + [11, -4], + [4, 13], + [112, 1], + [6, 42], + [-5, 7], + [-13, 255], + [-14, 255], + [43, 1] + ], + [ + [5118, 6189], + [0, -136], + [-15, -39], + [-2, -37], + [-25, -9], + [-38, -5], + [-10, -21], + [-18, -3] + ], + [ + [4680, 5793], + [1, 18], + [-2, 23], + [-11, 16], + [-5, 34], + [-2, 37] + ], + [ + [7737, 5644], + [-3, 44], + [9, 45], + [-10, 35], + [3, 65], + [-12, 30], + [-9, 71], + [-5, 75], + [-12, 49], + [-18, -30], + [-32, -42], + [-15, 5], + [-17, 14], + [9, 73], + [-6, 56], + [-21, 68], + [3, 21], + [-16, 7], + [-20, 49] + ], + [ + [7780, 6264], + [-16, -14], + [-16, -26], + [-20, -2], + [-12, -64], + [-12, -11], + [14, -52], + [17, -43], + [12, -39], + [-11, -51], + [-9, -11], + [6, -30], + [19, -47], + [3, -33], + [0, -27], + [11, -54], + [-16, -55], + [-13, -61] + ], + [ + [5538, 7532], + [-6, 4], + [-8, 19], + [-12, 12] + ], + [ + [5533, 7629], + [8, -10], + [4, -9], + [9, -6], + [10, -12], + [-2, -5] + ], + [ + [7437, 7970], + [29, 10], + [53, 51], + [42, 28], + [24, -18], + [29, -1], + [19, -28], + [28, -2], + [40, -15], + [27, 41], + [-11, 35], + [28, 61], + [31, -24], + [26, -7], + [32, -15], + [6, -44], + [39, -25], + [26, 11], + [36, 7], + [27, -7], + [28, -29], + [16, -30], + [26, 1], + [35, -10], + [26, 15], + [36, 9], + [41, 42], + [17, -6], + [14, -20], + [33, 5] + ], + [ + [5959, 4377], + [21, 5], + [34, -17], + [7, 8], + [19, 1], + [10, 18], + [17, -1], + [30, 23], + [22, 34] + ], + [ + [6119, 4448], + [5, -26], + [-1, -59], + [3, -52], + [1, -92], + [5, -29], + [-8, -43], + [-11, -41], + [-18, -36], + [-25, -23], + [-31, -28], + [-32, -64], + [-10, -11], + [-20, -42], + [-11, -13], + [-3, -42], + [14, -45], + [5, -35], + [0, -17], + [5, 3], + [-1, -58], + [-4, -28], + [6, -10], + [-4, -25], + [-11, -21], + [-23, -20], + [-34, -32], + [-12, -21], + [3, -25], + [7, -4], + [-3, -31] + ], + [ + [5911, 3478], + [-21, 0] + ], + [ + [5890, 3478], + [-2, 26], + [-4, 27] + ], + [ + [5884, 3531], + [-3, 21], + [5, 66], + [-7, 42], + [-13, 83] + ], + [ + [5866, 3743], + [29, 67], + [7, 43], + [5, 5], + [3, 35], + [-5, 17], + [1, 44], + [6, 41], + [0, 75], + [-15, 19], + [-13, 4], + [-6, 15], + [-13, 12], + [-23, -1], + [-2, 22] + ], + [ + [5840, 4141], + [-2, 42], + [84, 49] + ], + [ + [5922, 4232], + [16, -28], + [8, 5], + [11, -15], + [1, -23], + [-6, -28], + [2, -42], + [19, -36], + [8, 41], + [12, 12], + [-2, 76], + [-12, 43], + [-10, 19], + [-10, -1], + [-7, 77], + [7, 45] + ], + [ + [4661, 5921], + [-18, 41], + [-17, 43], + [-18, 16], + [-13, 17], + [-16, -1], + [-13, -12], + [-14, 5], + [-10, -19] + ], + [ + [4542, 6011], + [-2, 32], + [8, 29], + [3, 55], + [-3, 59], + [-3, 29], + [2, 30], + [-7, 28], + [-14, 25] + ], + [ + [4526, 6298], + [6, 20], + [108, -1], + [-5, 86], + [7, 30], + [26, 5], + [-1, 152], + [91, -4], + [0, 90] + ], + [ + [5922, 4232], + [-15, 15], + [9, 55], + [9, 21], + [-6, 49], + [6, 48], + [5, 16], + [-7, 50], + [-14, 26] + ], + [ + [5909, 4512], + [28, -11], + [5, -16], + [10, -28], + [7, -80] + ], + [ + [7836, 5425], + [7, -5], + [16, -36], + [12, -40], + [2, -39], + [-3, -27], + [2, -21], + [2, -35], + [10, -16], + [11, -52], + [-1, -20], + [-19, -4], + [-27, 44], + [-32, 47], + [-4, 30], + [-16, 39], + [-4, 49], + [-10, 32], + [4, 43], + [-7, 25] + ], + [ + [7779, 5439], + [5, 11], + [23, -26], + [2, -30], + [18, 7], + [9, 24] + ], + [ + [8045, 5176], + [21, -20], + [21, 11], + [6, 50], + [12, 11], + [33, 13], + [20, 47], + [14, 37] + ], + [ + [8206, 5379], + [22, 41], + [14, 47], + [11, 0], + [14, -30], + [1, -26], + [19, -16], + [23, -18], + [-2, -23], + [-19, -3], + [5, -29], + [-20, -20] + ], + [ + [5453, 3369], + [-20, 45], + [-11, 43], + [-6, 58], + [-7, 42], + [-9, 91], + [-1, 71], + [-3, 32], + [-11, 25], + [-15, 48], + [-14, 71], + [-6, 37], + [-23, 58], + [-2, 45] + ], + [ + [5644, 4022], + [23, 14], + [18, -4], + [11, -13], + [0, -5] + ], + [ + [5552, 3594], + [0, -218], + [-25, -30], + [-15, -4], + [-17, 11], + [-13, 4], + [-4, 25], + [-11, 17], + [-14, -30] + ], + [ + [9604, 3812], + [23, -36], + [14, -28], + [-10, -14], + [-16, 16], + [-19, 27], + [-18, 31], + [-19, 42], + [-4, 20], + [12, -1], + [16, -20], + [12, -20], + [9, -17] + ], + [ + [5412, 6408], + [7, -92], + [10, -15], + [1, -19], + [11, -20], + [-6, -25], + [-11, -120], + [-1, -77], + [-35, -56], + [-12, -78], + [11, -22], + [0, -38], + [18, -1], + [-3, -28] + ], + [ + [5393, 5795], + [-5, -1], + [-19, 64], + [-6, 3], + [-22, -33], + [-21, 17], + [-15, 3], + [-8, -8], + [-17, 2], + [-16, -25], + [-14, -2], + [-34, 31], + [-13, -15], + [-14, 1], + [-10, 23], + [-28, 22], + [-30, -7], + [-7, -13], + [-4, -34], + [-8, -24], + [-2, -53] + ], + [ + [5236, 5339], + [-29, -21], + [-11, 3], + [-10, -13], + [-23, 1], + [-15, 37], + [-9, 43], + [-19, 39], + [-21, -1], + [-25, 0] + ], + [ + [2619, 5713], + [-10, 18], + [-13, 24], + [-6, 20], + [-12, 19], + [-13, 26], + [3, 9], + [4, -9], + [2, 5] + ], + [ + [2690, 5943], + [-2, -5], + [-2, -13], + [3, -22], + [-6, -20], + [-3, -24], + [-1, -26], + [1, -15], + [1, -27], + [-4, -6], + [-3, -25], + [2, -15], + [-6, -16], + [2, -16], + [4, -9] + ], + [ + [5092, 8091], + [14, 16], + [24, 87], + [38, 25], + [23, -2] + ], + [ + [5863, 9167], + [-47, -24], + [-22, -5] + ], + [ + [5573, 9140], + [-17, -2], + [-4, -39], + [-53, 9], + [-7, -33], + [-27, 1], + [-18, -42], + [-28, -66], + [-43, -83], + [10, -20], + [-10, -24], + [-27, 1], + [-18, -55], + [2, -79], + [17, -29], + [-9, -70], + [-23, -40], + [-12, -34] + ], + [ + [5306, 8535], + [-19, 36], + [-55, -69], + [-37, -13], + [-38, 30], + [-10, 63], + [-9, 137], + [26, 38], + [73, 49], + [55, 61], + [51, 82], + [66, 115], + [47, 44], + [76, 74], + [61, 26], + [46, -3], + [42, 49], + [51, -3], + [50, 12], + [87, -43], + [-36, -16], + [30, -37] + ], + [ + [5686, 9657], + [-62, -24], + [-49, 13], + [19, 16], + [-16, 19], + [57, 11], + [11, -22], + [40, -13] + ], + [ + [5506, 9766], + [92, -44], + [-70, -23], + [-15, -44], + [-25, -11], + [-13, -49], + [-34, -2], + [-59, 36], + [25, 21], + [-42, 17], + [-54, 50], + [-21, 46], + [75, 21], + [16, -20], + [39, 0], + [11, 21], + [40, 2], + [35, -21] + ], + [ + [5706, 9808], + [55, -21], + [-41, -32], + [-81, -7], + [-82, 10], + [-5, 16], + [-40, 1], + [-30, 27], + [86, 17], + [40, -14], + [28, 17], + [70, -14] + ], + [ + [9805, 2640], + [6, -24], + [20, 24], + [8, -25], + [0, -25], + [-10, -27], + [-18, -44], + [-14, -24], + [10, -28], + [-22, -1], + [-23, -22], + [-8, -39], + [-16, -60], + [-21, -26], + [-14, -17], + [-26, 1], + [-18, 20], + [-30, 4], + [-5, 22], + [15, 43], + [35, 59], + [18, 11], + [20, 22], + [24, 31], + [16, 31], + [13, 44], + [10, 15], + [5, 33], + [19, 27], + [6, -25] + ], + [ + [9849, 2922], + [20, -63], + [1, 41], + [13, -16], + [4, -45], + [22, -19], + [19, -5], + [16, 22], + [14, -6], + [-7, -53], + [-8, -34], + [-22, 1], + [-7, -18], + [3, -25], + [-4, -11], + [-11, -32], + [-14, -41], + [-21, -23], + [-5, 15], + [-12, 9], + [16, 48], + [-9, 33], + [-30, 23], + [1, 22], + [20, 20], + [5, 46], + [-1, 38], + [-12, 40], + [1, 10], + [-13, 25], + [-22, 52], + [-12, 42], + [11, 4], + [15, -33], + [21, -15], + [8, -52] + ], + [ + [6475, 6041], + [-9, 41], + [-22, 98] + ], + [ + [6444, 6180], + [83, 59], + [19, 118], + [-13, 42] + ], + [ + [6566, 6530], + [12, -40], + [16, -22], + [20, -8], + [17, -10], + [12, -34], + [8, -20], + [10, -7], + [0, -13], + [-10, -36], + [-5, -16], + [-12, -19], + [-10, -41], + [-13, 3], + [-5, -14], + [-5, -30], + [4, -39], + [-3, -7], + [-13, 0], + [-17, -22], + [-3, -29], + [-6, -12], + [-18, 0], + [-10, -15], + [0, -24], + [-14, -16], + [-15, 5], + [-19, -19], + [-12, -4] + ], + [ + [6557, 6597], + [8, 20], + [3, -5], + [-2, -25], + [-4, -10] + ], + [ + [6893, 6457], + [-20, 15], + [-9, 43], + [-21, 45], + [-51, -12], + [-45, -1], + [-39, -8] + ], + [ + [2836, 5484], + [-9, 17], + [-6, 32], + [7, 16], + [-7, 4], + [-5, 20], + [-14, 16], + [-12, -4], + [-6, -20], + [-11, -15], + [-6, -2], + [-3, -13], + [13, -32], + [-7, -7], + [-4, -9], + [-13, -3], + [-5, 35], + [-4, -10], + [-9, 4], + [-5, 24], + [-12, 3], + [-7, 7], + [-12, 0], + [-1, -13], + [-3, 9] + ], + [ + [2707, 5623], + [10, -22], + [-1, -12], + [11, -3], + [3, 5], + [8, -14], + [13, 4], + [12, 15], + [17, 12], + [9, 17], + [16, -3], + [-1, -6], + [15, -2], + [12, -10], + [10, -18], + [10, -16] + ], + [ + [3045, 3974], + [-28, 33], + [-2, 25], + [-55, 59], + [-50, 65], + [-22, 36], + [-11, 49], + [4, 17], + [-23, 77], + [-28, 109], + [-26, 118], + [-11, 27], + [-9, 43], + [-21, 39], + [-20, 24], + [9, 26], + [-14, 57], + [9, 41], + [22, 37] + ], + [ + [8510, 5555], + [2, -40], + [2, -33], + [-9, -54], + [-11, 60], + [-13, -30], + [9, -43], + [-8, -28], + [-32, 35], + [-8, 42], + [8, 28], + [-17, 28], + [-9, -24], + [-13, 2], + [-21, -33], + [-4, 17], + [11, 50], + [17, 17], + [15, 22], + [10, -27], + [21, 17], + [5, 26], + [19, 1], + [-1, 46], + [22, -28], + [3, -30], + [2, -21] + ], + [ + [8443, 5665], + [-10, -20], + [-9, -37], + [-8, -17], + [-17, 40], + [5, 16], + [7, 17], + [3, 36], + [16, 4], + [-5, -40], + [21, 57], + [-3, -56] + ], + [ + [8291, 5608], + [-37, -56], + [14, 41], + [20, 37], + [16, 41], + [15, 58], + [5, -48], + [-18, -33], + [-15, -40] + ], + [ + [8385, 5760], + [16, -18], + [18, 0], + [0, -25], + [-13, -25], + [-18, -18], + [-1, 28], + [2, 30], + [-4, 28] + ], + [ + [8485, 5776], + [8, -66], + [-21, 16], + [0, -20], + [7, -37], + [-13, -13], + [-1, 42], + [-9, 3], + [-4, 36], + [16, -5], + [0, 22], + [-17, 45], + [27, -1], + [7, -22] + ], + [ + [8375, 5830], + [-7, -51], + [-12, 29], + [-15, 45], + [24, -2], + [10, -21] + ], + [ + [8369, 6151], + [17, -17], + [9, 15], + [2, -15], + [-4, -24], + [9, -43], + [-7, -49], + [-16, -19], + [-5, -48], + [7, -47], + [14, -7], + [13, 7], + [34, -32], + [-2, -32], + [9, -15], + [-3, -27], + [-22, 29], + [-10, 31], + [-7, -22], + [-18, 36], + [-25, -9], + [-14, 13], + [1, 25], + [9, 15], + [-8, 13], + [-4, -21], + [-14, 34], + [-4, 26], + [-1, 56], + [11, -19], + [3, 92], + [9, 54], + [17, 0] + ], + [ + [9329, 4655], + [-8, -6], + [-12, 22], + [-12, 38], + [-6, 45], + [4, 6], + [3, -18], + [8, -13], + [14, -38], + [13, -20], + [-4, -16] + ], + [ + [9221, 4734], + [-15, -5], + [-4, -17], + [-15, -14], + [-15, -14], + [-14, 0], + [-23, 18], + [-16, 16], + [2, 18], + [25, -8], + [15, 4], + [5, 29], + [4, 1], + [2, -31], + [16, 4], + [8, 20], + [16, 21], + [-4, 35], + [17, 1], + [6, -9], + [-1, -33], + [-9, -36] + ], + [ + [8916, 4904], + [48, -41], + [51, -34], + [19, -30], + [16, -30], + [4, -34], + [46, -37], + [7, -31], + [-25, -7], + [6, -39], + [25, -39], + [18, -62], + [15, 2], + [-1, -27], + [22, -10], + [-9, -11], + [30, -25], + [-3, -17], + [-18, -4], + [-7, 16], + [-24, 6], + [-28, 9], + [-22, 38], + [-16, 32], + [-14, 52], + [-36, 26], + [-24, -17], + [-17, -20], + [4, -43], + [-22, -20], + [-16, 9], + [-28, 3] + ], + [ + [9253, 4792], + [-9, -16], + [-5, 35], + [-6, 23], + [-13, 19], + [-16, 25], + [-20, 18], + [8, 14], + [15, -17], + [9, -13], + [12, -14], + [11, -25], + [11, -19], + [3, -30] + ], + [ + [5392, 8233], + [19, 18], + [43, 27], + [35, 20], + [28, -10], + [2, -14], + [27, -1] + ], + [ + [5546, 8273], + [34, -7], + [51, 1] + ], + [ + [5653, 8105], + [14, -52], + [-3, -17], + [-14, -6], + [-25, -50], + [7, -26], + [-6, 3] + ], + [ + [5626, 7957], + [-26, 23], + [-20, -8], + [-13, 6], + [-17, -13], + [-14, 21], + [-11, -8], + [-2, 4] + ], + [ + [3159, 6151], + [14, -5], + [5, -12], + [-7, -15], + [-21, 1], + [-17, -2], + [-1, 25], + [4, 9], + [23, -1] + ], + [ + [8628, 7562], + [4, -10] + ], + [ + [8632, 7552], + [-11, 3], + [-12, -20], + [-8, -20], + [1, -42], + [-14, -13], + [-5, -11], + [-11, -17], + [-18, -10], + [-12, -16], + [-1, -25], + [-3, -7], + [11, -9], + [15, -26] + ], + [ + [8504, 7288], + [-13, 11], + [-4, -11], + [-8, -5], + [-1, 11], + [-7, 5], + [-8, 10], + [8, 26], + [7, 7], + [-3, 11], + [7, 31], + [-2, 10], + [-16, 7], + [-13, 15] + ], + [ + [4792, 7249], + [-11, -15], + [-14, 8], + [-15, -6], + [5, 46], + [-3, 36], + [-12, 6], + [-7, 22], + [2, 39], + [11, 21], + [2, 24], + [6, 36], + [-1, 25], + [-5, 21], + [-1, 20] + ], + [ + [6411, 6520], + [-2, 43], + [7, 31], + [8, 6], + [8, -18], + [1, -35], + [-6, -35] + ], + [ + [6427, 6512], + [-8, -4], + [-8, 12] + ], + [ + [5630, 7886], + [12, 13], + [17, -7], + [18, 0], + [13, -14], + [10, 9], + [20, 5], + [7, 14], + [12, 0] + ], + [ + [5784, 7745], + [12, -11], + [13, 9], + [13, -10] + ], + [ + [5822, 7733], + [0, -15], + [-13, -13], + [-9, 6], + [-7, -71] + ], + [ + [5629, 7671], + [-5, 10], + [6, 10], + [-7, 7], + [-8, -13], + [-17, 17], + [-2, 25], + [-17, 14], + [-3, 18], + [-15, 24] + ], + [ + [8989, 8056], + [28, -105], + [-41, 19], + [-17, -85], + [27, -61], + [-1, -41], + [-21, 36], + [-18, -46], + [-5, 50], + [3, 57], + [-3, 64], + [6, 45], + [2, 79], + [-17, 58], + [3, 80], + [25, 28], + [-11, 27], + [13, 8], + [7, -39], + [10, -57], + [-1, -58], + [11, -59] + ], + [ + [5546, 8273], + [6, 26], + [38, 19] + ], + [ + [0, 9132], + [68, -45], + [73, -59], + [-3, -37], + [19, -15], + [-6, 43], + [75, -8], + [55, -56], + [-28, -26], + [-46, -6], + [0, -57], + [-11, -13], + [-26, 2], + [-22, 21], + [-36, 17], + [-7, 26], + [-28, 9], + [-31, -7], + [-16, 20], + [6, 22], + [-33, -14], + [13, -28], + [-16, -25] + ], + [ + [0, 8896], + [0, 236] + ], + [ + [0, 9282], + [9999, -40], + [-30, -3], + [-5, 19], + [-9964, 24] + ], + [ + [0, 9282], + [4, 3], + [23, 0], + [40, -17], + [-2, -8], + [-29, -14], + [-36, -4], + [0, 40] + ], + [ + [8988, 9383], + [-42, -1], + [-57, 7], + [-5, 3], + [27, 23], + [34, 6], + [40, -23], + [3, -15] + ], + [ + [9186, 9493], + [-32, -23], + [-44, 5], + [-52, 23], + [7, 20], + [51, -9], + [70, -16] + ], + [ + [9029, 9522], + [-22, -44], + [-102, 1], + [-46, -14], + [-55, 39], + [15, 40], + [37, 11], + [73, -2], + [100, -31] + ], + [ + [6598, 9235], + [-17, -5], + [-91, 8], + [-7, 26], + [-50, 16], + [-4, 32], + [28, 13], + [-1, 32], + [55, 50], + [-25, 7], + [66, 52], + [-7, 27], + [62, 31], + [91, 38], + [93, 11], + [48, 22], + [54, 8], + [19, -23], + [-19, -19], + [-98, -29], + [-85, -28], + [-86, -57], + [-42, -57], + [-43, -57], + [5, -49], + [54, -49] + ], + [ + [0, 8896], + [9963, -26], + [-36, 4], + [25, -31], + [17, -49], + [13, -16], + [3, -24], + [-7, -16], + [-52, 13], + [-78, -44], + [-25, -7], + [-42, -42], + [-40, -36], + [-11, -27], + [-39, 41], + [-73, -46], + [-12, 22], + [-27, -26], + [-37, 8], + [-9, -38], + [-33, -58], + [1, -24], + [31, -13], + [-4, -86], + [-25, -2], + [-12, -49], + [11, -26], + [-48, -30], + [-10, -67], + [-41, -15], + [-9, -60], + [-40, -55], + [-10, 41], + [-12, 86], + [-15, 131], + [13, 82], + [23, 35], + [2, 28], + [43, 13], + [50, 75], + [47, 60], + [50, 48], + [23, 83], + [-34, -5], + [-17, -49], + [-70, -65], + [-23, 73], + [-72, -20], + [-69, -99], + [23, -36], + [-62, -16], + [-43, -6], + [2, 43], + [-43, 9], + [-35, -29], + [-85, 10], + [-91, -18], + [-90, -115], + [-106, -139], + [43, -8], + [14, -37], + [27, -13], + [18, 30], + [30, -4], + [40, -65], + [1, -50], + [-21, -59], + [-3, -71], + [-12, -94], + [-42, -86], + [-9, -41], + [-38, -69], + [-38, -68], + [-18, -35], + [-37, -34], + [-17, -1], + [-17, 29], + [-38, -44], + [-4, -19] + ], + [ + [6363, 7799], + [-12, -35], + [-27, -10], + [-28, -61], + [25, -56], + [-2, -40], + [30, -70] + ], + [ + [6109, 7624], + [-35, 49], + [-32, 23], + [-24, 34], + [20, 10], + [23, 49], + [-15, 24], + [41, 24], + [-1, 13], + [-25, -10] + ], + [ + [6061, 7840], + [1, 26], + [14, 17], + [27, 4], + [5, 20], + [-7, 33], + [12, 30], + [-1, 18], + [-41, 19], + [-16, -1], + [-17, 28], + [-21, -9], + [-35, 20], + [0, 12], + [-10, 26], + [-22, 3], + [-2, 18], + [7, 12], + [-18, 33], + [-29, -5], + [-8, 3], + [-7, -14], + [-11, 3] + ], + [ + [5777, 8571], + [31, 33], + [-29, 28] + ], + [ + [5863, 9167], + [29, 20], + [46, -35], + [76, -14], + [105, -67], + [21, -28], + [2, -40], + [-31, -31], + [-45, -15], + [-124, 44], + [-21, -7], + [45, -43], + [2, -28], + [2, -60], + [36, -18], + [22, -15], + [3, 28], + [-17, 26], + [18, 22], + [67, -37], + [24, 15], + [-19, 43], + [65, 58], + [25, -4], + [26, -20], + [16, 40], + [-23, 35], + [14, 36], + [-21, 36], + [78, -18], + [16, -34], + [-35, -7], + [0, -33], + [22, -20], + [43, 13], + [7, 38], + [58, 28], + [97, 50], + [20, -3], + [-27, -35], + [35, -7], + [19, 21], + [52, 1], + [42, 25], + [31, -36], + [32, 39], + [-29, 35], + [14, 19], + [82, -18], + [39, -18], + [100, -68], + [19, 31], + [-28, 31], + [-1, 13], + [-34, 6], + [10, 28], + [-15, 46], + [-1, 19], + [51, 53], + [18, 54], + [21, 11], + [74, -15], + [5, -33], + [-26, -48], + [17, -19], + [9, -41], + [-6, -81], + [31, -36], + [-12, -40], + [-55, -84], + [32, -8], + [11, 21], + [31, 15], + [7, 29], + [24, 29], + [-16, 33], + [13, 39], + [-31, 5], + [-6, 33], + [22, 59], + [-36, 48], + [50, 40], + [-7, 42], + [14, 2], + [15, -33], + [-11, -57], + [29, -11], + [-12, 43], + [46, 23], + [58, 3], + [51, -34], + [-25, 49], + [-2, 63], + [48, 12], + [67, -2], + [60, 7], + [-23, 31], + [33, 39], + [31, 2], + [54, 29], + [74, 8], + [9, 16], + [73, 6], + [23, -14], + [62, 32], + [51, -1], + [8, 25], + [26, 25], + [66, 25], + [48, -19], + [-38, -15], + [63, -9], + [7, -29], + [25, 14], + [82, -1], + [62, -29], + [23, -22], + [-7, -30], + [-31, -18], + [-73, -33], + [-21, -17], + [35, -8], + [41, -15], + [25, 11], + [14, -38], + [12, 15], + [44, 10], + [90, -10], + [6, -28], + [116, -9], + [2, 46], + [59, -11], + [44, 1], + [45, -32], + [13, -37], + [-17, -25], + [35, -47], + [44, -24], + [27, 62], + [44, -26], + [48, 16], + [53, -18], + [21, 16], + [45, -8], + [-20, 55], + [37, 25], + [251, -38], + [24, -35], + [72, -45], + [112, 11], + [56, -10], + [23, -24], + [-4, -44], + [35, -16], + [37, 12], + [49, 1], + [52, -11], + [53, 6], + [49, -52], + [34, 19], + [-23, 37], + [13, 27], + [88, -17], + [58, 4], + [80, -29], + [-9960, -25] + ], + [ + [7918, 9684], + [-157, -23], + [51, 77], + [23, 7], + [21, -4], + [70, -33], + [-8, -24] + ], + [ + [6420, 9816], + [-37, -8], + [-25, -4], + [-4, -10], + [-33, -10], + [-30, 14], + [16, 19], + [-62, 2], + [54, 10], + [43, 1], + [5, -16], + [16, 14], + [26, 10], + [42, -13], + [-11, -9] + ], + [ + [7775, 9718], + [-60, -8], + [-78, 17], + [-46, 23], + [-21, 42], + [-38, 12], + [72, 40], + [60, 14], + [54, -30], + [64, -57], + [-7, -53] + ], + [ + [5844, 4990], + [11, -33], + [-1, -35], + [-8, -7] + ], + [ + [5821, 4978], + [7, -6], + [16, 18] + ], + [ + [4526, 6298], + [1, 25] + ], + [ + [6188, 6023], + [-4, 26], + [-8, 17], + [-2, 24], + [-15, 21], + [-15, 50], + [-7, 48], + [-20, 40], + [-12, 10], + [-18, 56], + [-4, 41], + [2, 35], + [-16, 66], + [-13, 23], + [-15, 12], + [-10, 34], + [2, 13], + [-8, 31], + [-8, 13], + [-11, 44], + [-17, 48], + [-14, 40], + [-14, 0], + [5, 33], + [1, 20], + [3, 24] + ], + [ + [6344, 6744], + [11, -51], + [14, -13], + [5, -21], + [18, -25], + [2, -24], + [-3, -20], + [4, -20], + [8, -16], + [4, -20], + [4, -14] + ], + [ + [6427, 6512], + [5, -22] + ], + [ + [6444, 6180], + [-80, -23], + [-26, -26], + [-20, -62], + [-13, -10], + [-7, 20], + [-11, -3], + [-27, 6], + [-5, 5], + [-32, -1], + [-7, -5], + [-12, 15], + [-7, -29], + [3, -25], + [-12, -19] + ], + [ + [5943, 5617], + [-4, 1], + [0, 29], + [-3, 20], + [-14, 24], + [-4, 42], + [4, 44], + [-13, 4], + [-2, -13], + [-17, -3], + [7, -17], + [2, -36], + [-15, -32], + [-14, -43], + [-14, -6], + [-23, 34], + [-11, -12], + [-3, -17], + [-14, -11], + [-1, -12], + [-28, 0], + [-3, 12], + [-20, 2], + [-10, -10], + [-8, 5], + [-14, 34], + [-5, 17], + [-20, -9], + [-8, -27], + [-7, -53], + [-10, -11], + [-8, -6] + ], + [ + [5663, 5567], + [-2, 2] + ], + [ + [5635, 5716], + [0, 14], + [-10, 17], + [-1, 35], + [-5, 23], + [-10, -4], + [3, 22], + [7, 25], + [-3, 24], + [9, 18], + [-6, 14], + [7, 36], + [13, 44], + [24, -4], + [-1, 234] + ], + [ + [6023, 6357], + [9, -58], + [-6, -10], + [4, -61], + [11, -71], + [10, -14], + [15, -22] + ], + [ + [5943, 5624], + [0, -7] + ], + [ + [5943, 5617], + [0, -46] + ], + [ + [5944, 5309], + [-17, -28], + [-20, 1], + [-22, -14], + [-18, 13], + [-11, -16] + ], + [ + [5682, 5544], + [-19, 23] + ], + [ + [4535, 5861], + [-11, 46], + [-14, 21], + [12, 11], + [14, 41], + [6, 31] + ], + [ + [4536, 5789], + [-4, 45] + ], + [ + [9502, 4438], + [8, -20], + [-19, 0], + [-11, 37], + [17, -15], + [5, -2] + ], + [ + [9467, 4474], + [-11, -1], + [-17, 6], + [-5, 9], + [1, 23], + [19, -9], + [9, -12], + [4, -16] + ], + [ + [9490, 4490], + [-4, -11], + [-21, 52], + [-5, 35], + [9, 0], + [10, -47], + [11, -29] + ], + [ + [9440, 4565], + [1, -12], + [-22, 25], + [-15, 21], + [-10, 20], + [4, 6], + [13, -14], + [23, -27], + [6, -19] + ], + [ + [9375, 4623], + [-5, -3], + [-13, 14], + [-11, 24], + [1, 10], + [17, -25], + [11, -20] + ], + [ + [4682, 5458], + [-8, 5], + [-20, 24], + [-14, 31], + [-5, 22], + [-3, 43] + ], + [ + [2561, 5848], + [-3, -14], + [-16, 1], + [-10, 6], + [-12, 12], + [-15, 3], + [-8, 13] + ], + [ + [6198, 5735], + [9, -11], + [5, -25], + [13, -24], + [14, -1], + [26, 16], + [30, 7], + [25, 18], + [13, 4], + [10, 11], + [16, 2] + ], + [ + [6359, 5732], + [0, -1], + [0, -25], + [0, -59], + [0, -31], + [-13, -36], + [-19, -50] + ], + [ + [6359, 5732], + [9, 1], + [13, 9], + [14, 6], + [14, 20], + [10, 0], + [1, -16], + [-3, -35], + [0, -31], + [-6, -21], + [-7, -64], + [-14, -66], + [-17, -75], + [-24, -87], + [-23, -66], + [-33, -81], + [-28, -48], + [-42, -58], + [-25, -45], + [-31, -72], + [-6, -31], + [-6, -14] + ], + [ + [3412, 5410], + [34, -11], + [2, 10], + [23, 4], + [30, -15] + ], + [ + [3489, 5306], + [10, -35], + [-4, -25] + ], + [ + [5626, 7957], + [-8, -15], + [-5, -24] + ], + [ + [5380, 7746], + [7, 5] + ], + [ + [5663, 8957], + [-47, -17], + [-27, -41], + [4, -36], + [-44, -48], + [-54, -50], + [-20, -84], + [20, -41], + [26, -33], + [-25, -67], + [-29, -14], + [-11, -99], + [-15, -55], + [-34, 6], + [-16, -47], + [-32, -3], + [-9, 56], + [-23, 67], + [-21, 84] + ], + [ + [5890, 3478], + [-5, -26], + [-17, -6], + [-16, 32], + [0, 20], + [7, 22], + [3, 17], + [8, 5], + [14, -11] + ], + [ + [5999, 7104], + [-2, 45], + [7, 25] + ], + [ + [6004, 7174], + [7, 13], + [7, 13], + [2, 33], + [9, -12], + [31, 17], + [14, -12], + [23, 1], + [32, 22], + [15, -1], + [32, 9] + ], + [ + [5051, 5420], + [-22, -12] + ], + [ + [7849, 5777], + [-25, 28], + [-24, -2], + [4, 47], + [-24, 0], + [-2, -65], + [-15, -87], + [-10, -52], + [2, -43], + [18, -2], + [12, -53], + [5, -52], + [15, -33], + [17, -7], + [14, -31] + ], + [ + [7779, 5439], + [-11, 23], + [-4, 29], + [-15, 34], + [-14, 28], + [-4, -35], + [-5, 33], + [3, 37], + [8, 56] + ], + [ + [6883, 7252], + [16, 60], + [-6, 44], + [-20, 14], + [7, 26], + [23, -3], + [13, 33], + [9, 38], + [37, 13], + [-6, -27], + [4, -17], + [12, 2] + ], + [ + [6497, 7255], + [-5, 42], + [4, 62], + [-22, 20], + [8, 40], + [-19, 4], + [6, 49], + [26, -14], + [25, 19], + [-20, 35], + [-8, 34], + [-23, -15], + [-3, -43], + [-8, 38] + ], + [ + [6554, 7498], + [31, 1], + [-4, 29], + [24, 21], + [23, 34], + [37, -31], + [3, -47], + [11, -12], + [30, 2], + [9, -10], + [14, -61], + [32, -41], + [18, -28], + [29, -29], + [37, -25], + [-1, -36] + ], + [ + [8471, 4532], + [3, 14], + [24, 13], + [19, 2], + [9, 8], + [10, -8], + [-10, -16], + [-29, -25], + [-23, -17] + ], + [ + [3286, 5693], + [16, 8], + [6, -2], + [-1, -44], + [-23, -7], + [-5, 6], + [8, 16], + [-1, 23] + ], + [ + [5233, 7240], + [31, 24], + [19, -7], + [-1, -30], + [24, 22], + [2, -12], + [-14, -29], + [0, -27], + [9, -15], + [-3, -51], + [-19, -29], + [6, -33], + [14, -1], + [7, -28], + [11, -9] + ], + [ + [6004, 7174], + [-11, 27], + [11, 22], + [-17, -5], + [-23, 13], + [-19, -34], + [-43, -6], + [-22, 31], + [-30, 2], + [-6, -24], + [-20, -7], + [-26, 31], + [-31, -1], + [-16, 59], + [-21, 33], + [14, 46], + [-18, 28], + [31, 56], + [43, 3], + [12, 45], + [53, -8], + [33, 38], + [32, 17], + [46, 1], + [49, -42], + [40, -22], + [32, 9], + [24, -6], + [33, 31] + ], + [ + [5777, 7539], + [3, -23], + [25, -19], + [-5, -14], + [-33, -3], + [-12, -19], + [-23, -31], + [-9, 27], + [0, 12] + ], + [ + [8382, 6499], + [-17, -95], + [-12, -49], + [-14, 50], + [-4, 44], + [17, 58], + [22, 45], + [13, -18], + [-5, -35] + ], + [ + [6088, 4781], + [-12, -73], + [1, -33], + [18, -22], + [1, -15], + [-8, -36], + [2, -18], + [-2, -28], + [10, -37], + [11, -58], + [10, -13] + ], + [ + [5909, 4512], + [-15, 18], + [-18, 10], + [-11, 10], + [-12, 15] + ], + [ + [5844, 4990], + [10, 8], + [31, -1], + [56, 4] + ], + [ + [6061, 7840], + [-22, -5], + [-18, -19], + [-26, -3], + [-24, -22], + [1, -37], + [14, -14], + [28, 4], + [-5, -21], + [-31, -11], + [-37, -34], + [-16, 12], + [6, 28], + [-30, 17], + [5, 12], + [26, 19], + [-8, 14], + [-43, 15], + [-2, 22], + [-25, -8], + [-11, -32], + [-21, -44] + ], + [ + [3517, 3063], + [-12, -38], + [-31, -32], + [-21, 11], + [-15, -6], + [-26, 25], + [-18, -1], + [-17, 32] + ], + [ + [679, 6185], + [-4, -10], + [-7, 8], + [1, 17], + [-4, 21], + [1, 7], + [5, 10], + [-2, 11], + [1, 6], + [3, -1], + [10, -10], + [5, -5], + [5, -8], + [7, -21], + [-1, -3], + [-11, -13], + [-9, -9] + ], + [ + [664, 6277], + [-9, -4], + [-5, 12], + [-3, 5], + [0, 4], + [3, 5], + [9, -6], + [8, -9], + [-3, -7] + ], + [ + [646, 6309], + [-1, -7], + [-15, 2], + [2, 7], + [14, -2] + ], + [ + [621, 6317], + [-2, -3], + [-2, 1], + [-9, 2], + [-4, 13], + [-1, 2], + [7, 8], + [3, -3], + [8, -20] + ], + [ + [574, 6356], + [-4, -6], + [-9, 11], + [1, 4], + [5, 6], + [6, -1], + [1, -14] + ], + [ + [3135, 7724], + [5, -19], + [-30, -29], + [-29, -20], + [-29, -18], + [-15, -35], + [-4, -13], + [-1, -31], + [10, -32], + [11, -1], + [-3, 21], + [8, -13], + [-2, -17], + [-19, -9], + [-13, 1], + [-20, -10], + [-12, -3], + [-17, -3], + [-23, -17], + [41, 11], + [8, -11], + [-39, -18], + [-17, 0], + [0, 7], + [-8, -16], + [8, -3], + [-6, -43], + [-20, -45], + [-2, 15], + [-6, 3], + [-9, 15], + [5, -32], + [7, -10], + [1, -23], + [-9, -23], + [-16, -47], + [-2, 3], + [8, 40], + [-14, 22], + [-3, 49], + [-5, -25], + [5, -38], + [-18, 10], + [19, -19], + [1, -57], + [8, -4], + [3, -20], + [4, -59], + [-17, -44], + [-29, -18], + [-18, -34], + [-14, -4], + [-14, -22], + [-4, -20], + [-31, -38], + [-16, -28], + [-13, -35], + [-4, -42], + [5, -41], + [9, -51], + [13, -41], + [0, -26], + [13, -69], + [-1, -39], + [-1, -23], + [-7, -36], + [-8, -8], + [-14, 7], + [-4, 26], + [-11, 14], + [-15, 51], + [-13, 45], + [-4, 23], + [6, 39], + [-8, 33], + [-22, 49], + [-10, 9], + [-28, -27], + [-5, 3], + [-14, 28], + [-17, 14], + [-32, -7], + [-24, 7], + [-21, -5], + [-12, -9], + [5, -15], + [0, -24], + [5, -12], + [-5, -8], + [-10, 9], + [-11, -11], + [-20, 2], + [-20, 31], + [-25, -8], + [-20, 14], + [-17, -4], + [-24, -14], + [-25, -44], + [-27, -25], + [-16, -28], + [-6, -27], + [0, -41], + [1, -28], + [5, -20] + ], + [ + [1746, 6980], + [-4, 30], + [-18, 34], + [-13, 7], + [-3, 17], + [-16, 3], + [-10, 16], + [-26, 6], + [-7, 9], + [-3, 32], + [-27, 60], + [-23, 82], + [1, 14], + [-13, 19], + [-21, 50], + [-4, 48], + [-15, 32], + [6, 49], + [-1, 51], + [-8, 45], + [10, 56], + [4, 53], + [3, 54], + [-5, 79], + [-9, 51], + [-8, 27], + [4, 12], + [40, -20], + [15, -56], + [7, 15], + [-5, 49], + [-9, 48] + ], + [ + [750, 8432], + [-28, -23], + [-14, 15], + [-4, 28], + [25, 21], + [15, 9], + [18, -4], + [12, -18], + [-24, -28] + ], + [ + [401, 8597], + [-18, -9], + [-18, 11], + [-17, 16], + [28, 10], + [22, -6], + [3, -22] + ], + [ + [230, 8826], + [17, -12], + [17, 6], + [23, -15], + [27, -8], + [-2, -7], + [-21, -12], + [-21, 13], + [-11, 11], + [-24, -4], + [-7, 5], + [2, 23] + ], + [ + [1374, 8295], + [-15, 22], + [-25, 19], + [-8, 52], + [-36, 47], + [-15, 56], + [-26, 4], + [-44, 2], + [-33, 17], + [-57, 61], + [-27, 11], + [-49, 21], + [-38, -5], + [-55, 27], + [-33, 25], + [-30, -12], + [5, -41], + [-15, -4], + [-32, -12], + [-25, -20], + [-30, -13], + [-4, 35], + [12, 58], + [30, 18], + [-8, 15], + [-35, -33], + [-19, -39], + [-40, -42], + [20, -29], + [-26, -42], + [-30, -25], + [-28, -18], + [-7, -26], + [-43, -31], + [-9, -28], + [-32, -25], + [-20, 5], + [-25, -17], + [-29, -20], + [-23, -20], + [-47, -16], + [-5, 9], + [31, 28], + [27, 18], + [29, 33], + [35, 6], + [14, 25], + [38, 35], + [6, 12], + [21, 21], + [5, 44], + [14, 35], + [-32, -18], + [-9, 11], + [-15, -22], + [-18, 30], + [-8, -21], + [-10, 29], + [-28, -23], + [-17, 0], + [-3, 35], + [5, 21], + [-17, 22], + [-37, -12], + [-23, 28], + [-19, 14], + [0, 34], + [-22, 25], + [11, 34], + [23, 33], + [10, 30], + [22, 4], + [19, -9], + [23, 28], + [20, -5], + [21, 19], + [-5, 27], + [-16, 10], + [21, 23], + [-17, -1], + [-30, -13], + [-8, -13], + [-22, 13], + [-39, -6], + [-41, 14], + [-12, 24], + [-35, 34], + [39, 25], + [62, 29], + [23, 0], + [-4, -30], + [59, 2], + [-23, 37], + [-34, 23], + [-20, 29], + [-26, 25], + [-38, 19], + [15, 31], + [49, 2], + [35, 27], + [7, 29], + [28, 28], + [28, 6], + [52, 27], + [26, -4], + [42, 31], + [42, -12], + [21, -27], + [12, 11], + [47, -3], + [-2, -14], + [43, -10], + [28, 6], + [59, -18], + [53, -6], + [21, -8], + [37, 10], + [42, -18], + [31, -8] + ], + [ + [3018, 5753], + [-1, -14], + [-16, -7], + [9, -26], + [0, -31], + [-12, -35], + [10, -47], + [12, 4], + [6, 43], + [-8, 21], + [-2, 45], + [35, 24], + [-4, 27], + [10, 19], + [10, -41], + [19, -1], + [18, -33], + [1, -20], + [25, 0], + [30, 6], + [16, -27], + [21, -7], + [16, 18], + [0, 15], + [34, 4], + [34, 1], + [-24, -18], + [10, -28], + [22, -4], + [21, -29], + [4, -48], + [15, 2], + [11, -14] + ], + [ + [8001, 6331], + [-37, -51], + [-24, -56], + [-6, -41], + [22, -62], + [25, -77], + [26, -37], + [17, -47], + [12, -109], + [-3, -104], + [-24, -39], + [-31, -38], + [-23, -49], + [-35, -55], + [-10, 37], + [8, 40], + [-21, 34] + ], + [ + [9661, 4085], + [-9, -8], + [-9, 26], + [1, 16], + [17, -34] + ], + [ + [9641, 4175], + [4, -47], + [-7, 7], + [-6, -3], + [-4, 16], + [0, 45], + [13, -18] + ], + [ + [6475, 6041], + [-21, -16], + [-5, -26], + [-1, -20], + [-27, -25], + [-45, -28], + [-24, -41], + [-13, -3], + [-8, 3], + [-16, -25], + [-18, -11], + [-23, -3], + [-7, -3], + [-6, -16], + [-8, -4], + [-4, -15], + [-14, 1], + [-9, -8], + [-19, 3], + [-7, 35], + [1, 32], + [-5, 17], + [-5, 44], + [-8, 24], + [5, 3], + [-2, 27], + [3, 12], + [-1, 25] + ], + [ + [5817, 3752], + [11, 0], + [14, -10], + [9, 7], + [15, -6] + ], + [ + [5911, 3478], + [-7, -43], + [-3, -49], + [-7, -27], + [-19, -30], + [-5, -8], + [-12, -30], + [-8, -31], + [-16, -42], + [-31, -61], + [-20, -36], + [-21, -26], + [-29, -23], + [-14, -3], + [-3, -17], + [-17, 9], + [-14, -11], + [-30, 11], + [-17, -7], + [-12, 3], + [-28, -23], + [-24, -10], + [-17, -22], + [-13, -1], + [-11, 21], + [-10, 1], + [-12, 26], + [-1, -8], + [-4, 16], + [0, 34], + [-9, 40], + [9, 11], + [0, 45], + [-19, 55], + [-14, 50], + [0, 1], + [-20, 76] + ], + [ + [5840, 4141], + [-21, -8], + [-15, -23], + [-4, -21], + [-10, -4], + [-24, -49], + [-15, -38], + [-10, -2], + [-9, 7], + [-31, 7] + ] + ], + "transform": { + "scale": [0.036003600360036005, 0.016927109510951093], + "translate": [-180, -85.609038] + } +} +; + Datamap.prototype.usaTopo = '__USA__'; + + /************************************** + Utilities + ***************************************/ + + //convert lat/lng coords to X / Y coords + Datamap.prototype.latLngToXY = function(lat, lng) { + return this.projection([lng, lat]); + }; + + //add <g> layer to root SVG + Datamap.prototype.addLayer = function( className, id, first ) { + var layer; + if ( first ) { + layer = this.svg.insert('g', ':first-child') + } + else { + layer = this.svg.append('g') + } + return layer.attr('id', id || '') + .attr('class', className || ''); + }; + + Datamap.prototype.updateChoropleth = function(data) { + var svg = this.svg; + for ( var subunit in data ) { + if ( data.hasOwnProperty(subunit) ) { + var color; + var subunitData = data[subunit] + if ( ! subunit ) { + continue; + } + else if ( typeof subunitData === "string" ) { + color = subunitData; + } + else if ( typeof subunitData.color === "string" ) { + color = subunitData.color; + } + else { + color = this.options.fills[ subunitData.fillKey ]; + } + //if it's an object, overriding the previous data + if ( subunitData === Object(subunitData) ) { + this.options.data[subunit] = defaults(subunitData, this.options.data[subunit] || {}); + var geo = this.svg.select('.' + subunit).attr('data-info', JSON.stringify(this.options.data[subunit])); + } + svg + .selectAll('.' + subunit) + .transition() + .style('fill', color); + } + } + }; + + Datamap.prototype.updatePopup = function (element, d, options) { + var self = this; + element.on('mousemove', null); + element.on('mousemove', function() { + var position = d3.mouse(self.options.element); + d3.select(self.svg[0][0].parentNode).select('.datamaps-hoverover') + .style('top', ( (position[1] + 30)) + "px") + .html(function() { + var data = JSON.parse(element.attr('data-info')); + //if ( !data ) return ''; + return options.popupTemplate(d, data); + }) + .style('left', ( position[0]) + "px"); + }); + + d3.select(self.svg[0][0].parentNode).select('.datamaps-hoverover').style('display', 'block'); + }; + + Datamap.prototype.addPlugin = function( name, pluginFn ) { + var self = this; + if ( typeof Datamap.prototype[name] === "undefined" ) { + Datamap.prototype[name] = function(data, options, callback, createNewLayer) { + var layer; + if ( typeof createNewLayer === "undefined" ) { + createNewLayer = false; + } + + if ( typeof options === 'function' ) { + callback = options; + options = undefined; + } + + options = defaults(options || {}, self.options[name + 'Config']); + + //add a single layer, reuse the old layer + if ( !createNewLayer && this.options[name + 'Layer'] ) { + layer = this.options[name + 'Layer']; + options = options || this.options[name + 'Options']; + } + else { + layer = this.addLayer(name); + this.options[name + 'Layer'] = layer; + this.options[name + 'Options'] = options; + } + pluginFn.apply(this, [layer, data, options]); + if ( callback ) { + callback(layer); + } + }; + } + }; + + // expose library + if ( typeof define === "function" && define.amd ) { + define( "datamaps", function(require) { d3 = require('d3'); topojson = require('topojson'); return Datamap; } ); + } + else { + window.Datamap = window.Datamaps = Datamap; + } + + if ( window.jQuery ) { + window.jQuery.fn.datamaps = function(options, callback) { + options = options || {}; + options.element = this[0]; + var datamap = new Datamap(options); + if ( typeof callback === "function" ) { + callback(datamap, options); + } + return this; + }; + } +})(); diff --git a/debian/missing-sources/dygraph.js b/debian/missing-sources/dygraph.js new file mode 100644 index 0000000..dff393a --- /dev/null +++ b/debian/missing-sources/dygraph.js @@ -0,0 +1,9464 @@ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Dygraph = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ +// shim for using process in browser +var process = module.exports = {}; + +// cached from whatever global is present so that test runners that stub it +// don't break things. But we need to wrap it in a try catch in case it is +// wrapped in strict mode code which doesn't define any globals. It's inside a +// function because try/catches deoptimize in certain engines. + +var cachedSetTimeout; +var cachedClearTimeout; + +function defaultSetTimout() { + throw new Error('setTimeout has not been defined'); +} +function defaultClearTimeout () { + throw new Error('clearTimeout has not been defined'); +} +(function () { + try { + if (typeof setTimeout === 'function') { + cachedSetTimeout = setTimeout; + } else { + cachedSetTimeout = defaultSetTimout; + } + } catch (e) { + cachedSetTimeout = defaultSetTimout; + } + try { + if (typeof clearTimeout === 'function') { + cachedClearTimeout = clearTimeout; + } else { + cachedClearTimeout = defaultClearTimeout; + } + } catch (e) { + cachedClearTimeout = defaultClearTimeout; + } +} ()) +function runTimeout(fun) { + if (cachedSetTimeout === setTimeout) { + //normal enviroments in sane situations + return setTimeout(fun, 0); + } + // if setTimeout wasn't available but was latter defined + if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { + cachedSetTimeout = setTimeout; + return setTimeout(fun, 0); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedSetTimeout(fun, 0); + } catch(e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedSetTimeout.call(null, fun, 0); + } catch(e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error + return cachedSetTimeout.call(this, fun, 0); + } + } + + +} +function runClearTimeout(marker) { + if (cachedClearTimeout === clearTimeout) { + //normal enviroments in sane situations + return clearTimeout(marker); + } + // if clearTimeout wasn't available but was latter defined + if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { + cachedClearTimeout = clearTimeout; + return clearTimeout(marker); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedClearTimeout(marker); + } catch (e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedClearTimeout.call(null, marker); + } catch (e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. + // Some versions of I.E. have different rules for clearTimeout vs setTimeout + return cachedClearTimeout.call(this, marker); + } + } + + + +} +var queue = []; +var draining = false; +var currentQueue; +var queueIndex = -1; + +function cleanUpNextTick() { + if (!draining || !currentQueue) { + return; + } + draining = false; + if (currentQueue.length) { + queue = currentQueue.concat(queue); + } else { + queueIndex = -1; + } + if (queue.length) { + drainQueue(); + } +} + +function drainQueue() { + if (draining) { + return; + } + var timeout = runTimeout(cleanUpNextTick); + draining = true; + + var len = queue.length; + while(len) { + currentQueue = queue; + queue = []; + while (++queueIndex < len) { + if (currentQueue) { + currentQueue[queueIndex].run(); + } + } + queueIndex = -1; + len = queue.length; + } + currentQueue = null; + draining = false; + runClearTimeout(timeout); +} + +process.nextTick = function (fun) { + var args = new Array(arguments.length - 1); + if (arguments.length > 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + runTimeout(drainQueue); + } +}; + +// v8 likes predictible objects +function Item(fun, array) { + this.fun = fun; + this.array = array; +} +Item.prototype.run = function () { + this.fun.apply(null, this.array); +}; +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; +process.version = ''; // empty string to avoid regexp issues +process.versions = {}; + +function noop() {} + +process.on = noop; +process.addListener = noop; +process.once = noop; +process.off = noop; +process.removeListener = noop; +process.removeAllListeners = noop; +process.emit = noop; +process.prependListener = noop; +process.prependOnceListener = noop; + +process.listeners = function (name) { return [] } + +process.binding = function (name) { + throw new Error('process.binding is not supported'); +}; + +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; +process.umask = function() { return 0; }; + +},{}],2:[function(require,module,exports){ +/** + * @license + * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com) + * MIT-licensed (http://opensource.org/licenses/MIT) + */ + +/** + * @fileoverview DataHandler implementation for the custom bars option. + * @author David Eberlein (david.eberlein@ch.sauter-bc.com) + */ + +/*global Dygraph:false */ +"use strict"; + +Object.defineProperty(exports, '__esModule', { + value: true +}); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +var _bars = require('./bars'); + +var _bars2 = _interopRequireDefault(_bars); + +/** + * @constructor + * @extends Dygraph.DataHandlers.BarsHandler + */ +var CustomBarsHandler = function CustomBarsHandler() {}; + +CustomBarsHandler.prototype = new _bars2['default'](); + +/** @inheritDoc */ +CustomBarsHandler.prototype.extractSeries = function (rawData, i, options) { + // TODO(danvk): pre-allocate series here. + var series = []; + var x, y, point; + var logScale = options.get('logscale'); + for (var j = 0; j < rawData.length; j++) { + x = rawData[j][0]; + point = rawData[j][i]; + if (logScale && point !== null) { + // On the log scale, points less than zero do not exist. + // This will create a gap in the chart. + if (point[0] <= 0 || point[1] <= 0 || point[2] <= 0) { + point = null; + } + } + // Extract to the unified data format. + if (point !== null) { + y = point[1]; + if (y !== null && !isNaN(y)) { + series.push([x, y, [point[0], point[2]]]); + } else { + series.push([x, y, [y, y]]); + } + } else { + series.push([x, null, [null, null]]); + } + } + return series; +}; + +/** @inheritDoc */ +CustomBarsHandler.prototype.rollingAverage = function (originalData, rollPeriod, options) { + rollPeriod = Math.min(rollPeriod, originalData.length); + var rollingData = []; + var y, low, high, mid, count, i, extremes; + + low = 0; + mid = 0; + high = 0; + count = 0; + for (i = 0; i < originalData.length; i++) { + y = originalData[i][1]; + extremes = originalData[i][2]; + rollingData[i] = originalData[i]; + + if (y !== null && !isNaN(y)) { + low += extremes[0]; + mid += y; + high += extremes[1]; + count += 1; + } + if (i - rollPeriod >= 0) { + var prev = originalData[i - rollPeriod]; + if (prev[1] !== null && !isNaN(prev[1])) { + low -= prev[2][0]; + mid -= prev[1]; + high -= prev[2][1]; + count -= 1; + } + } + if (count) { + rollingData[i] = [originalData[i][0], 1.0 * mid / count, [1.0 * low / count, 1.0 * high / count]]; + } else { + rollingData[i] = [originalData[i][0], null, [null, null]]; + } + } + + return rollingData; +}; + +exports['default'] = CustomBarsHandler; +module.exports = exports['default']; + +},{"./bars":5}],3:[function(require,module,exports){ +/** + * @license + * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com) + * MIT-licensed (http://opensource.org/licenses/MIT) + */ + +/** + * @fileoverview DataHandler implementation for the error bars option. + * @author David Eberlein (david.eberlein@ch.sauter-bc.com) + */ + +/*global Dygraph:false */ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + +var _bars = require('./bars'); + +var _bars2 = _interopRequireDefault(_bars); + +/** + * @constructor + * @extends BarsHandler + */ +var ErrorBarsHandler = function ErrorBarsHandler() {}; + +ErrorBarsHandler.prototype = new _bars2["default"](); + +/** @inheritDoc */ +ErrorBarsHandler.prototype.extractSeries = function (rawData, i, options) { + // TODO(danvk): pre-allocate series here. + var series = []; + var x, y, variance, point; + var sigma = options.get("sigma"); + var logScale = options.get('logscale'); + for (var j = 0; j < rawData.length; j++) { + x = rawData[j][0]; + point = rawData[j][i]; + if (logScale && point !== null) { + // On the log scale, points less than zero do not exist. + // This will create a gap in the chart. + if (point[0] <= 0 || point[0] - sigma * point[1] <= 0) { + point = null; + } + } + // Extract to the unified data format. + if (point !== null) { + y = point[0]; + if (y !== null && !isNaN(y)) { + variance = sigma * point[1]; + // preserve original error value in extras for further + // filtering + series.push([x, y, [y - variance, y + variance, point[1]]]); + } else { + series.push([x, y, [y, y, y]]); + } + } else { + series.push([x, null, [null, null, null]]); + } + } + return series; +}; + +/** @inheritDoc */ +ErrorBarsHandler.prototype.rollingAverage = function (originalData, rollPeriod, options) { + rollPeriod = Math.min(rollPeriod, originalData.length); + var rollingData = []; + var sigma = options.get("sigma"); + + var i, j, y, v, sum, num_ok, stddev, variance, value; + + // Calculate the rolling average for the first rollPeriod - 1 points + // where there is not enough data to roll over the full number of points + for (i = 0; i < originalData.length; i++) { + sum = 0; + variance = 0; + num_ok = 0; + for (j = Math.max(0, i - rollPeriod + 1); j < i + 1; j++) { + y = originalData[j][1]; + if (y === null || isNaN(y)) continue; + num_ok++; + sum += y; + variance += Math.pow(originalData[j][2][2], 2); + } + if (num_ok) { + stddev = Math.sqrt(variance) / num_ok; + value = sum / num_ok; + rollingData[i] = [originalData[i][0], value, [value - sigma * stddev, value + sigma * stddev]]; + } else { + // This explicitly preserves NaNs to aid with "independent + // series". + // See testRollingAveragePreservesNaNs. + v = rollPeriod == 1 ? originalData[i][1] : null; + rollingData[i] = [originalData[i][0], v, [v, v]]; + } + } + + return rollingData; +}; + +exports["default"] = ErrorBarsHandler; +module.exports = exports["default"]; + +},{"./bars":5}],4:[function(require,module,exports){ +/** + * @license + * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com) + * MIT-licensed (http://opensource.org/licenses/MIT) + */ + +/** + * @fileoverview DataHandler implementation for the combination + * of error bars and fractions options. + * @author David Eberlein (david.eberlein@ch.sauter-bc.com) + */ + +/*global Dygraph:false */ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + +var _bars = require('./bars'); + +var _bars2 = _interopRequireDefault(_bars); + +/** + * @constructor + * @extends Dygraph.DataHandlers.BarsHandler + */ +var FractionsBarsHandler = function FractionsBarsHandler() {}; + +FractionsBarsHandler.prototype = new _bars2["default"](); + +/** @inheritDoc */ +FractionsBarsHandler.prototype.extractSeries = function (rawData, i, options) { + // TODO(danvk): pre-allocate series here. + var series = []; + var x, y, point, num, den, value, stddev, variance; + var mult = 100.0; + var sigma = options.get("sigma"); + var logScale = options.get('logscale'); + for (var j = 0; j < rawData.length; j++) { + x = rawData[j][0]; + point = rawData[j][i]; + if (logScale && point !== null) { + // On the log scale, points less than zero do not exist. + // This will create a gap in the chart. + if (point[0] <= 0 || point[1] <= 0) { + point = null; + } + } + // Extract to the unified data format. + if (point !== null) { + num = point[0]; + den = point[1]; + if (num !== null && !isNaN(num)) { + value = den ? num / den : 0.0; + stddev = den ? sigma * Math.sqrt(value * (1 - value) / den) : 1.0; + variance = mult * stddev; + y = mult * value; + // preserve original values in extras for further filtering + series.push([x, y, [y - variance, y + variance, num, den]]); + } else { + series.push([x, num, [num, num, num, den]]); + } + } else { + series.push([x, null, [null, null, null, null]]); + } + } + return series; +}; + +/** @inheritDoc */ +FractionsBarsHandler.prototype.rollingAverage = function (originalData, rollPeriod, options) { + rollPeriod = Math.min(rollPeriod, originalData.length); + var rollingData = []; + var sigma = options.get("sigma"); + var wilsonInterval = options.get("wilsonInterval"); + + var low, high, i, stddev; + var num = 0; + var den = 0; // numerator/denominator + var mult = 100.0; + for (i = 0; i < originalData.length; i++) { + num += originalData[i][2][2]; + den += originalData[i][2][3]; + if (i - rollPeriod >= 0) { + num -= originalData[i - rollPeriod][2][2]; + den -= originalData[i - rollPeriod][2][3]; + } + + var date = originalData[i][0]; + var value = den ? num / den : 0.0; + if (wilsonInterval) { + // For more details on this confidence interval, see: + // http://en.wikipedia.org/wiki/Binomial_confidence_interval + if (den) { + var p = value < 0 ? 0 : value, + n = den; + var pm = sigma * Math.sqrt(p * (1 - p) / n + sigma * sigma / (4 * n * n)); + var denom = 1 + sigma * sigma / den; + low = (p + sigma * sigma / (2 * den) - pm) / denom; + high = (p + sigma * sigma / (2 * den) + pm) / denom; + rollingData[i] = [date, p * mult, [low * mult, high * mult]]; + } else { + rollingData[i] = [date, 0, [0, 0]]; + } + } else { + stddev = den ? sigma * Math.sqrt(value * (1 - value) / den) : 1.0; + rollingData[i] = [date, mult * value, [mult * (value - stddev), mult * (value + stddev)]]; + } + } + + return rollingData; +}; + +exports["default"] = FractionsBarsHandler; +module.exports = exports["default"]; + +},{"./bars":5}],5:[function(require,module,exports){ +/** + * @license + * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com) + * MIT-licensed (http://opensource.org/licenses/MIT) + */ + +/** + * @fileoverview DataHandler base implementation for the "bar" + * data formats. This implementation must be extended and the + * extractSeries and rollingAverage must be implemented. + * @author David Eberlein (david.eberlein@ch.sauter-bc.com) + */ + +/*global Dygraph:false */ +/*global DygraphLayout:false */ +"use strict"; + +Object.defineProperty(exports, '__esModule', { + value: true +}); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +var _datahandler = require('./datahandler'); + +var _datahandler2 = _interopRequireDefault(_datahandler); + +var _dygraphLayout = require('../dygraph-layout'); + +var _dygraphLayout2 = _interopRequireDefault(_dygraphLayout); + +/** + * @constructor + * @extends {Dygraph.DataHandler} + */ +var BarsHandler = function BarsHandler() { + _datahandler2['default'].call(this); +}; +BarsHandler.prototype = new _datahandler2['default'](); + +// TODO(danvk): figure out why the jsdoc has to be copy/pasted from superclass. +// (I get closure compiler errors if this isn't here.) +/** + * @override + * @param {!Array.<Array>} rawData The raw data passed into dygraphs where + * rawData[i] = [x,ySeries1,...,ySeriesN]. + * @param {!number} seriesIndex Index of the series to extract. All other + * series should be ignored. + * @param {!DygraphOptions} options Dygraph options. + * @return {Array.<[!number,?number,?]>} The series in the unified data format + * where series[i] = [x,y,{extras}]. + */ +BarsHandler.prototype.extractSeries = function (rawData, seriesIndex, options) { + // Not implemented here must be extended +}; + +/** + * @override + * @param {!Array.<[!number,?number,?]>} series The series in the unified + * data format where series[i] = [x,y,{extras}]. + * @param {!number} rollPeriod The number of points over which to average the data + * @param {!DygraphOptions} options The dygraph options. + * TODO(danvk): be more specific than "Array" here. + * @return {!Array.<[!number,?number,?]>} the rolled series. + */ +BarsHandler.prototype.rollingAverage = function (series, rollPeriod, options) { + // Not implemented here, must be extended. +}; + +/** @inheritDoc */ +BarsHandler.prototype.onPointsCreated_ = function (series, points) { + for (var i = 0; i < series.length; ++i) { + var item = series[i]; + var point = points[i]; + point.y_top = NaN; + point.y_bottom = NaN; + point.yval_minus = _datahandler2['default'].parseFloat(item[2][0]); + point.yval_plus = _datahandler2['default'].parseFloat(item[2][1]); + } +}; + +/** @inheritDoc */ +BarsHandler.prototype.getExtremeYValues = function (series, dateWindow, options) { + var minY = null, + maxY = null, + y; + + var firstIdx = 0; + var lastIdx = series.length - 1; + + for (var j = firstIdx; j <= lastIdx; j++) { + y = series[j][1]; + if (y === null || isNaN(y)) continue; + + var low = series[j][2][0]; + var high = series[j][2][1]; + + if (low > y) low = y; // this can happen with custom bars, + if (high < y) high = y; // e.g. in tests/custom-bars.html + + if (maxY === null || high > maxY) maxY = high; + if (minY === null || low < minY) minY = low; + } + + return [minY, maxY]; +}; + +/** @inheritDoc */ +BarsHandler.prototype.onLineEvaluated = function (points, axis, logscale) { + var point; + for (var j = 0; j < points.length; j++) { + // Copy over the error terms + point = points[j]; + point.y_top = _dygraphLayout2['default'].calcYNormal_(axis, point.yval_minus, logscale); + point.y_bottom = _dygraphLayout2['default'].calcYNormal_(axis, point.yval_plus, logscale); + } +}; + +exports['default'] = BarsHandler; +module.exports = exports['default']; + +},{"../dygraph-layout":13,"./datahandler":6}],6:[function(require,module,exports){ +/** + * @license + * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com) + * MIT-licensed (http://opensource.org/licenses/MIT) + */ + +/** + * @fileoverview This file contains the managment of data handlers + * @author David Eberlein (david.eberlein@ch.sauter-bc.com) + * + * The idea is to define a common, generic data format that works for all data + * structures supported by dygraphs. To make this possible, the DataHandler + * interface is introduced. This makes it possible, that dygraph itself can work + * with the same logic for every data type independent of the actual format and + * the DataHandler takes care of the data format specific jobs. + * DataHandlers are implemented for all data types supported by Dygraphs and + * return Dygraphs compliant formats. + * By default the correct DataHandler is chosen based on the options set. + * Optionally the user may use his own DataHandler (similar to the plugin + * system). + * + * + * The unified data format returend by each handler is defined as so: + * series[n][point] = [x,y,(extras)] + * + * This format contains the common basis that is needed to draw a simple line + * series extended by optional extras for more complex graphing types. It + * contains a primitive x value as first array entry, a primitive y value as + * second array entry and an optional extras object for additional data needed. + * + * x must always be a number. + * y must always be a number, NaN of type number or null. + * extras is optional and must be interpreted by the DataHandler. It may be of + * any type. + * + * In practice this might look something like this: + * default: [x, yVal] + * errorBar / customBar: [x, yVal, [yTopVariance, yBottomVariance] ] + * + */ +/*global Dygraph:false */ +/*global DygraphLayout:false */ + +"use strict"; + +/** + * + * The data handler is responsible for all data specific operations. All of the + * series data it receives and returns is always in the unified data format. + * Initially the unified data is created by the extractSeries method + * @constructor + */ +Object.defineProperty(exports, "__esModule", { + value: true +}); +var DygraphDataHandler = function DygraphDataHandler() {}; + +var handler = DygraphDataHandler; + +/** + * X-value array index constant for unified data samples. + * @const + * @type {number} + */ +handler.X = 0; + +/** + * Y-value array index constant for unified data samples. + * @const + * @type {number} + */ +handler.Y = 1; + +/** + * Extras-value array index constant for unified data samples. + * @const + * @type {number} + */ +handler.EXTRAS = 2; + +/** + * Extracts one series from the raw data (a 2D array) into an array of the + * unified data format. + * This is where undesirable points (i.e. negative values on log scales and + * missing values through which we wish to connect lines) are dropped. + * TODO(danvk): the "missing values" bit above doesn't seem right. + * + * @param {!Array.<Array>} rawData The raw data passed into dygraphs where + * rawData[i] = [x,ySeries1,...,ySeriesN]. + * @param {!number} seriesIndex Index of the series to extract. All other + * series should be ignored. + * @param {!DygraphOptions} options Dygraph options. + * @return {Array.<[!number,?number,?]>} The series in the unified data format + * where series[i] = [x,y,{extras}]. + */ +handler.prototype.extractSeries = function (rawData, seriesIndex, options) {}; + +/** + * Converts a series to a Point array. The resulting point array must be + * returned in increasing order of idx property. + * + * @param {!Array.<[!number,?number,?]>} series The series in the unified + * data format where series[i] = [x,y,{extras}]. + * @param {!string} setName Name of the series. + * @param {!number} boundaryIdStart Index offset of the first point, equal to the + * number of skipped points left of the date window minimum (if any). + * @return {!Array.<Dygraph.PointType>} List of points for this series. + */ +handler.prototype.seriesToPoints = function (series, setName, boundaryIdStart) { + // TODO(bhs): these loops are a hot-spot for high-point-count charts. In + // fact, + // on chrome+linux, they are 6 times more expensive than iterating through + // the + // points and drawing the lines. The brunt of the cost comes from allocating + // the |point| structures. + var points = []; + for (var i = 0; i < series.length; ++i) { + var item = series[i]; + var yraw = item[1]; + var yval = yraw === null ? null : handler.parseFloat(yraw); + var point = { + x: NaN, + y: NaN, + xval: handler.parseFloat(item[0]), + yval: yval, + name: setName, // TODO(danvk): is this really necessary? + idx: i + boundaryIdStart + }; + points.push(point); + } + this.onPointsCreated_(series, points); + return points; +}; + +/** + * Callback called for each series after the series points have been generated + * which will later be used by the plotters to draw the graph. + * Here data may be added to the seriesPoints which is needed by the plotters. + * The indexes of series and points are in sync meaning the original data + * sample for series[i] is points[i]. + * + * @param {!Array.<[!number,?number,?]>} series The series in the unified + * data format where series[i] = [x,y,{extras}]. + * @param {!Array.<Dygraph.PointType>} points The corresponding points passed + * to the plotter. + * @protected + */ +handler.prototype.onPointsCreated_ = function (series, points) {}; + +/** + * Calculates the rolling average of a data set. + * + * @param {!Array.<[!number,?number,?]>} series The series in the unified + * data format where series[i] = [x,y,{extras}]. + * @param {!number} rollPeriod The number of points over which to average the data + * @param {!DygraphOptions} options The dygraph options. + * @return {!Array.<[!number,?number,?]>} the rolled series. + */ +handler.prototype.rollingAverage = function (series, rollPeriod, options) {}; + +/** + * Computes the range of the data series (including confidence intervals). + * + * @param {!Array.<[!number,?number,?]>} series The series in the unified + * data format where series[i] = [x, y, {extras}]. + * @param {!Array.<number>} dateWindow The x-value range to display with + * the format: [min, max]. + * @param {!DygraphOptions} options The dygraph options. + * @return {Array.<number>} The low and high extremes of the series in the + * given window with the format: [low, high]. + */ +handler.prototype.getExtremeYValues = function (series, dateWindow, options) {}; + +/** + * Callback called for each series after the layouting data has been + * calculated before the series is drawn. Here normalized positioning data + * should be calculated for the extras of each point. + * + * @param {!Array.<Dygraph.PointType>} points The points passed to + * the plotter. + * @param {!Object} axis The axis on which the series will be plotted. + * @param {!boolean} logscale Weather or not to use a logscale. + */ +handler.prototype.onLineEvaluated = function (points, axis, logscale) {}; + +/** + * Optimized replacement for parseFloat, which was way too slow when almost + * all values were type number, with few edge cases, none of which were strings. + * @param {?number} val + * @return {number} + * @protected + */ +handler.parseFloat = function (val) { + // parseFloat(null) is NaN + if (val === null) { + return NaN; + } + + // Assume it's a number or NaN. If it's something else, I'll be shocked. + return val; +}; + +exports["default"] = DygraphDataHandler; +module.exports = exports["default"]; + +},{}],7:[function(require,module,exports){ +/** + * @license + * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com) + * MIT-licensed (http://opensource.org/licenses/MIT) + */ + +/** + * @fileoverview DataHandler implementation for the fractions option. + * @author David Eberlein (david.eberlein@ch.sauter-bc.com) + */ + +/*global Dygraph:false */ +"use strict"; + +Object.defineProperty(exports, '__esModule', { + value: true +}); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +var _datahandler = require('./datahandler'); + +var _datahandler2 = _interopRequireDefault(_datahandler); + +var _default = require('./default'); + +var _default2 = _interopRequireDefault(_default); + +/** + * @extends DefaultHandler + * @constructor + */ +var DefaultFractionHandler = function DefaultFractionHandler() {}; + +DefaultFractionHandler.prototype = new _default2['default'](); + +DefaultFractionHandler.prototype.extractSeries = function (rawData, i, options) { + // TODO(danvk): pre-allocate series here. + var series = []; + var x, y, point, num, den, value; + var mult = 100.0; + var logScale = options.get('logscale'); + for (var j = 0; j < rawData.length; j++) { + x = rawData[j][0]; + point = rawData[j][i]; + if (logScale && point !== null) { + // On the log scale, points less than zero do not exist. + // This will create a gap in the chart. + if (point[0] <= 0 || point[1] <= 0) { + point = null; + } + } + // Extract to the unified data format. + if (point !== null) { + num = point[0]; + den = point[1]; + if (num !== null && !isNaN(num)) { + value = den ? num / den : 0.0; + y = mult * value; + // preserve original values in extras for further filtering + series.push([x, y, [num, den]]); + } else { + series.push([x, num, [num, den]]); + } + } else { + series.push([x, null, [null, null]]); + } + } + return series; +}; + +DefaultFractionHandler.prototype.rollingAverage = function (originalData, rollPeriod, options) { + rollPeriod = Math.min(rollPeriod, originalData.length); + var rollingData = []; + + var i; + var num = 0; + var den = 0; // numerator/denominator + var mult = 100.0; + for (i = 0; i < originalData.length; i++) { + num += originalData[i][2][0]; + den += originalData[i][2][1]; + if (i - rollPeriod >= 0) { + num -= originalData[i - rollPeriod][2][0]; + den -= originalData[i - rollPeriod][2][1]; + } + + var date = originalData[i][0]; + var value = den ? num / den : 0.0; + rollingData[i] = [date, mult * value]; + } + + return rollingData; +}; + +exports['default'] = DefaultFractionHandler; +module.exports = exports['default']; + +},{"./datahandler":6,"./default":8}],8:[function(require,module,exports){ +/** + * @license + * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com) + * MIT-licensed (http://opensource.org/licenses/MIT) + */ + +/** + * @fileoverview DataHandler default implementation used for simple line charts. + * @author David Eberlein (david.eberlein@ch.sauter-bc.com) + */ + +/*global Dygraph:false */ +"use strict"; + +Object.defineProperty(exports, '__esModule', { + value: true +}); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +var _datahandler = require('./datahandler'); + +var _datahandler2 = _interopRequireDefault(_datahandler); + +/** + * @constructor + * @extends Dygraph.DataHandler + */ +var DefaultHandler = function DefaultHandler() {}; + +DefaultHandler.prototype = new _datahandler2['default'](); + +/** @inheritDoc */ +DefaultHandler.prototype.extractSeries = function (rawData, i, options) { + // TODO(danvk): pre-allocate series here. + var series = []; + var logScale = options.get('logscale'); + for (var j = 0; j < rawData.length; j++) { + var x = rawData[j][0]; + var point = rawData[j][i]; + if (logScale) { + // On the log scale, points less than zero do not exist. + // This will create a gap in the chart. + if (point <= 0) { + point = null; + } + } + series.push([x, point]); + } + return series; +}; + +/** @inheritDoc */ +DefaultHandler.prototype.rollingAverage = function (originalData, rollPeriod, options) { + rollPeriod = Math.min(rollPeriod, originalData.length); + var rollingData = []; + + var i, j, y, sum, num_ok; + // Calculate the rolling average for the first rollPeriod - 1 points + // where + // there is not enough data to roll over the full number of points + if (rollPeriod == 1) { + return originalData; + } + for (i = 0; i < originalData.length; i++) { + sum = 0; + num_ok = 0; + for (j = Math.max(0, i - rollPeriod + 1); j < i + 1; j++) { + y = originalData[j][1]; + if (y === null || isNaN(y)) continue; + num_ok++; + sum += originalData[j][1]; + } + if (num_ok) { + rollingData[i] = [originalData[i][0], sum / num_ok]; + } else { + rollingData[i] = [originalData[i][0], null]; + } + } + + return rollingData; +}; + +/** @inheritDoc */ +DefaultHandler.prototype.getExtremeYValues = function (series, dateWindow, options) { + var minY = null, + maxY = null, + y; + var firstIdx = 0, + lastIdx = series.length - 1; + + for (var j = firstIdx; j <= lastIdx; j++) { + y = series[j][1]; + if (y === null || isNaN(y)) continue; + if (maxY === null || y > maxY) { + maxY = y; + } + if (minY === null || y < minY) { + minY = y; + } + } + return [minY, maxY]; +}; + +exports['default'] = DefaultHandler; +module.exports = exports['default']; + +},{"./datahandler":6}],9:[function(require,module,exports){ +/** + * @license + * Copyright 2006 Dan Vanderkam (danvdk@gmail.com) + * MIT-licensed (http://opensource.org/licenses/MIT) + */ + +/** + * @fileoverview Based on PlotKit.CanvasRenderer, but modified to meet the + * needs of dygraphs. + * + * In particular, support for: + * - grid overlays + * - error bars + * - dygraphs attribute system + */ + +/** + * The DygraphCanvasRenderer class does the actual rendering of the chart onto + * a canvas. It's based on PlotKit.CanvasRenderer. + * @param {Object} element The canvas to attach to + * @param {Object} elementContext The 2d context of the canvas (injected so it + * can be mocked for testing.) + * @param {Layout} layout The DygraphLayout object for this graph. + * @constructor + */ + +/*global Dygraph:false */ +"use strict"; + +Object.defineProperty(exports, '__esModule', { + value: true +}); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } + +var _dygraphUtils = require('./dygraph-utils'); + +var utils = _interopRequireWildcard(_dygraphUtils); + +var _dygraph = require('./dygraph'); + +var _dygraph2 = _interopRequireDefault(_dygraph); + +/** + * @constructor + * + * This gets called when there are "new points" to chart. This is generally the + * case when the underlying data being charted has changed. It is _not_ called + * in the common case that the user has zoomed or is panning the view. + * + * The chart canvas has already been created by the Dygraph object. The + * renderer simply gets a drawing context. + * + * @param {Dygraph} dygraph The chart to which this renderer belongs. + * @param {HTMLCanvasElement} element The <canvas> DOM element on which to draw. + * @param {CanvasRenderingContext2D} elementContext The drawing context. + * @param {DygraphLayout} layout The chart's DygraphLayout object. + * + * TODO(danvk): remove the elementContext property. + */ +var DygraphCanvasRenderer = function DygraphCanvasRenderer(dygraph, element, elementContext, layout) { + this.dygraph_ = dygraph; + + this.layout = layout; + this.element = element; + this.elementContext = elementContext; + + this.height = dygraph.height_; + this.width = dygraph.width_; + + // --- check whether everything is ok before we return + if (!utils.isCanvasSupported(this.element)) { + throw "Canvas is not supported."; + } + + // internal state + this.area = layout.getPlotArea(); + + // Set up a clipping area for the canvas (and the interaction canvas). + // This ensures that we don't overdraw. + var ctx = this.dygraph_.canvas_ctx_; + ctx.beginPath(); + ctx.rect(this.area.x, this.area.y, this.area.w, this.area.h); + ctx.clip(); + + ctx = this.dygraph_.hidden_ctx_; + ctx.beginPath(); + ctx.rect(this.area.x, this.area.y, this.area.w, this.area.h); + ctx.clip(); +}; + +/** + * Clears out all chart content and DOM elements. + * This is called immediately before render() on every frame, including + * during zooms and pans. + * @private + */ +DygraphCanvasRenderer.prototype.clear = function () { + this.elementContext.clearRect(0, 0, this.width, this.height); +}; + +/** + * This method is responsible for drawing everything on the chart, including + * lines, error bars, fills and axes. + * It is called immediately after clear() on every frame, including during pans + * and zooms. + * @private + */ +DygraphCanvasRenderer.prototype.render = function () { + // attaches point.canvas{x,y} + this._updatePoints(); + + // actually draws the chart. + this._renderLineChart(); +}; + +/** + * Returns a predicate to be used with an iterator, which will + * iterate over points appropriately, depending on whether + * connectSeparatedPoints is true. When it's false, the predicate will + * skip over points with missing yVals. + */ +DygraphCanvasRenderer._getIteratorPredicate = function (connectSeparatedPoints) { + return connectSeparatedPoints ? DygraphCanvasRenderer._predicateThatSkipsEmptyPoints : null; +}; + +DygraphCanvasRenderer._predicateThatSkipsEmptyPoints = function (array, idx) { + return array[idx].yval !== null; +}; + +/** + * Draws a line with the styles passed in and calls all the drawPointCallbacks. + * @param {Object} e The dictionary passed to the plotter function. + * @private + */ +DygraphCanvasRenderer._drawStyledLine = function (e, color, strokeWidth, strokePattern, drawPoints, drawPointCallback, pointSize) { + var g = e.dygraph; + // TODO(konigsberg): Compute attributes outside this method call. + var stepPlot = g.getBooleanOption("stepPlot", e.setName); + + if (!utils.isArrayLike(strokePattern)) { + strokePattern = null; + } + + var drawGapPoints = g.getBooleanOption('drawGapEdgePoints', e.setName); + + var points = e.points; + var setName = e.setName; + var iter = utils.createIterator(points, 0, points.length, DygraphCanvasRenderer._getIteratorPredicate(g.getBooleanOption("connectSeparatedPoints", setName))); + + var stroking = strokePattern && strokePattern.length >= 2; + + var ctx = e.drawingContext; + ctx.save(); + if (stroking) { + if (ctx.setLineDash) ctx.setLineDash(strokePattern); + } + + var pointsOnLine = DygraphCanvasRenderer._drawSeries(e, iter, strokeWidth, pointSize, drawPoints, drawGapPoints, stepPlot, color); + DygraphCanvasRenderer._drawPointsOnLine(e, pointsOnLine, drawPointCallback, color, pointSize); + + if (stroking) { + if (ctx.setLineDash) ctx.setLineDash([]); + } + + ctx.restore(); +}; + +/** + * This does the actual drawing of lines on the canvas, for just one series. + * Returns a list of [canvasx, canvasy] pairs for points for which a + * drawPointCallback should be fired. These include isolated points, or all + * points if drawPoints=true. + * @param {Object} e The dictionary passed to the plotter function. + * @private + */ +DygraphCanvasRenderer._drawSeries = function (e, iter, strokeWidth, pointSize, drawPoints, drawGapPoints, stepPlot, color) { + + var prevCanvasX = null; + var prevCanvasY = null; + var nextCanvasY = null; + var isIsolated; // true if this point is isolated (no line segments) + var point; // the point being processed in the while loop + var pointsOnLine = []; // Array of [canvasx, canvasy] pairs. + var first = true; // the first cycle through the while loop + + var ctx = e.drawingContext; + ctx.beginPath(); + ctx.strokeStyle = color; + ctx.lineWidth = strokeWidth; + + // NOTE: we break the iterator's encapsulation here for about a 25% speedup. + var arr = iter.array_; + var limit = iter.end_; + var predicate = iter.predicate_; + + for (var i = iter.start_; i < limit; i++) { + point = arr[i]; + if (predicate) { + while (i < limit && !predicate(arr, i)) { + i++; + } + if (i == limit) break; + point = arr[i]; + } + + // FIXME: The 'canvasy != canvasy' test here catches NaN values but the test + // doesn't catch Infinity values. Could change this to + // !isFinite(point.canvasy), but I assume it avoids isNaN for performance? + if (point.canvasy === null || point.canvasy != point.canvasy) { + if (stepPlot && prevCanvasX !== null) { + // Draw a horizontal line to the start of the missing data + ctx.moveTo(prevCanvasX, prevCanvasY); + ctx.lineTo(point.canvasx, prevCanvasY); + } + prevCanvasX = prevCanvasY = null; + } else { + isIsolated = false; + if (drawGapPoints || prevCanvasX === null) { + iter.nextIdx_ = i; + iter.next(); + nextCanvasY = iter.hasNext ? iter.peek.canvasy : null; + + var isNextCanvasYNullOrNaN = nextCanvasY === null || nextCanvasY != nextCanvasY; + isIsolated = prevCanvasX === null && isNextCanvasYNullOrNaN; + if (drawGapPoints) { + // Also consider a point to be "isolated" if it's adjacent to a + // null point, excluding the graph edges. + if (!first && prevCanvasX === null || iter.hasNext && isNextCanvasYNullOrNaN) { + isIsolated = true; + } + } + } + + if (prevCanvasX !== null) { + if (strokeWidth) { + if (stepPlot) { + ctx.moveTo(prevCanvasX, prevCanvasY); + ctx.lineTo(point.canvasx, prevCanvasY); + } + + ctx.lineTo(point.canvasx, point.canvasy); + } + } else { + ctx.moveTo(point.canvasx, point.canvasy); + } + if (drawPoints || isIsolated) { + pointsOnLine.push([point.canvasx, point.canvasy, point.idx]); + } + prevCanvasX = point.canvasx; + prevCanvasY = point.canvasy; + } + first = false; + } + ctx.stroke(); + return pointsOnLine; +}; + +/** + * This fires the drawPointCallback functions, which draw dots on the points by + * default. This gets used when the "drawPoints" option is set, or when there + * are isolated points. + * @param {Object} e The dictionary passed to the plotter function. + * @private + */ +DygraphCanvasRenderer._drawPointsOnLine = function (e, pointsOnLine, drawPointCallback, color, pointSize) { + var ctx = e.drawingContext; + for (var idx = 0; idx < pointsOnLine.length; idx++) { + var cb = pointsOnLine[idx]; + ctx.save(); + drawPointCallback.call(e.dygraph, e.dygraph, e.setName, ctx, cb[0], cb[1], color, pointSize, cb[2]); + ctx.restore(); + } +}; + +/** + * Attaches canvas coordinates to the points array. + * @private + */ +DygraphCanvasRenderer.prototype._updatePoints = function () { + // Update Points + // TODO(danvk): here + // + // TODO(bhs): this loop is a hot-spot for high-point-count charts. These + // transformations can be pushed into the canvas via linear transformation + // matrices. + // NOTE(danvk): this is trickier than it sounds at first. The transformation + // needs to be done before the .moveTo() and .lineTo() calls, but must be + // undone before the .stroke() call to ensure that the stroke width is + // unaffected. An alternative is to reduce the stroke width in the + // transformed coordinate space, but you can't specify different values for + // each dimension (as you can with .scale()). The speedup here is ~12%. + var sets = this.layout.points; + for (var i = sets.length; i--;) { + var points = sets[i]; + for (var j = points.length; j--;) { + var point = points[j]; + point.canvasx = this.area.w * point.x + this.area.x; + point.canvasy = this.area.h * point.y + this.area.y; + } + } +}; + +/** + * Add canvas Actually draw the lines chart, including error bars. + * + * This function can only be called if DygraphLayout's points array has been + * updated with canvas{x,y} attributes, i.e. by + * DygraphCanvasRenderer._updatePoints. + * + * @param {string=} opt_seriesName when specified, only that series will + * be drawn. (This is used for expedited redrawing with highlightSeriesOpts) + * @param {CanvasRenderingContext2D} opt_ctx when specified, the drawing + * context. However, lines are typically drawn on the object's + * elementContext. + * @private + */ +DygraphCanvasRenderer.prototype._renderLineChart = function (opt_seriesName, opt_ctx) { + var ctx = opt_ctx || this.elementContext; + var i; + + var sets = this.layout.points; + var setNames = this.layout.setNames; + var setName; + + this.colors = this.dygraph_.colorsMap_; + + // Determine which series have specialized plotters. + var plotter_attr = this.dygraph_.getOption("plotter"); + var plotters = plotter_attr; + if (!utils.isArrayLike(plotters)) { + plotters = [plotters]; + } + + var setPlotters = {}; // series name -> plotter fn. + for (i = 0; i < setNames.length; i++) { + setName = setNames[i]; + var setPlotter = this.dygraph_.getOption("plotter", setName); + if (setPlotter == plotter_attr) continue; // not specialized. + + setPlotters[setName] = setPlotter; + } + + for (i = 0; i < plotters.length; i++) { + var plotter = plotters[i]; + var is_last = i == plotters.length - 1; + + for (var j = 0; j < sets.length; j++) { + setName = setNames[j]; + if (opt_seriesName && setName != opt_seriesName) continue; + + var points = sets[j]; + + // Only throw in the specialized plotters on the last iteration. + var p = plotter; + if (setName in setPlotters) { + if (is_last) { + p = setPlotters[setName]; + } else { + // Don't use the standard plotters in this case. + continue; + } + } + + var color = this.colors[setName]; + var strokeWidth = this.dygraph_.getOption("strokeWidth", setName); + + ctx.save(); + ctx.strokeStyle = color; + ctx.lineWidth = strokeWidth; + p({ + points: points, + setName: setName, + drawingContext: ctx, + color: color, + strokeWidth: strokeWidth, + dygraph: this.dygraph_, + axis: this.dygraph_.axisPropertiesForSeries(setName), + plotArea: this.area, + seriesIndex: j, + seriesCount: sets.length, + singleSeriesName: opt_seriesName, + allSeriesPoints: sets + }); + ctx.restore(); + } + } +}; + +/** + * Standard plotters. These may be used by clients via Dygraph.Plotters. + * See comments there for more details. + */ +DygraphCanvasRenderer._Plotters = { + linePlotter: function linePlotter(e) { + DygraphCanvasRenderer._linePlotter(e); + }, + + fillPlotter: function fillPlotter(e) { + DygraphCanvasRenderer._fillPlotter(e); + }, + + errorPlotter: function errorPlotter(e) { + DygraphCanvasRenderer._errorPlotter(e); + } +}; + +/** + * Plotter which draws the central lines for a series. + * @private + */ +DygraphCanvasRenderer._linePlotter = function (e) { + var g = e.dygraph; + var setName = e.setName; + var strokeWidth = e.strokeWidth; + + // TODO(danvk): Check if there's any performance impact of just calling + // getOption() inside of _drawStyledLine. Passing in so many parameters makes + // this code a bit nasty. + var borderWidth = g.getNumericOption("strokeBorderWidth", setName); + var drawPointCallback = g.getOption("drawPointCallback", setName) || utils.Circles.DEFAULT; + var strokePattern = g.getOption("strokePattern", setName); + var drawPoints = g.getBooleanOption("drawPoints", setName); + var pointSize = g.getNumericOption("pointSize", setName); + + if (borderWidth && strokeWidth) { + DygraphCanvasRenderer._drawStyledLine(e, g.getOption("strokeBorderColor", setName), strokeWidth + 2 * borderWidth, strokePattern, drawPoints, drawPointCallback, pointSize); + } + + DygraphCanvasRenderer._drawStyledLine(e, e.color, strokeWidth, strokePattern, drawPoints, drawPointCallback, pointSize); +}; + +/** + * Draws the shaded error bars/confidence intervals for each series. + * This happens before the center lines are drawn, since the center lines + * need to be drawn on top of the error bars for all series. + * @private + */ +DygraphCanvasRenderer._errorPlotter = function (e) { + var g = e.dygraph; + var setName = e.setName; + var errorBars = g.getBooleanOption("errorBars") || g.getBooleanOption("customBars"); + if (!errorBars) return; + + var fillGraph = g.getBooleanOption("fillGraph", setName); + if (fillGraph) { + console.warn("Can't use fillGraph option with error bars"); + } + + var ctx = e.drawingContext; + var color = e.color; + var fillAlpha = g.getNumericOption('fillAlpha', setName); + var stepPlot = g.getBooleanOption("stepPlot", setName); + var points = e.points; + + var iter = utils.createIterator(points, 0, points.length, DygraphCanvasRenderer._getIteratorPredicate(g.getBooleanOption("connectSeparatedPoints", setName))); + + var newYs; + + // setup graphics context + var prevX = NaN; + var prevY = NaN; + var prevYs = [-1, -1]; + // should be same color as the lines but only 15% opaque. + var rgb = utils.toRGB_(color); + var err_color = 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + fillAlpha + ')'; + ctx.fillStyle = err_color; + ctx.beginPath(); + + var isNullUndefinedOrNaN = function isNullUndefinedOrNaN(x) { + return x === null || x === undefined || isNaN(x); + }; + + while (iter.hasNext) { + var point = iter.next(); + if (!stepPlot && isNullUndefinedOrNaN(point.y) || stepPlot && !isNaN(prevY) && isNullUndefinedOrNaN(prevY)) { + prevX = NaN; + continue; + } + + newYs = [point.y_bottom, point.y_top]; + if (stepPlot) { + prevY = point.y; + } + + // The documentation specifically disallows nulls inside the point arrays, + // but in case it happens we should do something sensible. + if (isNaN(newYs[0])) newYs[0] = point.y; + if (isNaN(newYs[1])) newYs[1] = point.y; + + newYs[0] = e.plotArea.h * newYs[0] + e.plotArea.y; + newYs[1] = e.plotArea.h * newYs[1] + e.plotArea.y; + if (!isNaN(prevX)) { + if (stepPlot) { + ctx.moveTo(prevX, prevYs[0]); + ctx.lineTo(point.canvasx, prevYs[0]); + ctx.lineTo(point.canvasx, prevYs[1]); + } else { + ctx.moveTo(prevX, prevYs[0]); + ctx.lineTo(point.canvasx, newYs[0]); + ctx.lineTo(point.canvasx, newYs[1]); + } + ctx.lineTo(prevX, prevYs[1]); + ctx.closePath(); + } + prevYs = newYs; + prevX = point.canvasx; + } + ctx.fill(); +}; + +/** + * Proxy for CanvasRenderingContext2D which drops moveTo/lineTo calls which are + * superfluous. It accumulates all movements which haven't changed the x-value + * and only applies the two with the most extreme y-values. + * + * Calls to lineTo/moveTo must have non-decreasing x-values. + */ +DygraphCanvasRenderer._fastCanvasProxy = function (context) { + var pendingActions = []; // array of [type, x, y] tuples + var lastRoundedX = null; + var lastFlushedX = null; + + var LINE_TO = 1, + MOVE_TO = 2; + + var actionCount = 0; // number of moveTos and lineTos passed to context. + + // Drop superfluous motions + // Assumes all pendingActions have the same (rounded) x-value. + var compressActions = function compressActions(opt_losslessOnly) { + if (pendingActions.length <= 1) return; + + // Lossless compression: drop inconsequential moveTos. + for (var i = pendingActions.length - 1; i > 0; i--) { + var action = pendingActions[i]; + if (action[0] == MOVE_TO) { + var prevAction = pendingActions[i - 1]; + if (prevAction[1] == action[1] && prevAction[2] == action[2]) { + pendingActions.splice(i, 1); + } + } + } + + // Lossless compression: ... drop consecutive moveTos ... + for (var i = 0; i < pendingActions.length - 1;) /* incremented internally */{ + var action = pendingActions[i]; + if (action[0] == MOVE_TO && pendingActions[i + 1][0] == MOVE_TO) { + pendingActions.splice(i, 1); + } else { + i++; + } + } + + // Lossy compression: ... drop all but the extreme y-values ... + if (pendingActions.length > 2 && !opt_losslessOnly) { + // keep an initial moveTo, but drop all others. + var startIdx = 0; + if (pendingActions[0][0] == MOVE_TO) startIdx++; + var minIdx = null, + maxIdx = null; + for (var i = startIdx; i < pendingActions.length; i++) { + var action = pendingActions[i]; + if (action[0] != LINE_TO) continue; + if (minIdx === null && maxIdx === null) { + minIdx = i; + maxIdx = i; + } else { + var y = action[2]; + if (y < pendingActions[minIdx][2]) { + minIdx = i; + } else if (y > pendingActions[maxIdx][2]) { + maxIdx = i; + } + } + } + var minAction = pendingActions[minIdx], + maxAction = pendingActions[maxIdx]; + pendingActions.splice(startIdx, pendingActions.length - startIdx); + if (minIdx < maxIdx) { + pendingActions.push(minAction); + pendingActions.push(maxAction); + } else if (minIdx > maxIdx) { + pendingActions.push(maxAction); + pendingActions.push(minAction); + } else { + pendingActions.push(minAction); + } + } + }; + + var flushActions = function flushActions(opt_noLossyCompression) { + compressActions(opt_noLossyCompression); + for (var i = 0, len = pendingActions.length; i < len; i++) { + var action = pendingActions[i]; + if (action[0] == LINE_TO) { + context.lineTo(action[1], action[2]); + } else if (action[0] == MOVE_TO) { + context.moveTo(action[1], action[2]); + } + } + if (pendingActions.length) { + lastFlushedX = pendingActions[pendingActions.length - 1][1]; + } + actionCount += pendingActions.length; + pendingActions = []; + }; + + var addAction = function addAction(action, x, y) { + var rx = Math.round(x); + if (lastRoundedX === null || rx != lastRoundedX) { + // if there are large gaps on the x-axis, it's essential to keep the + // first and last point as well. + var hasGapOnLeft = lastRoundedX - lastFlushedX > 1, + hasGapOnRight = rx - lastRoundedX > 1, + hasGap = hasGapOnLeft || hasGapOnRight; + flushActions(hasGap); + lastRoundedX = rx; + } + pendingActions.push([action, x, y]); + }; + + return { + moveTo: function moveTo(x, y) { + addAction(MOVE_TO, x, y); + }, + lineTo: function lineTo(x, y) { + addAction(LINE_TO, x, y); + }, + + // for major operations like stroke/fill, we skip compression to ensure + // that there are no artifacts at the right edge. + stroke: function stroke() { + flushActions(true);context.stroke(); + }, + fill: function fill() { + flushActions(true);context.fill(); + }, + beginPath: function beginPath() { + flushActions(true);context.beginPath(); + }, + closePath: function closePath() { + flushActions(true);context.closePath(); + }, + + _count: function _count() { + return actionCount; + } + }; +}; + +/** + * Draws the shaded regions when "fillGraph" is set. Not to be confused with + * error bars. + * + * For stacked charts, it's more convenient to handle all the series + * simultaneously. So this plotter plots all the points on the first series + * it's asked to draw, then ignores all the other series. + * + * @private + */ +DygraphCanvasRenderer._fillPlotter = function (e) { + // Skip if we're drawing a single series for interactive highlight overlay. + if (e.singleSeriesName) return; + + // We'll handle all the series at once, not one-by-one. + if (e.seriesIndex !== 0) return; + + var g = e.dygraph; + var setNames = g.getLabels().slice(1); // remove x-axis + + // getLabels() includes names for invisible series, which are not included in + // allSeriesPoints. We remove those to make the two match. + // TODO(danvk): provide a simpler way to get this information. + for (var i = setNames.length; i >= 0; i--) { + if (!g.visibility()[i]) setNames.splice(i, 1); + } + + var anySeriesFilled = (function () { + for (var i = 0; i < setNames.length; i++) { + if (g.getBooleanOption("fillGraph", setNames[i])) return true; + } + return false; + })(); + + if (!anySeriesFilled) return; + + var area = e.plotArea; + var sets = e.allSeriesPoints; + var setCount = sets.length; + + var stackedGraph = g.getBooleanOption("stackedGraph"); + var colors = g.getColors(); + + // For stacked graphs, track the baseline for filling. + // + // The filled areas below graph lines are trapezoids with two + // vertical edges. The top edge is the line segment being drawn, and + // the baseline is the bottom edge. Each baseline corresponds to the + // top line segment from the previous stacked line. In the case of + // step plots, the trapezoids are rectangles. + var baseline = {}; + var currBaseline; + var prevStepPlot; // for different line drawing modes (line/step) per series + + // Helper function to trace a line back along the baseline. + var traceBackPath = function traceBackPath(ctx, baselineX, baselineY, pathBack) { + ctx.lineTo(baselineX, baselineY); + if (stackedGraph) { + for (var i = pathBack.length - 1; i >= 0; i--) { + var pt = pathBack[i]; + ctx.lineTo(pt[0], pt[1]); + } + } + }; + + // process sets in reverse order (needed for stacked graphs) + for (var setIdx = setCount - 1; setIdx >= 0; setIdx--) { + var ctx = e.drawingContext; + var setName = setNames[setIdx]; + if (!g.getBooleanOption('fillGraph', setName)) continue; + + var fillAlpha = g.getNumericOption('fillAlpha', setName); + var stepPlot = g.getBooleanOption('stepPlot', setName); + var color = colors[setIdx]; + var axis = g.axisPropertiesForSeries(setName); + var axisY = 1.0 + axis.minyval * axis.yscale; + if (axisY < 0.0) axisY = 0.0;else if (axisY > 1.0) axisY = 1.0; + axisY = area.h * axisY + area.y; + + var points = sets[setIdx]; + var iter = utils.createIterator(points, 0, points.length, DygraphCanvasRenderer._getIteratorPredicate(g.getBooleanOption("connectSeparatedPoints", setName))); + + // setup graphics context + var prevX = NaN; + var prevYs = [-1, -1]; + var newYs; + // should be same color as the lines but only 15% opaque. + var rgb = utils.toRGB_(color); + var err_color = 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + fillAlpha + ')'; + ctx.fillStyle = err_color; + ctx.beginPath(); + var last_x, + is_first = true; + + // If the point density is high enough, dropping segments on their way to + // the canvas justifies the overhead of doing so. + if (points.length > 2 * g.width_ || _dygraph2['default'].FORCE_FAST_PROXY) { + ctx = DygraphCanvasRenderer._fastCanvasProxy(ctx); + } + + // For filled charts, we draw points from left to right, then back along + // the x-axis to complete a shape for filling. + // For stacked plots, this "back path" is a more complex shape. This array + // stores the [x, y] values needed to trace that shape. + var pathBack = []; + + // TODO(danvk): there are a lot of options at play in this loop. + // The logic would be much clearer if some (e.g. stackGraph and + // stepPlot) were split off into separate sub-plotters. + var point; + while (iter.hasNext) { + point = iter.next(); + if (!utils.isOK(point.y) && !stepPlot) { + traceBackPath(ctx, prevX, prevYs[1], pathBack); + pathBack = []; + prevX = NaN; + if (point.y_stacked !== null && !isNaN(point.y_stacked)) { + baseline[point.canvasx] = area.h * point.y_stacked + area.y; + } + continue; + } + if (stackedGraph) { + if (!is_first && last_x == point.xval) { + continue; + } else { + is_first = false; + last_x = point.xval; + } + + currBaseline = baseline[point.canvasx]; + var lastY; + if (currBaseline === undefined) { + lastY = axisY; + } else { + if (prevStepPlot) { + lastY = currBaseline[0]; + } else { + lastY = currBaseline; + } + } + newYs = [point.canvasy, lastY]; + + if (stepPlot) { + // Step plots must keep track of the top and bottom of + // the baseline at each point. + if (prevYs[0] === -1) { + baseline[point.canvasx] = [point.canvasy, axisY]; + } else { + baseline[point.canvasx] = [point.canvasy, prevYs[0]]; + } + } else { + baseline[point.canvasx] = point.canvasy; + } + } else { + if (isNaN(point.canvasy) && stepPlot) { + newYs = [area.y + area.h, axisY]; + } else { + newYs = [point.canvasy, axisY]; + } + } + if (!isNaN(prevX)) { + // Move to top fill point + if (stepPlot) { + ctx.lineTo(point.canvasx, prevYs[0]); + ctx.lineTo(point.canvasx, newYs[0]); + } else { + ctx.lineTo(point.canvasx, newYs[0]); + } + + // Record the baseline for the reverse path. + if (stackedGraph) { + pathBack.push([prevX, prevYs[1]]); + if (prevStepPlot && currBaseline) { + // Draw to the bottom of the baseline + pathBack.push([point.canvasx, currBaseline[1]]); + } else { + pathBack.push([point.canvasx, newYs[1]]); + } + } + } else { + ctx.moveTo(point.canvasx, newYs[1]); + ctx.lineTo(point.canvasx, newYs[0]); + } + prevYs = newYs; + prevX = point.canvasx; + } + prevStepPlot = stepPlot; + if (newYs && point) { + traceBackPath(ctx, point.canvasx, newYs[1], pathBack); + pathBack = []; + } + ctx.fill(); + } +}; + +exports['default'] = DygraphCanvasRenderer; +module.exports = exports['default']; + +},{"./dygraph":18,"./dygraph-utils":17}],10:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } + +var _dygraphTickers = require('./dygraph-tickers'); + +var DygraphTickers = _interopRequireWildcard(_dygraphTickers); + +var _dygraphInteractionModel = require('./dygraph-interaction-model'); + +var _dygraphInteractionModel2 = _interopRequireDefault(_dygraphInteractionModel); + +var _dygraphCanvas = require('./dygraph-canvas'); + +var _dygraphCanvas2 = _interopRequireDefault(_dygraphCanvas); + +var _dygraphUtils = require('./dygraph-utils'); + +var utils = _interopRequireWildcard(_dygraphUtils); + +// Default attribute values. +var DEFAULT_ATTRS = { + highlightCircleSize: 3, + highlightSeriesOpts: null, + highlightSeriesBackgroundAlpha: 0.5, + highlightSeriesBackgroundColor: 'rgb(255, 255, 255)', + + labelsSeparateLines: false, + labelsShowZeroValues: true, + labelsKMB: false, + labelsKMG2: false, + showLabelsOnHighlight: true, + + digitsAfterDecimal: 2, + maxNumberWidth: 6, + sigFigs: null, + + strokeWidth: 1.0, + strokeBorderWidth: 0, + strokeBorderColor: "white", + + axisTickSize: 3, + axisLabelFontSize: 14, + rightGap: 5, + + showRoller: false, + xValueParser: undefined, + + delimiter: ',', + + sigma: 2.0, + errorBars: false, + fractions: false, + wilsonInterval: true, // only relevant if fractions is true + customBars: false, + fillGraph: false, + fillAlpha: 0.15, + connectSeparatedPoints: false, + + stackedGraph: false, + stackedGraphNaNFill: 'all', + hideOverlayOnMouseOut: true, + + legend: 'onmouseover', + stepPlot: false, + xRangePad: 0, + yRangePad: null, + drawAxesAtZero: false, + + // Sizes of the various chart labels. + titleHeight: 28, + xLabelHeight: 18, + yLabelWidth: 18, + + axisLineColor: "black", + axisLineWidth: 0.3, + gridLineWidth: 0.3, + axisLabelWidth: 50, + gridLineColor: "rgb(128,128,128)", + + interactionModel: _dygraphInteractionModel2['default'].defaultModel, + animatedZooms: false, // (for now) + + // Range selector options + showRangeSelector: false, + rangeSelectorHeight: 40, + rangeSelectorPlotStrokeColor: "#808FAB", + rangeSelectorPlotFillGradientColor: "white", + rangeSelectorPlotFillColor: "#A7B1C4", + rangeSelectorBackgroundStrokeColor: "gray", + rangeSelectorBackgroundLineWidth: 1, + rangeSelectorPlotLineWidth: 1.5, + rangeSelectorForegroundStrokeColor: "black", + rangeSelectorForegroundLineWidth: 1, + rangeSelectorAlpha: 0.6, + showInRangeSelector: null, + + // The ordering here ensures that central lines always appear above any + // fill bars/error bars. + plotter: [_dygraphCanvas2['default']._fillPlotter, _dygraphCanvas2['default']._errorPlotter, _dygraphCanvas2['default']._linePlotter], + + plugins: [], + + // per-axis options + axes: { + x: { + pixelsPerLabel: 70, + axisLabelWidth: 60, + axisLabelFormatter: utils.dateAxisLabelFormatter, + valueFormatter: utils.dateValueFormatter, + drawGrid: true, + drawAxis: true, + independentTicks: true, + ticker: DygraphTickers.dateTicker + }, + y: { + axisLabelWidth: 50, + pixelsPerLabel: 30, + valueFormatter: utils.numberValueFormatter, + axisLabelFormatter: utils.numberAxisLabelFormatter, + drawGrid: true, + drawAxis: true, + independentTicks: true, + ticker: DygraphTickers.numericTicks + }, + y2: { + axisLabelWidth: 50, + pixelsPerLabel: 30, + valueFormatter: utils.numberValueFormatter, + axisLabelFormatter: utils.numberAxisLabelFormatter, + drawAxis: true, // only applies when there are two axes of data. + drawGrid: false, + independentTicks: false, + ticker: DygraphTickers.numericTicks + } + } +}; + +exports['default'] = DEFAULT_ATTRS; +module.exports = exports['default']; + +},{"./dygraph-canvas":9,"./dygraph-interaction-model":12,"./dygraph-tickers":16,"./dygraph-utils":17}],11:[function(require,module,exports){ +/** + * @license + * Copyright 2011 Dan Vanderkam (danvdk@gmail.com) + * MIT-licensed (http://opensource.org/licenses/MIT) + */ + +/** + * @fileoverview A wrapper around the Dygraph class which implements the + * interface for a GViz (aka Google Visualization API) visualization. + * It is designed to be a drop-in replacement for Google's AnnotatedTimeline, + * so the documentation at + * http://code.google.com/apis/chart/interactive/docs/gallery/annotatedtimeline.html + * translates over directly. + * + * For a full demo, see: + * - http://dygraphs.com/tests/gviz.html + * - http://dygraphs.com/tests/annotation-gviz.html + */ + +/*global Dygraph:false */ +"use strict"; + +Object.defineProperty(exports, '__esModule', { + value: true +}); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +var _dygraph = require('./dygraph'); + +var _dygraph2 = _interopRequireDefault(_dygraph); + +/** + * A wrapper around Dygraph that implements the gviz API. + * @param {!HTMLDivElement} container The DOM object the visualization should + * live in. + * @constructor + */ +var GVizChart = function GVizChart(container) { + this.container = container; +}; + +/** + * @param {GVizDataTable} data + * @param {Object.<*>} options + */ +GVizChart.prototype.draw = function (data, options) { + // Clear out any existing dygraph. + // TODO(danvk): would it make more sense to simply redraw using the current + // date_graph object? + this.container.innerHTML = ''; + if (typeof this.date_graph != 'undefined') { + this.date_graph.destroy(); + } + + this.date_graph = new _dygraph2['default'](this.container, data, options); +}; + +/** + * Google charts compatible setSelection + * Only row selection is supported, all points in the row will be highlighted + * @param {Array.<{row:number}>} selection_array array of the selected cells + * @public + */ +GVizChart.prototype.setSelection = function (selection_array) { + var row = false; + if (selection_array.length) { + row = selection_array[0].row; + } + this.date_graph.setSelection(row); +}; + +/** + * Google charts compatible getSelection implementation + * @return {Array.<{row:number,column:number}>} array of the selected cells + * @public + */ +GVizChart.prototype.getSelection = function () { + var selection = []; + + var row = this.date_graph.getSelection(); + + if (row < 0) return selection; + + var points = this.date_graph.layout_.points; + for (var setIdx = 0; setIdx < points.length; ++setIdx) { + selection.push({ row: row, column: setIdx + 1 }); + } + + return selection; +}; + +exports['default'] = GVizChart; +module.exports = exports['default']; + +},{"./dygraph":18}],12:[function(require,module,exports){ +/** + * @license + * Copyright 2011 Robert Konigsberg (konigsberg@google.com) + * MIT-licensed (http://opensource.org/licenses/MIT) + */ + +/** + * @fileoverview The default interaction model for Dygraphs. This is kept out + * of dygraph.js for better navigability. + * @author Robert Konigsberg (konigsberg@google.com) + */ + +/*global Dygraph:false */ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj["default"] = obj; return newObj; } } + +var _dygraphUtils = require('./dygraph-utils'); + +var utils = _interopRequireWildcard(_dygraphUtils); + +/** + * You can drag this many pixels past the edge of the chart and still have it + * be considered a zoom. This makes it easier to zoom to the exact edge of the + * chart, a fairly common operation. + */ +var DRAG_EDGE_MARGIN = 100; + +/** + * A collection of functions to facilitate build custom interaction models. + * @class + */ +var DygraphInteraction = {}; + +/** + * Checks whether the beginning & ending of an event were close enough that it + * should be considered a click. If it should, dispatch appropriate events. + * Returns true if the event was treated as a click. + * + * @param {Event} event + * @param {Dygraph} g + * @param {Object} context + */ +DygraphInteraction.maybeTreatMouseOpAsClick = function (event, g, context) { + context.dragEndX = utils.dragGetX_(event, context); + context.dragEndY = utils.dragGetY_(event, context); + var regionWidth = Math.abs(context.dragEndX - context.dragStartX); + var regionHeight = Math.abs(context.dragEndY - context.dragStartY); + + if (regionWidth < 2 && regionHeight < 2 && g.lastx_ !== undefined && g.lastx_ != -1) { + DygraphInteraction.treatMouseOpAsClick(g, event, context); + } + + context.regionWidth = regionWidth; + context.regionHeight = regionHeight; +}; + +/** + * Called in response to an interaction model operation that + * should start the default panning behavior. + * + * It's used in the default callback for "mousedown" operations. + * Custom interaction model builders can use it to provide the default + * panning behavior. + * + * @param {Event} event the event object which led to the startPan call. + * @param {Dygraph} g The dygraph on which to act. + * @param {Object} context The dragging context object (with + * dragStartX/dragStartY/etc. properties). This function modifies the + * context. + */ +DygraphInteraction.startPan = function (event, g, context) { + var i, axis; + context.isPanning = true; + var xRange = g.xAxisRange(); + + if (g.getOptionForAxis("logscale", "x")) { + context.initialLeftmostDate = utils.log10(xRange[0]); + context.dateRange = utils.log10(xRange[1]) - utils.log10(xRange[0]); + } else { + context.initialLeftmostDate = xRange[0]; + context.dateRange = xRange[1] - xRange[0]; + } + context.xUnitsPerPixel = context.dateRange / (g.plotter_.area.w - 1); + + if (g.getNumericOption("panEdgeFraction")) { + var maxXPixelsToDraw = g.width_ * g.getNumericOption("panEdgeFraction"); + var xExtremes = g.xAxisExtremes(); // I REALLY WANT TO CALL THIS xTremes! + + var boundedLeftX = g.toDomXCoord(xExtremes[0]) - maxXPixelsToDraw; + var boundedRightX = g.toDomXCoord(xExtremes[1]) + maxXPixelsToDraw; + + var boundedLeftDate = g.toDataXCoord(boundedLeftX); + var boundedRightDate = g.toDataXCoord(boundedRightX); + context.boundedDates = [boundedLeftDate, boundedRightDate]; + + var boundedValues = []; + var maxYPixelsToDraw = g.height_ * g.getNumericOption("panEdgeFraction"); + + for (i = 0; i < g.axes_.length; i++) { + axis = g.axes_[i]; + var yExtremes = axis.extremeRange; + + var boundedTopY = g.toDomYCoord(yExtremes[0], i) + maxYPixelsToDraw; + var boundedBottomY = g.toDomYCoord(yExtremes[1], i) - maxYPixelsToDraw; + + var boundedTopValue = g.toDataYCoord(boundedTopY, i); + var boundedBottomValue = g.toDataYCoord(boundedBottomY, i); + + boundedValues[i] = [boundedTopValue, boundedBottomValue]; + } + context.boundedValues = boundedValues; + } + + // Record the range of each y-axis at the start of the drag. + // If any axis has a valueRange, then we want a 2D pan. + // We can't store data directly in g.axes_, because it does not belong to us + // and could change out from under us during a pan (say if there's a data + // update). + context.is2DPan = false; + context.axes = []; + for (i = 0; i < g.axes_.length; i++) { + axis = g.axes_[i]; + var axis_data = {}; + var yRange = g.yAxisRange(i); + // TODO(konigsberg): These values should be in |context|. + // In log scale, initialTopValue, dragValueRange and unitsPerPixel are log scale. + var logscale = g.attributes_.getForAxis("logscale", i); + if (logscale) { + axis_data.initialTopValue = utils.log10(yRange[1]); + axis_data.dragValueRange = utils.log10(yRange[1]) - utils.log10(yRange[0]); + } else { + axis_data.initialTopValue = yRange[1]; + axis_data.dragValueRange = yRange[1] - yRange[0]; + } + axis_data.unitsPerPixel = axis_data.dragValueRange / (g.plotter_.area.h - 1); + context.axes.push(axis_data); + + // While calculating axes, set 2dpan. + if (axis.valueRange) context.is2DPan = true; + } +}; + +/** + * Called in response to an interaction model operation that + * responds to an event that pans the view. + * + * It's used in the default callback for "mousemove" operations. + * Custom interaction model builders can use it to provide the default + * panning behavior. + * + * @param {Event} event the event object which led to the movePan call. + * @param {Dygraph} g The dygraph on which to act. + * @param {Object} context The dragging context object (with + * dragStartX/dragStartY/etc. properties). This function modifies the + * context. + */ +DygraphInteraction.movePan = function (event, g, context) { + context.dragEndX = utils.dragGetX_(event, context); + context.dragEndY = utils.dragGetY_(event, context); + + var minDate = context.initialLeftmostDate - (context.dragEndX - context.dragStartX) * context.xUnitsPerPixel; + if (context.boundedDates) { + minDate = Math.max(minDate, context.boundedDates[0]); + } + var maxDate = minDate + context.dateRange; + if (context.boundedDates) { + if (maxDate > context.boundedDates[1]) { + // Adjust minDate, and recompute maxDate. + minDate = minDate - (maxDate - context.boundedDates[1]); + maxDate = minDate + context.dateRange; + } + } + + if (g.getOptionForAxis("logscale", "x")) { + g.dateWindow_ = [Math.pow(utils.LOG_SCALE, minDate), Math.pow(utils.LOG_SCALE, maxDate)]; + } else { + g.dateWindow_ = [minDate, maxDate]; + } + + // y-axis scaling is automatic unless this is a full 2D pan. + if (context.is2DPan) { + + var pixelsDragged = context.dragEndY - context.dragStartY; + + // Adjust each axis appropriately. + for (var i = 0; i < g.axes_.length; i++) { + var axis = g.axes_[i]; + var axis_data = context.axes[i]; + var unitsDragged = pixelsDragged * axis_data.unitsPerPixel; + + var boundedValue = context.boundedValues ? context.boundedValues[i] : null; + + // In log scale, maxValue and minValue are the logs of those values. + var maxValue = axis_data.initialTopValue + unitsDragged; + if (boundedValue) { + maxValue = Math.min(maxValue, boundedValue[1]); + } + var minValue = maxValue - axis_data.dragValueRange; + if (boundedValue) { + if (minValue < boundedValue[0]) { + // Adjust maxValue, and recompute minValue. + maxValue = maxValue - (minValue - boundedValue[0]); + minValue = maxValue - axis_data.dragValueRange; + } + } + if (g.attributes_.getForAxis("logscale", i)) { + axis.valueRange = [Math.pow(utils.LOG_SCALE, minValue), Math.pow(utils.LOG_SCALE, maxValue)]; + } else { + axis.valueRange = [minValue, maxValue]; + } + } + } + + g.drawGraph_(false); +}; + +/** + * Called in response to an interaction model operation that + * responds to an event that ends panning. + * + * It's used in the default callback for "mouseup" operations. + * Custom interaction model builders can use it to provide the default + * panning behavior. + * + * @param {Event} event the event object which led to the endPan call. + * @param {Dygraph} g The dygraph on which to act. + * @param {Object} context The dragging context object (with + * dragStartX/dragStartY/etc. properties). This function modifies the + * context. + */ +DygraphInteraction.endPan = DygraphInteraction.maybeTreatMouseOpAsClick; + +/** + * Called in response to an interaction model operation that + * responds to an event that starts zooming. + * + * It's used in the default callback for "mousedown" operations. + * Custom interaction model builders can use it to provide the default + * zooming behavior. + * + * @param {Event} event the event object which led to the startZoom call. + * @param {Dygraph} g The dygraph on which to act. + * @param {Object} context The dragging context object (with + * dragStartX/dragStartY/etc. properties). This function modifies the + * context. + */ +DygraphInteraction.startZoom = function (event, g, context) { + context.isZooming = true; + context.zoomMoved = false; +}; + +/** + * Called in response to an interaction model operation that + * responds to an event that defines zoom boundaries. + * + * It's used in the default callback for "mousemove" operations. + * Custom interaction model builders can use it to provide the default + * zooming behavior. + * + * @param {Event} event the event object which led to the moveZoom call. + * @param {Dygraph} g The dygraph on which to act. + * @param {Object} context The dragging context object (with + * dragStartX/dragStartY/etc. properties). This function modifies the + * context. + */ +DygraphInteraction.moveZoom = function (event, g, context) { + context.zoomMoved = true; + context.dragEndX = utils.dragGetX_(event, context); + context.dragEndY = utils.dragGetY_(event, context); + + var xDelta = Math.abs(context.dragStartX - context.dragEndX); + var yDelta = Math.abs(context.dragStartY - context.dragEndY); + + // drag direction threshold for y axis is twice as large as x axis + context.dragDirection = xDelta < yDelta / 2 ? utils.VERTICAL : utils.HORIZONTAL; + + g.drawZoomRect_(context.dragDirection, context.dragStartX, context.dragEndX, context.dragStartY, context.dragEndY, context.prevDragDirection, context.prevEndX, context.prevEndY); + + context.prevEndX = context.dragEndX; + context.prevEndY = context.dragEndY; + context.prevDragDirection = context.dragDirection; +}; + +/** + * TODO(danvk): move this logic into dygraph.js + * @param {Dygraph} g + * @param {Event} event + * @param {Object} context + */ +DygraphInteraction.treatMouseOpAsClick = function (g, event, context) { + var clickCallback = g.getFunctionOption('clickCallback'); + var pointClickCallback = g.getFunctionOption('pointClickCallback'); + + var selectedPoint = null; + + // Find out if the click occurs on a point. + var closestIdx = -1; + var closestDistance = Number.MAX_VALUE; + + // check if the click was on a particular point. + for (var i = 0; i < g.selPoints_.length; i++) { + var p = g.selPoints_[i]; + var distance = Math.pow(p.canvasx - context.dragEndX, 2) + Math.pow(p.canvasy - context.dragEndY, 2); + if (!isNaN(distance) && (closestIdx == -1 || distance < closestDistance)) { + closestDistance = distance; + closestIdx = i; + } + } + + // Allow any click within two pixels of the dot. + var radius = g.getNumericOption('highlightCircleSize') + 2; + if (closestDistance <= radius * radius) { + selectedPoint = g.selPoints_[closestIdx]; + } + + if (selectedPoint) { + var e = { + cancelable: true, + point: selectedPoint, + canvasx: context.dragEndX, + canvasy: context.dragEndY + }; + var defaultPrevented = g.cascadeEvents_('pointClick', e); + if (defaultPrevented) { + // Note: this also prevents click / clickCallback from firing. + return; + } + if (pointClickCallback) { + pointClickCallback.call(g, event, selectedPoint); + } + } + + var e = { + cancelable: true, + xval: g.lastx_, // closest point by x value + pts: g.selPoints_, + canvasx: context.dragEndX, + canvasy: context.dragEndY + }; + if (!g.cascadeEvents_('click', e)) { + if (clickCallback) { + // TODO(danvk): pass along more info about the points, e.g. 'x' + clickCallback.call(g, event, g.lastx_, g.selPoints_); + } + } +}; + +/** + * Called in response to an interaction model operation that + * responds to an event that performs a zoom based on previously defined + * bounds.. + * + * It's used in the default callback for "mouseup" operations. + * Custom interaction model builders can use it to provide the default + * zooming behavior. + * + * @param {Event} event the event object which led to the endZoom call. + * @param {Dygraph} g The dygraph on which to end the zoom. + * @param {Object} context The dragging context object (with + * dragStartX/dragStartY/etc. properties). This function modifies the + * context. + */ +DygraphInteraction.endZoom = function (event, g, context) { + g.clearZoomRect_(); + context.isZooming = false; + DygraphInteraction.maybeTreatMouseOpAsClick(event, g, context); + + // The zoom rectangle is visibly clipped to the plot area, so its behavior + // should be as well. + // See http://code.google.com/p/dygraphs/issues/detail?id=280 + var plotArea = g.getArea(); + if (context.regionWidth >= 10 && context.dragDirection == utils.HORIZONTAL) { + var left = Math.min(context.dragStartX, context.dragEndX), + right = Math.max(context.dragStartX, context.dragEndX); + left = Math.max(left, plotArea.x); + right = Math.min(right, plotArea.x + plotArea.w); + if (left < right) { + g.doZoomX_(left, right); + } + context.cancelNextDblclick = true; + } else if (context.regionHeight >= 10 && context.dragDirection == utils.VERTICAL) { + var top = Math.min(context.dragStartY, context.dragEndY), + bottom = Math.max(context.dragStartY, context.dragEndY); + top = Math.max(top, plotArea.y); + bottom = Math.min(bottom, plotArea.y + plotArea.h); + if (top < bottom) { + g.doZoomY_(top, bottom); + } + context.cancelNextDblclick = true; + } + context.dragStartX = null; + context.dragStartY = null; +}; + +/** + * @private + */ +DygraphInteraction.startTouch = function (event, g, context) { + event.preventDefault(); // touch browsers are all nice. + if (event.touches.length > 1) { + // If the user ever puts two fingers down, it's not a double tap. + context.startTimeForDoubleTapMs = null; + } + + var touches = []; + for (var i = 0; i < event.touches.length; i++) { + var t = event.touches[i]; + // we dispense with 'dragGetX_' because all touchBrowsers support pageX + touches.push({ + pageX: t.pageX, + pageY: t.pageY, + dataX: g.toDataXCoord(t.pageX), + dataY: g.toDataYCoord(t.pageY) + // identifier: t.identifier + }); + } + context.initialTouches = touches; + + if (touches.length == 1) { + // This is just a swipe. + context.initialPinchCenter = touches[0]; + context.touchDirections = { x: true, y: true }; + } else if (touches.length >= 2) { + // It's become a pinch! + // In case there are 3+ touches, we ignore all but the "first" two. + + // only screen coordinates can be averaged (data coords could be log scale). + context.initialPinchCenter = { + pageX: 0.5 * (touches[0].pageX + touches[1].pageX), + pageY: 0.5 * (touches[0].pageY + touches[1].pageY), + + // TODO(danvk): remove + dataX: 0.5 * (touches[0].dataX + touches[1].dataX), + dataY: 0.5 * (touches[0].dataY + touches[1].dataY) + }; + + // Make pinches in a 45-degree swath around either axis 1-dimensional zooms. + var initialAngle = 180 / Math.PI * Math.atan2(context.initialPinchCenter.pageY - touches[0].pageY, touches[0].pageX - context.initialPinchCenter.pageX); + + // use symmetry to get it into the first quadrant. + initialAngle = Math.abs(initialAngle); + if (initialAngle > 90) initialAngle = 90 - initialAngle; + + context.touchDirections = { + x: initialAngle < 90 - 45 / 2, + y: initialAngle > 45 / 2 + }; + } + + // save the full x & y ranges. + context.initialRange = { + x: g.xAxisRange(), + y: g.yAxisRange() + }; +}; + +/** + * @private + */ +DygraphInteraction.moveTouch = function (event, g, context) { + // If the tap moves, then it's definitely not part of a double-tap. + context.startTimeForDoubleTapMs = null; + + var i, + touches = []; + for (i = 0; i < event.touches.length; i++) { + var t = event.touches[i]; + touches.push({ + pageX: t.pageX, + pageY: t.pageY + }); + } + var initialTouches = context.initialTouches; + + var c_now; + + // old and new centers. + var c_init = context.initialPinchCenter; + if (touches.length == 1) { + c_now = touches[0]; + } else { + c_now = { + pageX: 0.5 * (touches[0].pageX + touches[1].pageX), + pageY: 0.5 * (touches[0].pageY + touches[1].pageY) + }; + } + + // this is the "swipe" component + // we toss it out for now, but could use it in the future. + var swipe = { + pageX: c_now.pageX - c_init.pageX, + pageY: c_now.pageY - c_init.pageY + }; + var dataWidth = context.initialRange.x[1] - context.initialRange.x[0]; + var dataHeight = context.initialRange.y[0] - context.initialRange.y[1]; + swipe.dataX = swipe.pageX / g.plotter_.area.w * dataWidth; + swipe.dataY = swipe.pageY / g.plotter_.area.h * dataHeight; + var xScale, yScale; + + // The residual bits are usually split into scale & rotate bits, but we split + // them into x-scale and y-scale bits. + if (touches.length == 1) { + xScale = 1.0; + yScale = 1.0; + } else if (touches.length >= 2) { + var initHalfWidth = initialTouches[1].pageX - c_init.pageX; + xScale = (touches[1].pageX - c_now.pageX) / initHalfWidth; + + var initHalfHeight = initialTouches[1].pageY - c_init.pageY; + yScale = (touches[1].pageY - c_now.pageY) / initHalfHeight; + } + + // Clip scaling to [1/8, 8] to prevent too much blowup. + xScale = Math.min(8, Math.max(0.125, xScale)); + yScale = Math.min(8, Math.max(0.125, yScale)); + + var didZoom = false; + if (context.touchDirections.x) { + g.dateWindow_ = [c_init.dataX - swipe.dataX + (context.initialRange.x[0] - c_init.dataX) / xScale, c_init.dataX - swipe.dataX + (context.initialRange.x[1] - c_init.dataX) / xScale]; + didZoom = true; + } + + if (context.touchDirections.y) { + for (i = 0; i < 1 /*g.axes_.length*/; i++) { + var axis = g.axes_[i]; + var logscale = g.attributes_.getForAxis("logscale", i); + if (logscale) { + // TODO(danvk): implement + } else { + axis.valueRange = [c_init.dataY - swipe.dataY + (context.initialRange.y[0] - c_init.dataY) / yScale, c_init.dataY - swipe.dataY + (context.initialRange.y[1] - c_init.dataY) / yScale]; + didZoom = true; + } + } + } + + g.drawGraph_(false); + + // We only call zoomCallback on zooms, not pans, to mirror desktop behavior. + if (didZoom && touches.length > 1 && g.getFunctionOption('zoomCallback')) { + var viewWindow = g.xAxisRange(); + g.getFunctionOption("zoomCallback").call(g, viewWindow[0], viewWindow[1], g.yAxisRanges()); + } +}; + +/** + * @private + */ +DygraphInteraction.endTouch = function (event, g, context) { + if (event.touches.length !== 0) { + // this is effectively a "reset" + DygraphInteraction.startTouch(event, g, context); + } else if (event.changedTouches.length == 1) { + // Could be part of a "double tap" + // The heuristic here is that it's a double-tap if the two touchend events + // occur within 500ms and within a 50x50 pixel box. + var now = new Date().getTime(); + var t = event.changedTouches[0]; + if (context.startTimeForDoubleTapMs && now - context.startTimeForDoubleTapMs < 500 && context.doubleTapX && Math.abs(context.doubleTapX - t.screenX) < 50 && context.doubleTapY && Math.abs(context.doubleTapY - t.screenY) < 50) { + g.resetZoom(); + } else { + context.startTimeForDoubleTapMs = now; + context.doubleTapX = t.screenX; + context.doubleTapY = t.screenY; + } + } +}; + +// Determine the distance from x to [left, right]. +var distanceFromInterval = function distanceFromInterval(x, left, right) { + if (x < left) { + return left - x; + } else if (x > right) { + return x - right; + } else { + return 0; + } +}; + +/** + * Returns the number of pixels by which the event happens from the nearest + * edge of the chart. For events in the interior of the chart, this returns zero. + */ +var distanceFromChart = function distanceFromChart(event, g) { + var chartPos = utils.findPos(g.canvas_); + var box = { + left: chartPos.x, + right: chartPos.x + g.canvas_.offsetWidth, + top: chartPos.y, + bottom: chartPos.y + g.canvas_.offsetHeight + }; + + var pt = { + x: utils.pageX(event), + y: utils.pageY(event) + }; + + var dx = distanceFromInterval(pt.x, box.left, box.right), + dy = distanceFromInterval(pt.y, box.top, box.bottom); + return Math.max(dx, dy); +}; + +/** + * Default interation model for dygraphs. You can refer to specific elements of + * this when constructing your own interaction model, e.g.: + * g.updateOptions( { + * interactionModel: { + * mousedown: DygraphInteraction.defaultInteractionModel.mousedown + * } + * } ); + */ +DygraphInteraction.defaultModel = { + // Track the beginning of drag events + mousedown: function mousedown(event, g, context) { + // Right-click should not initiate a zoom. + if (event.button && event.button == 2) return; + + context.initializeMouseDown(event, g, context); + + if (event.altKey || event.shiftKey) { + DygraphInteraction.startPan(event, g, context); + } else { + DygraphInteraction.startZoom(event, g, context); + } + + // Note: we register mousemove/mouseup on document to allow some leeway for + // events to move outside of the chart. Interaction model events get + // registered on the canvas, which is too small to allow this. + var mousemove = function mousemove(event) { + if (context.isZooming) { + // When the mouse moves >200px from the chart edge, cancel the zoom. + var d = distanceFromChart(event, g); + if (d < DRAG_EDGE_MARGIN) { + DygraphInteraction.moveZoom(event, g, context); + } else { + if (context.dragEndX !== null) { + context.dragEndX = null; + context.dragEndY = null; + g.clearZoomRect_(); + } + } + } else if (context.isPanning) { + DygraphInteraction.movePan(event, g, context); + } + }; + var mouseup = function mouseup(event) { + if (context.isZooming) { + if (context.dragEndX !== null) { + DygraphInteraction.endZoom(event, g, context); + } else { + DygraphInteraction.maybeTreatMouseOpAsClick(event, g, context); + } + } else if (context.isPanning) { + DygraphInteraction.endPan(event, g, context); + } + + utils.removeEvent(document, 'mousemove', mousemove); + utils.removeEvent(document, 'mouseup', mouseup); + context.destroy(); + }; + + g.addAndTrackEvent(document, 'mousemove', mousemove); + g.addAndTrackEvent(document, 'mouseup', mouseup); + }, + willDestroyContextMyself: true, + + touchstart: function touchstart(event, g, context) { + DygraphInteraction.startTouch(event, g, context); + }, + touchmove: function touchmove(event, g, context) { + DygraphInteraction.moveTouch(event, g, context); + }, + touchend: function touchend(event, g, context) { + DygraphInteraction.endTouch(event, g, context); + }, + + // Disable zooming out if panning. + dblclick: function dblclick(event, g, context) { + if (context.cancelNextDblclick) { + context.cancelNextDblclick = false; + return; + } + + // Give plugins a chance to grab this event. + var e = { + canvasx: context.dragEndX, + canvasy: context.dragEndY, + cancelable: true + }; + if (g.cascadeEvents_('dblclick', e)) { + return; + } + + if (event.altKey || event.shiftKey) { + return; + } + g.resetZoom(); + } +}; + +/* +Dygraph.DEFAULT_ATTRS.interactionModel = DygraphInteraction.defaultModel; + +// old ways of accessing these methods/properties +Dygraph.defaultInteractionModel = DygraphInteraction.defaultModel; +Dygraph.endZoom = DygraphInteraction.endZoom; +Dygraph.moveZoom = DygraphInteraction.moveZoom; +Dygraph.startZoom = DygraphInteraction.startZoom; +Dygraph.endPan = DygraphInteraction.endPan; +Dygraph.movePan = DygraphInteraction.movePan; +Dygraph.startPan = DygraphInteraction.startPan; +*/ + +DygraphInteraction.nonInteractiveModel_ = { + mousedown: function mousedown(event, g, context) { + context.initializeMouseDown(event, g, context); + }, + mouseup: DygraphInteraction.maybeTreatMouseOpAsClick +}; + +// Default interaction model when using the range selector. +DygraphInteraction.dragIsPanInteractionModel = { + mousedown: function mousedown(event, g, context) { + context.initializeMouseDown(event, g, context); + DygraphInteraction.startPan(event, g, context); + }, + mousemove: function mousemove(event, g, context) { + if (context.isPanning) { + DygraphInteraction.movePan(event, g, context); + } + }, + mouseup: function mouseup(event, g, context) { + if (context.isPanning) { + DygraphInteraction.endPan(event, g, context); + } + } +}; + +exports["default"] = DygraphInteraction; +module.exports = exports["default"]; + +},{"./dygraph-utils":17}],13:[function(require,module,exports){ +/** + * @license + * Copyright 2011 Dan Vanderkam (danvdk@gmail.com) + * MIT-licensed (http://opensource.org/licenses/MIT) + */ + +/** + * @fileoverview Based on PlotKitLayout, but modified to meet the needs of + * dygraphs. + */ + +/*global Dygraph:false */ +"use strict"; + +Object.defineProperty(exports, '__esModule', { + value: true +}); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } + +var _dygraphUtils = require('./dygraph-utils'); + +var utils = _interopRequireWildcard(_dygraphUtils); + +/** + * Creates a new DygraphLayout object. + * + * This class contains all the data to be charted. + * It uses data coordinates, but also records the chart range (in data + * coordinates) and hence is able to calculate percentage positions ('In this + * view, Point A lies 25% down the x-axis.') + * + * Two things that it does not do are: + * 1. Record pixel coordinates for anything. + * 2. (oddly) determine anything about the layout of chart elements. + * + * The naming is a vestige of Dygraph's original PlotKit roots. + * + * @constructor + */ +var DygraphLayout = function DygraphLayout(dygraph) { + this.dygraph_ = dygraph; + /** + * Array of points for each series. + * + * [series index][row index in series] = |Point| structure, + * where series index refers to visible series only, and the + * point index is for the reduced set of points for the current + * zoom region (including one point just outside the window). + * All points in the same row index share the same X value. + * + * @type {Array.<Array.<Dygraph.PointType>>} + */ + this.points = []; + this.setNames = []; + this.annotations = []; + this.yAxes_ = null; + + // TODO(danvk): it's odd that xTicks_ and yTicks_ are inputs, but xticks and + // yticks are outputs. Clean this up. + this.xTicks_ = null; + this.yTicks_ = null; +}; + +/** + * Add points for a single series. + * + * @param {string} setname Name of the series. + * @param {Array.<Dygraph.PointType>} set_xy Points for the series. + */ +DygraphLayout.prototype.addDataset = function (setname, set_xy) { + this.points.push(set_xy); + this.setNames.push(setname); +}; + +/** + * Returns the box which the chart should be drawn in. This is the canvas's + * box, less space needed for the axis and chart labels. + * + * @return {{x: number, y: number, w: number, h: number}} + */ +DygraphLayout.prototype.getPlotArea = function () { + return this.area_; +}; + +// Compute the box which the chart should be drawn in. This is the canvas's +// box, less space needed for axis, chart labels, and other plug-ins. +// NOTE: This should only be called by Dygraph.predraw_(). +DygraphLayout.prototype.computePlotArea = function () { + var area = { + // TODO(danvk): per-axis setting. + x: 0, + y: 0 + }; + + area.w = this.dygraph_.width_ - area.x - this.dygraph_.getOption('rightGap'); + area.h = this.dygraph_.height_; + + // Let plugins reserve space. + var e = { + chart_div: this.dygraph_.graphDiv, + reserveSpaceLeft: function reserveSpaceLeft(px) { + var r = { + x: area.x, + y: area.y, + w: px, + h: area.h + }; + area.x += px; + area.w -= px; + return r; + }, + reserveSpaceRight: function reserveSpaceRight(px) { + var r = { + x: area.x + area.w - px, + y: area.y, + w: px, + h: area.h + }; + area.w -= px; + return r; + }, + reserveSpaceTop: function reserveSpaceTop(px) { + var r = { + x: area.x, + y: area.y, + w: area.w, + h: px + }; + area.y += px; + area.h -= px; + return r; + }, + reserveSpaceBottom: function reserveSpaceBottom(px) { + var r = { + x: area.x, + y: area.y + area.h - px, + w: area.w, + h: px + }; + area.h -= px; + return r; + }, + chartRect: function chartRect() { + return { x: area.x, y: area.y, w: area.w, h: area.h }; + } + }; + this.dygraph_.cascadeEvents_('layout', e); + + this.area_ = area; +}; + +DygraphLayout.prototype.setAnnotations = function (ann) { + // The Dygraph object's annotations aren't parsed. We parse them here and + // save a copy. If there is no parser, then the user must be using raw format. + this.annotations = []; + var parse = this.dygraph_.getOption('xValueParser') || function (x) { + return x; + }; + for (var i = 0; i < ann.length; i++) { + var a = {}; + if (!ann[i].xval && ann[i].x === undefined) { + console.error("Annotations must have an 'x' property"); + return; + } + if (ann[i].icon && !(ann[i].hasOwnProperty('width') && ann[i].hasOwnProperty('height'))) { + console.error("Must set width and height when setting " + "annotation.icon property"); + return; + } + utils.update(a, ann[i]); + if (!a.xval) a.xval = parse(a.x); + this.annotations.push(a); + } +}; + +DygraphLayout.prototype.setXTicks = function (xTicks) { + this.xTicks_ = xTicks; +}; + +// TODO(danvk): add this to the Dygraph object's API or move it into Layout. +DygraphLayout.prototype.setYAxes = function (yAxes) { + this.yAxes_ = yAxes; +}; + +DygraphLayout.prototype.evaluate = function () { + this._xAxis = {}; + this._evaluateLimits(); + this._evaluateLineCharts(); + this._evaluateLineTicks(); + this._evaluateAnnotations(); +}; + +DygraphLayout.prototype._evaluateLimits = function () { + var xlimits = this.dygraph_.xAxisRange(); + this._xAxis.minval = xlimits[0]; + this._xAxis.maxval = xlimits[1]; + var xrange = xlimits[1] - xlimits[0]; + this._xAxis.scale = xrange !== 0 ? 1 / xrange : 1.0; + + if (this.dygraph_.getOptionForAxis("logscale", 'x')) { + this._xAxis.xlogrange = utils.log10(this._xAxis.maxval) - utils.log10(this._xAxis.minval); + this._xAxis.xlogscale = this._xAxis.xlogrange !== 0 ? 1.0 / this._xAxis.xlogrange : 1.0; + } + for (var i = 0; i < this.yAxes_.length; i++) { + var axis = this.yAxes_[i]; + axis.minyval = axis.computedValueRange[0]; + axis.maxyval = axis.computedValueRange[1]; + axis.yrange = axis.maxyval - axis.minyval; + axis.yscale = axis.yrange !== 0 ? 1.0 / axis.yrange : 1.0; + + if (this.dygraph_.getOption("logscale")) { + axis.ylogrange = utils.log10(axis.maxyval) - utils.log10(axis.minyval); + axis.ylogscale = axis.ylogrange !== 0 ? 1.0 / axis.ylogrange : 1.0; + if (!isFinite(axis.ylogrange) || isNaN(axis.ylogrange)) { + console.error('axis ' + i + ' of graph at ' + axis.g + ' can\'t be displayed in log scale for range [' + axis.minyval + ' - ' + axis.maxyval + ']'); + } + } + } +}; + +DygraphLayout.calcXNormal_ = function (value, xAxis, logscale) { + if (logscale) { + return (utils.log10(value) - utils.log10(xAxis.minval)) * xAxis.xlogscale; + } else { + return (value - xAxis.minval) * xAxis.scale; + } +}; + +/** + * @param {DygraphAxisType} axis + * @param {number} value + * @param {boolean} logscale + * @return {number} + */ +DygraphLayout.calcYNormal_ = function (axis, value, logscale) { + if (logscale) { + var x = 1.0 - (utils.log10(value) - utils.log10(axis.minyval)) * axis.ylogscale; + return isFinite(x) ? x : NaN; // shim for v8 issue; see pull request 276 + } else { + return 1.0 - (value - axis.minyval) * axis.yscale; + } +}; + +DygraphLayout.prototype._evaluateLineCharts = function () { + var isStacked = this.dygraph_.getOption("stackedGraph"); + var isLogscaleForX = this.dygraph_.getOptionForAxis("logscale", 'x'); + + for (var setIdx = 0; setIdx < this.points.length; setIdx++) { + var points = this.points[setIdx]; + var setName = this.setNames[setIdx]; + var connectSeparated = this.dygraph_.getOption('connectSeparatedPoints', setName); + var axis = this.dygraph_.axisPropertiesForSeries(setName); + // TODO (konigsberg): use optionsForAxis instead. + var logscale = this.dygraph_.attributes_.getForSeries("logscale", setName); + + for (var j = 0; j < points.length; j++) { + var point = points[j]; + + // Range from 0-1 where 0 represents left and 1 represents right. + point.x = DygraphLayout.calcXNormal_(point.xval, this._xAxis, isLogscaleForX); + // Range from 0-1 where 0 represents top and 1 represents bottom + var yval = point.yval; + if (isStacked) { + point.y_stacked = DygraphLayout.calcYNormal_(axis, point.yval_stacked, logscale); + if (yval !== null && !isNaN(yval)) { + yval = point.yval_stacked; + } + } + if (yval === null) { + yval = NaN; + if (!connectSeparated) { + point.yval = NaN; + } + } + point.y = DygraphLayout.calcYNormal_(axis, yval, logscale); + } + + this.dygraph_.dataHandler_.onLineEvaluated(points, axis, logscale); + } +}; + +DygraphLayout.prototype._evaluateLineTicks = function () { + var i, tick, label, pos, v, has_tick; + this.xticks = []; + for (i = 0; i < this.xTicks_.length; i++) { + tick = this.xTicks_[i]; + label = tick.label; + has_tick = !('label_v' in tick); + v = has_tick ? tick.v : tick.label_v; + pos = this.dygraph_.toPercentXCoord(v); + if (pos >= 0.0 && pos < 1.0) { + this.xticks.push({ pos: pos, label: label, has_tick: has_tick }); + } + } + + this.yticks = []; + for (i = 0; i < this.yAxes_.length; i++) { + var axis = this.yAxes_[i]; + for (var j = 0; j < axis.ticks.length; j++) { + tick = axis.ticks[j]; + label = tick.label; + has_tick = !('label_v' in tick); + v = has_tick ? tick.v : tick.label_v; + pos = this.dygraph_.toPercentYCoord(v, i); + if (pos > 0.0 && pos <= 1.0) { + this.yticks.push({ axis: i, pos: pos, label: label, has_tick: has_tick }); + } + } + } +}; + +DygraphLayout.prototype._evaluateAnnotations = function () { + // Add the annotations to the point to which they belong. + // Make a map from (setName, xval) to annotation for quick lookups. + var i; + var annotations = {}; + for (i = 0; i < this.annotations.length; i++) { + var a = this.annotations[i]; + annotations[a.xval + "," + a.series] = a; + } + + this.annotated_points = []; + + // Exit the function early if there are no annotations. + if (!this.annotations || !this.annotations.length) { + return; + } + + // TODO(antrob): loop through annotations not points. + for (var setIdx = 0; setIdx < this.points.length; setIdx++) { + var points = this.points[setIdx]; + for (i = 0; i < points.length; i++) { + var p = points[i]; + var k = p.xval + "," + p.name; + if (k in annotations) { + p.annotation = annotations[k]; + this.annotated_points.push(p); + } + } + } +}; + +/** + * Convenience function to remove all the data sets from a graph + */ +DygraphLayout.prototype.removeAllDatasets = function () { + delete this.points; + delete this.setNames; + delete this.setPointsLengths; + delete this.setPointsOffsets; + this.points = []; + this.setNames = []; + this.setPointsLengths = []; + this.setPointsOffsets = []; +}; + +exports['default'] = DygraphLayout; +module.exports = exports['default']; + +},{"./dygraph-utils":17}],14:[function(require,module,exports){ +(function (process){ +/** + * @license + * Copyright 2011 Dan Vanderkam (danvdk@gmail.com) + * MIT-licensed (http://opensource.org/licenses/MIT) + */ + +"use strict"; + +Object.defineProperty(exports, '__esModule', { + value: true +}); +var OPTIONS_REFERENCE = null; + +// For "production" code, this gets removed by uglifyjs. +if (typeof process !== 'undefined') { + if ("development" != 'production') { + + // NOTE: in addition to parsing as JS, this snippet is expected to be valid + // JSON. This assumption cannot be checked in JS, but it will be checked when + // documentation is generated by the generate-documentation.py script. For the + // most part, this just means that you should always use double quotes. + OPTIONS_REFERENCE = // <JSON> + { + "xValueParser": { + "default": "parseFloat() or Date.parse()*", + "labels": ["CSV parsing"], + "type": "function(str) -> number", + "description": "A function which parses x-values (i.e. the dependent series). Must return a number, even when the values are dates. In this case, millis since epoch are used. This is used primarily for parsing CSV data. *=Dygraphs is slightly more accepting in the dates which it will parse. See code for details." + }, + "stackedGraph": { + "default": "false", + "labels": ["Data Line display"], + "type": "boolean", + "description": "If set, stack series on top of one another rather than drawing them independently. The first series specified in the input data will wind up on top of the chart and the last will be on bottom. NaN values are drawn as white areas without a line on top, see stackedGraphNaNFill for details." + }, + "stackedGraphNaNFill": { + "default": "all", + "labels": ["Data Line display"], + "type": "string", + "description": "Controls handling of NaN values inside a stacked graph. NaN values are interpolated/extended for stacking purposes, but the actual point value remains NaN in the legend display. Valid option values are \"all\" (interpolate internally, repeat leftmost and rightmost value as needed), \"inside\" (interpolate internally only, use zero outside leftmost and rightmost value), and \"none\" (treat NaN as zero everywhere)." + }, + "pointSize": { + "default": "1", + "labels": ["Data Line display"], + "type": "integer", + "description": "The size of the dot to draw on each point in pixels (see drawPoints). A dot is always drawn when a point is \"isolated\", i.e. there is a missing point on either side of it. This also controls the size of those dots." + }, + "drawPoints": { + "default": "false", + "labels": ["Data Line display"], + "type": "boolean", + "description": "Draw a small dot at each point, in addition to a line going through the point. This makes the individual data points easier to see, but can increase visual clutter in the chart. The small dot can be replaced with a custom rendering by supplying a <a href='#drawPointCallback'>drawPointCallback</a>." + }, + "drawGapEdgePoints": { + "default": "false", + "labels": ["Data Line display"], + "type": "boolean", + "description": "Draw points at the edges of gaps in the data. This improves visibility of small data segments or other data irregularities." + }, + "drawPointCallback": { + "default": "null", + "labels": ["Data Line display"], + "type": "function(g, seriesName, canvasContext, cx, cy, color, pointSize)", + "parameters": [["g", "the reference graph"], ["seriesName", "the name of the series"], ["canvasContext", "the canvas to draw on"], ["cx", "center x coordinate"], ["cy", "center y coordinate"], ["color", "series color"], ["pointSize", "the radius of the image."], ["idx", "the row-index of the point in the data."]], + "description": "Draw a custom item when drawPoints is enabled. Default is a small dot matching the series color. This method should constrain drawing to within pointSize pixels from (cx, cy). Also see <a href='#drawHighlightPointCallback'>drawHighlightPointCallback</a>" + }, + "height": { + "default": "320", + "labels": ["Overall display"], + "type": "integer", + "description": "Height, in pixels, of the chart. If the container div has been explicitly sized, this will be ignored." + }, + "zoomCallback": { + "default": "null", + "labels": ["Callbacks"], + "type": "function(minDate, maxDate, yRanges)", + "parameters": [["minDate", "milliseconds since epoch"], ["maxDate", "milliseconds since epoch."], ["yRanges", "is an array of [bottom, top] pairs, one for each y-axis."]], + "description": "A function to call when the zoom window is changed (either by zooming in or out). When animatedZooms is set, zoomCallback is called once at the end of the transition (it will not be called for intermediate frames)." + }, + "pointClickCallback": { + "snippet": "function(e, point){<br> alert(point);<br>}", + "default": "null", + "labels": ["Callbacks", "Interactive Elements"], + "type": "function(e, point)", + "parameters": [["e", "the event object for the click"], ["point", "the point that was clicked See <a href='#point_properties'>Point properties</a> for details"]], + "description": "A function to call when a data point is clicked. and the point that was clicked." + }, + "color": { + "default": "(see description)", + "labels": ["Data Series Colors"], + "type": "string", + "example": "red", + "description": "A per-series color definition. Used in conjunction with, and overrides, the colors option." + }, + "colors": { + "default": "(see description)", + "labels": ["Data Series Colors"], + "type": "array<string>", + "example": "['red', '#00FF00']", + "description": "List of colors for the data series. These can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"yellow\", etc. If not specified, equally-spaced points around a color wheel are used. Overridden by the 'color' option." + }, + "connectSeparatedPoints": { + "default": "false", + "labels": ["Data Line display"], + "type": "boolean", + "description": "Usually, when Dygraphs encounters a missing value in a data series, it interprets this as a gap and draws it as such. If, instead, the missing values represents an x-value for which only a different series has data, then you'll want to connect the dots by setting this to true. To explicitly include a gap with this option set, use a value of NaN." + }, + "highlightCallback": { + "default": "null", + "labels": ["Callbacks"], + "type": "function(event, x, points, row, seriesName)", + "description": "When set, this callback gets called every time a new point is highlighted.", + "parameters": [["event", "the JavaScript mousemove event"], ["x", "the x-coordinate of the highlighted points"], ["points", "an array of highlighted points: <code>[ {name: 'series', yval: y-value}, … ]</code>"], ["row", "integer index of the highlighted row in the data table, starting from 0"], ["seriesName", "name of the highlighted series, only present if highlightSeriesOpts is set."]] + }, + "drawHighlightPointCallback": { + "default": "null", + "labels": ["Data Line display"], + "type": "function(g, seriesName, canvasContext, cx, cy, color, pointSize)", + "parameters": [["g", "the reference graph"], ["seriesName", "the name of the series"], ["canvasContext", "the canvas to draw on"], ["cx", "center x coordinate"], ["cy", "center y coordinate"], ["color", "series color"], ["pointSize", "the radius of the image."], ["idx", "the row-index of the point in the data."]], + "description": "Draw a custom item when a point is highlighted. Default is a small dot matching the series color. This method should constrain drawing to within pointSize pixels from (cx, cy) Also see <a href='#drawPointCallback'>drawPointCallback</a>" + }, + "highlightSeriesOpts": { + "default": "null", + "labels": ["Interactive Elements"], + "type": "Object", + "description": "When set, the options from this object are applied to the timeseries closest to the mouse pointer for interactive highlighting. See also 'highlightCallback'. Example: highlightSeriesOpts: { strokeWidth: 3 }." + }, + "highlightSeriesBackgroundAlpha": { + "default": "0.5", + "labels": ["Interactive Elements"], + "type": "float", + "description": "Fade the background while highlighting series. 1=fully visible background (disable fading), 0=hiddden background (show highlighted series only)." + }, + "highlightSeriesBackgroundColor": { + "default": "rgb(255, 255, 255)", + "labels": ["Interactive Elements"], + "type": "string", + "description": "Sets the background color used to fade out the series in conjunction with 'highlightSeriesBackgroundAlpha'." + }, + "includeZero": { + "default": "false", + "labels": ["Axis display"], + "type": "boolean", + "description": "Usually, dygraphs will use the range of the data plus some padding to set the range of the y-axis. If this option is set, the y-axis will always include zero, typically as the lowest value. This can be used to avoid exaggerating the variance in the data" + }, + "rollPeriod": { + "default": "1", + "labels": ["Error Bars", "Rolling Averages"], + "type": "integer >= 1", + "description": "Number of days over which to average data. Discussed extensively above." + }, + "unhighlightCallback": { + "default": "null", + "labels": ["Callbacks"], + "type": "function(event)", + "parameters": [["event", "the mouse event"]], + "description": "When set, this callback gets called every time the user stops highlighting any point by mousing out of the graph." + }, + "axisTickSize": { + "default": "3.0", + "labels": ["Axis display"], + "type": "number", + "description": "The size of the line to display next to each tick mark on x- or y-axes." + }, + "labelsSeparateLines": { + "default": "false", + "labels": ["Legend"], + "type": "boolean", + "description": "Put <code><br/></code> between lines in the label string. Often used in conjunction with <strong>labelsDiv</strong>." + }, + "valueFormatter": { + "default": "Depends on the type of your data.", + "labels": ["Legend", "Value display/formatting"], + "type": "function(num or millis, opts, seriesName, dygraph, row, col)", + "description": "Function to provide a custom display format for the values displayed on mouseover. This does not affect the values that appear on tick marks next to the axes. To format those, see axisLabelFormatter. This is usually set on a <a href='per-axis.html'>per-axis</a> basis. .", + "parameters": [["num_or_millis", "The value to be formatted. This is always a number. For date axes, it's millis since epoch. You can call new Date(millis) to get a Date object."], ["opts", "This is a function you can call to access various options (e.g. opts('labelsKMB')). It returns per-axis values for the option when available."], ["seriesName", "The name of the series from which the point came, e.g. 'X', 'Y', 'A', etc."], ["dygraph", "The dygraph object for which the formatting is being done"], ["row", "The row of the data from which this point comes. g.getValue(row, 0) will return the x-value for this point."], ["col", "The column of the data from which this point comes. g.getValue(row, col) will return the original y-value for this point. This can be used to get the full confidence interval for the point, or access un-rolled values for the point."]] + }, + "annotationMouseOverHandler": { + "default": "null", + "labels": ["Annotations"], + "type": "function(annotation, point, dygraph, event)", + "description": "If provided, this function is called whenever the user mouses over an annotation." + }, + "annotationMouseOutHandler": { + "default": "null", + "labels": ["Annotations"], + "type": "function(annotation, point, dygraph, event)", + "parameters": [["annotation", "the annotation left"], ["point", "the point associated with the annotation"], ["dygraph", "the reference graph"], ["event", "the mouse event"]], + "description": "If provided, this function is called whenever the user mouses out of an annotation." + }, + "annotationClickHandler": { + "default": "null", + "labels": ["Annotations"], + "type": "function(annotation, point, dygraph, event)", + "parameters": [["annotation", "the annotation left"], ["point", "the point associated with the annotation"], ["dygraph", "the reference graph"], ["event", "the mouse event"]], + "description": "If provided, this function is called whenever the user clicks on an annotation." + }, + "annotationDblClickHandler": { + "default": "null", + "labels": ["Annotations"], + "type": "function(annotation, point, dygraph, event)", + "parameters": [["annotation", "the annotation left"], ["point", "the point associated with the annotation"], ["dygraph", "the reference graph"], ["event", "the mouse event"]], + "description": "If provided, this function is called whenever the user double-clicks on an annotation." + }, + "drawCallback": { + "default": "null", + "labels": ["Callbacks"], + "type": "function(dygraph, is_initial)", + "parameters": [["dygraph", "The graph being drawn"], ["is_initial", "True if this is the initial draw, false for subsequent draws."]], + "description": "When set, this callback gets called every time the dygraph is drawn. This includes the initial draw, after zooming and repeatedly while panning." + }, + "labelsKMG2": { + "default": "false", + "labels": ["Value display/formatting"], + "type": "boolean", + "description": "Show k/M/G for kilo/Mega/Giga on y-axis. This is different than <code>labelsKMB</code> in that it uses base 2, not 10." + }, + "delimiter": { + "default": ",", + "labels": ["CSV parsing"], + "type": "string", + "description": "The delimiter to look for when separating fields of a CSV file. Setting this to a tab is not usually necessary, since tab-delimited data is auto-detected." + }, + "axisLabelFontSize": { + "default": "14", + "labels": ["Axis display"], + "type": "integer", + "description": "Size of the font (in pixels) to use in the axis labels, both x- and y-axis." + }, + "underlayCallback": { + "default": "null", + "labels": ["Callbacks"], + "type": "function(context, area, dygraph)", + "parameters": [["context", "the canvas drawing context on which to draw"], ["area", "An object with {x,y,w,h} properties describing the drawing area."], ["dygraph", "the reference graph"]], + "description": "When set, this callback gets called before the chart is drawn. It details on how to use this." + }, + "width": { + "default": "480", + "labels": ["Overall display"], + "type": "integer", + "description": "Width, in pixels, of the chart. If the container div has been explicitly sized, this will be ignored." + }, + "pixelRatio": { + "default": "(devicePixelRatio / context.backingStoreRatio)", + "labels": ["Overall display"], + "type": "float", + "description": "Overrides the pixel ratio scaling factor for the canvas's 2d context. Ordinarily, this is set to the devicePixelRatio / (context.backingStoreRatio || 1), so on mobile devices, where the devicePixelRatio can be somewhere around 3, performance can be improved by overriding this value to something less precise, like 1, at the expense of resolution." + }, + "interactionModel": { + "default": "...", + "labels": ["Interactive Elements"], + "type": "Object", + "description": "TODO(konigsberg): document this" + }, + "ticker": { + "default": "Dygraph.dateTicker or Dygraph.numericTicks", + "labels": ["Axis display"], + "type": "function(min, max, pixels, opts, dygraph, vals) -> [{v: ..., label: ...}, ...]", + "parameters": [["min", ""], ["max", ""], ["pixels", ""], ["opts", ""], ["dygraph", "the reference graph"], ["vals", ""]], + "description": "This lets you specify an arbitrary function to generate tick marks on an axis. The tick marks are an array of (value, label) pairs. The built-in functions go to great lengths to choose good tick marks so, if you set this option, you'll most likely want to call one of them and modify the result. See dygraph-tickers.js for an extensive discussion. This is set on a <a href='per-axis.html'>per-axis</a> basis." + }, + "xAxisHeight": { + "default": "(null)", + "labels": ["Axis display"], + "type": "integer", + "description": "Height, in pixels, of the x-axis. If not set explicitly, this is computed based on axisLabelFontSize and axisTickSize." + }, + "showLabelsOnHighlight": { + "default": "true", + "labels": ["Interactive Elements", "Legend"], + "type": "boolean", + "description": "Whether to show the legend upon mouseover." + }, + "axis": { + "default": "(none)", + "labels": ["Axis display"], + "type": "string", + "description": "Set to either 'y1' or 'y2' to assign a series to a y-axis (primary or secondary). Must be set per-series." + }, + "pixelsPerLabel": { + "default": "70 (x-axis) or 30 (y-axes)", + "labels": ["Axis display", "Grid"], + "type": "integer", + "description": "Number of pixels to require between each x- and y-label. Larger values will yield a sparser axis with fewer ticks. This is set on a <a href='per-axis.html'>per-axis</a> basis." + }, + "labelsDiv": { + "default": "null", + "labels": ["Legend"], + "type": "DOM element or string", + "example": "<code style='font-size: small'>document.getElementById('foo')</code>or<code>'foo'", + "description": "Show data labels in an external div, rather than on the graph. This value can either be a div element or a div id." + }, + "fractions": { + "default": "false", + "labels": ["CSV parsing", "Error Bars"], + "type": "boolean", + "description": "When set, attempt to parse each cell in the CSV file as \"a/b\", where a and b are integers. The ratio will be plotted. This allows computation of Wilson confidence intervals (see below)." + }, + "logscale": { + "default": "false", + "labels": ["Axis display"], + "type": "boolean", + "description": "When set for the y-axis or x-axis, the graph shows that axis in log scale. Any values less than or equal to zero are not displayed. Showing log scale with ranges that go below zero will result in an unviewable graph.\n\n Not compatible with showZero. connectSeparatedPoints is ignored. This is ignored for date-based x-axes." + }, + "strokeWidth": { + "default": "1.0", + "labels": ["Data Line display"], + "type": "float", + "example": "0.5, 2.0", + "description": "The width of the lines connecting data points. This can be used to increase the contrast or some graphs." + }, + "strokePattern": { + "default": "null", + "labels": ["Data Line display"], + "type": "array<integer>", + "example": "[10, 2, 5, 2]", + "description": "A custom pattern array where the even index is a draw and odd is a space in pixels. If null then it draws a solid line. The array should have a even length as any odd lengthed array could be expressed as a smaller even length array. This is used to create dashed lines." + }, + "strokeBorderWidth": { + "default": "null", + "labels": ["Data Line display"], + "type": "float", + "example": "1.0", + "description": "Draw a border around graph lines to make crossing lines more easily distinguishable. Useful for graphs with many lines." + }, + "strokeBorderColor": { + "default": "white", + "labels": ["Data Line display"], + "type": "string", + "example": "red, #ccffdd", + "description": "Color for the line border used if strokeBorderWidth is set." + }, + "wilsonInterval": { + "default": "true", + "labels": ["Error Bars"], + "type": "boolean", + "description": "Use in conjunction with the \"fractions\" option. Instead of plotting +/- N standard deviations, dygraphs will compute a Wilson confidence interval and plot that. This has more reasonable behavior for ratios close to 0 or 1." + }, + "fillGraph": { + "default": "false", + "labels": ["Data Line display"], + "type": "boolean", + "description": "Should the area underneath the graph be filled? This option is not compatible with error bars. This may be set on a <a href='per-axis.html'>per-series</a> basis." + }, + "highlightCircleSize": { + "default": "3", + "labels": ["Interactive Elements"], + "type": "integer", + "description": "The size in pixels of the dot drawn over highlighted points." + }, + "gridLineColor": { + "default": "rgb(128,128,128)", + "labels": ["Grid"], + "type": "red, blue", + "description": "The color of the gridlines. This may be set on a per-axis basis to define each axis' grid separately." + }, + "gridLinePattern": { + "default": "null", + "labels": ["Grid"], + "type": "array<integer>", + "example": "[10, 2, 5, 2]", + "description": "A custom pattern array where the even index is a draw and odd is a space in pixels. If null then it draws a solid line. The array should have a even length as any odd lengthed array could be expressed as a smaller even length array. This is used to create dashed gridlines." + }, + "visibility": { + "default": "[true, true, ...]", + "labels": ["Data Line display"], + "type": "Array of booleans", + "description": "Which series should initially be visible? Once the Dygraph has been constructed, you can access and modify the visibility of each series using the <code>visibility</code> and <code>setVisibility</code> methods." + }, + "valueRange": { + "default": "Full range of the input is shown", + "labels": ["Axis display"], + "type": "Array of two numbers", + "example": "[10, 110]", + "description": "Explicitly set the vertical range of the graph to [low, high]. This may be set on a per-axis basis to define each y-axis separately. If either limit is unspecified, it will be calculated automatically (e.g. [null, 30] to automatically calculate just the lower bound)" + }, + "colorSaturation": { + "default": "1.0", + "labels": ["Data Series Colors"], + "type": "float (0.0 - 1.0)", + "description": "If <strong>colors</strong> is not specified, saturation of the automatically-generated data series colors." + }, + "hideOverlayOnMouseOut": { + "default": "true", + "labels": ["Interactive Elements", "Legend"], + "type": "boolean", + "description": "Whether to hide the legend when the mouse leaves the chart area." + }, + "legend": { + "default": "onmouseover", + "labels": ["Legend"], + "type": "string", + "description": "When to display the legend. By default, it only appears when a user mouses over the chart. Set it to \"always\" to always display a legend of some sort. When set to \"follow\", legend follows highlighted points." + }, + "legendFormatter": { + "default": "null", + "labels": ["Legend"], + "type": "function(data): string", + "params": [["data", "An object containing information about the selection (or lack of a selection). This includes formatted values and series information. See <a href=\"https://github.com/danvk/dygraphs/pull/683\">here</a> for sample values."]], + "description": "Set this to supply a custom formatter for the legend. See <a href=\"https://github.com/danvk/dygraphs/pull/683\">this comment</a> and the <a href=\"tests/legend-formatter.html\">legendFormatter demo</a> for usage." + }, + "labelsShowZeroValues": { + "default": "true", + "labels": ["Legend"], + "type": "boolean", + "description": "Show zero value labels in the labelsDiv." + }, + "stepPlot": { + "default": "false", + "labels": ["Data Line display"], + "type": "boolean", + "description": "When set, display the graph as a step plot instead of a line plot. This option may either be set for the whole graph or for single series." + }, + "labelsUTC": { + "default": "false", + "labels": ["Value display/formatting", "Axis display"], + "type": "boolean", + "description": "Show date/time labels according to UTC (instead of local time)." + }, + "labelsKMB": { + "default": "false", + "labels": ["Value display/formatting"], + "type": "boolean", + "description": "Show K/M/B for thousands/millions/billions on y-axis." + }, + "rightGap": { + "default": "5", + "labels": ["Overall display"], + "type": "integer", + "description": "Number of pixels to leave blank at the right edge of the Dygraph. This makes it easier to highlight the right-most data point." + }, + "drawAxesAtZero": { + "default": "false", + "labels": ["Axis display"], + "type": "boolean", + "description": "When set, draw the X axis at the Y=0 position and the Y axis at the X=0 position if those positions are inside the graph's visible area. Otherwise, draw the axes at the bottom or left graph edge as usual." + }, + "xRangePad": { + "default": "0", + "labels": ["Axis display"], + "type": "float", + "description": "Add the specified amount of extra space (in pixels) around the X-axis value range to ensure points at the edges remain visible." + }, + "yRangePad": { + "default": "null", + "labels": ["Axis display"], + "type": "float", + "description": "If set, add the specified amount of extra space (in pixels) around the Y-axis value range to ensure points at the edges remain visible. If unset, use the traditional Y padding algorithm." + }, + "axisLabelFormatter": { + "default": "Depends on the data type", + "labels": ["Axis display"], + "type": "function(number or Date, granularity, opts, dygraph)", + "parameters": [["number or date", "Either a number (for a numeric axis) or a Date object (for a date axis)"], ["granularity", "specifies how fine-grained the axis is. For date axes, this is a reference to the time granularity enumeration, defined in dygraph-tickers.js, e.g. Dygraph.WEEKLY."], ["opts", "a function which provides access to various options on the dygraph, e.g. opts('labelsKMB')."], ["dygraph", "the referenced graph"]], + "description": "Function to call to format the tick values that appear along an axis. This is usually set on a <a href='per-axis.html'>per-axis</a> basis." + }, + "clickCallback": { + "snippet": "function(e, date_millis){<br> alert(new Date(date_millis));<br>}", + "default": "null", + "labels": ["Callbacks"], + "type": "function(e, x, points)", + "parameters": [["e", "The event object for the click"], ["x", "The x value that was clicked (for dates, this is milliseconds since epoch)"], ["points", "The closest points along that date. See <a href='#point_properties'>Point properties</a> for details."]], + "description": "A function to call when the canvas is clicked." + }, + "labels": { + "default": "[\"X\", \"Y1\", \"Y2\", ...]*", + "labels": ["Legend"], + "type": "array<string>", + "description": "A name for each data series, including the independent (X) series. For CSV files and DataTable objections, this is determined by context. For raw data, this must be specified. If it is not, default values are supplied and a warning is logged." + }, + "dateWindow": { + "default": "Full range of the input is shown", + "labels": ["Axis display"], + "type": "Array of two numbers", + "example": "[<br> Date.parse('2006-01-01'),<br> (new Date()).valueOf()<br>]", + "description": "Initially zoom in on a section of the graph. Is of the form [earliest, latest], where earliest/latest are milliseconds since epoch. If the data for the x-axis is numeric, the values in dateWindow must also be numbers." + }, + "showRoller": { + "default": "false", + "labels": ["Interactive Elements", "Rolling Averages"], + "type": "boolean", + "description": "If the rolling average period text box should be shown." + }, + "sigma": { + "default": "2.0", + "labels": ["Error Bars"], + "type": "float", + "description": "When errorBars is set, shade this many standard deviations above/below each point." + }, + "customBars": { + "default": "false", + "labels": ["CSV parsing", "Error Bars"], + "type": "boolean", + "description": "When set, parse each CSV cell as \"low;middle;high\". Error bars will be drawn for each point between low and high, with the series itself going through middle." + }, + "colorValue": { + "default": "1.0", + "labels": ["Data Series Colors"], + "type": "float (0.0 - 1.0)", + "description": "If colors is not specified, value of the data series colors, as in hue/saturation/value. (0.0-1.0, default 0.5)" + }, + "errorBars": { + "default": "false", + "labels": ["CSV parsing", "Error Bars"], + "type": "boolean", + "description": "Does the data contain standard deviations? Setting this to true alters the input format (see above)." + }, + "displayAnnotations": { + "default": "false", + "labels": ["Annotations"], + "type": "boolean", + "description": "Only applies when Dygraphs is used as a GViz chart. Causes string columns following a data series to be interpreted as annotations on points in that series. This is the same format used by Google's AnnotatedTimeLine chart." + }, + "panEdgeFraction": { + "default": "null", + "labels": ["Axis display", "Interactive Elements"], + "type": "float", + "description": "A value representing the farthest a graph may be panned, in percent of the display. For example, a value of 0.1 means that the graph can only be panned 10% passed the edges of the displayed values. null means no bounds." + }, + "title": { + "labels": ["Chart labels"], + "type": "string", + "default": "null", + "description": "Text to display above the chart. You can supply any HTML for this value, not just text. If you wish to style it using CSS, use the 'dygraph-label' or 'dygraph-title' classes." + }, + "titleHeight": { + "default": "18", + "labels": ["Chart labels"], + "type": "integer", + "description": "Height of the chart title, in pixels. This also controls the default font size of the title. If you style the title on your own, this controls how much space is set aside above the chart for the title's div." + }, + "xlabel": { + "labels": ["Chart labels"], + "type": "string", + "default": "null", + "description": "Text to display below the chart's x-axis. You can supply any HTML for this value, not just text. If you wish to style it using CSS, use the 'dygraph-label' or 'dygraph-xlabel' classes." + }, + "xLabelHeight": { + "labels": ["Chart labels"], + "type": "integer", + "default": "18", + "description": "Height of the x-axis label, in pixels. This also controls the default font size of the x-axis label. If you style the label on your own, this controls how much space is set aside below the chart for the x-axis label's div." + }, + "ylabel": { + "labels": ["Chart labels"], + "type": "string", + "default": "null", + "description": "Text to display to the left of the chart's y-axis. You can supply any HTML for this value, not just text. If you wish to style it using CSS, use the 'dygraph-label' or 'dygraph-ylabel' classes. The text will be rotated 90 degrees by default, so CSS rules may behave in unintuitive ways. No additional space is set aside for a y-axis label. If you need more space, increase the width of the y-axis tick labels using the yAxisLabelWidth option. If you need a wider div for the y-axis label, either style it that way with CSS (but remember that it's rotated, so width is controlled by the 'height' property) or set the yLabelWidth option." + }, + "y2label": { + "labels": ["Chart labels"], + "type": "string", + "default": "null", + "description": "Text to display to the right of the chart's secondary y-axis. This label is only displayed if a secondary y-axis is present. See <a href='http://dygraphs.com/tests/two-axes.html'>this test</a> for an example of how to do this. The comments for the 'ylabel' option generally apply here as well. This label gets a 'dygraph-y2label' instead of a 'dygraph-ylabel' class." + }, + "yLabelWidth": { + "labels": ["Chart labels"], + "type": "integer", + "default": "18", + "description": "Width of the div which contains the y-axis label. Since the y-axis label appears rotated 90 degrees, this actually affects the height of its div." + }, + "drawGrid": { + "default": "true for x and y, false for y2", + "labels": ["Grid"], + "type": "boolean", + "description": "Whether to display gridlines in the chart. This may be set on a per-axis basis to define the visibility of each axis' grid separately." + }, + "independentTicks": { + "default": "true for y, false for y2", + "labels": ["Axis display", "Grid"], + "type": "boolean", + "description": "Only valid for y and y2, has no effect on x: This option defines whether the y axes should align their ticks or if they should be independent. Possible combinations: 1.) y=true, y2=false (default): y is the primary axis and the y2 ticks are aligned to the the ones of y. (only 1 grid) 2.) y=false, y2=true: y2 is the primary axis and the y ticks are aligned to the the ones of y2. (only 1 grid) 3.) y=true, y2=true: Both axis are independent and have their own ticks. (2 grids) 4.) y=false, y2=false: Invalid configuration causes an error." + }, + "drawAxis": { + "default": "true for x and y, false for y2", + "labels": ["Axis display"], + "type": "boolean", + "description": "Whether to draw the specified axis. This may be set on a per-axis basis to define the visibility of each axis separately. Setting this to false also prevents axis ticks from being drawn and reclaims the space for the chart grid/lines." + }, + "gridLineWidth": { + "default": "0.3", + "labels": ["Grid"], + "type": "float", + "description": "Thickness (in pixels) of the gridlines drawn under the chart. The vertical/horizontal gridlines can be turned off entirely by using the drawGrid option. This may be set on a per-axis basis to define each axis' grid separately." + }, + "axisLineWidth": { + "default": "0.3", + "labels": ["Axis display"], + "type": "float", + "description": "Thickness (in pixels) of the x- and y-axis lines." + }, + "axisLineColor": { + "default": "black", + "labels": ["Axis display"], + "type": "string", + "description": "Color of the x- and y-axis lines. Accepts any value which the HTML canvas strokeStyle attribute understands, e.g. 'black' or 'rgb(0, 100, 255)'." + }, + "fillAlpha": { + "default": "0.15", + "labels": ["Error Bars", "Data Series Colors"], + "type": "float (0.0 - 1.0)", + "description": "Error bars (or custom bars) for each series are drawn in the same color as the series, but with partial transparency. This sets the transparency. A value of 0.0 means that the error bars will not be drawn, whereas a value of 1.0 means that the error bars will be as dark as the line for the series itself. This can be used to produce chart lines whose thickness varies at each point." + }, + "axisLabelWidth": { + "default": "50 (y-axis), 60 (x-axis)", + "labels": ["Axis display", "Chart labels"], + "type": "integer", + "description": "Width (in pixels) of the containing divs for x- and y-axis labels. For the y-axis, this also controls the width of the y-axis. Note that for the x-axis, this is independent from pixelsPerLabel, which controls the spacing between labels." + }, + "sigFigs": { + "default": "null", + "labels": ["Value display/formatting"], + "type": "integer", + "description": "By default, dygraphs displays numbers with a fixed number of digits after the decimal point. If you'd prefer to have a fixed number of significant figures, set this option to that number of sig figs. A value of 2, for instance, would cause 1 to be display as 1.0 and 1234 to be displayed as 1.23e+3." + }, + "digitsAfterDecimal": { + "default": "2", + "labels": ["Value display/formatting"], + "type": "integer", + "description": "Unless it's run in scientific mode (see the <code>sigFigs</code> option), dygraphs displays numbers with <code>digitsAfterDecimal</code> digits after the decimal point. Trailing zeros are not displayed, so with a value of 2 you'll get '0', '0.1', '0.12', '123.45' but not '123.456' (it will be rounded to '123.46'). Numbers with absolute value less than 0.1^digitsAfterDecimal (i.e. those which would show up as '0.00') will be displayed in scientific notation." + }, + "maxNumberWidth": { + "default": "6", + "labels": ["Value display/formatting"], + "type": "integer", + "description": "When displaying numbers in normal (not scientific) mode, large numbers will be displayed with many trailing zeros (e.g. 100000000 instead of 1e9). This can lead to unwieldy y-axis labels. If there are more than <code>maxNumberWidth</code> digits to the left of the decimal in a number, dygraphs will switch to scientific notation, even when not operating in scientific mode. If you'd like to see all those digits, set this to something large, like 20 or 30." + }, + "file": { + "default": "(set when constructed)", + "labels": ["Data"], + "type": "string (URL of CSV or CSV), GViz DataTable or 2D Array", + "description": "Sets the data being displayed in the chart. This can only be set when calling updateOptions; it cannot be set from the constructor. For a full description of valid data formats, see the <a href='http://dygraphs.com/data.html'>Data Formats</a> page." + }, + "timingName": { + "default": "null", + "labels": ["Debugging", "Deprecated"], + "type": "string", + "description": "Set this option to log timing information. The value of the option will be logged along with the timimg, so that you can distinguish multiple dygraphs on the same page." + }, + "showRangeSelector": { + "default": "false", + "labels": ["Range Selector"], + "type": "boolean", + "description": "Show or hide the range selector widget." + }, + "rangeSelectorHeight": { + "default": "40", + "labels": ["Range Selector"], + "type": "integer", + "description": "Height, in pixels, of the range selector widget. This option can only be specified at Dygraph creation time." + }, + "rangeSelectorPlotStrokeColor": { + "default": "#808FAB", + "labels": ["Range Selector"], + "type": "string", + "description": "The range selector mini plot stroke color. This can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"yellow\". You can also specify null or \"\" to turn off stroke." + }, + "rangeSelectorPlotFillColor": { + "default": "#A7B1C4", + "labels": ["Range Selector"], + "type": "string", + "description": "The range selector mini plot fill color. This can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"yellow\". You can also specify null or \"\" to turn off fill." + }, + "rangeSelectorPlotFillGradientColor": { + "default": "white", + "labels": ["Range Selector"], + "type": "string", + "description": "The top color for the range selector mini plot fill color gradient. This can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"rgba(255,100,200,42)\" or \"yellow\". You can also specify null or \"\" to disable the gradient and fill with one single color." + }, + "rangeSelectorBackgroundStrokeColor": { + "default": "gray", + "labels": ["Range Selector"], + "type": "string", + "description": "The color of the lines below and on both sides of the range selector mini plot. This can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"yellow\"." + }, + "rangeSelectorBackgroundLineWidth": { + "default": "1", + "labels": ["Range Selector"], + "type": "float", + "description": "The width of the lines below and on both sides of the range selector mini plot." + }, + "rangeSelectorPlotLineWidth": { + "default": "1.5", + "labels": ["Range Selector"], + "type": "float", + "description": "The width of the range selector mini plot line." + }, + "rangeSelectorForegroundStrokeColor": { + "default": "black", + "labels": ["Range Selector"], + "type": "string", + "description": "The color of the lines in the interactive layer of the range selector. This can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"yellow\"." + }, + "rangeSelectorForegroundLineWidth": { + "default": "1", + "labels": ["Range Selector"], + "type": "float", + "description": "The width the lines in the interactive layer of the range selector." + }, + "rangeSelectorAlpha": { + "default": "0.6", + "labels": ["Range Selector"], + "type": "float (0.0 - 1.0)", + "description": "The transparency of the veil that is drawn over the unselected portions of the range selector mini plot. A value of 0 represents full transparency and the unselected portions of the mini plot will appear as normal. A value of 1 represents full opacity and the unselected portions of the mini plot will be hidden." + }, + "showInRangeSelector": { + "default": "null", + "labels": ["Range Selector"], + "type": "boolean", + "description": "Mark this series for inclusion in the range selector. The mini plot curve will be an average of all such series. If this is not specified for any series, the default behavior is to average all the visible series. Setting it for one series will result in that series being charted alone in the range selector. Once it's set for a single series, it needs to be set for all series which should be included (regardless of visibility)." + }, + "animatedZooms": { + "default": "false", + "labels": ["Interactive Elements"], + "type": "boolean", + "description": "Set this option to animate the transition between zoom windows. Applies to programmatic and interactive zooms. Note that if you also set a drawCallback, it will be called several times on each zoom. If you set a zoomCallback, it will only be called after the animation is complete." + }, + "plotter": { + "default": "[DygraphCanvasRenderer.Plotters.fillPlotter, DygraphCanvasRenderer.Plotters.errorPlotter, DygraphCanvasRenderer.Plotters.linePlotter]", + "labels": ["Data Line display"], + "type": "array or function", + "description": "A function (or array of functions) which plot each data series on the chart. TODO(danvk): more details! May be set per-series." + }, + "axes": { + "default": "null", + "labels": ["Configuration"], + "type": "Object", + "description": "Defines per-axis options. Valid keys are 'x', 'y' and 'y2'. Only some options may be set on a per-axis basis. If an option may be set in this way, it will be noted on this page. See also documentation on <a href='http://dygraphs.com/per-axis.html'>per-series and per-axis options</a>." + }, + "series": { + "default": "null", + "labels": ["Series"], + "type": "Object", + "description": "Defines per-series options. Its keys match the y-axis label names, and the values are dictionaries themselves that contain options specific to that series." + }, + "plugins": { + "default": "[]", + "labels": ["Configuration"], + "type": "Array<plugin>", + "description": "Defines per-graph plugins. Useful for per-graph customization" + }, + "dataHandler": { + "default": "(depends on data)", + "labels": ["Data"], + "type": "Dygraph.DataHandler", + "description": "Custom DataHandler. This is an advanced customization. See http://bit.ly/151E7Aq." + } + }; // </JSON> + // NOTE: in addition to parsing as JS, this snippet is expected to be valid + // JSON. This assumption cannot be checked in JS, but it will be checked when + // documentation is generated by the generate-documentation.py script. For the + // most part, this just means that you should always use double quotes. + + // Do a quick sanity check on the options reference. + var warn = function warn(msg) { + if (window.console) window.console.warn(msg); + }; + var flds = ['type', 'default', 'description']; + var valid_cats = ['Annotations', 'Axis display', 'Chart labels', 'CSV parsing', 'Callbacks', 'Data', 'Data Line display', 'Data Series Colors', 'Error Bars', 'Grid', 'Interactive Elements', 'Range Selector', 'Legend', 'Overall display', 'Rolling Averages', 'Series', 'Value display/formatting', 'Zooming', 'Debugging', 'Configuration', 'Deprecated']; + var i; + var cats = {}; + for (i = 0; i < valid_cats.length; i++) cats[valid_cats[i]] = true; + + for (var k in OPTIONS_REFERENCE) { + if (!OPTIONS_REFERENCE.hasOwnProperty(k)) continue; + var op = OPTIONS_REFERENCE[k]; + for (i = 0; i < flds.length; i++) { + if (!op.hasOwnProperty(flds[i])) { + warn('Option ' + k + ' missing "' + flds[i] + '" property'); + } else if (typeof op[flds[i]] != 'string') { + warn(k + '.' + flds[i] + ' must be of type string'); + } + } + var labels = op.labels; + if (typeof labels !== 'object') { + warn('Option "' + k + '" is missing a "labels": [...] option'); + } else { + for (i = 0; i < labels.length; i++) { + if (!cats.hasOwnProperty(labels[i])) { + warn('Option "' + k + '" has label "' + labels[i] + '", which is invalid.'); + } + } + } + } + } +} + +exports['default'] = OPTIONS_REFERENCE; +module.exports = exports['default']; + +}).call(this,require('_process')) + +},{"_process":1}],15:[function(require,module,exports){ +(function (process){ +/** + * @license + * Copyright 2011 Dan Vanderkam (danvdk@gmail.com) + * MIT-licensed (http://opensource.org/licenses/MIT) + */ + +/** + * @fileoverview DygraphOptions is responsible for parsing and returning + * information about options. + */ + +// TODO: remove this jshint directive & fix the warnings. +/*jshint sub:true */ +"use strict"; + +Object.defineProperty(exports, '__esModule', { + value: true +}); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } + +var _dygraphUtils = require('./dygraph-utils'); + +var utils = _interopRequireWildcard(_dygraphUtils); + +var _dygraphDefaultAttrs = require('./dygraph-default-attrs'); + +var _dygraphDefaultAttrs2 = _interopRequireDefault(_dygraphDefaultAttrs); + +var _dygraphOptionsReference = require('./dygraph-options-reference'); + +var _dygraphOptionsReference2 = _interopRequireDefault(_dygraphOptionsReference); + +/* + * Interesting member variables: (REMOVING THIS LIST AS I CLOSURIZE) + * global_ - global attributes (common among all graphs, AIUI) + * user - attributes set by the user + * series_ - { seriesName -> { idx, yAxis, options }} + */ + +/** + * This parses attributes into an object that can be easily queried. + * + * It doesn't necessarily mean that all options are available, specifically + * if labels are not yet available, since those drive details of the per-series + * and per-axis options. + * + * @param {Dygraph} dygraph The chart to which these options belong. + * @constructor + */ +var DygraphOptions = function DygraphOptions(dygraph) { + /** + * The dygraph. + * @type {!Dygraph} + */ + this.dygraph_ = dygraph; + + /** + * Array of axis index to { series : [ series names ] , options : { axis-specific options. } + * @type {Array.<{series : Array.<string>, options : Object}>} @private + */ + this.yAxes_ = []; + + /** + * Contains x-axis specific options, which are stored in the options key. + * This matches the yAxes_ object structure (by being a dictionary with an + * options element) allowing for shared code. + * @type {options: Object} @private + */ + this.xAxis_ = {}; + this.series_ = {}; + + // Once these two objects are initialized, you can call get(); + this.global_ = this.dygraph_.attrs_; + this.user_ = this.dygraph_.user_attrs_ || {}; + + /** + * A list of series in columnar order. + * @type {Array.<string>} + */ + this.labels_ = []; + + this.highlightSeries_ = this.get("highlightSeriesOpts") || {}; + this.reparseSeries(); +}; + +/** + * Not optimal, but does the trick when you're only using two axes. + * If we move to more axes, this can just become a function. + * + * @type {Object.<number>} + * @private + */ +DygraphOptions.AXIS_STRING_MAPPINGS_ = { + 'y': 0, + 'Y': 0, + 'y1': 0, + 'Y1': 0, + 'y2': 1, + 'Y2': 1 +}; + +/** + * @param {string|number} axis + * @private + */ +DygraphOptions.axisToIndex_ = function (axis) { + if (typeof axis == "string") { + if (DygraphOptions.AXIS_STRING_MAPPINGS_.hasOwnProperty(axis)) { + return DygraphOptions.AXIS_STRING_MAPPINGS_[axis]; + } + throw "Unknown axis : " + axis; + } + if (typeof axis == "number") { + if (axis === 0 || axis === 1) { + return axis; + } + throw "Dygraphs only supports two y-axes, indexed from 0-1."; + } + if (axis) { + throw "Unknown axis : " + axis; + } + // No axis specification means axis 0. + return 0; +}; + +/** + * Reparses options that are all related to series. This typically occurs when + * options are either updated, or source data has been made available. + * + * TODO(konigsberg): The method name is kind of weak; fix. + */ +DygraphOptions.prototype.reparseSeries = function () { + var labels = this.get("labels"); + if (!labels) { + return; // -- can't do more for now, will parse after getting the labels. + } + + this.labels_ = labels.slice(1); + + this.yAxes_ = [{ series: [], options: {} }]; // Always one axis at least. + this.xAxis_ = { options: {} }; + this.series_ = {}; + + // Series are specified in the series element: + // + // { + // labels: [ "X", "foo", "bar" ], + // pointSize: 3, + // series : { + // foo : {}, // options for foo + // bar : {} // options for bar + // } + // } + // + // So, if series is found, it's expected to contain per-series data, otherwise set a + // default. + var seriesDict = this.user_.series || {}; + for (var idx = 0; idx < this.labels_.length; idx++) { + var seriesName = this.labels_[idx]; + var optionsForSeries = seriesDict[seriesName] || {}; + var yAxis = DygraphOptions.axisToIndex_(optionsForSeries["axis"]); + + this.series_[seriesName] = { + idx: idx, + yAxis: yAxis, + options: optionsForSeries }; + + if (!this.yAxes_[yAxis]) { + this.yAxes_[yAxis] = { series: [seriesName], options: {} }; + } else { + this.yAxes_[yAxis].series.push(seriesName); + } + } + + var axis_opts = this.user_["axes"] || {}; + utils.update(this.yAxes_[0].options, axis_opts["y"] || {}); + if (this.yAxes_.length > 1) { + utils.update(this.yAxes_[1].options, axis_opts["y2"] || {}); + } + utils.update(this.xAxis_.options, axis_opts["x"] || {}); + + // For "production" code, this gets removed by uglifyjs. + if (typeof process !== 'undefined') { + if ("development" != 'production') { + this.validateOptions_(); + } + } +}; + +/** + * Get a global value. + * + * @param {string} name the name of the option. + */ +DygraphOptions.prototype.get = function (name) { + var result = this.getGlobalUser_(name); + if (result !== null) { + return result; + } + return this.getGlobalDefault_(name); +}; + +DygraphOptions.prototype.getGlobalUser_ = function (name) { + if (this.user_.hasOwnProperty(name)) { + return this.user_[name]; + } + return null; +}; + +DygraphOptions.prototype.getGlobalDefault_ = function (name) { + if (this.global_.hasOwnProperty(name)) { + return this.global_[name]; + } + if (_dygraphDefaultAttrs2['default'].hasOwnProperty(name)) { + return _dygraphDefaultAttrs2['default'][name]; + } + return null; +}; + +/** + * Get a value for a specific axis. If there is no specific value for the axis, + * the global value is returned. + * + * @param {string} name the name of the option. + * @param {string|number} axis the axis to search. Can be the string representation + * ("y", "y2") or the axis number (0, 1). + */ +DygraphOptions.prototype.getForAxis = function (name, axis) { + var axisIdx; + var axisString; + + // Since axis can be a number or a string, straighten everything out here. + if (typeof axis == 'number') { + axisIdx = axis; + axisString = axisIdx === 0 ? "y" : "y2"; + } else { + if (axis == "y1") { + axis = "y"; + } // Standardize on 'y'. Is this bad? I think so. + if (axis == "y") { + axisIdx = 0; + } else if (axis == "y2") { + axisIdx = 1; + } else if (axis == "x") { + axisIdx = -1; // simply a placeholder for below. + } else { + throw "Unknown axis " + axis; + } + axisString = axis; + } + + var userAxis = axisIdx == -1 ? this.xAxis_ : this.yAxes_[axisIdx]; + + // Search the user-specified axis option first. + if (userAxis) { + // This condition could be removed if we always set up this.yAxes_ for y2. + var axisOptions = userAxis.options; + if (axisOptions.hasOwnProperty(name)) { + return axisOptions[name]; + } + } + + // User-specified global options second. + // But, hack, ignore globally-specified 'logscale' for 'x' axis declaration. + if (!(axis === 'x' && name === 'logscale')) { + var result = this.getGlobalUser_(name); + if (result !== null) { + return result; + } + } + // Default axis options third. + var defaultAxisOptions = _dygraphDefaultAttrs2['default'].axes[axisString]; + if (defaultAxisOptions.hasOwnProperty(name)) { + return defaultAxisOptions[name]; + } + + // Default global options last. + return this.getGlobalDefault_(name); +}; + +/** + * Get a value for a specific series. If there is no specific value for the series, + * the value for the axis is returned (and afterwards, the global value.) + * + * @param {string} name the name of the option. + * @param {string} series the series to search. + */ +DygraphOptions.prototype.getForSeries = function (name, series) { + // Honors indexes as series. + if (series === this.dygraph_.getHighlightSeries()) { + if (this.highlightSeries_.hasOwnProperty(name)) { + return this.highlightSeries_[name]; + } + } + + if (!this.series_.hasOwnProperty(series)) { + throw "Unknown series: " + series; + } + + var seriesObj = this.series_[series]; + var seriesOptions = seriesObj["options"]; + if (seriesOptions.hasOwnProperty(name)) { + return seriesOptions[name]; + } + + return this.getForAxis(name, seriesObj["yAxis"]); +}; + +/** + * Returns the number of y-axes on the chart. + * @return {number} the number of axes. + */ +DygraphOptions.prototype.numAxes = function () { + return this.yAxes_.length; +}; + +/** + * Return the y-axis for a given series, specified by name. + */ +DygraphOptions.prototype.axisForSeries = function (series) { + return this.series_[series].yAxis; +}; + +/** + * Returns the options for the specified axis. + */ +// TODO(konigsberg): this is y-axis specific. Support the x axis. +DygraphOptions.prototype.axisOptions = function (yAxis) { + return this.yAxes_[yAxis].options; +}; + +/** + * Return the series associated with an axis. + */ +DygraphOptions.prototype.seriesForAxis = function (yAxis) { + return this.yAxes_[yAxis].series; +}; + +/** + * Return the list of all series, in their columnar order. + */ +DygraphOptions.prototype.seriesNames = function () { + return this.labels_; +}; + +// For "production" code, this gets removed by uglifyjs. +if (typeof process !== 'undefined') { + if ("development" != 'production') { + + /** + * Validate all options. + * This requires OPTIONS_REFERENCE, which is only available in debug builds. + * @private + */ + DygraphOptions.prototype.validateOptions_ = function () { + if (typeof _dygraphOptionsReference2['default'] === 'undefined') { + throw 'Called validateOptions_ in prod build.'; + } + + var that = this; + var validateOption = function validateOption(optionName) { + if (!_dygraphOptionsReference2['default'][optionName]) { + that.warnInvalidOption_(optionName); + } + }; + + var optionsDicts = [this.xAxis_.options, this.yAxes_[0].options, this.yAxes_[1] && this.yAxes_[1].options, this.global_, this.user_, this.highlightSeries_]; + var names = this.seriesNames(); + for (var i = 0; i < names.length; i++) { + var name = names[i]; + if (this.series_.hasOwnProperty(name)) { + optionsDicts.push(this.series_[name].options); + } + } + for (var i = 0; i < optionsDicts.length; i++) { + var dict = optionsDicts[i]; + if (!dict) continue; + for (var optionName in dict) { + if (dict.hasOwnProperty(optionName)) { + validateOption(optionName); + } + } + } + }; + + var WARNINGS = {}; // Only show any particular warning once. + + /** + * Logs a warning about invalid options. + * TODO: make this throw for testing + * @private + */ + DygraphOptions.prototype.warnInvalidOption_ = function (optionName) { + if (!WARNINGS[optionName]) { + WARNINGS[optionName] = true; + var isSeries = this.labels_.indexOf(optionName) >= 0; + if (isSeries) { + console.warn('Use new-style per-series options (saw ' + optionName + ' as top-level options key). See http://bit.ly/1tceaJs'); + } else { + console.warn('Unknown option ' + optionName + ' (full list of options at dygraphs.com/options.html'); + } + throw "invalid option " + optionName; + } + }; + + // Reset list of previously-shown warnings. Used for testing. + DygraphOptions.resetWarnings_ = function () { + WARNINGS = {}; + }; + } +} + +exports['default'] = DygraphOptions; +module.exports = exports['default']; + +}).call(this,require('_process')) + +},{"./dygraph-default-attrs":10,"./dygraph-options-reference":14,"./dygraph-utils":17,"_process":1}],16:[function(require,module,exports){ +/** + * @license + * Copyright 2011 Dan Vanderkam (danvdk@gmail.com) + * MIT-licensed (http://opensource.org/licenses/MIT) + */ + +/** + * @fileoverview Description of this file. + * @author danvk@google.com (Dan Vanderkam) + * + * A ticker is a function with the following interface: + * + * function(a, b, pixels, options_view, dygraph, forced_values); + * -> [ { v: tick1_v, label: tick1_label[, label_v: label_v1] }, + * { v: tick2_v, label: tick2_label[, label_v: label_v2] }, + * ... + * ] + * + * The returned value is called a "tick list". + * + * Arguments + * --------- + * + * [a, b] is the range of the axis for which ticks are being generated. For a + * numeric axis, these will simply be numbers. For a date axis, these will be + * millis since epoch (convertable to Date objects using "new Date(a)" and "new + * Date(b)"). + * + * opts provides access to chart- and axis-specific options. It can be used to + * access number/date formatting code/options, check for a log scale, etc. + * + * pixels is the length of the axis in pixels. opts('pixelsPerLabel') is the + * minimum amount of space to be allotted to each label. For instance, if + * pixels=400 and opts('pixelsPerLabel')=40 then the ticker should return + * between zero and ten (400/40) ticks. + * + * dygraph is the Dygraph object for which an axis is being constructed. + * + * forced_values is used for secondary y-axes. The tick positions are typically + * set by the primary y-axis, so the secondary y-axis has no choice in where to + * put these. It simply has to generate labels for these data values. + * + * Tick lists + * ---------- + * Typically a tick will have both a grid/tick line and a label at one end of + * that line (at the bottom for an x-axis, at left or right for the y-axis). + * + * A tick may be missing one of these two components: + * - If "label_v" is specified instead of "v", then there will be no tick or + * gridline, just a label. + * - Similarly, if "label" is not specified, then there will be a gridline + * without a label. + * + * This flexibility is useful in a few situations: + * - For log scales, some of the tick lines may be too close to all have labels. + * - For date scales where years are being displayed, it is desirable to display + * tick marks at the beginnings of years but labels (e.g. "2006") in the + * middle of the years. + */ + +/*jshint sub:true */ +/*global Dygraph:false */ +"use strict"; + +Object.defineProperty(exports, '__esModule', { + value: true +}); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } + +var _dygraphUtils = require('./dygraph-utils'); + +var utils = _interopRequireWildcard(_dygraphUtils); + +/** @typedef {Array.<{v:number, label:string, label_v:(string|undefined)}>} */ +var TickList = undefined; // the ' = undefined' keeps jshint happy. + +/** @typedef {function( + * number, + * number, + * number, + * function(string):*, + * Dygraph=, + * Array.<number>= + * ): TickList} + */ +var Ticker = undefined; // the ' = undefined' keeps jshint happy. + +/** @type {Ticker} */ +var numericLinearTicks = function numericLinearTicks(a, b, pixels, opts, dygraph, vals) { + var nonLogscaleOpts = function nonLogscaleOpts(opt) { + if (opt === 'logscale') return false; + return opts(opt); + }; + return numericTicks(a, b, pixels, nonLogscaleOpts, dygraph, vals); +}; + +exports.numericLinearTicks = numericLinearTicks; +/** @type {Ticker} */ +var numericTicks = function numericTicks(a, b, pixels, opts, dygraph, vals) { + var pixels_per_tick = /** @type{number} */opts('pixelsPerLabel'); + var ticks = []; + var i, j, tickV, nTicks; + if (vals) { + for (i = 0; i < vals.length; i++) { + ticks.push({ v: vals[i] }); + } + } else { + // TODO(danvk): factor this log-scale block out into a separate function. + if (opts("logscale")) { + nTicks = Math.floor(pixels / pixels_per_tick); + var minIdx = utils.binarySearch(a, PREFERRED_LOG_TICK_VALUES, 1); + var maxIdx = utils.binarySearch(b, PREFERRED_LOG_TICK_VALUES, -1); + if (minIdx == -1) { + minIdx = 0; + } + if (maxIdx == -1) { + maxIdx = PREFERRED_LOG_TICK_VALUES.length - 1; + } + // Count the number of tick values would appear, if we can get at least + // nTicks / 4 accept them. + var lastDisplayed = null; + if (maxIdx - minIdx >= nTicks / 4) { + for (var idx = maxIdx; idx >= minIdx; idx--) { + var tickValue = PREFERRED_LOG_TICK_VALUES[idx]; + var pixel_coord = Math.log(tickValue / a) / Math.log(b / a) * pixels; + var tick = { v: tickValue }; + if (lastDisplayed === null) { + lastDisplayed = { + tickValue: tickValue, + pixel_coord: pixel_coord + }; + } else { + if (Math.abs(pixel_coord - lastDisplayed.pixel_coord) >= pixels_per_tick) { + lastDisplayed = { + tickValue: tickValue, + pixel_coord: pixel_coord + }; + } else { + tick.label = ""; + } + } + ticks.push(tick); + } + // Since we went in backwards order. + ticks.reverse(); + } + } + + // ticks.length won't be 0 if the log scale function finds values to insert. + if (ticks.length === 0) { + // Basic idea: + // Try labels every 1, 2, 5, 10, 20, 50, 100, etc. + // Calculate the resulting tick spacing (i.e. this.height_ / nTicks). + // The first spacing greater than pixelsPerYLabel is what we use. + // TODO(danvk): version that works on a log scale. + var kmg2 = opts("labelsKMG2"); + var mults, base; + if (kmg2) { + mults = [1, 2, 4, 8, 16, 32, 64, 128, 256]; + base = 16; + } else { + mults = [1, 2, 5, 10, 20, 50, 100]; + base = 10; + } + + // Get the maximum number of permitted ticks based on the + // graph's pixel size and pixels_per_tick setting. + var max_ticks = Math.ceil(pixels / pixels_per_tick); + + // Now calculate the data unit equivalent of this tick spacing. + // Use abs() since graphs may have a reversed Y axis. + var units_per_tick = Math.abs(b - a) / max_ticks; + + // Based on this, get a starting scale which is the largest + // integer power of the chosen base (10 or 16) that still remains + // below the requested pixels_per_tick spacing. + var base_power = Math.floor(Math.log(units_per_tick) / Math.log(base)); + var base_scale = Math.pow(base, base_power); + + // Now try multiples of the starting scale until we find one + // that results in tick marks spaced sufficiently far apart. + // The "mults" array should cover the range 1 .. base^2 to + // adjust for rounding and edge effects. + var scale, low_val, high_val, spacing; + for (j = 0; j < mults.length; j++) { + scale = base_scale * mults[j]; + low_val = Math.floor(a / scale) * scale; + high_val = Math.ceil(b / scale) * scale; + nTicks = Math.abs(high_val - low_val) / scale; + spacing = pixels / nTicks; + if (spacing > pixels_per_tick) break; + } + + // Construct the set of ticks. + // Allow reverse y-axis if it's explicitly requested. + if (low_val > high_val) scale *= -1; + for (i = 0; i <= nTicks; i++) { + tickV = low_val + i * scale; + ticks.push({ v: tickV }); + } + } + } + + var formatter = /**@type{AxisLabelFormatter}*/opts('axisLabelFormatter'); + + // Add labels to the ticks. + for (i = 0; i < ticks.length; i++) { + if (ticks[i].label !== undefined) continue; // Use current label. + // TODO(danvk): set granularity to something appropriate here. + ticks[i].label = formatter.call(dygraph, ticks[i].v, 0, opts, dygraph); + } + + return ticks; +}; + +exports.numericTicks = numericTicks; +/** @type {Ticker} */ +var dateTicker = function dateTicker(a, b, pixels, opts, dygraph, vals) { + var chosen = pickDateTickGranularity(a, b, pixels, opts); + + if (chosen >= 0) { + return getDateAxis(a, b, chosen, opts, dygraph); + } else { + // this can happen if self.width_ is zero. + return []; + } +}; + +exports.dateTicker = dateTicker; +// Time granularity enumeration +var Granularity = { + MILLISECONDLY: 0, + TWO_MILLISECONDLY: 1, + FIVE_MILLISECONDLY: 2, + TEN_MILLISECONDLY: 3, + FIFTY_MILLISECONDLY: 4, + HUNDRED_MILLISECONDLY: 5, + FIVE_HUNDRED_MILLISECONDLY: 6, + SECONDLY: 7, + TWO_SECONDLY: 8, + FIVE_SECONDLY: 9, + TEN_SECONDLY: 10, + THIRTY_SECONDLY: 11, + MINUTELY: 12, + TWO_MINUTELY: 13, + FIVE_MINUTELY: 14, + TEN_MINUTELY: 15, + THIRTY_MINUTELY: 16, + HOURLY: 17, + TWO_HOURLY: 18, + SIX_HOURLY: 19, + DAILY: 20, + TWO_DAILY: 21, + WEEKLY: 22, + MONTHLY: 23, + QUARTERLY: 24, + BIANNUAL: 25, + ANNUAL: 26, + DECADAL: 27, + CENTENNIAL: 28, + NUM_GRANULARITIES: 29 +}; + +exports.Granularity = Granularity; +// Date components enumeration (in the order of the arguments in Date) +// TODO: make this an @enum +var DateField = { + DATEFIELD_Y: 0, + DATEFIELD_M: 1, + DATEFIELD_D: 2, + DATEFIELD_HH: 3, + DATEFIELD_MM: 4, + DATEFIELD_SS: 5, + DATEFIELD_MS: 6, + NUM_DATEFIELDS: 7 +}; + +/** + * The value of datefield will start at an even multiple of "step", i.e. + * if datefield=SS and step=5 then the first tick will be on a multiple of 5s. + * + * For granularities <= HOURLY, ticks are generated every `spacing` ms. + * + * At coarser granularities, ticks are generated by incrementing `datefield` by + * `step`. In this case, the `spacing` value is only used to estimate the + * number of ticks. It should roughly correspond to the spacing between + * adjacent ticks. + * + * @type {Array.<{datefield:number, step:number, spacing:number}>} + */ +var TICK_PLACEMENT = []; +TICK_PLACEMENT[Granularity.MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 1, spacing: 1 }; +TICK_PLACEMENT[Granularity.TWO_MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 2, spacing: 2 }; +TICK_PLACEMENT[Granularity.FIVE_MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 5, spacing: 5 }; +TICK_PLACEMENT[Granularity.TEN_MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 10, spacing: 10 }; +TICK_PLACEMENT[Granularity.FIFTY_MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 50, spacing: 50 }; +TICK_PLACEMENT[Granularity.HUNDRED_MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 100, spacing: 100 }; +TICK_PLACEMENT[Granularity.FIVE_HUNDRED_MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 500, spacing: 500 }; +TICK_PLACEMENT[Granularity.SECONDLY] = { datefield: DateField.DATEFIELD_SS, step: 1, spacing: 1000 * 1 }; +TICK_PLACEMENT[Granularity.TWO_SECONDLY] = { datefield: DateField.DATEFIELD_SS, step: 2, spacing: 1000 * 2 }; +TICK_PLACEMENT[Granularity.FIVE_SECONDLY] = { datefield: DateField.DATEFIELD_SS, step: 5, spacing: 1000 * 5 }; +TICK_PLACEMENT[Granularity.TEN_SECONDLY] = { datefield: DateField.DATEFIELD_SS, step: 10, spacing: 1000 * 10 }; +TICK_PLACEMENT[Granularity.THIRTY_SECONDLY] = { datefield: DateField.DATEFIELD_SS, step: 30, spacing: 1000 * 30 }; +TICK_PLACEMENT[Granularity.MINUTELY] = { datefield: DateField.DATEFIELD_MM, step: 1, spacing: 1000 * 60 }; +TICK_PLACEMENT[Granularity.TWO_MINUTELY] = { datefield: DateField.DATEFIELD_MM, step: 2, spacing: 1000 * 60 * 2 }; +TICK_PLACEMENT[Granularity.FIVE_MINUTELY] = { datefield: DateField.DATEFIELD_MM, step: 5, spacing: 1000 * 60 * 5 }; +TICK_PLACEMENT[Granularity.TEN_MINUTELY] = { datefield: DateField.DATEFIELD_MM, step: 10, spacing: 1000 * 60 * 10 }; +TICK_PLACEMENT[Granularity.THIRTY_MINUTELY] = { datefield: DateField.DATEFIELD_MM, step: 30, spacing: 1000 * 60 * 30 }; +TICK_PLACEMENT[Granularity.HOURLY] = { datefield: DateField.DATEFIELD_HH, step: 1, spacing: 1000 * 3600 }; +TICK_PLACEMENT[Granularity.TWO_HOURLY] = { datefield: DateField.DATEFIELD_HH, step: 2, spacing: 1000 * 3600 * 2 }; +TICK_PLACEMENT[Granularity.SIX_HOURLY] = { datefield: DateField.DATEFIELD_HH, step: 6, spacing: 1000 * 3600 * 6 }; +TICK_PLACEMENT[Granularity.DAILY] = { datefield: DateField.DATEFIELD_D, step: 1, spacing: 1000 * 86400 }; +TICK_PLACEMENT[Granularity.TWO_DAILY] = { datefield: DateField.DATEFIELD_D, step: 2, spacing: 1000 * 86400 * 2 }; +TICK_PLACEMENT[Granularity.WEEKLY] = { datefield: DateField.DATEFIELD_D, step: 7, spacing: 1000 * 604800 }; +TICK_PLACEMENT[Granularity.MONTHLY] = { datefield: DateField.DATEFIELD_M, step: 1, spacing: 1000 * 7200 * 365.2524 }; // 1e3 * 60 * 60 * 24 * 365.2524 / 12 +TICK_PLACEMENT[Granularity.QUARTERLY] = { datefield: DateField.DATEFIELD_M, step: 3, spacing: 1000 * 21600 * 365.2524 }; // 1e3 * 60 * 60 * 24 * 365.2524 / 4 +TICK_PLACEMENT[Granularity.BIANNUAL] = { datefield: DateField.DATEFIELD_M, step: 6, spacing: 1000 * 43200 * 365.2524 }; // 1e3 * 60 * 60 * 24 * 365.2524 / 2 +TICK_PLACEMENT[Granularity.ANNUAL] = { datefield: DateField.DATEFIELD_Y, step: 1, spacing: 1000 * 86400 * 365.2524 }; // 1e3 * 60 * 60 * 24 * 365.2524 * 1 +TICK_PLACEMENT[Granularity.DECADAL] = { datefield: DateField.DATEFIELD_Y, step: 10, spacing: 1000 * 864000 * 365.2524 }; // 1e3 * 60 * 60 * 24 * 365.2524 * 10 +TICK_PLACEMENT[Granularity.CENTENNIAL] = { datefield: DateField.DATEFIELD_Y, step: 100, spacing: 1000 * 8640000 * 365.2524 }; // 1e3 * 60 * 60 * 24 * 365.2524 * 100 + +/** + * This is a list of human-friendly values at which to show tick marks on a log + * scale. It is k * 10^n, where k=1..9 and n=-39..+39, so: + * ..., 1, 2, 3, 4, 5, ..., 9, 10, 20, 30, ..., 90, 100, 200, 300, ... + * NOTE: this assumes that utils.LOG_SCALE = 10. + * @type {Array.<number>} + */ +var PREFERRED_LOG_TICK_VALUES = (function () { + var vals = []; + for (var power = -39; power <= 39; power++) { + var range = Math.pow(10, power); + for (var mult = 1; mult <= 9; mult++) { + var val = range * mult; + vals.push(val); + } + } + return vals; +})(); + +/** + * Determine the correct granularity of ticks on a date axis. + * + * @param {number} a Left edge of the chart (ms) + * @param {number} b Right edge of the chart (ms) + * @param {number} pixels Size of the chart in the relevant dimension (width). + * @param {function(string):*} opts Function mapping from option name -> value. + * @return {number} The appropriate axis granularity for this chart. See the + * enumeration of possible values in dygraph-tickers.js. + */ +var pickDateTickGranularity = function pickDateTickGranularity(a, b, pixels, opts) { + var pixels_per_tick = /** @type{number} */opts('pixelsPerLabel'); + for (var i = 0; i < Granularity.NUM_GRANULARITIES; i++) { + var num_ticks = numDateTicks(a, b, i); + if (pixels / num_ticks >= pixels_per_tick) { + return i; + } + } + return -1; +}; + +/** + * Compute the number of ticks on a date axis for a given granularity. + * @param {number} start_time + * @param {number} end_time + * @param {number} granularity (one of the granularities enumerated above) + * @return {number} (Approximate) number of ticks that would result. + */ +var numDateTicks = function numDateTicks(start_time, end_time, granularity) { + var spacing = TICK_PLACEMENT[granularity].spacing; + return Math.round(1.0 * (end_time - start_time) / spacing); +}; + +/** + * Compute the positions and labels of ticks on a date axis for a given granularity. + * @param {number} start_time + * @param {number} end_time + * @param {number} granularity (one of the granularities enumerated above) + * @param {function(string):*} opts Function mapping from option name -> value. + * @param {Dygraph=} dg + * @return {!TickList} + */ +var getDateAxis = function getDateAxis(start_time, end_time, granularity, opts, dg) { + var formatter = /** @type{AxisLabelFormatter} */opts("axisLabelFormatter"); + var utc = opts("labelsUTC"); + var accessors = utc ? utils.DateAccessorsUTC : utils.DateAccessorsLocal; + + var datefield = TICK_PLACEMENT[granularity].datefield; + var step = TICK_PLACEMENT[granularity].step; + var spacing = TICK_PLACEMENT[granularity].spacing; + + // Choose a nice tick position before the initial instant. + // Currently, this code deals properly with the existent daily granularities: + // DAILY (with step of 1) and WEEKLY (with step of 7 but specially handled). + // Other daily granularities (say TWO_DAILY) should also be handled specially + // by setting the start_date_offset to 0. + var start_date = new Date(start_time); + var date_array = []; + date_array[DateField.DATEFIELD_Y] = accessors.getFullYear(start_date); + date_array[DateField.DATEFIELD_M] = accessors.getMonth(start_date); + date_array[DateField.DATEFIELD_D] = accessors.getDate(start_date); + date_array[DateField.DATEFIELD_HH] = accessors.getHours(start_date); + date_array[DateField.DATEFIELD_MM] = accessors.getMinutes(start_date); + date_array[DateField.DATEFIELD_SS] = accessors.getSeconds(start_date); + date_array[DateField.DATEFIELD_MS] = accessors.getMilliseconds(start_date); + + var start_date_offset = date_array[datefield] % step; + if (granularity == Granularity.WEEKLY) { + // This will put the ticks on Sundays. + start_date_offset = accessors.getDay(start_date); + } + + date_array[datefield] -= start_date_offset; + for (var df = datefield + 1; df < DateField.NUM_DATEFIELDS; df++) { + // The minimum value is 1 for the day of month, and 0 for all other fields. + date_array[df] = df === DateField.DATEFIELD_D ? 1 : 0; + } + + // Generate the ticks. + // For granularities not coarser than HOURLY we use the fact that: + // the number of milliseconds between ticks is constant + // and equal to the defined spacing. + // Otherwise we rely on the 'roll over' property of the Date functions: + // when some date field is set to a value outside of its logical range, + // the excess 'rolls over' the next (more significant) field. + // However, when using local time with DST transitions, + // there are dates that do not represent any time value at all + // (those in the hour skipped at the 'spring forward'), + // and the JavaScript engines usually return an equivalent value. + // Hence we have to check that the date is properly increased at each step, + // returning a date at a nice tick position. + var ticks = []; + var tick_date = accessors.makeDate.apply(null, date_array); + var tick_time = tick_date.getTime(); + if (granularity <= Granularity.HOURLY) { + if (tick_time < start_time) { + tick_time += spacing; + tick_date = new Date(tick_time); + } + while (tick_time <= end_time) { + ticks.push({ v: tick_time, + label: formatter.call(dg, tick_date, granularity, opts, dg) + }); + tick_time += spacing; + tick_date = new Date(tick_time); + } + } else { + if (tick_time < start_time) { + date_array[datefield] += step; + tick_date = accessors.makeDate.apply(null, date_array); + tick_time = tick_date.getTime(); + } + while (tick_time <= end_time) { + if (granularity >= Granularity.DAILY || accessors.getHours(tick_date) % step === 0) { + ticks.push({ v: tick_time, + label: formatter.call(dg, tick_date, granularity, opts, dg) + }); + } + date_array[datefield] += step; + tick_date = accessors.makeDate.apply(null, date_array); + tick_time = tick_date.getTime(); + } + } + return ticks; +}; +exports.getDateAxis = getDateAxis; + +},{"./dygraph-utils":17}],17:[function(require,module,exports){ +/** + * @license + * Copyright 2011 Dan Vanderkam (danvdk@gmail.com) + * MIT-licensed (http://opensource.org/licenses/MIT) + */ + +/** + * @fileoverview This file contains utility functions used by dygraphs. These + * are typically static (i.e. not related to any particular dygraph). Examples + * include date/time formatting functions, basic algorithms (e.g. binary + * search) and generic DOM-manipulation functions. + */ + +/*global Dygraph:false, Node:false */ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.removeEvent = removeEvent; +exports.cancelEvent = cancelEvent; +exports.hsvToRGB = hsvToRGB; +exports.findPos = findPos; +exports.pageX = pageX; +exports.pageY = pageY; +exports.dragGetX_ = dragGetX_; +exports.dragGetY_ = dragGetY_; +exports.isOK = isOK; +exports.isValidPoint = isValidPoint; +exports.floatFormat = floatFormat; +exports.zeropad = zeropad; +exports.hmsString_ = hmsString_; +exports.dateString_ = dateString_; +exports.round_ = round_; +exports.binarySearch = binarySearch; +exports.dateParser = dateParser; +exports.dateStrToMillis = dateStrToMillis; +exports.update = update; +exports.updateDeep = updateDeep; +exports.isArrayLike = isArrayLike; +exports.isDateLike = isDateLike; +exports.clone = clone; +exports.createCanvas = createCanvas; +exports.getContextPixelRatio = getContextPixelRatio; +exports.Iterator = Iterator; +exports.createIterator = createIterator; +exports.repeatAndCleanup = repeatAndCleanup; +exports.isPixelChangingOptionList = isPixelChangingOptionList; +exports.detectLineDelimiter = detectLineDelimiter; +exports.isNodeContainedBy = isNodeContainedBy; +exports.pow = pow; +exports.toRGB_ = toRGB_; +exports.isCanvasSupported = isCanvasSupported; +exports.parseFloat_ = parseFloat_; +exports.numberValueFormatter = numberValueFormatter; +exports.numberAxisLabelFormatter = numberAxisLabelFormatter; +exports.dateAxisLabelFormatter = dateAxisLabelFormatter; +exports.dateValueFormatter = dateValueFormatter; + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj["default"] = obj; return newObj; } } + +var _dygraphTickers = require('./dygraph-tickers'); + +var DygraphTickers = _interopRequireWildcard(_dygraphTickers); + +var LOG_SCALE = 10; +exports.LOG_SCALE = LOG_SCALE; +var LN_TEN = Math.log(LOG_SCALE); + +exports.LN_TEN = LN_TEN; +/** + * @private + * @param {number} x + * @return {number} + */ +var log10 = function log10(x) { + return Math.log(x) / LN_TEN; +}; + +exports.log10 = log10; +/** + * @private + * @param {number} r0 + * @param {number} r1 + * @param {number} pct + * @return {number} + */ +var logRangeFraction = function logRangeFraction(r0, r1, pct) { + // Computing the inverse of toPercentXCoord. The function was arrived at with + // the following steps: + // + // Original calcuation: + // pct = (log(x) - log(xRange[0])) / (log(xRange[1]) - log(xRange[0]))); + // + // Multiply both sides by the right-side denominator. + // pct * (log(xRange[1] - log(xRange[0]))) = log(x) - log(xRange[0]) + // + // add log(xRange[0]) to both sides + // log(xRange[0]) + (pct * (log(xRange[1]) - log(xRange[0])) = log(x); + // + // Swap both sides of the equation, + // log(x) = log(xRange[0]) + (pct * (log(xRange[1]) - log(xRange[0])) + // + // Use both sides as the exponent in 10^exp and we're done. + // x = 10 ^ (log(xRange[0]) + (pct * (log(xRange[1]) - log(xRange[0]))) + + var logr0 = log10(r0); + var logr1 = log10(r1); + var exponent = logr0 + pct * (logr1 - logr0); + var value = Math.pow(LOG_SCALE, exponent); + return value; +}; + +exports.logRangeFraction = logRangeFraction; +/** A dotted line stroke pattern. */ +var DOTTED_LINE = [2, 2]; +exports.DOTTED_LINE = DOTTED_LINE; +/** A dashed line stroke pattern. */ +var DASHED_LINE = [7, 3]; +exports.DASHED_LINE = DASHED_LINE; +/** A dot dash stroke pattern. */ +var DOT_DASH_LINE = [7, 2, 2, 2]; + +exports.DOT_DASH_LINE = DOT_DASH_LINE; +// Directions for panning and zooming. Use bit operations when combined +// values are possible. +var HORIZONTAL = 1; +exports.HORIZONTAL = HORIZONTAL; +var VERTICAL = 2; + +exports.VERTICAL = VERTICAL; +/** + * Return the 2d context for a dygraph canvas. + * + * This method is only exposed for the sake of replacing the function in + * automated tests. + * + * @param {!HTMLCanvasElement} canvas + * @return {!CanvasRenderingContext2D} + * @private + */ +var getContext = function getContext(canvas) { + return (/** @type{!CanvasRenderingContext2D}*/canvas.getContext("2d") + ); +}; + +exports.getContext = getContext; +/** + * Add an event handler. + * @param {!Node} elem The element to add the event to. + * @param {string} type The type of the event, e.g. 'click' or 'mousemove'. + * @param {function(Event):(boolean|undefined)} fn The function to call + * on the event. The function takes one parameter: the event object. + * @private + */ +var addEvent = function addEvent(elem, type, fn) { + elem.addEventListener(type, fn, false); +}; + +exports.addEvent = addEvent; +/** + * Remove an event handler. + * @param {!Node} elem The element to remove the event from. + * @param {string} type The type of the event, e.g. 'click' or 'mousemove'. + * @param {function(Event):(boolean|undefined)} fn The function to call + * on the event. The function takes one parameter: the event object. + */ + +function removeEvent(elem, type, fn) { + elem.removeEventListener(type, fn, false); +} + +; + +/** + * Cancels further processing of an event. This is useful to prevent default + * browser actions, e.g. highlighting text on a double-click. + * Based on the article at + * http://www.switchonthecode.com/tutorials/javascript-tutorial-the-scroll-wheel + * @param {!Event} e The event whose normal behavior should be canceled. + * @private + */ + +function cancelEvent(e) { + e = e ? e : window.event; + if (e.stopPropagation) { + e.stopPropagation(); + } + if (e.preventDefault) { + e.preventDefault(); + } + e.cancelBubble = true; + e.cancel = true; + e.returnValue = false; + return false; +} + +; + +/** + * Convert hsv values to an rgb(r,g,b) string. Taken from MochiKit.Color. This + * is used to generate default series colors which are evenly spaced on the + * color wheel. + * @param { number } hue Range is 0.0-1.0. + * @param { number } saturation Range is 0.0-1.0. + * @param { number } value Range is 0.0-1.0. + * @return { string } "rgb(r,g,b)" where r, g and b range from 0-255. + * @private + */ + +function hsvToRGB(hue, saturation, value) { + var red; + var green; + var blue; + if (saturation === 0) { + red = value; + green = value; + blue = value; + } else { + var i = Math.floor(hue * 6); + var f = hue * 6 - i; + var p = value * (1 - saturation); + var q = value * (1 - saturation * f); + var t = value * (1 - saturation * (1 - f)); + switch (i) { + case 1: + red = q;green = value;blue = p;break; + case 2: + red = p;green = value;blue = t;break; + case 3: + red = p;green = q;blue = value;break; + case 4: + red = t;green = p;blue = value;break; + case 5: + red = value;green = p;blue = q;break; + case 6: // fall through + case 0: + red = value;green = t;blue = p;break; + } + } + red = Math.floor(255 * red + 0.5); + green = Math.floor(255 * green + 0.5); + blue = Math.floor(255 * blue + 0.5); + return 'rgb(' + red + ',' + green + ',' + blue + ')'; +} + +; + +/** + * Find the coordinates of an object relative to the top left of the page. + * + * @param {Node} obj + * @return {{x:number,y:number}} + * @private + */ + +function findPos(obj) { + var p = obj.getBoundingClientRect(), + w = window, + d = document.documentElement; + + return { + x: p.left + (w.pageXOffset || d.scrollLeft), + y: p.top + (w.pageYOffset || d.scrollTop) + }; +} + +; + +/** + * Returns the x-coordinate of the event in a coordinate system where the + * top-left corner of the page (not the window) is (0,0). + * Taken from MochiKit.Signal + * @param {!Event} e + * @return {number} + * @private + */ + +function pageX(e) { + return !e.pageX || e.pageX < 0 ? 0 : e.pageX; +} + +; + +/** + * Returns the y-coordinate of the event in a coordinate system where the + * top-left corner of the page (not the window) is (0,0). + * Taken from MochiKit.Signal + * @param {!Event} e + * @return {number} + * @private + */ + +function pageY(e) { + return !e.pageY || e.pageY < 0 ? 0 : e.pageY; +} + +; + +/** + * Converts page the x-coordinate of the event to pixel x-coordinates on the + * canvas (i.e. DOM Coords). + * @param {!Event} e Drag event. + * @param {!DygraphInteractionContext} context Interaction context object. + * @return {number} The amount by which the drag has moved to the right. + */ + +function dragGetX_(e, context) { + return pageX(e) - context.px; +} + +; + +/** + * Converts page the y-coordinate of the event to pixel y-coordinates on the + * canvas (i.e. DOM Coords). + * @param {!Event} e Drag event. + * @param {!DygraphInteractionContext} context Interaction context object. + * @return {number} The amount by which the drag has moved down. + */ + +function dragGetY_(e, context) { + return pageY(e) - context.py; +} + +; + +/** + * This returns true unless the parameter is 0, null, undefined or NaN. + * TODO(danvk): rename this function to something like 'isNonZeroNan'. + * + * @param {number} x The number to consider. + * @return {boolean} Whether the number is zero or NaN. + * @private + */ + +function isOK(x) { + return !!x && !isNaN(x); +} + +; + +/** + * @param {{x:?number,y:?number,yval:?number}} p The point to consider, valid + * points are {x, y} objects + * @param {boolean=} opt_allowNaNY Treat point with y=NaN as valid + * @return {boolean} Whether the point has numeric x and y. + * @private + */ + +function isValidPoint(p, opt_allowNaNY) { + if (!p) return false; // null or undefined object + if (p.yval === null) return false; // missing point + if (p.x === null || p.x === undefined) return false; + if (p.y === null || p.y === undefined) return false; + if (isNaN(p.x) || !opt_allowNaNY && isNaN(p.y)) return false; + return true; +} + +; + +/** + * Number formatting function which mimics the behavior of %g in printf, i.e. + * either exponential or fixed format (without trailing 0s) is used depending on + * the length of the generated string. The advantage of this format is that + * there is a predictable upper bound on the resulting string length, + * significant figures are not dropped, and normal numbers are not displayed in + * exponential notation. + * + * NOTE: JavaScript's native toPrecision() is NOT a drop-in replacement for %g. + * It creates strings which are too long for absolute values between 10^-4 and + * 10^-6, e.g. '0.00001' instead of '1e-5'. See tests/number-format.html for + * output examples. + * + * @param {number} x The number to format + * @param {number=} opt_precision The precision to use, default 2. + * @return {string} A string formatted like %g in printf. The max generated + * string length should be precision + 6 (e.g 1.123e+300). + */ + +function floatFormat(x, opt_precision) { + // Avoid invalid precision values; [1, 21] is the valid range. + var p = Math.min(Math.max(1, opt_precision || 2), 21); + + // This is deceptively simple. The actual algorithm comes from: + // + // Max allowed length = p + 4 + // where 4 comes from 'e+n' and '.'. + // + // Length of fixed format = 2 + y + p + // where 2 comes from '0.' and y = # of leading zeroes. + // + // Equating the two and solving for y yields y = 2, or 0.00xxxx which is + // 1.0e-3. + // + // Since the behavior of toPrecision() is identical for larger numbers, we + // don't have to worry about the other bound. + // + // Finally, the argument for toExponential() is the number of trailing digits, + // so we take off 1 for the value before the '.'. + return Math.abs(x) < 1.0e-3 && x !== 0.0 ? x.toExponential(p - 1) : x.toPrecision(p); +} + +; + +/** + * Converts '9' to '09' (useful for dates) + * @param {number} x + * @return {string} + * @private + */ + +function zeropad(x) { + if (x < 10) return "0" + x;else return "" + x; +} + +; + +/** + * Date accessors to get the parts of a calendar date (year, month, + * day, hour, minute, second and millisecond) according to local time, + * and factory method to call the Date constructor with an array of arguments. + */ +var DateAccessorsLocal = { + getFullYear: function getFullYear(d) { + return d.getFullYear(); + }, + getMonth: function getMonth(d) { + return d.getMonth(); + }, + getDate: function getDate(d) { + return d.getDate(); + }, + getHours: function getHours(d) { + return d.getHours(); + }, + getMinutes: function getMinutes(d) { + return d.getMinutes(); + }, + getSeconds: function getSeconds(d) { + return d.getSeconds(); + }, + getMilliseconds: function getMilliseconds(d) { + return d.getMilliseconds(); + }, + getDay: function getDay(d) { + return d.getDay(); + }, + makeDate: function makeDate(y, m, d, hh, mm, ss, ms) { + return new Date(y, m, d, hh, mm, ss, ms); + } +}; + +exports.DateAccessorsLocal = DateAccessorsLocal; +/** + * Date accessors to get the parts of a calendar date (year, month, + * day of month, hour, minute, second and millisecond) according to UTC time, + * and factory method to call the Date constructor with an array of arguments. + */ +var DateAccessorsUTC = { + getFullYear: function getFullYear(d) { + return d.getUTCFullYear(); + }, + getMonth: function getMonth(d) { + return d.getUTCMonth(); + }, + getDate: function getDate(d) { + return d.getUTCDate(); + }, + getHours: function getHours(d) { + return d.getUTCHours(); + }, + getMinutes: function getMinutes(d) { + return d.getUTCMinutes(); + }, + getSeconds: function getSeconds(d) { + return d.getUTCSeconds(); + }, + getMilliseconds: function getMilliseconds(d) { + return d.getUTCMilliseconds(); + }, + getDay: function getDay(d) { + return d.getUTCDay(); + }, + makeDate: function makeDate(y, m, d, hh, mm, ss, ms) { + return new Date(Date.UTC(y, m, d, hh, mm, ss, ms)); + } +}; + +exports.DateAccessorsUTC = DateAccessorsUTC; +/** + * Return a string version of the hours, minutes and seconds portion of a date. + * @param {number} hh The hours (from 0-23) + * @param {number} mm The minutes (from 0-59) + * @param {number} ss The seconds (from 0-59) + * @return {string} A time of the form "HH:MM" or "HH:MM:SS" + * @private + */ + +function hmsString_(hh, mm, ss, ms) { + var ret = zeropad(hh) + ":" + zeropad(mm); + if (ss) { + ret += ":" + zeropad(ss); + if (ms) { + var str = "" + ms; + ret += "." + ('000' + str).substring(str.length); + } + } + return ret; +} + +; + +/** + * Convert a JS date (millis since epoch) to a formatted string. + * @param {number} time The JavaScript time value (ms since epoch) + * @param {boolean} utc Whether output UTC or local time + * @return {string} A date of one of these forms: + * "YYYY/MM/DD", "YYYY/MM/DD HH:MM" or "YYYY/MM/DD HH:MM:SS" + * @private + */ + +function dateString_(time, utc) { + var accessors = utc ? DateAccessorsUTC : DateAccessorsLocal; + var date = new Date(time); + var y = accessors.getFullYear(date); + var m = accessors.getMonth(date); + var d = accessors.getDate(date); + var hh = accessors.getHours(date); + var mm = accessors.getMinutes(date); + var ss = accessors.getSeconds(date); + var ms = accessors.getMilliseconds(date); + // Get a year string: + var year = "" + y; + // Get a 0 padded month string + var month = zeropad(m + 1); //months are 0-offset, sigh + // Get a 0 padded day string + var day = zeropad(d); + var frac = hh * 3600 + mm * 60 + ss + 1e-3 * ms; + var ret = year + "/" + month + "/" + day; + if (frac) { + ret += " " + hmsString_(hh, mm, ss, ms); + } + return ret; +} + +; + +/** + * Round a number to the specified number of digits past the decimal point. + * @param {number} num The number to round + * @param {number} places The number of decimals to which to round + * @return {number} The rounded number + * @private + */ + +function round_(num, places) { + var shift = Math.pow(10, places); + return Math.round(num * shift) / shift; +} + +; + +/** + * Implementation of binary search over an array. + * Currently does not work when val is outside the range of arry's values. + * @param {number} val the value to search for + * @param {Array.<number>} arry is the value over which to search + * @param {number} abs If abs > 0, find the lowest entry greater than val + * If abs < 0, find the highest entry less than val. + * If abs == 0, find the entry that equals val. + * @param {number=} low The first index in arry to consider (optional) + * @param {number=} high The last index in arry to consider (optional) + * @return {number} Index of the element, or -1 if it isn't found. + * @private + */ + +function binarySearch(_x, _x2, _x3, _x4, _x5) { + var _again = true; + + _function: while (_again) { + var val = _x, + arry = _x2, + abs = _x3, + low = _x4, + high = _x5; + _again = false; + + if (low === null || low === undefined || high === null || high === undefined) { + low = 0; + high = arry.length - 1; + } + if (low > high) { + return -1; + } + if (abs === null || abs === undefined) { + abs = 0; + } + var validIndex = function validIndex(idx) { + return idx >= 0 && idx < arry.length; + }; + var mid = parseInt((low + high) / 2, 10); + var element = arry[mid]; + var idx; + if (element == val) { + return mid; + } else if (element > val) { + if (abs > 0) { + // Accept if element > val, but also if prior element < val. + idx = mid - 1; + if (validIndex(idx) && arry[idx] < val) { + return mid; + } + } + _x = val; + _x2 = arry; + _x3 = abs; + _x4 = low; + _x5 = mid - 1; + _again = true; + validIndex = mid = element = idx = undefined; + continue _function; + } else if (element < val) { + if (abs < 0) { + // Accept if element < val, but also if prior element > val. + idx = mid + 1; + if (validIndex(idx) && arry[idx] > val) { + return mid; + } + } + _x = val; + _x2 = arry; + _x3 = abs; + _x4 = mid + 1; + _x5 = high; + _again = true; + validIndex = mid = element = idx = undefined; + continue _function; + } + return -1; // can't actually happen, but makes closure compiler happy + } +} + +; + +/** + * Parses a date, returning the number of milliseconds since epoch. This can be + * passed in as an xValueParser in the Dygraph constructor. + * TODO(danvk): enumerate formats that this understands. + * + * @param {string} dateStr A date in a variety of possible string formats. + * @return {number} Milliseconds since epoch. + * @private + */ + +function dateParser(dateStr) { + var dateStrSlashed; + var d; + + // Let the system try the format first, with one caveat: + // YYYY-MM-DD[ HH:MM:SS] is interpreted as UTC by a variety of browsers. + // dygraphs displays dates in local time, so this will result in surprising + // inconsistencies. But if you specify "T" or "Z" (i.e. YYYY-MM-DDTHH:MM:SS), + // then you probably know what you're doing, so we'll let you go ahead. + // Issue: http://code.google.com/p/dygraphs/issues/detail?id=255 + if (dateStr.search("-") == -1 || dateStr.search("T") != -1 || dateStr.search("Z") != -1) { + d = dateStrToMillis(dateStr); + if (d && !isNaN(d)) return d; + } + + if (dateStr.search("-") != -1) { + // e.g. '2009-7-12' or '2009-07-12' + dateStrSlashed = dateStr.replace("-", "/", "g"); + while (dateStrSlashed.search("-") != -1) { + dateStrSlashed = dateStrSlashed.replace("-", "/"); + } + d = dateStrToMillis(dateStrSlashed); + } else if (dateStr.length == 8) { + // e.g. '20090712' + // TODO(danvk): remove support for this format. It's confusing. + dateStrSlashed = dateStr.substr(0, 4) + "/" + dateStr.substr(4, 2) + "/" + dateStr.substr(6, 2); + d = dateStrToMillis(dateStrSlashed); + } else { + // Any format that Date.parse will accept, e.g. "2009/07/12" or + // "2009/07/12 12:34:56" + d = dateStrToMillis(dateStr); + } + + if (!d || isNaN(d)) { + console.error("Couldn't parse " + dateStr + " as a date"); + } + return d; +} + +; + +/** + * This is identical to JavaScript's built-in Date.parse() method, except that + * it doesn't get replaced with an incompatible method by aggressive JS + * libraries like MooTools or Joomla. + * @param {string} str The date string, e.g. "2011/05/06" + * @return {number} millis since epoch + * @private + */ + +function dateStrToMillis(str) { + return new Date(str).getTime(); +} + +; + +// These functions are all based on MochiKit. +/** + * Copies all the properties from o to self. + * + * @param {!Object} self + * @param {!Object} o + * @return {!Object} + */ + +function update(self, o) { + if (typeof o != 'undefined' && o !== null) { + for (var k in o) { + if (o.hasOwnProperty(k)) { + self[k] = o[k]; + } + } + } + return self; +} + +; + +/** + * Copies all the properties from o to self. + * + * @param {!Object} self + * @param {!Object} o + * @return {!Object} + * @private + */ + +function updateDeep(self, o) { + // Taken from http://stackoverflow.com/questions/384286/javascript-isdom-how-do-you-check-if-a-javascript-object-is-a-dom-object + function isNode(o) { + return typeof Node === "object" ? o instanceof Node : typeof o === "object" && typeof o.nodeType === "number" && typeof o.nodeName === "string"; + } + + if (typeof o != 'undefined' && o !== null) { + for (var k in o) { + if (o.hasOwnProperty(k)) { + if (o[k] === null) { + self[k] = null; + } else if (isArrayLike(o[k])) { + self[k] = o[k].slice(); + } else if (isNode(o[k])) { + // DOM objects are shallowly-copied. + self[k] = o[k]; + } else if (typeof o[k] == 'object') { + if (typeof self[k] != 'object' || self[k] === null) { + self[k] = {}; + } + updateDeep(self[k], o[k]); + } else { + self[k] = o[k]; + } + } + } + } + return self; +} + +; + +/** + * @param {*} o + * @return {boolean} + * @private + */ + +function isArrayLike(o) { + var typ = typeof o; + if (typ != 'object' && !(typ == 'function' && typeof o.item == 'function') || o === null || typeof o.length != 'number' || o.nodeType === 3) { + return false; + } + return true; +} + +; + +/** + * @param {Object} o + * @return {boolean} + * @private + */ + +function isDateLike(o) { + if (typeof o != "object" || o === null || typeof o.getTime != 'function') { + return false; + } + return true; +} + +; + +/** + * Note: this only seems to work for arrays. + * @param {!Array} o + * @return {!Array} + * @private + */ + +function clone(o) { + // TODO(danvk): figure out how MochiKit's version works + var r = []; + for (var i = 0; i < o.length; i++) { + if (isArrayLike(o[i])) { + r.push(clone(o[i])); + } else { + r.push(o[i]); + } + } + return r; +} + +; + +/** + * Create a new canvas element. + * + * @return {!HTMLCanvasElement} + * @private + */ + +function createCanvas() { + return document.createElement('canvas'); +} + +; + +/** + * Returns the context's pixel ratio, which is the ratio between the device + * pixel ratio and the backing store ratio. Typically this is 1 for conventional + * displays, and > 1 for HiDPI displays (such as the Retina MBP). + * See http://www.html5rocks.com/en/tutorials/canvas/hidpi/ for more details. + * + * @param {!CanvasRenderingContext2D} context The canvas's 2d context. + * @return {number} The ratio of the device pixel ratio and the backing store + * ratio for the specified context. + */ + +function getContextPixelRatio(context) { + try { + var devicePixelRatio = window.devicePixelRatio; + var backingStoreRatio = context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1; + if (devicePixelRatio !== undefined) { + return devicePixelRatio / backingStoreRatio; + } else { + // At least devicePixelRatio must be defined for this ratio to make sense. + // We default backingStoreRatio to 1: this does not exist on some browsers + // (i.e. desktop Chrome). + return 1; + } + } catch (e) { + return 1; + } +} + +; + +/** + * TODO(danvk): use @template here when it's better supported for classes. + * @param {!Array} array + * @param {number} start + * @param {number} length + * @param {function(!Array,?):boolean=} predicate + * @constructor + */ + +function Iterator(array, start, length, predicate) { + start = start || 0; + length = length || array.length; + this.hasNext = true; // Use to identify if there's another element. + this.peek = null; // Use for look-ahead + this.start_ = start; + this.array_ = array; + this.predicate_ = predicate; + this.end_ = Math.min(array.length, start + length); + this.nextIdx_ = start - 1; // use -1 so initial advance works. + this.next(); // ignoring result. +} + +; + +/** + * @return {Object} + */ +Iterator.prototype.next = function () { + if (!this.hasNext) { + return null; + } + var obj = this.peek; + + var nextIdx = this.nextIdx_ + 1; + var found = false; + while (nextIdx < this.end_) { + if (!this.predicate_ || this.predicate_(this.array_, nextIdx)) { + this.peek = this.array_[nextIdx]; + found = true; + break; + } + nextIdx++; + } + this.nextIdx_ = nextIdx; + if (!found) { + this.hasNext = false; + this.peek = null; + } + return obj; +}; + +/** + * Returns a new iterator over array, between indexes start and + * start + length, and only returns entries that pass the accept function + * + * @param {!Array} array the array to iterate over. + * @param {number} start the first index to iterate over, 0 if absent. + * @param {number} length the number of elements in the array to iterate over. + * This, along with start, defines a slice of the array, and so length + * doesn't imply the number of elements in the iterator when accept doesn't + * always accept all values. array.length when absent. + * @param {function(?):boolean=} opt_predicate a function that takes + * parameters array and idx, which returns true when the element should be + * returned. If omitted, all elements are accepted. + * @private + */ + +function createIterator(array, start, length, opt_predicate) { + return new Iterator(array, start, length, opt_predicate); +} + +; + +// Shim layer with setTimeout fallback. +// From: http://paulirish.com/2011/requestanimationframe-for-smart-animating/ +// Should be called with the window context: +// Dygraph.requestAnimFrame.call(window, function() {}) +var requestAnimFrame = (function () { + return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) { + window.setTimeout(callback, 1000 / 60); + }; +})(); + +exports.requestAnimFrame = requestAnimFrame; +/** + * Call a function at most maxFrames times at an attempted interval of + * framePeriodInMillis, then call a cleanup function once. repeatFn is called + * once immediately, then at most (maxFrames - 1) times asynchronously. If + * maxFrames==1, then cleanup_fn() is also called synchronously. This function + * is used to sequence animation. + * @param {function(number)} repeatFn Called repeatedly -- takes the frame + * number (from 0 to maxFrames-1) as an argument. + * @param {number} maxFrames The max number of times to call repeatFn + * @param {number} framePeriodInMillis Max requested time between frames. + * @param {function()} cleanupFn A function to call after all repeatFn calls. + * @private + */ + +function repeatAndCleanup(repeatFn, maxFrames, framePeriodInMillis, cleanupFn) { + var frameNumber = 0; + var previousFrameNumber; + var startTime = new Date().getTime(); + repeatFn(frameNumber); + if (maxFrames == 1) { + cleanupFn(); + return; + } + var maxFrameArg = maxFrames - 1; + + (function loop() { + if (frameNumber >= maxFrames) return; + requestAnimFrame.call(window, function () { + // Determine which frame to draw based on the delay so far. Will skip + // frames if necessary. + var currentTime = new Date().getTime(); + var delayInMillis = currentTime - startTime; + previousFrameNumber = frameNumber; + frameNumber = Math.floor(delayInMillis / framePeriodInMillis); + var frameDelta = frameNumber - previousFrameNumber; + // If we predict that the subsequent repeatFn call will overshoot our + // total frame target, so our last call will cause a stutter, then jump to + // the last call immediately. If we're going to cause a stutter, better + // to do it faster than slower. + var predictOvershootStutter = frameNumber + frameDelta > maxFrameArg; + if (predictOvershootStutter || frameNumber >= maxFrameArg) { + repeatFn(maxFrameArg); // Ensure final call with maxFrameArg. + cleanupFn(); + } else { + if (frameDelta !== 0) { + // Don't call repeatFn with duplicate frames. + repeatFn(frameNumber); + } + loop(); + } + }); + })(); +} + +; + +// A whitelist of options that do not change pixel positions. +var pixelSafeOptions = { + 'annotationClickHandler': true, + 'annotationDblClickHandler': true, + 'annotationMouseOutHandler': true, + 'annotationMouseOverHandler': true, + 'axisLineColor': true, + 'axisLineWidth': true, + 'clickCallback': true, + 'drawCallback': true, + 'drawHighlightPointCallback': true, + 'drawPoints': true, + 'drawPointCallback': true, + 'drawGrid': true, + 'fillAlpha': true, + 'gridLineColor': true, + 'gridLineWidth': true, + 'hideOverlayOnMouseOut': true, + 'highlightCallback': true, + 'highlightCircleSize': true, + 'interactionModel': true, + 'labelsDiv': true, + 'labelsKMB': true, + 'labelsKMG2': true, + 'labelsSeparateLines': true, + 'labelsShowZeroValues': true, + 'legend': true, + 'panEdgeFraction': true, + 'pixelsPerYLabel': true, + 'pointClickCallback': true, + 'pointSize': true, + 'rangeSelectorPlotFillColor': true, + 'rangeSelectorPlotFillGradientColor': true, + 'rangeSelectorPlotStrokeColor': true, + 'rangeSelectorBackgroundStrokeColor': true, + 'rangeSelectorBackgroundLineWidth': true, + 'rangeSelectorPlotLineWidth': true, + 'rangeSelectorForegroundStrokeColor': true, + 'rangeSelectorForegroundLineWidth': true, + 'rangeSelectorAlpha': true, + 'showLabelsOnHighlight': true, + 'showRoller': true, + 'strokeWidth': true, + 'underlayCallback': true, + 'unhighlightCallback': true, + 'zoomCallback': true +}; + +/** + * This function will scan the option list and determine if they + * require us to recalculate the pixel positions of each point. + * TODO: move this into dygraph-options.js + * @param {!Array.<string>} labels a list of options to check. + * @param {!Object} attrs + * @return {boolean} true if the graph needs new points else false. + * @private + */ + +function isPixelChangingOptionList(labels, attrs) { + // Assume that we do not require new points. + // This will change to true if we actually do need new points. + + // Create a dictionary of series names for faster lookup. + // If there are no labels, then the dictionary stays empty. + var seriesNamesDictionary = {}; + if (labels) { + for (var i = 1; i < labels.length; i++) { + seriesNamesDictionary[labels[i]] = true; + } + } + + // Scan through a flat (i.e. non-nested) object of options. + // Returns true/false depending on whether new points are needed. + var scanFlatOptions = function scanFlatOptions(options) { + for (var property in options) { + if (options.hasOwnProperty(property) && !pixelSafeOptions[property]) { + return true; + } + } + return false; + }; + + // Iterate through the list of updated options. + for (var property in attrs) { + if (!attrs.hasOwnProperty(property)) continue; + + // Find out of this field is actually a series specific options list. + if (property == 'highlightSeriesOpts' || seriesNamesDictionary[property] && !attrs.series) { + // This property value is a list of options for this series. + if (scanFlatOptions(attrs[property])) return true; + } else if (property == 'series' || property == 'axes') { + // This is twice-nested options list. + var perSeries = attrs[property]; + for (var series in perSeries) { + if (perSeries.hasOwnProperty(series) && scanFlatOptions(perSeries[series])) { + return true; + } + } + } else { + // If this was not a series specific option list, check if it's a pixel + // changing property. + if (!pixelSafeOptions[property]) return true; + } + } + + return false; +} + +; + +var Circles = { + DEFAULT: function DEFAULT(g, name, ctx, canvasx, canvasy, color, radius) { + ctx.beginPath(); + ctx.fillStyle = color; + ctx.arc(canvasx, canvasy, radius, 0, 2 * Math.PI, false); + ctx.fill(); + } + // For more shapes, include extras/shapes.js +}; + +exports.Circles = Circles; +/** + * Determine whether |data| is delimited by CR, CRLF, LF, LFCR. + * @param {string} data + * @return {?string} the delimiter that was detected (or null on failure). + */ + +function detectLineDelimiter(data) { + for (var i = 0; i < data.length; i++) { + var code = data.charAt(i); + if (code === '\r') { + // Might actually be "\r\n". + if (i + 1 < data.length && data.charAt(i + 1) === '\n') { + return '\r\n'; + } + return code; + } + if (code === '\n') { + // Might actually be "\n\r". + if (i + 1 < data.length && data.charAt(i + 1) === '\r') { + return '\n\r'; + } + return code; + } + } + + return null; +} + +; + +/** + * Is one node contained by another? + * @param {Node} containee The contained node. + * @param {Node} container The container node. + * @return {boolean} Whether containee is inside (or equal to) container. + * @private + */ + +function isNodeContainedBy(containee, container) { + if (container === null || containee === null) { + return false; + } + var containeeNode = /** @type {Node} */containee; + while (containeeNode && containeeNode !== container) { + containeeNode = containeeNode.parentNode; + } + return containeeNode === container; +} + +; + +// This masks some numeric issues in older versions of Firefox, +// where 1.0/Math.pow(10,2) != Math.pow(10,-2). +/** @type {function(number,number):number} */ + +function pow(base, exp) { + if (exp < 0) { + return 1.0 / Math.pow(base, -exp); + } + return Math.pow(base, exp); +} + +; + +var RGBA_RE = /^rgba?\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})(?:,\s*([01](?:\.\d+)?))?\)$/; + +/** + * Helper for toRGB_ which parses strings of the form: + * rgb(123, 45, 67) + * rgba(123, 45, 67, 0.5) + * @return parsed {r,g,b,a?} tuple or null. + */ +function parseRGBA(rgbStr) { + var bits = RGBA_RE.exec(rgbStr); + if (!bits) return null; + var r = parseInt(bits[1], 10), + g = parseInt(bits[2], 10), + b = parseInt(bits[3], 10); + if (bits[4]) { + return { r: r, g: g, b: b, a: parseFloat(bits[4]) }; + } else { + return { r: r, g: g, b: b }; + } +} + +/** + * Converts any valid CSS color (hex, rgb(), named color) to an RGB tuple. + * + * @param {!string} colorStr Any valid CSS color string. + * @return {{r:number,g:number,b:number,a:number?}} Parsed RGB tuple. + * @private + */ + +function toRGB_(colorStr) { + // Strategy: First try to parse colorStr directly. This is fast & avoids DOM + // manipulation. If that fails (e.g. for named colors like 'red'), then + // create a hidden DOM element and parse its computed color. + var rgb = parseRGBA(colorStr); + if (rgb) return rgb; + + var div = document.createElement('div'); + div.style.backgroundColor = colorStr; + div.style.visibility = 'hidden'; + document.body.appendChild(div); + var rgbStr = window.getComputedStyle(div, null).backgroundColor; + document.body.removeChild(div); + return parseRGBA(rgbStr); +} + +; + +/** + * Checks whether the browser supports the <canvas> tag. + * @param {HTMLCanvasElement=} opt_canvasElement Pass a canvas element as an + * optimization if you have one. + * @return {boolean} Whether the browser supports canvas. + */ + +function isCanvasSupported(opt_canvasElement) { + try { + var canvas = opt_canvasElement || document.createElement("canvas"); + canvas.getContext("2d"); + } catch (e) { + return false; + } + return true; +} + +; + +/** + * Parses the value as a floating point number. This is like the parseFloat() + * built-in, but with a few differences: + * - the empty string is parsed as null, rather than NaN. + * - if the string cannot be parsed at all, an error is logged. + * If the string can't be parsed, this method returns null. + * @param {string} x The string to be parsed + * @param {number=} opt_line_no The line number from which the string comes. + * @param {string=} opt_line The text of the line from which the string comes. + */ + +function parseFloat_(x, opt_line_no, opt_line) { + var val = parseFloat(x); + if (!isNaN(val)) return val; + + // Try to figure out what happeend. + // If the value is the empty string, parse it as null. + if (/^ *$/.test(x)) return null; + + // If it was actually "NaN", return it as NaN. + if (/^ *nan *$/i.test(x)) return NaN; + + // Looks like a parsing error. + var msg = "Unable to parse '" + x + "' as a number"; + if (opt_line !== undefined && opt_line_no !== undefined) { + msg += " on line " + (1 + (opt_line_no || 0)) + " ('" + opt_line + "') of CSV."; + } + console.error(msg); + + return null; +} + +; + +// Label constants for the labelsKMB and labelsKMG2 options. +// (i.e. '100000' -> '100K') +var KMB_LABELS = ['K', 'M', 'B', 'T', 'Q']; +var KMG2_BIG_LABELS = ['k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']; +var KMG2_SMALL_LABELS = ['m', 'u', 'n', 'p', 'f', 'a', 'z', 'y']; + +/** + * @private + * Return a string version of a number. This respects the digitsAfterDecimal + * and maxNumberWidth options. + * @param {number} x The number to be formatted + * @param {Dygraph} opts An options view + */ + +function numberValueFormatter(x, opts) { + var sigFigs = opts('sigFigs'); + + if (sigFigs !== null) { + // User has opted for a fixed number of significant figures. + return floatFormat(x, sigFigs); + } + + var digits = opts('digitsAfterDecimal'); + var maxNumberWidth = opts('maxNumberWidth'); + + var kmb = opts('labelsKMB'); + var kmg2 = opts('labelsKMG2'); + + var label; + + // switch to scientific notation if we underflow or overflow fixed display. + if (x !== 0.0 && (Math.abs(x) >= Math.pow(10, maxNumberWidth) || Math.abs(x) < Math.pow(10, -digits))) { + label = x.toExponential(digits); + } else { + label = '' + round_(x, digits); + } + + if (kmb || kmg2) { + var k; + var k_labels = []; + var m_labels = []; + if (kmb) { + k = 1000; + k_labels = KMB_LABELS; + } + if (kmg2) { + if (kmb) console.warn("Setting both labelsKMB and labelsKMG2. Pick one!"); + k = 1024; + k_labels = KMG2_BIG_LABELS; + m_labels = KMG2_SMALL_LABELS; + } + + var absx = Math.abs(x); + var n = pow(k, k_labels.length); + for (var j = k_labels.length - 1; j >= 0; j--, n /= k) { + if (absx >= n) { + label = round_(x / n, digits) + k_labels[j]; + break; + } + } + if (kmg2) { + // TODO(danvk): clean up this logic. Why so different than kmb? + var x_parts = String(x.toExponential()).split('e-'); + if (x_parts.length === 2 && x_parts[1] >= 3 && x_parts[1] <= 24) { + if (x_parts[1] % 3 > 0) { + label = round_(x_parts[0] / pow(10, x_parts[1] % 3), digits); + } else { + label = Number(x_parts[0]).toFixed(2); + } + label += m_labels[Math.floor(x_parts[1] / 3) - 1]; + } + } + } + + return label; +} + +; + +/** + * variant for use as an axisLabelFormatter. + * @private + */ + +function numberAxisLabelFormatter(x, granularity, opts) { + return numberValueFormatter.call(this, x, opts); +} + +; + +/** + * @type {!Array.<string>} + * @private + * @constant + */ +var SHORT_MONTH_NAMES_ = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; + +/** + * Convert a JS date to a string appropriate to display on an axis that + * is displaying values at the stated granularity. This respects the + * labelsUTC option. + * @param {Date} date The date to format + * @param {number} granularity One of the Dygraph granularity constants + * @param {Dygraph} opts An options view + * @return {string} The date formatted as local time + * @private + */ + +function dateAxisLabelFormatter(date, granularity, opts) { + var utc = opts('labelsUTC'); + var accessors = utc ? DateAccessorsUTC : DateAccessorsLocal; + + var year = accessors.getFullYear(date), + month = accessors.getMonth(date), + day = accessors.getDate(date), + hours = accessors.getHours(date), + mins = accessors.getMinutes(date), + secs = accessors.getSeconds(date), + millis = accessors.getMilliseconds(date); + + if (granularity >= DygraphTickers.Granularity.DECADAL) { + return '' + year; + } else if (granularity >= DygraphTickers.Granularity.MONTHLY) { + return SHORT_MONTH_NAMES_[month] + ' ' + year; + } else { + var frac = hours * 3600 + mins * 60 + secs + 1e-3 * millis; + if (frac === 0 || granularity >= DygraphTickers.Granularity.DAILY) { + // e.g. '21 Jan' (%d%b) + return zeropad(day) + ' ' + SHORT_MONTH_NAMES_[month]; + } else if (granularity < DygraphTickers.Granularity.SECONDLY) { + // e.g. 40.310 (meaning 40 seconds and 310 milliseconds) + var str = "" + millis; + return zeropad(secs) + "." + ('000' + str).substring(str.length); + } else if (granularity > DygraphTickers.Granularity.MINUTELY) { + return hmsString_(hours, mins, secs, 0); + } else { + return hmsString_(hours, mins, secs, millis); + } + } +} + +; +// alias in case anyone is referencing the old method. +// Dygraph.dateAxisFormatter = Dygraph.dateAxisLabelFormatter; + +/** + * Return a string version of a JS date for a value label. This respects the + * labelsUTC option. + * @param {Date} date The date to be formatted + * @param {Dygraph} opts An options view + * @private + */ + +function dateValueFormatter(d, opts) { + return dateString_(d, opts('labelsUTC')); +} + +; + +},{"./dygraph-tickers":16}],18:[function(require,module,exports){ +(function (process){ +/** + * @license + * Copyright 2006 Dan Vanderkam (danvdk@gmail.com) + * MIT-licensed (http://opensource.org/licenses/MIT) + */ /** + * @fileoverview Creates an interactive, zoomable graph based on a CSV file or + * string. Dygraph can handle multiple series with or without error bars. The + * date/value ranges will be automatically set. Dygraph uses the + * <canvas> tag, so it only works in FF1.5+. + * @author danvdk@gmail.com (Dan Vanderkam) + + Usage: + <div id="graphdiv" style="width:800px; height:500px;"></div> + <script type="text/javascript"> + new Dygraph(document.getElementById("graphdiv"), + "datafile.csv", // CSV file with headers + { }); // options + </script> + + The CSV file is of the form + + Date,SeriesA,SeriesB,SeriesC + YYYYMMDD,A1,B1,C1 + YYYYMMDD,A2,B2,C2 + + If the 'errorBars' option is set in the constructor, the input should be of + the form + Date,SeriesA,SeriesB,... + YYYYMMDD,A1,sigmaA1,B1,sigmaB1,... + YYYYMMDD,A2,sigmaA2,B2,sigmaB2,... + + If the 'fractions' option is set, the input should be of the form: + + Date,SeriesA,SeriesB,... + YYYYMMDD,A1/B1,A2/B2,... + YYYYMMDD,A1/B1,A2/B2,... + + And error bars will be calculated automatically using a binomial distribution. + + For further documentation and examples, see http://dygraphs.com/ + */'use strict';Object.defineProperty(exports,'__esModule',{value:true});var _slicedToArray=(function(){function sliceIterator(arr,i){var _arr=[];var _n=true;var _d=false;var _e=undefined;try{for(var _i=arr[Symbol.iterator](),_s;!(_n = (_s = _i.next()).done);_n = true) {_arr.push(_s.value);if(i && _arr.length === i)break;}}catch(err) {_d = true;_e = err;}finally {try{if(!_n && _i['return'])_i['return']();}finally {if(_d)throw _e;}}return _arr;}return function(arr,i){if(Array.isArray(arr)){return arr;}else if(Symbol.iterator in Object(arr)){return sliceIterator(arr,i);}else {throw new TypeError('Invalid attempt to destructure non-iterable instance');}};})();function _interopRequireWildcard(obj){if(obj && obj.__esModule){return obj;}else {var newObj={};if(obj != null){for(var key in obj) {if(Object.prototype.hasOwnProperty.call(obj,key))newObj[key] = obj[key];}}newObj['default'] = obj;return newObj;}}function _interopRequireDefault(obj){return obj && obj.__esModule?obj:{'default':obj};}var _dygraphLayout=require('./dygraph-layout');var _dygraphLayout2=_interopRequireDefault(_dygraphLayout);var _dygraphCanvas=require('./dygraph-canvas');var _dygraphCanvas2=_interopRequireDefault(_dygraphCanvas);var _dygraphOptions=require('./dygraph-options');var _dygraphOptions2=_interopRequireDefault(_dygraphOptions);var _dygraphInteractionModel=require('./dygraph-interaction-model');var _dygraphInteractionModel2=_interopRequireDefault(_dygraphInteractionModel);var _dygraphTickers=require('./dygraph-tickers');var DygraphTickers=_interopRequireWildcard(_dygraphTickers);var _dygraphUtils=require('./dygraph-utils');var utils=_interopRequireWildcard(_dygraphUtils);var _dygraphDefaultAttrs=require('./dygraph-default-attrs');var _dygraphDefaultAttrs2=_interopRequireDefault(_dygraphDefaultAttrs);var _dygraphOptionsReference=require('./dygraph-options-reference');var _dygraphOptionsReference2=_interopRequireDefault(_dygraphOptionsReference);var _iframeTarp=require('./iframe-tarp');var _iframeTarp2=_interopRequireDefault(_iframeTarp);var _datahandlerDefault=require('./datahandler/default');var _datahandlerDefault2=_interopRequireDefault(_datahandlerDefault);var _datahandlerBarsError=require('./datahandler/bars-error');var _datahandlerBarsError2=_interopRequireDefault(_datahandlerBarsError);var _datahandlerBarsCustom=require('./datahandler/bars-custom');var _datahandlerBarsCustom2=_interopRequireDefault(_datahandlerBarsCustom);var _datahandlerDefaultFractions=require('./datahandler/default-fractions');var _datahandlerDefaultFractions2=_interopRequireDefault(_datahandlerDefaultFractions);var _datahandlerBarsFractions=require('./datahandler/bars-fractions');var _datahandlerBarsFractions2=_interopRequireDefault(_datahandlerBarsFractions);var _datahandlerBars=require('./datahandler/bars');var _datahandlerBars2=_interopRequireDefault(_datahandlerBars);var _pluginsAnnotations=require('./plugins/annotations');var _pluginsAnnotations2=_interopRequireDefault(_pluginsAnnotations);var _pluginsAxes=require('./plugins/axes');var _pluginsAxes2=_interopRequireDefault(_pluginsAxes);var _pluginsChartLabels=require('./plugins/chart-labels');var _pluginsChartLabels2=_interopRequireDefault(_pluginsChartLabels);var _pluginsGrid=require('./plugins/grid');var _pluginsGrid2=_interopRequireDefault(_pluginsGrid);var _pluginsLegend=require('./plugins/legend');var _pluginsLegend2=_interopRequireDefault(_pluginsLegend);var _pluginsRangeSelector=require('./plugins/range-selector');var _pluginsRangeSelector2=_interopRequireDefault(_pluginsRangeSelector);var _dygraphGviz=require('./dygraph-gviz');var _dygraphGviz2=_interopRequireDefault(_dygraphGviz);"use strict"; /** + * Creates an interactive, zoomable chart. + * + * @constructor + * @param {div | String} div A div or the id of a div into which to construct + * the chart. + * @param {String | Function} file A file containing CSV data or a function + * that returns this data. The most basic expected format for each line is + * "YYYY/MM/DD,val1,val2,...". For more information, see + * http://dygraphs.com/data.html. + * @param {Object} attrs Various other attributes, e.g. errorBars determines + * whether the input data contains error ranges. For a complete list of + * options, see http://dygraphs.com/options.html. + */var Dygraph=function Dygraph(div,data,opts){this.__init__(div,data,opts);};Dygraph.NAME = "Dygraph";Dygraph.VERSION = "2.1.0"; // Various default values +Dygraph.DEFAULT_ROLL_PERIOD = 1;Dygraph.DEFAULT_WIDTH = 480;Dygraph.DEFAULT_HEIGHT = 320; // For max 60 Hz. animation: +Dygraph.ANIMATION_STEPS = 12;Dygraph.ANIMATION_DURATION = 200; /** + * Standard plotters. These may be used by clients. + * Available plotters are: + * - Dygraph.Plotters.linePlotter: draws central lines (most common) + * - Dygraph.Plotters.errorPlotter: draws error bars + * - Dygraph.Plotters.fillPlotter: draws fills under lines (used with fillGraph) + * + * By default, the plotter is [fillPlotter, errorPlotter, linePlotter]. + * This causes all the lines to be drawn over all the fills/error bars. + */Dygraph.Plotters = _dygraphCanvas2['default']._Plotters; // Used for initializing annotation CSS rules only once. +Dygraph.addedAnnotationCSS = false; /** + * Initializes the Dygraph. This creates a new DIV and constructs the PlotKit + * and context <canvas> inside of it. See the constructor for details. + * on the parameters. + * @param {Element} div the Element to render the graph into. + * @param {string | Function} file Source data + * @param {Object} attrs Miscellaneous other options + * @private + */Dygraph.prototype.__init__ = function(div,file,attrs){this.is_initial_draw_ = true;this.readyFns_ = []; // Support two-argument constructor +if(attrs === null || attrs === undefined){attrs = {};}attrs = Dygraph.copyUserAttrs_(attrs);if(typeof div == 'string'){div = document.getElementById(div);}if(!div){throw new Error('Constructing dygraph with a non-existent div!');} // Copy the important bits into the object +// TODO(danvk): most of these should just stay in the attrs_ dictionary. +this.maindiv_ = div;this.file_ = file;this.rollPeriod_ = attrs.rollPeriod || Dygraph.DEFAULT_ROLL_PERIOD;this.previousVerticalX_ = -1;this.fractions_ = attrs.fractions || false;this.dateWindow_ = attrs.dateWindow || null;this.annotations_ = []; // Clear the div. This ensure that, if multiple dygraphs are passed the same +// div, then only one will be drawn. +div.innerHTML = ""; // For historical reasons, the 'width' and 'height' options trump all CSS +// rules _except_ for an explicit 'width' or 'height' on the div. +// As an added convenience, if the div has zero height (like <div></div> does +// without any styles), then we use a default height/width. +if(div.style.width === '' && attrs.width){div.style.width = attrs.width + "px";}if(div.style.height === '' && attrs.height){div.style.height = attrs.height + "px";}if(div.style.height === '' && div.clientHeight === 0){div.style.height = Dygraph.DEFAULT_HEIGHT + "px";if(div.style.width === ''){div.style.width = Dygraph.DEFAULT_WIDTH + "px";}} // These will be zero if the dygraph's div is hidden. In that case, +// use the user-specified attributes if present. If not, use zero +// and assume the user will call resize to fix things later. +this.width_ = div.clientWidth || attrs.width || 0;this.height_ = div.clientHeight || attrs.height || 0; // TODO(danvk): set fillGraph to be part of attrs_ here, not user_attrs_. +if(attrs.stackedGraph){attrs.fillGraph = true; // TODO(nikhilk): Add any other stackedGraph checks here. +} // DEPRECATION WARNING: All option processing should be moved from +// attrs_ and user_attrs_ to options_, which holds all this information. +// +// Dygraphs has many options, some of which interact with one another. +// To keep track of everything, we maintain two sets of options: +// +// this.user_attrs_ only options explicitly set by the user. +// this.attrs_ defaults, options derived from user_attrs_, data. +// +// Options are then accessed this.attr_('attr'), which first looks at +// user_attrs_ and then computed attrs_. This way Dygraphs can set intelligent +// defaults without overriding behavior that the user specifically asks for. +this.user_attrs_ = {};utils.update(this.user_attrs_,attrs); // This sequence ensures that Dygraph.DEFAULT_ATTRS is never modified. +this.attrs_ = {};utils.updateDeep(this.attrs_,_dygraphDefaultAttrs2['default']);this.boundaryIds_ = [];this.setIndexByName_ = {};this.datasetIndex_ = [];this.registeredEvents_ = [];this.eventListeners_ = {};this.attributes_ = new _dygraphOptions2['default'](this); // Create the containing DIV and other interactive elements +this.createInterface_(); // Activate plugins. +this.plugins_ = [];var plugins=Dygraph.PLUGINS.concat(this.getOption('plugins'));for(var i=0;i < plugins.length;i++) { // the plugins option may contain either plugin classes or instances. +// Plugin instances contain an activate method. +var Plugin=plugins[i]; // either a constructor or an instance. +var pluginInstance;if(typeof Plugin.activate !== 'undefined'){pluginInstance = Plugin;}else {pluginInstance = new Plugin();}var pluginDict={plugin:pluginInstance,events:{},options:{},pluginOptions:{}};var handlers=pluginInstance.activate(this);for(var eventName in handlers) {if(!handlers.hasOwnProperty(eventName))continue; // TODO(danvk): validate eventName. +pluginDict.events[eventName] = handlers[eventName];}this.plugins_.push(pluginDict);} // At this point, plugins can no longer register event handlers. +// Construct a map from event -> ordered list of [callback, plugin]. +for(var i=0;i < this.plugins_.length;i++) {var plugin_dict=this.plugins_[i];for(var eventName in plugin_dict.events) {if(!plugin_dict.events.hasOwnProperty(eventName))continue;var callback=plugin_dict.events[eventName];var pair=[plugin_dict.plugin,callback];if(!(eventName in this.eventListeners_)){this.eventListeners_[eventName] = [pair];}else {this.eventListeners_[eventName].push(pair);}}}this.createDragInterface_();this.start_();}; /** + * Triggers a cascade of events to the various plugins which are interested in them. + * Returns true if the "default behavior" should be prevented, i.e. if one + * of the event listeners called event.preventDefault(). + * @private + */Dygraph.prototype.cascadeEvents_ = function(name,extra_props){if(!(name in this.eventListeners_))return false; // QUESTION: can we use objects & prototypes to speed this up? +var e={dygraph:this,cancelable:false,defaultPrevented:false,preventDefault:function preventDefault(){if(!e.cancelable)throw "Cannot call preventDefault on non-cancelable event.";e.defaultPrevented = true;},propagationStopped:false,stopPropagation:function stopPropagation(){e.propagationStopped = true;}};utils.update(e,extra_props);var callback_plugin_pairs=this.eventListeners_[name];if(callback_plugin_pairs){for(var i=callback_plugin_pairs.length - 1;i >= 0;i--) {var plugin=callback_plugin_pairs[i][0];var callback=callback_plugin_pairs[i][1];callback.call(plugin,e);if(e.propagationStopped)break;}}return e.defaultPrevented;}; /** + * Fetch a plugin instance of a particular class. Only for testing. + * @private + * @param {!Class} type The type of the plugin. + * @return {Object} Instance of the plugin, or null if there is none. + */Dygraph.prototype.getPluginInstance_ = function(type){for(var i=0;i < this.plugins_.length;i++) {var p=this.plugins_[i];if(p.plugin instanceof type){return p.plugin;}}return null;}; /** + * Returns the zoomed status of the chart for one or both axes. + * + * Axis is an optional parameter. Can be set to 'x' or 'y'. + * + * The zoomed status for an axis is set whenever a user zooms using the mouse + * or when the dateWindow or valueRange are updated. Double-clicking or calling + * resetZoom() resets the zoom status for the chart. + */Dygraph.prototype.isZoomed = function(axis){var isZoomedX=!!this.dateWindow_;if(axis === 'x')return isZoomedX;var isZoomedY=this.axes_.map(function(axis){return !!axis.valueRange;}).indexOf(true) >= 0;if(axis === null || axis === undefined){return isZoomedX || isZoomedY;}if(axis === 'y')return isZoomedY;throw new Error('axis parameter is [' + axis + '] must be null, \'x\' or \'y\'.');}; /** + * Returns information about the Dygraph object, including its containing ID. + */Dygraph.prototype.toString = function(){var maindiv=this.maindiv_;var id=maindiv && maindiv.id?maindiv.id:maindiv;return "[Dygraph " + id + "]";}; /** + * @private + * Returns the value of an option. This may be set by the user (either in the + * constructor or by calling updateOptions) or by dygraphs, and may be set to a + * per-series value. + * @param {string} name The name of the option, e.g. 'rollPeriod'. + * @param {string} [seriesName] The name of the series to which the option + * will be applied. If no per-series value of this option is available, then + * the global value is returned. This is optional. + * @return { ... } The value of the option. + */Dygraph.prototype.attr_ = function(name,seriesName){ // For "production" code, this gets removed by uglifyjs. +if(typeof process !== 'undefined'){if("development" != 'production'){if(typeof _dygraphOptionsReference2['default'] === 'undefined'){console.error('Must include options reference JS for testing');}else if(!_dygraphOptionsReference2['default'].hasOwnProperty(name)){console.error('Dygraphs is using property ' + name + ', which has no ' + 'entry in the Dygraphs.OPTIONS_REFERENCE listing.'); // Only log this error once. +_dygraphOptionsReference2['default'][name] = true;}}}return seriesName?this.attributes_.getForSeries(name,seriesName):this.attributes_.get(name);}; /** + * Returns the current value for an option, as set in the constructor or via + * updateOptions. You may pass in an (optional) series name to get per-series + * values for the option. + * + * All values returned by this method should be considered immutable. If you + * modify them, there is no guarantee that the changes will be honored or that + * dygraphs will remain in a consistent state. If you want to modify an option, + * use updateOptions() instead. + * + * @param {string} name The name of the option (e.g. 'strokeWidth') + * @param {string=} opt_seriesName Series name to get per-series values. + * @return {*} The value of the option. + */Dygraph.prototype.getOption = function(name,opt_seriesName){return this.attr_(name,opt_seriesName);}; /** + * Like getOption(), but specifically returns a number. + * This is a convenience function for working with the Closure Compiler. + * @param {string} name The name of the option (e.g. 'strokeWidth') + * @param {string=} opt_seriesName Series name to get per-series values. + * @return {number} The value of the option. + * @private + */Dygraph.prototype.getNumericOption = function(name,opt_seriesName){return (/** @type{number} */this.getOption(name,opt_seriesName));}; /** + * Like getOption(), but specifically returns a string. + * This is a convenience function for working with the Closure Compiler. + * @param {string} name The name of the option (e.g. 'strokeWidth') + * @param {string=} opt_seriesName Series name to get per-series values. + * @return {string} The value of the option. + * @private + */Dygraph.prototype.getStringOption = function(name,opt_seriesName){return (/** @type{string} */this.getOption(name,opt_seriesName));}; /** + * Like getOption(), but specifically returns a boolean. + * This is a convenience function for working with the Closure Compiler. + * @param {string} name The name of the option (e.g. 'strokeWidth') + * @param {string=} opt_seriesName Series name to get per-series values. + * @return {boolean} The value of the option. + * @private + */Dygraph.prototype.getBooleanOption = function(name,opt_seriesName){return (/** @type{boolean} */this.getOption(name,opt_seriesName));}; /** + * Like getOption(), but specifically returns a function. + * This is a convenience function for working with the Closure Compiler. + * @param {string} name The name of the option (e.g. 'strokeWidth') + * @param {string=} opt_seriesName Series name to get per-series values. + * @return {function(...)} The value of the option. + * @private + */Dygraph.prototype.getFunctionOption = function(name,opt_seriesName){return (/** @type{function(...)} */this.getOption(name,opt_seriesName));};Dygraph.prototype.getOptionForAxis = function(name,axis){return this.attributes_.getForAxis(name,axis);}; /** + * @private + * @param {string} axis The name of the axis (i.e. 'x', 'y' or 'y2') + * @return { ... } A function mapping string -> option value + */Dygraph.prototype.optionsViewForAxis_ = function(axis){var self=this;return function(opt){var axis_opts=self.user_attrs_.axes;if(axis_opts && axis_opts[axis] && axis_opts[axis].hasOwnProperty(opt)){return axis_opts[axis][opt];} // I don't like that this is in a second spot. +if(axis === 'x' && opt === 'logscale'){ // return the default value. +// TODO(konigsberg): pull the default from a global default. +return false;} // user-specified attributes always trump defaults, even if they're less +// specific. +if(typeof self.user_attrs_[opt] != 'undefined'){return self.user_attrs_[opt];}axis_opts = self.attrs_.axes;if(axis_opts && axis_opts[axis] && axis_opts[axis].hasOwnProperty(opt)){return axis_opts[axis][opt];} // check old-style axis options +// TODO(danvk): add a deprecation warning if either of these match. +if(axis == 'y' && self.axes_[0].hasOwnProperty(opt)){return self.axes_[0][opt];}else if(axis == 'y2' && self.axes_[1].hasOwnProperty(opt)){return self.axes_[1][opt];}return self.attr_(opt);};}; /** + * Returns the current rolling period, as set by the user or an option. + * @return {number} The number of points in the rolling window + */Dygraph.prototype.rollPeriod = function(){return this.rollPeriod_;}; /** + * Returns the currently-visible x-range. This can be affected by zooming, + * panning or a call to updateOptions. + * Returns a two-element array: [left, right]. + * If the Dygraph has dates on the x-axis, these will be millis since epoch. + */Dygraph.prototype.xAxisRange = function(){return this.dateWindow_?this.dateWindow_:this.xAxisExtremes();}; /** + * Returns the lower- and upper-bound x-axis values of the data set. + */Dygraph.prototype.xAxisExtremes = function(){var pad=this.getNumericOption('xRangePad') / this.plotter_.area.w;if(this.numRows() === 0){return [0 - pad,1 + pad];}var left=this.rawData_[0][0];var right=this.rawData_[this.rawData_.length - 1][0];if(pad){ // Must keep this in sync with dygraph-layout _evaluateLimits() +var range=right - left;left -= range * pad;right += range * pad;}return [left,right];}; /** + * Returns the lower- and upper-bound y-axis values for each axis. These are + * the ranges you'll get if you double-click to zoom out or call resetZoom(). + * The return value is an array of [low, high] tuples, one for each y-axis. + */Dygraph.prototype.yAxisExtremes = function(){ // TODO(danvk): this is pretty inefficient +var packed=this.gatherDatasets_(this.rolledSeries_,null);var extremes=packed.extremes;var saveAxes=this.axes_;this.computeYAxisRanges_(extremes);var newAxes=this.axes_;this.axes_ = saveAxes;return newAxes.map(function(axis){return axis.extremeRange;});}; /** + * Returns the currently-visible y-range for an axis. This can be affected by + * zooming, panning or a call to updateOptions. Axis indices are zero-based. If + * called with no arguments, returns the range of the first axis. + * Returns a two-element array: [bottom, top]. + */Dygraph.prototype.yAxisRange = function(idx){if(typeof idx == "undefined")idx = 0;if(idx < 0 || idx >= this.axes_.length){return null;}var axis=this.axes_[idx];return [axis.computedValueRange[0],axis.computedValueRange[1]];}; /** + * Returns the currently-visible y-ranges for each axis. This can be affected by + * zooming, panning, calls to updateOptions, etc. + * Returns an array of [bottom, top] pairs, one for each y-axis. + */Dygraph.prototype.yAxisRanges = function(){var ret=[];for(var i=0;i < this.axes_.length;i++) {ret.push(this.yAxisRange(i));}return ret;}; // TODO(danvk): use these functions throughout dygraphs. +/** + * Convert from data coordinates to canvas/div X/Y coordinates. + * If specified, do this conversion for the coordinate system of a particular + * axis. Uses the first axis by default. + * Returns a two-element array: [X, Y] + * + * Note: use toDomXCoord instead of toDomCoords(x, null) and use toDomYCoord + * instead of toDomCoords(null, y, axis). + */Dygraph.prototype.toDomCoords = function(x,y,axis){return [this.toDomXCoord(x),this.toDomYCoord(y,axis)];}; /** + * Convert from data x coordinates to canvas/div X coordinate. + * If specified, do this conversion for the coordinate system of a particular + * axis. + * Returns a single value or null if x is null. + */Dygraph.prototype.toDomXCoord = function(x){if(x === null){return null;}var area=this.plotter_.area;var xRange=this.xAxisRange();return area.x + (x - xRange[0]) / (xRange[1] - xRange[0]) * area.w;}; /** + * Convert from data x coordinates to canvas/div Y coordinate and optional + * axis. Uses the first axis by default. + * + * returns a single value or null if y is null. + */Dygraph.prototype.toDomYCoord = function(y,axis){var pct=this.toPercentYCoord(y,axis);if(pct === null){return null;}var area=this.plotter_.area;return area.y + pct * area.h;}; /** + * Convert from canvas/div coords to data coordinates. + * If specified, do this conversion for the coordinate system of a particular + * axis. Uses the first axis by default. + * Returns a two-element array: [X, Y]. + * + * Note: use toDataXCoord instead of toDataCoords(x, null) and use toDataYCoord + * instead of toDataCoords(null, y, axis). + */Dygraph.prototype.toDataCoords = function(x,y,axis){return [this.toDataXCoord(x),this.toDataYCoord(y,axis)];}; /** + * Convert from canvas/div x coordinate to data coordinate. + * + * If x is null, this returns null. + */Dygraph.prototype.toDataXCoord = function(x){if(x === null){return null;}var area=this.plotter_.area;var xRange=this.xAxisRange();if(!this.attributes_.getForAxis("logscale",'x')){return xRange[0] + (x - area.x) / area.w * (xRange[1] - xRange[0]);}else {var pct=(x - area.x) / area.w;return utils.logRangeFraction(xRange[0],xRange[1],pct);}}; /** + * Convert from canvas/div y coord to value. + * + * If y is null, this returns null. + * if axis is null, this uses the first axis. + */Dygraph.prototype.toDataYCoord = function(y,axis){if(y === null){return null;}var area=this.plotter_.area;var yRange=this.yAxisRange(axis);if(typeof axis == "undefined")axis = 0;if(!this.attributes_.getForAxis("logscale",axis)){return yRange[0] + (area.y + area.h - y) / area.h * (yRange[1] - yRange[0]);}else { // Computing the inverse of toDomCoord. +var pct=(y - area.y) / area.h; // Note reversed yRange, y1 is on top with pct==0. +return utils.logRangeFraction(yRange[1],yRange[0],pct);}}; /** + * Converts a y for an axis to a percentage from the top to the + * bottom of the drawing area. + * + * If the coordinate represents a value visible on the canvas, then + * the value will be between 0 and 1, where 0 is the top of the canvas. + * However, this method will return values outside the range, as + * values can fall outside the canvas. + * + * If y is null, this returns null. + * if axis is null, this uses the first axis. + * + * @param {number} y The data y-coordinate. + * @param {number} [axis] The axis number on which the data coordinate lives. + * @return {number} A fraction in [0, 1] where 0 = the top edge. + */Dygraph.prototype.toPercentYCoord = function(y,axis){if(y === null){return null;}if(typeof axis == "undefined")axis = 0;var yRange=this.yAxisRange(axis);var pct;var logscale=this.attributes_.getForAxis("logscale",axis);if(logscale){var logr0=utils.log10(yRange[0]);var logr1=utils.log10(yRange[1]);pct = (logr1 - utils.log10(y)) / (logr1 - logr0);}else { // yRange[1] - y is unit distance from the bottom. +// yRange[1] - yRange[0] is the scale of the range. +// (yRange[1] - y) / (yRange[1] - yRange[0]) is the % from the bottom. +pct = (yRange[1] - y) / (yRange[1] - yRange[0]);}return pct;}; /** + * Converts an x value to a percentage from the left to the right of + * the drawing area. + * + * If the coordinate represents a value visible on the canvas, then + * the value will be between 0 and 1, where 0 is the left of the canvas. + * However, this method will return values outside the range, as + * values can fall outside the canvas. + * + * If x is null, this returns null. + * @param {number} x The data x-coordinate. + * @return {number} A fraction in [0, 1] where 0 = the left edge. + */Dygraph.prototype.toPercentXCoord = function(x){if(x === null){return null;}var xRange=this.xAxisRange();var pct;var logscale=this.attributes_.getForAxis("logscale",'x');if(logscale === true){ // logscale can be null so we test for true explicitly. +var logr0=utils.log10(xRange[0]);var logr1=utils.log10(xRange[1]);pct = (utils.log10(x) - logr0) / (logr1 - logr0);}else { // x - xRange[0] is unit distance from the left. +// xRange[1] - xRange[0] is the scale of the range. +// The full expression below is the % from the left. +pct = (x - xRange[0]) / (xRange[1] - xRange[0]);}return pct;}; /** + * Returns the number of columns (including the independent variable). + * @return {number} The number of columns. + */Dygraph.prototype.numColumns = function(){if(!this.rawData_)return 0;return this.rawData_[0]?this.rawData_[0].length:this.attr_("labels").length;}; /** + * Returns the number of rows (excluding any header/label row). + * @return {number} The number of rows, less any header. + */Dygraph.prototype.numRows = function(){if(!this.rawData_)return 0;return this.rawData_.length;}; /** + * Returns the value in the given row and column. If the row and column exceed + * the bounds on the data, returns null. Also returns null if the value is + * missing. + * @param {number} row The row number of the data (0-based). Row 0 is the + * first row of data, not a header row. + * @param {number} col The column number of the data (0-based) + * @return {number} The value in the specified cell or null if the row/col + * were out of range. + */Dygraph.prototype.getValue = function(row,col){if(row < 0 || row > this.rawData_.length)return null;if(col < 0 || col > this.rawData_[row].length)return null;return this.rawData_[row][col];}; /** + * Generates interface elements for the Dygraph: a containing div, a div to + * display the current point, and a textbox to adjust the rolling average + * period. Also creates the Renderer/Layout elements. + * @private + */Dygraph.prototype.createInterface_ = function(){ // Create the all-enclosing graph div +var enclosing=this.maindiv_;this.graphDiv = document.createElement("div"); // TODO(danvk): any other styles that are useful to set here? +this.graphDiv.style.textAlign = 'left'; // This is a CSS "reset" +this.graphDiv.style.position = 'relative';enclosing.appendChild(this.graphDiv); // Create the canvas for interactive parts of the chart. +this.canvas_ = utils.createCanvas();this.canvas_.style.position = "absolute"; // ... and for static parts of the chart. +this.hidden_ = this.createPlotKitCanvas_(this.canvas_);this.canvas_ctx_ = utils.getContext(this.canvas_);this.hidden_ctx_ = utils.getContext(this.hidden_);this.resizeElements_(); // The interactive parts of the graph are drawn on top of the chart. +this.graphDiv.appendChild(this.hidden_);this.graphDiv.appendChild(this.canvas_);this.mouseEventElement_ = this.createMouseEventElement_(); // Create the grapher +this.layout_ = new _dygraphLayout2['default'](this);var dygraph=this;this.mouseMoveHandler_ = function(e){dygraph.mouseMove_(e);};this.mouseOutHandler_ = function(e){ // The mouse has left the chart if: +// 1. e.target is inside the chart +// 2. e.relatedTarget is outside the chart +var target=e.target || e.fromElement;var relatedTarget=e.relatedTarget || e.toElement;if(utils.isNodeContainedBy(target,dygraph.graphDiv) && !utils.isNodeContainedBy(relatedTarget,dygraph.graphDiv)){dygraph.mouseOut_(e);}};this.addAndTrackEvent(window,'mouseout',this.mouseOutHandler_);this.addAndTrackEvent(this.mouseEventElement_,'mousemove',this.mouseMoveHandler_); // Don't recreate and register the resize handler on subsequent calls. +// This happens when the graph is resized. +if(!this.resizeHandler_){this.resizeHandler_ = function(e){dygraph.resize();}; // Update when the window is resized. +// TODO(danvk): drop frames depending on complexity of the chart. +this.addAndTrackEvent(window,'resize',this.resizeHandler_);}};Dygraph.prototype.resizeElements_ = function(){this.graphDiv.style.width = this.width_ + "px";this.graphDiv.style.height = this.height_ + "px";var pixelRatioOption=this.getNumericOption('pixelRatio');var canvasScale=pixelRatioOption || utils.getContextPixelRatio(this.canvas_ctx_);this.canvas_.width = this.width_ * canvasScale;this.canvas_.height = this.height_ * canvasScale;this.canvas_.style.width = this.width_ + "px"; // for IE +this.canvas_.style.height = this.height_ + "px"; // for IE +if(canvasScale !== 1){this.canvas_ctx_.scale(canvasScale,canvasScale);}var hiddenScale=pixelRatioOption || utils.getContextPixelRatio(this.hidden_ctx_);this.hidden_.width = this.width_ * hiddenScale;this.hidden_.height = this.height_ * hiddenScale;this.hidden_.style.width = this.width_ + "px"; // for IE +this.hidden_.style.height = this.height_ + "px"; // for IE +if(hiddenScale !== 1){this.hidden_ctx_.scale(hiddenScale,hiddenScale);}}; /** + * Detach DOM elements in the dygraph and null out all data references. + * Calling this when you're done with a dygraph can dramatically reduce memory + * usage. See, e.g., the tests/perf.html example. + */Dygraph.prototype.destroy = function(){this.canvas_ctx_.restore();this.hidden_ctx_.restore(); // Destroy any plugins, in the reverse order that they were registered. +for(var i=this.plugins_.length - 1;i >= 0;i--) {var p=this.plugins_.pop();if(p.plugin.destroy)p.plugin.destroy();}var removeRecursive=function removeRecursive(node){while(node.hasChildNodes()) {removeRecursive(node.firstChild);node.removeChild(node.firstChild);}};this.removeTrackedEvents_(); // remove mouse event handlers (This may not be necessary anymore) +utils.removeEvent(window,'mouseout',this.mouseOutHandler_);utils.removeEvent(this.mouseEventElement_,'mousemove',this.mouseMoveHandler_); // remove window handlers +utils.removeEvent(window,'resize',this.resizeHandler_);this.resizeHandler_ = null;removeRecursive(this.maindiv_);var nullOut=function nullOut(obj){for(var n in obj) {if(typeof obj[n] === 'object'){obj[n] = null;}}}; // These may not all be necessary, but it can't hurt... +nullOut(this.layout_);nullOut(this.plotter_);nullOut(this);}; /** + * Creates the canvas on which the chart will be drawn. Only the Renderer ever + * draws on this particular canvas. All Dygraph work (i.e. drawing hover dots + * or the zoom rectangles) is done on this.canvas_. + * @param {Object} canvas The Dygraph canvas over which to overlay the plot + * @return {Object} The newly-created canvas + * @private + */Dygraph.prototype.createPlotKitCanvas_ = function(canvas){var h=utils.createCanvas();h.style.position = "absolute"; // TODO(danvk): h should be offset from canvas. canvas needs to include +// some extra area to make it easier to zoom in on the far left and far +// right. h needs to be precisely the plot area, so that clipping occurs. +h.style.top = canvas.style.top;h.style.left = canvas.style.left;h.width = this.width_;h.height = this.height_;h.style.width = this.width_ + "px"; // for IE +h.style.height = this.height_ + "px"; // for IE +return h;}; /** + * Creates an overlay element used to handle mouse events. + * @return {Object} The mouse event element. + * @private + */Dygraph.prototype.createMouseEventElement_ = function(){return this.canvas_;}; /** + * Generate a set of distinct colors for the data series. This is done with a + * color wheel. Saturation/Value are customizable, and the hue is + * equally-spaced around the color wheel. If a custom set of colors is + * specified, that is used instead. + * @private + */Dygraph.prototype.setColors_ = function(){var labels=this.getLabels();var num=labels.length - 1;this.colors_ = [];this.colorsMap_ = {}; // These are used for when no custom colors are specified. +var sat=this.getNumericOption('colorSaturation') || 1.0;var val=this.getNumericOption('colorValue') || 0.5;var half=Math.ceil(num / 2);var colors=this.getOption('colors');var visibility=this.visibility();for(var i=0;i < num;i++) {if(!visibility[i]){continue;}var label=labels[i + 1];var colorStr=this.attributes_.getForSeries('color',label);if(!colorStr){if(colors){colorStr = colors[i % colors.length];}else { // alternate colors for high contrast. +var idx=i % 2?half + (i + 1) / 2:Math.ceil((i + 1) / 2);var hue=1.0 * idx / (1 + num);colorStr = utils.hsvToRGB(hue,sat,val);}}this.colors_.push(colorStr);this.colorsMap_[label] = colorStr;}}; /** + * Return the list of colors. This is either the list of colors passed in the + * attributes or the autogenerated list of rgb(r,g,b) strings. + * This does not return colors for invisible series. + * @return {Array.<string>} The list of colors. + */Dygraph.prototype.getColors = function(){return this.colors_;}; /** + * Returns a few attributes of a series, i.e. its color, its visibility, which + * axis it's assigned to, and its column in the original data. + * Returns null if the series does not exist. + * Otherwise, returns an object with column, visibility, color and axis properties. + * The "axis" property will be set to 1 for y1 and 2 for y2. + * The "column" property can be fed back into getValue(row, column) to get + * values for this series. + */Dygraph.prototype.getPropertiesForSeries = function(series_name){var idx=-1;var labels=this.getLabels();for(var i=1;i < labels.length;i++) {if(labels[i] == series_name){idx = i;break;}}if(idx == -1)return null;return {name:series_name,column:idx,visible:this.visibility()[idx - 1],color:this.colorsMap_[series_name],axis:1 + this.attributes_.axisForSeries(series_name)};}; /** + * Create the text box to adjust the averaging period + * @private + */Dygraph.prototype.createRollInterface_ = function(){var _this=this; // Create a roller if one doesn't exist already. +var roller=this.roller_;if(!roller){this.roller_ = roller = document.createElement("input");roller.type = "text";roller.style.display = "none";roller.className = 'dygraph-roller';this.graphDiv.appendChild(roller);}var display=this.getBooleanOption('showRoller')?'block':'none';var area=this.getArea();var textAttr={"top":area.y + area.h - 25 + "px","left":area.x + 1 + "px","display":display};roller.size = "2";roller.value = this.rollPeriod_;utils.update(roller.style,textAttr);roller.onchange = function(){return _this.adjustRoll(roller.value);};}; /** + * Set up all the mouse handlers needed to capture dragging behavior for zoom + * events. + * @private + */Dygraph.prototype.createDragInterface_ = function(){var context={ // Tracks whether the mouse is down right now +isZooming:false,isPanning:false, // is this drag part of a pan? +is2DPan:false, // if so, is that pan 1- or 2-dimensional? +dragStartX:null, // pixel coordinates +dragStartY:null, // pixel coordinates +dragEndX:null, // pixel coordinates +dragEndY:null, // pixel coordinates +dragDirection:null,prevEndX:null, // pixel coordinates +prevEndY:null, // pixel coordinates +prevDragDirection:null,cancelNextDblclick:false, // see comment in dygraph-interaction-model.js +// The value on the left side of the graph when a pan operation starts. +initialLeftmostDate:null, // The number of units each pixel spans. (This won't be valid for log +// scales) +xUnitsPerPixel:null, // TODO(danvk): update this comment +// The range in second/value units that the viewport encompasses during a +// panning operation. +dateRange:null, // Top-left corner of the canvas, in DOM coords +// TODO(konigsberg): Rename topLeftCanvasX, topLeftCanvasY. +px:0,py:0, // Values for use with panEdgeFraction, which limit how far outside the +// graph's data boundaries it can be panned. +boundedDates:null, // [minDate, maxDate] +boundedValues:null, // [[minValue, maxValue] ...] +// We cover iframes during mouse interactions. See comments in +// dygraph-utils.js for more info on why this is a good idea. +tarp:new _iframeTarp2['default'](), // contextB is the same thing as this context object but renamed. +initializeMouseDown:function initializeMouseDown(event,g,contextB){ // prevents mouse drags from selecting page text. +if(event.preventDefault){event.preventDefault(); // Firefox, Chrome, etc. +}else {event.returnValue = false; // IE +event.cancelBubble = true;}var canvasPos=utils.findPos(g.canvas_);contextB.px = canvasPos.x;contextB.py = canvasPos.y;contextB.dragStartX = utils.dragGetX_(event,contextB);contextB.dragStartY = utils.dragGetY_(event,contextB);contextB.cancelNextDblclick = false;contextB.tarp.cover();},destroy:function destroy(){var context=this;if(context.isZooming || context.isPanning){context.isZooming = false;context.dragStartX = null;context.dragStartY = null;}if(context.isPanning){context.isPanning = false;context.draggingDate = null;context.dateRange = null;for(var i=0;i < self.axes_.length;i++) {delete self.axes_[i].draggingValue;delete self.axes_[i].dragValueRange;}}context.tarp.uncover();}};var interactionModel=this.getOption("interactionModel"); // Self is the graph. +var self=this; // Function that binds the graph and context to the handler. +var bindHandler=function bindHandler(handler){return function(event){handler(event,self,context);};};for(var eventName in interactionModel) {if(!interactionModel.hasOwnProperty(eventName))continue;this.addAndTrackEvent(this.mouseEventElement_,eventName,bindHandler(interactionModel[eventName]));} // If the user releases the mouse button during a drag, but not over the +// canvas, then it doesn't count as a zooming action. +if(!interactionModel.willDestroyContextMyself){var mouseUpHandler=function mouseUpHandler(event){context.destroy();};this.addAndTrackEvent(document,'mouseup',mouseUpHandler);}}; /** + * Draw a gray zoom rectangle over the desired area of the canvas. Also clears + * up any previous zoom rectangles that were drawn. This could be optimized to + * avoid extra redrawing, but it's tricky to avoid interactions with the status + * dots. + * + * @param {number} direction the direction of the zoom rectangle. Acceptable + * values are utils.HORIZONTAL and utils.VERTICAL. + * @param {number} startX The X position where the drag started, in canvas + * coordinates. + * @param {number} endX The current X position of the drag, in canvas coords. + * @param {number} startY The Y position where the drag started, in canvas + * coordinates. + * @param {number} endY The current Y position of the drag, in canvas coords. + * @param {number} prevDirection the value of direction on the previous call to + * this function. Used to avoid excess redrawing + * @param {number} prevEndX The value of endX on the previous call to this + * function. Used to avoid excess redrawing + * @param {number} prevEndY The value of endY on the previous call to this + * function. Used to avoid excess redrawing + * @private + */Dygraph.prototype.drawZoomRect_ = function(direction,startX,endX,startY,endY,prevDirection,prevEndX,prevEndY){var ctx=this.canvas_ctx_; // Clean up from the previous rect if necessary +if(prevDirection == utils.HORIZONTAL){ctx.clearRect(Math.min(startX,prevEndX),this.layout_.getPlotArea().y,Math.abs(startX - prevEndX),this.layout_.getPlotArea().h);}else if(prevDirection == utils.VERTICAL){ctx.clearRect(this.layout_.getPlotArea().x,Math.min(startY,prevEndY),this.layout_.getPlotArea().w,Math.abs(startY - prevEndY));} // Draw a light-grey rectangle to show the new viewing area +if(direction == utils.HORIZONTAL){if(endX && startX){ctx.fillStyle = "rgba(128,128,128,0.33)";ctx.fillRect(Math.min(startX,endX),this.layout_.getPlotArea().y,Math.abs(endX - startX),this.layout_.getPlotArea().h);}}else if(direction == utils.VERTICAL){if(endY && startY){ctx.fillStyle = "rgba(128,128,128,0.33)";ctx.fillRect(this.layout_.getPlotArea().x,Math.min(startY,endY),this.layout_.getPlotArea().w,Math.abs(endY - startY));}}}; /** + * Clear the zoom rectangle (and perform no zoom). + * @private + */Dygraph.prototype.clearZoomRect_ = function(){this.currentZoomRectArgs_ = null;this.canvas_ctx_.clearRect(0,0,this.width_,this.height_);}; /** + * Zoom to something containing [lowX, highX]. These are pixel coordinates in + * the canvas. The exact zoom window may be slightly larger if there are no data + * points near lowX or highX. Don't confuse this function with doZoomXDates, + * which accepts dates that match the raw data. This function redraws the graph. + * + * @param {number} lowX The leftmost pixel value that should be visible. + * @param {number} highX The rightmost pixel value that should be visible. + * @private + */Dygraph.prototype.doZoomX_ = function(lowX,highX){this.currentZoomRectArgs_ = null; // Find the earliest and latest dates contained in this canvasx range. +// Convert the call to date ranges of the raw data. +var minDate=this.toDataXCoord(lowX);var maxDate=this.toDataXCoord(highX);this.doZoomXDates_(minDate,maxDate);}; /** + * Zoom to something containing [minDate, maxDate] values. Don't confuse this + * method with doZoomX which accepts pixel coordinates. This function redraws + * the graph. + * + * @param {number} minDate The minimum date that should be visible. + * @param {number} maxDate The maximum date that should be visible. + * @private + */Dygraph.prototype.doZoomXDates_ = function(minDate,maxDate){var _this2=this; // TODO(danvk): when xAxisRange is null (i.e. "fit to data", the animation +// can produce strange effects. Rather than the x-axis transitioning slowly +// between values, it can jerk around.) +var old_window=this.xAxisRange();var new_window=[minDate,maxDate];var zoomCallback=this.getFunctionOption('zoomCallback');this.doAnimatedZoom(old_window,new_window,null,null,function(){if(zoomCallback){zoomCallback.call(_this2,minDate,maxDate,_this2.yAxisRanges());}});}; /** + * Zoom to something containing [lowY, highY]. These are pixel coordinates in + * the canvas. This function redraws the graph. + * + * @param {number} lowY The topmost pixel value that should be visible. + * @param {number} highY The lowest pixel value that should be visible. + * @private + */Dygraph.prototype.doZoomY_ = function(lowY,highY){var _this3=this;this.currentZoomRectArgs_ = null; // Find the highest and lowest values in pixel range for each axis. +// Note that lowY (in pixels) corresponds to the max Value (in data coords). +// This is because pixels increase as you go down on the screen, whereas data +// coordinates increase as you go up the screen. +var oldValueRanges=this.yAxisRanges();var newValueRanges=[];for(var i=0;i < this.axes_.length;i++) {var hi=this.toDataYCoord(lowY,i);var low=this.toDataYCoord(highY,i);newValueRanges.push([low,hi]);}var zoomCallback=this.getFunctionOption('zoomCallback');this.doAnimatedZoom(null,null,oldValueRanges,newValueRanges,function(){if(zoomCallback){var _xAxisRange=_this3.xAxisRange();var _xAxisRange2=_slicedToArray(_xAxisRange,2);var minX=_xAxisRange2[0];var maxX=_xAxisRange2[1];zoomCallback.call(_this3,minX,maxX,_this3.yAxisRanges());}});}; /** + * Transition function to use in animations. Returns values between 0.0 + * (totally old values) and 1.0 (totally new values) for each frame. + * @private + */Dygraph.zoomAnimationFunction = function(frame,numFrames){var k=1.5;return (1.0 - Math.pow(k,-frame)) / (1.0 - Math.pow(k,-numFrames));}; /** + * Reset the zoom to the original view coordinates. This is the same as + * double-clicking on the graph. + */Dygraph.prototype.resetZoom = function(){var _this4=this;var dirtyX=this.isZoomed('x');var dirtyY=this.isZoomed('y');var dirty=dirtyX || dirtyY; // Clear any selection, since it's likely to be drawn in the wrong place. +this.clearSelection();if(!dirty)return; // Calculate extremes to avoid lack of padding on reset. +var _xAxisExtremes=this.xAxisExtremes();var _xAxisExtremes2=_slicedToArray(_xAxisExtremes,2);var minDate=_xAxisExtremes2[0];var maxDate=_xAxisExtremes2[1];var animatedZooms=this.getBooleanOption('animatedZooms');var zoomCallback=this.getFunctionOption('zoomCallback'); // TODO(danvk): merge this block w/ the code below. +// TODO(danvk): factor out a generic, public zoomTo method. +if(!animatedZooms){this.dateWindow_ = null;this.axes_.forEach(function(axis){if(axis.valueRange)delete axis.valueRange;});this.drawGraph_();if(zoomCallback){zoomCallback.call(this,minDate,maxDate,this.yAxisRanges());}return;}var oldWindow=null,newWindow=null,oldValueRanges=null,newValueRanges=null;if(dirtyX){oldWindow = this.xAxisRange();newWindow = [minDate,maxDate];}if(dirtyY){oldValueRanges = this.yAxisRanges();newValueRanges = this.yAxisExtremes();}this.doAnimatedZoom(oldWindow,newWindow,oldValueRanges,newValueRanges,function(){_this4.dateWindow_ = null;_this4.axes_.forEach(function(axis){if(axis.valueRange)delete axis.valueRange;});if(zoomCallback){zoomCallback.call(_this4,minDate,maxDate,_this4.yAxisRanges());}});}; /** + * Combined animation logic for all zoom functions. + * either the x parameters or y parameters may be null. + * @private + */Dygraph.prototype.doAnimatedZoom = function(oldXRange,newXRange,oldYRanges,newYRanges,callback){var _this5=this;var steps=this.getBooleanOption("animatedZooms")?Dygraph.ANIMATION_STEPS:1;var windows=[];var valueRanges=[];var step,frac;if(oldXRange !== null && newXRange !== null){for(step = 1;step <= steps;step++) {frac = Dygraph.zoomAnimationFunction(step,steps);windows[step - 1] = [oldXRange[0] * (1 - frac) + frac * newXRange[0],oldXRange[1] * (1 - frac) + frac * newXRange[1]];}}if(oldYRanges !== null && newYRanges !== null){for(step = 1;step <= steps;step++) {frac = Dygraph.zoomAnimationFunction(step,steps);var thisRange=[];for(var j=0;j < this.axes_.length;j++) {thisRange.push([oldYRanges[j][0] * (1 - frac) + frac * newYRanges[j][0],oldYRanges[j][1] * (1 - frac) + frac * newYRanges[j][1]]);}valueRanges[step - 1] = thisRange;}}utils.repeatAndCleanup(function(step){if(valueRanges.length){for(var i=0;i < _this5.axes_.length;i++) {var w=valueRanges[step][i];_this5.axes_[i].valueRange = [w[0],w[1]];}}if(windows.length){_this5.dateWindow_ = windows[step];}_this5.drawGraph_();},steps,Dygraph.ANIMATION_DURATION / steps,callback);}; /** + * Get the current graph's area object. + * + * Returns: {x, y, w, h} + */Dygraph.prototype.getArea = function(){return this.plotter_.area;}; /** + * Convert a mouse event to DOM coordinates relative to the graph origin. + * + * Returns a two-element array: [X, Y]. + */Dygraph.prototype.eventToDomCoords = function(event){if(event.offsetX && event.offsetY){return [event.offsetX,event.offsetY];}else {var eventElementPos=utils.findPos(this.mouseEventElement_);var canvasx=utils.pageX(event) - eventElementPos.x;var canvasy=utils.pageY(event) - eventElementPos.y;return [canvasx,canvasy];}}; /** + * Given a canvas X coordinate, find the closest row. + * @param {number} domX graph-relative DOM X coordinate + * Returns {number} row number. + * @private + */Dygraph.prototype.findClosestRow = function(domX){var minDistX=Infinity;var closestRow=-1;var sets=this.layout_.points;for(var i=0;i < sets.length;i++) {var points=sets[i];var len=points.length;for(var j=0;j < len;j++) {var point=points[j];if(!utils.isValidPoint(point,true))continue;var dist=Math.abs(point.canvasx - domX);if(dist < minDistX){minDistX = dist;closestRow = point.idx;}}}return closestRow;}; /** + * Given canvas X,Y coordinates, find the closest point. + * + * This finds the individual data point across all visible series + * that's closest to the supplied DOM coordinates using the standard + * Euclidean X,Y distance. + * + * @param {number} domX graph-relative DOM X coordinate + * @param {number} domY graph-relative DOM Y coordinate + * Returns: {row, seriesName, point} + * @private + */Dygraph.prototype.findClosestPoint = function(domX,domY){var minDist=Infinity;var dist,dx,dy,point,closestPoint,closestSeries,closestRow;for(var setIdx=this.layout_.points.length - 1;setIdx >= 0;--setIdx) {var points=this.layout_.points[setIdx];for(var i=0;i < points.length;++i) {point = points[i];if(!utils.isValidPoint(point))continue;dx = point.canvasx - domX;dy = point.canvasy - domY;dist = dx * dx + dy * dy;if(dist < minDist){minDist = dist;closestPoint = point;closestSeries = setIdx;closestRow = point.idx;}}}var name=this.layout_.setNames[closestSeries];return {row:closestRow,seriesName:name,point:closestPoint};}; /** + * Given canvas X,Y coordinates, find the touched area in a stacked graph. + * + * This first finds the X data point closest to the supplied DOM X coordinate, + * then finds the series which puts the Y coordinate on top of its filled area, + * using linear interpolation between adjacent point pairs. + * + * @param {number} domX graph-relative DOM X coordinate + * @param {number} domY graph-relative DOM Y coordinate + * Returns: {row, seriesName, point} + * @private + */Dygraph.prototype.findStackedPoint = function(domX,domY){var row=this.findClosestRow(domX);var closestPoint,closestSeries;for(var setIdx=0;setIdx < this.layout_.points.length;++setIdx) {var boundary=this.getLeftBoundary_(setIdx);var rowIdx=row - boundary;var points=this.layout_.points[setIdx];if(rowIdx >= points.length)continue;var p1=points[rowIdx];if(!utils.isValidPoint(p1))continue;var py=p1.canvasy;if(domX > p1.canvasx && rowIdx + 1 < points.length){ // interpolate series Y value using next point +var p2=points[rowIdx + 1];if(utils.isValidPoint(p2)){var dx=p2.canvasx - p1.canvasx;if(dx > 0){var r=(domX - p1.canvasx) / dx;py += r * (p2.canvasy - p1.canvasy);}}}else if(domX < p1.canvasx && rowIdx > 0){ // interpolate series Y value using previous point +var p0=points[rowIdx - 1];if(utils.isValidPoint(p0)){var dx=p1.canvasx - p0.canvasx;if(dx > 0){var r=(p1.canvasx - domX) / dx;py += r * (p0.canvasy - p1.canvasy);}}} // Stop if the point (domX, py) is above this series' upper edge +if(setIdx === 0 || py < domY){closestPoint = p1;closestSeries = setIdx;}}var name=this.layout_.setNames[closestSeries];return {row:row,seriesName:name,point:closestPoint};}; /** + * When the mouse moves in the canvas, display information about a nearby data + * point and draw dots over those points in the data series. This function + * takes care of cleanup of previously-drawn dots. + * @param {Object} event The mousemove event from the browser. + * @private + */Dygraph.prototype.mouseMove_ = function(event){ // This prevents JS errors when mousing over the canvas before data loads. +var points=this.layout_.points;if(points === undefined || points === null)return;var canvasCoords=this.eventToDomCoords(event);var canvasx=canvasCoords[0];var canvasy=canvasCoords[1];var highlightSeriesOpts=this.getOption("highlightSeriesOpts");var selectionChanged=false;if(highlightSeriesOpts && !this.isSeriesLocked()){var closest;if(this.getBooleanOption("stackedGraph")){closest = this.findStackedPoint(canvasx,canvasy);}else {closest = this.findClosestPoint(canvasx,canvasy);}selectionChanged = this.setSelection(closest.row,closest.seriesName);}else {var idx=this.findClosestRow(canvasx);selectionChanged = this.setSelection(idx);}var callback=this.getFunctionOption("highlightCallback");if(callback && selectionChanged){callback.call(this,event,this.lastx_,this.selPoints_,this.lastRow_,this.highlightSet_);}}; /** + * Fetch left offset from the specified set index or if not passed, the + * first defined boundaryIds record (see bug #236). + * @private + */Dygraph.prototype.getLeftBoundary_ = function(setIdx){if(this.boundaryIds_[setIdx]){return this.boundaryIds_[setIdx][0];}else {for(var i=0;i < this.boundaryIds_.length;i++) {if(this.boundaryIds_[i] !== undefined){return this.boundaryIds_[i][0];}}return 0;}};Dygraph.prototype.animateSelection_ = function(direction){var totalSteps=10;var millis=30;if(this.fadeLevel === undefined)this.fadeLevel = 0;if(this.animateId === undefined)this.animateId = 0;var start=this.fadeLevel;var steps=direction < 0?start:totalSteps - start;if(steps <= 0){if(this.fadeLevel){this.updateSelection_(1.0);}return;}var thisId=++this.animateId;var that=this;var cleanupIfClearing=function cleanupIfClearing(){ // if we haven't reached fadeLevel 0 in the max frame time, +// ensure that the clear happens and just go to 0 +if(that.fadeLevel !== 0 && direction < 0){that.fadeLevel = 0;that.clearSelection();}};utils.repeatAndCleanup(function(n){ // ignore simultaneous animations +if(that.animateId != thisId)return;that.fadeLevel += direction;if(that.fadeLevel === 0){that.clearSelection();}else {that.updateSelection_(that.fadeLevel / totalSteps);}},steps,millis,cleanupIfClearing);}; /** + * Draw dots over the selectied points in the data series. This function + * takes care of cleanup of previously-drawn dots. + * @private + */Dygraph.prototype.updateSelection_ = function(opt_animFraction){ /*var defaultPrevented = */this.cascadeEvents_('select',{selectedRow:this.lastRow_ === -1?undefined:this.lastRow_,selectedX:this.lastx_ === -1?undefined:this.lastx_,selectedPoints:this.selPoints_}); // TODO(danvk): use defaultPrevented here? +// Clear the previously drawn vertical, if there is one +var i;var ctx=this.canvas_ctx_;if(this.getOption('highlightSeriesOpts')){ctx.clearRect(0,0,this.width_,this.height_);var alpha=1.0 - this.getNumericOption('highlightSeriesBackgroundAlpha');var backgroundColor=utils.toRGB_(this.getOption('highlightSeriesBackgroundColor'));if(alpha){ // Activating background fade includes an animation effect for a gradual +// fade. TODO(klausw): make this independently configurable if it causes +// issues? Use a shared preference to control animations? +var animateBackgroundFade=true;if(animateBackgroundFade){if(opt_animFraction === undefined){ // start a new animation +this.animateSelection_(1);return;}alpha *= opt_animFraction;}ctx.fillStyle = 'rgba(' + backgroundColor.r + ',' + backgroundColor.g + ',' + backgroundColor.b + ',' + alpha + ')';ctx.fillRect(0,0,this.width_,this.height_);} // Redraw only the highlighted series in the interactive canvas (not the +// static plot canvas, which is where series are usually drawn). +this.plotter_._renderLineChart(this.highlightSet_,ctx);}else if(this.previousVerticalX_ >= 0){ // Determine the maximum highlight circle size. +var maxCircleSize=0;var labels=this.attr_('labels');for(i = 1;i < labels.length;i++) {var r=this.getNumericOption('highlightCircleSize',labels[i]);if(r > maxCircleSize)maxCircleSize = r;}var px=this.previousVerticalX_;ctx.clearRect(px - maxCircleSize - 1,0,2 * maxCircleSize + 2,this.height_);}if(this.selPoints_.length > 0){ // Draw colored circles over the center of each selected point +var canvasx=this.selPoints_[0].canvasx;ctx.save();for(i = 0;i < this.selPoints_.length;i++) {var pt=this.selPoints_[i];if(isNaN(pt.canvasy))continue;var circleSize=this.getNumericOption('highlightCircleSize',pt.name);var callback=this.getFunctionOption("drawHighlightPointCallback",pt.name);var color=this.plotter_.colors[pt.name];if(!callback){callback = utils.Circles.DEFAULT;}ctx.lineWidth = this.getNumericOption('strokeWidth',pt.name);ctx.strokeStyle = color;ctx.fillStyle = color;callback.call(this,this,pt.name,ctx,canvasx,pt.canvasy,color,circleSize,pt.idx);}ctx.restore();this.previousVerticalX_ = canvasx;}}; /** + * Manually set the selected points and display information about them in the + * legend. The selection can be cleared using clearSelection() and queried + * using getSelection(). + * + * To set a selected series but not a selected point, call setSelection with + * row=false and the selected series name. + * + * @param {number} row Row number that should be highlighted (i.e. appear with + * hover dots on the chart). + * @param {seriesName} optional series name to highlight that series with the + * the highlightSeriesOpts setting. + * @param { locked } optional If true, keep seriesName selected when mousing + * over the graph, disabling closest-series highlighting. Call clearSelection() + * to unlock it. + */Dygraph.prototype.setSelection = function(row,opt_seriesName,opt_locked){ // Extract the points we've selected +this.selPoints_ = [];var changed=false;if(row !== false && row >= 0){if(row != this.lastRow_)changed = true;this.lastRow_ = row;for(var setIdx=0;setIdx < this.layout_.points.length;++setIdx) {var points=this.layout_.points[setIdx]; // Check if the point at the appropriate index is the point we're looking +// for. If it is, just use it, otherwise search the array for a point +// in the proper place. +var setRow=row - this.getLeftBoundary_(setIdx);if(setRow >= 0 && setRow < points.length && points[setRow].idx == row){var point=points[setRow];if(point.yval !== null)this.selPoints_.push(point);}else {for(var pointIdx=0;pointIdx < points.length;++pointIdx) {var point=points[pointIdx];if(point.idx == row){if(point.yval !== null){this.selPoints_.push(point);}break;}}}}}else {if(this.lastRow_ >= 0)changed = true;this.lastRow_ = -1;}if(this.selPoints_.length){this.lastx_ = this.selPoints_[0].xval;}else {this.lastx_ = -1;}if(opt_seriesName !== undefined){if(this.highlightSet_ !== opt_seriesName)changed = true;this.highlightSet_ = opt_seriesName;}if(opt_locked !== undefined){this.lockedSet_ = opt_locked;}if(changed){this.updateSelection_(undefined);}return changed;}; /** + * The mouse has left the canvas. Clear out whatever artifacts remain + * @param {Object} event the mouseout event from the browser. + * @private + */Dygraph.prototype.mouseOut_ = function(event){if(this.getFunctionOption("unhighlightCallback")){this.getFunctionOption("unhighlightCallback").call(this,event);}if(this.getBooleanOption("hideOverlayOnMouseOut") && !this.lockedSet_){this.clearSelection();}}; /** + * Clears the current selection (i.e. points that were highlighted by moving + * the mouse over the chart). + */Dygraph.prototype.clearSelection = function(){this.cascadeEvents_('deselect',{});this.lockedSet_ = false; // Get rid of the overlay data +if(this.fadeLevel){this.animateSelection_(-1);return;}this.canvas_ctx_.clearRect(0,0,this.width_,this.height_);this.fadeLevel = 0;this.selPoints_ = [];this.lastx_ = -1;this.lastRow_ = -1;this.highlightSet_ = null;}; /** + * Returns the number of the currently selected row. To get data for this row, + * you can use the getValue method. + * @return {number} row number, or -1 if nothing is selected + */Dygraph.prototype.getSelection = function(){if(!this.selPoints_ || this.selPoints_.length < 1){return -1;}for(var setIdx=0;setIdx < this.layout_.points.length;setIdx++) {var points=this.layout_.points[setIdx];for(var row=0;row < points.length;row++) {if(points[row].x == this.selPoints_[0].x){return points[row].idx;}}}return -1;}; /** + * Returns the name of the currently-highlighted series. + * Only available when the highlightSeriesOpts option is in use. + */Dygraph.prototype.getHighlightSeries = function(){return this.highlightSet_;}; /** + * Returns true if the currently-highlighted series was locked + * via setSelection(..., seriesName, true). + */Dygraph.prototype.isSeriesLocked = function(){return this.lockedSet_;}; /** + * Fires when there's data available to be graphed. + * @param {string} data Raw CSV data to be plotted + * @private + */Dygraph.prototype.loadedEvent_ = function(data){this.rawData_ = this.parseCSV_(data);this.cascadeDataDidUpdateEvent_();this.predraw_();}; /** + * Add ticks on the x-axis representing years, months, quarters, weeks, or days + * @private + */Dygraph.prototype.addXTicks_ = function(){ // Determine the correct ticks scale on the x-axis: quarterly, monthly, ... +var range;if(this.dateWindow_){range = [this.dateWindow_[0],this.dateWindow_[1]];}else {range = this.xAxisExtremes();}var xAxisOptionsView=this.optionsViewForAxis_('x');var xTicks=xAxisOptionsView('ticker')(range[0],range[1],this.plotter_.area.w, // TODO(danvk): should be area.width +xAxisOptionsView,this); // var msg = 'ticker(' + range[0] + ', ' + range[1] + ', ' + this.width_ + ', ' + this.attr_('pixelsPerXLabel') + ') -> ' + JSON.stringify(xTicks); +// console.log(msg); +this.layout_.setXTicks(xTicks);}; /** + * Returns the correct handler class for the currently set options. + * @private + */Dygraph.prototype.getHandlerClass_ = function(){var handlerClass;if(this.attr_('dataHandler')){handlerClass = this.attr_('dataHandler');}else if(this.fractions_){if(this.getBooleanOption('errorBars')){handlerClass = _datahandlerBarsFractions2['default'];}else {handlerClass = _datahandlerDefaultFractions2['default'];}}else if(this.getBooleanOption('customBars')){handlerClass = _datahandlerBarsCustom2['default'];}else if(this.getBooleanOption('errorBars')){handlerClass = _datahandlerBarsError2['default'];}else {handlerClass = _datahandlerDefault2['default'];}return handlerClass;}; /** + * @private + * This function is called once when the chart's data is changed or the options + * dictionary is updated. It is _not_ called when the user pans or zooms. The + * idea is that values derived from the chart's data can be computed here, + * rather than every time the chart is drawn. This includes things like the + * number of axes, rolling averages, etc. + */Dygraph.prototype.predraw_ = function(){var start=new Date(); // Create the correct dataHandler +this.dataHandler_ = new (this.getHandlerClass_())();this.layout_.computePlotArea(); // TODO(danvk): move more computations out of drawGraph_ and into here. +this.computeYAxes_();if(!this.is_initial_draw_){this.canvas_ctx_.restore();this.hidden_ctx_.restore();}this.canvas_ctx_.save();this.hidden_ctx_.save(); // Create a new plotter. +this.plotter_ = new _dygraphCanvas2['default'](this,this.hidden_,this.hidden_ctx_,this.layout_); // The roller sits in the bottom left corner of the chart. We don't know where +// this will be until the options are available, so it's positioned here. +this.createRollInterface_();this.cascadeEvents_('predraw'); // Convert the raw data (a 2D array) into the internal format and compute +// rolling averages. +this.rolledSeries_ = [null]; // x-axis is the first series and it's special +for(var i=1;i < this.numColumns();i++) { // var logScale = this.attr_('logscale', i); // TODO(klausw): this looks wrong // konigsberg thinks so too. +var series=this.dataHandler_.extractSeries(this.rawData_,i,this.attributes_);if(this.rollPeriod_ > 1){series = this.dataHandler_.rollingAverage(series,this.rollPeriod_,this.attributes_);}this.rolledSeries_.push(series);} // If the data or options have changed, then we'd better redraw. +this.drawGraph_(); // This is used to determine whether to do various animations. +var end=new Date();this.drawingTimeMs_ = end - start;}; /** + * Point structure. + * + * xval_* and yval_* are the original unscaled data values, + * while x_* and y_* are scaled to the range (0.0-1.0) for plotting. + * yval_stacked is the cumulative Y value used for stacking graphs, + * and bottom/top/minus/plus are used for error bar graphs. + * + * @typedef {{ + * idx: number, + * name: string, + * x: ?number, + * xval: ?number, + * y_bottom: ?number, + * y: ?number, + * y_stacked: ?number, + * y_top: ?number, + * yval_minus: ?number, + * yval: ?number, + * yval_plus: ?number, + * yval_stacked + * }} + */Dygraph.PointType = undefined; /** + * Calculates point stacking for stackedGraph=true. + * + * For stacking purposes, interpolate or extend neighboring data across + * NaN values based on stackedGraphNaNFill settings. This is for display + * only, the underlying data value as shown in the legend remains NaN. + * + * @param {Array.<Dygraph.PointType>} points Point array for a single series. + * Updates each Point's yval_stacked property. + * @param {Array.<number>} cumulativeYval Accumulated top-of-graph stacked Y + * values for the series seen so far. Index is the row number. Updated + * based on the current series's values. + * @param {Array.<number>} seriesExtremes Min and max values, updated + * to reflect the stacked values. + * @param {string} fillMethod Interpolation method, one of 'all', 'inside', or + * 'none'. + * @private + */Dygraph.stackPoints_ = function(points,cumulativeYval,seriesExtremes,fillMethod){var lastXval=null;var prevPoint=null;var nextPoint=null;var nextPointIdx=-1; // Find the next stackable point starting from the given index. +var updateNextPoint=function updateNextPoint(idx){ // If we've previously found a non-NaN point and haven't gone past it yet, +// just use that. +if(nextPointIdx >= idx)return; // We haven't found a non-NaN point yet or have moved past it, +// look towards the right to find a non-NaN point. +for(var j=idx;j < points.length;++j) { // Clear out a previously-found point (if any) since it's no longer +// valid, we shouldn't use it for interpolation anymore. +nextPoint = null;if(!isNaN(points[j].yval) && points[j].yval !== null){nextPointIdx = j;nextPoint = points[j];break;}}};for(var i=0;i < points.length;++i) {var point=points[i];var xval=point.xval;if(cumulativeYval[xval] === undefined){cumulativeYval[xval] = 0;}var actualYval=point.yval;if(isNaN(actualYval) || actualYval === null){if(fillMethod == 'none'){actualYval = 0;}else { // Interpolate/extend for stacking purposes if possible. +updateNextPoint(i);if(prevPoint && nextPoint && fillMethod != 'none'){ // Use linear interpolation between prevPoint and nextPoint. +actualYval = prevPoint.yval + (nextPoint.yval - prevPoint.yval) * ((xval - prevPoint.xval) / (nextPoint.xval - prevPoint.xval));}else if(prevPoint && fillMethod == 'all'){actualYval = prevPoint.yval;}else if(nextPoint && fillMethod == 'all'){actualYval = nextPoint.yval;}else {actualYval = 0;}}}else {prevPoint = point;}var stackedYval=cumulativeYval[xval];if(lastXval != xval){ // If an x-value is repeated, we ignore the duplicates. +stackedYval += actualYval;cumulativeYval[xval] = stackedYval;}lastXval = xval;point.yval_stacked = stackedYval;if(stackedYval > seriesExtremes[1]){seriesExtremes[1] = stackedYval;}if(stackedYval < seriesExtremes[0]){seriesExtremes[0] = stackedYval;}}}; /** + * Loop over all fields and create datasets, calculating extreme y-values for + * each series and extreme x-indices as we go. + * + * dateWindow is passed in as an explicit parameter so that we can compute + * extreme values "speculatively", i.e. without actually setting state on the + * dygraph. + * + * @param {Array.<Array.<Array.<(number|Array<number>)>>} rolledSeries, where + * rolledSeries[seriesIndex][row] = raw point, where + * seriesIndex is the column number starting with 1, and + * rawPoint is [x,y] or [x, [y, err]] or [x, [y, yminus, yplus]]. + * @param {?Array.<number>} dateWindow [xmin, xmax] pair, or null. + * @return {{ + * points: Array.<Array.<Dygraph.PointType>>, + * seriesExtremes: Array.<Array.<number>>, + * boundaryIds: Array.<number>}} + * @private + */Dygraph.prototype.gatherDatasets_ = function(rolledSeries,dateWindow){var boundaryIds=[];var points=[];var cumulativeYval=[]; // For stacked series. +var extremes={}; // series name -> [low, high] +var seriesIdx,sampleIdx;var firstIdx,lastIdx;var axisIdx; // Loop over the fields (series). Go from the last to the first, +// because if they're stacked that's how we accumulate the values. +var num_series=rolledSeries.length - 1;var series;for(seriesIdx = num_series;seriesIdx >= 1;seriesIdx--) {if(!this.visibility()[seriesIdx - 1])continue; // Prune down to the desired range, if necessary (for zooming) +// Because there can be lines going to points outside of the visible area, +// we actually prune to visible points, plus one on either side. +if(dateWindow){series = rolledSeries[seriesIdx];var low=dateWindow[0];var high=dateWindow[1]; // TODO(danvk): do binary search instead of linear search. +// TODO(danvk): pass firstIdx and lastIdx directly to the renderer. +firstIdx = null;lastIdx = null;for(sampleIdx = 0;sampleIdx < series.length;sampleIdx++) {if(series[sampleIdx][0] >= low && firstIdx === null){firstIdx = sampleIdx;}if(series[sampleIdx][0] <= high){lastIdx = sampleIdx;}}if(firstIdx === null)firstIdx = 0;var correctedFirstIdx=firstIdx;var isInvalidValue=true;while(isInvalidValue && correctedFirstIdx > 0) {correctedFirstIdx--; // check if the y value is null. +isInvalidValue = series[correctedFirstIdx][1] === null;}if(lastIdx === null)lastIdx = series.length - 1;var correctedLastIdx=lastIdx;isInvalidValue = true;while(isInvalidValue && correctedLastIdx < series.length - 1) {correctedLastIdx++;isInvalidValue = series[correctedLastIdx][1] === null;}if(correctedFirstIdx !== firstIdx){firstIdx = correctedFirstIdx;}if(correctedLastIdx !== lastIdx){lastIdx = correctedLastIdx;}boundaryIds[seriesIdx - 1] = [firstIdx,lastIdx]; // .slice's end is exclusive, we want to include lastIdx. +series = series.slice(firstIdx,lastIdx + 1);}else {series = rolledSeries[seriesIdx];boundaryIds[seriesIdx - 1] = [0,series.length - 1];}var seriesName=this.attr_("labels")[seriesIdx];var seriesExtremes=this.dataHandler_.getExtremeYValues(series,dateWindow,this.getBooleanOption("stepPlot",seriesName));var seriesPoints=this.dataHandler_.seriesToPoints(series,seriesName,boundaryIds[seriesIdx - 1][0]);if(this.getBooleanOption("stackedGraph")){axisIdx = this.attributes_.axisForSeries(seriesName);if(cumulativeYval[axisIdx] === undefined){cumulativeYval[axisIdx] = [];}Dygraph.stackPoints_(seriesPoints,cumulativeYval[axisIdx],seriesExtremes,this.getBooleanOption("stackedGraphNaNFill"));}extremes[seriesName] = seriesExtremes;points[seriesIdx] = seriesPoints;}return {points:points,extremes:extremes,boundaryIds:boundaryIds};}; /** + * Update the graph with new data. This method is called when the viewing area + * has changed. If the underlying data or options have changed, predraw_ will + * be called before drawGraph_ is called. + * + * @private + */Dygraph.prototype.drawGraph_ = function(){var start=new Date(); // This is used to set the second parameter to drawCallback, below. +var is_initial_draw=this.is_initial_draw_;this.is_initial_draw_ = false;this.layout_.removeAllDatasets();this.setColors_();this.attrs_.pointSize = 0.5 * this.getNumericOption('highlightCircleSize');var packed=this.gatherDatasets_(this.rolledSeries_,this.dateWindow_);var points=packed.points;var extremes=packed.extremes;this.boundaryIds_ = packed.boundaryIds;this.setIndexByName_ = {};var labels=this.attr_("labels");var dataIdx=0;for(var i=1;i < points.length;i++) {if(!this.visibility()[i - 1])continue;this.layout_.addDataset(labels[i],points[i]);this.datasetIndex_[i] = dataIdx++;}for(var i=0;i < labels.length;i++) {this.setIndexByName_[labels[i]] = i;}this.computeYAxisRanges_(extremes);this.layout_.setYAxes(this.axes_);this.addXTicks_(); // Tell PlotKit to use this new data and render itself +this.layout_.evaluate();this.renderGraph_(is_initial_draw);if(this.getStringOption("timingName")){var end=new Date();console.log(this.getStringOption("timingName") + " - drawGraph: " + (end - start) + "ms");}}; /** + * This does the work of drawing the chart. It assumes that the layout and axis + * scales have already been set (e.g. by predraw_). + * + * @private + */Dygraph.prototype.renderGraph_ = function(is_initial_draw){this.cascadeEvents_('clearChart');this.plotter_.clear();var underlayCallback=this.getFunctionOption('underlayCallback');if(underlayCallback){ // NOTE: we pass the dygraph object to this callback twice to avoid breaking +// users who expect a deprecated form of this callback. +underlayCallback.call(this,this.hidden_ctx_,this.layout_.getPlotArea(),this,this);}var e={canvas:this.hidden_,drawingContext:this.hidden_ctx_};this.cascadeEvents_('willDrawChart',e);this.plotter_.render();this.cascadeEvents_('didDrawChart',e);this.lastRow_ = -1; // because plugins/legend.js clears the legend +// TODO(danvk): is this a performance bottleneck when panning? +// The interaction canvas should already be empty in that situation. +this.canvas_.getContext('2d').clearRect(0,0,this.width_,this.height_);var drawCallback=this.getFunctionOption("drawCallback");if(drawCallback !== null){drawCallback.call(this,this,is_initial_draw);}if(is_initial_draw){this.readyFired_ = true;while(this.readyFns_.length > 0) {var fn=this.readyFns_.pop();fn(this);}}}; /** + * @private + * Determine properties of the y-axes which are independent of the data + * currently being displayed. This includes things like the number of axes and + * the style of the axes. It does not include the range of each axis and its + * tick marks. + * This fills in this.axes_. + * axes_ = [ { options } ] + * indices are into the axes_ array. + */Dygraph.prototype.computeYAxes_ = function(){var axis,index,opts,v; // this.axes_ doesn't match this.attributes_.axes_.options. It's used for +// data computation as well as options storage. +// Go through once and add all the axes. +this.axes_ = [];for(axis = 0;axis < this.attributes_.numAxes();axis++) { // Add a new axis, making a copy of its per-axis options. +opts = {g:this};utils.update(opts,this.attributes_.axisOptions(axis));this.axes_[axis] = opts;}for(axis = 0;axis < this.axes_.length;axis++) {if(axis === 0){opts = this.optionsViewForAxis_('y' + (axis?'2':''));v = opts("valueRange");if(v)this.axes_[axis].valueRange = v;}else { // To keep old behavior +var axes=this.user_attrs_.axes;if(axes && axes.y2){v = axes.y2.valueRange;if(v)this.axes_[axis].valueRange = v;}}}}; /** + * Returns the number of y-axes on the chart. + * @return {number} the number of axes. + */Dygraph.prototype.numAxes = function(){return this.attributes_.numAxes();}; /** + * @private + * Returns axis properties for the given series. + * @param {string} setName The name of the series for which to get axis + * properties, e.g. 'Y1'. + * @return {Object} The axis properties. + */Dygraph.prototype.axisPropertiesForSeries = function(series){ // TODO(danvk): handle errors. +return this.axes_[this.attributes_.axisForSeries(series)];}; /** + * @private + * Determine the value range and tick marks for each axis. + * @param {Object} extremes A mapping from seriesName -> [low, high] + * This fills in the valueRange and ticks fields in each entry of this.axes_. + */Dygraph.prototype.computeYAxisRanges_ = function(extremes){var isNullUndefinedOrNaN=function isNullUndefinedOrNaN(num){return isNaN(parseFloat(num));};var numAxes=this.attributes_.numAxes();var ypadCompat,span,series,ypad;var p_axis; // Compute extreme values, a span and tick marks for each axis. +for(var i=0;i < numAxes;i++) {var axis=this.axes_[i];var logscale=this.attributes_.getForAxis("logscale",i);var includeZero=this.attributes_.getForAxis("includeZero",i);var independentTicks=this.attributes_.getForAxis("independentTicks",i);series = this.attributes_.seriesForAxis(i); // Add some padding. This supports two Y padding operation modes: +// +// - backwards compatible (yRangePad not set): +// 10% padding for automatic Y ranges, but not for user-supplied +// ranges, and move a close-to-zero edge to zero, since drawing at the edge +// results in invisible lines. Unfortunately lines drawn at the edge of a +// user-supplied range will still be invisible. If logscale is +// set, add a variable amount of padding at the top but +// none at the bottom. +// +// - new-style (yRangePad set by the user): +// always add the specified Y padding. +// +ypadCompat = true;ypad = 0.1; // add 10% +var yRangePad=this.getNumericOption('yRangePad');if(yRangePad !== null){ypadCompat = false; // Convert pixel padding to ratio +ypad = yRangePad / this.plotter_.area.h;}if(series.length === 0){ // If no series are defined or visible then use a reasonable default +axis.extremeRange = [0,1];}else { // Calculate the extremes of extremes. +var minY=Infinity; // extremes[series[0]][0]; +var maxY=-Infinity; // extremes[series[0]][1]; +var extremeMinY,extremeMaxY;for(var j=0;j < series.length;j++) { // this skips invisible series +if(!extremes.hasOwnProperty(series[j]))continue; // Only use valid extremes to stop null data series' from corrupting the scale. +extremeMinY = extremes[series[j]][0];if(extremeMinY !== null){minY = Math.min(extremeMinY,minY);}extremeMaxY = extremes[series[j]][1];if(extremeMaxY !== null){maxY = Math.max(extremeMaxY,maxY);}} // Include zero if requested by the user. +if(includeZero && !logscale){if(minY > 0)minY = 0;if(maxY < 0)maxY = 0;} // Ensure we have a valid scale, otherwise default to [0, 1] for safety. +if(minY == Infinity)minY = 0;if(maxY == -Infinity)maxY = 1;span = maxY - minY; // special case: if we have no sense of scale, center on the sole value. +if(span === 0){if(maxY !== 0){span = Math.abs(maxY);}else { // ... and if the sole value is zero, use range 0-1. +maxY = 1;span = 1;}}var maxAxisY=maxY,minAxisY=minY;if(ypadCompat){if(logscale){maxAxisY = maxY + ypad * span;minAxisY = minY;}else {maxAxisY = maxY + ypad * span;minAxisY = minY - ypad * span; // Backwards-compatible behavior: Move the span to start or end at zero if it's +// close to zero. +if(minAxisY < 0 && minY >= 0)minAxisY = 0;if(maxAxisY > 0 && maxY <= 0)maxAxisY = 0;}}axis.extremeRange = [minAxisY,maxAxisY];}if(axis.valueRange){ // This is a user-set value range for this axis. +var y0=isNullUndefinedOrNaN(axis.valueRange[0])?axis.extremeRange[0]:axis.valueRange[0];var y1=isNullUndefinedOrNaN(axis.valueRange[1])?axis.extremeRange[1]:axis.valueRange[1];axis.computedValueRange = [y0,y1];}else {axis.computedValueRange = axis.extremeRange;}if(!ypadCompat){ // When using yRangePad, adjust the upper/lower bounds to add +// padding unless the user has zoomed/panned the Y axis range. +if(logscale){y0 = axis.computedValueRange[0];y1 = axis.computedValueRange[1];var y0pct=ypad / (2 * ypad - 1);var y1pct=(ypad - 1) / (2 * ypad - 1);axis.computedValueRange[0] = utils.logRangeFraction(y0,y1,y0pct);axis.computedValueRange[1] = utils.logRangeFraction(y0,y1,y1pct);}else {y0 = axis.computedValueRange[0];y1 = axis.computedValueRange[1];span = y1 - y0;axis.computedValueRange[0] = y0 - span * ypad;axis.computedValueRange[1] = y1 + span * ypad;}}if(independentTicks){axis.independentTicks = independentTicks;var opts=this.optionsViewForAxis_('y' + (i?'2':''));var ticker=opts('ticker');axis.ticks = ticker(axis.computedValueRange[0],axis.computedValueRange[1],this.plotter_.area.h,opts,this); // Define the first independent axis as primary axis. +if(!p_axis)p_axis = axis;}}if(p_axis === undefined){throw "Configuration Error: At least one axis has to have the \"independentTicks\" option activated.";} // Add ticks. By default, all axes inherit the tick positions of the +// primary axis. However, if an axis is specifically marked as having +// independent ticks, then that is permissible as well. +for(var i=0;i < numAxes;i++) {var axis=this.axes_[i];if(!axis.independentTicks){var opts=this.optionsViewForAxis_('y' + (i?'2':''));var ticker=opts('ticker');var p_ticks=p_axis.ticks;var p_scale=p_axis.computedValueRange[1] - p_axis.computedValueRange[0];var scale=axis.computedValueRange[1] - axis.computedValueRange[0];var tick_values=[];for(var k=0;k < p_ticks.length;k++) {var y_frac=(p_ticks[k].v - p_axis.computedValueRange[0]) / p_scale;var y_val=axis.computedValueRange[0] + y_frac * scale;tick_values.push(y_val);}axis.ticks = ticker(axis.computedValueRange[0],axis.computedValueRange[1],this.plotter_.area.h,opts,this,tick_values);}}}; /** + * Detects the type of the str (date or numeric) and sets the various + * formatting attributes in this.attrs_ based on this type. + * @param {string} str An x value. + * @private + */Dygraph.prototype.detectTypeFromString_ = function(str){var isDate=false;var dashPos=str.indexOf('-'); // could be 2006-01-01 _or_ 1.0e-2 +if(dashPos > 0 && str[dashPos - 1] != 'e' && str[dashPos - 1] != 'E' || str.indexOf('/') >= 0 || isNaN(parseFloat(str))){isDate = true;}else if(str.length == 8 && str > '19700101' && str < '20371231'){ // TODO(danvk): remove support for this format. +isDate = true;}this.setXAxisOptions_(isDate);};Dygraph.prototype.setXAxisOptions_ = function(isDate){if(isDate){this.attrs_.xValueParser = utils.dateParser;this.attrs_.axes.x.valueFormatter = utils.dateValueFormatter;this.attrs_.axes.x.ticker = DygraphTickers.dateTicker;this.attrs_.axes.x.axisLabelFormatter = utils.dateAxisLabelFormatter;}else { /** @private (shut up, jsdoc!) */this.attrs_.xValueParser = function(x){return parseFloat(x);}; // TODO(danvk): use Dygraph.numberValueFormatter here? +/** @private (shut up, jsdoc!) */this.attrs_.axes.x.valueFormatter = function(x){return x;};this.attrs_.axes.x.ticker = DygraphTickers.numericTicks;this.attrs_.axes.x.axisLabelFormatter = this.attrs_.axes.x.valueFormatter;}}; /** + * @private + * Parses a string in a special csv format. We expect a csv file where each + * line is a date point, and the first field in each line is the date string. + * We also expect that all remaining fields represent series. + * if the errorBars attribute is set, then interpret the fields as: + * date, series1, stddev1, series2, stddev2, ... + * @param {[Object]} data See above. + * + * @return [Object] An array with one entry for each row. These entries + * are an array of cells in that row. The first entry is the parsed x-value for + * the row. The second, third, etc. are the y-values. These can take on one of + * three forms, depending on the CSV and constructor parameters: + * 1. numeric value + * 2. [ value, stddev ] + * 3. [ low value, center value, high value ] + */Dygraph.prototype.parseCSV_ = function(data){var ret=[];var line_delimiter=utils.detectLineDelimiter(data);var lines=data.split(line_delimiter || "\n");var vals,j; // Use the default delimiter or fall back to a tab if that makes sense. +var delim=this.getStringOption('delimiter');if(lines[0].indexOf(delim) == -1 && lines[0].indexOf('\t') >= 0){delim = '\t';}var start=0;if(!('labels' in this.user_attrs_)){ // User hasn't explicitly set labels, so they're (presumably) in the CSV. +start = 1;this.attrs_.labels = lines[0].split(delim); // NOTE: _not_ user_attrs_. +this.attributes_.reparseSeries();}var line_no=0;var xParser;var defaultParserSet=false; // attempt to auto-detect x value type +var expectedCols=this.attr_("labels").length;var outOfOrder=false;for(var i=start;i < lines.length;i++) {var line=lines[i];line_no = i;if(line.length === 0)continue; // skip blank lines +if(line[0] == '#')continue; // skip comment lines +var inFields=line.split(delim);if(inFields.length < 2)continue;var fields=[];if(!defaultParserSet){this.detectTypeFromString_(inFields[0]);xParser = this.getFunctionOption("xValueParser");defaultParserSet = true;}fields[0] = xParser(inFields[0],this); // If fractions are expected, parse the numbers as "A/B" +if(this.fractions_){for(j = 1;j < inFields.length;j++) { // TODO(danvk): figure out an appropriate way to flag parse errors. +vals = inFields[j].split("/");if(vals.length != 2){console.error('Expected fractional "num/den" values in CSV data ' + "but found a value '" + inFields[j] + "' on line " + (1 + i) + " ('" + line + "') which is not of this form.");fields[j] = [0,0];}else {fields[j] = [utils.parseFloat_(vals[0],i,line),utils.parseFloat_(vals[1],i,line)];}}}else if(this.getBooleanOption("errorBars")){ // If there are error bars, values are (value, stddev) pairs +if(inFields.length % 2 != 1){console.error('Expected alternating (value, stdev.) pairs in CSV data ' + 'but line ' + (1 + i) + ' has an odd number of values (' + (inFields.length - 1) + "): '" + line + "'");}for(j = 1;j < inFields.length;j += 2) {fields[(j + 1) / 2] = [utils.parseFloat_(inFields[j],i,line),utils.parseFloat_(inFields[j + 1],i,line)];}}else if(this.getBooleanOption("customBars")){ // Bars are a low;center;high tuple +for(j = 1;j < inFields.length;j++) {var val=inFields[j];if(/^ *$/.test(val)){fields[j] = [null,null,null];}else {vals = val.split(";");if(vals.length == 3){fields[j] = [utils.parseFloat_(vals[0],i,line),utils.parseFloat_(vals[1],i,line),utils.parseFloat_(vals[2],i,line)];}else {console.warn('When using customBars, values must be either blank ' + 'or "low;center;high" tuples (got "' + val + '" on line ' + (1 + i));}}}}else { // Values are just numbers +for(j = 1;j < inFields.length;j++) {fields[j] = utils.parseFloat_(inFields[j],i,line);}}if(ret.length > 0 && fields[0] < ret[ret.length - 1][0]){outOfOrder = true;}if(fields.length != expectedCols){console.error("Number of columns in line " + i + " (" + fields.length + ") does not agree with number of labels (" + expectedCols + ") " + line);} // If the user specified the 'labels' option and none of the cells of the +// first row parsed correctly, then they probably double-specified the +// labels. We go with the values set in the option, discard this row and +// log a warning to the JS console. +if(i === 0 && this.attr_('labels')){var all_null=true;for(j = 0;all_null && j < fields.length;j++) {if(fields[j])all_null = false;}if(all_null){console.warn("The dygraphs 'labels' option is set, but the first row " + "of CSV data ('" + line + "') appears to also contain " + "labels. Will drop the CSV labels and use the option " + "labels.");continue;}}ret.push(fields);}if(outOfOrder){console.warn("CSV is out of order; order it correctly to speed loading.");ret.sort(function(a,b){return a[0] - b[0];});}return ret;}; // In native format, all values must be dates or numbers. +// This check isn't perfect but will catch most mistaken uses of strings. +function validateNativeFormat(data){var firstRow=data[0];var firstX=firstRow[0];if(typeof firstX !== 'number' && !utils.isDateLike(firstX)){throw new Error('Expected number or date but got ' + typeof firstX + ': ' + firstX + '.');}for(var i=1;i < firstRow.length;i++) {var val=firstRow[i];if(val === null || val === undefined)continue;if(typeof val === 'number')continue;if(utils.isArrayLike(val))continue; // e.g. error bars or custom bars. +throw new Error('Expected number or array but got ' + typeof val + ': ' + val + '.');}} /** + * The user has provided their data as a pre-packaged JS array. If the x values + * are numeric, this is the same as dygraphs' internal format. If the x values + * are dates, we need to convert them from Date objects to ms since epoch. + * @param {!Array} data + * @return {Object} data with numeric x values. + * @private + */Dygraph.prototype.parseArray_ = function(data){ // Peek at the first x value to see if it's numeric. +if(data.length === 0){console.error("Can't plot empty data set");return null;}if(data[0].length === 0){console.error("Data set cannot contain an empty row");return null;}validateNativeFormat(data);var i;if(this.attr_("labels") === null){console.warn("Using default labels. Set labels explicitly via 'labels' " + "in the options parameter");this.attrs_.labels = ["X"];for(i = 1;i < data[0].length;i++) {this.attrs_.labels.push("Y" + i); // Not user_attrs_. +}this.attributes_.reparseSeries();}else {var num_labels=this.attr_("labels");if(num_labels.length != data[0].length){console.error("Mismatch between number of labels (" + num_labels + ")" + " and number of columns in array (" + data[0].length + ")");return null;}}if(utils.isDateLike(data[0][0])){ // Some intelligent defaults for a date x-axis. +this.attrs_.axes.x.valueFormatter = utils.dateValueFormatter;this.attrs_.axes.x.ticker = DygraphTickers.dateTicker;this.attrs_.axes.x.axisLabelFormatter = utils.dateAxisLabelFormatter; // Assume they're all dates. +var parsedData=utils.clone(data);for(i = 0;i < data.length;i++) {if(parsedData[i].length === 0){console.error("Row " + (1 + i) + " of data is empty");return null;}if(parsedData[i][0] === null || typeof parsedData[i][0].getTime != 'function' || isNaN(parsedData[i][0].getTime())){console.error("x value in row " + (1 + i) + " is not a Date");return null;}parsedData[i][0] = parsedData[i][0].getTime();}return parsedData;}else { // Some intelligent defaults for a numeric x-axis. +/** @private (shut up, jsdoc!) */this.attrs_.axes.x.valueFormatter = function(x){return x;};this.attrs_.axes.x.ticker = DygraphTickers.numericTicks;this.attrs_.axes.x.axisLabelFormatter = utils.numberAxisLabelFormatter;return data;}}; /** + * Parses a DataTable object from gviz. + * The data is expected to have a first column that is either a date or a + * number. All subsequent columns must be numbers. If there is a clear mismatch + * between this.xValueParser_ and the type of the first column, it will be + * fixed. Fills out rawData_. + * @param {!google.visualization.DataTable} data See above. + * @private + */Dygraph.prototype.parseDataTable_ = function(data){var shortTextForAnnotationNum=function shortTextForAnnotationNum(num){ // converts [0-9]+ [A-Z][a-z]* +// example: 0=A, 1=B, 25=Z, 26=Aa, 27=Ab +// and continues like.. Ba Bb .. Za .. Zz..Aaa...Zzz Aaaa Zzzz +var shortText=String.fromCharCode(65 /* A */ + num % 26);num = Math.floor(num / 26);while(num > 0) {shortText = String.fromCharCode(65 /* A */ + (num - 1) % 26) + shortText.toLowerCase();num = Math.floor((num - 1) / 26);}return shortText;};var cols=data.getNumberOfColumns();var rows=data.getNumberOfRows();var indepType=data.getColumnType(0);if(indepType == 'date' || indepType == 'datetime'){this.attrs_.xValueParser = utils.dateParser;this.attrs_.axes.x.valueFormatter = utils.dateValueFormatter;this.attrs_.axes.x.ticker = DygraphTickers.dateTicker;this.attrs_.axes.x.axisLabelFormatter = utils.dateAxisLabelFormatter;}else if(indepType == 'number'){this.attrs_.xValueParser = function(x){return parseFloat(x);};this.attrs_.axes.x.valueFormatter = function(x){return x;};this.attrs_.axes.x.ticker = DygraphTickers.numericTicks;this.attrs_.axes.x.axisLabelFormatter = this.attrs_.axes.x.valueFormatter;}else {throw new Error("only 'date', 'datetime' and 'number' types are supported " + "for column 1 of DataTable input (Got '" + indepType + "')");} // Array of the column indices which contain data (and not annotations). +var colIdx=[];var annotationCols={}; // data index -> [annotation cols] +var hasAnnotations=false;var i,j;for(i = 1;i < cols;i++) {var type=data.getColumnType(i);if(type == 'number'){colIdx.push(i);}else if(type == 'string' && this.getBooleanOption('displayAnnotations')){ // This is OK -- it's an annotation column. +var dataIdx=colIdx[colIdx.length - 1];if(!annotationCols.hasOwnProperty(dataIdx)){annotationCols[dataIdx] = [i];}else {annotationCols[dataIdx].push(i);}hasAnnotations = true;}else {throw new Error("Only 'number' is supported as a dependent type with Gviz." + " 'string' is only supported if displayAnnotations is true");}} // Read column labels +// TODO(danvk): add support back for errorBars +var labels=[data.getColumnLabel(0)];for(i = 0;i < colIdx.length;i++) {labels.push(data.getColumnLabel(colIdx[i]));if(this.getBooleanOption("errorBars"))i += 1;}this.attrs_.labels = labels;cols = labels.length;var ret=[];var outOfOrder=false;var annotations=[];for(i = 0;i < rows;i++) {var row=[];if(typeof data.getValue(i,0) === 'undefined' || data.getValue(i,0) === null){console.warn("Ignoring row " + i + " of DataTable because of undefined or null first column.");continue;}if(indepType == 'date' || indepType == 'datetime'){row.push(data.getValue(i,0).getTime());}else {row.push(data.getValue(i,0));}if(!this.getBooleanOption("errorBars")){for(j = 0;j < colIdx.length;j++) {var col=colIdx[j];row.push(data.getValue(i,col));if(hasAnnotations && annotationCols.hasOwnProperty(col) && data.getValue(i,annotationCols[col][0]) !== null){var ann={};ann.series = data.getColumnLabel(col);ann.xval = row[0];ann.shortText = shortTextForAnnotationNum(annotations.length);ann.text = '';for(var k=0;k < annotationCols[col].length;k++) {if(k)ann.text += "\n";ann.text += data.getValue(i,annotationCols[col][k]);}annotations.push(ann);}} // Strip out infinities, which give dygraphs problems later on. +for(j = 0;j < row.length;j++) {if(!isFinite(row[j]))row[j] = null;}}else {for(j = 0;j < cols - 1;j++) {row.push([data.getValue(i,1 + 2 * j),data.getValue(i,2 + 2 * j)]);}}if(ret.length > 0 && row[0] < ret[ret.length - 1][0]){outOfOrder = true;}ret.push(row);}if(outOfOrder){console.warn("DataTable is out of order; order it correctly to speed loading.");ret.sort(function(a,b){return a[0] - b[0];});}this.rawData_ = ret;if(annotations.length > 0){this.setAnnotations(annotations,true);}this.attributes_.reparseSeries();}; /** + * Signals to plugins that the chart data has updated. + * This happens after the data has updated but before the chart has redrawn. + * @private + */Dygraph.prototype.cascadeDataDidUpdateEvent_ = function(){ // TODO(danvk): there are some issues checking xAxisRange() and using +// toDomCoords from handlers of this event. The visible range should be set +// when the chart is drawn, not derived from the data. +this.cascadeEvents_('dataDidUpdate',{});}; /** + * Get the CSV data. If it's in a function, call that function. If it's in a + * file, do an XMLHttpRequest to get it. + * @private + */Dygraph.prototype.start_ = function(){var data=this.file_; // Functions can return references of all other types. +if(typeof data == 'function'){data = data();}if(utils.isArrayLike(data)){this.rawData_ = this.parseArray_(data);this.cascadeDataDidUpdateEvent_();this.predraw_();}else if(typeof data == 'object' && typeof data.getColumnRange == 'function'){ // must be a DataTable from gviz. +this.parseDataTable_(data);this.cascadeDataDidUpdateEvent_();this.predraw_();}else if(typeof data == 'string'){ // Heuristic: a newline means it's CSV data. Otherwise it's an URL. +var line_delimiter=utils.detectLineDelimiter(data);if(line_delimiter){this.loadedEvent_(data);}else { // REMOVE_FOR_IE +var req;if(window.XMLHttpRequest){ // Firefox, Opera, IE7, and other browsers will use the native object +req = new XMLHttpRequest();}else { // IE 5 and 6 will use the ActiveX control +req = new ActiveXObject("Microsoft.XMLHTTP");}var caller=this;req.onreadystatechange = function(){if(req.readyState == 4){if(req.status === 200 || // Normal http +req.status === 0){ // Chrome w/ --allow-file-access-from-files +caller.loadedEvent_(req.responseText);}}};req.open("GET",data,true);req.send(null);}}else {console.error("Unknown data format: " + typeof data);}}; /** + * Changes various properties of the graph. These can include: + * <ul> + * <li>file: changes the source data for the graph</li> + * <li>errorBars: changes whether the data contains stddev</li> + * </ul> + * + * There's a huge variety of options that can be passed to this method. For a + * full list, see http://dygraphs.com/options.html. + * + * @param {Object} input_attrs The new properties and values + * @param {boolean} block_redraw Usually the chart is redrawn after every + * call to updateOptions(). If you know better, you can pass true to + * explicitly block the redraw. This can be useful for chaining + * updateOptions() calls, avoiding the occasional infinite loop and + * preventing redraws when it's not necessary (e.g. when updating a + * callback). + */Dygraph.prototype.updateOptions = function(input_attrs,block_redraw){if(typeof block_redraw == 'undefined')block_redraw = false; // copyUserAttrs_ drops the "file" parameter as a convenience to us. +var file=input_attrs.file;var attrs=Dygraph.copyUserAttrs_(input_attrs); // TODO(danvk): this is a mess. Move these options into attr_. +if('rollPeriod' in attrs){this.rollPeriod_ = attrs.rollPeriod;}if('dateWindow' in attrs){this.dateWindow_ = attrs.dateWindow;} // TODO(danvk): validate per-series options. +// Supported: +// strokeWidth +// pointSize +// drawPoints +// highlightCircleSize +// Check if this set options will require new points. +var requiresNewPoints=utils.isPixelChangingOptionList(this.attr_("labels"),attrs);utils.updateDeep(this.user_attrs_,attrs);this.attributes_.reparseSeries();if(file){ // This event indicates that the data is about to change, but hasn't yet. +// TODO(danvk): support cancellation of the update via this event. +this.cascadeEvents_('dataWillUpdate',{});this.file_ = file;if(!block_redraw)this.start_();}else {if(!block_redraw){if(requiresNewPoints){this.predraw_();}else {this.renderGraph_(false);}}}}; /** + * Make a copy of input attributes, removing file as a convenience. + * @private + */Dygraph.copyUserAttrs_ = function(attrs){var my_attrs={};for(var k in attrs) {if(!attrs.hasOwnProperty(k))continue;if(k == 'file')continue;if(attrs.hasOwnProperty(k))my_attrs[k] = attrs[k];}return my_attrs;}; /** + * Resizes the dygraph. If no parameters are specified, resizes to fill the + * containing div (which has presumably changed size since the dygraph was + * instantiated. If the width/height are specified, the div will be resized. + * + * This is far more efficient than destroying and re-instantiating a + * Dygraph, since it doesn't have to reparse the underlying data. + * + * @param {number} width Width (in pixels) + * @param {number} height Height (in pixels) + */Dygraph.prototype.resize = function(width,height){if(this.resize_lock){return;}this.resize_lock = true;if(width === null != (height === null)){console.warn("Dygraph.resize() should be called with zero parameters or " + "two non-NULL parameters. Pretending it was zero.");width = height = null;}var old_width=this.width_;var old_height=this.height_;if(width){this.maindiv_.style.width = width + "px";this.maindiv_.style.height = height + "px";this.width_ = width;this.height_ = height;}else {this.width_ = this.maindiv_.clientWidth;this.height_ = this.maindiv_.clientHeight;}if(old_width != this.width_ || old_height != this.height_){ // Resizing a canvas erases it, even when the size doesn't change, so +// any resize needs to be followed by a redraw. +this.resizeElements_();this.predraw_();}this.resize_lock = false;}; /** + * Adjusts the number of points in the rolling average. Updates the graph to + * reflect the new averaging period. + * @param {number} length Number of points over which to average the data. + */Dygraph.prototype.adjustRoll = function(length){this.rollPeriod_ = length;this.predraw_();}; /** + * Returns a boolean array of visibility statuses. + */Dygraph.prototype.visibility = function(){ // Do lazy-initialization, so that this happens after we know the number of +// data series. +if(!this.getOption("visibility")){this.attrs_.visibility = [];} // TODO(danvk): it looks like this could go into an infinite loop w/ user_attrs. +while(this.getOption("visibility").length < this.numColumns() - 1) {this.attrs_.visibility.push(true);}return this.getOption("visibility");}; /** + * Changes the visibility of one or more series. + * + * @param {number|number[]|object} num the series index or an array of series indices + * or a boolean array of visibility states by index + * or an object mapping series numbers, as keys, to + * visibility state (boolean values) + * @param {boolean} value the visibility state expressed as a boolean + */Dygraph.prototype.setVisibility = function(num,value){var x=this.visibility();var numIsObject=false;if(!Array.isArray(num)){if(num !== null && typeof num === 'object'){numIsObject = true;}else {num = [num];}}if(numIsObject){for(var i in num) {if(num.hasOwnProperty(i)){if(i < 0 || i >= x.length){console.warn("Invalid series number in setVisibility: " + i);}else {x[i] = num[i];}}}}else {for(var i=0;i < num.length;i++) {if(typeof num[i] === 'boolean'){if(i >= x.length){console.warn("Invalid series number in setVisibility: " + i);}else {x[i] = num[i];}}else {if(num[i] < 0 || num[i] >= x.length){console.warn("Invalid series number in setVisibility: " + num[i]);}else {x[num[i]] = value;}}}}this.predraw_();}; /** + * How large of an area will the dygraph render itself in? + * This is used for testing. + * @return A {width: w, height: h} object. + * @private + */Dygraph.prototype.size = function(){return {width:this.width_,height:this.height_};}; /** + * Update the list of annotations and redraw the chart. + * See dygraphs.com/annotations.html for more info on how to use annotations. + * @param ann {Array} An array of annotation objects. + * @param suppressDraw {Boolean} Set to "true" to block chart redraw (optional). + */Dygraph.prototype.setAnnotations = function(ann,suppressDraw){ // Only add the annotation CSS rule once we know it will be used. +this.annotations_ = ann;if(!this.layout_){console.warn("Tried to setAnnotations before dygraph was ready. " + "Try setting them in a ready() block. See " + "dygraphs.com/tests/annotation.html");return;}this.layout_.setAnnotations(this.annotations_);if(!suppressDraw){this.predraw_();}}; /** + * Return the list of annotations. + */Dygraph.prototype.annotations = function(){return this.annotations_;}; /** + * Get the list of label names for this graph. The first column is the + * x-axis, so the data series names start at index 1. + * + * Returns null when labels have not yet been defined. + */Dygraph.prototype.getLabels = function(){var labels=this.attr_("labels");return labels?labels.slice():null;}; /** + * Get the index of a series (column) given its name. The first column is the + * x-axis, so the data series start with index 1. + */Dygraph.prototype.indexFromSetName = function(name){return this.setIndexByName_[name];}; /** + * Find the row number corresponding to the given x-value. + * Returns null if there is no such x-value in the data. + * If there are multiple rows with the same x-value, this will return the + * first one. + * @param {number} xVal The x-value to look for (e.g. millis since epoch). + * @return {?number} The row number, which you can pass to getValue(), or null. + */Dygraph.prototype.getRowForX = function(xVal){var low=0,high=this.numRows() - 1;while(low <= high) {var idx=high + low >> 1;var x=this.getValue(idx,0);if(x < xVal){low = idx + 1;}else if(x > xVal){high = idx - 1;}else if(low != idx){ // equal, but there may be an earlier match. +high = idx;}else {return idx;}}return null;}; /** + * Trigger a callback when the dygraph has drawn itself and is ready to be + * manipulated. This is primarily useful when dygraphs has to do an XHR for the + * data (i.e. a URL is passed as the data source) and the chart is drawn + * asynchronously. If the chart has already drawn, the callback will fire + * immediately. + * + * This is a good place to call setAnnotation(). + * + * @param {function(!Dygraph)} callback The callback to trigger when the chart + * is ready. + */Dygraph.prototype.ready = function(callback){if(this.is_initial_draw_){this.readyFns_.push(callback);}else {callback.call(this,this);}}; /** + * Add an event handler. This event handler is kept until the graph is + * destroyed with a call to graph.destroy(). + * + * @param {!Node} elem The element to add the event to. + * @param {string} type The type of the event, e.g. 'click' or 'mousemove'. + * @param {function(Event):(boolean|undefined)} fn The function to call + * on the event. The function takes one parameter: the event object. + * @private + */Dygraph.prototype.addAndTrackEvent = function(elem,type,fn){utils.addEvent(elem,type,fn);this.registeredEvents_.push({elem:elem,type:type,fn:fn});};Dygraph.prototype.removeTrackedEvents_ = function(){if(this.registeredEvents_){for(var idx=0;idx < this.registeredEvents_.length;idx++) {var reg=this.registeredEvents_[idx];utils.removeEvent(reg.elem,reg.type,reg.fn);}}this.registeredEvents_ = [];}; // Installed plugins, in order of precedence (most-general to most-specific). +Dygraph.PLUGINS = [_pluginsLegend2['default'],_pluginsAxes2['default'],_pluginsRangeSelector2['default'], // Has to be before ChartLabels so that its callbacks are called after ChartLabels' callbacks. +_pluginsChartLabels2['default'],_pluginsAnnotations2['default'],_pluginsGrid2['default']]; // There are many symbols which have historically been available through the +// Dygraph class. These are exported here for backwards compatibility. +Dygraph.GVizChart = _dygraphGviz2['default'];Dygraph.DASHED_LINE = utils.DASHED_LINE;Dygraph.DOT_DASH_LINE = utils.DOT_DASH_LINE;Dygraph.dateAxisLabelFormatter = utils.dateAxisLabelFormatter;Dygraph.toRGB_ = utils.toRGB_;Dygraph.findPos = utils.findPos;Dygraph.pageX = utils.pageX;Dygraph.pageY = utils.pageY;Dygraph.dateString_ = utils.dateString_;Dygraph.defaultInteractionModel = _dygraphInteractionModel2['default'].defaultModel;Dygraph.nonInteractiveModel = Dygraph.nonInteractiveModel_ = _dygraphInteractionModel2['default'].nonInteractiveModel_;Dygraph.Circles = utils.Circles;Dygraph.Plugins = {Legend:_pluginsLegend2['default'],Axes:_pluginsAxes2['default'],Annotations:_pluginsAnnotations2['default'],ChartLabels:_pluginsChartLabels2['default'],Grid:_pluginsGrid2['default'],RangeSelector:_pluginsRangeSelector2['default']};Dygraph.DataHandlers = {DefaultHandler:_datahandlerDefault2['default'],BarsHandler:_datahandlerBars2['default'],CustomBarsHandler:_datahandlerBarsCustom2['default'],DefaultFractionHandler:_datahandlerDefaultFractions2['default'],ErrorBarsHandler:_datahandlerBarsError2['default'],FractionsBarsHandler:_datahandlerBarsFractions2['default']};Dygraph.startPan = _dygraphInteractionModel2['default'].startPan;Dygraph.startZoom = _dygraphInteractionModel2['default'].startZoom;Dygraph.movePan = _dygraphInteractionModel2['default'].movePan;Dygraph.moveZoom = _dygraphInteractionModel2['default'].moveZoom;Dygraph.endPan = _dygraphInteractionModel2['default'].endPan;Dygraph.endZoom = _dygraphInteractionModel2['default'].endZoom;Dygraph.numericLinearTicks = DygraphTickers.numericLinearTicks;Dygraph.numericTicks = DygraphTickers.numericTicks;Dygraph.dateTicker = DygraphTickers.dateTicker;Dygraph.Granularity = DygraphTickers.Granularity;Dygraph.getDateAxis = DygraphTickers.getDateAxis;Dygraph.floatFormat = utils.floatFormat;exports['default'] = Dygraph;module.exports = exports['default']; + +}).call(this,require('_process')) + +},{"./datahandler/bars":5,"./datahandler/bars-custom":2,"./datahandler/bars-error":3,"./datahandler/bars-fractions":4,"./datahandler/default":8,"./datahandler/default-fractions":7,"./dygraph-canvas":9,"./dygraph-default-attrs":10,"./dygraph-gviz":11,"./dygraph-interaction-model":12,"./dygraph-layout":13,"./dygraph-options":15,"./dygraph-options-reference":14,"./dygraph-tickers":16,"./dygraph-utils":17,"./iframe-tarp":19,"./plugins/annotations":20,"./plugins/axes":21,"./plugins/chart-labels":22,"./plugins/grid":23,"./plugins/legend":24,"./plugins/range-selector":25,"_process":1}],19:[function(require,module,exports){ +/** + * To create a "drag" interaction, you typically register a mousedown event + * handler on the element where the drag begins. In that handler, you register a + * mouseup handler on the window to determine when the mouse is released, + * wherever that release happens. This works well, except when the user releases + * the mouse over an off-domain iframe. In that case, the mouseup event is + * handled by the iframe and never bubbles up to the window handler. + * + * To deal with this issue, we cover iframes with high z-index divs to make sure + * they don't capture mouseup. + * + * Usage: + * element.addEventListener('mousedown', function() { + * var tarper = new IFrameTarp(); + * tarper.cover(); + * var mouseUpHandler = function() { + * ... + * window.removeEventListener(mouseUpHandler); + * tarper.uncover(); + * }; + * window.addEventListener('mouseup', mouseUpHandler); + * }; + * + * @constructor + */ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj["default"] = obj; return newObj; } } + +var _dygraphUtils = require('./dygraph-utils'); + +var utils = _interopRequireWildcard(_dygraphUtils); + +function IFrameTarp() { + /** @type {Array.<!HTMLDivElement>} */ + this.tarps = []; +}; + +/** + * Find all the iframes in the document and cover them with high z-index + * transparent divs. + */ +IFrameTarp.prototype.cover = function () { + var iframes = document.getElementsByTagName("iframe"); + for (var i = 0; i < iframes.length; i++) { + var iframe = iframes[i]; + var pos = utils.findPos(iframe), + x = pos.x, + y = pos.y, + width = iframe.offsetWidth, + height = iframe.offsetHeight; + + var div = document.createElement("div"); + div.style.position = "absolute"; + div.style.left = x + 'px'; + div.style.top = y + 'px'; + div.style.width = width + 'px'; + div.style.height = height + 'px'; + div.style.zIndex = 999; + document.body.appendChild(div); + this.tarps.push(div); + } +}; + +/** + * Remove all the iframe covers. You should call this in a mouseup handler. + */ +IFrameTarp.prototype.uncover = function () { + for (var i = 0; i < this.tarps.length; i++) { + this.tarps[i].parentNode.removeChild(this.tarps[i]); + } + this.tarps = []; +}; + +exports["default"] = IFrameTarp; +module.exports = exports["default"]; + +},{"./dygraph-utils":17}],20:[function(require,module,exports){ +/** + * @license + * Copyright 2012 Dan Vanderkam (danvdk@gmail.com) + * MIT-licensed (http://opensource.org/licenses/MIT) + */ + +/*global Dygraph:false */ + +"use strict"; + +/** +Current bits of jankiness: +- Uses dygraph.layout_ to get the parsed annotations. +- Uses dygraph.plotter_.area + +It would be nice if the plugin didn't require so much special support inside +the core dygraphs classes, but annotations involve quite a bit of parsing and +layout. + +TODO(danvk): cache DOM elements. +*/ + +Object.defineProperty(exports, "__esModule", { + value: true +}); +var annotations = function annotations() { + this.annotations_ = []; +}; + +annotations.prototype.toString = function () { + return "Annotations Plugin"; +}; + +annotations.prototype.activate = function (g) { + return { + clearChart: this.clearChart, + didDrawChart: this.didDrawChart + }; +}; + +annotations.prototype.detachLabels = function () { + for (var i = 0; i < this.annotations_.length; i++) { + var a = this.annotations_[i]; + if (a.parentNode) a.parentNode.removeChild(a); + this.annotations_[i] = null; + } + this.annotations_ = []; +}; + +annotations.prototype.clearChart = function (e) { + this.detachLabels(); +}; + +annotations.prototype.didDrawChart = function (e) { + var g = e.dygraph; + + // Early out in the (common) case of zero annotations. + var points = g.layout_.annotated_points; + if (!points || points.length === 0) return; + + var containerDiv = e.canvas.parentNode; + + var bindEvt = function bindEvt(eventName, classEventName, pt) { + return function (annotation_event) { + var a = pt.annotation; + if (a.hasOwnProperty(eventName)) { + a[eventName](a, pt, g, annotation_event); + } else if (g.getOption(classEventName)) { + g.getOption(classEventName)(a, pt, g, annotation_event); + } + }; + }; + + // Add the annotations one-by-one. + var area = e.dygraph.getArea(); + + // x-coord to sum of previous annotation's heights (used for stacking). + var xToUsedHeight = {}; + + for (var i = 0; i < points.length; i++) { + var p = points[i]; + if (p.canvasx < area.x || p.canvasx > area.x + area.w || p.canvasy < area.y || p.canvasy > area.y + area.h) { + continue; + } + + var a = p.annotation; + var tick_height = 6; + if (a.hasOwnProperty("tickHeight")) { + tick_height = a.tickHeight; + } + + // TODO: deprecate axisLabelFontSize in favor of CSS + var div = document.createElement("div"); + div.style['fontSize'] = g.getOption('axisLabelFontSize') + "px"; + var className = 'dygraph-annotation'; + if (!a.hasOwnProperty('icon')) { + // camelCase class names are deprecated. + className += ' dygraphDefaultAnnotation dygraph-default-annotation'; + } + if (a.hasOwnProperty('cssClass')) { + className += " " + a.cssClass; + } + div.className = className; + + var width = a.hasOwnProperty('width') ? a.width : 16; + var height = a.hasOwnProperty('height') ? a.height : 16; + if (a.hasOwnProperty('icon')) { + var img = document.createElement("img"); + img.src = a.icon; + img.width = width; + img.height = height; + div.appendChild(img); + } else if (p.annotation.hasOwnProperty('shortText')) { + div.appendChild(document.createTextNode(p.annotation.shortText)); + } + var left = p.canvasx - width / 2; + div.style.left = left + "px"; + var divTop = 0; + if (a.attachAtBottom) { + var y = area.y + area.h - height - tick_height; + if (xToUsedHeight[left]) { + y -= xToUsedHeight[left]; + } else { + xToUsedHeight[left] = 0; + } + xToUsedHeight[left] += tick_height + height; + divTop = y; + } else { + divTop = p.canvasy - height - tick_height; + } + div.style.top = divTop + "px"; + div.style.width = width + "px"; + div.style.height = height + "px"; + div.title = p.annotation.text; + div.style.color = g.colorsMap_[p.name]; + div.style.borderColor = g.colorsMap_[p.name]; + a.div = div; + + g.addAndTrackEvent(div, 'click', bindEvt('clickHandler', 'annotationClickHandler', p, this)); + g.addAndTrackEvent(div, 'mouseover', bindEvt('mouseOverHandler', 'annotationMouseOverHandler', p, this)); + g.addAndTrackEvent(div, 'mouseout', bindEvt('mouseOutHandler', 'annotationMouseOutHandler', p, this)); + g.addAndTrackEvent(div, 'dblclick', bindEvt('dblClickHandler', 'annotationDblClickHandler', p, this)); + + containerDiv.appendChild(div); + this.annotations_.push(div); + + var ctx = e.drawingContext; + ctx.save(); + ctx.strokeStyle = a.hasOwnProperty('tickColor') ? a.tickColor : g.colorsMap_[p.name]; + ctx.lineWidth = a.hasOwnProperty('tickWidth') ? a.tickWidth : g.getOption('strokeWidth'); + ctx.beginPath(); + if (!a.attachAtBottom) { + ctx.moveTo(p.canvasx, p.canvasy); + ctx.lineTo(p.canvasx, p.canvasy - 2 - tick_height); + } else { + var y = divTop + height; + ctx.moveTo(p.canvasx, y); + ctx.lineTo(p.canvasx, y + tick_height); + } + ctx.closePath(); + ctx.stroke(); + ctx.restore(); + } +}; + +annotations.prototype.destroy = function () { + this.detachLabels(); +}; + +exports["default"] = annotations; +module.exports = exports["default"]; + +},{}],21:[function(require,module,exports){ +/** + * @license + * Copyright 2012 Dan Vanderkam (danvdk@gmail.com) + * MIT-licensed (http://opensource.org/licenses/MIT) + */ + +/*global Dygraph:false */ + +'use strict'; + +/* +Bits of jankiness: +- Direct layout access +- Direct area access +- Should include calculation of ticks, not just the drawing. + +Options left to make axis-friendly. + ('drawAxesAtZero') + ('xAxisHeight') +*/ + +Object.defineProperty(exports, '__esModule', { + value: true +}); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } + +var _dygraphUtils = require('../dygraph-utils'); + +var utils = _interopRequireWildcard(_dygraphUtils); + +/** + * Draws the axes. This includes the labels on the x- and y-axes, as well + * as the tick marks on the axes. + * It does _not_ draw the grid lines which span the entire chart. + */ +var axes = function axes() { + this.xlabels_ = []; + this.ylabels_ = []; +}; + +axes.prototype.toString = function () { + return 'Axes Plugin'; +}; + +axes.prototype.activate = function (g) { + return { + layout: this.layout, + clearChart: this.clearChart, + willDrawChart: this.willDrawChart + }; +}; + +axes.prototype.layout = function (e) { + var g = e.dygraph; + + if (g.getOptionForAxis('drawAxis', 'y')) { + var w = g.getOptionForAxis('axisLabelWidth', 'y') + 2 * g.getOptionForAxis('axisTickSize', 'y'); + e.reserveSpaceLeft(w); + } + + if (g.getOptionForAxis('drawAxis', 'x')) { + var h; + // NOTE: I think this is probably broken now, since g.getOption() now + // hits the dictionary. (That is, g.getOption('xAxisHeight') now always + // has a value.) + if (g.getOption('xAxisHeight')) { + h = g.getOption('xAxisHeight'); + } else { + h = g.getOptionForAxis('axisLabelFontSize', 'x') + 2 * g.getOptionForAxis('axisTickSize', 'x'); + } + e.reserveSpaceBottom(h); + } + + if (g.numAxes() == 2) { + if (g.getOptionForAxis('drawAxis', 'y2')) { + var w = g.getOptionForAxis('axisLabelWidth', 'y2') + 2 * g.getOptionForAxis('axisTickSize', 'y2'); + e.reserveSpaceRight(w); + } + } else if (g.numAxes() > 2) { + g.error('Only two y-axes are supported at this time. (Trying ' + 'to use ' + g.numAxes() + ')'); + } +}; + +axes.prototype.detachLabels = function () { + function removeArray(ary) { + for (var i = 0; i < ary.length; i++) { + var el = ary[i]; + if (el.parentNode) el.parentNode.removeChild(el); + } + } + + removeArray(this.xlabels_); + removeArray(this.ylabels_); + this.xlabels_ = []; + this.ylabels_ = []; +}; + +axes.prototype.clearChart = function (e) { + this.detachLabels(); +}; + +axes.prototype.willDrawChart = function (e) { + var _this = this; + + var g = e.dygraph; + + if (!g.getOptionForAxis('drawAxis', 'x') && !g.getOptionForAxis('drawAxis', 'y') && !g.getOptionForAxis('drawAxis', 'y2')) { + return; + } + + // Round pixels to half-integer boundaries for crisper drawing. + function halfUp(x) { + return Math.round(x) + 0.5; + } + function halfDown(y) { + return Math.round(y) - 0.5; + } + + var context = e.drawingContext; + var containerDiv = e.canvas.parentNode; + var canvasWidth = g.width_; // e.canvas.width is affected by pixel ratio. + var canvasHeight = g.height_; + + var label, x, y, tick, i; + + var makeLabelStyle = function makeLabelStyle(axis) { + return { + position: 'absolute', + fontSize: g.getOptionForAxis('axisLabelFontSize', axis) + 'px', + width: g.getOptionForAxis('axisLabelWidth', axis) + 'px' + }; + }; + + var labelStyles = { + x: makeLabelStyle('x'), + y: makeLabelStyle('y'), + y2: makeLabelStyle('y2') + }; + + var makeDiv = function makeDiv(txt, axis, prec_axis) { + /* + * This seems to be called with the following three sets of axis/prec_axis: + * x: undefined + * y: y1 + * y: y2 + */ + var div = document.createElement('div'); + var labelStyle = labelStyles[prec_axis == 'y2' ? 'y2' : axis]; + utils.update(div.style, labelStyle); + // TODO: combine outer & inner divs + var inner_div = document.createElement('div'); + inner_div.className = 'dygraph-axis-label' + ' dygraph-axis-label-' + axis + (prec_axis ? ' dygraph-axis-label-' + prec_axis : ''); + inner_div.innerHTML = txt; + div.appendChild(inner_div); + return div; + }; + + // axis lines + context.save(); + + var layout = g.layout_; + var area = e.dygraph.plotter_.area; + + // Helper for repeated axis-option accesses. + var makeOptionGetter = function makeOptionGetter(axis) { + return function (option) { + return g.getOptionForAxis(option, axis); + }; + }; + + if (g.getOptionForAxis('drawAxis', 'y')) { + if (layout.yticks && layout.yticks.length > 0) { + var num_axes = g.numAxes(); + var getOptions = [makeOptionGetter('y'), makeOptionGetter('y2')]; + layout.yticks.forEach(function (tick) { + if (tick.label === undefined) return; // this tick only has a grid line. + x = area.x; + var sgn = 1; + var prec_axis = 'y1'; + var getAxisOption = getOptions[0]; + if (tick.axis == 1) { + // right-side y-axis + x = area.x + area.w; + sgn = -1; + prec_axis = 'y2'; + getAxisOption = getOptions[1]; + } + var fontSize = getAxisOption('axisLabelFontSize'); + y = area.y + tick.pos * area.h; + + /* Tick marks are currently clipped, so don't bother drawing them. + context.beginPath(); + context.moveTo(halfUp(x), halfDown(y)); + context.lineTo(halfUp(x - sgn * this.attr_('axisTickSize')), halfDown(y)); + context.closePath(); + context.stroke(); + */ + + label = makeDiv(tick.label, 'y', num_axes == 2 ? prec_axis : null); + var top = y - fontSize / 2; + if (top < 0) top = 0; + + if (top + fontSize + 3 > canvasHeight) { + label.style.bottom = '0'; + } else { + label.style.top = top + 'px'; + } + // TODO: replace these with css classes? + if (tick.axis === 0) { + label.style.left = area.x - getAxisOption('axisLabelWidth') - getAxisOption('axisTickSize') + 'px'; + label.style.textAlign = 'right'; + } else if (tick.axis == 1) { + label.style.left = area.x + area.w + getAxisOption('axisTickSize') + 'px'; + label.style.textAlign = 'left'; + } + label.style.width = getAxisOption('axisLabelWidth') + 'px'; + containerDiv.appendChild(label); + _this.ylabels_.push(label); + }); + + // The lowest tick on the y-axis often overlaps with the leftmost + // tick on the x-axis. Shift the bottom tick up a little bit to + // compensate if necessary. + var bottomTick = this.ylabels_[0]; + // Interested in the y2 axis also? + var fontSize = g.getOptionForAxis('axisLabelFontSize', 'y'); + var bottom = parseInt(bottomTick.style.top, 10) + fontSize; + if (bottom > canvasHeight - fontSize) { + bottomTick.style.top = parseInt(bottomTick.style.top, 10) - fontSize / 2 + 'px'; + } + } + + // draw a vertical line on the left to separate the chart from the labels. + var axisX; + if (g.getOption('drawAxesAtZero')) { + var r = g.toPercentXCoord(0); + if (r > 1 || r < 0 || isNaN(r)) r = 0; + axisX = halfUp(area.x + r * area.w); + } else { + axisX = halfUp(area.x); + } + + context.strokeStyle = g.getOptionForAxis('axisLineColor', 'y'); + context.lineWidth = g.getOptionForAxis('axisLineWidth', 'y'); + + context.beginPath(); + context.moveTo(axisX, halfDown(area.y)); + context.lineTo(axisX, halfDown(area.y + area.h)); + context.closePath(); + context.stroke(); + + // if there's a secondary y-axis, draw a vertical line for that, too. + if (g.numAxes() == 2) { + context.strokeStyle = g.getOptionForAxis('axisLineColor', 'y2'); + context.lineWidth = g.getOptionForAxis('axisLineWidth', 'y2'); + context.beginPath(); + context.moveTo(halfDown(area.x + area.w), halfDown(area.y)); + context.lineTo(halfDown(area.x + area.w), halfDown(area.y + area.h)); + context.closePath(); + context.stroke(); + } + } + + if (g.getOptionForAxis('drawAxis', 'x')) { + if (layout.xticks) { + var getAxisOption = makeOptionGetter('x'); + layout.xticks.forEach(function (tick) { + if (tick.label === undefined) return; // this tick only has a grid line. + x = area.x + tick.pos * area.w; + y = area.y + area.h; + + /* Tick marks are currently clipped, so don't bother drawing them. + context.beginPath(); + context.moveTo(halfUp(x), halfDown(y)); + context.lineTo(halfUp(x), halfDown(y + this.attr_('axisTickSize'))); + context.closePath(); + context.stroke(); + */ + + label = makeDiv(tick.label, 'x'); + label.style.textAlign = 'center'; + label.style.top = y + getAxisOption('axisTickSize') + 'px'; + + var left = x - getAxisOption('axisLabelWidth') / 2; + if (left + getAxisOption('axisLabelWidth') > canvasWidth) { + left = canvasWidth - getAxisOption('axisLabelWidth'); + label.style.textAlign = 'right'; + } + if (left < 0) { + left = 0; + label.style.textAlign = 'left'; + } + + label.style.left = left + 'px'; + label.style.width = getAxisOption('axisLabelWidth') + 'px'; + containerDiv.appendChild(label); + _this.xlabels_.push(label); + }); + } + + context.strokeStyle = g.getOptionForAxis('axisLineColor', 'x'); + context.lineWidth = g.getOptionForAxis('axisLineWidth', 'x'); + context.beginPath(); + var axisY; + if (g.getOption('drawAxesAtZero')) { + var r = g.toPercentYCoord(0, 0); + if (r > 1 || r < 0) r = 1; + axisY = halfDown(area.y + r * area.h); + } else { + axisY = halfDown(area.y + area.h); + } + context.moveTo(halfUp(area.x), axisY); + context.lineTo(halfUp(area.x + area.w), axisY); + context.closePath(); + context.stroke(); + } + + context.restore(); +}; + +exports['default'] = axes; +module.exports = exports['default']; + +},{"../dygraph-utils":17}],22:[function(require,module,exports){ +/** + * @license + * Copyright 2012 Dan Vanderkam (danvdk@gmail.com) + * MIT-licensed (http://opensource.org/licenses/MIT) + */ +/*global Dygraph:false */ + +"use strict"; + +// TODO(danvk): move chart label options out of dygraphs and into the plugin. +// TODO(danvk): only tear down & rebuild the DIVs when it's necessary. + +Object.defineProperty(exports, "__esModule", { + value: true +}); +var chart_labels = function chart_labels() { + this.title_div_ = null; + this.xlabel_div_ = null; + this.ylabel_div_ = null; + this.y2label_div_ = null; +}; + +chart_labels.prototype.toString = function () { + return "ChartLabels Plugin"; +}; + +chart_labels.prototype.activate = function (g) { + return { + layout: this.layout, + // clearChart: this.clearChart, + didDrawChart: this.didDrawChart + }; +}; + +// QUESTION: should there be a plugin-utils.js? +var createDivInRect = function createDivInRect(r) { + var div = document.createElement('div'); + div.style.position = 'absolute'; + div.style.left = r.x + 'px'; + div.style.top = r.y + 'px'; + div.style.width = r.w + 'px'; + div.style.height = r.h + 'px'; + return div; +}; + +// Detach and null out any existing nodes. +chart_labels.prototype.detachLabels_ = function () { + var els = [this.title_div_, this.xlabel_div_, this.ylabel_div_, this.y2label_div_]; + for (var i = 0; i < els.length; i++) { + var el = els[i]; + if (!el) continue; + if (el.parentNode) el.parentNode.removeChild(el); + } + + this.title_div_ = null; + this.xlabel_div_ = null; + this.ylabel_div_ = null; + this.y2label_div_ = null; +}; + +var createRotatedDiv = function createRotatedDiv(g, box, axis, classes, html) { + // TODO(danvk): is this outer div actually necessary? + var div = document.createElement("div"); + div.style.position = 'absolute'; + if (axis == 1) { + // NOTE: this is cheating. Should be positioned relative to the box. + div.style.left = '0px'; + } else { + div.style.left = box.x + 'px'; + } + div.style.top = box.y + 'px'; + div.style.width = box.w + 'px'; + div.style.height = box.h + 'px'; + div.style.fontSize = g.getOption('yLabelWidth') - 2 + 'px'; + + var inner_div = document.createElement("div"); + inner_div.style.position = 'absolute'; + inner_div.style.width = box.h + 'px'; + inner_div.style.height = box.w + 'px'; + inner_div.style.top = box.h / 2 - box.w / 2 + 'px'; + inner_div.style.left = box.w / 2 - box.h / 2 + 'px'; + // TODO: combine inner_div and class_div. + inner_div.className = 'dygraph-label-rotate-' + (axis == 1 ? 'right' : 'left'); + + var class_div = document.createElement("div"); + class_div.className = classes; + class_div.innerHTML = html; + + inner_div.appendChild(class_div); + div.appendChild(inner_div); + return div; +}; + +chart_labels.prototype.layout = function (e) { + this.detachLabels_(); + + var g = e.dygraph; + var div = e.chart_div; + if (g.getOption('title')) { + // QUESTION: should this return an absolutely-positioned div instead? + var title_rect = e.reserveSpaceTop(g.getOption('titleHeight')); + this.title_div_ = createDivInRect(title_rect); + this.title_div_.style.fontSize = g.getOption('titleHeight') - 8 + 'px'; + + var class_div = document.createElement("div"); + class_div.className = 'dygraph-label dygraph-title'; + class_div.innerHTML = g.getOption('title'); + this.title_div_.appendChild(class_div); + div.appendChild(this.title_div_); + } + + if (g.getOption('xlabel')) { + var x_rect = e.reserveSpaceBottom(g.getOption('xLabelHeight')); + this.xlabel_div_ = createDivInRect(x_rect); + this.xlabel_div_.style.fontSize = g.getOption('xLabelHeight') - 2 + 'px'; + + var class_div = document.createElement("div"); + class_div.className = 'dygraph-label dygraph-xlabel'; + class_div.innerHTML = g.getOption('xlabel'); + this.xlabel_div_.appendChild(class_div); + div.appendChild(this.xlabel_div_); + } + + if (g.getOption('ylabel')) { + // It would make sense to shift the chart here to make room for the y-axis + // label, but the default yAxisLabelWidth is large enough that this results + // in overly-padded charts. The y-axis label should fit fine. If it + // doesn't, the yAxisLabelWidth option can be increased. + var y_rect = e.reserveSpaceLeft(0); + + this.ylabel_div_ = createRotatedDiv(g, y_rect, 1, // primary (left) y-axis + 'dygraph-label dygraph-ylabel', g.getOption('ylabel')); + div.appendChild(this.ylabel_div_); + } + + if (g.getOption('y2label') && g.numAxes() == 2) { + // same logic applies here as for ylabel. + var y2_rect = e.reserveSpaceRight(0); + this.y2label_div_ = createRotatedDiv(g, y2_rect, 2, // secondary (right) y-axis + 'dygraph-label dygraph-y2label', g.getOption('y2label')); + div.appendChild(this.y2label_div_); + } +}; + +chart_labels.prototype.didDrawChart = function (e) { + var g = e.dygraph; + if (this.title_div_) { + this.title_div_.children[0].innerHTML = g.getOption('title'); + } + if (this.xlabel_div_) { + this.xlabel_div_.children[0].innerHTML = g.getOption('xlabel'); + } + if (this.ylabel_div_) { + this.ylabel_div_.children[0].children[0].innerHTML = g.getOption('ylabel'); + } + if (this.y2label_div_) { + this.y2label_div_.children[0].children[0].innerHTML = g.getOption('y2label'); + } +}; + +chart_labels.prototype.clearChart = function () {}; + +chart_labels.prototype.destroy = function () { + this.detachLabels_(); +}; + +exports["default"] = chart_labels; +module.exports = exports["default"]; + +},{}],23:[function(require,module,exports){ +/** + * @license + * Copyright 2012 Dan Vanderkam (danvdk@gmail.com) + * MIT-licensed (http://opensource.org/licenses/MIT) + */ +/*global Dygraph:false */ + +/* + +Current bits of jankiness: +- Direct layout access +- Direct area access + +*/ + +"use strict"; + +/** + * Draws the gridlines, i.e. the gray horizontal & vertical lines running the + * length of the chart. + * + * @constructor + */ +Object.defineProperty(exports, "__esModule", { + value: true +}); +var grid = function grid() {}; + +grid.prototype.toString = function () { + return "Gridline Plugin"; +}; + +grid.prototype.activate = function (g) { + return { + willDrawChart: this.willDrawChart + }; +}; + +grid.prototype.willDrawChart = function (e) { + // Draw the new X/Y grid. Lines appear crisper when pixels are rounded to + // half-integers. This prevents them from drawing in two rows/cols. + var g = e.dygraph; + var ctx = e.drawingContext; + var layout = g.layout_; + var area = e.dygraph.plotter_.area; + + function halfUp(x) { + return Math.round(x) + 0.5; + } + function halfDown(y) { + return Math.round(y) - 0.5; + } + + var x, y, i, ticks; + if (g.getOptionForAxis('drawGrid', 'y')) { + var axes = ["y", "y2"]; + var strokeStyles = [], + lineWidths = [], + drawGrid = [], + stroking = [], + strokePattern = []; + for (var i = 0; i < axes.length; i++) { + drawGrid[i] = g.getOptionForAxis('drawGrid', axes[i]); + if (drawGrid[i]) { + strokeStyles[i] = g.getOptionForAxis('gridLineColor', axes[i]); + lineWidths[i] = g.getOptionForAxis('gridLineWidth', axes[i]); + strokePattern[i] = g.getOptionForAxis('gridLinePattern', axes[i]); + stroking[i] = strokePattern[i] && strokePattern[i].length >= 2; + } + } + ticks = layout.yticks; + ctx.save(); + // draw grids for the different y axes + ticks.forEach(function (tick) { + if (!tick.has_tick) return; + var axis = tick.axis; + if (drawGrid[axis]) { + ctx.save(); + if (stroking[axis]) { + if (ctx.setLineDash) ctx.setLineDash(strokePattern[axis]); + } + ctx.strokeStyle = strokeStyles[axis]; + ctx.lineWidth = lineWidths[axis]; + + x = halfUp(area.x); + y = halfDown(area.y + tick.pos * area.h); + ctx.beginPath(); + ctx.moveTo(x, y); + ctx.lineTo(x + area.w, y); + ctx.stroke(); + + ctx.restore(); + } + }); + ctx.restore(); + } + + // draw grid for x axis + if (g.getOptionForAxis('drawGrid', 'x')) { + ticks = layout.xticks; + ctx.save(); + var strokePattern = g.getOptionForAxis('gridLinePattern', 'x'); + var stroking = strokePattern && strokePattern.length >= 2; + if (stroking) { + if (ctx.setLineDash) ctx.setLineDash(strokePattern); + } + ctx.strokeStyle = g.getOptionForAxis('gridLineColor', 'x'); + ctx.lineWidth = g.getOptionForAxis('gridLineWidth', 'x'); + ticks.forEach(function (tick) { + if (!tick.has_tick) return; + x = halfUp(area.x + tick.pos * area.w); + y = halfDown(area.y + area.h); + ctx.beginPath(); + ctx.moveTo(x, y); + ctx.lineTo(x, area.y); + ctx.closePath(); + ctx.stroke(); + }); + if (stroking) { + if (ctx.setLineDash) ctx.setLineDash([]); + } + ctx.restore(); + } +}; + +grid.prototype.destroy = function () {}; + +exports["default"] = grid; +module.exports = exports["default"]; + +},{}],24:[function(require,module,exports){ +/** + * @license + * Copyright 2012 Dan Vanderkam (danvdk@gmail.com) + * MIT-licensed (http://opensource.org/licenses/MIT) + */ +/*global Dygraph:false */ + +/* +Current bits of jankiness: +- Uses two private APIs: + 1. Dygraph.optionsViewForAxis_ + 2. dygraph.plotter_.area +- Registers for a "predraw" event, which should be renamed. +- I call calculateEmWidthInDiv more often than needed. +*/ + +/*global Dygraph:false */ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj["default"] = obj; return newObj; } } + +var _dygraphUtils = require('../dygraph-utils'); + +var utils = _interopRequireWildcard(_dygraphUtils); + +/** + * Creates the legend, which appears when the user hovers over the chart. + * The legend can be either a user-specified or generated div. + * + * @constructor + */ +var Legend = function Legend() { + this.legend_div_ = null; + this.is_generated_div_ = false; // do we own this div, or was it user-specified? +}; + +Legend.prototype.toString = function () { + return "Legend Plugin"; +}; + +/** + * This is called during the dygraph constructor, after options have been set + * but before the data is available. + * + * Proper tasks to do here include: + * - Reading your own options + * - DOM manipulation + * - Registering event listeners + * + * @param {Dygraph} g Graph instance. + * @return {object.<string, function(ev)>} Mapping of event names to callbacks. + */ +Legend.prototype.activate = function (g) { + var div; + + var userLabelsDiv = g.getOption('labelsDiv'); + if (userLabelsDiv && null !== userLabelsDiv) { + if (typeof userLabelsDiv == "string" || userLabelsDiv instanceof String) { + div = document.getElementById(userLabelsDiv); + } else { + div = userLabelsDiv; + } + } else { + div = document.createElement("div"); + div.className = "dygraph-legend"; + // TODO(danvk): come up with a cleaner way to expose this. + g.graphDiv.appendChild(div); + this.is_generated_div_ = true; + } + + this.legend_div_ = div; + this.one_em_width_ = 10; // just a guess, will be updated. + + return { + select: this.select, + deselect: this.deselect, + // TODO(danvk): rethink the name "predraw" before we commit to it in any API. + predraw: this.predraw, + didDrawChart: this.didDrawChart + }; +}; + +// Needed for dashed lines. +var calculateEmWidthInDiv = function calculateEmWidthInDiv(div) { + var sizeSpan = document.createElement('span'); + sizeSpan.setAttribute('style', 'margin: 0; padding: 0 0 0 1em; border: 0;'); + div.appendChild(sizeSpan); + var oneEmWidth = sizeSpan.offsetWidth; + div.removeChild(sizeSpan); + return oneEmWidth; +}; + +var escapeHTML = function escapeHTML(str) { + return str.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">"); +}; + +Legend.prototype.select = function (e) { + var xValue = e.selectedX; + var points = e.selectedPoints; + var row = e.selectedRow; + + var legendMode = e.dygraph.getOption('legend'); + if (legendMode === 'never') { + this.legend_div_.style.display = 'none'; + return; + } + + if (legendMode === 'follow') { + // create floating legend div + var area = e.dygraph.plotter_.area; + var labelsDivWidth = this.legend_div_.offsetWidth; + var yAxisLabelWidth = e.dygraph.getOptionForAxis('axisLabelWidth', 'y'); + // determine floating [left, top] coordinates of the legend div + // within the plotter_ area + // offset 50 px to the right and down from the first selection point + // 50 px is guess based on mouse cursor size + var leftLegend = points[0].x * area.w + 50; + var topLegend = points[0].y * area.h - 50; + + // if legend floats to end of the chart area, it flips to the other + // side of the selection point + if (leftLegend + labelsDivWidth + 1 > area.w) { + leftLegend = leftLegend - 2 * 50 - labelsDivWidth - (yAxisLabelWidth - area.x); + } + + e.dygraph.graphDiv.appendChild(this.legend_div_); + this.legend_div_.style.left = yAxisLabelWidth + leftLegend + "px"; + this.legend_div_.style.top = topLegend + "px"; + } + + var html = Legend.generateLegendHTML(e.dygraph, xValue, points, this.one_em_width_, row); + this.legend_div_.innerHTML = html; + this.legend_div_.style.display = ''; +}; + +Legend.prototype.deselect = function (e) { + var legendMode = e.dygraph.getOption('legend'); + if (legendMode !== 'always') { + this.legend_div_.style.display = "none"; + } + + // Have to do this every time, since styles might have changed. + var oneEmWidth = calculateEmWidthInDiv(this.legend_div_); + this.one_em_width_ = oneEmWidth; + + var html = Legend.generateLegendHTML(e.dygraph, undefined, undefined, oneEmWidth, null); + this.legend_div_.innerHTML = html; +}; + +Legend.prototype.didDrawChart = function (e) { + this.deselect(e); +}; + +// Right edge should be flush with the right edge of the charting area (which +// may not be the same as the right edge of the div, if we have two y-axes. +// TODO(danvk): is any of this really necessary? Could just set "right" in "activate". +/** + * Position the labels div so that: + * - its right edge is flush with the right edge of the charting area + * - its top edge is flush with the top edge of the charting area + * @private + */ +Legend.prototype.predraw = function (e) { + // Don't touch a user-specified labelsDiv. + if (!this.is_generated_div_) return; + + // TODO(danvk): only use real APIs for this. + e.dygraph.graphDiv.appendChild(this.legend_div_); + var area = e.dygraph.getArea(); + var labelsDivWidth = this.legend_div_.offsetWidth; + this.legend_div_.style.left = area.x + area.w - labelsDivWidth - 1 + "px"; + this.legend_div_.style.top = area.y + "px"; +}; + +/** + * Called when dygraph.destroy() is called. + * You should null out any references and detach any DOM elements. + */ +Legend.prototype.destroy = function () { + this.legend_div_ = null; +}; + +/** + * Generates HTML for the legend which is displayed when hovering over the + * chart. If no selected points are specified, a default legend is returned + * (this may just be the empty string). + * @param {number} x The x-value of the selected points. + * @param {Object} sel_points List of selected points for the given + * x-value. Should have properties like 'name', 'yval' and 'canvasy'. + * @param {number} oneEmWidth The pixel width for 1em in the legend. Only + * relevant when displaying a legend with no selection (i.e. {legend: + * 'always'}) and with dashed lines. + * @param {number} row The selected row index. + * @private + */ +Legend.generateLegendHTML = function (g, x, sel_points, oneEmWidth, row) { + // Data about the selection to pass to legendFormatter + var data = { + dygraph: g, + x: x, + series: [] + }; + + var labelToSeries = {}; + var labels = g.getLabels(); + if (labels) { + for (var i = 1; i < labels.length; i++) { + var series = g.getPropertiesForSeries(labels[i]); + var strokePattern = g.getOption('strokePattern', labels[i]); + var seriesData = { + dashHTML: generateLegendDashHTML(strokePattern, series.color, oneEmWidth), + label: labels[i], + labelHTML: escapeHTML(labels[i]), + isVisible: series.visible, + color: series.color + }; + + data.series.push(seriesData); + labelToSeries[labels[i]] = seriesData; + } + } + + if (typeof x !== 'undefined') { + var xOptView = g.optionsViewForAxis_('x'); + var xvf = xOptView('valueFormatter'); + data.xHTML = xvf.call(g, x, xOptView, labels[0], g, row, 0); + + var yOptViews = []; + var num_axes = g.numAxes(); + for (var i = 0; i < num_axes; i++) { + // TODO(danvk): remove this use of a private API + yOptViews[i] = g.optionsViewForAxis_('y' + (i ? 1 + i : '')); + } + + var showZeros = g.getOption('labelsShowZeroValues'); + var highlightSeries = g.getHighlightSeries(); + for (i = 0; i < sel_points.length; i++) { + var pt = sel_points[i]; + var seriesData = labelToSeries[pt.name]; + seriesData.y = pt.yval; + + if (pt.yval === 0 && !showZeros || isNaN(pt.canvasy)) { + seriesData.isVisible = false; + continue; + } + + var series = g.getPropertiesForSeries(pt.name); + var yOptView = yOptViews[series.axis - 1]; + var fmtFunc = yOptView('valueFormatter'); + var yHTML = fmtFunc.call(g, pt.yval, yOptView, pt.name, g, row, labels.indexOf(pt.name)); + + utils.update(seriesData, { yHTML: yHTML }); + + if (pt.name == highlightSeries) { + seriesData.isHighlighted = true; + } + } + } + + var formatter = g.getOption('legendFormatter') || Legend.defaultFormatter; + return formatter.call(g, data); +}; + +Legend.defaultFormatter = function (data) { + var g = data.dygraph; + + // TODO(danvk): deprecate this option in place of {legend: 'never'} + // XXX should this logic be in the formatter? + if (g.getOption('showLabelsOnHighlight') !== true) return ''; + + var sepLines = g.getOption('labelsSeparateLines'); + var html; + + if (typeof data.x === 'undefined') { + // TODO: this check is duplicated in generateLegendHTML. Put it in one place. + if (g.getOption('legend') != 'always') { + return ''; + } + + html = ''; + for (var i = 0; i < data.series.length; i++) { + var series = data.series[i]; + if (!series.isVisible) continue; + + if (html !== '') html += sepLines ? '<br/>' : ' '; + html += "<span style='font-weight: bold; color: " + series.color + ";'>" + series.dashHTML + " " + series.labelHTML + "</span>"; + } + return html; + } + + html = data.xHTML + ':'; + for (var i = 0; i < data.series.length; i++) { + var series = data.series[i]; + if (!series.isVisible) continue; + if (sepLines) html += '<br>'; + var cls = series.isHighlighted ? ' class="highlight"' : ''; + html += "<span" + cls + "> <b><span style='color: " + series.color + ";'>" + series.labelHTML + "</span></b>: " + series.yHTML + "</span>"; + } + return html; +}; + +/** + * Generates html for the "dash" displayed on the legend when using "legend: always". + * In particular, this works for dashed lines with any stroke pattern. It will + * try to scale the pattern to fit in 1em width. Or if small enough repeat the + * pattern for 1em width. + * + * @param strokePattern The pattern + * @param color The color of the series. + * @param oneEmWidth The width in pixels of 1em in the legend. + * @private + */ +// TODO(danvk): cache the results of this +function generateLegendDashHTML(strokePattern, color, oneEmWidth) { + // Easy, common case: a solid line + if (!strokePattern || strokePattern.length <= 1) { + return "<div class=\"dygraph-legend-line\" style=\"border-bottom-color: " + color + ";\"></div>"; + } + + var i, j, paddingLeft, marginRight; + var strokePixelLength = 0, + segmentLoop = 0; + var normalizedPattern = []; + var loop; + + // Compute the length of the pixels including the first segment twice, + // since we repeat it. + for (i = 0; i <= strokePattern.length; i++) { + strokePixelLength += strokePattern[i % strokePattern.length]; + } + + // See if we can loop the pattern by itself at least twice. + loop = Math.floor(oneEmWidth / (strokePixelLength - strokePattern[0])); + if (loop > 1) { + // This pattern fits at least two times, no scaling just convert to em; + for (i = 0; i < strokePattern.length; i++) { + normalizedPattern[i] = strokePattern[i] / oneEmWidth; + } + // Since we are repeating the pattern, we don't worry about repeating the + // first segment in one draw. + segmentLoop = normalizedPattern.length; + } else { + // If the pattern doesn't fit in the legend we scale it to fit. + loop = 1; + for (i = 0; i < strokePattern.length; i++) { + normalizedPattern[i] = strokePattern[i] / strokePixelLength; + } + // For the scaled patterns we do redraw the first segment. + segmentLoop = normalizedPattern.length + 1; + } + + // Now make the pattern. + var dash = ""; + for (j = 0; j < loop; j++) { + for (i = 0; i < segmentLoop; i += 2) { + // The padding is the drawn segment. + paddingLeft = normalizedPattern[i % normalizedPattern.length]; + if (i < strokePattern.length) { + // The margin is the space segment. + marginRight = normalizedPattern[(i + 1) % normalizedPattern.length]; + } else { + // The repeated first segment has no right margin. + marginRight = 0; + } + dash += "<div class=\"dygraph-legend-dash\" style=\"margin-right: " + marginRight + "em; padding-left: " + paddingLeft + "em;\"></div>"; + } + } + return dash; +}; + +exports["default"] = Legend; +module.exports = exports["default"]; + +},{"../dygraph-utils":17}],25:[function(require,module,exports){ +/** + * @license + * Copyright 2011 Paul Felix (paul.eric.felix@gmail.com) + * MIT-licensed (http://opensource.org/licenses/MIT) + */ +/*global Dygraph:false,TouchEvent:false */ + +/** + * @fileoverview This file contains the RangeSelector plugin used to provide + * a timeline range selector widget for dygraphs. + */ + +/*global Dygraph:false */ +"use strict"; + +Object.defineProperty(exports, '__esModule', { + value: true +}); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } + +var _dygraphUtils = require('../dygraph-utils'); + +var utils = _interopRequireWildcard(_dygraphUtils); + +var _dygraphInteractionModel = require('../dygraph-interaction-model'); + +var _dygraphInteractionModel2 = _interopRequireDefault(_dygraphInteractionModel); + +var _iframeTarp = require('../iframe-tarp'); + +var _iframeTarp2 = _interopRequireDefault(_iframeTarp); + +var rangeSelector = function rangeSelector() { + this.hasTouchInterface_ = typeof TouchEvent != 'undefined'; + this.isMobileDevice_ = /mobile|android/gi.test(navigator.appVersion); + this.interfaceCreated_ = false; +}; + +rangeSelector.prototype.toString = function () { + return "RangeSelector Plugin"; +}; + +rangeSelector.prototype.activate = function (dygraph) { + this.dygraph_ = dygraph; + if (this.getOption_('showRangeSelector')) { + this.createInterface_(); + } + return { + layout: this.reserveSpace_, + predraw: this.renderStaticLayer_, + didDrawChart: this.renderInteractiveLayer_ + }; +}; + +rangeSelector.prototype.destroy = function () { + this.bgcanvas_ = null; + this.fgcanvas_ = null; + this.leftZoomHandle_ = null; + this.rightZoomHandle_ = null; +}; + +//------------------------------------------------------------------ +// Private methods +//------------------------------------------------------------------ + +rangeSelector.prototype.getOption_ = function (name, opt_series) { + return this.dygraph_.getOption(name, opt_series); +}; + +rangeSelector.prototype.setDefaultOption_ = function (name, value) { + this.dygraph_.attrs_[name] = value; +}; + +/** + * @private + * Creates the range selector elements and adds them to the graph. + */ +rangeSelector.prototype.createInterface_ = function () { + this.createCanvases_(); + this.createZoomHandles_(); + this.initInteraction_(); + + // Range selector and animatedZooms have a bad interaction. See issue 359. + if (this.getOption_('animatedZooms')) { + console.warn('Animated zooms and range selector are not compatible; disabling animatedZooms.'); + this.dygraph_.updateOptions({ animatedZooms: false }, true); + } + + this.interfaceCreated_ = true; + this.addToGraph_(); +}; + +/** + * @private + * Adds the range selector to the graph. + */ +rangeSelector.prototype.addToGraph_ = function () { + var graphDiv = this.graphDiv_ = this.dygraph_.graphDiv; + graphDiv.appendChild(this.bgcanvas_); + graphDiv.appendChild(this.fgcanvas_); + graphDiv.appendChild(this.leftZoomHandle_); + graphDiv.appendChild(this.rightZoomHandle_); +}; + +/** + * @private + * Removes the range selector from the graph. + */ +rangeSelector.prototype.removeFromGraph_ = function () { + var graphDiv = this.graphDiv_; + graphDiv.removeChild(this.bgcanvas_); + graphDiv.removeChild(this.fgcanvas_); + graphDiv.removeChild(this.leftZoomHandle_); + graphDiv.removeChild(this.rightZoomHandle_); + this.graphDiv_ = null; +}; + +/** + * @private + * Called by Layout to allow range selector to reserve its space. + */ +rangeSelector.prototype.reserveSpace_ = function (e) { + if (this.getOption_('showRangeSelector')) { + e.reserveSpaceBottom(this.getOption_('rangeSelectorHeight') + 4); + } +}; + +/** + * @private + * Renders the static portion of the range selector at the predraw stage. + */ +rangeSelector.prototype.renderStaticLayer_ = function () { + if (!this.updateVisibility_()) { + return; + } + this.resize_(); + this.drawStaticLayer_(); +}; + +/** + * @private + * Renders the interactive portion of the range selector after the chart has been drawn. + */ +rangeSelector.prototype.renderInteractiveLayer_ = function () { + if (!this.updateVisibility_() || this.isChangingRange_) { + return; + } + this.placeZoomHandles_(); + this.drawInteractiveLayer_(); +}; + +/** + * @private + * Check to see if the range selector is enabled/disabled and update visibility accordingly. + */ +rangeSelector.prototype.updateVisibility_ = function () { + var enabled = this.getOption_('showRangeSelector'); + if (enabled) { + if (!this.interfaceCreated_) { + this.createInterface_(); + } else if (!this.graphDiv_ || !this.graphDiv_.parentNode) { + this.addToGraph_(); + } + } else if (this.graphDiv_) { + this.removeFromGraph_(); + var dygraph = this.dygraph_; + setTimeout(function () { + dygraph.width_ = 0;dygraph.resize(); + }, 1); + } + return enabled; +}; + +/** + * @private + * Resizes the range selector. + */ +rangeSelector.prototype.resize_ = function () { + function setElementRect(canvas, context, rect, pixelRatioOption) { + var canvasScale = pixelRatioOption || utils.getContextPixelRatio(context); + + canvas.style.top = rect.y + 'px'; + canvas.style.left = rect.x + 'px'; + canvas.width = rect.w * canvasScale; + canvas.height = rect.h * canvasScale; + canvas.style.width = rect.w + 'px'; + canvas.style.height = rect.h + 'px'; + + if (canvasScale != 1) { + context.scale(canvasScale, canvasScale); + } + } + + var plotArea = this.dygraph_.layout_.getPlotArea(); + + var xAxisLabelHeight = 0; + if (this.dygraph_.getOptionForAxis('drawAxis', 'x')) { + xAxisLabelHeight = this.getOption_('xAxisHeight') || this.getOption_('axisLabelFontSize') + 2 * this.getOption_('axisTickSize'); + } + this.canvasRect_ = { + x: plotArea.x, + y: plotArea.y + plotArea.h + xAxisLabelHeight + 4, + w: plotArea.w, + h: this.getOption_('rangeSelectorHeight') + }; + + var pixelRatioOption = this.dygraph_.getNumericOption('pixelRatio'); + setElementRect(this.bgcanvas_, this.bgcanvas_ctx_, this.canvasRect_, pixelRatioOption); + setElementRect(this.fgcanvas_, this.fgcanvas_ctx_, this.canvasRect_, pixelRatioOption); +}; + +/** + * @private + * Creates the background and foreground canvases. + */ +rangeSelector.prototype.createCanvases_ = function () { + this.bgcanvas_ = utils.createCanvas(); + this.bgcanvas_.className = 'dygraph-rangesel-bgcanvas'; + this.bgcanvas_.style.position = 'absolute'; + this.bgcanvas_.style.zIndex = 9; + this.bgcanvas_ctx_ = utils.getContext(this.bgcanvas_); + + this.fgcanvas_ = utils.createCanvas(); + this.fgcanvas_.className = 'dygraph-rangesel-fgcanvas'; + this.fgcanvas_.style.position = 'absolute'; + this.fgcanvas_.style.zIndex = 9; + this.fgcanvas_.style.cursor = 'default'; + this.fgcanvas_ctx_ = utils.getContext(this.fgcanvas_); +}; + +/** + * @private + * Creates the zoom handle elements. + */ +rangeSelector.prototype.createZoomHandles_ = function () { + var img = new Image(); + img.className = 'dygraph-rangesel-zoomhandle'; + img.style.position = 'absolute'; + img.style.zIndex = 10; + img.style.visibility = 'hidden'; // Initially hidden so they don't show up in the wrong place. + img.style.cursor = 'col-resize'; + // TODO: change image to more options + img.width = 9; + img.height = 16; + img.src = 'data:image/png;base64,' + 'iVBORw0KGgoAAAANSUhEUgAAAAkAAAAQCAYAAADESFVDAAAAAXNSR0IArs4c6QAAAAZiS0dEANAA' + 'zwDP4Z7KegAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAAd0SU1FB9sHGw0cMqdt1UwAAAAZdEVYdENv' + 'bW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAaElEQVQoz+3SsRFAQBCF4Z9WJM8KCDVwownl' + '6YXsTmCUsyKGkZzcl7zkz3YLkypgAnreFmDEpHkIwVOMfpdi9CEEN2nGpFdwD03yEqDtOgCaun7s' + 'qSTDH32I1pQA2Pb9sZecAxc5r3IAb21d6878xsAAAAAASUVORK5CYII='; + + if (this.isMobileDevice_) { + img.width *= 2; + img.height *= 2; + } + + this.leftZoomHandle_ = img; + this.rightZoomHandle_ = img.cloneNode(false); +}; + +/** + * @private + * Sets up the interaction for the range selector. + */ +rangeSelector.prototype.initInteraction_ = function () { + var self = this; + var topElem = document; + var clientXLast = 0; + var handle = null; + var isZooming = false; + var isPanning = false; + var dynamic = !this.isMobileDevice_; + + // We cover iframes during mouse interactions. See comments in + // dygraph-utils.js for more info on why this is a good idea. + var tarp = new _iframeTarp2['default'](); + + // functions, defined below. Defining them this way (rather than with + // "function foo() {...}" makes JSHint happy. + var toXDataWindow, onZoomStart, onZoom, onZoomEnd, doZoom, isMouseInPanZone, onPanStart, onPan, onPanEnd, doPan, onCanvasHover; + + // Touch event functions + var onZoomHandleTouchEvent, onCanvasTouchEvent, addTouchEvents; + + toXDataWindow = function (zoomHandleStatus) { + var xDataLimits = self.dygraph_.xAxisExtremes(); + var fact = (xDataLimits[1] - xDataLimits[0]) / self.canvasRect_.w; + var xDataMin = xDataLimits[0] + (zoomHandleStatus.leftHandlePos - self.canvasRect_.x) * fact; + var xDataMax = xDataLimits[0] + (zoomHandleStatus.rightHandlePos - self.canvasRect_.x) * fact; + return [xDataMin, xDataMax]; + }; + + onZoomStart = function (e) { + utils.cancelEvent(e); + isZooming = true; + clientXLast = e.clientX; + handle = e.target ? e.target : e.srcElement; + if (e.type === 'mousedown' || e.type === 'dragstart') { + // These events are removed manually. + utils.addEvent(topElem, 'mousemove', onZoom); + utils.addEvent(topElem, 'mouseup', onZoomEnd); + } + self.fgcanvas_.style.cursor = 'col-resize'; + tarp.cover(); + return true; + }; + + onZoom = function (e) { + if (!isZooming) { + return false; + } + utils.cancelEvent(e); + + var delX = e.clientX - clientXLast; + if (Math.abs(delX) < 4) { + return true; + } + clientXLast = e.clientX; + + // Move handle. + var zoomHandleStatus = self.getZoomHandleStatus_(); + var newPos; + if (handle == self.leftZoomHandle_) { + newPos = zoomHandleStatus.leftHandlePos + delX; + newPos = Math.min(newPos, zoomHandleStatus.rightHandlePos - handle.width - 3); + newPos = Math.max(newPos, self.canvasRect_.x); + } else { + newPos = zoomHandleStatus.rightHandlePos + delX; + newPos = Math.min(newPos, self.canvasRect_.x + self.canvasRect_.w); + newPos = Math.max(newPos, zoomHandleStatus.leftHandlePos + handle.width + 3); + } + var halfHandleWidth = handle.width / 2; + handle.style.left = newPos - halfHandleWidth + 'px'; + self.drawInteractiveLayer_(); + + // Zoom on the fly. + if (dynamic) { + doZoom(); + } + return true; + }; + + onZoomEnd = function (e) { + if (!isZooming) { + return false; + } + isZooming = false; + tarp.uncover(); + utils.removeEvent(topElem, 'mousemove', onZoom); + utils.removeEvent(topElem, 'mouseup', onZoomEnd); + self.fgcanvas_.style.cursor = 'default'; + + // If on a slower device, zoom now. + if (!dynamic) { + doZoom(); + } + return true; + }; + + doZoom = function () { + try { + var zoomHandleStatus = self.getZoomHandleStatus_(); + self.isChangingRange_ = true; + if (!zoomHandleStatus.isZoomed) { + self.dygraph_.resetZoom(); + } else { + var xDataWindow = toXDataWindow(zoomHandleStatus); + self.dygraph_.doZoomXDates_(xDataWindow[0], xDataWindow[1]); + } + } finally { + self.isChangingRange_ = false; + } + }; + + isMouseInPanZone = function (e) { + var rect = self.leftZoomHandle_.getBoundingClientRect(); + var leftHandleClientX = rect.left + rect.width / 2; + rect = self.rightZoomHandle_.getBoundingClientRect(); + var rightHandleClientX = rect.left + rect.width / 2; + return e.clientX > leftHandleClientX && e.clientX < rightHandleClientX; + }; + + onPanStart = function (e) { + if (!isPanning && isMouseInPanZone(e) && self.getZoomHandleStatus_().isZoomed) { + utils.cancelEvent(e); + isPanning = true; + clientXLast = e.clientX; + if (e.type === 'mousedown') { + // These events are removed manually. + utils.addEvent(topElem, 'mousemove', onPan); + utils.addEvent(topElem, 'mouseup', onPanEnd); + } + return true; + } + return false; + }; + + onPan = function (e) { + if (!isPanning) { + return false; + } + utils.cancelEvent(e); + + var delX = e.clientX - clientXLast; + if (Math.abs(delX) < 4) { + return true; + } + clientXLast = e.clientX; + + // Move range view + var zoomHandleStatus = self.getZoomHandleStatus_(); + var leftHandlePos = zoomHandleStatus.leftHandlePos; + var rightHandlePos = zoomHandleStatus.rightHandlePos; + var rangeSize = rightHandlePos - leftHandlePos; + if (leftHandlePos + delX <= self.canvasRect_.x) { + leftHandlePos = self.canvasRect_.x; + rightHandlePos = leftHandlePos + rangeSize; + } else if (rightHandlePos + delX >= self.canvasRect_.x + self.canvasRect_.w) { + rightHandlePos = self.canvasRect_.x + self.canvasRect_.w; + leftHandlePos = rightHandlePos - rangeSize; + } else { + leftHandlePos += delX; + rightHandlePos += delX; + } + var halfHandleWidth = self.leftZoomHandle_.width / 2; + self.leftZoomHandle_.style.left = leftHandlePos - halfHandleWidth + 'px'; + self.rightZoomHandle_.style.left = rightHandlePos - halfHandleWidth + 'px'; + self.drawInteractiveLayer_(); + + // Do pan on the fly. + if (dynamic) { + doPan(); + } + return true; + }; + + onPanEnd = function (e) { + if (!isPanning) { + return false; + } + isPanning = false; + utils.removeEvent(topElem, 'mousemove', onPan); + utils.removeEvent(topElem, 'mouseup', onPanEnd); + // If on a slower device, do pan now. + if (!dynamic) { + doPan(); + } + return true; + }; + + doPan = function () { + try { + self.isChangingRange_ = true; + self.dygraph_.dateWindow_ = toXDataWindow(self.getZoomHandleStatus_()); + self.dygraph_.drawGraph_(false); + } finally { + self.isChangingRange_ = false; + } + }; + + onCanvasHover = function (e) { + if (isZooming || isPanning) { + return; + } + var cursor = isMouseInPanZone(e) ? 'move' : 'default'; + if (cursor != self.fgcanvas_.style.cursor) { + self.fgcanvas_.style.cursor = cursor; + } + }; + + onZoomHandleTouchEvent = function (e) { + if (e.type == 'touchstart' && e.targetTouches.length == 1) { + if (onZoomStart(e.targetTouches[0])) { + utils.cancelEvent(e); + } + } else if (e.type == 'touchmove' && e.targetTouches.length == 1) { + if (onZoom(e.targetTouches[0])) { + utils.cancelEvent(e); + } + } else { + onZoomEnd(e); + } + }; + + onCanvasTouchEvent = function (e) { + if (e.type == 'touchstart' && e.targetTouches.length == 1) { + if (onPanStart(e.targetTouches[0])) { + utils.cancelEvent(e); + } + } else if (e.type == 'touchmove' && e.targetTouches.length == 1) { + if (onPan(e.targetTouches[0])) { + utils.cancelEvent(e); + } + } else { + onPanEnd(e); + } + }; + + addTouchEvents = function (elem, fn) { + var types = ['touchstart', 'touchend', 'touchmove', 'touchcancel']; + for (var i = 0; i < types.length; i++) { + self.dygraph_.addAndTrackEvent(elem, types[i], fn); + } + }; + + this.setDefaultOption_('interactionModel', _dygraphInteractionModel2['default'].dragIsPanInteractionModel); + this.setDefaultOption_('panEdgeFraction', 0.0001); + + var dragStartEvent = window.opera ? 'mousedown' : 'dragstart'; + this.dygraph_.addAndTrackEvent(this.leftZoomHandle_, dragStartEvent, onZoomStart); + this.dygraph_.addAndTrackEvent(this.rightZoomHandle_, dragStartEvent, onZoomStart); + + this.dygraph_.addAndTrackEvent(this.fgcanvas_, 'mousedown', onPanStart); + this.dygraph_.addAndTrackEvent(this.fgcanvas_, 'mousemove', onCanvasHover); + + // Touch events + if (this.hasTouchInterface_) { + addTouchEvents(this.leftZoomHandle_, onZoomHandleTouchEvent); + addTouchEvents(this.rightZoomHandle_, onZoomHandleTouchEvent); + addTouchEvents(this.fgcanvas_, onCanvasTouchEvent); + } +}; + +/** + * @private + * Draws the static layer in the background canvas. + */ +rangeSelector.prototype.drawStaticLayer_ = function () { + var ctx = this.bgcanvas_ctx_; + ctx.clearRect(0, 0, this.canvasRect_.w, this.canvasRect_.h); + try { + this.drawMiniPlot_(); + } catch (ex) { + console.warn(ex); + } + + var margin = 0.5; + this.bgcanvas_ctx_.lineWidth = this.getOption_('rangeSelectorBackgroundLineWidth'); + ctx.strokeStyle = this.getOption_('rangeSelectorBackgroundStrokeColor'); + ctx.beginPath(); + ctx.moveTo(margin, margin); + ctx.lineTo(margin, this.canvasRect_.h - margin); + ctx.lineTo(this.canvasRect_.w - margin, this.canvasRect_.h - margin); + ctx.lineTo(this.canvasRect_.w - margin, margin); + ctx.stroke(); +}; + +/** + * @private + * Draws the mini plot in the background canvas. + */ +rangeSelector.prototype.drawMiniPlot_ = function () { + var fillStyle = this.getOption_('rangeSelectorPlotFillColor'); + var fillGradientStyle = this.getOption_('rangeSelectorPlotFillGradientColor'); + var strokeStyle = this.getOption_('rangeSelectorPlotStrokeColor'); + if (!fillStyle && !strokeStyle) { + return; + } + + var stepPlot = this.getOption_('stepPlot'); + + var combinedSeriesData = this.computeCombinedSeriesAndLimits_(); + var yRange = combinedSeriesData.yMax - combinedSeriesData.yMin; + + // Draw the mini plot. + var ctx = this.bgcanvas_ctx_; + var margin = 0.5; + + var xExtremes = this.dygraph_.xAxisExtremes(); + var xRange = Math.max(xExtremes[1] - xExtremes[0], 1.e-30); + var xFact = (this.canvasRect_.w - margin) / xRange; + var yFact = (this.canvasRect_.h - margin) / yRange; + var canvasWidth = this.canvasRect_.w - margin; + var canvasHeight = this.canvasRect_.h - margin; + + var prevX = null, + prevY = null; + + ctx.beginPath(); + ctx.moveTo(margin, canvasHeight); + for (var i = 0; i < combinedSeriesData.data.length; i++) { + var dataPoint = combinedSeriesData.data[i]; + var x = dataPoint[0] !== null ? (dataPoint[0] - xExtremes[0]) * xFact : NaN; + var y = dataPoint[1] !== null ? canvasHeight - (dataPoint[1] - combinedSeriesData.yMin) * yFact : NaN; + + // Skip points that don't change the x-value. Overly fine-grained points + // can cause major slowdowns with the ctx.fill() call below. + if (!stepPlot && prevX !== null && Math.round(x) == Math.round(prevX)) { + continue; + } + + if (isFinite(x) && isFinite(y)) { + if (prevX === null) { + ctx.lineTo(x, canvasHeight); + } else if (stepPlot) { + ctx.lineTo(x, prevY); + } + ctx.lineTo(x, y); + prevX = x; + prevY = y; + } else { + if (prevX !== null) { + if (stepPlot) { + ctx.lineTo(x, prevY); + ctx.lineTo(x, canvasHeight); + } else { + ctx.lineTo(prevX, canvasHeight); + } + } + prevX = prevY = null; + } + } + ctx.lineTo(canvasWidth, canvasHeight); + ctx.closePath(); + + if (fillStyle) { + var lingrad = this.bgcanvas_ctx_.createLinearGradient(0, 0, 0, canvasHeight); + if (fillGradientStyle) { + lingrad.addColorStop(0, fillGradientStyle); + } + lingrad.addColorStop(1, fillStyle); + this.bgcanvas_ctx_.fillStyle = lingrad; + ctx.fill(); + } + + if (strokeStyle) { + this.bgcanvas_ctx_.strokeStyle = strokeStyle; + this.bgcanvas_ctx_.lineWidth = this.getOption_('rangeSelectorPlotLineWidth'); + ctx.stroke(); + } +}; + +/** + * @private + * Computes and returns the combined series data along with min/max for the mini plot. + * The combined series consists of averaged values for all series. + * When series have error bars, the error bars are ignored. + * @return {Object} An object containing combined series array, ymin, ymax. + */ +rangeSelector.prototype.computeCombinedSeriesAndLimits_ = function () { + var g = this.dygraph_; + var logscale = this.getOption_('logscale'); + var i; + + // Select series to combine. By default, all series are combined. + var numColumns = g.numColumns(); + var labels = g.getLabels(); + var includeSeries = new Array(numColumns); + var anySet = false; + var visibility = g.visibility(); + var inclusion = []; + + for (i = 1; i < numColumns; i++) { + var include = this.getOption_('showInRangeSelector', labels[i]); + inclusion.push(include); + if (include !== null) anySet = true; // it's set explicitly for this series + } + + if (anySet) { + for (i = 1; i < numColumns; i++) { + includeSeries[i] = inclusion[i - 1]; + } + } else { + for (i = 1; i < numColumns; i++) { + includeSeries[i] = visibility[i - 1]; + } + } + + // Create a combined series (average of selected series values). + // TODO(danvk): short-circuit if there's only one series. + var rolledSeries = []; + var dataHandler = g.dataHandler_; + var options = g.attributes_; + for (i = 1; i < g.numColumns(); i++) { + if (!includeSeries[i]) continue; + var series = dataHandler.extractSeries(g.rawData_, i, options); + if (g.rollPeriod() > 1) { + series = dataHandler.rollingAverage(series, g.rollPeriod(), options); + } + + rolledSeries.push(series); + } + + var combinedSeries = []; + for (i = 0; i < rolledSeries[0].length; i++) { + var sum = 0; + var count = 0; + for (var j = 0; j < rolledSeries.length; j++) { + var y = rolledSeries[j][i][1]; + if (y === null || isNaN(y)) continue; + count++; + sum += y; + } + combinedSeries.push([rolledSeries[0][i][0], sum / count]); + } + + // Compute the y range. + var yMin = Number.MAX_VALUE; + var yMax = -Number.MAX_VALUE; + for (i = 0; i < combinedSeries.length; i++) { + var yVal = combinedSeries[i][1]; + if (yVal !== null && isFinite(yVal) && (!logscale || yVal > 0)) { + yMin = Math.min(yMin, yVal); + yMax = Math.max(yMax, yVal); + } + } + + // Convert Y data to log scale if needed. + // Also, expand the Y range to compress the mini plot a little. + var extraPercent = 0.25; + if (logscale) { + yMax = utils.log10(yMax); + yMax += yMax * extraPercent; + yMin = utils.log10(yMin); + for (i = 0; i < combinedSeries.length; i++) { + combinedSeries[i][1] = utils.log10(combinedSeries[i][1]); + } + } else { + var yExtra; + var yRange = yMax - yMin; + if (yRange <= Number.MIN_VALUE) { + yExtra = yMax * extraPercent; + } else { + yExtra = yRange * extraPercent; + } + yMax += yExtra; + yMin -= yExtra; + } + + return { data: combinedSeries, yMin: yMin, yMax: yMax }; +}; + +/** + * @private + * Places the zoom handles in the proper position based on the current X data window. + */ +rangeSelector.prototype.placeZoomHandles_ = function () { + var xExtremes = this.dygraph_.xAxisExtremes(); + var xWindowLimits = this.dygraph_.xAxisRange(); + var xRange = xExtremes[1] - xExtremes[0]; + var leftPercent = Math.max(0, (xWindowLimits[0] - xExtremes[0]) / xRange); + var rightPercent = Math.max(0, (xExtremes[1] - xWindowLimits[1]) / xRange); + var leftCoord = this.canvasRect_.x + this.canvasRect_.w * leftPercent; + var rightCoord = this.canvasRect_.x + this.canvasRect_.w * (1 - rightPercent); + var handleTop = Math.max(this.canvasRect_.y, this.canvasRect_.y + (this.canvasRect_.h - this.leftZoomHandle_.height) / 2); + var halfHandleWidth = this.leftZoomHandle_.width / 2; + this.leftZoomHandle_.style.left = leftCoord - halfHandleWidth + 'px'; + this.leftZoomHandle_.style.top = handleTop + 'px'; + this.rightZoomHandle_.style.left = rightCoord - halfHandleWidth + 'px'; + this.rightZoomHandle_.style.top = this.leftZoomHandle_.style.top; + + this.leftZoomHandle_.style.visibility = 'visible'; + this.rightZoomHandle_.style.visibility = 'visible'; +}; + +/** + * @private + * Draws the interactive layer in the foreground canvas. + */ +rangeSelector.prototype.drawInteractiveLayer_ = function () { + var ctx = this.fgcanvas_ctx_; + ctx.clearRect(0, 0, this.canvasRect_.w, this.canvasRect_.h); + var margin = 1; + var width = this.canvasRect_.w - margin; + var height = this.canvasRect_.h - margin; + var zoomHandleStatus = this.getZoomHandleStatus_(); + + ctx.strokeStyle = this.getOption_('rangeSelectorForegroundStrokeColor'); + ctx.lineWidth = this.getOption_('rangeSelectorForegroundLineWidth'); + if (!zoomHandleStatus.isZoomed) { + ctx.beginPath(); + ctx.moveTo(margin, margin); + ctx.lineTo(margin, height); + ctx.lineTo(width, height); + ctx.lineTo(width, margin); + ctx.stroke(); + } else { + var leftHandleCanvasPos = Math.max(margin, zoomHandleStatus.leftHandlePos - this.canvasRect_.x); + var rightHandleCanvasPos = Math.min(width, zoomHandleStatus.rightHandlePos - this.canvasRect_.x); + + ctx.fillStyle = 'rgba(240, 240, 240, ' + this.getOption_('rangeSelectorAlpha').toString() + ')'; + ctx.fillRect(0, 0, leftHandleCanvasPos, this.canvasRect_.h); + ctx.fillRect(rightHandleCanvasPos, 0, this.canvasRect_.w - rightHandleCanvasPos, this.canvasRect_.h); + + ctx.beginPath(); + ctx.moveTo(margin, margin); + ctx.lineTo(leftHandleCanvasPos, margin); + ctx.lineTo(leftHandleCanvasPos, height); + ctx.lineTo(rightHandleCanvasPos, height); + ctx.lineTo(rightHandleCanvasPos, margin); + ctx.lineTo(width, margin); + ctx.stroke(); + } +}; + +/** + * @private + * Returns the current zoom handle position information. + * @return {Object} The zoom handle status. + */ +rangeSelector.prototype.getZoomHandleStatus_ = function () { + var halfHandleWidth = this.leftZoomHandle_.width / 2; + var leftHandlePos = parseFloat(this.leftZoomHandle_.style.left) + halfHandleWidth; + var rightHandlePos = parseFloat(this.rightZoomHandle_.style.left) + halfHandleWidth; + return { + leftHandlePos: leftHandlePos, + rightHandlePos: rightHandlePos, + isZoomed: leftHandlePos - 1 > this.canvasRect_.x || rightHandlePos + 1 < this.canvasRect_.x + this.canvasRect_.w + }; +}; + +exports['default'] = rangeSelector; +module.exports = exports['default']; + +},{"../dygraph-interaction-model":12,"../dygraph-utils":17,"../iframe-tarp":19}]},{},[18])(18) +}); +//# sourceMappingURL=dygraph.js.map diff --git a/debian/missing-sources/epoch.css b/debian/missing-sources/epoch.css new file mode 100644 index 0000000..84d97e3 --- /dev/null +++ b/debian/missing-sources/epoch.css @@ -0,0 +1,1494 @@ +/* Epoch Master SCSS Includes the core styles and all the themes to produce the complete epoch css file. By Ryan Sandor Richards Copyright 2013 Fastly, Inc. */ +/* Core Epoch Styles */ +/** Generates the styles needed to define a fill color for a given class name. @param $name Name of the class to use (sans the leading .) @param $color Fill color to associate with the class name. */ +/** Produces categorical color classes for plots (excluding heatmaps). @param $list List of colors to use for each category. @param $entries Size of the input list. */ +/** Produces categorical colors for heatmaps. @param $list List of colors to use for categories. @param $entries Size of the input list. */ +.epoch .axis path, .epoch .axis line { shape-rendering: crispEdges; } + +.epoch .axis.canvas .tick line { shape-rendering: geometricPrecision; } + +/* Canvas Styles Reference Container The reference container is an SVG that is automatically created when Epoch is loaded. It is used by the canvas based plots to obtain color information from the page styles by creating reference elements and then reading their computed styles. Note: don't mess with this ;) */ +div#_canvas_css_reference { width: 0; height: 0; position: absolute; top: -1000px; left: -1000px; } + +div#_canvas_css_reference svg { position: absolute; width: 0; height: 0; top: -1000px; left: -1000px; } + +/* theme/_default.scss - Default Color Theme Categorical Colors Adapted from d3: https://github.com/mbostock/d3/wiki/Ordinal-Scales#categorical-colors */ +.epoch { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 12pt; } + +.epoch .axis path, .epoch .axis line { fill: transparent; stroke: #000; } + +.epoch .axis .tick text { font-size: 9pt; } + +.epoch .line { fill: transparent; stroke-width: 2px; } + +.epoch.sparklines .line { stroke-width: 1px; } + +.epoch .area { stroke: transparent; } + +.epoch .arc.pie { stroke: #fff; stroke-width: 1.5px; } + +.epoch .arc.pie text { stroke: transparent; fill: white; font-size: 9pt; } + +.epoch .gauge-labels .value { text-anchor: middle; font-size: 140%; fill: #666; } + +.epoch.gauge-tiny { width: 120px; height: 90px; } + +.epoch.gauge-tiny .gauge-labels .value { font-size: 80%; } + +.epoch.gauge-tiny .gauge .arc.outer { stroke-width: 2px; } + +.epoch.gauge-small { width: 180px; height: 135px; } + +.epoch.gauge-small .gauge-labels .value { font-size: 120%; } + +.epoch.gauge-small .gauge .arc.outer { stroke-width: 3px; } + +.epoch.gauge-medium { width: 240px; height: 180px; } + +.epoch.gauge-medium .gauge .arc.outer { stroke-width: 3px; } + +.epoch.gauge-large { width: 320px; height: 240px; } + +.epoch.gauge-large .gauge-labels .value { font-size: 180%; } + +.epoch .gauge .arc.outer { stroke-width: 4px; stroke: #666; } + +.epoch .gauge .arc.inner { stroke-width: 1px; stroke: #555; } + +.epoch .gauge .tick { stroke-width: 1px; stroke: #555; } + +.epoch .gauge .needle { fill: orange; } + +.epoch .gauge .needle-base { fill: #666; } + +.epoch div.ref.category1, .epoch.category10 div.ref.category1 { background-color: #1f77b4; } + +.epoch .category1 .line, .epoch.category10 .category1 .line { stroke: #1f77b4; } + +.epoch .category1 .area, .epoch .category1 .dot, .epoch.category10 .category1 .area, .epoch.category10 .category1 .dot { fill: #1f77b4; stroke: transparent; } + +.epoch .arc.category1 path, .epoch.category10 .arc.category1 path { fill: #1f77b4; } + +.epoch .bar.category1, .epoch.category10 .bar.category1 { fill: #1f77b4; } + +.epoch div.ref.category2, .epoch.category10 div.ref.category2 { background-color: #ff7f0e; } + +.epoch .category2 .line, .epoch.category10 .category2 .line { stroke: #ff7f0e; } + +.epoch .category2 .area, .epoch .category2 .dot, .epoch.category10 .category2 .area, .epoch.category10 .category2 .dot { fill: #ff7f0e; stroke: transparent; } + +.epoch .arc.category2 path, .epoch.category10 .arc.category2 path { fill: #ff7f0e; } + +.epoch .bar.category2, .epoch.category10 .bar.category2 { fill: #ff7f0e; } + +.epoch div.ref.category3, .epoch.category10 div.ref.category3 { background-color: #2ca02c; } + +.epoch .category3 .line, .epoch.category10 .category3 .line { stroke: #2ca02c; } + +.epoch .category3 .area, .epoch .category3 .dot, .epoch.category10 .category3 .area, .epoch.category10 .category3 .dot { fill: #2ca02c; stroke: transparent; } + +.epoch .arc.category3 path, .epoch.category10 .arc.category3 path { fill: #2ca02c; } + +.epoch .bar.category3, .epoch.category10 .bar.category3 { fill: #2ca02c; } + +.epoch div.ref.category4, .epoch.category10 div.ref.category4 { background-color: #d62728; } + +.epoch .category4 .line, .epoch.category10 .category4 .line { stroke: #d62728; } + +.epoch .category4 .area, .epoch .category4 .dot, .epoch.category10 .category4 .area, .epoch.category10 .category4 .dot { fill: #d62728; stroke: transparent; } + +.epoch .arc.category4 path, .epoch.category10 .arc.category4 path { fill: #d62728; } + +.epoch .bar.category4, .epoch.category10 .bar.category4 { fill: #d62728; } + +.epoch div.ref.category5, .epoch.category10 div.ref.category5 { background-color: #9467bd; } + +.epoch .category5 .line, .epoch.category10 .category5 .line { stroke: #9467bd; } + +.epoch .category5 .area, .epoch .category5 .dot, .epoch.category10 .category5 .area, .epoch.category10 .category5 .dot { fill: #9467bd; stroke: transparent; } + +.epoch .arc.category5 path, .epoch.category10 .arc.category5 path { fill: #9467bd; } + +.epoch .bar.category5, .epoch.category10 .bar.category5 { fill: #9467bd; } + +.epoch div.ref.category6, .epoch.category10 div.ref.category6 { background-color: #8c564b; } + +.epoch .category6 .line, .epoch.category10 .category6 .line { stroke: #8c564b; } + +.epoch .category6 .area, .epoch .category6 .dot, .epoch.category10 .category6 .area, .epoch.category10 .category6 .dot { fill: #8c564b; stroke: transparent; } + +.epoch .arc.category6 path, .epoch.category10 .arc.category6 path { fill: #8c564b; } + +.epoch .bar.category6, .epoch.category10 .bar.category6 { fill: #8c564b; } + +.epoch div.ref.category7, .epoch.category10 div.ref.category7 { background-color: #e377c2; } + +.epoch .category7 .line, .epoch.category10 .category7 .line { stroke: #e377c2; } + +.epoch .category7 .area, .epoch .category7 .dot, .epoch.category10 .category7 .area, .epoch.category10 .category7 .dot { fill: #e377c2; stroke: transparent; } + +.epoch .arc.category7 path, .epoch.category10 .arc.category7 path { fill: #e377c2; } + +.epoch .bar.category7, .epoch.category10 .bar.category7 { fill: #e377c2; } + +.epoch div.ref.category8, .epoch.category10 div.ref.category8 { background-color: #7f7f7f; } + +.epoch .category8 .line, .epoch.category10 .category8 .line { stroke: #7f7f7f; } + +.epoch .category8 .area, .epoch .category8 .dot, .epoch.category10 .category8 .area, .epoch.category10 .category8 .dot { fill: #7f7f7f; stroke: transparent; } + +.epoch .arc.category8 path, .epoch.category10 .arc.category8 path { fill: #7f7f7f; } + +.epoch .bar.category8, .epoch.category10 .bar.category8 { fill: #7f7f7f; } + +.epoch div.ref.category9, .epoch.category10 div.ref.category9 { background-color: #bcbd22; } + +.epoch .category9 .line, .epoch.category10 .category9 .line { stroke: #bcbd22; } + +.epoch .category9 .area, .epoch .category9 .dot, .epoch.category10 .category9 .area, .epoch.category10 .category9 .dot { fill: #bcbd22; stroke: transparent; } + +.epoch .arc.category9 path, .epoch.category10 .arc.category9 path { fill: #bcbd22; } + +.epoch .bar.category9, .epoch.category10 .bar.category9 { fill: #bcbd22; } + +.epoch div.ref.category10, .epoch.category10 div.ref.category10 { background-color: #17becf; } + +.epoch .category10 .line, .epoch.category10 .category10 .line { stroke: #17becf; } + +.epoch .category10 .area, .epoch .category10 .dot, .epoch.category10 .category10 .area, .epoch.category10 .category10 .dot { fill: #17becf; stroke: transparent; } + +.epoch .arc.category10 path, .epoch.category10 .arc.category10 path { fill: #17becf; } + +.epoch .bar.category10, .epoch.category10 .bar.category10 { fill: #17becf; } + +.epoch.category20 div.ref.category1 { background-color: #1f77b4; } + +.epoch.category20 .category1 .line { stroke: #1f77b4; } + +.epoch.category20 .category1 .area, .epoch.category20 .category1 .dot { fill: #1f77b4; stroke: transparent; } + +.epoch.category20 .arc.category1 path { fill: #1f77b4; } + +.epoch.category20 .bar.category1 { fill: #1f77b4; } + +.epoch.category20 div.ref.category2 { background-color: #aec7e8; } + +.epoch.category20 .category2 .line { stroke: #aec7e8; } + +.epoch.category20 .category2 .area, .epoch.category20 .category2 .dot { fill: #aec7e8; stroke: transparent; } + +.epoch.category20 .arc.category2 path { fill: #aec7e8; } + +.epoch.category20 .bar.category2 { fill: #aec7e8; } + +.epoch.category20 div.ref.category3 { background-color: #ff7f0e; } + +.epoch.category20 .category3 .line { stroke: #ff7f0e; } + +.epoch.category20 .category3 .area, .epoch.category20 .category3 .dot { fill: #ff7f0e; stroke: transparent; } + +.epoch.category20 .arc.category3 path { fill: #ff7f0e; } + +.epoch.category20 .bar.category3 { fill: #ff7f0e; } + +.epoch.category20 div.ref.category4 { background-color: #ffbb78; } + +.epoch.category20 .category4 .line { stroke: #ffbb78; } + +.epoch.category20 .category4 .area, .epoch.category20 .category4 .dot { fill: #ffbb78; stroke: transparent; } + +.epoch.category20 .arc.category4 path { fill: #ffbb78; } + +.epoch.category20 .bar.category4 { fill: #ffbb78; } + +.epoch.category20 div.ref.category5 { background-color: #2ca02c; } + +.epoch.category20 .category5 .line { stroke: #2ca02c; } + +.epoch.category20 .category5 .area, .epoch.category20 .category5 .dot { fill: #2ca02c; stroke: transparent; } + +.epoch.category20 .arc.category5 path { fill: #2ca02c; } + +.epoch.category20 .bar.category5 { fill: #2ca02c; } + +.epoch.category20 div.ref.category6 { background-color: #98df8a; } + +.epoch.category20 .category6 .line { stroke: #98df8a; } + +.epoch.category20 .category6 .area, .epoch.category20 .category6 .dot { fill: #98df8a; stroke: transparent; } + +.epoch.category20 .arc.category6 path { fill: #98df8a; } + +.epoch.category20 .bar.category6 { fill: #98df8a; } + +.epoch.category20 div.ref.category7 { background-color: #d62728; } + +.epoch.category20 .category7 .line { stroke: #d62728; } + +.epoch.category20 .category7 .area, .epoch.category20 .category7 .dot { fill: #d62728; stroke: transparent; } + +.epoch.category20 .arc.category7 path { fill: #d62728; } + +.epoch.category20 .bar.category7 { fill: #d62728; } + +.epoch.category20 div.ref.category8 { background-color: #ff9896; } + +.epoch.category20 .category8 .line { stroke: #ff9896; } + +.epoch.category20 .category8 .area, .epoch.category20 .category8 .dot { fill: #ff9896; stroke: transparent; } + +.epoch.category20 .arc.category8 path { fill: #ff9896; } + +.epoch.category20 .bar.category8 { fill: #ff9896; } + +.epoch.category20 div.ref.category9 { background-color: #9467bd; } + +.epoch.category20 .category9 .line { stroke: #9467bd; } + +.epoch.category20 .category9 .area, .epoch.category20 .category9 .dot { fill: #9467bd; stroke: transparent; } + +.epoch.category20 .arc.category9 path { fill: #9467bd; } + +.epoch.category20 .bar.category9 { fill: #9467bd; } + +.epoch.category20 div.ref.category10 { background-color: #c5b0d5; } + +.epoch.category20 .category10 .line { stroke: #c5b0d5; } + +.epoch.category20 .category10 .area, .epoch.category20 .category10 .dot { fill: #c5b0d5; stroke: transparent; } + +.epoch.category20 .arc.category10 path { fill: #c5b0d5; } + +.epoch.category20 .bar.category10 { fill: #c5b0d5; } + +.epoch.category20 div.ref.category11 { background-color: #8c564b; } + +.epoch.category20 .category11 .line { stroke: #8c564b; } + +.epoch.category20 .category11 .area, .epoch.category20 .category11 .dot { fill: #8c564b; stroke: transparent; } + +.epoch.category20 .arc.category11 path { fill: #8c564b; } + +.epoch.category20 .bar.category11 { fill: #8c564b; } + +.epoch.category20 div.ref.category12 { background-color: #c49c94; } + +.epoch.category20 .category12 .line { stroke: #c49c94; } + +.epoch.category20 .category12 .area, .epoch.category20 .category12 .dot { fill: #c49c94; stroke: transparent; } + +.epoch.category20 .arc.category12 path { fill: #c49c94; } + +.epoch.category20 .bar.category12 { fill: #c49c94; } + +.epoch.category20 div.ref.category13 { background-color: #e377c2; } + +.epoch.category20 .category13 .line { stroke: #e377c2; } + +.epoch.category20 .category13 .area, .epoch.category20 .category13 .dot { fill: #e377c2; stroke: transparent; } + +.epoch.category20 .arc.category13 path { fill: #e377c2; } + +.epoch.category20 .bar.category13 { fill: #e377c2; } + +.epoch.category20 div.ref.category14 { background-color: #f7b6d2; } + +.epoch.category20 .category14 .line { stroke: #f7b6d2; } + +.epoch.category20 .category14 .area, .epoch.category20 .category14 .dot { fill: #f7b6d2; stroke: transparent; } + +.epoch.category20 .arc.category14 path { fill: #f7b6d2; } + +.epoch.category20 .bar.category14 { fill: #f7b6d2; } + +.epoch.category20 div.ref.category15 { background-color: #7f7f7f; } + +.epoch.category20 .category15 .line { stroke: #7f7f7f; } + +.epoch.category20 .category15 .area, .epoch.category20 .category15 .dot { fill: #7f7f7f; stroke: transparent; } + +.epoch.category20 .arc.category15 path { fill: #7f7f7f; } + +.epoch.category20 .bar.category15 { fill: #7f7f7f; } + +.epoch.category20 div.ref.category16 { background-color: #c7c7c7; } + +.epoch.category20 .category16 .line { stroke: #c7c7c7; } + +.epoch.category20 .category16 .area, .epoch.category20 .category16 .dot { fill: #c7c7c7; stroke: transparent; } + +.epoch.category20 .arc.category16 path { fill: #c7c7c7; } + +.epoch.category20 .bar.category16 { fill: #c7c7c7; } + +.epoch.category20 div.ref.category17 { background-color: #bcbd22; } + +.epoch.category20 .category17 .line { stroke: #bcbd22; } + +.epoch.category20 .category17 .area, .epoch.category20 .category17 .dot { fill: #bcbd22; stroke: transparent; } + +.epoch.category20 .arc.category17 path { fill: #bcbd22; } + +.epoch.category20 .bar.category17 { fill: #bcbd22; } + +.epoch.category20 div.ref.category18 { background-color: #dbdb8d; } + +.epoch.category20 .category18 .line { stroke: #dbdb8d; } + +.epoch.category20 .category18 .area, .epoch.category20 .category18 .dot { fill: #dbdb8d; stroke: transparent; } + +.epoch.category20 .arc.category18 path { fill: #dbdb8d; } + +.epoch.category20 .bar.category18 { fill: #dbdb8d; } + +.epoch.category20 div.ref.category19 { background-color: #17becf; } + +.epoch.category20 .category19 .line { stroke: #17becf; } + +.epoch.category20 .category19 .area, .epoch.category20 .category19 .dot { fill: #17becf; stroke: transparent; } + +.epoch.category20 .arc.category19 path { fill: #17becf; } + +.epoch.category20 .bar.category19 { fill: #17becf; } + +.epoch.category20 div.ref.category20 { background-color: #9edae5; } + +.epoch.category20 .category20 .line { stroke: #9edae5; } + +.epoch.category20 .category20 .area, .epoch.category20 .category20 .dot { fill: #9edae5; stroke: transparent; } + +.epoch.category20 .arc.category20 path { fill: #9edae5; } + +.epoch.category20 .bar.category20 { fill: #9edae5; } + +.epoch.category20b div.ref.category1 { background-color: #393b79; } + +.epoch.category20b .category1 .line { stroke: #393b79; } + +.epoch.category20b .category1 .area, .epoch.category20b .category1 .dot { fill: #393b79; stroke: transparent; } + +.epoch.category20b .arc.category1 path { fill: #393b79; } + +.epoch.category20b .bar.category1 { fill: #393b79; } + +.epoch.category20b div.ref.category2 { background-color: #5254a3; } + +.epoch.category20b .category2 .line { stroke: #5254a3; } + +.epoch.category20b .category2 .area, .epoch.category20b .category2 .dot { fill: #5254a3; stroke: transparent; } + +.epoch.category20b .arc.category2 path { fill: #5254a3; } + +.epoch.category20b .bar.category2 { fill: #5254a3; } + +.epoch.category20b div.ref.category3 { background-color: #6b6ecf; } + +.epoch.category20b .category3 .line { stroke: #6b6ecf; } + +.epoch.category20b .category3 .area, .epoch.category20b .category3 .dot { fill: #6b6ecf; stroke: transparent; } + +.epoch.category20b .arc.category3 path { fill: #6b6ecf; } + +.epoch.category20b .bar.category3 { fill: #6b6ecf; } + +.epoch.category20b div.ref.category4 { background-color: #9c9ede; } + +.epoch.category20b .category4 .line { stroke: #9c9ede; } + +.epoch.category20b .category4 .area, .epoch.category20b .category4 .dot { fill: #9c9ede; stroke: transparent; } + +.epoch.category20b .arc.category4 path { fill: #9c9ede; } + +.epoch.category20b .bar.category4 { fill: #9c9ede; } + +.epoch.category20b div.ref.category5 { background-color: #637939; } + +.epoch.category20b .category5 .line { stroke: #637939; } + +.epoch.category20b .category5 .area, .epoch.category20b .category5 .dot { fill: #637939; stroke: transparent; } + +.epoch.category20b .arc.category5 path { fill: #637939; } + +.epoch.category20b .bar.category5 { fill: #637939; } + +.epoch.category20b div.ref.category6 { background-color: #8ca252; } + +.epoch.category20b .category6 .line { stroke: #8ca252; } + +.epoch.category20b .category6 .area, .epoch.category20b .category6 .dot { fill: #8ca252; stroke: transparent; } + +.epoch.category20b .arc.category6 path { fill: #8ca252; } + +.epoch.category20b .bar.category6 { fill: #8ca252; } + +.epoch.category20b div.ref.category7 { background-color: #b5cf6b; } + +.epoch.category20b .category7 .line { stroke: #b5cf6b; } + +.epoch.category20b .category7 .area, .epoch.category20b .category7 .dot { fill: #b5cf6b; stroke: transparent; } + +.epoch.category20b .arc.category7 path { fill: #b5cf6b; } + +.epoch.category20b .bar.category7 { fill: #b5cf6b; } + +.epoch.category20b div.ref.category8 { background-color: #cedb9c; } + +.epoch.category20b .category8 .line { stroke: #cedb9c; } + +.epoch.category20b .category8 .area, .epoch.category20b .category8 .dot { fill: #cedb9c; stroke: transparent; } + +.epoch.category20b .arc.category8 path { fill: #cedb9c; } + +.epoch.category20b .bar.category8 { fill: #cedb9c; } + +.epoch.category20b div.ref.category9 { background-color: #8c6d31; } + +.epoch.category20b .category9 .line { stroke: #8c6d31; } + +.epoch.category20b .category9 .area, .epoch.category20b .category9 .dot { fill: #8c6d31; stroke: transparent; } + +.epoch.category20b .arc.category9 path { fill: #8c6d31; } + +.epoch.category20b .bar.category9 { fill: #8c6d31; } + +.epoch.category20b div.ref.category10 { background-color: #bd9e39; } + +.epoch.category20b .category10 .line { stroke: #bd9e39; } + +.epoch.category20b .category10 .area, .epoch.category20b .category10 .dot { fill: #bd9e39; stroke: transparent; } + +.epoch.category20b .arc.category10 path { fill: #bd9e39; } + +.epoch.category20b .bar.category10 { fill: #bd9e39; } + +.epoch.category20b div.ref.category11 { background-color: #e7ba52; } + +.epoch.category20b .category11 .line { stroke: #e7ba52; } + +.epoch.category20b .category11 .area, .epoch.category20b .category11 .dot { fill: #e7ba52; stroke: transparent; } + +.epoch.category20b .arc.category11 path { fill: #e7ba52; } + +.epoch.category20b .bar.category11 { fill: #e7ba52; } + +.epoch.category20b div.ref.category12 { background-color: #e7cb94; } + +.epoch.category20b .category12 .line { stroke: #e7cb94; } + +.epoch.category20b .category12 .area, .epoch.category20b .category12 .dot { fill: #e7cb94; stroke: transparent; } + +.epoch.category20b .arc.category12 path { fill: #e7cb94; } + +.epoch.category20b .bar.category12 { fill: #e7cb94; } + +.epoch.category20b div.ref.category13 { background-color: #843c39; } + +.epoch.category20b .category13 .line { stroke: #843c39; } + +.epoch.category20b .category13 .area, .epoch.category20b .category13 .dot { fill: #843c39; stroke: transparent; } + +.epoch.category20b .arc.category13 path { fill: #843c39; } + +.epoch.category20b .bar.category13 { fill: #843c39; } + +.epoch.category20b div.ref.category14 { background-color: #ad494a; } + +.epoch.category20b .category14 .line { stroke: #ad494a; } + +.epoch.category20b .category14 .area, .epoch.category20b .category14 .dot { fill: #ad494a; stroke: transparent; } + +.epoch.category20b .arc.category14 path { fill: #ad494a; } + +.epoch.category20b .bar.category14 { fill: #ad494a; } + +.epoch.category20b div.ref.category15 { background-color: #d6616b; } + +.epoch.category20b .category15 .line { stroke: #d6616b; } + +.epoch.category20b .category15 .area, .epoch.category20b .category15 .dot { fill: #d6616b; stroke: transparent; } + +.epoch.category20b .arc.category15 path { fill: #d6616b; } + +.epoch.category20b .bar.category15 { fill: #d6616b; } + +.epoch.category20b div.ref.category16 { background-color: #e7969c; } + +.epoch.category20b .category16 .line { stroke: #e7969c; } + +.epoch.category20b .category16 .area, .epoch.category20b .category16 .dot { fill: #e7969c; stroke: transparent; } + +.epoch.category20b .arc.category16 path { fill: #e7969c; } + +.epoch.category20b .bar.category16 { fill: #e7969c; } + +.epoch.category20b div.ref.category17 { background-color: #7b4173; } + +.epoch.category20b .category17 .line { stroke: #7b4173; } + +.epoch.category20b .category17 .area, .epoch.category20b .category17 .dot { fill: #7b4173; stroke: transparent; } + +.epoch.category20b .arc.category17 path { fill: #7b4173; } + +.epoch.category20b .bar.category17 { fill: #7b4173; } + +.epoch.category20b div.ref.category18 { background-color: #a55194; } + +.epoch.category20b .category18 .line { stroke: #a55194; } + +.epoch.category20b .category18 .area, .epoch.category20b .category18 .dot { fill: #a55194; stroke: transparent; } + +.epoch.category20b .arc.category18 path { fill: #a55194; } + +.epoch.category20b .bar.category18 { fill: #a55194; } + +.epoch.category20b div.ref.category19 { background-color: #ce6dbd; } + +.epoch.category20b .category19 .line { stroke: #ce6dbd; } + +.epoch.category20b .category19 .area, .epoch.category20b .category19 .dot { fill: #ce6dbd; stroke: transparent; } + +.epoch.category20b .arc.category19 path { fill: #ce6dbd; } + +.epoch.category20b .bar.category19 { fill: #ce6dbd; } + +.epoch.category20b div.ref.category20 { background-color: #de9ed6; } + +.epoch.category20b .category20 .line { stroke: #de9ed6; } + +.epoch.category20b .category20 .area, .epoch.category20b .category20 .dot { fill: #de9ed6; stroke: transparent; } + +.epoch.category20b .arc.category20 path { fill: #de9ed6; } + +.epoch.category20b .bar.category20 { fill: #de9ed6; } + +.epoch.category20c div.ref.category1 { background-color: #3182bd; } + +.epoch.category20c .category1 .line { stroke: #3182bd; } + +.epoch.category20c .category1 .area, .epoch.category20c .category1 .dot { fill: #3182bd; stroke: transparent; } + +.epoch.category20c .arc.category1 path { fill: #3182bd; } + +.epoch.category20c .bar.category1 { fill: #3182bd; } + +.epoch.category20c div.ref.category2 { background-color: #6baed6; } + +.epoch.category20c .category2 .line { stroke: #6baed6; } + +.epoch.category20c .category2 .area, .epoch.category20c .category2 .dot { fill: #6baed6; stroke: transparent; } + +.epoch.category20c .arc.category2 path { fill: #6baed6; } + +.epoch.category20c .bar.category2 { fill: #6baed6; } + +.epoch.category20c div.ref.category3 { background-color: #9ecae1; } + +.epoch.category20c .category3 .line { stroke: #9ecae1; } + +.epoch.category20c .category3 .area, .epoch.category20c .category3 .dot { fill: #9ecae1; stroke: transparent; } + +.epoch.category20c .arc.category3 path { fill: #9ecae1; } + +.epoch.category20c .bar.category3 { fill: #9ecae1; } + +.epoch.category20c div.ref.category4 { background-color: #c6dbef; } + +.epoch.category20c .category4 .line { stroke: #c6dbef; } + +.epoch.category20c .category4 .area, .epoch.category20c .category4 .dot { fill: #c6dbef; stroke: transparent; } + +.epoch.category20c .arc.category4 path { fill: #c6dbef; } + +.epoch.category20c .bar.category4 { fill: #c6dbef; } + +.epoch.category20c div.ref.category5 { background-color: #e6550d; } + +.epoch.category20c .category5 .line { stroke: #e6550d; } + +.epoch.category20c .category5 .area, .epoch.category20c .category5 .dot { fill: #e6550d; stroke: transparent; } + +.epoch.category20c .arc.category5 path { fill: #e6550d; } + +.epoch.category20c .bar.category5 { fill: #e6550d; } + +.epoch.category20c div.ref.category6 { background-color: #fd8d3c; } + +.epoch.category20c .category6 .line { stroke: #fd8d3c; } + +.epoch.category20c .category6 .area, .epoch.category20c .category6 .dot { fill: #fd8d3c; stroke: transparent; } + +.epoch.category20c .arc.category6 path { fill: #fd8d3c; } + +.epoch.category20c .bar.category6 { fill: #fd8d3c; } + +.epoch.category20c div.ref.category7 { background-color: #fdae6b; } + +.epoch.category20c .category7 .line { stroke: #fdae6b; } + +.epoch.category20c .category7 .area, .epoch.category20c .category7 .dot { fill: #fdae6b; stroke: transparent; } + +.epoch.category20c .arc.category7 path { fill: #fdae6b; } + +.epoch.category20c .bar.category7 { fill: #fdae6b; } + +.epoch.category20c div.ref.category8 { background-color: #fdd0a2; } + +.epoch.category20c .category8 .line { stroke: #fdd0a2; } + +.epoch.category20c .category8 .area, .epoch.category20c .category8 .dot { fill: #fdd0a2; stroke: transparent; } + +.epoch.category20c .arc.category8 path { fill: #fdd0a2; } + +.epoch.category20c .bar.category8 { fill: #fdd0a2; } + +.epoch.category20c div.ref.category9 { background-color: #31a354; } + +.epoch.category20c .category9 .line { stroke: #31a354; } + +.epoch.category20c .category9 .area, .epoch.category20c .category9 .dot { fill: #31a354; stroke: transparent; } + +.epoch.category20c .arc.category9 path { fill: #31a354; } + +.epoch.category20c .bar.category9 { fill: #31a354; } + +.epoch.category20c div.ref.category10 { background-color: #74c476; } + +.epoch.category20c .category10 .line { stroke: #74c476; } + +.epoch.category20c .category10 .area, .epoch.category20c .category10 .dot { fill: #74c476; stroke: transparent; } + +.epoch.category20c .arc.category10 path { fill: #74c476; } + +.epoch.category20c .bar.category10 { fill: #74c476; } + +.epoch.category20c div.ref.category11 { background-color: #a1d99b; } + +.epoch.category20c .category11 .line { stroke: #a1d99b; } + +.epoch.category20c .category11 .area, .epoch.category20c .category11 .dot { fill: #a1d99b; stroke: transparent; } + +.epoch.category20c .arc.category11 path { fill: #a1d99b; } + +.epoch.category20c .bar.category11 { fill: #a1d99b; } + +.epoch.category20c div.ref.category12 { background-color: #c7e9c0; } + +.epoch.category20c .category12 .line { stroke: #c7e9c0; } + +.epoch.category20c .category12 .area, .epoch.category20c .category12 .dot { fill: #c7e9c0; stroke: transparent; } + +.epoch.category20c .arc.category12 path { fill: #c7e9c0; } + +.epoch.category20c .bar.category12 { fill: #c7e9c0; } + +.epoch.category20c div.ref.category13 { background-color: #756bb1; } + +.epoch.category20c .category13 .line { stroke: #756bb1; } + +.epoch.category20c .category13 .area, .epoch.category20c .category13 .dot { fill: #756bb1; stroke: transparent; } + +.epoch.category20c .arc.category13 path { fill: #756bb1; } + +.epoch.category20c .bar.category13 { fill: #756bb1; } + +.epoch.category20c div.ref.category14 { background-color: #9e9ac8; } + +.epoch.category20c .category14 .line { stroke: #9e9ac8; } + +.epoch.category20c .category14 .area, .epoch.category20c .category14 .dot { fill: #9e9ac8; stroke: transparent; } + +.epoch.category20c .arc.category14 path { fill: #9e9ac8; } + +.epoch.category20c .bar.category14 { fill: #9e9ac8; } + +.epoch.category20c div.ref.category15 { background-color: #bcbddc; } + +.epoch.category20c .category15 .line { stroke: #bcbddc; } + +.epoch.category20c .category15 .area, .epoch.category20c .category15 .dot { fill: #bcbddc; stroke: transparent; } + +.epoch.category20c .arc.category15 path { fill: #bcbddc; } + +.epoch.category20c .bar.category15 { fill: #bcbddc; } + +.epoch.category20c div.ref.category16 { background-color: #dadaeb; } + +.epoch.category20c .category16 .line { stroke: #dadaeb; } + +.epoch.category20c .category16 .area, .epoch.category20c .category16 .dot { fill: #dadaeb; stroke: transparent; } + +.epoch.category20c .arc.category16 path { fill: #dadaeb; } + +.epoch.category20c .bar.category16 { fill: #dadaeb; } + +.epoch.category20c div.ref.category17 { background-color: #636363; } + +.epoch.category20c .category17 .line { stroke: #636363; } + +.epoch.category20c .category17 .area, .epoch.category20c .category17 .dot { fill: #636363; stroke: transparent; } + +.epoch.category20c .arc.category17 path { fill: #636363; } + +.epoch.category20c .bar.category17 { fill: #636363; } + +.epoch.category20c div.ref.category18 { background-color: #969696; } + +.epoch.category20c .category18 .line { stroke: #969696; } + +.epoch.category20c .category18 .area, .epoch.category20c .category18 .dot { fill: #969696; stroke: transparent; } + +.epoch.category20c .arc.category18 path { fill: #969696; } + +.epoch.category20c .bar.category18 { fill: #969696; } + +.epoch.category20c div.ref.category19 { background-color: #bdbdbd; } + +.epoch.category20c .category19 .line { stroke: #bdbdbd; } + +.epoch.category20c .category19 .area, .epoch.category20c .category19 .dot { fill: #bdbdbd; stroke: transparent; } + +.epoch.category20c .arc.category19 path { fill: #bdbdbd; } + +.epoch.category20c .bar.category19 { fill: #bdbdbd; } + +.epoch.category20c div.ref.category20 { background-color: #d9d9d9; } + +.epoch.category20c .category20 .line { stroke: #d9d9d9; } + +.epoch.category20c .category20 .area, .epoch.category20c .category20 .dot { fill: #d9d9d9; stroke: transparent; } + +.epoch.category20c .arc.category20 path { fill: #d9d9d9; } + +.epoch.category20c .bar.category20 { fill: #d9d9d9; } + +/* Heatmap Colors The heatmap real-time graph uses color blending to choose the color for which to render each bucket. Basic d3 categorical colors do not work well in this instance because the colors in the sequence vary wildly in precieved luminosity. Below we define small subsets that should work well together because they are of similar luminosities. Note: darker colors work better since we use opacity to denote "height" or "intensity" for each bucket after picking a mix color. */ +.epoch .category1 .bucket, .epoch.heatmap5 .category1 .bucket { fill: #1f77b4; } + +.epoch .category2 .bucket, .epoch.heatmap5 .category2 .bucket { fill: #2ca02c; } + +.epoch .category3 .bucket, .epoch.heatmap5 .category3 .bucket { fill: #d62728; } + +.epoch .category4 .bucket, .epoch.heatmap5 .category4 .bucket { fill: #8c564b; } + +.epoch .category5 .bucket, .epoch.heatmap5 .category5 .bucket { fill: #7f7f7f; } + +/* theme/_dark.scss - Theme design for dark page backgrounds. Designed by Ryan Sandor Richards */ +.epoch-theme-dark .epoch .axis path, .epoch-theme-dark .epoch .axis line { stroke: #d0d0d0; } + +.epoch-theme-dark .epoch .axis .tick text { fill: #d0d0d0; } + +.epoch-theme-dark .arc.pie { stroke: #333; } + +.epoch-theme-dark .arc.pie text { fill: #333; } + +.epoch-theme-dark .epoch .gauge-labels .value { fill: #BBB; } + +.epoch-theme-dark .epoch .gauge .arc.outer { stroke: #999; } + +.epoch-theme-dark .epoch .gauge .arc.inner { stroke: #AAA; } + +.epoch-theme-dark .epoch .gauge .tick { stroke: #AAA; } + +.epoch-theme-dark .epoch .gauge .needle { fill: #F3DE88; } + +.epoch-theme-dark .epoch .gauge .needle-base { fill: #999; } + +.epoch-theme-dark .epoch div.ref.category1, .epoch-theme-dark .epoch.category10 div.ref.category1 { background-color: #909CFF; } + +.epoch-theme-dark .epoch .category1 .line, .epoch-theme-dark .epoch.category10 .category1 .line { stroke: #909CFF; } + +.epoch-theme-dark .epoch .category1 .area, .epoch-theme-dark .epoch .category1 .dot, .epoch-theme-dark .epoch.category10 .category1 .area, .epoch-theme-dark .epoch.category10 .category1 .dot { fill: #909CFF; stroke: transparent; } + +.epoch-theme-dark .epoch .arc.category1 path, .epoch-theme-dark .epoch.category10 .arc.category1 path { fill: #909CFF; } + +.epoch-theme-dark .epoch .bar.category1, .epoch-theme-dark .epoch.category10 .bar.category1 { fill: #909CFF; } + +.epoch-theme-dark .epoch div.ref.category2, .epoch-theme-dark .epoch.category10 div.ref.category2 { background-color: #FFAC89; } + +.epoch-theme-dark .epoch .category2 .line, .epoch-theme-dark .epoch.category10 .category2 .line { stroke: #FFAC89; } + +.epoch-theme-dark .epoch .category2 .area, .epoch-theme-dark .epoch .category2 .dot, .epoch-theme-dark .epoch.category10 .category2 .area, .epoch-theme-dark .epoch.category10 .category2 .dot { fill: #FFAC89; stroke: transparent; } + +.epoch-theme-dark .epoch .arc.category2 path, .epoch-theme-dark .epoch.category10 .arc.category2 path { fill: #FFAC89; } + +.epoch-theme-dark .epoch .bar.category2, .epoch-theme-dark .epoch.category10 .bar.category2 { fill: #FFAC89; } + +.epoch-theme-dark .epoch div.ref.category3, .epoch-theme-dark .epoch.category10 div.ref.category3 { background-color: #E889E8; } + +.epoch-theme-dark .epoch .category3 .line, .epoch-theme-dark .epoch.category10 .category3 .line { stroke: #E889E8; } + +.epoch-theme-dark .epoch .category3 .area, .epoch-theme-dark .epoch .category3 .dot, .epoch-theme-dark .epoch.category10 .category3 .area, .epoch-theme-dark .epoch.category10 .category3 .dot { fill: #E889E8; stroke: transparent; } + +.epoch-theme-dark .epoch .arc.category3 path, .epoch-theme-dark .epoch.category10 .arc.category3 path { fill: #E889E8; } + +.epoch-theme-dark .epoch .bar.category3, .epoch-theme-dark .epoch.category10 .bar.category3 { fill: #E889E8; } + +.epoch-theme-dark .epoch div.ref.category4, .epoch-theme-dark .epoch.category10 div.ref.category4 { background-color: #78E8D3; } + +.epoch-theme-dark .epoch .category4 .line, .epoch-theme-dark .epoch.category10 .category4 .line { stroke: #78E8D3; } + +.epoch-theme-dark .epoch .category4 .area, .epoch-theme-dark .epoch .category4 .dot, .epoch-theme-dark .epoch.category10 .category4 .area, .epoch-theme-dark .epoch.category10 .category4 .dot { fill: #78E8D3; stroke: transparent; } + +.epoch-theme-dark .epoch .arc.category4 path, .epoch-theme-dark .epoch.category10 .arc.category4 path { fill: #78E8D3; } + +.epoch-theme-dark .epoch .bar.category4, .epoch-theme-dark .epoch.category10 .bar.category4 { fill: #78E8D3; } + +.epoch-theme-dark .epoch div.ref.category5, .epoch-theme-dark .epoch.category10 div.ref.category5 { background-color: #C2FF97; } + +.epoch-theme-dark .epoch .category5 .line, .epoch-theme-dark .epoch.category10 .category5 .line { stroke: #C2FF97; } + +.epoch-theme-dark .epoch .category5 .area, .epoch-theme-dark .epoch .category5 .dot, .epoch-theme-dark .epoch.category10 .category5 .area, .epoch-theme-dark .epoch.category10 .category5 .dot { fill: #C2FF97; stroke: transparent; } + +.epoch-theme-dark .epoch .arc.category5 path, .epoch-theme-dark .epoch.category10 .arc.category5 path { fill: #C2FF97; } + +.epoch-theme-dark .epoch .bar.category5, .epoch-theme-dark .epoch.category10 .bar.category5 { fill: #C2FF97; } + +.epoch-theme-dark .epoch div.ref.category6, .epoch-theme-dark .epoch.category10 div.ref.category6 { background-color: #B7BCD1; } + +.epoch-theme-dark .epoch .category6 .line, .epoch-theme-dark .epoch.category10 .category6 .line { stroke: #B7BCD1; } + +.epoch-theme-dark .epoch .category6 .area, .epoch-theme-dark .epoch .category6 .dot, .epoch-theme-dark .epoch.category10 .category6 .area, .epoch-theme-dark .epoch.category10 .category6 .dot { fill: #B7BCD1; stroke: transparent; } + +.epoch-theme-dark .epoch .arc.category6 path, .epoch-theme-dark .epoch.category10 .arc.category6 path { fill: #B7BCD1; } + +.epoch-theme-dark .epoch .bar.category6, .epoch-theme-dark .epoch.category10 .bar.category6 { fill: #B7BCD1; } + +.epoch-theme-dark .epoch div.ref.category7, .epoch-theme-dark .epoch.category10 div.ref.category7 { background-color: #FF857F; } + +.epoch-theme-dark .epoch .category7 .line, .epoch-theme-dark .epoch.category10 .category7 .line { stroke: #FF857F; } + +.epoch-theme-dark .epoch .category7 .area, .epoch-theme-dark .epoch .category7 .dot, .epoch-theme-dark .epoch.category10 .category7 .area, .epoch-theme-dark .epoch.category10 .category7 .dot { fill: #FF857F; stroke: transparent; } + +.epoch-theme-dark .epoch .arc.category7 path, .epoch-theme-dark .epoch.category10 .arc.category7 path { fill: #FF857F; } + +.epoch-theme-dark .epoch .bar.category7, .epoch-theme-dark .epoch.category10 .bar.category7 { fill: #FF857F; } + +.epoch-theme-dark .epoch div.ref.category8, .epoch-theme-dark .epoch.category10 div.ref.category8 { background-color: #F3DE88; } + +.epoch-theme-dark .epoch .category8 .line, .epoch-theme-dark .epoch.category10 .category8 .line { stroke: #F3DE88; } + +.epoch-theme-dark .epoch .category8 .area, .epoch-theme-dark .epoch .category8 .dot, .epoch-theme-dark .epoch.category10 .category8 .area, .epoch-theme-dark .epoch.category10 .category8 .dot { fill: #F3DE88; stroke: transparent; } + +.epoch-theme-dark .epoch .arc.category8 path, .epoch-theme-dark .epoch.category10 .arc.category8 path { fill: #F3DE88; } + +.epoch-theme-dark .epoch .bar.category8, .epoch-theme-dark .epoch.category10 .bar.category8 { fill: #F3DE88; } + +.epoch-theme-dark .epoch div.ref.category9, .epoch-theme-dark .epoch.category10 div.ref.category9 { background-color: #C9935E; } + +.epoch-theme-dark .epoch .category9 .line, .epoch-theme-dark .epoch.category10 .category9 .line { stroke: #C9935E; } + +.epoch-theme-dark .epoch .category9 .area, .epoch-theme-dark .epoch .category9 .dot, .epoch-theme-dark .epoch.category10 .category9 .area, .epoch-theme-dark .epoch.category10 .category9 .dot { fill: #C9935E; stroke: transparent; } + +.epoch-theme-dark .epoch .arc.category9 path, .epoch-theme-dark .epoch.category10 .arc.category9 path { fill: #C9935E; } + +.epoch-theme-dark .epoch .bar.category9, .epoch-theme-dark .epoch.category10 .bar.category9 { fill: #C9935E; } + +.epoch-theme-dark .epoch div.ref.category10, .epoch-theme-dark .epoch.category10 div.ref.category10 { background-color: #A488FF; } + +.epoch-theme-dark .epoch .category10 .line, .epoch-theme-dark .epoch.category10 .category10 .line { stroke: #A488FF; } + +.epoch-theme-dark .epoch .category10 .area, .epoch-theme-dark .epoch .category10 .dot, .epoch-theme-dark .epoch.category10 .category10 .area, .epoch-theme-dark .epoch.category10 .category10 .dot { fill: #A488FF; stroke: transparent; } + +.epoch-theme-dark .epoch .arc.category10 path, .epoch-theme-dark .epoch.category10 .arc.category10 path { fill: #A488FF; } + +.epoch-theme-dark .epoch .bar.category10, .epoch-theme-dark .epoch.category10 .bar.category10 { fill: #A488FF; } + +.epoch-theme-dark .epoch.category20 div.ref.category1 { background-color: #909CFF; } + +.epoch-theme-dark .epoch.category20 .category1 .line { stroke: #909CFF; } + +.epoch-theme-dark .epoch.category20 .category1 .area, .epoch-theme-dark .epoch.category20 .category1 .dot { fill: #909CFF; stroke: transparent; } + +.epoch-theme-dark .epoch.category20 .arc.category1 path { fill: #909CFF; } + +.epoch-theme-dark .epoch.category20 .bar.category1 { fill: #909CFF; } + +.epoch-theme-dark .epoch.category20 div.ref.category2 { background-color: #626AAD; } + +.epoch-theme-dark .epoch.category20 .category2 .line { stroke: #626AAD; } + +.epoch-theme-dark .epoch.category20 .category2 .area, .epoch-theme-dark .epoch.category20 .category2 .dot { fill: #626AAD; stroke: transparent; } + +.epoch-theme-dark .epoch.category20 .arc.category2 path { fill: #626AAD; } + +.epoch-theme-dark .epoch.category20 .bar.category2 { fill: #626AAD; } + +.epoch-theme-dark .epoch.category20 div.ref.category3 { background-color: #FFAC89; } + +.epoch-theme-dark .epoch.category20 .category3 .line { stroke: #FFAC89; } + +.epoch-theme-dark .epoch.category20 .category3 .area, .epoch-theme-dark .epoch.category20 .category3 .dot { fill: #FFAC89; stroke: transparent; } + +.epoch-theme-dark .epoch.category20 .arc.category3 path { fill: #FFAC89; } + +.epoch-theme-dark .epoch.category20 .bar.category3 { fill: #FFAC89; } + +.epoch-theme-dark .epoch.category20 div.ref.category4 { background-color: #BD7F66; } + +.epoch-theme-dark .epoch.category20 .category4 .line { stroke: #BD7F66; } + +.epoch-theme-dark .epoch.category20 .category4 .area, .epoch-theme-dark .epoch.category20 .category4 .dot { fill: #BD7F66; stroke: transparent; } + +.epoch-theme-dark .epoch.category20 .arc.category4 path { fill: #BD7F66; } + +.epoch-theme-dark .epoch.category20 .bar.category4 { fill: #BD7F66; } + +.epoch-theme-dark .epoch.category20 div.ref.category5 { background-color: #E889E8; } + +.epoch-theme-dark .epoch.category20 .category5 .line { stroke: #E889E8; } + +.epoch-theme-dark .epoch.category20 .category5 .area, .epoch-theme-dark .epoch.category20 .category5 .dot { fill: #E889E8; stroke: transparent; } + +.epoch-theme-dark .epoch.category20 .arc.category5 path { fill: #E889E8; } + +.epoch-theme-dark .epoch.category20 .bar.category5 { fill: #E889E8; } + +.epoch-theme-dark .epoch.category20 div.ref.category6 { background-color: #995A99; } + +.epoch-theme-dark .epoch.category20 .category6 .line { stroke: #995A99; } + +.epoch-theme-dark .epoch.category20 .category6 .area, .epoch-theme-dark .epoch.category20 .category6 .dot { fill: #995A99; stroke: transparent; } + +.epoch-theme-dark .epoch.category20 .arc.category6 path { fill: #995A99; } + +.epoch-theme-dark .epoch.category20 .bar.category6 { fill: #995A99; } + +.epoch-theme-dark .epoch.category20 div.ref.category7 { background-color: #78E8D3; } + +.epoch-theme-dark .epoch.category20 .category7 .line { stroke: #78E8D3; } + +.epoch-theme-dark .epoch.category20 .category7 .area, .epoch-theme-dark .epoch.category20 .category7 .dot { fill: #78E8D3; stroke: transparent; } + +.epoch-theme-dark .epoch.category20 .arc.category7 path { fill: #78E8D3; } + +.epoch-theme-dark .epoch.category20 .bar.category7 { fill: #78E8D3; } + +.epoch-theme-dark .epoch.category20 div.ref.category8 { background-color: #4F998C; } + +.epoch-theme-dark .epoch.category20 .category8 .line { stroke: #4F998C; } + +.epoch-theme-dark .epoch.category20 .category8 .area, .epoch-theme-dark .epoch.category20 .category8 .dot { fill: #4F998C; stroke: transparent; } + +.epoch-theme-dark .epoch.category20 .arc.category8 path { fill: #4F998C; } + +.epoch-theme-dark .epoch.category20 .bar.category8 { fill: #4F998C; } + +.epoch-theme-dark .epoch.category20 div.ref.category9 { background-color: #C2FF97; } + +.epoch-theme-dark .epoch.category20 .category9 .line { stroke: #C2FF97; } + +.epoch-theme-dark .epoch.category20 .category9 .area, .epoch-theme-dark .epoch.category20 .category9 .dot { fill: #C2FF97; stroke: transparent; } + +.epoch-theme-dark .epoch.category20 .arc.category9 path { fill: #C2FF97; } + +.epoch-theme-dark .epoch.category20 .bar.category9 { fill: #C2FF97; } + +.epoch-theme-dark .epoch.category20 div.ref.category10 { background-color: #789E5E; } + +.epoch-theme-dark .epoch.category20 .category10 .line { stroke: #789E5E; } + +.epoch-theme-dark .epoch.category20 .category10 .area, .epoch-theme-dark .epoch.category20 .category10 .dot { fill: #789E5E; stroke: transparent; } + +.epoch-theme-dark .epoch.category20 .arc.category10 path { fill: #789E5E; } + +.epoch-theme-dark .epoch.category20 .bar.category10 { fill: #789E5E; } + +.epoch-theme-dark .epoch.category20 div.ref.category11 { background-color: #B7BCD1; } + +.epoch-theme-dark .epoch.category20 .category11 .line { stroke: #B7BCD1; } + +.epoch-theme-dark .epoch.category20 .category11 .area, .epoch-theme-dark .epoch.category20 .category11 .dot { fill: #B7BCD1; stroke: transparent; } + +.epoch-theme-dark .epoch.category20 .arc.category11 path { fill: #B7BCD1; } + +.epoch-theme-dark .epoch.category20 .bar.category11 { fill: #B7BCD1; } + +.epoch-theme-dark .epoch.category20 div.ref.category12 { background-color: #7F8391; } + +.epoch-theme-dark .epoch.category20 .category12 .line { stroke: #7F8391; } + +.epoch-theme-dark .epoch.category20 .category12 .area, .epoch-theme-dark .epoch.category20 .category12 .dot { fill: #7F8391; stroke: transparent; } + +.epoch-theme-dark .epoch.category20 .arc.category12 path { fill: #7F8391; } + +.epoch-theme-dark .epoch.category20 .bar.category12 { fill: #7F8391; } + +.epoch-theme-dark .epoch.category20 div.ref.category13 { background-color: #CCB889; } + +.epoch-theme-dark .epoch.category20 .category13 .line { stroke: #CCB889; } + +.epoch-theme-dark .epoch.category20 .category13 .area, .epoch-theme-dark .epoch.category20 .category13 .dot { fill: #CCB889; stroke: transparent; } + +.epoch-theme-dark .epoch.category20 .arc.category13 path { fill: #CCB889; } + +.epoch-theme-dark .epoch.category20 .bar.category13 { fill: #CCB889; } + +.epoch-theme-dark .epoch.category20 div.ref.category14 { background-color: #A1906B; } + +.epoch-theme-dark .epoch.category20 .category14 .line { stroke: #A1906B; } + +.epoch-theme-dark .epoch.category20 .category14 .area, .epoch-theme-dark .epoch.category20 .category14 .dot { fill: #A1906B; stroke: transparent; } + +.epoch-theme-dark .epoch.category20 .arc.category14 path { fill: #A1906B; } + +.epoch-theme-dark .epoch.category20 .bar.category14 { fill: #A1906B; } + +.epoch-theme-dark .epoch.category20 div.ref.category15 { background-color: #F3DE88; } + +.epoch-theme-dark .epoch.category20 .category15 .line { stroke: #F3DE88; } + +.epoch-theme-dark .epoch.category20 .category15 .area, .epoch-theme-dark .epoch.category20 .category15 .dot { fill: #F3DE88; stroke: transparent; } + +.epoch-theme-dark .epoch.category20 .arc.category15 path { fill: #F3DE88; } + +.epoch-theme-dark .epoch.category20 .bar.category15 { fill: #F3DE88; } + +.epoch-theme-dark .epoch.category20 div.ref.category16 { background-color: #A89A5E; } + +.epoch-theme-dark .epoch.category20 .category16 .line { stroke: #A89A5E; } + +.epoch-theme-dark .epoch.category20 .category16 .area, .epoch-theme-dark .epoch.category20 .category16 .dot { fill: #A89A5E; stroke: transparent; } + +.epoch-theme-dark .epoch.category20 .arc.category16 path { fill: #A89A5E; } + +.epoch-theme-dark .epoch.category20 .bar.category16 { fill: #A89A5E; } + +.epoch-theme-dark .epoch.category20 div.ref.category17 { background-color: #FF857F; } + +.epoch-theme-dark .epoch.category20 .category17 .line { stroke: #FF857F; } + +.epoch-theme-dark .epoch.category20 .category17 .area, .epoch-theme-dark .epoch.category20 .category17 .dot { fill: #FF857F; stroke: transparent; } + +.epoch-theme-dark .epoch.category20 .arc.category17 path { fill: #FF857F; } + +.epoch-theme-dark .epoch.category20 .bar.category17 { fill: #FF857F; } + +.epoch-theme-dark .epoch.category20 div.ref.category18 { background-color: #BA615D; } + +.epoch-theme-dark .epoch.category20 .category18 .line { stroke: #BA615D; } + +.epoch-theme-dark .epoch.category20 .category18 .area, .epoch-theme-dark .epoch.category20 .category18 .dot { fill: #BA615D; stroke: transparent; } + +.epoch-theme-dark .epoch.category20 .arc.category18 path { fill: #BA615D; } + +.epoch-theme-dark .epoch.category20 .bar.category18 { fill: #BA615D; } + +.epoch-theme-dark .epoch.category20 div.ref.category19 { background-color: #A488FF; } + +.epoch-theme-dark .epoch.category20 .category19 .line { stroke: #A488FF; } + +.epoch-theme-dark .epoch.category20 .category19 .area, .epoch-theme-dark .epoch.category20 .category19 .dot { fill: #A488FF; stroke: transparent; } + +.epoch-theme-dark .epoch.category20 .arc.category19 path { fill: #A488FF; } + +.epoch-theme-dark .epoch.category20 .bar.category19 { fill: #A488FF; } + +.epoch-theme-dark .epoch.category20 div.ref.category20 { background-color: #7662B8; } + +.epoch-theme-dark .epoch.category20 .category20 .line { stroke: #7662B8; } + +.epoch-theme-dark .epoch.category20 .category20 .area, .epoch-theme-dark .epoch.category20 .category20 .dot { fill: #7662B8; stroke: transparent; } + +.epoch-theme-dark .epoch.category20 .arc.category20 path { fill: #7662B8; } + +.epoch-theme-dark .epoch.category20 .bar.category20 { fill: #7662B8; } + +.epoch-theme-dark .epoch.category20b div.ref.category1 { background-color: #909CFF; } + +.epoch-theme-dark .epoch.category20b .category1 .line { stroke: #909CFF; } + +.epoch-theme-dark .epoch.category20b .category1 .area, .epoch-theme-dark .epoch.category20b .category1 .dot { fill: #909CFF; stroke: transparent; } + +.epoch-theme-dark .epoch.category20b .arc.category1 path { fill: #909CFF; } + +.epoch-theme-dark .epoch.category20b .bar.category1 { fill: #909CFF; } + +.epoch-theme-dark .epoch.category20b div.ref.category2 { background-color: #7680D1; } + +.epoch-theme-dark .epoch.category20b .category2 .line { stroke: #7680D1; } + +.epoch-theme-dark .epoch.category20b .category2 .area, .epoch-theme-dark .epoch.category20b .category2 .dot { fill: #7680D1; stroke: transparent; } + +.epoch-theme-dark .epoch.category20b .arc.category2 path { fill: #7680D1; } + +.epoch-theme-dark .epoch.category20b .bar.category2 { fill: #7680D1; } + +.epoch-theme-dark .epoch.category20b div.ref.category3 { background-color: #656DB2; } + +.epoch-theme-dark .epoch.category20b .category3 .line { stroke: #656DB2; } + +.epoch-theme-dark .epoch.category20b .category3 .area, .epoch-theme-dark .epoch.category20b .category3 .dot { fill: #656DB2; stroke: transparent; } + +.epoch-theme-dark .epoch.category20b .arc.category3 path { fill: #656DB2; } + +.epoch-theme-dark .epoch.category20b .bar.category3 { fill: #656DB2; } + +.epoch-theme-dark .epoch.category20b div.ref.category4 { background-color: #525992; } + +.epoch-theme-dark .epoch.category20b .category4 .line { stroke: #525992; } + +.epoch-theme-dark .epoch.category20b .category4 .area, .epoch-theme-dark .epoch.category20b .category4 .dot { fill: #525992; stroke: transparent; } + +.epoch-theme-dark .epoch.category20b .arc.category4 path { fill: #525992; } + +.epoch-theme-dark .epoch.category20b .bar.category4 { fill: #525992; } + +.epoch-theme-dark .epoch.category20b div.ref.category5 { background-color: #FFAC89; } + +.epoch-theme-dark .epoch.category20b .category5 .line { stroke: #FFAC89; } + +.epoch-theme-dark .epoch.category20b .category5 .area, .epoch-theme-dark .epoch.category20b .category5 .dot { fill: #FFAC89; stroke: transparent; } + +.epoch-theme-dark .epoch.category20b .arc.category5 path { fill: #FFAC89; } + +.epoch-theme-dark .epoch.category20b .bar.category5 { fill: #FFAC89; } + +.epoch-theme-dark .epoch.category20b div.ref.category6 { background-color: #D18D71; } + +.epoch-theme-dark .epoch.category20b .category6 .line { stroke: #D18D71; } + +.epoch-theme-dark .epoch.category20b .category6 .area, .epoch-theme-dark .epoch.category20b .category6 .dot { fill: #D18D71; stroke: transparent; } + +.epoch-theme-dark .epoch.category20b .arc.category6 path { fill: #D18D71; } + +.epoch-theme-dark .epoch.category20b .bar.category6 { fill: #D18D71; } + +.epoch-theme-dark .epoch.category20b div.ref.category7 { background-color: #AB735C; } + +.epoch-theme-dark .epoch.category20b .category7 .line { stroke: #AB735C; } + +.epoch-theme-dark .epoch.category20b .category7 .area, .epoch-theme-dark .epoch.category20b .category7 .dot { fill: #AB735C; stroke: transparent; } + +.epoch-theme-dark .epoch.category20b .arc.category7 path { fill: #AB735C; } + +.epoch-theme-dark .epoch.category20b .bar.category7 { fill: #AB735C; } + +.epoch-theme-dark .epoch.category20b div.ref.category8 { background-color: #92624E; } + +.epoch-theme-dark .epoch.category20b .category8 .line { stroke: #92624E; } + +.epoch-theme-dark .epoch.category20b .category8 .area, .epoch-theme-dark .epoch.category20b .category8 .dot { fill: #92624E; stroke: transparent; } + +.epoch-theme-dark .epoch.category20b .arc.category8 path { fill: #92624E; } + +.epoch-theme-dark .epoch.category20b .bar.category8 { fill: #92624E; } + +.epoch-theme-dark .epoch.category20b div.ref.category9 { background-color: #E889E8; } + +.epoch-theme-dark .epoch.category20b .category9 .line { stroke: #E889E8; } + +.epoch-theme-dark .epoch.category20b .category9 .area, .epoch-theme-dark .epoch.category20b .category9 .dot { fill: #E889E8; stroke: transparent; } + +.epoch-theme-dark .epoch.category20b .arc.category9 path { fill: #E889E8; } + +.epoch-theme-dark .epoch.category20b .bar.category9 { fill: #E889E8; } + +.epoch-theme-dark .epoch.category20b div.ref.category10 { background-color: #BA6EBA; } + +.epoch-theme-dark .epoch.category20b .category10 .line { stroke: #BA6EBA; } + +.epoch-theme-dark .epoch.category20b .category10 .area, .epoch-theme-dark .epoch.category20b .category10 .dot { fill: #BA6EBA; stroke: transparent; } + +.epoch-theme-dark .epoch.category20b .arc.category10 path { fill: #BA6EBA; } + +.epoch-theme-dark .epoch.category20b .bar.category10 { fill: #BA6EBA; } + +.epoch-theme-dark .epoch.category20b div.ref.category11 { background-color: #9B5C9B; } + +.epoch-theme-dark .epoch.category20b .category11 .line { stroke: #9B5C9B; } + +.epoch-theme-dark .epoch.category20b .category11 .area, .epoch-theme-dark .epoch.category20b .category11 .dot { fill: #9B5C9B; stroke: transparent; } + +.epoch-theme-dark .epoch.category20b .arc.category11 path { fill: #9B5C9B; } + +.epoch-theme-dark .epoch.category20b .bar.category11 { fill: #9B5C9B; } + +.epoch-theme-dark .epoch.category20b div.ref.category12 { background-color: #7B487B; } + +.epoch-theme-dark .epoch.category20b .category12 .line { stroke: #7B487B; } + +.epoch-theme-dark .epoch.category20b .category12 .area, .epoch-theme-dark .epoch.category20b .category12 .dot { fill: #7B487B; stroke: transparent; } + +.epoch-theme-dark .epoch.category20b .arc.category12 path { fill: #7B487B; } + +.epoch-theme-dark .epoch.category20b .bar.category12 { fill: #7B487B; } + +.epoch-theme-dark .epoch.category20b div.ref.category13 { background-color: #78E8D3; } + +.epoch-theme-dark .epoch.category20b .category13 .line { stroke: #78E8D3; } + +.epoch-theme-dark .epoch.category20b .category13 .area, .epoch-theme-dark .epoch.category20b .category13 .dot { fill: #78E8D3; stroke: transparent; } + +.epoch-theme-dark .epoch.category20b .arc.category13 path { fill: #78E8D3; } + +.epoch-theme-dark .epoch.category20b .bar.category13 { fill: #78E8D3; } + +.epoch-theme-dark .epoch.category20b div.ref.category14 { background-color: #60BAAA; } + +.epoch-theme-dark .epoch.category20b .category14 .line { stroke: #60BAAA; } + +.epoch-theme-dark .epoch.category20b .category14 .area, .epoch-theme-dark .epoch.category20b .category14 .dot { fill: #60BAAA; stroke: transparent; } + +.epoch-theme-dark .epoch.category20b .arc.category14 path { fill: #60BAAA; } + +.epoch-theme-dark .epoch.category20b .bar.category14 { fill: #60BAAA; } + +.epoch-theme-dark .epoch.category20b div.ref.category15 { background-color: #509B8D; } + +.epoch-theme-dark .epoch.category20b .category15 .line { stroke: #509B8D; } + +.epoch-theme-dark .epoch.category20b .category15 .area, .epoch-theme-dark .epoch.category20b .category15 .dot { fill: #509B8D; stroke: transparent; } + +.epoch-theme-dark .epoch.category20b .arc.category15 path { fill: #509B8D; } + +.epoch-theme-dark .epoch.category20b .bar.category15 { fill: #509B8D; } + +.epoch-theme-dark .epoch.category20b div.ref.category16 { background-color: #3F7B70; } + +.epoch-theme-dark .epoch.category20b .category16 .line { stroke: #3F7B70; } + +.epoch-theme-dark .epoch.category20b .category16 .area, .epoch-theme-dark .epoch.category20b .category16 .dot { fill: #3F7B70; stroke: transparent; } + +.epoch-theme-dark .epoch.category20b .arc.category16 path { fill: #3F7B70; } + +.epoch-theme-dark .epoch.category20b .bar.category16 { fill: #3F7B70; } + +.epoch-theme-dark .epoch.category20b div.ref.category17 { background-color: #C2FF97; } + +.epoch-theme-dark .epoch.category20b .category17 .line { stroke: #C2FF97; } + +.epoch-theme-dark .epoch.category20b .category17 .area, .epoch-theme-dark .epoch.category20b .category17 .dot { fill: #C2FF97; stroke: transparent; } + +.epoch-theme-dark .epoch.category20b .arc.category17 path { fill: #C2FF97; } + +.epoch-theme-dark .epoch.category20b .bar.category17 { fill: #C2FF97; } + +.epoch-theme-dark .epoch.category20b div.ref.category18 { background-color: #9FD17C; } + +.epoch-theme-dark .epoch.category20b .category18 .line { stroke: #9FD17C; } + +.epoch-theme-dark .epoch.category20b .category18 .area, .epoch-theme-dark .epoch.category20b .category18 .dot { fill: #9FD17C; stroke: transparent; } + +.epoch-theme-dark .epoch.category20b .arc.category18 path { fill: #9FD17C; } + +.epoch-theme-dark .epoch.category20b .bar.category18 { fill: #9FD17C; } + +.epoch-theme-dark .epoch.category20b div.ref.category19 { background-color: #7DA361; } + +.epoch-theme-dark .epoch.category20b .category19 .line { stroke: #7DA361; } + +.epoch-theme-dark .epoch.category20b .category19 .area, .epoch-theme-dark .epoch.category20b .category19 .dot { fill: #7DA361; stroke: transparent; } + +.epoch-theme-dark .epoch.category20b .arc.category19 path { fill: #7DA361; } + +.epoch-theme-dark .epoch.category20b .bar.category19 { fill: #7DA361; } + +.epoch-theme-dark .epoch.category20b div.ref.category20 { background-color: #65854E; } + +.epoch-theme-dark .epoch.category20b .category20 .line { stroke: #65854E; } + +.epoch-theme-dark .epoch.category20b .category20 .area, .epoch-theme-dark .epoch.category20b .category20 .dot { fill: #65854E; stroke: transparent; } + +.epoch-theme-dark .epoch.category20b .arc.category20 path { fill: #65854E; } + +.epoch-theme-dark .epoch.category20b .bar.category20 { fill: #65854E; } + +.epoch-theme-dark .epoch.category20c div.ref.category1 { background-color: #B7BCD1; } + +.epoch-theme-dark .epoch.category20c .category1 .line { stroke: #B7BCD1; } + +.epoch-theme-dark .epoch.category20c .category1 .area, .epoch-theme-dark .epoch.category20c .category1 .dot { fill: #B7BCD1; stroke: transparent; } + +.epoch-theme-dark .epoch.category20c .arc.category1 path { fill: #B7BCD1; } + +.epoch-theme-dark .epoch.category20c .bar.category1 { fill: #B7BCD1; } + +.epoch-theme-dark .epoch.category20c div.ref.category2 { background-color: #979DAD; } + +.epoch-theme-dark .epoch.category20c .category2 .line { stroke: #979DAD; } + +.epoch-theme-dark .epoch.category20c .category2 .area, .epoch-theme-dark .epoch.category20c .category2 .dot { fill: #979DAD; stroke: transparent; } + +.epoch-theme-dark .epoch.category20c .arc.category2 path { fill: #979DAD; } + +.epoch-theme-dark .epoch.category20c .bar.category2 { fill: #979DAD; } + +.epoch-theme-dark .epoch.category20c div.ref.category3 { background-color: #6E717D; } + +.epoch-theme-dark .epoch.category20c .category3 .line { stroke: #6E717D; } + +.epoch-theme-dark .epoch.category20c .category3 .area, .epoch-theme-dark .epoch.category20c .category3 .dot { fill: #6E717D; stroke: transparent; } + +.epoch-theme-dark .epoch.category20c .arc.category3 path { fill: #6E717D; } + +.epoch-theme-dark .epoch.category20c .bar.category3 { fill: #6E717D; } + +.epoch-theme-dark .epoch.category20c div.ref.category4 { background-color: #595C66; } + +.epoch-theme-dark .epoch.category20c .category4 .line { stroke: #595C66; } + +.epoch-theme-dark .epoch.category20c .category4 .area, .epoch-theme-dark .epoch.category20c .category4 .dot { fill: #595C66; stroke: transparent; } + +.epoch-theme-dark .epoch.category20c .arc.category4 path { fill: #595C66; } + +.epoch-theme-dark .epoch.category20c .bar.category4 { fill: #595C66; } + +.epoch-theme-dark .epoch.category20c div.ref.category5 { background-color: #FF857F; } + +.epoch-theme-dark .epoch.category20c .category5 .line { stroke: #FF857F; } + +.epoch-theme-dark .epoch.category20c .category5 .area, .epoch-theme-dark .epoch.category20c .category5 .dot { fill: #FF857F; stroke: transparent; } + +.epoch-theme-dark .epoch.category20c .arc.category5 path { fill: #FF857F; } + +.epoch-theme-dark .epoch.category20c .bar.category5 { fill: #FF857F; } + +.epoch-theme-dark .epoch.category20c div.ref.category6 { background-color: #DE746E; } + +.epoch-theme-dark .epoch.category20c .category6 .line { stroke: #DE746E; } + +.epoch-theme-dark .epoch.category20c .category6 .area, .epoch-theme-dark .epoch.category20c .category6 .dot { fill: #DE746E; stroke: transparent; } + +.epoch-theme-dark .epoch.category20c .arc.category6 path { fill: #DE746E; } + +.epoch-theme-dark .epoch.category20c .bar.category6 { fill: #DE746E; } + +.epoch-theme-dark .epoch.category20c div.ref.category7 { background-color: #B55F5A; } + +.epoch-theme-dark .epoch.category20c .category7 .line { stroke: #B55F5A; } + +.epoch-theme-dark .epoch.category20c .category7 .area, .epoch-theme-dark .epoch.category20c .category7 .dot { fill: #B55F5A; stroke: transparent; } + +.epoch-theme-dark .epoch.category20c .arc.category7 path { fill: #B55F5A; } + +.epoch-theme-dark .epoch.category20c .bar.category7 { fill: #B55F5A; } + +.epoch-theme-dark .epoch.category20c div.ref.category8 { background-color: #964E4B; } + +.epoch-theme-dark .epoch.category20c .category8 .line { stroke: #964E4B; } + +.epoch-theme-dark .epoch.category20c .category8 .area, .epoch-theme-dark .epoch.category20c .category8 .dot { fill: #964E4B; stroke: transparent; } + +.epoch-theme-dark .epoch.category20c .arc.category8 path { fill: #964E4B; } + +.epoch-theme-dark .epoch.category20c .bar.category8 { fill: #964E4B; } + +.epoch-theme-dark .epoch.category20c div.ref.category9 { background-color: #F3DE88; } + +.epoch-theme-dark .epoch.category20c .category9 .line { stroke: #F3DE88; } + +.epoch-theme-dark .epoch.category20c .category9 .area, .epoch-theme-dark .epoch.category20c .category9 .dot { fill: #F3DE88; stroke: transparent; } + +.epoch-theme-dark .epoch.category20c .arc.category9 path { fill: #F3DE88; } + +.epoch-theme-dark .epoch.category20c .bar.category9 { fill: #F3DE88; } + +.epoch-theme-dark .epoch.category20c div.ref.category10 { background-color: #DBC87B; } + +.epoch-theme-dark .epoch.category20c .category10 .line { stroke: #DBC87B; } + +.epoch-theme-dark .epoch.category20c .category10 .area, .epoch-theme-dark .epoch.category20c .category10 .dot { fill: #DBC87B; stroke: transparent; } + +.epoch-theme-dark .epoch.category20c .arc.category10 path { fill: #DBC87B; } + +.epoch-theme-dark .epoch.category20c .bar.category10 { fill: #DBC87B; } + +.epoch-theme-dark .epoch.category20c div.ref.category11 { background-color: #BAAA68; } + +.epoch-theme-dark .epoch.category20c .category11 .line { stroke: #BAAA68; } + +.epoch-theme-dark .epoch.category20c .category11 .area, .epoch-theme-dark .epoch.category20c .category11 .dot { fill: #BAAA68; stroke: transparent; } + +.epoch-theme-dark .epoch.category20c .arc.category11 path { fill: #BAAA68; } + +.epoch-theme-dark .epoch.category20c .bar.category11 { fill: #BAAA68; } + +.epoch-theme-dark .epoch.category20c div.ref.category12 { background-color: #918551; } + +.epoch-theme-dark .epoch.category20c .category12 .line { stroke: #918551; } + +.epoch-theme-dark .epoch.category20c .category12 .area, .epoch-theme-dark .epoch.category20c .category12 .dot { fill: #918551; stroke: transparent; } + +.epoch-theme-dark .epoch.category20c .arc.category12 path { fill: #918551; } + +.epoch-theme-dark .epoch.category20c .bar.category12 { fill: #918551; } + +.epoch-theme-dark .epoch.category20c div.ref.category13 { background-color: #C9935E; } + +.epoch-theme-dark .epoch.category20c .category13 .line { stroke: #C9935E; } + +.epoch-theme-dark .epoch.category20c .category13 .area, .epoch-theme-dark .epoch.category20c .category13 .dot { fill: #C9935E; stroke: transparent; } + +.epoch-theme-dark .epoch.category20c .arc.category13 path { fill: #C9935E; } + +.epoch-theme-dark .epoch.category20c .bar.category13 { fill: #C9935E; } + +.epoch-theme-dark .epoch.category20c div.ref.category14 { background-color: #B58455; } + +.epoch-theme-dark .epoch.category20c .category14 .line { stroke: #B58455; } + +.epoch-theme-dark .epoch.category20c .category14 .area, .epoch-theme-dark .epoch.category20c .category14 .dot { fill: #B58455; stroke: transparent; } + +.epoch-theme-dark .epoch.category20c .arc.category14 path { fill: #B58455; } + +.epoch-theme-dark .epoch.category20c .bar.category14 { fill: #B58455; } + +.epoch-theme-dark .epoch.category20c div.ref.category15 { background-color: #997048; } + +.epoch-theme-dark .epoch.category20c .category15 .line { stroke: #997048; } + +.epoch-theme-dark .epoch.category20c .category15 .area, .epoch-theme-dark .epoch.category20c .category15 .dot { fill: #997048; stroke: transparent; } + +.epoch-theme-dark .epoch.category20c .arc.category15 path { fill: #997048; } + +.epoch-theme-dark .epoch.category20c .bar.category15 { fill: #997048; } + +.epoch-theme-dark .epoch.category20c div.ref.category16 { background-color: #735436; } + +.epoch-theme-dark .epoch.category20c .category16 .line { stroke: #735436; } + +.epoch-theme-dark .epoch.category20c .category16 .area, .epoch-theme-dark .epoch.category20c .category16 .dot { fill: #735436; stroke: transparent; } + +.epoch-theme-dark .epoch.category20c .arc.category16 path { fill: #735436; } + +.epoch-theme-dark .epoch.category20c .bar.category16 { fill: #735436; } + +.epoch-theme-dark .epoch.category20c div.ref.category17 { background-color: #A488FF; } + +.epoch-theme-dark .epoch.category20c .category17 .line { stroke: #A488FF; } + +.epoch-theme-dark .epoch.category20c .category17 .area, .epoch-theme-dark .epoch.category20c .category17 .dot { fill: #A488FF; stroke: transparent; } + +.epoch-theme-dark .epoch.category20c .arc.category17 path { fill: #A488FF; } + +.epoch-theme-dark .epoch.category20c .bar.category17 { fill: #A488FF; } + +.epoch-theme-dark .epoch.category20c div.ref.category18 { background-color: #8670D1; } + +.epoch-theme-dark .epoch.category20c .category18 .line { stroke: #8670D1; } + +.epoch-theme-dark .epoch.category20c .category18 .area, .epoch-theme-dark .epoch.category20c .category18 .dot { fill: #8670D1; stroke: transparent; } + +.epoch-theme-dark .epoch.category20c .arc.category18 path { fill: #8670D1; } + +.epoch-theme-dark .epoch.category20c .bar.category18 { fill: #8670D1; } + +.epoch-theme-dark .epoch.category20c div.ref.category19 { background-color: #705CAD; } + +.epoch-theme-dark .epoch.category20c .category19 .line { stroke: #705CAD; } + +.epoch-theme-dark .epoch.category20c .category19 .area, .epoch-theme-dark .epoch.category20c .category19 .dot { fill: #705CAD; stroke: transparent; } + +.epoch-theme-dark .epoch.category20c .arc.category19 path { fill: #705CAD; } + +.epoch-theme-dark .epoch.category20c .bar.category19 { fill: #705CAD; } + +.epoch-theme-dark .epoch.category20c div.ref.category20 { background-color: #52447F; } + +.epoch-theme-dark .epoch.category20c .category20 .line { stroke: #52447F; } + +.epoch-theme-dark .epoch.category20c .category20 .area, .epoch-theme-dark .epoch.category20c .category20 .dot { fill: #52447F; stroke: transparent; } + +.epoch-theme-dark .epoch.category20c .arc.category20 path { fill: #52447F; } + +.epoch-theme-dark .epoch.category20c .bar.category20 { fill: #52447F; } diff --git a/debian/missing-sources/epoch.js b/debian/missing-sources/epoch.js new file mode 100644 index 0000000..92bc514 --- /dev/null +++ b/debian/missing-sources/epoch.js @@ -0,0 +1,3924 @@ +var base, base1, base2, base3; + +if (window.Epoch == null) { + window.Epoch = {}; +} + +if ((base = window.Epoch).Chart == null) { + base.Chart = {}; +} + +if ((base1 = window.Epoch).Time == null) { + base1.Time = {}; +} + +if ((base2 = window.Epoch).Util == null) { + base2.Util = {}; +} + +if ((base3 = window.Epoch).Formats == null) { + base3.Formats = {}; +} + +Epoch.warn = function(msg) { + return (console.warn || console.log)("Epoch Warning: " + msg); +}; + +Epoch.exception = function(msg) { + throw "Epoch Error: " + msg; +}; + +Epoch.TestContext = (function() { + var VOID_METHODS; + + VOID_METHODS = ['arc', 'arcTo', 'beginPath', 'bezierCurveTo', 'clearRect', 'clip', 'closePath', 'drawImage', 'fill', 'fillRect', 'fillText', 'moveTo', 'quadraticCurveTo', 'rect', 'restore', 'rotate', 'save', 'scale', 'scrollPathIntoView', 'setLineDash', 'setTransform', 'stroke', 'strokeRect', 'strokeText', 'transform', 'translate', 'lineTo']; + + function TestContext() { + var i, len, method; + this._log = []; + for (i = 0, len = VOID_METHODS.length; i < len; i++) { + method = VOID_METHODS[i]; + this._makeFauxMethod(method); + } + } + + TestContext.prototype._makeFauxMethod = function(name) { + return this[name] = function() { + var arg; + return this._log.push(name + "(" + (((function() { + var i, len, results; + results = []; + for (i = 0, len = arguments.length; i < len; i++) { + arg = arguments[i]; + results.push(arg.toString()); + } + return results; + }).apply(this, arguments)).join(',')) + ")"); + }; + }; + + TestContext.prototype.getImageData = function() { + var arg; + this._log.push("getImageData(" + (((function() { + var i, len, results; + results = []; + for (i = 0, len = arguments.length; i < len; i++) { + arg = arguments[i]; + results.push(arg.toString()); + } + return results; + }).apply(this, arguments)).join(',')) + ")"); + return { + width: 0, + height: 0, + resolution: 1.0, + data: [] + }; + }; + + return TestContext; + +})(); + +var ref, typeFunction, + hasProp = {}.hasOwnProperty; + +typeFunction = function(objectName) { + return function(v) { + return Object.prototype.toString.call(v) === ("[object " + objectName + "]"); + }; +}; + +Epoch.isArray = (ref = Array.isArray) != null ? ref : typeFunction('Array'); + +Epoch.isObject = typeFunction('Object'); + +Epoch.isString = typeFunction('String'); + +Epoch.isFunction = typeFunction('Function'); + +Epoch.isNumber = typeFunction('Number'); + +Epoch.isElement = function(v) { + if (typeof HTMLElement !== "undefined" && HTMLElement !== null) { + return v instanceof HTMLElement; + } else { + return (v != null) && Epoch.isObject(v) && v.nodeType === 1 && Epoch.isString(v.nodeName); + } +}; + +Epoch.isNonEmptyArray = function(v) { + return Epoch.isArray(v) && v.length > 0; +}; + +Epoch.Util.copy = function(original) { + var copy, k, v; + if (original == null) { + return null; + } + copy = {}; + for (k in original) { + if (!hasProp.call(original, k)) continue; + v = original[k]; + copy[k] = v; + } + return copy; +}; + +Epoch.Util.defaults = function(options, defaults) { + var bothAreObjects, def, k, opt, result, v; + result = Epoch.Util.copy(options); + for (k in defaults) { + if (!hasProp.call(defaults, k)) continue; + v = defaults[k]; + opt = options[k]; + def = defaults[k]; + bothAreObjects = Epoch.isObject(opt) && Epoch.isObject(def); + if ((opt != null) && (def != null)) { + if (bothAreObjects && !Epoch.isArray(opt)) { + result[k] = Epoch.Util.defaults(opt, def); + } else { + result[k] = opt; + } + } else if (opt != null) { + result[k] = opt; + } else { + result[k] = def; + } + } + return result; +}; + +Epoch.Util.formatSI = function(v, fixed, fixIntegers) { + var base, i, label, q, ref1; + if (fixed == null) { + fixed = 1; + } + if (fixIntegers == null) { + fixIntegers = false; + } + if (v < 1000) { + q = v; + if (!((q | 0) === q && !fixIntegers)) { + q = q.toFixed(fixed); + } + return q; + } + ref1 = ['K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']; + for (i in ref1) { + if (!hasProp.call(ref1, i)) continue; + label = ref1[i]; + base = Math.pow(10, ((i | 0) + 1) * 3); + if (v >= base && v < Math.pow(10, ((i | 0) + 2) * 3)) { + q = v / base; + if (!((q % 1) === 0 && !fixIntegers)) { + q = q.toFixed(fixed); + } + return q + " " + label; + } + } +}; + +Epoch.Util.formatBytes = function(v, fixed, fix_integers) { + var base, i, label, q, ref1; + if (fixed == null) { + fixed = 1; + } + if (fix_integers == null) { + fix_integers = false; + } + if (v < 1024) { + q = v; + if (!((q % 1) === 0 && !fix_integers)) { + q = q.toFixed(fixed); + } + return q + " B"; + } + ref1 = ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; + for (i in ref1) { + if (!hasProp.call(ref1, i)) continue; + label = ref1[i]; + base = Math.pow(1024, (i | 0) + 1); + if (v >= base && v < Math.pow(1024, (i | 0) + 2)) { + q = v / base; + if (!((q % 1) === 0 && !fix_integers)) { + q = q.toFixed(fixed); + } + return q + " " + label; + } + } +}; + +Epoch.Util.dasherize = function(str) { + return Epoch.Util.trim(str).replace("\n", '').replace(/\s+/g, '-').toLowerCase(); +}; + +Epoch.Util.domain = function(layers, key) { + var domain, entry, j, l, layer, len, len1, ref1, set; + if (key == null) { + key = 'x'; + } + set = {}; + domain = []; + for (j = 0, len = layers.length; j < len; j++) { + layer = layers[j]; + ref1 = layer.values; + for (l = 0, len1 = ref1.length; l < len1; l++) { + entry = ref1[l]; + if (set[entry[key]] != null) { + continue; + } + domain.push(entry[key]); + set[entry[key]] = true; + } + } + return domain; +}; + +Epoch.Util.trim = function(string) { + if (!Epoch.isString(string)) { + return null; + } + return string.replace(/^\s+/g, '').replace(/\s+$/g, ''); +}; + +Epoch.Util.getComputedStyle = function(element, pseudoElement) { + if (Epoch.isFunction(window.getComputedStyle)) { + return window.getComputedStyle(element, pseudoElement); + } else if (element.currentStyle != null) { + return element.currentStyle; + } +}; + +Epoch.Util.toRGBA = function(color, opacity) { + var all, b, g, parts, r, result, v; + if ((parts = color.match(/^rgba\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*[0-9\.]+\)/))) { + all = parts[0], r = parts[1], g = parts[2], b = parts[3]; + result = "rgba(" + r + "," + g + "," + b + "," + opacity + ")"; + } else if ((v = d3.rgb(color))) { + result = "rgba(" + v.r + "," + v.g + "," + v.b + "," + opacity + ")"; + } + return result; +}; + +Epoch.Util.getContext = function(node, type) { + if (type == null) { + type = '2d'; + } + return node.getContext(type); +}; + +Epoch.Events = (function() { + function Events() { + this._events = {}; + } + + Events.prototype.on = function(name, callback) { + var base1; + if (callback == null) { + return; + } + if ((base1 = this._events)[name] == null) { + base1[name] = []; + } + return this._events[name].push(callback); + }; + + Events.prototype.onAll = function(map) { + var callback, name, results; + if (!Epoch.isObject(map)) { + return; + } + results = []; + for (name in map) { + if (!hasProp.call(map, name)) continue; + callback = map[name]; + results.push(this.on(name, callback)); + } + return results; + }; + + Events.prototype.off = function(name, callback) { + var i, results; + if (!Epoch.isArray(this._events[name])) { + return; + } + if (callback == null) { + return delete this._events[name]; + } + results = []; + while ((i = this._events[name].indexOf(callback)) >= 0) { + results.push(this._events[name].splice(i, 1)); + } + return results; + }; + + Events.prototype.offAll = function(mapOrList) { + var callback, j, len, name, results, results1; + if (Epoch.isArray(mapOrList)) { + results = []; + for (j = 0, len = mapOrList.length; j < len; j++) { + name = mapOrList[j]; + results.push(this.off(name)); + } + return results; + } else if (Epoch.isObject(mapOrList)) { + results1 = []; + for (name in mapOrList) { + if (!hasProp.call(mapOrList, name)) continue; + callback = mapOrList[name]; + results1.push(this.off(name, callback)); + } + return results1; + } + }; + + Events.prototype.trigger = function(name) { + var args, callback, fn, i, j, len, ref1, results; + if (this._events[name] == null) { + return; + } + args = (function() { + var j, ref1, results; + results = []; + for (i = j = 1, ref1 = arguments.length; 1 <= ref1 ? j < ref1 : j > ref1; i = 1 <= ref1 ? ++j : --j) { + results.push(arguments[i]); + } + return results; + }).apply(this, arguments); + ref1 = this._events[name]; + results = []; + for (j = 0, len = ref1.length; j < len; j++) { + callback = ref1[j]; + fn = null; + if (Epoch.isString(callback)) { + fn = this[callback]; + } else if (Epoch.isFunction(callback)) { + fn = callback; + } + if (fn == null) { + Epoch.exception("Callback for event '" + name + "' is not a function or reference to a method."); + } + results.push(fn.apply(this, args)); + } + return results; + }; + + return Events; + +})(); + +Epoch.Util.flatten = function(multiarray) { + var array, item, j, l, len, len1, result; + if (!Array.isArray(multiarray)) { + throw new Error('Epoch.Util.flatten only accepts arrays'); + } + result = []; + for (j = 0, len = multiarray.length; j < len; j++) { + array = multiarray[j]; + if (Array.isArray(array)) { + for (l = 0, len1 = array.length; l < len1; l++) { + item = array[l]; + result.push(item); + } + } else { + result.push(array); + } + } + return result; +}; + +d3.selection.prototype.width = function(value) { + if ((value != null) && Epoch.isString(value)) { + return this.style('width', value); + } else if ((value != null) && Epoch.isNumber(value)) { + return this.style('width', value + "px"); + } else { + return +Epoch.Util.getComputedStyle(this.node(), null).width.replace('px', ''); + } +}; + +d3.selection.prototype.height = function(value) { + if ((value != null) && Epoch.isString(value)) { + return this.style('height', value); + } else if ((value != null) && Epoch.isNumber(value)) { + return this.style('height', value + "px"); + } else { + return +Epoch.Util.getComputedStyle(this.node(), null).height.replace('px', ''); + } +}; + +var d3Seconds; + +Epoch.Formats.regular = function(d) { + return d; +}; + +Epoch.Formats.si = function(d) { + return Epoch.Util.formatSI(d); +}; + +Epoch.Formats.percent = function(d) { + return (d * 100).toFixed(1) + "%"; +}; + +Epoch.Formats.seconds = function(t) { + return d3Seconds(new Date(t * 1000)); +}; + +d3Seconds = d3.time.format('%I:%M:%S %p'); + +Epoch.Formats.bytes = function(d) { + return Epoch.Util.formatBytes(d); +}; + +var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; + +Epoch.Chart.Base = (function(superClass) { + var defaults, optionListeners; + + extend(Base, superClass); + + defaults = { + width: 320, + height: 240, + dataFormat: null + }; + + optionListeners = { + 'option:width': 'dimensionsChanged', + 'option:height': 'dimensionsChanged', + 'layer:shown': 'layerChanged', + 'layer:hidden': 'layerChanged' + }; + + function Base(options1) { + this.options = options1 != null ? options1 : {}; + Base.__super__.constructor.call(this); + if (this.options.model) { + if (this.options.model.hasData() != null) { + this.setData(this.options.model.getData(this.options.type, this.options.dataFormat)); + } else { + this.setData(this.options.data || []); + } + this.options.model.on('data:updated', (function(_this) { + return function() { + return _this.setDataFromModel(); + }; + })(this)); + } else { + this.setData(this.options.data || []); + } + if (this.options.el != null) { + this.el = d3.select(this.options.el); + } + this.width = this.options.width; + this.height = this.options.height; + if (this.el != null) { + if (this.width == null) { + this.width = this.el.width(); + } + if (this.height == null) { + this.height = this.el.height(); + } + } else { + if (this.width == null) { + this.width = defaults.width; + } + if (this.height == null) { + this.height = defaults.height; + } + this.el = d3.select(document.createElement('DIV')).attr('width', this.width).attr('height', this.height); + } + this.onAll(optionListeners); + } + + Base.prototype._getAllOptions = function() { + return Epoch.Util.defaults({}, this.options); + }; + + Base.prototype._getOption = function(key) { + var parts, scope, subkey; + parts = key.split('.'); + scope = this.options; + while (parts.length && (scope != null)) { + subkey = parts.shift(); + scope = scope[subkey]; + } + return scope; + }; + + Base.prototype._setOption = function(key, value) { + var parts, scope, subkey; + parts = key.split('.'); + scope = this.options; + while (parts.length) { + subkey = parts.shift(); + if (parts.length === 0) { + scope[subkey] = arguments[1]; + this.trigger("option:" + arguments[0]); + return; + } + if (scope[subkey] == null) { + scope[subkey] = {}; + } + scope = scope[subkey]; + } + }; + + Base.prototype._setManyOptions = function(options, prefix) { + var key, results, value; + if (prefix == null) { + prefix = ''; + } + results = []; + for (key in options) { + if (!hasProp.call(options, key)) continue; + value = options[key]; + if (Epoch.isObject(value)) { + results.push(this._setManyOptions(value, (prefix + key) + ".")); + } else { + results.push(this._setOption(prefix + key, value)); + } + } + return results; + }; + + Base.prototype.option = function() { + if (arguments.length === 0) { + return this._getAllOptions(); + } else if (arguments.length === 1 && Epoch.isString(arguments[0])) { + return this._getOption(arguments[0]); + } else if (arguments.length === 2 && Epoch.isString(arguments[0])) { + return this._setOption(arguments[0], arguments[1]); + } else if (arguments.length === 1 && Epoch.isObject(arguments[0])) { + return this._setManyOptions(arguments[0]); + } + }; + + Base.prototype.setDataFromModel = function() { + var prepared; + prepared = this._prepareData(this.options.model.getData(this.options.type, this.options.dataFormat)); + this.data = this._annotateLayers(prepared); + return this.draw(); + }; + + Base.prototype.setData = function(data, options) { + var prepared; + if (options == null) { + options = {}; + } + prepared = this._prepareData((this.rawData = this._formatData(data))); + return this.data = this._annotateLayers(prepared); + }; + + Base.prototype._prepareData = function(data) { + return data; + }; + + Base.prototype._formatData = function(data) { + return Epoch.Data.formatData(data, this.options.type, this.options.dataFormat); + }; + + Base.prototype._annotateLayers = function(data) { + var category, classes, i, layer, len; + category = 1; + for (i = 0, len = data.length; i < len; i++) { + layer = data[i]; + classes = ['layer']; + classes.push("category" + category); + layer.category = category; + layer.visible = true; + if (layer.label != null) { + classes.push(Epoch.Util.dasherize(layer.label)); + } + layer.className = classes.join(' '); + category++; + } + return data; + }; + + Base.prototype._findLayer = function(labelOrIndex) { + var i, index, l, layer, len, ref; + layer = null; + if (Epoch.isString(labelOrIndex)) { + ref = this.data; + for (i = 0, len = ref.length; i < len; i++) { + l = ref[i]; + if (l.label === labelOrIndex) { + layer = l; + break; + } + } + } else if (Epoch.isNumber(labelOrIndex)) { + index = parseInt(labelOrIndex); + if (!(index < 0 || index >= this.data.length)) { + layer = this.data[index]; + } + } + return layer; + }; + + Base.prototype.showLayer = function(labelOrIndex) { + var layer; + if (!(layer = this._findLayer(labelOrIndex))) { + return; + } + if (layer.visible) { + return; + } + layer.visible = true; + return this.trigger('layer:shown'); + }; + + Base.prototype.hideLayer = function(labelOrIndex) { + var layer; + if (!(layer = this._findLayer(labelOrIndex))) { + return; + } + if (!layer.visible) { + return; + } + layer.visible = false; + return this.trigger('layer:hidden'); + }; + + Base.prototype.toggleLayer = function(labelOrIndex) { + var layer; + if (!(layer = this._findLayer(labelOrIndex))) { + return; + } + layer.visible = !layer.visible; + if (layer.visible) { + return this.trigger('layer:shown'); + } else { + return this.trigger('layer:hidden'); + } + }; + + Base.prototype.isLayerVisible = function(labelOrIndex) { + var layer; + if (!(layer = this._findLayer(labelOrIndex))) { + return null; + } + return layer.visible; + }; + + Base.prototype.getVisibleLayers = function() { + return this.data.filter(function(layer) { + return layer.visible; + }); + }; + + Base.prototype.update = function(data, draw) { + if (draw == null) { + draw = true; + } + this.setData(data); + if (draw) { + return this.draw(); + } + }; + + Base.prototype.draw = function() { + return this.trigger('draw'); + }; + + Base.prototype._getScaleDomain = function(givenDomain) { + var layers, maxFn, minFn, values; + if (Array.isArray(givenDomain)) { + return givenDomain; + } + if (Epoch.isString(givenDomain)) { + layers = this.getVisibleLayers().filter(function(l) { + return l.range === givenDomain; + }).map(function(l) { + return l.values; + }); + if ((layers != null) && layers.length) { + values = Epoch.Util.flatten(layers).map(function(d) { + return d.y; + }); + minFn = function(memo, curr) { + if (curr < memo) { + return curr; + } else { + return memo; + } + }; + maxFn = function(memo, curr) { + if (curr > memo) { + return curr; + } else { + return memo; + } + }; + return [values.reduce(minFn, values[0]), values.reduce(maxFn, values[0])]; + } + } + if (Array.isArray(this.options.range)) { + return this.options.range; + } else if (this.options.range && Array.isArray(this.options.range.left)) { + return this.options.range.left; + } else if (this.options.range && Array.isArray(this.options.range.right)) { + return this.options.range.right; + } else { + return this.extent(function(d) { + return d.y; + }); + } + }; + + Base.prototype.extent = function(cmp) { + return [ + d3.min(this.getVisibleLayers(), function(layer) { + return d3.min(layer.values, cmp); + }), d3.max(this.getVisibleLayers(), function(layer) { + return d3.max(layer.values, cmp); + }) + ]; + }; + + Base.prototype.dimensionsChanged = function() { + this.width = this.option('width') || this.width; + this.height = this.option('height') || this.height; + this.el.width(this.width); + return this.el.height(this.height); + }; + + Base.prototype.layerChanged = function() { + return this.draw(); + }; + + return Base; + +})(Epoch.Events); + +Epoch.Chart.SVG = (function(superClass) { + extend(SVG, superClass); + + function SVG(options1) { + this.options = options1 != null ? options1 : {}; + SVG.__super__.constructor.call(this, this.options); + if (this.el != null) { + this.svg = this.el.append('svg'); + } else { + this.svg = d3.select(document.createElement('svg')); + } + this.svg.attr({ + xmlns: 'http://www.w3.org/2000/svg', + width: this.width, + height: this.height + }); + } + + SVG.prototype.dimensionsChanged = function() { + SVG.__super__.dimensionsChanged.call(this); + return this.svg.attr('width', this.width).attr('height', this.height); + }; + + return SVG; + +})(Epoch.Chart.Base); + +Epoch.Chart.Canvas = (function(superClass) { + extend(Canvas, superClass); + + function Canvas(options1) { + this.options = options1 != null ? options1 : {}; + Canvas.__super__.constructor.call(this, this.options); + if (this.options.pixelRatio != null) { + this.pixelRatio = this.options.pixelRatio; + } else if (window.devicePixelRatio != null) { + this.pixelRatio = window.devicePixelRatio; + } else { + this.pixelRatio = 1; + } + this.canvas = d3.select(document.createElement('CANVAS')); + this.canvas.style({ + 'width': this.width + "px", + 'height': this.height + "px" + }); + this.canvas.attr({ + width: this.getWidth(), + height: this.getHeight() + }); + if (this.el != null) { + this.el.node().appendChild(this.canvas.node()); + } + this.ctx = Epoch.Util.getContext(this.canvas.node()); + } + + Canvas.prototype.getWidth = function() { + return this.width * this.pixelRatio; + }; + + Canvas.prototype.getHeight = function() { + return this.height * this.pixelRatio; + }; + + Canvas.prototype.clear = function() { + return this.ctx.clearRect(0, 0, this.getWidth(), this.getHeight()); + }; + + Canvas.prototype.getStyles = function(selector) { + return Epoch.QueryCSS.getStyles(selector, this.el); + }; + + Canvas.prototype.dimensionsChanged = function() { + Canvas.__super__.dimensionsChanged.call(this); + this.canvas.style({ + 'width': this.width + "px", + 'height': this.height + "px" + }); + return this.canvas.attr({ + width: this.getWidth(), + height: this.getHeight() + }); + }; + + Canvas.prototype.redraw = function() { + Epoch.QueryCSS.purge(); + return this.draw(); + }; + + return Canvas; + +})(Epoch.Chart.Base); + +var QueryCSS; + +QueryCSS = (function() { + var CONTAINER_HASH_ATTR, PUT_EXPR, REFERENCE_CONTAINER_ID, containerCount, logging, nextContainerId, put; + + function QueryCSS() {} + + REFERENCE_CONTAINER_ID = '_canvas_css_reference'; + + CONTAINER_HASH_ATTR = 'data-epoch-container-id'; + + containerCount = 0; + + nextContainerId = function() { + return "epoch-container-" + (containerCount++); + }; + + PUT_EXPR = /^([^#. ]+)?(#[^. ]+)?(\.[^# ]+)?$/; + + logging = false; + + put = function(selector) { + var classNames, element, id, match, tag, whole; + match = selector.match(PUT_EXPR); + if (match == null) { + return Epoch.error('Query CSS cannot match given selector: ' + selector); + } + whole = match[0], tag = match[1], id = match[2], classNames = match[3]; + tag = (tag != null ? tag : 'div').toUpperCase(); + element = document.createElement(tag); + if (id != null) { + element.id = id.substr(1); + } + if (classNames != null) { + element.className = classNames.substr(1).replace(/\./g, ' '); + } + return element; + }; + + QueryCSS.log = function(b) { + return logging = b; + }; + + QueryCSS.cache = {}; + + QueryCSS.styleList = ['fill', 'stroke', 'stroke-width']; + + QueryCSS.container = null; + + QueryCSS.purge = function() { + return QueryCSS.cache = {}; + }; + + QueryCSS.getContainer = function() { + var container; + if (QueryCSS.container != null) { + return QueryCSS.container; + } + container = document.createElement('DIV'); + container.id = REFERENCE_CONTAINER_ID; + document.body.appendChild(container); + return QueryCSS.container = d3.select(container); + }; + + QueryCSS.hash = function(selector, container) { + var containerId; + containerId = container.attr(CONTAINER_HASH_ATTR); + if (containerId == null) { + containerId = nextContainerId(); + container.attr(CONTAINER_HASH_ATTR, containerId); + } + return containerId + "__" + selector; + }; + + QueryCSS.getStyles = function(selector, container) { + var cache, cacheKey, el, element, i, j, k, len, len1, len2, name, parent, parentNode, parents, ref, ref1, ref2, root, sel, selectorList, styles, subSelector; + cacheKey = QueryCSS.hash(selector, container); + cache = QueryCSS.cache[cacheKey]; + if (cache != null) { + return cache; + } + parents = []; + parentNode = container.node().parentNode; + while ((parentNode != null) && parentNode.nodeName.toLowerCase() !== 'body') { + parents.unshift(parentNode); + parentNode = parentNode.parentNode; + } + parents.push(container.node()); + selectorList = []; + for (i = 0, len = parents.length; i < len; i++) { + element = parents[i]; + sel = element.nodeName.toLowerCase(); + if ((element.id != null) && element.id.length > 0) { + sel += '#' + element.id; + } + if ((element.className != null) && element.className.length > 0) { + sel += '.' + Epoch.Util.trim(element.className).replace(/\s+/g, '.'); + } + selectorList.push(sel); + } + selectorList.push('svg'); + ref1 = Epoch.Util.trim(selector).split(/\s+/); + for (j = 0, len1 = ref1.length; j < len1; j++) { + subSelector = ref1[j]; + selectorList.push(subSelector); + } + if (logging) { + console.log(selectorList); + } + parent = root = put(selectorList.shift()); + while (selectorList.length) { + el = put(selectorList.shift()); + parent.appendChild(el); + parent = el; + } + if (logging) { + console.log(root); + } + QueryCSS.getContainer().node().appendChild(root); + ref = d3.select('#' + REFERENCE_CONTAINER_ID + ' ' + selector); + styles = {}; + ref2 = QueryCSS.styleList; + for (k = 0, len2 = ref2.length; k < len2; k++) { + name = ref2[k]; + styles[name] = ref.style(name); + } + QueryCSS.cache[cacheKey] = styles; + QueryCSS.getContainer().html(''); + return styles; + }; + + return QueryCSS; + +})(); + +Epoch.QueryCSS = QueryCSS; + +var applyLayerLabel, base, + hasProp = {}.hasOwnProperty, + slice = [].slice; + +if (Epoch.Data == null) { + Epoch.Data = {}; +} + +if ((base = Epoch.Data).Format == null) { + base.Format = {}; +} + +applyLayerLabel = function(layer, options, i, keys) { + var autoLabels, keyLabels, label, labels, ref; + if (keys == null) { + keys = []; + } + ref = [options.labels, options.autoLabels, options.keyLabels], labels = ref[0], autoLabels = ref[1], keyLabels = ref[2]; + if ((labels != null) && Epoch.isArray(labels) && labels.length > i) { + layer.label = labels[i]; + } else if (keyLabels && keys.length > i) { + layer.label = keys[i]; + } else if (autoLabels) { + label = []; + while (i >= 0) { + label.push(String.fromCharCode(65 + (i % 26))); + i -= 26; + } + layer.label = label.join(''); + } + return layer; +}; + +Epoch.Data.Format.array = (function() { + var buildLayers, defaultOptions, format, formatBasicPlot, formatHeatmap, formatPie, formatTimePlot; + defaultOptions = { + x: function(d, i) { + return i; + }, + y: function(d, i) { + return d; + }, + time: function(d, i, startTime) { + return parseInt(startTime) + parseInt(i); + }, + type: 'area', + autoLabels: false, + labels: [], + startTime: parseInt(new Date().getTime() / 1000) + }; + buildLayers = function(data, options, mapFn) { + var i, result, series; + result = []; + if (Epoch.isArray(data[0])) { + for (i in data) { + if (!hasProp.call(data, i)) continue; + series = data[i]; + result.push(applyLayerLabel({ + values: series.map(mapFn) + }, options, parseInt(i))); + } + } else { + result.push(applyLayerLabel({ + values: data.map(mapFn) + }, options, 0)); + } + return result; + }; + formatBasicPlot = function(data, options) { + return buildLayers(data, options, function(d, i) { + return { + x: options.x(d, i), + y: options.y(d, i) + }; + }); + }; + formatTimePlot = function(data, options) { + return buildLayers(data, options, function(d, i) { + return { + time: options.time(d, i, options.startTime), + y: options.y(d, i) + }; + }); + }; + formatHeatmap = function(data, options) { + return buildLayers(data, options, function(d, i) { + return { + time: options.time(d, i, options.startTime), + histogram: d + }; + }); + }; + formatPie = function(data, options) { + var i, result, v; + result = []; + for (i in data) { + if (!hasProp.call(data, i)) continue; + v = data[i]; + if (!Epoch.isNumber(data[0])) { + return []; + } + result.push(applyLayerLabel({ + value: v + }, options, i)); + } + return result; + }; + format = function(data, options) { + var opt; + if (data == null) { + data = []; + } + if (options == null) { + options = {}; + } + if (!Epoch.isNonEmptyArray(data)) { + return []; + } + opt = Epoch.Util.defaults(options, defaultOptions); + if (opt.type === 'time.heatmap') { + return formatHeatmap(data, opt); + } else if (opt.type.match(/^time\./)) { + return formatTimePlot(data, opt); + } else if (opt.type === 'pie') { + return formatPie(data, opt); + } else { + return formatBasicPlot(data, opt); + } + }; + format.entry = function(datum, options) { + var d, data, k, layer, len, opt, ref, results; + if (options == null) { + options = {}; + } + if (options.type === 'time.gauge') { + if (datum == null) { + return 0; + } + opt = Epoch.Util.defaults(options, defaultOptions); + d = Epoch.isArray(datum) ? datum[0] : datum; + return opt.y(d, 0); + } + if (datum == null) { + return []; + } + if (options.startTime == null) { + options.startTime = parseInt(new Date().getTime() / 1000); + } + if (Epoch.isArray(datum)) { + data = datum.map(function(d) { + return [d]; + }); + } else { + data = [datum]; + } + ref = format(data, options); + results = []; + for (k = 0, len = ref.length; k < len; k++) { + layer = ref[k]; + results.push(layer.values[0]); + } + return results; + }; + return format; +})(); + +Epoch.Data.Format.tuple = (function() { + var buildLayers, defaultOptions, format; + defaultOptions = { + x: function(d, i) { + return d; + }, + y: function(d, i) { + return d; + }, + time: function(d, i) { + return d; + }, + type: 'area', + autoLabels: false, + labels: [] + }; + buildLayers = function(data, options, mapFn) { + var i, result, series; + if (!Epoch.isArray(data[0])) { + return []; + } + result = []; + if (Epoch.isArray(data[0][0])) { + for (i in data) { + if (!hasProp.call(data, i)) continue; + series = data[i]; + result.push(applyLayerLabel({ + values: series.map(mapFn) + }, options, parseInt(i))); + } + } else { + result.push(applyLayerLabel({ + values: data.map(mapFn) + }, options, 0)); + } + return result; + }; + format = function(data, options) { + var opt; + if (data == null) { + data = []; + } + if (options == null) { + options = {}; + } + if (!Epoch.isNonEmptyArray(data)) { + return []; + } + opt = Epoch.Util.defaults(options, defaultOptions); + if (opt.type === 'pie' || opt.type === 'time.heatmap' || opt.type === 'time.gauge') { + return []; + } else if (opt.type.match(/^time\./)) { + return buildLayers(data, opt, function(d, i) { + return { + time: opt.time(d[0], parseInt(i)), + y: opt.y(d[1], parseInt(i)) + }; + }); + } else { + return buildLayers(data, opt, function(d, i) { + return { + x: opt.x(d[0], parseInt(i)), + y: opt.y(d[1], parseInt(i)) + }; + }); + } + }; + format.entry = function(datum, options) { + var data, k, layer, len, ref, results; + if (options == null) { + options = {}; + } + if (datum == null) { + return []; + } + if (options.startTime == null) { + options.startTime = parseInt(new Date().getTime() / 1000); + } + if (Epoch.isArray(datum) && Epoch.isArray(datum[0])) { + data = datum.map(function(d) { + return [d]; + }); + } else { + data = [datum]; + } + ref = format(data, options); + results = []; + for (k = 0, len = ref.length; k < len; k++) { + layer = ref[k]; + results.push(layer.values[0]); + } + return results; + }; + return format; +})(); + +Epoch.Data.Format.keyvalue = (function() { + var buildLayers, defaultOptions, format, formatBasicPlot, formatTimePlot; + defaultOptions = { + type: 'area', + x: function(d, i) { + return parseInt(i); + }, + y: function(d, i) { + return d; + }, + time: function(d, i, startTime) { + return parseInt(startTime) + parseInt(i); + }, + labels: [], + autoLabels: false, + keyLabels: true, + startTime: parseInt(new Date().getTime() / 1000) + }; + buildLayers = function(data, keys, options, mapFn) { + var d, i, j, key, result, values; + result = []; + for (j in keys) { + if (!hasProp.call(keys, j)) continue; + key = keys[j]; + values = []; + for (i in data) { + if (!hasProp.call(data, i)) continue; + d = data[i]; + values.push(mapFn(d, key, parseInt(i))); + } + result.push(applyLayerLabel({ + values: values + }, options, parseInt(j), keys)); + } + return result; + }; + formatBasicPlot = function(data, keys, options) { + return buildLayers(data, keys, options, function(d, key, i) { + var x; + if (Epoch.isString(options.x)) { + x = d[options.x]; + } else { + x = options.x(d, parseInt(i)); + } + return { + x: x, + y: options.y(d[key], parseInt(i)) + }; + }); + }; + formatTimePlot = function(data, keys, options, rangeName) { + if (rangeName == null) { + rangeName = 'y'; + } + return buildLayers(data, keys, options, function(d, key, i) { + var value; + if (Epoch.isString(options.time)) { + value = { + time: d[options.time] + }; + } else { + value = { + time: options.time(d, parseInt(i), options.startTime) + }; + } + value[rangeName] = options.y(d[key], parseInt(i)); + return value; + }); + }; + format = function(data, keys, options) { + var opt; + if (data == null) { + data = []; + } + if (keys == null) { + keys = []; + } + if (options == null) { + options = {}; + } + if (!(Epoch.isNonEmptyArray(data) && Epoch.isNonEmptyArray(keys))) { + return []; + } + opt = Epoch.Util.defaults(options, defaultOptions); + if (opt.type === 'pie' || opt.type === 'time.gauge') { + return []; + } else if (opt.type === 'time.heatmap') { + return formatTimePlot(data, keys, opt, 'histogram'); + } else if (opt.type.match(/^time\./)) { + return formatTimePlot(data, keys, opt); + } else { + return formatBasicPlot(data, keys, opt); + } + }; + format.entry = function(datum, keys, options) { + var k, layer, len, ref, results; + if (keys == null) { + keys = []; + } + if (options == null) { + options = {}; + } + if (!((datum != null) && Epoch.isNonEmptyArray(keys))) { + return []; + } + if (options.startTime == null) { + options.startTime = parseInt(new Date().getTime() / 1000); + } + ref = format([datum], keys, options); + results = []; + for (k = 0, len = ref.length; k < len; k++) { + layer = ref[k]; + results.push(layer.values[0]); + } + return results; + }; + return format; +})(); + +Epoch.data = function() { + var args, formatFn, formatter; + formatter = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : []; + if ((formatFn = Epoch.Data.Format[formatter]) == null) { + return []; + } + return formatFn.apply(formatFn, args); +}; + +Epoch.Data.formatData = function(data, type, dataFormat) { + var a, args, k, len, opts, ref; + if (data == null) { + data = []; + } + if (!Epoch.isNonEmptyArray(data)) { + return data; + } + if (Epoch.isString(dataFormat)) { + opts = { + type: type + }; + return Epoch.data(dataFormat, data, opts); + } + if (!Epoch.isObject(dataFormat)) { + return data; + } + if (!((dataFormat.name != null) && Epoch.isString(dataFormat.name))) { + return data; + } + if (Epoch.Data.Format[dataFormat.name] == null) { + return data; + } + args = [dataFormat.name, data]; + if ((dataFormat["arguments"] != null) && Epoch.isArray(dataFormat["arguments"])) { + ref = dataFormat["arguments"]; + for (k = 0, len = ref.length; k < len; k++) { + a = ref[k]; + args.push(a); + } + } + if (dataFormat.options != null) { + opts = dataFormat.options; + if (type != null) { + if (opts.type == null) { + opts.type = type; + } + } + args.push(opts); + } else if (type != null) { + args.push({ + type: type + }); + } + return Epoch.data.apply(Epoch.data, args); +}; + +Epoch.Data.formatEntry = function(datum, type, format) { + var a, args, dataFormat, entry, k, len, opts, ref; + if (format == null) { + return datum; + } + if (Epoch.isString(format)) { + opts = { + type: type + }; + return Epoch.Data.Format[format].entry(datum, opts); + } + if (!Epoch.isObject(format)) { + return datum; + } + if (!((format.name != null) && Epoch.isString(format.name))) { + return datum; + } + if (Epoch.Data.Format[format.name] == null) { + return datum; + } + dataFormat = Epoch.Util.defaults(format, {}); + args = [datum]; + if ((dataFormat["arguments"] != null) && Epoch.isArray(dataFormat["arguments"])) { + ref = dataFormat["arguments"]; + for (k = 0, len = ref.length; k < len; k++) { + a = ref[k]; + args.push(a); + } + } + if (dataFormat.options != null) { + opts = dataFormat.options; + opts.type = type; + args.push(opts); + } else if (type != null) { + args.push({ + type: type + }); + } + entry = Epoch.Data.Format[dataFormat.name].entry; + return entry.apply(entry, args); +}; + +var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; + +Epoch.Model = (function(superClass) { + var defaults; + + extend(Model, superClass); + + defaults = { + dataFormat: null + }; + + function Model(options) { + if (options == null) { + options = {}; + } + Model.__super__.constructor.call(this); + options = Epoch.Util.defaults(options, defaults); + this.dataFormat = options.dataFormat; + this.data = options.data; + this.loading = false; + } + + Model.prototype.setData = function(data) { + this.data = data; + return this.trigger('data:updated'); + }; + + Model.prototype.push = function(entry) { + this.entry = entry; + return this.trigger('data:push'); + }; + + Model.prototype.hasData = function() { + return this.data != null; + }; + + Model.prototype.getData = function(type, dataFormat) { + if (dataFormat == null) { + dataFormat = this.dataFormat; + } + return Epoch.Data.formatData(this.data, type, dataFormat); + }; + + Model.prototype.getNext = function(type, dataFormat) { + if (dataFormat == null) { + dataFormat = this.dataFormat; + } + return Epoch.Data.formatEntry(this.entry, type, dataFormat); + }; + + return Model; + +})(Epoch.Events); + +var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; + +Epoch.Chart.Plot = (function(superClass) { + var defaultAxisMargins, defaults, optionListeners; + + extend(Plot, superClass); + + defaults = { + domain: null, + range: null, + axes: ['left', 'bottom'], + ticks: { + top: 14, + bottom: 14, + left: 5, + right: 5 + }, + tickFormats: { + top: Epoch.Formats.regular, + bottom: Epoch.Formats.regular, + left: Epoch.Formats.si, + right: Epoch.Formats.si + } + }; + + defaultAxisMargins = { + top: 25, + right: 50, + bottom: 25, + left: 50 + }; + + optionListeners = { + 'option:margins.top': 'marginsChanged', + 'option:margins.right': 'marginsChanged', + 'option:margins.bottom': 'marginsChanged', + 'option:margins.left': 'marginsChanged', + 'option:axes': 'axesChanged', + 'option:ticks.top': 'ticksChanged', + 'option:ticks.right': 'ticksChanged', + 'option:ticks.bottom': 'ticksChanged', + 'option:ticks.left': 'ticksChanged', + 'option:tickFormats.top': 'tickFormatsChanged', + 'option:tickFormats.right': 'tickFormatsChanged', + 'option:tickFormats.bottom': 'tickFormatsChanged', + 'option:tickFormats.left': 'tickFormatsChanged', + 'option:domain': 'domainChanged', + 'option:range': 'rangeChanged' + }; + + function Plot(options) { + var givenMargins, i, len, pos, ref; + this.options = options != null ? options : {}; + givenMargins = Epoch.Util.copy(this.options.margins) || {}; + Plot.__super__.constructor.call(this, this.options = Epoch.Util.defaults(this.options, defaults)); + this.margins = {}; + ref = ['top', 'right', 'bottom', 'left']; + for (i = 0, len = ref.length; i < len; i++) { + pos = ref[i]; + this.margins[pos] = (this.options.margins != null) && (this.options.margins[pos] != null) ? this.options.margins[pos] : this.hasAxis(pos) ? defaultAxisMargins[pos] : 6; + } + this.g = this.svg.append("g").attr("transform", "translate(" + this.margins.left + ", " + this.margins.top + ")"); + this.onAll(optionListeners); + } + + Plot.prototype.setTickFormat = function(axis, fn) { + return this.options.tickFormats[axis] = fn; + }; + + Plot.prototype.hasAxis = function(axis) { + return this.options.axes.indexOf(axis) > -1; + }; + + Plot.prototype.innerWidth = function() { + return this.width - (this.margins.left + this.margins.right); + }; + + Plot.prototype.innerHeight = function() { + return this.height - (this.margins.top + this.margins.bottom); + }; + + Plot.prototype.x = function() { + var domain, ref; + domain = (ref = this.options.domain) != null ? ref : this.extent(function(d) { + return d.x; + }); + return d3.scale.linear().domain(domain).range([0, this.innerWidth()]); + }; + + Plot.prototype.y = function(givenDomain) { + return d3.scale.linear().domain(this._getScaleDomain(givenDomain)).range([this.innerHeight(), 0]); + }; + + Plot.prototype.bottomAxis = function() { + return d3.svg.axis().scale(this.x()).orient('bottom').ticks(this.options.ticks.bottom).tickFormat(this.options.tickFormats.bottom); + }; + + Plot.prototype.topAxis = function() { + return d3.svg.axis().scale(this.x()).orient('top').ticks(this.options.ticks.top).tickFormat(this.options.tickFormats.top); + }; + + Plot.prototype.leftAxis = function() { + var range; + range = this.options.range ? this.options.range.left : null; + return d3.svg.axis().scale(this.y(range)).orient('left').ticks(this.options.ticks.left).tickFormat(this.options.tickFormats.left); + }; + + Plot.prototype.rightAxis = function() { + var range; + range = this.options.range ? this.options.range.right : null; + return d3.svg.axis().scale(this.y(range)).orient('right').ticks(this.options.ticks.right).tickFormat(this.options.tickFormats.right); + }; + + Plot.prototype.draw = function() { + if (this._axesDrawn) { + this._redrawAxes(); + } else { + this._drawAxes(); + } + return Plot.__super__.draw.call(this); + }; + + Plot.prototype._redrawAxes = function() { + if (this.hasAxis('bottom')) { + this.g.selectAll('.x.axis.bottom').transition().duration(500).ease('linear').call(this.bottomAxis()); + } + if (this.hasAxis('top')) { + this.g.selectAll('.x.axis.top').transition().duration(500).ease('linear').call(this.topAxis()); + } + if (this.hasAxis('left')) { + this.g.selectAll('.y.axis.left').transition().duration(500).ease('linear').call(this.leftAxis()); + } + if (this.hasAxis('right')) { + return this.g.selectAll('.y.axis.right').transition().duration(500).ease('linear').call(this.rightAxis()); + } + }; + + Plot.prototype._drawAxes = function() { + if (this.hasAxis('bottom')) { + this.g.append("g").attr("class", "x axis bottom").attr("transform", "translate(0, " + (this.innerHeight()) + ")").call(this.bottomAxis()); + } + if (this.hasAxis('top')) { + this.g.append("g").attr('class', 'x axis top').call(this.topAxis()); + } + if (this.hasAxis('left')) { + this.g.append("g").attr("class", "y axis left").call(this.leftAxis()); + } + if (this.hasAxis('right')) { + this.g.append('g').attr('class', 'y axis right').attr('transform', "translate(" + (this.innerWidth()) + ", 0)").call(this.rightAxis()); + } + return this._axesDrawn = true; + }; + + Plot.prototype.dimensionsChanged = function() { + Plot.__super__.dimensionsChanged.call(this); + this.g.selectAll('.axis').remove(); + this._axesDrawn = false; + return this.draw(); + }; + + Plot.prototype.marginsChanged = function() { + var pos, ref, size; + if (this.options.margins == null) { + return; + } + ref = this.options.margins; + for (pos in ref) { + if (!hasProp.call(ref, pos)) continue; + size = ref[pos]; + if (size == null) { + this.margins[pos] = 6; + } else { + this.margins[pos] = size; + } + } + this.g.transition().duration(750).attr("transform", "translate(" + this.margins.left + ", " + this.margins.top + ")"); + return this.draw(); + }; + + Plot.prototype.axesChanged = function() { + var i, len, pos, ref; + ref = ['top', 'right', 'bottom', 'left']; + for (i = 0, len = ref.length; i < len; i++) { + pos = ref[i]; + if ((this.options.margins != null) && (this.options.margins[pos] != null)) { + continue; + } + if (this.hasAxis(pos)) { + this.margins[pos] = defaultAxisMargins[pos]; + } else { + this.margins[pos] = 6; + } + } + this.g.transition().duration(750).attr("transform", "translate(" + this.margins.left + ", " + this.margins.top + ")"); + this.g.selectAll('.axis').remove(); + this._axesDrawn = false; + return this.draw(); + }; + + Plot.prototype.ticksChanged = function() { + return this.draw(); + }; + + Plot.prototype.tickFormatsChanged = function() { + return this.draw(); + }; + + Plot.prototype.domainChanged = function() { + return this.draw(); + }; + + Plot.prototype.rangeChanged = function() { + return this.draw(); + }; + + return Plot; + +})(Epoch.Chart.SVG); + +var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; + +Epoch.Chart.Area = (function(superClass) { + extend(Area, superClass); + + function Area(options) { + var base; + this.options = options != null ? options : {}; + if ((base = this.options).type == null) { + base.type = 'area'; + } + Area.__super__.constructor.call(this, this.options); + this.draw(); + } + + Area.prototype.y = function() { + var a, i, k, layer, len, ref, ref1, ref2, v; + a = []; + ref = this.getVisibleLayers(); + for (i = 0, len = ref.length; i < len; i++) { + layer = ref[i]; + ref1 = layer.values; + for (k in ref1) { + if (!hasProp.call(ref1, k)) continue; + v = ref1[k]; + if (a[k] != null) { + a[k] += v.y; + } + if (a[k] == null) { + a[k] = v.y; + } + } + } + return d3.scale.linear().domain((ref2 = this.options.range) != null ? ref2 : [0, d3.max(a)]).range([this.height - this.margins.top - this.margins.bottom, 0]); + }; + + Area.prototype.draw = function() { + var area, data, layer, layers, ref, stack, x, y; + ref = [this.x(), this.y(), this.getVisibleLayers()], x = ref[0], y = ref[1], layers = ref[2]; + this.g.selectAll('.layer').remove(); + if (layers.length === 0) { + return; + } + area = d3.svg.area().x(function(d) { + return x(d.x); + }).y0(function(d) { + return y(d.y0); + }).y1(function(d) { + return y(d.y0 + d.y); + }); + stack = d3.layout.stack().values(function(d) { + return d.values; + }); + data = stack(layers); + layer = this.g.selectAll('.layer').data(layers, function(d) { + return d.category; + }); + layer.select('.area').attr('d', function(d) { + return area(d.values); + }); + layer.enter().append('g').attr('class', function(d) { + return d.className; + }); + layer.append('path').attr('class', 'area').attr('d', function(d) { + return area(d.values); + }); + return Area.__super__.draw.call(this); + }; + + return Area; + +})(Epoch.Chart.Plot); + +var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; + +Epoch.Chart.Bar = (function(superClass) { + var defaults, horizontal_defaults, horizontal_specific, optionListeners; + + extend(Bar, superClass); + + defaults = { + type: 'bar', + style: 'grouped', + orientation: 'vertical', + padding: { + bar: 0.08, + group: 0.1 + }, + outerPadding: { + bar: 0.08, + group: 0.1 + } + }; + + horizontal_specific = { + tickFormats: { + top: Epoch.Formats.si, + bottom: Epoch.Formats.si, + left: Epoch.Formats.regular, + right: Epoch.Formats.regular + } + }; + + horizontal_defaults = Epoch.Util.defaults(horizontal_specific, defaults); + + optionListeners = { + 'option:orientation': 'orientationChanged', + 'option:padding': 'paddingChanged', + 'option:outerPadding': 'paddingChanged', + 'option:padding:bar': 'paddingChanged', + 'option:padding:group': 'paddingChanged', + 'option:outerPadding:bar': 'paddingChanged', + 'option:outerPadding:group': 'paddingChanged' + }; + + function Bar(options) { + this.options = options != null ? options : {}; + if (this._isHorizontal()) { + this.options = Epoch.Util.defaults(this.options, horizontal_defaults); + } else { + this.options = Epoch.Util.defaults(this.options, defaults); + } + Bar.__super__.constructor.call(this, this.options); + this.onAll(optionListeners); + this.draw(); + } + + Bar.prototype._isVertical = function() { + return this.options.orientation === 'vertical'; + }; + + Bar.prototype._isHorizontal = function() { + return this.options.orientation === 'horizontal'; + }; + + Bar.prototype.x = function() { + var extent; + if (this._isVertical()) { + return d3.scale.ordinal().domain(Epoch.Util.domain(this.getVisibleLayers())).rangeRoundBands([0, this.innerWidth()], this.options.padding.group, this.options.outerPadding.group); + } else { + extent = this.extent(function(d) { + return d.y; + }); + extent[0] = Math.min(0, extent[0]); + return d3.scale.linear().domain(extent).range([0, this.width - this.margins.left - this.margins.right]); + } + }; + + Bar.prototype.x1 = function(x0) { + var layer; + return d3.scale.ordinal().domain((function() { + var j, len, ref, results; + ref = this.getVisibleLayers(); + results = []; + for (j = 0, len = ref.length; j < len; j++) { + layer = ref[j]; + results.push(layer.category); + } + return results; + }).call(this)).rangeRoundBands([0, x0.rangeBand()], this.options.padding.bar, this.options.outerPadding.bar); + }; + + Bar.prototype.y = function() { + var extent; + if (this._isVertical()) { + extent = this.extent(function(d) { + return d.y; + }); + extent[0] = Math.min(0, extent[0]); + return d3.scale.linear().domain(extent).range([this.height - this.margins.top - this.margins.bottom, 0]); + } else { + return d3.scale.ordinal().domain(Epoch.Util.domain(this.getVisibleLayers())).rangeRoundBands([0, this.innerHeight()], this.options.padding.group, this.options.outerPadding.group); + } + }; + + Bar.prototype.y1 = function(y0) { + var layer; + return d3.scale.ordinal().domain((function() { + var j, len, ref, results; + ref = this.getVisibleLayers(); + results = []; + for (j = 0, len = ref.length; j < len; j++) { + layer = ref[j]; + results.push(layer.category); + } + return results; + }).call(this)).rangeRoundBands([0, y0.rangeBand()], this.options.padding.bar, this.options.outerPadding.bar); + }; + + Bar.prototype._remapData = function() { + var className, entry, j, k, l, layer, len, len1, map, name, ref, ref1, results, v; + map = {}; + ref = this.getVisibleLayers(); + for (j = 0, len = ref.length; j < len; j++) { + layer = ref[j]; + className = 'bar ' + layer.className.replace(/\s*layer\s*/, ''); + ref1 = layer.values; + for (l = 0, len1 = ref1.length; l < len1; l++) { + entry = ref1[l]; + if (map[name = entry.x] == null) { + map[name] = []; + } + map[entry.x].push({ + label: layer.category, + y: entry.y, + className: className + }); + } + } + results = []; + for (k in map) { + if (!hasProp.call(map, k)) continue; + v = map[k]; + results.push({ + group: k, + values: v + }); + } + return results; + }; + + Bar.prototype.draw = function() { + if (this._isVertical()) { + this._drawVertical(); + } else { + this._drawHorizontal(); + } + return Bar.__super__.draw.call(this); + }; + + Bar.prototype._drawVertical = function() { + var data, height, layer, rects, ref, x0, x1, y; + ref = [this.x(), this.y()], x0 = ref[0], y = ref[1]; + x1 = this.x1(x0); + height = this.height - this.margins.top - this.margins.bottom; + data = this._remapData(); + layer = this.g.selectAll(".layer").data(data, function(d) { + return d.group; + }); + layer.transition().duration(750).attr("transform", function(d) { + return "translate(" + (x0(d.group)) + ", 0)"; + }); + layer.enter().append("g").attr('class', 'layer').attr("transform", function(d) { + return "translate(" + (x0(d.group)) + ", 0)"; + }); + rects = layer.selectAll('rect').data(function(group) { + return group.values; + }); + rects.attr('class', function(d) { + return d.className; + }); + rects.transition().duration(600).attr('x', function(d) { + return x1(d.label); + }).attr('y', function(d) { + return y(d.y); + }).attr('width', x1.rangeBand()).attr('height', function(d) { + return height - y(d.y); + }); + rects.enter().append('rect').attr('class', function(d) { + return d.className; + }).attr('x', function(d) { + return x1(d.label); + }).attr('y', function(d) { + return y(d.y); + }).attr('width', x1.rangeBand()).attr('height', function(d) { + return height - y(d.y); + }); + rects.exit().transition().duration(150).style('opacity', '0').remove(); + return layer.exit().transition().duration(750).style('opacity', '0').remove(); + }; + + Bar.prototype._drawHorizontal = function() { + var data, layer, rects, ref, width, x, y0, y1; + ref = [this.x(), this.y()], x = ref[0], y0 = ref[1]; + y1 = this.y1(y0); + width = this.width - this.margins.left - this.margins.right; + data = this._remapData(); + layer = this.g.selectAll(".layer").data(data, function(d) { + return d.group; + }); + layer.transition().duration(750).attr("transform", function(d) { + return "translate(0, " + (y0(d.group)) + ")"; + }); + layer.enter().append("g").attr('class', 'layer').attr("transform", function(d) { + return "translate(0, " + (y0(d.group)) + ")"; + }); + rects = layer.selectAll('rect').data(function(group) { + return group.values; + }); + rects.attr('class', function(d) { + return d.className; + }); + rects.transition().duration(600).attr('x', function(d) { + return 0; + }).attr('y', function(d) { + return y1(d.label); + }).attr('height', y1.rangeBand()).attr('width', function(d) { + return x(d.y); + }); + rects.enter().append('rect').attr('class', function(d) { + return d.className; + }).attr('x', function(d) { + return 0; + }).attr('y', function(d) { + return y1(d.label); + }).attr('height', y1.rangeBand()).attr('width', function(d) { + return x(d.y); + }); + rects.exit().transition().duration(150).style('opacity', '0').remove(); + return layer.exit().transition().duration(750).style('opacity', '0').remove(); + }; + + Bar.prototype._getTickValues = function(numTicks, dataKey) { + var i, step, tickValues, total; + if (dataKey == null) { + dataKey = 'x'; + } + if (this.data[0] == null) { + return []; + } + total = this.data[0].values.length; + step = Math.ceil(total / numTicks) | 0; + return tickValues = (function() { + var j, ref, ref1, results; + results = []; + for (i = j = 0, ref = total, ref1 = step; ref1 > 0 ? j < ref : j > ref; i = j += ref1) { + results.push(this.data[0].values[i].x); + } + return results; + }).call(this); + }; + + Bar.prototype.bottomAxis = function() { + var axis; + axis = d3.svg.axis().scale(this.x()).orient('bottom').ticks(this.options.ticks.bottom).tickFormat(this.options.tickFormats.bottom); + if (this._isVertical() && (this.options.ticks.bottom != null)) { + axis.tickValues(this._getTickValues(this.options.ticks.bottom)); + } + return axis; + }; + + Bar.prototype.topAxis = function() { + var axis; + axis = d3.svg.axis().scale(this.x()).orient('top').ticks(this.options.ticks.top).tickFormat(this.options.tickFormats.top); + if (this._isVertical() && (this.options.ticks.top != null)) { + axis.tickValues(this._getTickValues(this.options.ticks.top)); + } + return axis; + }; + + Bar.prototype.leftAxis = function() { + var axis; + axis = d3.svg.axis().scale(this.y()).orient('left').ticks(this.options.ticks.left).tickFormat(this.options.tickFormats.left); + if (this._isHorizontal() && (this.options.ticks.left != null)) { + axis.tickValues(this._getTickValues(this.options.ticks.left)); + } + return axis; + }; + + Bar.prototype.rightAxis = function() { + var axis; + axis = d3.svg.axis().scale(this.y()).orient('right').ticks(this.options.ticks.right).tickFormat(this.options.tickFormats.right); + if (this._isHorizontal() && (this.options.ticks.right != null)) { + axis.tickValues(this._getTickValues(this.options.ticks.right)); + } + return axis; + }; + + Bar.prototype.orientationChanged = function() { + var bottom, left, right, top; + top = this.options.tickFormats.top; + bottom = this.options.tickFormats.bottom; + left = this.options.tickFormats.left; + right = this.options.tickFormats.right; + this.options.tickFormats.left = top; + this.options.tickFormats.right = bottom; + this.options.tickFormats.top = left; + this.options.tickFormats.bottom = right; + return this.draw(); + }; + + Bar.prototype.paddingChanged = function() { + return this.draw(); + }; + + return Bar; + +})(Epoch.Chart.Plot); + +var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; + +Epoch.Chart.Histogram = (function(superClass) { + var defaults, optionListeners; + + extend(Histogram, superClass); + + defaults = { + type: 'histogram', + domain: [0, 100], + bucketRange: [0, 100], + buckets: 10, + cutOutliers: false + }; + + optionListeners = { + 'option:bucketRange': 'bucketRangeChanged', + 'option:buckets': 'bucketsChanged', + 'option:cutOutliers': 'cutOutliersChanged' + }; + + function Histogram(options) { + this.options = options != null ? options : {}; + Histogram.__super__.constructor.call(this, this.options = Epoch.Util.defaults(this.options, defaults)); + this.onAll(optionListeners); + this.draw(); + } + + Histogram.prototype._prepareData = function(data) { + var bucketSize, buckets, i, index, j, k, l, layer, len, len1, point, prepared, preparedLayer, ref, v; + bucketSize = (this.options.bucketRange[1] - this.options.bucketRange[0]) / this.options.buckets; + prepared = []; + for (j = 0, len = data.length; j < len; j++) { + layer = data[j]; + buckets = (function() { + var l, ref, results; + results = []; + for (i = l = 0, ref = this.options.buckets; 0 <= ref ? l < ref : l > ref; i = 0 <= ref ? ++l : --l) { + results.push(0); + } + return results; + }).call(this); + ref = layer.values; + for (l = 0, len1 = ref.length; l < len1; l++) { + point = ref[l]; + index = parseInt((point.x - this.options.bucketRange[0]) / bucketSize); + if (this.options.cutOutliers && ((index < 0) || (index >= this.options.buckets))) { + continue; + } + if (index < 0) { + index = 0; + } else if (index >= this.options.buckets) { + index = this.options.buckets - 1; + } + buckets[index] += parseInt(point.y); + } + preparedLayer = { + values: buckets.map(function(d, i) { + return { + x: parseInt(i) * bucketSize, + y: d + }; + }) + }; + for (k in layer) { + if (!hasProp.call(layer, k)) continue; + v = layer[k]; + if (k !== 'values') { + preparedLayer[k] = v; + } + } + prepared.push(preparedLayer); + } + return prepared; + }; + + Histogram.prototype.resetData = function() { + this.setData(this.rawData); + return this.draw(); + }; + + Histogram.prototype.bucketRangeChanged = function() { + return this.resetData(); + }; + + Histogram.prototype.bucketsChanged = function() { + return this.resetData(); + }; + + Histogram.prototype.cutOutliersChanged = function() { + return this.resetData(); + }; + + return Histogram; + +})(Epoch.Chart.Bar); + +var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; + +Epoch.Chart.Line = (function(superClass) { + extend(Line, superClass); + + function Line(options) { + var base; + this.options = options != null ? options : {}; + if ((base = this.options).type == null) { + base.type = 'line'; + } + Line.__super__.constructor.call(this, this.options); + this.draw(); + } + + Line.prototype.line = function(layer) { + var ref, x, y; + ref = [this.x(), this.y(layer.range)], x = ref[0], y = ref[1]; + return d3.svg.line().x(function(d) { + return x(d.x); + }).y(function(d) { + return y(d.y); + }); + }; + + Line.prototype.draw = function() { + var layer, layers, ref, x, y; + ref = [this.x(), this.y(), this.getVisibleLayers()], x = ref[0], y = ref[1], layers = ref[2]; + if (layers.length === 0) { + return this.g.selectAll('.layer').remove(); + } + layer = this.g.selectAll('.layer').data(layers, function(d) { + return d.category; + }); + layer.select('.line').transition().duration(500).attr('d', (function(_this) { + return function(l) { + return _this.line(l)(l.values); + }; + })(this)); + layer.enter().append('g').attr('class', function(l) { + return l.className; + }).append('path').attr('class', 'line').attr('d', (function(_this) { + return function(l) { + return _this.line(l)(l.values); + }; + })(this)); + layer.exit().transition().duration(750).style('opacity', '0').remove(); + return Line.__super__.draw.call(this); + }; + + return Line; + +})(Epoch.Chart.Plot); + +var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; + +Epoch.Chart.Pie = (function(superClass) { + var defaults; + + extend(Pie, superClass); + + defaults = { + type: 'pie', + margin: 10, + inner: 0 + }; + + function Pie(options) { + this.options = options != null ? options : {}; + Pie.__super__.constructor.call(this, this.options = Epoch.Util.defaults(this.options, defaults)); + this.pie = d3.layout.pie().sort(null).value(function(d) { + return d.value; + }); + this.arc = d3.svg.arc().outerRadius((function(_this) { + return function() { + return (Math.max(_this.width, _this.height) / 2) - _this.options.margin; + }; + })(this)).innerRadius((function(_this) { + return function() { + return _this.options.inner; + }; + })(this)); + this.g = this.svg.append('g').attr("transform", "translate(" + (this.width / 2) + ", " + (this.height / 2) + ")"); + this.on('option:margin', 'marginChanged'); + this.on('option:inner', 'innerChanged'); + this.draw(); + } + + Pie.prototype.draw = function() { + var arcs, path, text; + this.g.selectAll('.arc').remove(); + arcs = this.g.selectAll(".arc").data(this.pie(this.getVisibleLayers()), function(d) { + return d.data.category; + }); + arcs.enter().append('g').attr('class', function(d) { + return "arc pie " + d.data.className; + }); + arcs.select('path').attr('d', this.arc); + arcs.select('text').attr("transform", (function(_this) { + return function(d) { + return "translate(" + (_this.arc.centroid(d)) + ")"; + }; + })(this)).text(function(d) { + return d.data.label || d.data.category; + }); + path = arcs.append("path").attr("d", this.arc).each(function(d) { + return this._current = d; + }); + text = arcs.append("text").attr("transform", (function(_this) { + return function(d) { + return "translate(" + (_this.arc.centroid(d)) + ")"; + }; + })(this)).attr("dy", ".35em").style("text-anchor", "middle").text(function(d) { + return d.data.label || d.data.category; + }); + return Pie.__super__.draw.call(this); + }; + + Pie.prototype.marginChanged = function() { + return this.draw(); + }; + + Pie.prototype.innerChanged = function() { + return this.draw(); + }; + + return Pie; + +})(Epoch.Chart.SVG); + +var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; + +Epoch.Chart.Scatter = (function(superClass) { + var defaults; + + extend(Scatter, superClass); + + defaults = { + type: 'scatter', + radius: 3.5, + axes: ['top', 'bottom', 'left', 'right'] + }; + + function Scatter(options) { + this.options = options != null ? options : {}; + Scatter.__super__.constructor.call(this, this.options = Epoch.Util.defaults(this.options, defaults)); + this.on('option:radius', 'radiusChanged'); + this.draw(); + } + + Scatter.prototype.draw = function() { + var dots, layer, layers, radius, ref, x, y; + ref = [this.x(), this.y(), this.getVisibleLayers()], x = ref[0], y = ref[1], layers = ref[2]; + radius = this.options.radius; + if (layers.length === 0) { + return this.g.selectAll('.layer').remove(); + } + layer = this.g.selectAll('.layer').data(layers, function(d) { + return d.category; + }); + layer.enter().append('g').attr('class', function(d) { + return d.className; + }); + dots = layer.selectAll('.dot').data(function(l) { + return l.values; + }); + dots.transition().duration(500).attr("r", function(d) { + var ref1; + return (ref1 = d.r) != null ? ref1 : radius; + }).attr("cx", function(d) { + return x(d.x); + }).attr("cy", function(d) { + return y(d.y); + }); + dots.enter().append('circle').attr('class', 'dot').attr("r", function(d) { + var ref1; + return (ref1 = d.r) != null ? ref1 : radius; + }).attr("cx", function(d) { + return x(d.x); + }).attr("cy", function(d) { + return y(d.y); + }); + dots.exit().transition().duration(750).style('opacity', 0).remove(); + layer.exit().transition().duration(750).style('opacity', 0).remove(); + return Scatter.__super__.draw.call(this); + }; + + Scatter.prototype.radiusChanged = function() { + return this.draw(); + }; + + return Scatter; + +})(Epoch.Chart.Plot); + +var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; + +Epoch.Time.Plot = (function(superClass) { + var defaultAxisMargins, defaults, optionListeners; + + extend(Plot, superClass); + + defaults = { + range: null, + fps: 24, + historySize: 120, + windowSize: 40, + queueSize: 10, + axes: ['bottom'], + ticks: { + time: 15, + left: 5, + right: 5 + }, + tickFormats: { + top: Epoch.Formats.seconds, + bottom: Epoch.Formats.seconds, + left: Epoch.Formats.si, + right: Epoch.Formats.si + } + }; + + defaultAxisMargins = { + top: 25, + right: 50, + bottom: 25, + left: 50 + }; + + optionListeners = { + 'option:margins': 'marginsChanged', + 'option:margins.top': 'marginsChanged', + 'option:margins.right': 'marginsChanged', + 'option:margins.bottom': 'marginsChanged', + 'option:margins.left': 'marginsChanged', + 'option:axes': 'axesChanged', + 'option:ticks': 'ticksChanged', + 'option:ticks.top': 'ticksChanged', + 'option:ticks.right': 'ticksChanged', + 'option:ticks.bottom': 'ticksChanged', + 'option:ticks.left': 'ticksChanged', + 'option:tickFormats': 'tickFormatsChanged', + 'option:tickFormats.top': 'tickFormatsChanged', + 'option:tickFormats.right': 'tickFormatsChanged', + 'option:tickFormats.bottom': 'tickFormatsChanged', + 'option:tickFormats.left': 'tickFormatsChanged' + }; + + function Plot(options) { + var givenMargins, l, len, pos, ref; + this.options = options; + givenMargins = Epoch.Util.copy(this.options.margins) || {}; + Plot.__super__.constructor.call(this, this.options = Epoch.Util.defaults(this.options, defaults)); + if (this.options.model) { + this.options.model.on('data:push', (function(_this) { + return function() { + return _this.pushFromModel(); + }; + })(this)); + } + this._queue = []; + this.margins = {}; + ref = ['top', 'right', 'bottom', 'left']; + for (l = 0, len = ref.length; l < len; l++) { + pos = ref[l]; + this.margins[pos] = (this.options.margins != null) && (this.options.margins[pos] != null) ? this.options.margins[pos] : this.hasAxis(pos) ? defaultAxisMargins[pos] : 6; + } + this.svg = this.el.insert('svg', ':first-child').attr('width', this.width).attr('height', this.height).style('z-index', '1000'); + if (this.el.style('position') !== 'absolute' && this.el.style('position') !== 'relative') { + this.el.style('position', 'relative'); + } + this.canvas.style({ + position: 'absolute', + 'z-index': '999' + }); + this._sizeCanvas(); + this.animation = { + interval: null, + active: false, + delta: (function(_this) { + return function() { + return -(_this.w() / _this.options.fps); + }; + })(this), + tickDelta: (function(_this) { + return function() { + return -((_this.w() / _this.pixelRatio) / _this.options.fps); + }; + })(this), + frame: 0, + duration: this.options.fps + }; + this._buildAxes(); + this.animationCallback = (function(_this) { + return function() { + return _this._animate(); + }; + })(this); + this.onAll(optionListeners); + } + + Plot.prototype._sizeCanvas = function() { + this.canvas.attr({ + width: this.innerWidth(), + height: this.innerHeight() + }); + return this.canvas.style({ + width: (this.innerWidth() / this.pixelRatio) + "px", + height: (this.innerHeight() / this.pixelRatio) + "px", + top: this.margins.top + "px", + left: this.margins.left + "px" + }); + }; + + Plot.prototype._buildAxes = function() { + this.svg.selectAll('.axis').remove(); + this._prepareTimeAxes(); + return this._prepareRangeAxes(); + }; + + Plot.prototype._annotateLayers = function(prepared) { + var classes, copy, data, i, layer, start; + data = []; + for (i in prepared) { + if (!hasProp.call(prepared, i)) continue; + layer = prepared[i]; + copy = Epoch.Util.copy(layer); + start = Math.max(0, layer.values.length - this.options.historySize); + copy.values = layer.values.slice(start); + classes = ['layer']; + classes.push("category" + ((i | 0) + 1)); + if (layer.label != null) { + classes.push(Epoch.Util.dasherize(layer.label)); + } + copy.className = classes.join(' '); + copy.visible = true; + data.push(copy); + } + return data; + }; + + Plot.prototype._offsetX = function() { + return 0; + }; + + Plot.prototype._prepareTimeAxes = function() { + var axis; + if (this.hasAxis('bottom')) { + axis = this.bottomAxis = this.svg.append('g').attr('class', "x axis bottom canvas").attr('transform', "translate(" + (this.margins.left - 1) + ", " + (this.innerHeight() / this.pixelRatio + this.margins.top) + ")"); + axis.append('path').attr('class', 'domain').attr('d', "M0,0H" + (this.innerWidth() / this.pixelRatio + 1)); + } + if (this.hasAxis('top')) { + axis = this.topAxis = this.svg.append('g').attr('class', "x axis top canvas").attr('transform', "translate(" + (this.margins.left - 1) + ", " + this.margins.top + ")"); + axis.append('path').attr('class', 'domain').attr('d', "M0,0H" + (this.innerWidth() / this.pixelRatio + 1)); + } + return this._resetInitialTimeTicks(); + }; + + Plot.prototype._resetInitialTimeTicks = function() { + var i, k, l, layer, len, ref, ref1, results, tickInterval; + tickInterval = this.options.ticks.time; + this._ticks = []; + this._tickTimer = tickInterval; + if (this.bottomAxis != null) { + this.bottomAxis.selectAll('.tick').remove(); + } + if (this.topAxis != null) { + this.topAxis.selectAll('.tick').remove(); + } + ref = this.data; + results = []; + for (l = 0, len = ref.length; l < len; l++) { + layer = ref[l]; + if (!Epoch.isNonEmptyArray(layer.values)) { + continue; + } + ref1 = [this.options.windowSize - 1, layer.values.length - 1], i = ref1[0], k = ref1[1]; + while (i >= 0 && k >= 0) { + this._pushTick(i, layer.values[k].time, false, true); + i -= tickInterval; + k -= tickInterval; + } + break; + } + return results; + }; + + Plot.prototype._prepareRangeAxes = function() { + if (this.hasAxis('left')) { + this.svg.append("g").attr("class", "y axis left").attr('transform', "translate(" + (this.margins.left - 1) + ", " + this.margins.top + ")").call(this.leftAxis()); + } + if (this.hasAxis('right')) { + return this.svg.append('g').attr('class', 'y axis right').attr('transform', "translate(" + (this.width - this.margins.right) + ", " + this.margins.top + ")").call(this.rightAxis()); + } + }; + + Plot.prototype.leftAxis = function() { + var axis, ticks; + ticks = this.options.ticks.left; + axis = d3.svg.axis().scale(this.ySvgLeft()).orient('left').tickFormat(this.options.tickFormats.left); + if (ticks === 2) { + return axis.tickValues(this.extent(function(d) { + return d.y; + })); + } else { + return axis.ticks(ticks); + } + }; + + Plot.prototype.rightAxis = function() { + var axis, extent, ticks; + extent = this.extent(function(d) { + return d.y; + }); + ticks = this.options.ticks.right; + axis = d3.svg.axis().scale(this.ySvgRight()).orient('right').tickFormat(this.options.tickFormats.right); + if (ticks === 2) { + return axis.tickValues(this.extent(function(d) { + return d.y; + })); + } else { + return axis.ticks(ticks); + } + }; + + Plot.prototype.hasAxis = function(name) { + return this.options.axes.indexOf(name) > -1; + }; + + Plot.prototype.innerWidth = function() { + return (this.width - (this.margins.left + this.margins.right)) * this.pixelRatio; + }; + + Plot.prototype.innerHeight = function() { + return (this.height - (this.margins.top + this.margins.bottom)) * this.pixelRatio; + }; + + Plot.prototype._prepareEntry = function(entry) { + return entry; + }; + + Plot.prototype._prepareLayers = function(layers) { + return layers; + }; + + Plot.prototype._startTransition = function() { + if (this.animation.active === true || this._queue.length === 0) { + return; + } + this.trigger('transition:start'); + this._shift(); + this.animation.active = true; + return this.animation.interval = setInterval(this.animationCallback, 1000 / this.options.fps); + }; + + Plot.prototype._stopTransition = function() { + var firstTick, l, lastTick, layer, len, ref, ref1; + if (!this.inTransition()) { + return; + } + ref = this.data; + for (l = 0, len = ref.length; l < len; l++) { + layer = ref[l]; + if (!(layer.values.length > this.options.windowSize + 1)) { + continue; + } + layer.values.shift(); + } + ref1 = [this._ticks[0], this._ticks[this._ticks.length - 1]], firstTick = ref1[0], lastTick = ref1[1]; + if ((lastTick != null) && lastTick.enter) { + lastTick.enter = false; + lastTick.opacity = 1; + } + if ((firstTick != null) && firstTick.exit) { + this._shiftTick(); + } + this.animation.frame = 0; + this.trigger('transition:end'); + if (this._queue.length > 0) { + return this._shift(); + } else { + this.animation.active = false; + return clearInterval(this.animation.interval); + } + }; + + Plot.prototype.inTransition = function() { + return this.animation.active; + }; + + Plot.prototype.push = function(layers) { + layers = this._prepareLayers(layers); + if (this._queue.length > this.options.queueSize) { + this._queue.splice(this.options.queueSize, this._queue.length - this.options.queueSize); + } + if (this._queue.length === this.options.queueSize) { + return false; + } + this._queue.push(layers.map((function(_this) { + return function(entry) { + return _this._prepareEntry(entry); + }; + })(this))); + this.trigger('push'); + if (!this.inTransition()) { + return this._startTransition(); + } + }; + + Plot.prototype.pushFromModel = function() { + return this.push(this.options.model.getNext(this.options.type, this.options.dataFormat)); + }; + + Plot.prototype._shift = function() { + var entry, i, layer, ref; + this.trigger('before:shift'); + entry = this._queue.shift(); + ref = this.data; + for (i in ref) { + if (!hasProp.call(ref, i)) continue; + layer = ref[i]; + layer.values.push(entry[i]); + } + this._updateTicks(entry[0].time); + this._transitionRangeAxes(); + return this.trigger('after:shift'); + }; + + Plot.prototype._transitionRangeAxes = function() { + if (this.hasAxis('left')) { + this.svg.selectAll('.y.axis.left').transition().duration(500).ease('linear').call(this.leftAxis()); + } + if (this.hasAxis('right')) { + return this.svg.selectAll('.y.axis.right').transition().duration(500).ease('linear').call(this.rightAxis()); + } + }; + + Plot.prototype._animate = function() { + if (!this.inTransition()) { + return; + } + if (++this.animation.frame === this.animation.duration) { + this._stopTransition(); + } + this.draw(this.animation.frame * this.animation.delta()); + return this._updateTimeAxes(); + }; + + Plot.prototype.y = function(givenDomain) { + return d3.scale.linear().domain(this._getScaleDomain(givenDomain)).range([this.innerHeight(), 0]); + }; + + Plot.prototype.ySvg = function(givenDomain) { + return d3.scale.linear().domain(this._getScaleDomain(givenDomain)).range([this.innerHeight() / this.pixelRatio, 0]); + }; + + Plot.prototype.ySvgLeft = function() { + if (this.options.range != null) { + return this.ySvg(this.options.range.left); + } else { + return this.ySvg(); + } + }; + + Plot.prototype.ySvgRight = function() { + if (this.options.range != null) { + return this.ySvg(this.options.range.right); + } else { + return this.ySvg(); + } + }; + + Plot.prototype.w = function() { + return this.innerWidth() / this.options.windowSize; + }; + + Plot.prototype._updateTicks = function(newTime) { + if (!(this.hasAxis('top') || this.hasAxis('bottom'))) { + return; + } + if (!((++this._tickTimer) % this.options.ticks.time)) { + this._pushTick(this.options.windowSize, newTime, true); + } + if (!(this._ticks.length > 0)) { + return; + } + if (!(this._ticks[0].x - (this.w() / this.pixelRatio) >= 0)) { + return this._ticks[0].exit = true; + } + }; + + Plot.prototype._pushTick = function(bucket, time, enter, reverse) { + var g, tick; + if (enter == null) { + enter = false; + } + if (reverse == null) { + reverse = false; + } + if (!(this.hasAxis('top') || this.hasAxis('bottom'))) { + return; + } + tick = { + time: time, + x: bucket * (this.w() / this.pixelRatio) + this._offsetX(), + opacity: enter ? 0 : 1, + enter: enter ? true : false, + exit: false + }; + if (this.hasAxis('bottom')) { + g = this.bottomAxis.append('g').attr('class', 'tick major').attr('transform', "translate(" + (tick.x + 1) + ",0)").style('opacity', tick.opacity); + g.append('line').attr('y2', 6); + g.append('text').attr('text-anchor', 'middle').attr('dy', 19).text(this.options.tickFormats.bottom(tick.time)); + tick.bottomEl = g; + } + if (this.hasAxis('top')) { + g = this.topAxis.append('g').attr('class', 'tick major').attr('transform', "translate(" + (tick.x + 1) + ",0)").style('opacity', tick.opacity); + g.append('line').attr('y2', -6); + g.append('text').attr('text-anchor', 'middle').attr('dy', -10).text(this.options.tickFormats.top(tick.time)); + tick.topEl = g; + } + if (reverse) { + this._ticks.unshift(tick); + } else { + this._ticks.push(tick); + } + return tick; + }; + + Plot.prototype._shiftTick = function() { + var tick; + if (!(this._ticks.length > 0)) { + return; + } + tick = this._ticks.shift(); + if (tick.topEl != null) { + tick.topEl.remove(); + } + if (tick.bottomEl != null) { + return tick.bottomEl.remove(); + } + }; + + Plot.prototype._updateTimeAxes = function() { + var dop, dx, l, len, ref, ref1, results, tick; + if (!(this.hasAxis('top') || this.hasAxis('bottom'))) { + return; + } + ref = [this.animation.tickDelta(), 1 / this.options.fps], dx = ref[0], dop = ref[1]; + ref1 = this._ticks; + results = []; + for (l = 0, len = ref1.length; l < len; l++) { + tick = ref1[l]; + tick.x += dx; + if (this.hasAxis('bottom')) { + tick.bottomEl.attr('transform', "translate(" + (tick.x + 1) + ",0)"); + } + if (this.hasAxis('top')) { + tick.topEl.attr('transform', "translate(" + (tick.x + 1) + ",0)"); + } + if (tick.enter) { + tick.opacity += dop; + } else if (tick.exit) { + tick.opacity -= dop; + } + if (tick.enter || tick.exit) { + if (this.hasAxis('bottom')) { + tick.bottomEl.style('opacity', tick.opacity); + } + if (this.hasAxis('top')) { + results.push(tick.topEl.style('opacity', tick.opacity)); + } else { + results.push(void 0); + } + } else { + results.push(void 0); + } + } + return results; + }; + + Plot.prototype.draw = function(delta) { + if (delta == null) { + delta = 0; + } + return Plot.__super__.draw.call(this); + }; + + Plot.prototype.dimensionsChanged = function() { + Plot.__super__.dimensionsChanged.call(this); + this.svg.attr('width', this.width).attr('height', this.height); + this._sizeCanvas(); + this._buildAxes(); + return this.draw(this.animation.frame * this.animation.delta()); + }; + + Plot.prototype.axesChanged = function() { + var l, len, pos, ref; + ref = ['top', 'right', 'bottom', 'left']; + for (l = 0, len = ref.length; l < len; l++) { + pos = ref[l]; + if ((this.options.margins != null) && (this.options.margins[pos] != null)) { + continue; + } + if (this.hasAxis(pos)) { + this.margins[pos] = defaultAxisMargins[pos]; + } else { + this.margins[pos] = 6; + } + } + this._sizeCanvas(); + this._buildAxes(); + return this.draw(this.animation.frame * this.animation.delta()); + }; + + Plot.prototype.ticksChanged = function() { + this._resetInitialTimeTicks(); + this._transitionRangeAxes(); + return this.draw(this.animation.frame * this.animation.delta()); + }; + + Plot.prototype.tickFormatsChanged = function() { + this._resetInitialTimeTicks(); + this._transitionRangeAxes(); + return this.draw(this.animation.frame * this.animation.delta()); + }; + + Plot.prototype.marginsChanged = function() { + var pos, ref, size; + if (this.options.margins == null) { + return; + } + ref = this.options.margins; + for (pos in ref) { + if (!hasProp.call(ref, pos)) continue; + size = ref[pos]; + if (size == null) { + this.margins[pos] = 6; + } else { + this.margins[pos] = size; + } + } + this._sizeCanvas(); + return this.draw(this.animation.frame * this.animation.delta()); + }; + + Plot.prototype.layerChanged = function() { + this._transitionRangeAxes(); + return Plot.__super__.layerChanged.call(this); + }; + + return Plot; + +})(Epoch.Chart.Canvas); + +Epoch.Time.Stack = (function(superClass) { + extend(Stack, superClass); + + function Stack() { + return Stack.__super__.constructor.apply(this, arguments); + } + + Stack.prototype._stackLayers = function() { + var i, l, layer, layers, ref, results, y0; + if (!((layers = this.getVisibleLayers()).length > 0)) { + return; + } + results = []; + for (i = l = 0, ref = layers[0].values.length; 0 <= ref ? l < ref : l > ref; i = 0 <= ref ? ++l : --l) { + y0 = 0; + results.push((function() { + var len, m, results1; + results1 = []; + for (m = 0, len = layers.length; m < len; m++) { + layer = layers[m]; + layer.values[i].y0 = y0; + results1.push(y0 += layer.values[i].y); + } + return results1; + })()); + } + return results; + }; + + Stack.prototype._prepareLayers = function(layers) { + var d, i, y0; + y0 = 0; + for (i in layers) { + if (!hasProp.call(layers, i)) continue; + d = layers[i]; + if (!this.data[i].visible) { + continue; + } + d.y0 = y0; + y0 += d.y; + } + return layers; + }; + + Stack.prototype.setData = function(data) { + Stack.__super__.setData.call(this, data); + return this._stackLayers(); + }; + + Stack.prototype.extent = function() { + var i, j, l, layers, m, max, ref, ref1, ref2, sum; + ref = [0, this.getVisibleLayers()], max = ref[0], layers = ref[1]; + if (!layers.length) { + return [0, 0]; + } + for (i = l = 0, ref1 = layers[0].values.length; 0 <= ref1 ? l < ref1 : l > ref1; i = 0 <= ref1 ? ++l : --l) { + sum = 0; + for (j = m = 0, ref2 = layers.length; 0 <= ref2 ? m < ref2 : m > ref2; j = 0 <= ref2 ? ++m : --m) { + sum += layers[j].values[i].y; + } + if (sum > max) { + max = sum; + } + } + return [0, max]; + }; + + Stack.prototype.layerChanged = function() { + var l, layers, len, ref; + this._stackLayers(); + ref = this._queue; + for (l = 0, len = ref.length; l < len; l++) { + layers = ref[l]; + this._prepareLayers(layers); + } + return Stack.__super__.layerChanged.call(this); + }; + + return Stack; + +})(Epoch.Time.Plot); + +var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; + +Epoch.Time.Area = (function(superClass) { + extend(Area, superClass); + + function Area(options) { + var base; + this.options = options != null ? options : {}; + if ((base = this.options).type == null) { + base.type = 'time.area'; + } + Area.__super__.constructor.call(this, this.options); + this.draw(); + } + + Area.prototype.setStyles = function(layer) { + var styles; + if ((layer != null) && (layer.className != null)) { + styles = this.getStyles("g." + (layer.className.replace(/\s/g, '.')) + " path.area"); + } else { + styles = this.getStyles("g path.area"); + } + this.ctx.fillStyle = styles.fill; + if (styles.stroke != null) { + this.ctx.strokeStyle = styles.stroke; + } + if (styles['stroke-width'] != null) { + return this.ctx.lineWidth = styles['stroke-width'].replace('px', ''); + } + }; + + Area.prototype._drawAreas = function(delta) { + var args, borderX, entry, firstX, i, j, k, l, layer, layers, ref, ref1, ref2, results, trans, w, y; + if (delta == null) { + delta = 0; + } + ref = [this.y(), this.w(), this.getVisibleLayers()], y = ref[0], w = ref[1], layers = ref[2]; + results = []; + for (i = l = ref1 = layers.length - 1; ref1 <= 0 ? l <= 0 : l >= 0; i = ref1 <= 0 ? ++l : --l) { + if (!(layer = layers[i])) { + continue; + } + this.setStyles(layer); + this.ctx.beginPath(); + ref2 = [this.options.windowSize, layer.values.length, this.inTransition()], j = ref2[0], k = ref2[1], trans = ref2[2]; + firstX = null; + while ((--j >= -2) && (--k >= 0)) { + entry = layer.values[k]; + args = [(j + 1) * w + delta, y(entry.y + entry.y0)]; + if (trans) { + args[0] += w; + } + if (i === this.options.windowSize - 1) { + this.ctx.moveTo.apply(this.ctx, args); + } else { + this.ctx.lineTo.apply(this.ctx, args); + } + } + if (trans) { + borderX = (j + 3) * w + delta; + } else { + borderX = (j + 2) * w + delta; + } + this.ctx.lineTo(borderX, this.innerHeight()); + this.ctx.lineTo(this.width * this.pixelRatio + w + delta, this.innerHeight()); + this.ctx.closePath(); + results.push(this.ctx.fill()); + } + return results; + }; + + Area.prototype._drawStrokes = function(delta) { + var args, entry, firstX, i, k, l, layer, layers, ref, ref1, ref2, results, trans, w, y; + if (delta == null) { + delta = 0; + } + ref = [this.y(), this.w(), this.getVisibleLayers()], y = ref[0], w = ref[1], layers = ref[2]; + results = []; + for (i = l = ref1 = layers.length - 1; ref1 <= 0 ? l <= 0 : l >= 0; i = ref1 <= 0 ? ++l : --l) { + if (!(layer = layers[i])) { + continue; + } + this.setStyles(layer); + this.ctx.beginPath(); + ref2 = [this.options.windowSize, layer.values.length, this.inTransition()], i = ref2[0], k = ref2[1], trans = ref2[2]; + firstX = null; + while ((--i >= -2) && (--k >= 0)) { + entry = layer.values[k]; + args = [(i + 1) * w + delta, y(entry.y + entry.y0)]; + if (trans) { + args[0] += w; + } + if (i === this.options.windowSize - 1) { + this.ctx.moveTo.apply(this.ctx, args); + } else { + this.ctx.lineTo.apply(this.ctx, args); + } + } + results.push(this.ctx.stroke()); + } + return results; + }; + + Area.prototype.draw = function(delta) { + if (delta == null) { + delta = 0; + } + this.clear(); + this._drawAreas(delta); + this._drawStrokes(delta); + return Area.__super__.draw.call(this); + }; + + return Area; + +})(Epoch.Time.Stack); + +var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; + +Epoch.Time.Bar = (function(superClass) { + extend(Bar, superClass); + + function Bar(options) { + var base; + this.options = options != null ? options : {}; + if ((base = this.options).type == null) { + base.type = 'time.bar'; + } + Bar.__super__.constructor.call(this, this.options); + this.draw(); + } + + Bar.prototype._offsetX = function() { + return 0.5 * this.w() / this.pixelRatio; + }; + + Bar.prototype.setStyles = function(className) { + var styles; + styles = this.getStyles("rect.bar." + (className.replace(/\s/g, '.'))); + this.ctx.fillStyle = styles.fill; + if ((styles.stroke == null) || styles.stroke === 'none') { + this.ctx.strokeStyle = 'transparent'; + } else { + this.ctx.strokeStyle = styles.stroke; + } + if (styles['stroke-width'] != null) { + return this.ctx.lineWidth = styles['stroke-width'].replace('px', ''); + } + }; + + Bar.prototype.draw = function(delta) { + var args, entry, ex, ey, ey0, i, iBoundry, j, k, layer, len, ref, ref1, ref2, ref3, trans, w, y; + if (delta == null) { + delta = 0; + } + this.clear(); + ref = [this.y(), this.w()], y = ref[0], w = ref[1]; + ref1 = this.getVisibleLayers(); + for (j = 0, len = ref1.length; j < len; j++) { + layer = ref1[j]; + if (!Epoch.isNonEmptyArray(layer.values)) { + continue; + } + this.setStyles(layer.className); + ref2 = [this.options.windowSize, layer.values.length, this.inTransition()], i = ref2[0], k = ref2[1], trans = ref2[2]; + iBoundry = trans ? -1 : 0; + while ((--i >= iBoundry) && (--k >= 0)) { + entry = layer.values[k]; + ref3 = [i * w + delta, entry.y, entry.y0], ex = ref3[0], ey = ref3[1], ey0 = ref3[2]; + if (trans) { + ex += w; + } + args = [ex + 1, y(ey + ey0), w - 2, this.innerHeight() - y(ey) + 0.5 * this.pixelRatio]; + this.ctx.fillRect.apply(this.ctx, args); + this.ctx.strokeRect.apply(this.ctx, args); + } + } + return Bar.__super__.draw.call(this); + }; + + return Bar; + +})(Epoch.Time.Stack); + +var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; + +Epoch.Time.Gauge = (function(superClass) { + var defaults, optionListeners; + + extend(Gauge, superClass); + + defaults = { + type: 'time.gauge', + domain: [0, 1], + ticks: 10, + tickSize: 5, + tickOffset: 5, + fps: 34, + format: Epoch.Formats.percent + }; + + optionListeners = { + 'option:domain': 'domainChanged', + 'option:ticks': 'ticksChanged', + 'option:tickSize': 'tickSizeChanged', + 'option:tickOffset': 'tickOffsetChanged', + 'option:format': 'formatChanged' + }; + + function Gauge(options) { + this.options = options != null ? options : {}; + Gauge.__super__.constructor.call(this, this.options = Epoch.Util.defaults(this.options, defaults)); + this.value = this.options.value || 0; + if (this.options.model) { + this.options.model.on('data:push', (function(_this) { + return function() { + return _this.pushFromModel(); + }; + })(this)); + } + if (this.el.style('position') !== 'absolute' && this.el.style('position') !== 'relative') { + this.el.style('position', 'relative'); + } + this.svg = this.el.insert('svg', ':first-child').attr('width', this.width).attr('height', this.height).attr('class', 'gauge-labels'); + this.svg.style({ + 'position': 'absolute', + 'z-index': '1' + }); + this.svg.append('g').attr('transform', "translate(" + (this.textX()) + ", " + (this.textY()) + ")").append('text').attr('class', 'value').text(this.options.format(this.value)); + this.animation = { + interval: null, + active: false, + delta: 0, + target: 0 + }; + this._animate = (function(_this) { + return function() { + if (Math.abs(_this.animation.target - _this.value) < Math.abs(_this.animation.delta)) { + _this.value = _this.animation.target; + clearInterval(_this.animation.interval); + _this.animation.active = false; + } else { + _this.value += _this.animation.delta; + } + _this.svg.select('text.value').text(_this.options.format(_this.value)); + return _this.draw(); + }; + })(this); + this.onAll(optionListeners); + this.draw(); + } + + Gauge.prototype.update = function(value) { + this.animation.target = value; + this.animation.delta = (value - this.value) / this.options.fps; + if (!this.animation.active) { + this.animation.interval = setInterval(this._animate, 1000 / this.options.fps); + return this.animation.active = true; + } + }; + + Gauge.prototype.push = function(value) { + return this.update(value); + }; + + Gauge.prototype.pushFromModel = function() { + var next; + next = this.options.model.getNext(this.options.type, this.options.dataFormat); + return this.update(next); + }; + + Gauge.prototype.radius = function() { + return this.getHeight() / 1.58; + }; + + Gauge.prototype.centerX = function() { + return this.getWidth() / 2; + }; + + Gauge.prototype.centerY = function() { + return 0.68 * this.getHeight(); + }; + + Gauge.prototype.textX = function() { + return this.width / 2; + }; + + Gauge.prototype.textY = function() { + return 0.48 * this.height; + }; + + Gauge.prototype.getAngle = function(value) { + var a, b, ref; + ref = this.options.domain, a = ref[0], b = ref[1]; + return ((value - a) / (b - a)) * (Math.PI + 2 * Math.PI / 8) - Math.PI / 2 - Math.PI / 8; + }; + + Gauge.prototype.setStyles = function(selector) { + var styles; + styles = this.getStyles(selector); + this.ctx.fillStyle = styles.fill; + this.ctx.strokeStyle = styles.stroke; + if (styles['stroke-width'] != null) { + return this.ctx.lineWidth = styles['stroke-width'].replace('px', ''); + } + }; + + Gauge.prototype.draw = function() { + var a, c, cx, cy, i, j, r, ref, ref1, ref2, ref3, s, t, tickOffset, tickSize, x1, x2, y1, y2; + ref = [this.centerX(), this.centerY(), this.radius()], cx = ref[0], cy = ref[1], r = ref[2]; + ref1 = [this.options.tickOffset, this.options.tickSize], tickOffset = ref1[0], tickSize = ref1[1]; + this.clear(); + t = d3.scale.linear().domain([0, this.options.ticks]).range([-(9 / 8) * Math.PI, Math.PI / 8]); + this.setStyles('.epoch .gauge .tick'); + this.ctx.beginPath(); + for (i = j = 0, ref2 = this.options.ticks; 0 <= ref2 ? j <= ref2 : j >= ref2; i = 0 <= ref2 ? ++j : --j) { + a = t(i); + ref3 = [Math.cos(a), Math.sin(a)], c = ref3[0], s = ref3[1]; + x1 = c * (r - tickOffset) + cx; + y1 = s * (r - tickOffset) + cy; + x2 = c * (r - tickOffset - tickSize) + cx; + y2 = s * (r - tickOffset - tickSize) + cy; + this.ctx.moveTo(x1, y1); + this.ctx.lineTo(x2, y2); + } + this.ctx.stroke(); + this.setStyles('.epoch .gauge .arc.outer'); + this.ctx.beginPath(); + this.ctx.arc(cx, cy, r, -(9 / 8) * Math.PI, (1 / 8) * Math.PI, false); + this.ctx.stroke(); + this.setStyles('.epoch .gauge .arc.inner'); + this.ctx.beginPath(); + this.ctx.arc(cx, cy, r - 10, -(9 / 8) * Math.PI, (1 / 8) * Math.PI, false); + this.ctx.stroke(); + this.drawNeedle(); + return Gauge.__super__.draw.call(this); + }; + + Gauge.prototype.drawNeedle = function() { + var cx, cy, r, ratio, ref; + ref = [this.centerX(), this.centerY(), this.radius()], cx = ref[0], cy = ref[1], r = ref[2]; + ratio = this.value / this.options.domain[1]; + this.setStyles('.epoch .gauge .needle'); + this.ctx.beginPath(); + this.ctx.save(); + this.ctx.translate(cx, cy); + this.ctx.rotate(this.getAngle(this.value)); + this.ctx.moveTo(4 * this.pixelRatio, 0); + this.ctx.lineTo(-4 * this.pixelRatio, 0); + this.ctx.lineTo(-1 * this.pixelRatio, 19 - r); + this.ctx.lineTo(1, 19 - r); + this.ctx.fill(); + this.setStyles('.epoch .gauge .needle-base'); + this.ctx.beginPath(); + this.ctx.arc(0, 0, this.getWidth() / 25, 0, 2 * Math.PI); + this.ctx.fill(); + return this.ctx.restore(); + }; + + Gauge.prototype.domainChanged = function() { + return this.draw(); + }; + + Gauge.prototype.ticksChanged = function() { + return this.draw(); + }; + + Gauge.prototype.tickSizeChanged = function() { + return this.draw(); + }; + + Gauge.prototype.tickOffsetChanged = function() { + return this.draw(); + }; + + Gauge.prototype.formatChanged = function() { + return this.svg.select('text.value').text(this.options.format(this.value)); + }; + + return Gauge; + +})(Epoch.Chart.Canvas); + +var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; + +Epoch.Time.Heatmap = (function(superClass) { + var colorFunctions, defaults, optionListeners; + + extend(Heatmap, superClass); + + defaults = { + type: 'time.heatmap', + buckets: 10, + bucketRange: [0, 100], + opacity: 'linear', + bucketPadding: 2, + paintZeroValues: false, + cutOutliers: false + }; + + colorFunctions = { + root: function(value, max) { + return Math.pow(value / max, 0.5); + }, + linear: function(value, max) { + return value / max; + }, + quadratic: function(value, max) { + return Math.pow(value / max, 2); + }, + cubic: function(value, max) { + return Math.pow(value / max, 3); + }, + quartic: function(value, max) { + return Math.pow(value / max, 4); + }, + quintic: function(value, max) { + return Math.pow(value / max, 5); + } + }; + + optionListeners = { + 'option:buckets': 'bucketsChanged', + 'option:bucketRange': 'bucketRangeChanged', + 'option:opacity': 'opacityChanged', + 'option:bucketPadding': 'bucketPaddingChanged', + 'option:paintZeroValues': 'paintZeroValuesChanged', + 'option:cutOutliers': 'cutOutliersChanged' + }; + + function Heatmap(options) { + this.options = options != null ? options : {}; + Heatmap.__super__.constructor.call(this, this.options = Epoch.Util.defaults(this.options, defaults)); + this._setOpacityFunction(); + this._setupPaintCanvas(); + this.onAll(optionListeners); + this.draw(); + } + + Heatmap.prototype._setOpacityFunction = function() { + if (Epoch.isString(this.options.opacity)) { + this._opacityFn = colorFunctions[this.options.opacity]; + if (this._opacityFn == null) { + return Epoch.exception("Unknown coloring function provided '" + this.options.opacity + "'"); + } + } else if (Epoch.isFunction(this.options.opacity)) { + return this._opacityFn = this.options.opacity; + } else { + return Epoch.exception("Unknown type for provided coloring function."); + } + }; + + Heatmap.prototype.setData = function(data) { + var k, layer, len, ref, results; + Heatmap.__super__.setData.call(this, data); + ref = this.data; + results = []; + for (k = 0, len = ref.length; k < len; k++) { + layer = ref[k]; + results.push(layer.values = layer.values.map((function(_this) { + return function(entry) { + return _this._prepareEntry(entry); + }; + })(this))); + } + return results; + }; + + Heatmap.prototype._getBuckets = function(entry) { + var bucketSize, count, i, index, k, prepared, ref, ref1, value; + prepared = { + time: entry.time, + max: 0, + buckets: (function() { + var k, ref, results; + results = []; + for (i = k = 0, ref = this.options.buckets; 0 <= ref ? k < ref : k > ref; i = 0 <= ref ? ++k : --k) { + results.push(0); + } + return results; + }).call(this) + }; + bucketSize = (this.options.bucketRange[1] - this.options.bucketRange[0]) / this.options.buckets; + ref = entry.histogram; + for (value in ref) { + if (!hasProp.call(ref, value)) continue; + count = ref[value]; + index = parseInt((value - this.options.bucketRange[0]) / bucketSize); + if (this.options.cutOutliers && ((index < 0) || (index >= this.options.buckets))) { + continue; + } + if (index < 0) { + index = 0; + } else if (index >= this.options.buckets) { + index = this.options.buckets - 1; + } + prepared.buckets[index] += parseInt(count); + } + for (i = k = 0, ref1 = prepared.buckets.length; 0 <= ref1 ? k < ref1 : k > ref1; i = 0 <= ref1 ? ++k : --k) { + prepared.max = Math.max(prepared.max, prepared.buckets[i]); + } + return prepared; + }; + + Heatmap.prototype.y = function() { + return d3.scale.linear().domain(this.options.bucketRange).range([this.innerHeight(), 0]); + }; + + Heatmap.prototype.ySvg = function() { + return d3.scale.linear().domain(this.options.bucketRange).range([this.innerHeight() / this.pixelRatio, 0]); + }; + + Heatmap.prototype.h = function() { + return this.innerHeight() / this.options.buckets; + }; + + Heatmap.prototype._offsetX = function() { + return 0.5 * this.w() / this.pixelRatio; + }; + + Heatmap.prototype._setupPaintCanvas = function() { + this.paintWidth = (this.options.windowSize + 1) * this.w(); + this.paintHeight = this.height * this.pixelRatio; + this.paint = document.createElement('CANVAS'); + this.paint.width = this.paintWidth; + this.paint.height = this.paintHeight; + this.p = Epoch.Util.getContext(this.paint); + this.redraw(); + this.on('after:shift', '_paintEntry'); + this.on('transition:end', '_shiftPaintCanvas'); + return this.on('transition:end', (function(_this) { + return function() { + return _this.draw(_this.animation.frame * _this.animation.delta()); + }; + })(this)); + }; + + Heatmap.prototype.redraw = function() { + var drawColumn, entryIndex; + if (!(Epoch.isNonEmptyArray(this.data) && Epoch.isNonEmptyArray(this.data[0].values))) { + return; + } + entryIndex = this.data[0].values.length; + drawColumn = this.options.windowSize; + if (this.inTransition()) { + drawColumn++; + } + while ((--entryIndex >= 0) && (--drawColumn >= 0)) { + this._paintEntry(entryIndex, drawColumn); + } + return this.draw(this.animation.frame * this.animation.delta()); + }; + + Heatmap.prototype._computeColor = function(value, max, color) { + return Epoch.Util.toRGBA(color, this._opacityFn(value, max)); + }; + + Heatmap.prototype._paintEntry = function(entryIndex, drawColumn) { + var bucket, bucketTotals, color, count, entries, entry, h, i, j, k, layer, len, len1, m, max, maxTotal, ref, ref1, ref2, results, styles, sum, w, xPos; + if (entryIndex == null) { + entryIndex = null; + } + if (drawColumn == null) { + drawColumn = null; + } + ref = [this.w(), this.h()], w = ref[0], h = ref[1]; + if (entryIndex == null) { + entryIndex = this.data[0].values.length - 1; + } + if (drawColumn == null) { + drawColumn = this.options.windowSize; + } + entries = []; + bucketTotals = (function() { + var k, ref1, results; + results = []; + for (i = k = 0, ref1 = this.options.buckets; 0 <= ref1 ? k < ref1 : k > ref1; i = 0 <= ref1 ? ++k : --k) { + results.push(0); + } + return results; + }).call(this); + maxTotal = 0; + ref1 = this.getVisibleLayers(); + for (k = 0, len = ref1.length; k < len; k++) { + layer = ref1[k]; + entry = this._getBuckets(layer.values[entryIndex]); + ref2 = entry.buckets; + for (bucket in ref2) { + if (!hasProp.call(ref2, bucket)) continue; + count = ref2[bucket]; + bucketTotals[bucket] += count; + } + maxTotal += entry.max; + styles = this.getStyles("." + (layer.className.split(' ').join('.')) + " rect.bucket"); + entry.color = styles.fill; + entries.push(entry); + } + xPos = drawColumn * w; + this.p.clearRect(xPos, 0, w, this.paintHeight); + j = this.options.buckets; + results = []; + for (bucket in bucketTotals) { + if (!hasProp.call(bucketTotals, bucket)) continue; + sum = bucketTotals[bucket]; + color = this._avgLab(entries, bucket); + max = 0; + for (m = 0, len1 = entries.length; m < len1; m++) { + entry = entries[m]; + max += (entry.buckets[bucket] / sum) * maxTotal; + } + if (sum > 0 || this.options.paintZeroValues) { + this.p.fillStyle = this._computeColor(sum, max, color); + this.p.fillRect(xPos, (j - 1) * h, w - this.options.bucketPadding, h - this.options.bucketPadding); + } + results.push(j--); + } + return results; + }; + + Heatmap.prototype._shiftPaintCanvas = function() { + var data; + data = this.p.getImageData(this.w(), 0, this.paintWidth - this.w(), this.paintHeight); + return this.p.putImageData(data, 0, 0); + }; + + Heatmap.prototype._avgLab = function(entries, bucket) { + var a, b, color, entry, i, k, l, len, ratio, ref, total, value; + ref = [0, 0, 0, 0], l = ref[0], a = ref[1], b = ref[2], total = ref[3]; + for (k = 0, len = entries.length; k < len; k++) { + entry = entries[k]; + if (entry.buckets[bucket] == null) { + continue; + } + total += entry.buckets[bucket]; + } + for (i in entries) { + if (!hasProp.call(entries, i)) continue; + entry = entries[i]; + if (entry.buckets[bucket] != null) { + value = entry.buckets[bucket] | 0; + } else { + value = 0; + } + ratio = value / total; + color = d3.lab(entry.color); + l += ratio * color.l; + a += ratio * color.a; + b += ratio * color.b; + } + return d3.lab(l, a, b).toString(); + }; + + Heatmap.prototype.draw = function(delta) { + if (delta == null) { + delta = 0; + } + this.clear(); + this.ctx.drawImage(this.paint, delta, 0); + return Heatmap.__super__.draw.call(this); + }; + + Heatmap.prototype.bucketsChanged = function() { + return this.redraw(); + }; + + Heatmap.prototype.bucketRangeChanged = function() { + this._transitionRangeAxes(); + return this.redraw(); + }; + + Heatmap.prototype.opacityChanged = function() { + this._setOpacityFunction(); + return this.redraw(); + }; + + Heatmap.prototype.bucketPaddingChanged = function() { + return this.redraw(); + }; + + Heatmap.prototype.paintZeroValuesChanged = function() { + return this.redraw(); + }; + + Heatmap.prototype.cutOutliersChanged = function() { + return this.redraw(); + }; + + Heatmap.prototype.layerChanged = function() { + return this.redraw(); + }; + + return Heatmap; + +})(Epoch.Time.Plot); + +var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + hasProp = {}.hasOwnProperty; + +Epoch.Time.Line = (function(superClass) { + extend(Line, superClass); + + function Line(options) { + var base; + this.options = options != null ? options : {}; + if ((base = this.options).type == null) { + base.type = 'time.line'; + } + Line.__super__.constructor.call(this, this.options); + this.draw(); + } + + Line.prototype.setStyles = function(className) { + var styles; + styles = this.getStyles("g." + (className.replace(/\s/g, '.')) + " path.line"); + this.ctx.fillStyle = styles.fill; + this.ctx.strokeStyle = styles.stroke; + return this.ctx.lineWidth = this.pixelRatio * styles['stroke-width'].replace('px', ''); + }; + + Line.prototype.draw = function(delta) { + var args, entry, i, j, k, layer, len, ref, ref1, trans, w, y; + if (delta == null) { + delta = 0; + } + this.clear(); + w = this.w(); + ref = this.getVisibleLayers(); + for (j = 0, len = ref.length; j < len; j++) { + layer = ref[j]; + if (!Epoch.isNonEmptyArray(layer.values)) { + continue; + } + this.setStyles(layer.className); + this.ctx.beginPath(); + y = this.y(layer.range); + ref1 = [this.options.windowSize, layer.values.length, this.inTransition()], i = ref1[0], k = ref1[1], trans = ref1[2]; + while ((--i >= -2) && (--k >= 0)) { + entry = layer.values[k]; + args = [(i + 1) * w + delta, y(entry.y)]; + if (trans) { + args[0] += w; + } + if (i === this.options.windowSize - 1) { + this.ctx.moveTo.apply(this.ctx, args); + } else { + this.ctx.lineTo.apply(this.ctx, args); + } + } + this.ctx.stroke(); + } + return Line.__super__.draw.call(this); + }; + + return Line; + +})(Epoch.Time.Plot); + +Epoch._typeMap = { + 'area': Epoch.Chart.Area, + 'bar': Epoch.Chart.Bar, + 'line': Epoch.Chart.Line, + 'pie': Epoch.Chart.Pie, + 'scatter': Epoch.Chart.Scatter, + 'histogram': Epoch.Chart.Histogram, + 'time.area': Epoch.Time.Area, + 'time.bar': Epoch.Time.Bar, + 'time.line': Epoch.Time.Line, + 'time.gauge': Epoch.Time.Gauge, + 'time.heatmap': Epoch.Time.Heatmap +}; + +var jQueryModule; + +jQueryModule = function($) { + var DATA_NAME; + DATA_NAME = 'epoch-chart'; + return $.fn.epoch = function(options) { + var chart, klass; + options.el = this.get(0); + if ((chart = this.data(DATA_NAME)) == null) { + klass = Epoch._typeMap[options.type]; + if (klass == null) { + Epoch.exception("Unknown chart type '" + options.type + "'"); + } + this.data(DATA_NAME, (chart = new klass(options))); + } + return chart; + }; +}; + +if (window.jQuery != null) { + jQueryModule(jQuery); +} + +var MooToolsModule; + +MooToolsModule = function() { + var DATA_NAME; + DATA_NAME = 'epoch-chart'; + return Element.implement('epoch', function(options) { + var chart, klass, self; + self = $$(this); + if ((chart = self.retrieve(DATA_NAME)[0]) == null) { + options.el = this; + klass = Epoch._typeMap[options.type]; + if (klass == null) { + Epoch.exception("Unknown chart type '" + options.type + "'"); + } + self.store(DATA_NAME, (chart = new klass(options))); + } + return chart; + }); +}; + +if (window.MooTools != null) { + MooToolsModule(); +} + +var zeptoModule; + +zeptoModule = function($) { + var DATA_NAME, chartId, chartMap, next_cid; + DATA_NAME = 'epoch-chart'; + chartMap = {}; + chartId = 0; + next_cid = function() { + return DATA_NAME + "-" + (++chartId); + }; + return $.extend($.fn, { + epoch: function(options) { + var chart, cid, klass; + if ((cid = this.data(DATA_NAME)) != null) { + return chartMap[cid]; + } + options.el = this.get(0); + klass = Epoch._typeMap[options.type]; + if (klass == null) { + Epoch.exception("Unknown chart type '" + options.type + "'"); + } + this.data(DATA_NAME, (cid = next_cid())); + chart = new klass(options); + chartMap[cid] = chart; + return chart; + } + }); +}; + +if (window.Zepto != null) { + zeptoModule(Zepto); +} diff --git a/debian/missing-sources/epoch/.travis.yml b/debian/missing-sources/epoch/.travis.yml new file mode 100644 index 0000000..c3bbff7 --- /dev/null +++ b/debian/missing-sources/epoch/.travis.yml @@ -0,0 +1,4 @@ +language: node_js +node_js: + - "4.1.1" +sudo: false diff --git a/debian/missing-sources/epoch/CHANGELOG.md b/debian/missing-sources/epoch/CHANGELOG.md new file mode 100644 index 0000000..0492850 --- /dev/null +++ b/debian/missing-sources/epoch/CHANGELOG.md @@ -0,0 +1,102 @@ +# Epoch Changelog + +## 0.8.4 - October 30th, 2015 +### Bug Fixes +* Fixed bower css path (@ftaiolivista) + +## 0.8.3 - October 17th, 2015 +### Enhancements / Features +* Added `redraw` method for clearing styles on canvas based charts (#196, @woozyking) + +## 0.8.2 - October 13th, 2015 +### Enhancements / Features +* Charts now auto draw on construction (#195) + +## 0.8.1 - October 13th, 2015 +### Enhancements / Features +* Added packagist/composer package manager support (#202) + +### Bug Fixes +* Real-time charts no-longer error when pushing first data point after initialized + with empty data layers. (#203) + +## 0.8.0 - October 10th, 2015 +### Enhancements / Features +* Multi-axis support for basic and real-time line plots +* Added new gulp build-system (for development) + +## 0.7.1 - October 4th, 2015 +* Moved minified source to `dist/js` and `dist/css` respectively +* Added non-minified source to aforementioned directories + +## 0.7.0 - October 4th, 2015 + +### Enhancements / Features +* New basic chart: Histogram +* New Feature: Data formatters +* Chart layers can now be hidden/shown + +### Bug Fixes +* Ticks now working for ordinal scaled bar charts +* Fixed CSS builds by updating NPM sass-node package +* Removed versions from minified release files (@RyanNielson) +* Time based graphs can now have fixed ranges (@willwhitney) +* NPM Package: epoch-charting (@sompylasar) +* Right axes now using correct formatters (@Dav1dde) +* Add 'main' attribute enabling webpack support. (@WRidder) +* Fixed Bower D3 Dependencies (@loopj) +* Fixed CSS errors by using `transparent` instead of `none` (@mwsmith2) +* Fixed bower "version" property (@kkirsche) + +## 0.6.0 - July 21st, 2014 + +### Enhancements / Features + +* Source code restructure for easier programming +* Replaced Compass with node-sass +* Removed put.js from the repository +* Removed dependency on jQuery +* Added CSS controlled themes + * New "Dark" theme for dark backgrounds +* Registered with bower +* Added option accessor / mutator to all charts (making them adaptive) +* Added bubble charts (special case of scatter plots) +* Added MooTools and Zepto Adapters +* Added Core Library Unit Testing +* New `domain` and `range` options for basic charts + +### Bug Fixes + +* Event `.off` method was completely busted, fixed +* Swapped terminology for horizontal and vertical bar plots +* Removed `isVisible` and related rendering hacks (caused all sorts of woe) + + +## 0.5.2 - June 24th, 2014 + +### Enhancements / Features + +* #36 - Fixed the readme to focus on development +* #54 - Added vertical orientation option to the basic bar chart + +## 0.5.1 - June 23rd, 2014 + +### Bug Fixes + +* #52 - Replaced instances of `$` with `jQuery` (ambiguous, otherwise) + +## 0.5.0 - June 23rd, 2014 + +### Enhancements / Features + +* #32 - QueryCSS greatly enhanced - now builds a full DOM context when computing styles +* #42 - Heat map now allows for painting of "zero" values via a new `paintZeroValues` option +* #43 - Heat map color computation abstracted out of `_paintEntry` (makes it easier to extend) + +### Bug Fixes + +* #22 - Fixed an issue with pie chart transitions +* #30 - Layers without labels now correctly render on a various basic charts +* #31 - Real-time Line Chart thickness fixed by taking pixel ratio into account +* #41 - Fixed bucketing issues with the Heat Map +* #46 - Removed default black stroke from the Real-Time Area chart diff --git a/debian/missing-sources/epoch/LICENSE b/debian/missing-sources/epoch/LICENSE new file mode 100644 index 0000000..3e8d8f8 --- /dev/null +++ b/debian/missing-sources/epoch/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Fastly, Inc. + +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. diff --git a/debian/missing-sources/epoch/README.md b/debian/missing-sources/epoch/README.md new file mode 100644 index 0000000..5095f3a --- /dev/null +++ b/debian/missing-sources/epoch/README.md @@ -0,0 +1,135 @@ +## Epoch +By Ryan Sandor Richards + +[![Build Status](https://travis-ci.org/epochjs/epoch.svg?branch=master)](https://travis-ci.org/epochjs/epoch) +[![Dependency Status](https://david-dm.org/epochjs/epoch.svg)](https://david-dm.org/epochjs/epoch) +[![devDependency Status](https://david-dm.org/epochjs/epoch/dev-status.svg)](https://david-dm.org/epochjs/epoch#info=devDependencies) + +Epoch is a general purpose charting library for application developers and visualization designers. It focuses on two different aspects of visualization programming: **basic charts** for creating historical reports, and **real-time charts** for displaying frequently updating timeseries data. + +To get started using Epoch, please refer to the [Epoch Project Site](http://epochjs.github.io/epoch/). There you can find full documentation and guides to help you start using Epoch right away. + +### Installation +Epoch can be easily installed via the following package managers: + +* [npm](https://www.npmjs.com/package/epoch-charting) +* [bower](http://bower.io/search/?q=epoch) +* [packagist](https://packagist.org/packages/epochjs/epoch) + +If you don't see your favorite package manager in the list above feel free to +[open up an issue](https://github.com/epochjs/epoch/issues/new) and let us know. +Finally, you can download any release of the library from the +[project releases page](https://github.com/epochjs/epoch/releases). + +**Important:** Epoch requires [d3](https://github.com/mbostock/d3). In order to +work properly your page must load d3 before epoch. + +#### Public CDN URLs +If you don't want to host the files yourself, you can use +[jsDelivr](http://www.jsdelivr.com/) to serve the files: + +1. Visit [epoch page on jsDelvr](http://www.jsdelivr.com/projects/epoch). +2. Copy the provided URL's and link to them in your project. + +### Developing Epoch + +Developing Epoch is a reasonably straight forward process. In this section we'll +cover the basic on how to develop Epoch by detailing common build task, exploring +how the source is arranged, and finally show how to use rendering tests to aid +development. + +#### Configuring Development Environment + +Epoch requires the following for development: + +1. [Node.js](https://nodejs.org/en/) (v4.1.1+) +2. [NPM](https://www.npmjs.com/) (v2.1.0+) + +Once both are installed on your machine you will need to run `npm install` from +the repository's root directory in order to install the npm packages required +to develop epoch. + +Once you have installed the required npm packages you can use `gulp build` to +fully rebuild the source (see more information about gulp tasks below). + + +#### Basic Development Process + +The best way to start contributing to Epoch is to follow these steps: + +1. Change to the source directory for the project +2. Run `gulp watch` to recompile the project after source files change +3. Make changes in a source file (either in `src/` or `sass/`) +4. In a web browser open the `test/index.html` and browse the rendering tests +5. Use the rendering tests to see if your changes had the desired result +6. Ensure unit tests with pass `npm test` + +#### Testing + +Epoch uses two types of testing to ensure that changes do not cause unintended +side effects. The first, unit tests, ensure that the core functional components +of the library work as expected. The second, rendering tests, allow you to +ensure that charts and graphs are correctly rendered. + +It is important to keep both unit test and rendering tests up-to-date! When +developing, use the following guidelines: + +* When adding new features make sure to add new tests +* When changing existing functionality, ensure that the appropriate both types + of tests still pass +* If you want to make a new type of chart, add a whole new test suite for that + chart! + +Keeping the tests current makes it easier for others to review your code and +spot issues. Also, pull requests without appropriate testing will not be +merged. + + +#### Gulp Tasks + +Epoch uses [gulp](https://github.com/gulpjs/gulp) to perform various tasks. The +`gulpfile.js` file defines the following tasks: + +* `gulp clean` - Cleans the `dist/` directory. +* `gulp build` - Builds the CoffeeScript and Sass source into the `dist/` + directory. +* `gulp watch` - Starts a watch script to recompile CoffeeScript and Sass when + any files change. + +#### Source Structure + +The directory structure for the Epoch project follows some basic guidelines, here's an overview of how it is structured: + +``` +dist/ - Compiled JavaScript and CSS source +src/ - Main source directory + core/ - Core Epoch Library Files + util.coffee - Library Utility Routines + d3.coffee - d3 Extensions + format.coffee - Data formatters + chart.coffee - Base Chart Classes + css.coffee - CSS Querying Engine + adapters/ - 3rd Party Library Adapters (currently only jQuery) + basic/ - Basic Chart Classes + time/ - Real-time Chart Classes + adapters.coffee - Options / Global Classes for Adapter Implementations + basic.coffee - Base Classes for Basic Charts + data.coffee - Data Formatting + epoch.coffee - Main source file, defines name spaces, etc. + model.coffee - Data Model + time.coffee - Base Classes for Real-Time Charts +sass/ - Scss source for the default epoch stylesheet +tests/ + render/ - Rendering tests + basic/ - Basic chart rendering tests + real-time/ - Real-time rendering tests + unit/ - Unit tests +``` + +### Release Checklist + +- Run `npm test` and ensure all tests pass +- Run `npm version [major|minor|patch]` +- Run `npm publish` +- Update CHANGELOG.md with the changes since last release +- Update the `gh-pages` branch's library version in `_config.yml` diff --git a/debian/missing-sources/epoch/bower.json b/debian/missing-sources/epoch/bower.json new file mode 100644 index 0000000..36ee8e6 --- /dev/null +++ b/debian/missing-sources/epoch/bower.json @@ -0,0 +1,37 @@ +{ + "name": "epoch", + "description": "A general purpose, real-time visualization library.", + "main": [ + "dist/js/epoch.min.js", + "dist/css/epoch.min.css" + ], + "license": "MIT", + "ignore": [ + "**/.*", + "/src", + "/sass", + "/tests", + "/Cakefile", + "/CHANGELOG.md", + "/package.json" + ], + "authors": [ + "Ryan Sandor Richards <sandor.richards@gmail.com>" + ], + "keywords": [ + "fastly", + "realtime", + "graph", + "chart", + "stats", + "visualization" + ], + "homepage": "https://fastly.github.io/epoch/", + "repository": { + "type": "git", + "url": "git://github.com/fastly/epoch.git" + }, + "dependencies": { + "d3": "^3.4.13" + } +} diff --git a/debian/missing-sources/epoch/composer.json b/debian/missing-sources/epoch/composer.json new file mode 100644 index 0000000..184efe0 --- /dev/null +++ b/debian/missing-sources/epoch/composer.json @@ -0,0 +1,22 @@ +{ + "name": "epochjs/epoch", + "description": "A general purpose, real-time visualization library.", + "keywords": [ + "epoch", + "d3", + "chart", + "graph", + "plot", + "real-time" + ], + "homepage": "https://github.com/epochjs/epoch", + "license": "MIT", + "authors": [ + { + "name": "Ryan Sandor Richards" + } + ], + "require": { + "mbostock/d3": "@stable" + } +} diff --git a/debian/missing-sources/epoch/gulpfile.js b/debian/missing-sources/epoch/gulpfile.js new file mode 100644 index 0000000..4f3b975 --- /dev/null +++ b/debian/missing-sources/epoch/gulpfile.js @@ -0,0 +1,108 @@ +'use strict'; + +require('coffee-script/register'); // For coffee-script mocha unit tests + +var gulp = require('gulp'); +var coffee = require('gulp-coffee'); +var concat = require('gulp-concat'); +var mocha = require('gulp-mocha'); +var order = require('gulp-order'); +var rename = require('gulp-rename'); +var sass = require('gulp-sass'); +var uglify = require('gulp-uglify'); +var gutil = require('gulp-util'); +var del = require('del'); +var exec = require('child_process').exec; + +/** + * Common directories used by tasks below. + * @type {object} + */ +var path = { + source: { + coffee: 'src/', + sass: 'sass/' + }, + dist: { + js: 'dist/js/', + css: 'dist/css/' + }, + test: { + unit: 'tests/unit/' + }, + doc: 'doc/' +}; + +/** + * The default task simply calls the master 'build' task. + */ +gulp.task('default', ['build']); + +/** + * Builds the distribution files by packaging the compiled javascript source + * into the `dist/js/` directory and building the css into the `dist/css` + * directory + */ +gulp.task('build', ['sass', 'sass-minify'], function () { + gulp.src(path.source.coffee + '**/*.coffee') + .pipe(coffee({bare: true}).on('error', gutil.log)) + .pipe(order([ + 'epoch.js', + 'core/context.js', + 'core/util.js', + 'core/d3.js', + 'core/format.js', + 'core/chart.js', + 'core/css.js', + 'data.js', + 'model.js', + 'basic.js', + 'basic/*.js', + 'time.js', + 'time/*.js', + 'adapters.js', + 'adapters/*.js' + ])) + .pipe(concat('epoch.js')) + .pipe(gulp.dest(path.dist.js)) + .pipe(uglify().on('error', gutil.log)) + .pipe(rename('epoch.min.js')) + .pipe(gulp.dest(path.dist.js)); +}); + +/** + * Generates epoch CSS from Sass source. + */ +gulp.task('sass', function () { + gulp.src(path.source.sass + 'epoch.scss') + .pipe(sass({ outputStyle: 'compact' })) + .pipe(rename('epoch.css')) + .pipe(gulp.dest(path.dist.css)); +}); + +/** + * Generates the minified version of the epoch css from sass source. + */ +gulp.task('sass-minify', function () { + gulp.src(path.source.sass + 'epoch.scss') + .pipe(sass({ outputStyle: 'compressed' })) + .pipe(rename('epoch.min.css')) + .pipe(gulp.dest(path.dist.css)); +}); + +/** + * Watch script for recompiling JavaScript and CSS + */ +gulp.task('watch', function () { + gulp.watch(path.source.coffee + '**/*.coffee', ['build']); + gulp.watch(path.source.sass + '**/*.scss', ['sass', 'sass-minify']); +}); + +/** + * Cleans all build and distribution files. + */ +gulp.task('clean', function (cb) { + del([ path.dist.js, path.dist.css]).then(function () { + cb(); + }); +}); diff --git a/debian/missing-sources/epoch/package.json b/debian/missing-sources/epoch/package.json new file mode 100644 index 0000000..80f14ef --- /dev/null +++ b/debian/missing-sources/epoch/package.json @@ -0,0 +1,55 @@ +{ + "name": "epoch-charting", + "version": "0.8.4", + "description": "A general purpose real-time charting library for building beautiful, smooth, and high performance visualizations.", + "keywords": [ + "chart", + "charting", + "visualization", + "svg", + "animation", + "canvas", + "d3" + ], + "homepage": "http://fastly.github.io/epoch/", + "author": { + "name": "rsandor", + "url": "https://github.com/rsandor" + }, + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/fastly/epoch.git" + }, + "main": "dist/js/epoch.js", + "dependencies": { + "d3": "^3.4.13" + }, + "devDependencies": { + "chai": "^3.3.0", + "codo": "^2.0.11", + "coffee-script": "^1.10.0", + "del": "^2.0.2", + "gulp": "^3.9.0", + "gulp-coffee": "^2.3.1", + "gulp-concat": "^2.6.0", + "gulp-mocha": "^2.1.3", + "gulp-order": "^1.1.1", + "gulp-rename": "^1.2.2", + "gulp-sass": "^2.0.4", + "gulp-uglify": "^1.4.1", + "gulp-util": "^3.0.6", + "jsdom": "^7.0.2", + "mocha": "^2.3.3", + "node-minify": "^1.2.1", + "node-sass": "^3.3.3", + "sinon": "^1.17.1", + "xmlhttprequest": "^1.7.0" + }, + "scripts": { + "build": "gulp build", + "unit": "mocha --recursive --compilers coffee:coffee-script/register tests/unit/", + "test": "npm run build && npm run unit", + "codo": "./node_modules/.bin/codo --quiet --private --name Epoch --readme README.md --title 'Epoch Documentation' --output codo-doc src - LICENSE" + } +} diff --git a/debian/missing-sources/epoch/sass/_core.scss b/debian/missing-sources/epoch/sass/_core.scss new file mode 100644 index 0000000..93bc767 --- /dev/null +++ b/debian/missing-sources/epoch/sass/_core.scss @@ -0,0 +1,99 @@ +/* + * Core Epoch Styles + */ + +/** + * Generates the styles needed to define a fill color for a given class name. + * @param $name Name of the class to use (sans the leading .) + * @param $color Fill color to associate with the class name. + */ +@mixin epoch-color($name, $color) { + div.ref.#{$name} { + background-color: $color; + } + .#{$name} { + .line { stroke: $color; } + .area, .dot { fill: $color; } + } + .arc.#{$name} path { + fill: $color; + } + .bar.#{$name} { + fill: $color; + } + .#{$name} { + .bucket { fill: $color; } + } +} + +/** + * Produces categorical color classes for plots (excluding heatmaps). + * @param $list List of colors to use for each category. + * @param $entries Size of the input list. + */ +@mixin epoch-category-colors($list, $entries) { + @for $i from 1 through $entries { + div.ref.category#{$i} { + background-color: nth($list, $i); + } + .category#{$i} { + .line { stroke: nth($list, $i); } + .area, .dot { fill: nth($list, $i); stroke: rgba(0,0,0,0); } + } + .arc.category#{$i} path { + fill: nth($list, $i); + } + .bar.category#{$i} { + fill: nth($list, $i); + } + } +} + +/** + * Produces categorical colors for heatmaps. + * @param $list List of colors to use for categories. + * @param $entries Size of the input list. + */ +@mixin epoch-heatmap-colors($list, $entries) { + @for $i from 1 through $entries { + .category#{$i} { + .bucket { fill: nth($list, $i); } + } + } +} + +// Axis and Tick Shape Rendering +.epoch { + .axis path, .axis line { + shape-rendering: crispEdges; + } + + .axis.canvas .tick line { + shape-rendering: geometricPrecision; + } +} + +/* + * Canvas Styles Reference Container + * + * The reference container is an SVG that is automatically created when Epoch is loaded. It is used + * by the canvas based plots to obtain color information from the page styles by creating reference + * elements and then reading their computed styles. + * + * Note: don't mess with this ;) + */ +div#_canvas_css_reference { + width: 0; + height: 0; + position: absolute; + top: -1000px; + left: -1000px; + svg { + position: absolute; + width: 0; + height: 0; + top: -1000px; + left: -1000px; + } +} + diff --git a/debian/missing-sources/epoch/sass/epoch.scss b/debian/missing-sources/epoch/sass/epoch.scss new file mode 100644 index 0000000..2689262 --- /dev/null +++ b/debian/missing-sources/epoch/sass/epoch.scss @@ -0,0 +1,12 @@ +/* + * Epoch Master SCSS + * + * Includes the core styles and all the themes to produce the complete epoch css file. + * + * By Ryan Sandor Richards + * Copyright 2013 Fastly, Inc. + */ + +@import "core"; +@import "themes/default"; +@import "themes/dark"; diff --git a/debian/missing-sources/epoch/sass/themes/_dark.scss b/debian/missing-sources/epoch/sass/themes/_dark.scss new file mode 100644 index 0000000..e343eff --- /dev/null +++ b/debian/missing-sources/epoch/sass/themes/_dark.scss @@ -0,0 +1,95 @@ +/* + * theme/_dark.scss - Theme design for dark page backgrounds. + * Designed by Ryan Sandor Richards + */ + +$axisAndText: #d0d0d0; +$background: #333; + +.epoch-theme-dark { + // Axes and Ticks + .epoch { + .axis path, .axis line { + stroke: $axisAndText; + } + .axis .tick text { + fill: $axisAndText; + } + } + + // Pie Charts + .arc.pie { + stroke: $background; + } + + .arc.pie text { + fill: $background; + } + + // Gauges + .epoch .gauge-labels .value { + fill: #BBB; + } + + .epoch .gauge { + .arc.outer { + stroke: #999; + } + .arc.inner { + stroke: #AAA; + } + .tick { + stroke: #AAA; + } + + .needle { + fill: #F3DE88; + } + + .needle-base { + fill: #999; + } + } + + // Categorical Colors + $dark_category10: + #909CFF, #FFAC89, #E889E8, #78E8D3, #C2FF97, + #B7BCD1, #FF857F, #F3DE88, #C9935E, #A488FF; + + .epoch, .epoch.category10 { + @include epoch-category-colors($dark_category10, 10); + } + + $dark_category20: + #909CFF, #626AAD, #FFAC89, #BD7F66, + #E889E8, #995A99, #78E8D3, #4F998C, + #C2FF97, #789E5E, #B7BCD1, #7F8391, + #CCB889, #A1906B, #F3DE88, #A89A5E, + #FF857F, #BA615D, #A488FF, #7662B8; + + .epoch.category20 { + @include epoch-category-colors($dark_category20, 20); + } + + $dark_category20b: + #909CFF, #7680D1, #656DB2, #525992, + #FFAC89, #D18D71, #AB735C, #92624E, + #E889E8, #BA6EBA, #9B5C9B, #7B487B, + #78E8D3, #60BAAA, #509B8D, #3F7B70, + #C2FF97, #9FD17C, #7DA361, #65854E; + + .epoch.category20b { + @include epoch-category-colors($dark_category20b, 20); + } + + $dark_category20c: + #B7BCD1, #979DAD, #6E717D, #595C66, + #FF857F, #DE746E, #B55F5A, #964E4B, + #F3DE88, #DBC87B, #BAAA68, #918551, + #C9935E, #B58455, #997048, #735436, + #A488FF, #8670D1, #705CAD, #52447F; + + .epoch.category20c { + @include epoch-category-colors($dark_category20c, 20); + } +} diff --git a/debian/missing-sources/epoch/sass/themes/_default.scss b/debian/missing-sources/epoch/sass/themes/_default.scss new file mode 100644 index 0000000..d3075c8 --- /dev/null +++ b/debian/missing-sources/epoch/sass/themes/_default.scss @@ -0,0 +1,189 @@ +/* + * theme/_default.scss - Default Color Theme + * Categorical Colors Adapted from d3: + * https://github.com/mbostock/d3/wiki/Ordinal-Scales#categorical-colors + */ + +// Fonts +.epoch { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 12pt; +} + +// Axes and Ticks +.epoch { + .axis path, .axis line { + fill: transparent; + stroke: #000; + } + + .axis .tick text { + font-size: 9pt; + } +} + +// Line Charts +.epoch .line { + fill: transparent; + stroke-width: 2px; +} + +.epoch.sparklines .line { + stroke-width: 1px; +} + + +// Area Charts +.epoch .area { + stroke: transparent; +} + +// Pie Charts +.epoch { + .arc.pie { + stroke: #fff; + stroke-width: 1.5px; + } + + .arc.pie text { + stroke: transparent; + fill: white; + font-size: 9pt; + } +} + +// Gauge Charts +.epoch .gauge-labels { + .value { + text-anchor: middle; + font-size: 140%; + fill: #666; + } +} + +.epoch.gauge-tiny { + width: 120px; height: 90px; + + .gauge-labels { + .value { font-size: 80%; } + } + + .gauge { + .arc.outer { + stroke-width: 2px; + } + } +} + +.epoch.gauge-small { + width: 180px; height: 135px; + + .gauge-labels { + .value { font-size: 120%; } + } + + .gauge { + .arc.outer { + stroke-width: 3px; + } + } +} + +.epoch.gauge-medium { + width: 240px; height: 180px; + + .gauge { + .arc.outer { + stroke-width: 3px; + } + } +} + +.epoch.gauge-large { + width: 320px; height: 240px; + .gauge-labels { + .value { font-size: 180%; } + } +} + +.epoch .gauge { + .arc.outer { + stroke-width: 4px; + stroke: #666; + } + .arc.inner { + stroke-width: 1px; + stroke: #555; + } + .tick { + stroke-width: 1px; + stroke: #555; + } + + .needle { + fill: orange; + } + + .needle-base { + fill: #666; + } +} + +// Categorical Colors +$category10: + #1f77b4, #ff7f0e, #2ca02c, #d62728, #9467bd, + #8c564b, #e377c2, #7f7f7f, #bcbd22, #17becf; + +.epoch, .epoch.category10 { + @include epoch-category-colors($category10, 10); +} + +$category20: + #1f77b4, #aec7e8, #ff7f0e, #ffbb78, #2ca02c, + #98df8a, #d62728, #ff9896, #9467bd, #c5b0d5, + #8c564b, #c49c94, #e377c2, #f7b6d2, #7f7f7f, + #c7c7c7, #bcbd22, #dbdb8d, #17becf, #9edae5; + +.epoch.category20 { + @include epoch-category-colors($category20, 20); +} + +$category20b: + #393b79, #5254a3, #6b6ecf, #9c9ede, #637939, + #8ca252, #b5cf6b, #cedb9c, #8c6d31, #bd9e39, + #e7ba52, #e7cb94, #843c39, #ad494a, #d6616b, + #e7969c, #7b4173, #a55194, #ce6dbd, #de9ed6; + +.epoch.category20b { + @include epoch-category-colors($category20b, 20); +} + +$category20c: + #3182bd, #6baed6, #9ecae1, #c6dbef, + #e6550d, #fd8d3c, #fdae6b, #fdd0a2, + #31a354, #74c476, #a1d99b, #c7e9c0, + #756bb1, #9e9ac8, #bcbddc, #dadaeb, + #636363, #969696, #bdbdbd, #d9d9d9; + +.epoch.category20c { + @include epoch-category-colors($category20c, 20); +} + + +/* + * Heatmap Colors + * + * The heatmap real-time graph uses color blending to choose the color for which to render each bucket. + * Basic d3 categorical colors do not work well in this instance because the colors in the sequence + * vary wildly in precieved luminosity. + * + * Below we define small subsets that should work well together because they are of similar luminosities. + * Note: darker colors work better since we use opacity to denote "height" or "intensity" for each bucket + * after picking a mix color. + */ +$heatmap5: + #1f77b4, #2ca02c, #d62728, #8c564b, #7f7f7f; + +.epoch, .epoch.heatmap5 { + @include epoch-heatmap-colors($heatmap5, 5); +} diff --git a/debian/missing-sources/epoch/src/adapters.coffee b/debian/missing-sources/epoch/src/adapters.coffee new file mode 100644 index 0000000..e378af5 --- /dev/null +++ b/debian/missing-sources/epoch/src/adapters.coffee @@ -0,0 +1,13 @@ +# Maps short string names to classes for library adapters. +Epoch._typeMap = + 'area': Epoch.Chart.Area + 'bar': Epoch.Chart.Bar + 'line': Epoch.Chart.Line + 'pie': Epoch.Chart.Pie + 'scatter': Epoch.Chart.Scatter + 'histogram': Epoch.Chart.Histogram + 'time.area': Epoch.Time.Area + 'time.bar': Epoch.Time.Bar + 'time.line': Epoch.Time.Line + 'time.gauge': Epoch.Time.Gauge + 'time.heatmap': Epoch.Time.Heatmap diff --git a/debian/missing-sources/epoch/src/adapters/MooTools.coffee b/debian/missing-sources/epoch/src/adapters/MooTools.coffee new file mode 100644 index 0000000..8b3d544 --- /dev/null +++ b/debian/missing-sources/epoch/src/adapters/MooTools.coffee @@ -0,0 +1,19 @@ +MooToolsModule = -> + # Data key to use for storing a reference to the chart instance on an element. + DATA_NAME = 'epoch-chart' + + # Adds an Epoch chart of the given type to the referenced element. + # @param [Object] options Options for the chart. + # @option options [String] type The type of chart to append to the referenced element. + # @return [Object] The chart instance that was associated with the containing element. + Element.implement 'epoch', (options) -> + self = $$(this) + unless (chart = self.retrieve(DATA_NAME)[0])? + options.el = this + klass = Epoch._typeMap[options.type] + unless klass? + Epoch.exception "Unknown chart type '#{options.type}'" + self.store DATA_NAME, (chart = new klass options) + return chart + +MooToolsModule() if window.MooTools? diff --git a/debian/missing-sources/epoch/src/adapters/jQuery.coffee b/debian/missing-sources/epoch/src/adapters/jQuery.coffee new file mode 100644 index 0000000..e115bad --- /dev/null +++ b/debian/missing-sources/epoch/src/adapters/jQuery.coffee @@ -0,0 +1,18 @@ +jQueryModule = ($) -> + # Data key to use for storing a reference to the chart instance on an element. + DATA_NAME = 'epoch-chart' + + # Adds an Epoch chart of the given type to the referenced element. + # @param [Object] options Options for the chart. + # @option options [String] type The type of chart to append to the referenced element. + # @return [Object] The chart instance that was associated with the containing element. + $.fn.epoch = (options) -> + options.el = @get(0) + unless (chart = @data(DATA_NAME))? + klass = Epoch._typeMap[options.type] + unless klass? + Epoch.exception "Unknown chart type '#{options.type}'" + @data DATA_NAME, (chart = new klass options) + return chart + +jQueryModule(jQuery) if window.jQuery? diff --git a/debian/missing-sources/epoch/src/adapters/zepto.coffee b/debian/missing-sources/epoch/src/adapters/zepto.coffee new file mode 100644 index 0000000..81153dc --- /dev/null +++ b/debian/missing-sources/epoch/src/adapters/zepto.coffee @@ -0,0 +1,27 @@ +zeptoModule = ($) -> + # For mapping charts to selected elements + DATA_NAME = 'epoch-chart' + chartMap = {} + chartId = 0 + next_cid = -> "#{DATA_NAME}-#{++chartId}" + + # Adds an Epoch chart of the given type to the referenced element. + # @param [Object] options Options for the chart. + # @option options [String] type The type of chart to append to the referenced element. + # @return [Object] The chart instance that was associated with the containing element. + $.extend $.fn, + epoch: (options) -> + return chartMap[cid] if (cid = @data(DATA_NAME))? + options.el = @get(0) + + klass = Epoch._typeMap[options.type] + unless klass? + Epoch.exception "Unknown chart type '#{options.type}'" + + @data DATA_NAME, (cid = next_cid()) + chart = new klass options + chartMap[cid] = chart + + return chart + +zeptoModule(Zepto) if window.Zepto? diff --git a/debian/missing-sources/epoch/src/basic.coffee b/debian/missing-sources/epoch/src/basic.coffee new file mode 100644 index 0000000..8af3465 --- /dev/null +++ b/debian/missing-sources/epoch/src/basic.coffee @@ -0,0 +1,243 @@ +# Base class for all two-dimensional basic d3 charts. This class handles axes and +# margins so that subclasses can focus on the construction of particular chart +# types. +class Epoch.Chart.Plot extends Epoch.Chart.SVG + defaults = + domain: null, + range: null, + axes: ['left', 'bottom'] + ticks: + top: 14 + bottom: 14 + left: 5 + right: 5 + tickFormats: + top: Epoch.Formats.regular + bottom: Epoch.Formats.regular + left: Epoch.Formats.si + right: Epoch.Formats.si + + defaultAxisMargins = + top: 25 + right: 50 + bottom: 25 + left: 50 + + optionListeners = + 'option:margins.top': 'marginsChanged' + 'option:margins.right': 'marginsChanged' + 'option:margins.bottom': 'marginsChanged' + 'option:margins.left': 'marginsChanged' + 'option:axes': 'axesChanged' + 'option:ticks.top': 'ticksChanged' + 'option:ticks.right': 'ticksChanged' + 'option:ticks.bottom': 'ticksChanged' + 'option:ticks.left': 'ticksChanged' + 'option:tickFormats.top': 'tickFormatsChanged' + 'option:tickFormats.right': 'tickFormatsChanged' + 'option:tickFormats.bottom': 'tickFormatsChanged' + 'option:tickFormats.left': 'tickFormatsChanged' + 'option:domain': 'domainChanged' + 'option:range': 'rangeChanged' + + # Creates a new plot chart. + # @param [Object] options Options to use when constructing the plot. + # @option options [Object] margins For setting explicit values for the top, + # right, bottom, and left margins in the visualization. Normally these can + # be omitted and the class will set appropriately sized margins given which + # axes are specified. + # @option options [Array] axes A list of axes to display (top, left, bottom, right). + # @option options [Object] ticks Number of ticks to place on the top, left bottom + # and right axes. + # @option options [Object] tickFormats What tick formatting functions to use for + # the top, bottom, left, and right axes. + constructor: (@options={}) -> + givenMargins = Epoch.Util.copy(@options.margins) or {} + super(@options = Epoch.Util.defaults(@options, defaults)) + + # Margins are used in a special way and only for making room for axes. + # However, a user may explicitly set margins in the options, so we need + # to determine if they did so, and zero out the ones they didn't if no + # axis is present. + @margins = {} + for pos in ['top', 'right', 'bottom', 'left'] + @margins[pos] = if @options.margins? and @options.margins[pos]? + @options.margins[pos] + else if @hasAxis(pos) + defaultAxisMargins[pos] + else + 6 + + # Add a translation for the top and left margins + @g = @svg.append("g") + .attr("transform", "translate(#{@margins.left}, #{@margins.top})") + + # Register option change events + @onAll optionListeners + + # Sets the tick formatting function to use on the given axis. + # @param [String] axis Name of the axis. + # @param [Function] fn Formatting function to use. + setTickFormat: (axis, fn) -> + @options.tickFormats[axis] = fn + + # @return [Boolean] <code>true</code> if the chart has an axis with a given name, <code>false</code> otherwise. + # @param [String] axis Name of axis to check. + hasAxis: (axis) -> + @options.axes.indexOf(axis) > -1 + + # @return [Number] Width of the visualization portion of the chart (width - margins). + innerWidth: -> + @width - (@margins.left + @margins.right) + + # @return [Number] Height of the visualization portion of the chart (height - margins). + innerHeight: -> + @height - (@margins.top + @margins.bottom) + + # @return [Function] The x scale for the visualization. + x: -> + domain = @options.domain ? @extent((d) -> d.x) + d3.scale.linear() + .domain(domain) + .range([0, @innerWidth()]) + + # @return [Function] The y scale for the visualization. + y: (givenDomain) -> + d3.scale.linear() + .domain(@_getScaleDomain(givenDomain)) + .range([@innerHeight(), 0]) + + # @return [Function] d3 axis to use for the bottom of the visualization. + bottomAxis: -> + d3.svg.axis().scale(@x()).orient('bottom') + .ticks(@options.ticks.bottom) + .tickFormat(@options.tickFormats.bottom) + + # @return [Function] d3 axis to use for the top of the visualization. + topAxis: -> + d3.svg.axis().scale(@x()).orient('top') + .ticks(@options.ticks.top) + .tickFormat(@options.tickFormats.top) + + # @return [Function] d3 axis to use on the left of the visualization. + leftAxis: -> + range = if @options.range then @options.range.left else null + d3.svg.axis().scale(@y(range)).orient('left') + .ticks(@options.ticks.left) + .tickFormat(@options.tickFormats.left) + + # @return [Function] d3 axis to use on the right of the visualization. + rightAxis: -> + range = if @options.range then @options.range.right else null + d3.svg.axis().scale(@y(range)).orient('right') + .ticks(@options.ticks.right) + .tickFormat(@options.tickFormats.right) + + # Renders the axes for the visualization (subclasses must implement specific + # drawing routines). + draw: -> + if @_axesDrawn + @_redrawAxes() + else + @_drawAxes() + super() + + # Redraws the axes for the visualization. + _redrawAxes: -> + if @hasAxis('bottom') + @g.selectAll('.x.axis.bottom').transition() + .duration(500) + .ease('linear') + .call(@bottomAxis()) + if @hasAxis('top') + @g.selectAll('.x.axis.top').transition() + .duration(500) + .ease('linear') + .call(@topAxis()) + if @hasAxis('left') + @g.selectAll('.y.axis.left').transition() + .duration(500) + .ease('linear') + .call(@leftAxis()) + if @hasAxis('right') + @g.selectAll('.y.axis.right').transition() + .duration(500) + .ease('linear') + .call(@rightAxis()) + + # Draws the initial axes for the visualization. + _drawAxes: -> + if @hasAxis('bottom') + @g.append("g") + .attr("class", "x axis bottom") + .attr("transform", "translate(0, #{@innerHeight()})") + .call(@bottomAxis()) + if @hasAxis('top') + @g.append("g") + .attr('class', 'x axis top') + .call(@topAxis()) + if @hasAxis('left') + @g.append("g") + .attr("class", "y axis left") + .call(@leftAxis()) + if @hasAxis('right') + @g.append('g') + .attr('class', 'y axis right') + .attr('transform', "translate(#{@innerWidth()}, 0)") + .call(@rightAxis()) + @_axesDrawn = true + + dimensionsChanged: -> + super() + @g.selectAll('.axis').remove() + @_axesDrawn = false + @draw() + + # Updates margins in response to a <code>option:margin.*</code> event. + marginsChanged: -> + return unless @options.margins? + for own pos, size of @options.margins + unless size? + @margins[pos] = 6 + else + @margins[pos] = size + + @g.transition() + .duration(750) + .attr("transform", "translate(#{@margins.left}, #{@margins.top})") + + @draw() + + # Updates axes in response to a <code>option:axes</code> event. + axesChanged: -> + # Remove default axis margins + for pos in ['top', 'right', 'bottom', 'left'] + continue if @options.margins? and @options.margins[pos]? + if @hasAxis(pos) + @margins[pos] = defaultAxisMargins[pos] + else + @margins[pos] = 6 + + # Update the margin offset + @g.transition() + .duration(750) + .attr("transform", "translate(#{@margins.left}, #{@margins.top})") + + # Remove the axes and redraw + @g.selectAll('.axis').remove() + @_axesDrawn = false + @draw() + + # Updates ticks in response to a <code>option:ticks.*</code> event. + ticksChanged: -> @draw() + + # Updates tick formats in response to a <code>option:tickFormats.*</code> event. + tickFormatsChanged: -> @draw() + + # Updates chart in response to a <code>option:domain</code> event. + domainChanged: -> @draw() + + # Updates chart in response to a <code>option:range</code> event. + rangeChanged: -> @draw() + +# "They will see us waving from such great heights, come down now..." - The Postal Service diff --git a/debian/missing-sources/epoch/src/basic/area.coffee b/debian/missing-sources/epoch/src/basic/area.coffee new file mode 100644 index 0000000..e44b480 --- /dev/null +++ b/debian/missing-sources/epoch/src/basic/area.coffee @@ -0,0 +1,51 @@ + +# Static stacked area chart implementation using d3. +class Epoch.Chart.Area extends Epoch.Chart.Plot + constructor: (@options={}) -> + @options.type ?= 'area' + super(@options) + @draw() + + # Generates a scale needed to appropriately render the stacked visualization. + # @return [Function] The y scale for the visualization. + y: -> + a = [] + for layer in @getVisibleLayers() + for own k, v of layer.values + a[k] += v.y if a[k]? + a[k] = v.y unless a[k]? + d3.scale.linear() + .domain(@options.range ? [0, d3.max(a)]) + .range([@height - @margins.top - @margins.bottom, 0]) + + # Renders the SVG elements needed to display the stacked area chart. + draw: -> + [x, y, layers] = [@x(), @y(), @getVisibleLayers()] + + @g.selectAll('.layer').remove() + return if layers.length == 0 + + area = d3.svg.area() + .x((d) -> x(d.x)) + .y0((d) -> y(d.y0)) + .y1((d) -> y(d.y0 + d.y)) + + stack = d3.layout.stack() + .values((d) -> d.values) + + data = stack layers + + layer = @g.selectAll('.layer') + .data(layers, (d) -> d.category) + + layer.select('.area') + .attr('d', (d) -> area(d.values)) + + layer.enter().append('g') + .attr('class', (d) -> d.className) + + layer.append('path') + .attr('class', 'area') + .attr('d', (d) -> area(d.values)) + + super() diff --git a/debian/missing-sources/epoch/src/basic/bar.coffee b/debian/missing-sources/epoch/src/basic/bar.coffee new file mode 100644 index 0000000..8fbc427 --- /dev/null +++ b/debian/missing-sources/epoch/src/basic/bar.coffee @@ -0,0 +1,273 @@ +# Static bar chart implementation (using d3). +class Epoch.Chart.Bar extends Epoch.Chart.Plot + defaults = + type: 'bar' + style: 'grouped' + orientation: 'vertical' + padding: + bar: 0.08 + group: 0.1 + outerPadding: + bar: 0.08 + group: 0.1 + + horizontal_specific = + tickFormats: + top: Epoch.Formats.si + bottom: Epoch.Formats.si + left: Epoch.Formats.regular + right: Epoch.Formats.regular + + horizontal_defaults = Epoch.Util.defaults(horizontal_specific, defaults) + + optionListeners = + 'option:orientation': 'orientationChanged' + 'option:padding': 'paddingChanged' + 'option:outerPadding': 'paddingChanged' + 'option:padding:bar': 'paddingChanged' + 'option:padding:group': 'paddingChanged' + 'option:outerPadding:bar': 'paddingChanged' + 'option:outerPadding:group': 'paddingChanged' + + constructor: (@options={}) -> + if @_isHorizontal() + @options = Epoch.Util.defaults(@options, horizontal_defaults) + else + @options = Epoch.Util.defaults(@options, defaults) + super(@options) + @onAll optionListeners + @draw() + + # @return [Boolean] True if the chart is vertical, false otherwise + _isVertical: -> + @options.orientation == 'vertical' + + # @return [Boolean] True if the chart is horizontal, false otherwise + _isHorizontal: -> + @options.orientation == 'horizontal' + + # @return [Function] The scale used to generate the chart's x scale. + x: -> + if @_isVertical() + d3.scale.ordinal() + .domain(Epoch.Util.domain(@getVisibleLayers())) + .rangeRoundBands([0, @innerWidth()], @options.padding.group, @options.outerPadding.group) + else + extent = @extent((d) -> d.y) + extent[0] = Math.min(0, extent[0]) + d3.scale.linear() + .domain(extent) + .range([0, @width - @margins.left - @margins.right]) + + # @return [Function] The x scale used to render the horizontal bar chart. + x1: (x0) -> + d3.scale.ordinal() + .domain((layer.category for layer in @getVisibleLayers())) + .rangeRoundBands([0, x0.rangeBand()], @options.padding.bar, @options.outerPadding.bar) + + # @return [Function] The y scale used to render the bar chart. + y: -> + if @_isVertical() + extent = @extent((d) -> d.y) + extent[0] = Math.min(0, extent[0]) + d3.scale.linear() + .domain(extent) + .range([@height - @margins.top - @margins.bottom, 0]) + else + d3.scale.ordinal() + .domain(Epoch.Util.domain(@getVisibleLayers())) + .rangeRoundBands([0, @innerHeight()], @options.padding.group, @options.outerPadding.group) + + # @return [Function] The x scale used to render the vertical bar chart. + y1: (y0) -> + d3.scale.ordinal() + .domain((layer.category for layer in @getVisibleLayers())) + .rangeRoundBands([0, y0.rangeBand()], @options.padding.bar, @options.outerPadding.bar) + + # Remaps the bar chart data into a form that is easier to display. + # @return [Array] The reorganized data. + _remapData: -> + map = {} + for layer in @getVisibleLayers() + className = 'bar ' + layer.className.replace(/\s*layer\s*/, '') + for entry in layer.values + map[entry.x] ?= [] + map[entry.x].push { label: layer.category, y: entry.y, className: className } + ({group: k, values: v} for own k, v of map) + + # Draws the bar char. + draw: -> + if @_isVertical() + @_drawVertical() + else + @_drawHorizontal() + super() + + # Draws the bar chart with a vertical orientation + _drawVertical: -> + [x0, y] = [@x(), @y()] + x1 = @x1(x0) + height = @height - @margins.top - @margins.bottom + data = @_remapData() + + # 1) Join + layer = @g.selectAll(".layer") + .data(data, (d) -> d.group) + + # 2) Update + layer.transition().duration(750) + .attr("transform", (d) -> "translate(#{x0(d.group)}, 0)") + + # 3) Enter / Create + layer.enter().append("g") + .attr('class', 'layer') + .attr("transform", (d) -> "translate(#{x0(d.group)}, 0)") + + rects = layer.selectAll('rect') + .data((group) -> group.values) + + rects.attr('class', (d) -> d.className) + + rects.transition().duration(600) + .attr('x', (d) -> x1(d.label)) + .attr('y', (d) -> y(d.y)) + .attr('width', x1.rangeBand()) + .attr('height', (d) -> height - y(d.y)) + + rects.enter().append('rect') + .attr('class', (d) -> d.className) + .attr('x', (d) -> x1(d.label)) + .attr('y', (d) -> y(d.y)) + .attr('width', x1.rangeBand()) + .attr('height', (d) -> height - y(d.y)) + + rects.exit().transition() + .duration(150) + .style('opacity', '0') + .remove() + + # 4) Update new and existing + + # 5) Exit / Remove + layer.exit() + .transition() + .duration(750) + .style('opacity', '0') + .remove() + + # Draws the bar chart with a horizontal orientation + _drawHorizontal: -> + [x, y0] = [@x(), @y()] + y1 = @y1(y0) + width = @width - @margins.left - @margins.right + data = @_remapData() + + # 1) Join + layer = @g.selectAll(".layer") + .data(data, (d) -> d.group) + + # 2) Update + layer.transition().duration(750) + .attr("transform", (d) -> "translate(0, #{y0(d.group)})") + + # 3) Enter / Create + layer.enter().append("g") + .attr('class', 'layer') + .attr("transform", (d) -> "translate(0, #{y0(d.group)})") + + rects = layer.selectAll('rect') + .data((group) -> group.values) + + rects.attr('class', (d) -> d.className) + + rects.transition().duration(600) + .attr('x', (d) -> 0) + .attr('y', (d) -> y1(d.label)) + .attr('height', y1.rangeBand()) + .attr('width', (d) -> x(d.y)) + + rects.enter().append('rect') + .attr('class', (d) -> d.className) + .attr('x', (d) -> 0) + .attr('y', (d) -> y1(d.label)) + .attr('height', y1.rangeBand()) + .attr('width', (d) -> x(d.y)) + + rects.exit().transition() + .duration(150) + .style('opacity', '0') + .remove() + + # 4) Update new and existing + + # 5) Exit / Remove + layer.exit() + .transition() + .duration(750) + .style('opacity', '0') + .remove() + + # Generates specific tick marks to emulate d3's linear scale axis ticks + # for ordinal scales. Note: this should only be called if the user has + # defined a set number of ticks for a given axis. + # @param [Number] numTicks Number of ticks to generate + # @param [String] dataKey Property name of a datum to use for the tick value + # @return [Array] The ticks for the given axis + _getTickValues: (numTicks, dataKey='x') -> + return [] unless @data[0]? + total = @data[0].values.length + step = Math.ceil(total / numTicks)|0 + tickValues = (@data[0].values[i].x for i in [0...total] by step) + + # @return [Function] d3 axis to use for the bottom of the visualization. + bottomAxis: -> + axis = d3.svg.axis().scale(@x()).orient('bottom') + .ticks(@options.ticks.bottom) + .tickFormat(@options.tickFormats.bottom) + if @_isVertical() and @options.ticks.bottom? + axis.tickValues @_getTickValues(@options.ticks.bottom) + axis + + # @return [Function] d3 axis to use for the top of the visualization. + topAxis: -> + axis = d3.svg.axis().scale(@x()).orient('top') + .ticks(@options.ticks.top) + .tickFormat(@options.tickFormats.top) + if @_isVertical() and @options.ticks.top? + axis.tickValues @_getTickValues(@options.ticks.top) + axis + + # @return [Function] d3 axis to use on the left of the visualization. + leftAxis: -> + axis = d3.svg.axis().scale(@y()).orient('left') + .ticks(@options.ticks.left) + .tickFormat(@options.tickFormats.left) + if @_isHorizontal() and @options.ticks.left? + axis.tickValues @_getTickValues(@options.ticks.left) + axis + + # @return [Function] d3 axis to use on the right of the visualization. + rightAxis: -> + axis = d3.svg.axis().scale(@y()).orient('right') + .ticks(@options.ticks.right) + .tickFormat(@options.tickFormats.right) + if @_isHorizontal() and @options.ticks.right? + axis.tickValues @_getTickValues(@options.ticks.right) + axis + + # Updates orientation in response <code>option:orientation</code>. + orientationChanged: -> + top = @options.tickFormats.top + bottom = @options.tickFormats.bottom + left = @options.tickFormats.left + right = @options.tickFormats.right + + @options.tickFormats.left = top + @options.tickFormats.right = bottom + @options.tickFormats.top = left + @options.tickFormats.bottom = right + + @draw() + + # Updates padding in response to <code>option:padding:*</code> and <code>option:outerPadding:*</code>. + paddingChanged: -> @draw() diff --git a/debian/missing-sources/epoch/src/basic/histogram.coffee b/debian/missing-sources/epoch/src/basic/histogram.coffee new file mode 100644 index 0000000..4548e67 --- /dev/null +++ b/debian/missing-sources/epoch/src/basic/histogram.coffee @@ -0,0 +1,61 @@ +class Epoch.Chart.Histogram extends Epoch.Chart.Bar + defaults = + type: 'histogram' + domain: [0, 100] + bucketRange: [0, 100] + buckets: 10 + cutOutliers: false + + optionListeners = + 'option:bucketRange': 'bucketRangeChanged' + 'option:buckets': 'bucketsChanged' + 'option:cutOutliers': 'cutOutliersChanged' + + constructor: (@options={}) -> + super(@options = Epoch.Util.defaults(@options, defaults)) + @onAll optionListeners + @draw() + + # Prepares data by sorting it into histogram buckets as instructed by the chart options. + # @param [Array] data Data to prepare for rendering. + # @return [Array] The data prepared to be displayed as a histogram. + _prepareData: (data) -> + bucketSize = (@options.bucketRange[1] - @options.bucketRange[0]) / @options.buckets + + prepared = [] + for layer in data + buckets = (0 for i in [0...@options.buckets]) + for point in layer.values + index = parseInt((point.x - @options.bucketRange[0]) / bucketSize) + + if @options.cutOutliers and ((index < 0) or (index >= @options.buckets)) + continue + if index < 0 + index = 0 + else if index >= @options.buckets + index = @options.buckets - 1 + + buckets[index] += parseInt point.y + + preparedLayer = { values: (buckets.map (d, i) -> {x: parseInt(i) * bucketSize, y: d}) } + for own k, v of layer + preparedLayer[k] = v unless k == 'values' + + prepared.push preparedLayer + + return prepared + + # Called when options change, this prepares the raw data for the chart according to the new + # options, sets it, and renders the chart. + resetData: -> + @setData @rawData + @draw() + + # Updates the chart in response to an <code>option:bucketRange</code> event. + bucketRangeChanged: -> @resetData() + + # Updates the chart in response to an <code>option:buckets</code> event. + bucketsChanged: -> @resetData() + + # Updates the chart in response to an <code>option:cutOutliers</code> event. + cutOutliersChanged: -> @resetData() diff --git a/debian/missing-sources/epoch/src/basic/line.coffee b/debian/missing-sources/epoch/src/basic/line.coffee new file mode 100644 index 0000000..de56780 --- /dev/null +++ b/debian/missing-sources/epoch/src/basic/line.coffee @@ -0,0 +1,46 @@ +# Static line chart implementation (using d3). +class Epoch.Chart.Line extends Epoch.Chart.Plot + constructor: (@options={}) -> + @options.type ?= 'line' + super(@options) + @draw() + + # @return [Function] The line generator used to construct the plot. + line: (layer) -> + [x, y] = [@x(), @y(layer.range)] + d3.svg.line() + .x((d) -> x(d.x)) + .y((d) -> y(d.y)) + + # Draws the line chart. + draw: -> + [x, y, layers] = [@x(), @y(), @getVisibleLayers()] + + # Zero visible layers, just drop all and get out + if layers.length == 0 + return @g.selectAll('.layer').remove() + + # 1) Join + layer = @g.selectAll('.layer') + .data(layers, (d) -> d.category) + + # 2) Update (only existing) + layer.select('.line').transition().duration(500) + .attr('d', (l) => @line(l)(l.values)) + + # 3) Enter (Create) + layer.enter().append('g') + .attr('class', (l) -> l.className) + .append('path') + .attr('class', 'line') + .attr('d', (l) => @line(l)(l.values)) + + # 4) Update (existing & new) + # Nuuupp + + # 5) Exit (Remove) + layer.exit().transition().duration(750) + .style('opacity', '0') + .remove() + + super() diff --git a/debian/missing-sources/epoch/src/basic/pie.coffee b/debian/missing-sources/epoch/src/basic/pie.coffee new file mode 100644 index 0000000..e794a2f --- /dev/null +++ b/debian/missing-sources/epoch/src/basic/pie.coffee @@ -0,0 +1,59 @@ + +# Static Pie Chart implementation (using d3). +class Epoch.Chart.Pie extends Epoch.Chart.SVG + defaults = + type: 'pie' + margin: 10 + inner: 0 + + # Creates a new pie chart. + # @param [Object] options Options for the pie chart. + # @option options [Number] margin Margins to add around the pie chart (default: 10). + # @option options [Number] inner The inner radius for the chart (default: 0). + constructor: (@options={}) -> + super(@options = Epoch.Util.defaults(@options, defaults)) + @pie = d3.layout.pie().sort(null) + .value (d) -> d.value + @arc = d3.svg.arc() + .outerRadius(=> (Math.max(@width, @height) / 2) - @options.margin) + .innerRadius(=> @options.inner) + @g = @svg.append('g') + .attr("transform", "translate(#{@width/2}, #{@height/2})") + @on 'option:margin', 'marginChanged' + @on 'option:inner', 'innerChanged' + @draw() + + # Draws the pie chart + draw: -> + @g.selectAll('.arc').remove() + + arcs = @g.selectAll(".arc") + .data(@pie(@getVisibleLayers()), (d) -> d.data.category) + + arcs.enter().append('g') + .attr('class', (d) -> "arc pie " + d.data.className) + + arcs.select('path') + .attr('d', @arc) + + arcs.select('text') + .attr("transform", (d) => "translate(#{@arc.centroid(d)})") + .text((d) -> d.data.label or d.data.category) + + path = arcs.append("path") + .attr("d", @arc) + .each((d) -> @._current = d) + + text = arcs.append("text") + .attr("transform", (d) => "translate(#{@arc.centroid(d)})") + .attr("dy", ".35em") + .style("text-anchor", "middle") + .text((d) -> d.data.label or d.data.category) + + super() + + # Updates margins in response to an <code>option:margin</code> event. + marginChanged: -> @draw() + + # Updates inner margin in response to an <code>option:inner</code> event. + innerChanged: -> @draw() diff --git a/debian/missing-sources/epoch/src/basic/scatter.coffee b/debian/missing-sources/epoch/src/basic/scatter.coffee new file mode 100644 index 0000000..b8563ec --- /dev/null +++ b/debian/missing-sources/epoch/src/basic/scatter.coffee @@ -0,0 +1,59 @@ + +# Static scatter plot implementation (using d3). +class Epoch.Chart.Scatter extends Epoch.Chart.Plot + defaults = + type: 'scatter' + radius: 3.5 + axes: ['top', 'bottom', 'left', 'right'] + + # Creates a new scatter plot. + # @param [Object] options Options for the plot. + # @option options [Number] radius The default radius to use for the points in + # the plot (default 3.5). This can be overrwitten by individual points. + constructor: (@options={}) -> + super(@options = Epoch.Util.defaults(@options, defaults)) + @on 'option:radius', 'radiusChanged' + @draw() + + # Draws the scatter plot. + draw: -> + [x, y, layers] = [@x(), @y(), @getVisibleLayers()] + radius = @options.radius + + if layers.length == 0 + return @g.selectAll('.layer').remove() + + layer = @g.selectAll('.layer') + .data(layers, (d) -> d.category) + + layer.enter().append('g') + .attr('class', (d) -> d.className) + + dots = layer.selectAll('.dot') + .data((l) -> l.values) + + dots.transition().duration(500) + .attr("r", (d) -> d.r ? radius) + .attr("cx", (d) -> x(d.x)) + .attr("cy", (d) -> y(d.y)) + + dots.enter().append('circle') + .attr('class', 'dot') + .attr("r", (d) -> d.r ? radius) + .attr("cx", (d) -> x(d.x)) + .attr("cy", (d) -> y(d.y)) + + dots.exit().transition() + .duration(750) + .style('opacity', 0) + .remove() + + layer.exit().transition() + .duration(750) + .style('opacity', 0) + .remove() + + super() + + # Updates radius in response to an <code>option:radius</code> event. + radiusChanged: -> @draw() diff --git a/debian/missing-sources/epoch/src/core/chart.coffee b/debian/missing-sources/epoch/src/core/chart.coffee new file mode 100644 index 0000000..068335a --- /dev/null +++ b/debian/missing-sources/epoch/src/core/chart.coffee @@ -0,0 +1,361 @@ +# The base class for all charts in Epoch. Defines chart dimensions, keeps a reference +# of the chart's containing elements. And defines core method for handling data and +# drawing. +class Epoch.Chart.Base extends Epoch.Events + defaults = + width: 320 + height: 240 + dataFormat: null + + optionListeners = + 'option:width': 'dimensionsChanged' + 'option:height': 'dimensionsChanged' + 'layer:shown': 'layerChanged' + 'layer:hidden': 'layerChanged' + + # Creates a new base chart. + # @param [Object] options Options to set for this chart. + # @option options [Integer] width Sets an explicit width for the visualization. + # @option options [Integer] height Sets an explicit height for the visualization. + # @option options [Object, String] dataFormat Specific data format for the chart. + # @option options [Object] model Data model for the chart. + constructor: (@options={}) -> + super() + + if @options.model + if @options.model.hasData()? + @setData(@options.model.getData(@options.type, @options.dataFormat)) + else + @setData(@options.data or []) + @options.model.on 'data:updated', => @setDataFromModel() + else + @setData(@options.data or []) + + if @options.el? + @el = d3.select(@options.el) + + @width = @options.width + @height = @options.height + + if @el? + @width = @el.width() unless @width? + @height = @el.height() unless @height? + else + @width = defaults.width unless @width? + @height = defaults.height unless @height? + @el = d3.select(document.createElement('DIV')) + .attr('width', @width) + .attr('height', @height) + + @onAll optionListeners + + # @return [Object] A copy of this charts options. + _getAllOptions: -> + Epoch.Util.defaults({}, @options) + + # Chart option accessor. + # @param key Name of the option to fetch. Can be hierarchical, e.g. 'margins.left' + # @return The requested option if found, undefined otherwise. + _getOption: (key) -> + parts = key.split('.') + scope = @options + while parts.length and scope? + subkey = parts.shift() + scope = scope[subkey] + scope + + # Chart option mutator. + # @param key Name of the option to fetch. Can be hierarchical, e.g. 'margins.top' + # @param value Value to set for the option. + # @event option:`key` Triggers an option event with the given key being set. + _setOption: (key, value) -> + parts = key.split('.') + scope = @options + while parts.length + subkey = parts.shift() + if parts.length == 0 + scope[subkey] = arguments[1] + @trigger "option:#{arguments[0]}" + return + unless scope[subkey]? + scope[subkey] = {} + scope = scope[subkey] + + # Sets all options given an object of mixed hierarchical keys and nested objects. + # @param [Object] options Options to set. + # @event option:* Triggers an option event for each key that was set + _setManyOptions: (options, prefix='') -> + for own key, value of options + if Epoch.isObject(value) + @_setManyOptions value, "#{prefix + key}." + else + @_setOption prefix + key, value + + # General accessor / mutator for chart options. + # + # @overload option() + # Fetches chart options. + # @return a copy of this chart's options. + # + # @overload option(name) + # Fetches the value the option with the given name. + # @param [String] name Name of the option to fetch. Can be hierarchical, e.g. <code>'margins.left'</code> + # @return The requested option if found, <code>undefined</code> otherwise. + # + # @overload option(name, value) + # Sets an option and triggers the associated event. + # @param [String] name Name of the option to fetch. Can be hierarchical, e.g. 'margins.top' + # @param value Value to set for the option. + # @event option:`name` Triggers an option event with the given key being set. + # + # @overload option(options) + # Sets multiple options at once. + # @param [Object] options Options to set for the chart. + # @event option:* Triggers an option event for each key that was set. + option: -> + if arguments.length == 0 + @_getAllOptions() + else if arguments.length == 1 and Epoch.isString(arguments[0]) + @_getOption arguments[0] + else if arguments.length == 2 and Epoch.isString(arguments[0]) + @_setOption arguments[0], arguments[1] + else if arguments.length == 1 and Epoch.isObject(arguments[0]) + @_setManyOptions arguments[0] + + # Retrieves and sets data from the chart's model + setDataFromModel: -> + prepared = @_prepareData @options.model.getData(@options.type, @options.dataFormat) + @data = @_annotateLayers(prepared) + @draw() + + # Set the initial data for the chart. + # @param data Data to initially set for the given chart. The data format can vary + # from chart to chart. The base class assumes that the data provided will be an + # array of layers. + setData: (data, options={}) -> + prepared = @_prepareData (@rawData = @_formatData(data)) + @data = @_annotateLayers(prepared) + + # Performs post formatted data preparation. + # @param data Data to prepare before setting. + # @return The prepared data. + _prepareData: (data) -> data + + # Performs data formatting before setting the charts data + # @param data Data to be formatted. + # @return The chart specific formatted data. + _formatData: (data) -> + Epoch.Data.formatData(data, @options.type, @options.dataFormat) + + # Annotates data to add class names, categories, and initial visibility states + _annotateLayers: (data) -> + category = 1 + for layer in data + classes = ['layer'] + classes.push "category#{category}" + layer.category = category + layer.visible = true + classes.push(Epoch.Util.dasherize layer.label) if layer.label? + layer.className = classes.join(' ') + category++ + return data + + # Finds a layer in the chart's current data that has the given label or index. + # @param [String, Number] labelOrIndex The label or index of the layer to find. + _findLayer: (labelOrIndex) -> + layer = null + if Epoch.isString(labelOrIndex) + for l in @data + if l.label == labelOrIndex + layer = l + break + else if Epoch.isNumber(labelOrIndex) + index = parseInt(labelOrIndex) + layer = @data[index] unless index < 0 or index >= @data.length + return layer + + # Instructs the chart that a data layer should be displayed. + # @param [String, Number] labelOrIndex The label or index of the layer to show. + # @event 'layer:shown' If a layer that was previously hidden now became visible. + showLayer: (labelOrIndex) -> + return unless (layer = @_findLayer labelOrIndex) + return if layer.visible + layer.visible = true + @trigger 'layer:shown' + + # Instructs the chart that a data layer should not be displayed. + # @param [String, Number] labelOrIndex The label or index of the layer to hide. + # @event 'layer:hidden' If a layer that was visible was made hidden. + hideLayer: (labelOrIndex) -> + return unless (layer = @_findLayer labelOrIndex) + return unless layer.visible + layer.visible = false + @trigger 'layer:hidden' + + # Instructs the chart that a data layer's visibility should be toggled. + # @param [String, Number] labelOrIndex The label or index of the layer to toggle. + # @event 'layer:shown' If the layer was made visible + # @event 'layer:hidden' If the layer was made invisible + toggleLayer: (labelOrIndex) -> + return unless (layer = @_findLayer labelOrIndex) + layer.visible = !layer.visible + if layer.visible + @trigger 'layer:shown' + else + @trigger 'layer:hidden' + + # Determines whether or not a data layer is visible. + # @param [String, Number] labelOrIndex The label or index of the layer to toggle. + # @return <code>true</code> if the layer is visible, <code>false</code> otherwise. + isLayerVisible: (labelOrIndex) -> + return null unless (layer = @_findLayer labelOrIndex) + layer.visible + + # Calculates an array of layers in the charts data that are flagged as visible. + # @return [Array] The chart's visible layers. + getVisibleLayers: -> + return @data.filter((layer) -> layer.visible) + + # Updates the chart with new data. + # @param data Data to replace the current data for the chart. + # @param [Boolean] draw Whether or not to redraw the chart after the data has been set. + # Default: true. + update: (data, draw=true) -> + @setData data + @draw() if draw + + # Draws the chart. Triggers the 'draw' event, subclasses should call super() after drawing to + # ensure that the event is triggered. + # @abstract Must be overriden in child classes to perform chart specific drawing. + draw: -> @trigger 'draw' + + # Determines a resulting scale domain for the y axis given a domain + # @param [Mixed] givenDomain A set domain, a label associated with mutliple + # layers, or null. + # @return The domain for the y scale + _getScaleDomain: (givenDomain) -> + # Explicitly set given domain + if Array.isArray(givenDomain) + return givenDomain + + # Check for "labeled" layer ranges + if Epoch.isString(givenDomain) + layers = @getVisibleLayers() + .filter((l) -> l.range == givenDomain) + .map((l) -> l.values) + if layers? && layers.length + values = Epoch.Util.flatten(layers).map((d) -> d.y) + minFn = (memo, curr) -> if curr < memo then curr else memo + maxFn = (memo, curr) -> if curr > memo then curr else memo + return [values.reduce(minFn, values[0]), values.reduce(maxFn, values[0])] + + # Find the domain based on chart options + if Array.isArray(@options.range) + @options.range + else if @options.range && Array.isArray(@options.range.left) + @options.range.left + else if @options.range && Array.isArray(@options.range.right) + @options.range.right + else + @extent((d) -> d.y) + + # Calculates an extent throughout the layers based on the given comparator. + # @param [Function] cmp Comparator to use for performing the min and max for the extent + # calculation. + # @return [Array] an extent array with the first element as the minimum value in the + # chart's data set and the second element as the maximum. + extent: (cmp) -> + [ + d3.min(@getVisibleLayers(), (layer) -> d3.min(layer.values, cmp)), + d3.max(@getVisibleLayers(), (layer) -> d3.max(layer.values, cmp)) + ] + + # Updates the width and height members and container dimensions in response to an + # 'option:width' or 'option:height' event. + dimensionsChanged: -> + @width = @option('width') or @width + @height = @option('height') or @height + @el.width(@width) + @el.height(@height) + + # Updates the chart in response to a layer being shown or hidden + layerChanged: -> + @draw() + +# Base class for all SVG charts (via d3). +class Epoch.Chart.SVG extends Epoch.Chart.Base + # Initializes the chart and places the rendering SVG in the specified HTML + # containing element. + # @param [Object] options Options for the SVG chart. + # @option options [HTMLElement] el Container element for the chart. + # @option options [Array] data Layered data used to render the chart. + constructor: (@options={}) -> + super(@options) + if @el? + @svg = @el.append('svg') + else + @svg = d3.select(document.createElement('svg')) + @svg.attr + xmlns: 'http://www.w3.org/2000/svg', + width: @width, + height: @height + + # Resizes the svg element in response to a 'option:width' or 'option:height' event. + dimensionsChanged: -> + super() + @svg.attr('width', @width).attr('height', @height) + +# Base Class for all Canvas based charts. +class Epoch.Chart.Canvas extends Epoch.Chart.Base + # Initializes the chart and places the rendering canvas in the specified + # HTML container element. + # @param [Object] options Options for the SVG chart. + # @option options [HTMLElement] el Container element for the chart. + # @option options [Array] data Layered data used to render the chart. + constructor: (@options={}) -> + super(@options) + + if @options.pixelRatio? + @pixelRatio = @options.pixelRatio + else if window.devicePixelRatio? + @pixelRatio = window.devicePixelRatio + else + @pixelRatio = 1 + + @canvas = d3.select( document.createElement('CANVAS') ) + @canvas.style + 'width': "#{@width}px" + 'height': "#{@height}px" + + @canvas.attr + width: @getWidth() + height: @getHeight() + + @el.node().appendChild @canvas.node() if @el? + @ctx = Epoch.Util.getContext @canvas.node() + + # @return [Number] width of the canvas with respect to the pixel ratio of the display + getWidth: -> @width * @pixelRatio + + # @return [Number] height of the canvas with respect to the pixel ratio of the display + getHeight: -> @height * @pixelRatio + + # Clears the render canvas. + clear: -> + @ctx.clearRect(0, 0, @getWidth(), @getHeight()) + + # @return [Object] computed styles for the given selector in the context of this chart. + # @param [String] selector The selector used to compute the styles. + getStyles: (selector) -> + Epoch.QueryCSS.getStyles(selector, @el) + + # Resizes the canvas element when the dimensions of the container change + dimensionsChanged: -> + super() + @canvas.style {'width': "#{@width}px", 'height': "#{@height}px"} + @canvas.attr { width: @getWidth(), height: @getHeight() } + + # Purges QueryCSS cache and redraws the Canvas based chart. + redraw: -> + Epoch.QueryCSS.purge() + @draw() diff --git a/debian/missing-sources/epoch/src/core/context.coffee b/debian/missing-sources/epoch/src/core/context.coffee new file mode 100644 index 0000000..8566552 --- /dev/null +++ b/debian/missing-sources/epoch/src/core/context.coffee @@ -0,0 +1,25 @@ +# Rendering context used for unit testing. +class Epoch.TestContext + VOID_METHODS = [ + 'arc', 'arcTo', 'beginPath', 'bezierCurveTo', 'clearRect', + 'clip', 'closePath', 'drawImage', 'fill', 'fillRect', 'fillText', + 'moveTo', 'quadraticCurveTo', 'rect', 'restore', 'rotate', 'save', + 'scale', 'scrollPathIntoView', 'setLineDash', 'setTransform', + 'stroke', 'strokeRect', 'strokeText', 'transform', 'translate', 'lineTo' + ] + + # Creates a new test rendering context. + constructor: -> + @_log = [] + @_makeFauxMethod(method) for method in VOID_METHODS + + # Creates a fake method with the given name that logs the method called + # and arguments passed when executed. + # @param name Name of the fake method to create. + _makeFauxMethod: (name) -> + @[name] = -> @_log.push "#{name}(#{(arg.toString() for arg in arguments).join(',')})" + + # Faux method that emulates the "getImageData" method + getImageData: -> + @_log.push "getImageData(#{(arg.toString() for arg in arguments).join(',')})" + return { width: 0, height: 0, resolution: 1.0, data: [] } diff --git a/debian/missing-sources/epoch/src/core/css.coffee b/debian/missing-sources/epoch/src/core/css.coffee new file mode 100644 index 0000000..03a7310 --- /dev/null +++ b/debian/missing-sources/epoch/src/core/css.coffee @@ -0,0 +1,131 @@ +# Singelton class used to query CSS styles by way of reference elements. +# This allows canvas based visualizations to use the same styles as their +# SVG counterparts. +class QueryCSS + # Reference container id + REFERENCE_CONTAINER_ID = '_canvas_css_reference' + + # Container Hash Attribute + CONTAINER_HASH_ATTR = 'data-epoch-container-id' + + # Handles automatic container id generation + containerCount = 0 + nextContainerId = -> "epoch-container-#{containerCount++}" + + # Expression used to derive tag name, id, and class names from + # selectors given the the put method. + PUT_EXPR = /^([^#. ]+)?(#[^. ]+)?(\.[^# ]+)?$/ + + # Whether or not to log full selector lists + logging = false + + # Converts selectors into actual dom elements (replaces put.js) + # Limited the functionality to what Epoch actually needs to + # operate correctly. We detect class names, ids, and element + # tag names. + put = (selector) -> + match = selector.match(PUT_EXPR) + return Epoch.error('Query CSS cannot match given selector: ' + selector) unless match? + [whole, tag, id, classNames] = match + tag = (tag ? 'div').toUpperCase() + + element = document.createElement(tag) + element.id = id.substr(1) if id? + if classNames? + element.className = classNames.substr(1).replace(/\./g, ' ') + + return element + + # Lets the user set whether or not to log selector lists and resulting DOM trees. + # Useful for debugging QueryCSS itself. + @log: (b) -> + logging = b + + # Key-Value cache for computed styles that we found using this class. + @cache = {} + + # List of styles to pull from the full list of computed styles + @styleList = ['fill', 'stroke', 'stroke-width'] + + # The svg reference container + @container = null + + # Purges the selector to style cache + @purge: -> + QueryCSS.cache = {} + + # Gets the reference element container. + @getContainer: -> + return QueryCSS.container if QueryCSS.container? + container = document.createElement('DIV') + container.id = REFERENCE_CONTAINER_ID + document.body.appendChild(container) + QueryCSS.container = d3.select(container) + + # @return [String] A unique identifier for the given container and selector. + # @param [String] selector Selector from which to derive the styles + # @param container The containing element for a chart. + @hash: (selector, container) -> + containerId = container.attr(CONTAINER_HASH_ATTR) + unless containerId? + containerId = nextContainerId() + container.attr(CONTAINER_HASH_ATTR, containerId) + return "#{containerId}__#{selector}" + + # @return The computed styles for the given selector in the given container element. + # @param [String] selector Selector from which to derive the styles. + # @param container HTML containing element in which to place the reference SVG. + @getStyles: (selector, container) -> + # 0) Check for cached styles + cacheKey = QueryCSS.hash(selector, container) + cache = QueryCSS.cache[cacheKey] + return cache if cache? + + # 1) Build a full reference tree (parents, container, and selector elements) + parents = [] + parentNode = container.node().parentNode + + while parentNode? and parentNode.nodeName.toLowerCase() != 'body' + parents.unshift parentNode + parentNode = parentNode.parentNode + parents.push container.node() + + selectorList = [] + for element in parents + sel = element.nodeName.toLowerCase() + if element.id? and element.id.length > 0 + sel += '#' + element.id + if element.className? and element.className.length > 0 + sel += '.' + Epoch.Util.trim(element.className).replace(/\s+/g, '.') + selectorList.push sel + + selectorList.push('svg') + + for subSelector in Epoch.Util.trim(selector).split(/\s+/) + selectorList.push(subSelector) + + console.log(selectorList) if logging + + parent = root = put(selectorList.shift()) + while selectorList.length + el = put(selectorList.shift()) + parent.appendChild el + parent = el + + console.log(root) if logging + + # 2) Place the reference tree and fetch styles given the selector + QueryCSS.getContainer().node().appendChild(root) + + ref = d3.select('#' + REFERENCE_CONTAINER_ID + ' ' + selector) + styles = {} + for name in QueryCSS.styleList + styles[name] = ref.style(name) + QueryCSS.cache[cacheKey] = styles + + # 3) Cleanup and return the styles + QueryCSS.getContainer().html('') + return styles + + +Epoch.QueryCSS = QueryCSS
\ No newline at end of file diff --git a/debian/missing-sources/epoch/src/core/d3.coffee b/debian/missing-sources/epoch/src/core/d3.coffee new file mode 100644 index 0000000..a33d717 --- /dev/null +++ b/debian/missing-sources/epoch/src/core/d3.coffee @@ -0,0 +1,25 @@ +# Gets the width of the first node, or sets the width of all the nodes +# in a d3 selection. +# @param value [Number, String] (optional) Width to set for all the nodes in the selection. +# @return The selection if setting the width of the nodes, or the width +# in pixels of the first node in the selection. +d3.selection::width = (value) -> + if value? and Epoch.isString(value) + @style('width', value) + else if value? and Epoch.isNumber(value) + @style('width', "#{value}px") + else + +Epoch.Util.getComputedStyle(@node(), null).width.replace('px', '') + +# Gets the height of the first node, or sets the height of all the nodes +# in a d3 selection. +# @param value (optional) Height to set for all the nodes in the selection. +# @return The selection if setting the height of the nodes, or the height +# in pixels of the first node in the selection. +d3.selection::height = (value) -> + if value? and Epoch.isString(value) + @style('height', value) + else if value? and Epoch.isNumber(value) + @style('height', "#{value}px") + else + +Epoch.Util.getComputedStyle(@node(), null).height.replace('px', '')
\ No newline at end of file diff --git a/debian/missing-sources/epoch/src/core/format.coffee b/debian/missing-sources/epoch/src/core/format.coffee new file mode 100644 index 0000000..d65932f --- /dev/null +++ b/debian/missing-sources/epoch/src/core/format.coffee @@ -0,0 +1,15 @@ +# Tick formatter identity. +Epoch.Formats.regular = (d) -> d + +# Tick formatter that formats the numbers using standard SI postfixes. +Epoch.Formats.si = (d) -> Epoch.Util.formatSI(d) + +# Tick formatter for percentages. +Epoch.Formats.percent = (d) -> (d*100).toFixed(1) + "%" + +# Tick formatter for seconds from timestamp data. +Epoch.Formats.seconds = (t) -> d3Seconds(new Date(t*1000)) +d3Seconds = d3.time.format('%I:%M:%S %p') + +# Tick formatter for bytes +Epoch.Formats.bytes = (d) -> Epoch.Util.formatBytes(d) diff --git a/debian/missing-sources/epoch/src/core/util.coffee b/debian/missing-sources/epoch/src/core/util.coffee new file mode 100644 index 0000000..30995bf --- /dev/null +++ b/debian/missing-sources/epoch/src/core/util.coffee @@ -0,0 +1,236 @@ +typeFunction = (objectName) -> (v) -> + Object::toString.call(v) == "[object #{objectName}]" + +# @return [Boolean] <code>true</code> if the given value is an array, <code>false</code> otherwise. +# @param v Value to test. +Epoch.isArray = Array.isArray ? typeFunction('Array') + +# @return [Boolean] <code>true</code> if the given value is an object, <code>false</code> otherwise. +# @param v Value to test. +Epoch.isObject = typeFunction('Object') + +# @return [Boolean] <code>true</code> if the given value is a string, <code>false</code> otherwise. +# @param v Value to test. +Epoch.isString = typeFunction('String') + +# @return [Boolean] <code>true</code> if the given value is a function, <code>false</code> otherwise. +# @param v Value to test. +Epoch.isFunction = typeFunction('Function') + +# @return [Boolean] <code>true</code> if the given value is a number, <code>false</code> otherwise. +# @param v Value to test. +Epoch.isNumber = typeFunction('Number') + +# Attempts to determine if a given value represents a DOM element. The result is always correct if the +# browser implements DOM Level 2, but one can fool it on certain versions of IE. Adapted from: +# <a href="http://goo.gl/yaD9hV">Stack Overflow #384286</a>. +# @return [Boolean] <code>true</code> if the given value is a DOM element, <code>false</code> otherwise. +# @param v Value to test. +Epoch.isElement = (v) -> + if HTMLElement? + v instanceof HTMLElement + else + v? and Epoch.isObject(v) and v.nodeType == 1 and Epoch.isString(v.nodeName) + +# Determines if a given value is a non-empty array. +# @param v Value to test. +# @return [Boolean] <code>true</code> if the given value is an array with at least one element. +Epoch.isNonEmptyArray = (v) -> + Epoch.isArray(v) and v.length > 0 + +# Generates shallow copy of an object. +# @return A shallow copy of the given object. +# @param [Object] original Object for which to make the shallow copy. +Epoch.Util.copy = (original) -> + return null unless original? + copy = {} + copy[k] = v for own k, v of original + return copy + +# Creates a deep copy of the given options filling in missing defaults. +# @param [Object] options Options to copy. +# @param [Object] defaults Default values for the options. +Epoch.Util.defaults = (options, defaults) -> + result = Epoch.Util.copy(options) + for own k, v of defaults + opt = options[k] + def = defaults[k] + bothAreObjects = Epoch.isObject(opt) and Epoch.isObject(def) + + if opt? and def? + if bothAreObjects and not Epoch.isArray(opt) + result[k] = Epoch.Util.defaults(opt, def) + else + result[k] = opt + else if opt? + result[k] = opt + else + result[k] = def + + return result + +# Formats numbers with standard postfixes (e.g. K, M, G) +# @param [Number] v Value to format. +# @param [Integer] fixed Number of floating point digits to fix after conversion. +# @param [Boolean] fixIntegers Whether or not to add floating point digits to non-floating point results. +# @example Formatting a very large number +# Epoch.Util.formatSI(1120000) == "1.1 M" +Epoch.Util.formatSI = (v, fixed=1, fixIntegers=false) -> + if v < 1000 + q = v + q = q.toFixed(fixed) unless (q|0) == q and !fixIntegers + return q + + for own i, label of ['K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'] + base = Math.pow(10, ((i|0)+1)*3) + if v >= base and v < Math.pow(10, ((i|0)+2)*3) + q = v/base + q = q.toFixed(fixed) unless (q % 1) == 0 and !fixIntegers + return "#{q} #{label}" + +# Formats large bandwidth and disk space usage numbers with byte postfixes (e.g. KB, MB, GB, etc.) +# @param [Number] v Value to format. +# @param [Integer] fixed Number of floating point digits to fix after conversion. +# @param [Boolean] fixIntegers Whether or not to add floating point digits to non-floating point results. +# @example Formatting a large number of bytes +# Epoch.Util.formatBytes(5.21 * Math.pow(2, 20)) == "5.2 MB" +Epoch.Util.formatBytes = (v, fixed=1, fix_integers=false) -> + if v < 1024 + q = v + q = q.toFixed(fixed) unless (q % 1) == 0 and !fix_integers + return "#{q} B" + + for own i, label of ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] + base = Math.pow(1024, (i|0)+1) + if v >= base and v < Math.pow(1024, (i|0)+2) + q = v/base + q = q.toFixed(fixed) unless (q % 1) == 0 and !fix_integers + return "#{q} #{label}" + +# @return a "dasherized" css class names from a given string +# @example Using dasherize +# Epoch.Util.dasherize('My Awesome Name') == 'my-awesome-name' +Epoch.Util.dasherize = (str) -> + Epoch.Util.trim(str).replace("\n", '').replace(/\s+/g, '-').toLowerCase() + +# @return the full domain of a given variable from an array of layers +# @param [Array] layers Layered plot data. +# @param [String] key The key name of the value at on each entry in the layers. +Epoch.Util.domain = (layers, key='x') -> + set = {} + domain = [] + for layer in layers + for entry in layer.values + continue if set[entry[key]]? + domain.push(entry[key]) + set[entry[key]] = true + return domain + +# Strips whitespace from the beginning and end of a string. +# @param [String] string String to trim. +# @return [String] The string without leading or trailing whitespace. +# Returns null if the given parameter was not a string. +Epoch.Util.trim = (string) -> + return null unless Epoch.isString(string) + string.replace(/^\s+/g, '').replace(/\s+$/g, '') + +# Returns the computed styles of an element in the document +# @param [HTMLElement] Element for which to fetch the styles. +# @param [String] pseudoElement Pseudo selectors on which to search for the element. +# @return [Object] The styles for the given element. +Epoch.Util.getComputedStyle = (element, pseudoElement) -> + if Epoch.isFunction(window.getComputedStyle) + window.getComputedStyle(element, pseudoElement) + else if element.currentStyle? + element.currentStyle + +# Converts a CSS color string into an RGBA string with the given opacity +# @param [String] color Color string to convert into an rgba +# @param [Number] opacity Opacity to use for the resulting color. +# @return the resulting rgba color string. +Epoch.Util.toRGBA = (color, opacity) -> + if (parts = color.match /^rgba\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*[0-9\.]+\)/) + [all, r, g, b] = parts + result = "rgba(#{r},#{g},#{b},#{opacity})" + else if (v = d3.rgb color) + result = "rgba(#{v.r},#{v.g},#{v.b},#{opacity})" + return result + +# Obtains a graphics context for the given canvas node. Nice to have +# this abstracted out in case we want to support WebGL in the future. +# Also allows us to setup a special context when unit testing, as +# jsdom doesn't have canvas support, and node-canvas is a pain in the +# butt to install properly across different platforms. +Epoch.Util.getContext = (node, type='2d') -> + node.getContext(type) + +# Basic eventing base class for all Epoch classes. +class Epoch.Events + constructor: -> + @_events = {} + + # Registers a callback to a given event. + # @param [String] name Name of the event. + # @param [Function, String] callback Either a closure to call when the event fires + # or a string that denotes a method name to call on this object. + on: (name, callback) -> + return unless callback? + @_events[name] ?= [] + @_events[name].push callback + + # Registers a map of event names to given callbacks. This method calls <code>.on</code> + # directly for each of the events given. + # @param [Object] map A map of event names to callbacks. + onAll: (map) -> + return unless Epoch.isObject(map) + @on(name, callback) for own name, callback of map + + # Removes a specific callback listener or all listeners for a given event. + # @param [String] name Name of the event. + # @param [Function, String] callback (Optional) Callback to remove from the listener list. + # If this parameter is not provided then all listeners will be removed for the event. + off: (name, callback) -> + return unless Epoch.isArray(@_events[name]) + return delete(@_events[name]) unless callback? + while (i = @_events[name].indexOf(callback)) >= 0 + @_events[name].splice(i, 1) + + # Removes a set of callback listeners for all events given in the map or array of strings. + # This method calls <code>.off</code> directly for each event and callback to remove. + # @param [Object, Array] mapOrList Either a map that associates event names to specific callbacks + # or an array of event names for which to completely remove listeners. + offAll: (mapOrList) -> + if Epoch.isArray(mapOrList) + @off(name) for name in mapOrList + else if Epoch.isObject(mapOrList) + @off(name, callback) for own name, callback of mapOrList + + # Triggers an event causing all active listeners to be executed. + # @param [String] name Name of the event to fire. + trigger: (name) -> + return unless @_events[name]? + args = (arguments[i] for i in [1...arguments.length]) + for callback in @_events[name] + fn = null + if Epoch.isString(callback) + fn = @[callback] + else if Epoch.isFunction(callback) + fn = callback + unless fn? + Epoch.exception "Callback for event '#{name}' is not a function or reference to a method." + fn.apply @, args + +# Performs a single pass flatten on a multi-array +# @param [Array] multiarray A deep multi-array to flatten +# @returns [Array] A single pass flatten of the multi-array +Epoch.Util.flatten = (multiarray) -> + if !Array.isArray(multiarray) + throw new Error('Epoch.Util.flatten only accepts arrays') + result = [] + for array in multiarray + if Array.isArray(array) + for item in array + result.push item + else + result.push array + result diff --git a/debian/missing-sources/epoch/src/data.coffee b/debian/missing-sources/epoch/src/data.coffee new file mode 100644 index 0000000..7627fd4 --- /dev/null +++ b/debian/missing-sources/epoch/src/data.coffee @@ -0,0 +1,311 @@ +Epoch.Data ?= {} +Epoch.Data.Format ?= {} + +# Private Helper Function for data formats below +applyLayerLabel = (layer, options, i, keys=[]) -> + [labels, autoLabels, keyLabels] = [options.labels, options.autoLabels, options.keyLabels] + if labels? and Epoch.isArray(labels) and labels.length > i + layer.label = labels[i] + else if keyLabels and keys.length > i + layer.label = keys[i] + else if autoLabels + label = [] + while i >= 0 + label.push String.fromCharCode(65+(i%26)) + i -= 26 + layer.label = label.join('') + return layer + +# Formats a given input array for the chart of the specified type. Notes: +# +# * Basic pie charts require a flat array of numbers +# * Real-time histogram charts require sparse histogram objects +# +# @param data Data array to format (can be multidimensional to allow for multiple layers). +# @option options [String] type Type of chart for which to format the data. +# @option options [Function] x(d, i) Maps the data to x values given a data point and the index of the point. +# @option options [Function] y(d, i) Maps the data to y values given a data point and the index of the point. +# @option options [Function] time(d, i, startTime) Maps the data to time values for real-time plots given the point and index. +# @option options [Array] labels Labels to apply to each data layer. +# @option options [Boolean] autoLabels Apply labels of ascending capital letters to each layer if true. +# @option options [Number] startTime Unix timestamp used as the starting point for auto acsending times in +# real-time data formatting. +Epoch.Data.Format.array = (-> + defaultOptions = + x: (d, i) -> i + y: (d, i) -> d + time: (d, i, startTime) -> parseInt(startTime) + parseInt(i) + type: 'area' + autoLabels: false + labels: [] + startTime: parseInt(new Date().getTime() / 1000) + + buildLayers = (data, options, mapFn) -> + result = [] + if Epoch.isArray(data[0]) + for own i, series of data + result.push applyLayerLabel({values: series.map(mapFn)}, options, parseInt(i)) + else + result.push applyLayerLabel({values: data.map(mapFn)}, options, 0) + return result + + formatBasicPlot = (data, options) -> + buildLayers data, options, (d, i) -> + { x: options.x(d, i), y: options.y(d, i) } + + formatTimePlot = (data, options) -> + buildLayers data, options, (d, i) -> + { time: options.time(d, i, options.startTime), y: options.y(d, i) } + + formatHeatmap = (data, options) -> + buildLayers data, options, (d, i) -> + { time: options.time(d, i, options.startTime), histogram: d } + + formatPie = (data, options) -> + result = [] + for own i, v of data + return [] unless Epoch.isNumber(data[0]) + result.push applyLayerLabel({ value: v }, options, i) + return result + + format = (data=[], options={}) -> + return [] unless Epoch.isNonEmptyArray(data) + opt = Epoch.Util.defaults options, defaultOptions + + if opt.type == 'time.heatmap' + formatHeatmap data, opt + else if opt.type.match /^time\./ + formatTimePlot data, opt + else if opt.type == 'pie' + formatPie data, opt + else + formatBasicPlot data, opt + + format.entry = (datum, options={}) -> + if options.type == 'time.gauge' + return 0 unless datum? + opt = Epoch.Util.defaults options, defaultOptions + d = if Epoch.isArray(datum) then datum[0] else datum + return opt.y(d, 0) + + return [] unless datum? + unless options.startTime? + options.startTime = parseInt(new Date().getTime() / 1000) + + if Epoch.isArray(datum) + data = datum.map (d) -> [d] + else + data = [datum] + + (layer.values[0] for layer in format(data, options)) + + return format +)() + +# Formats an input array of tuples such that the first element of the tuple is set +# as the x-coordinate and the second element as the y-coordinate. Supports layers +# of tupled series. For real-time plots the first element of a tuple is set as the +# time component of the value. +# +# This formatter will return an empty array if the chart <code>type</code> option is +# set as 'time.heatmap', 'time.gauge', or 'pie'. +# +# @param data Data array to format (can be multidimensional to allow for multiple layers). +# @option options [String] type Type of chart for which to format the data. +# @option options [Function] x(d, i) Maps the data to x values given a data point and the index of the point. +# @option options [Function] y(d, i) Maps the data to y values given a data point and the index of the point. +# @option options [Function] time(d, i, startTime) Maps the data to time values for real-time plots given the point and index. +# @option options [Array] labels Labels to apply to each data layer. +# @option options [Boolean] autoLabels Apply labels of ascending capital letters to each layer if true. +Epoch.Data.Format.tuple = (-> + defaultOptions = + x: (d, i) -> d + y: (d, i) -> d + time: (d, i) -> d + type: 'area' + autoLabels: false + labels: [] + + buildLayers = (data, options, mapFn) -> + return [] unless Epoch.isArray(data[0]) + result = [] + if Epoch.isArray(data[0][0]) + for own i, series of data + result.push applyLayerLabel({values: series.map(mapFn)}, options, parseInt(i)) + else + result.push applyLayerLabel({values: data.map(mapFn)}, options, 0) + return result + + format = (data=[], options={}) -> + return [] unless Epoch.isNonEmptyArray(data) + opt = Epoch.Util.defaults options, defaultOptions + + if opt.type == 'pie' or opt.type == 'time.heatmap' or opt.type == 'time.gauge' + return [] + else if opt.type.match /^time\./ + buildLayers data, opt, (d, i) -> + {time: opt.time(d[0], parseInt(i)), y: opt.y(d[1], parseInt(i))} + else + buildLayers data, opt, (d, i) -> + {x: opt.x(d[0], parseInt(i)), y: opt.y(d[1], parseInt(i))} + + format.entry = (datum, options={}) -> + return [] unless datum? + unless options.startTime? + options.startTime = parseInt(new Date().getTime() / 1000) + + if Epoch.isArray(datum) and Epoch.isArray(datum[0]) + data = datum.map (d) -> [d] + else + data = [datum] + + (layer.values[0] for layer in format(data, options)) + + return format +)() + +# This formatter expects to be passed a flat array of objects and a list of keys. +# It then extracts the value for each key across each of the objects in the array +# to produce multi-layer plot data of the given chart type. Note that this formatter +# also can be passed an <code>x</code> or <code>time</code> option as a string that +# allows the programmer specify a key to use for the value of the first component +# (x or time) of each resulting layer value. +# +# Note that this format does not work with basic pie charts nor real-time gauge charts. +# +# @param [Array] data Flat array of objects to format. +# @param [Array] keys List of keys used to extract data from each of the objects. +# @option options [String] type Type of chart for which to format the data. +# @option options [Function, String] x Either the key to use for the x-componet of +# the resulting values or a function of the data at that point and index of the data. +# @option options [Function, String] time Either an object key or function to use for the +# time-component of resulting real-time plot values. +# @option options [Function] y(d, i) Maps the data to y values given a data point and the index of the point. +# @option options [Array] labels Labels to apply to each data layer. +# @option options [Boolean] autoLabels Apply labels of ascending capital letters to each layer if true. +# @option options [Boolean] keyLabels Apply labels using the keys passed to the formatter (defaults to true). +# @option options [Number] startTime Unix timestamp used as the starting point for auto acsending times in +# real-time data formatting. +Epoch.Data.Format.keyvalue = (-> + defaultOptions = + type: 'area', + x: (d, i) -> parseInt(i) + y: (d, i) -> d + time: (d, i, startTime) -> parseInt(startTime) + parseInt(i) + labels: [] + autoLabels: false + keyLabels: true + startTime: parseInt(new Date().getTime() / 1000) + + buildLayers = (data, keys, options, mapFn) -> + result = [] + for own j, key of keys + values = [] + for own i, d of data + values.push mapFn(d, key, parseInt(i)) + result.push applyLayerLabel({ values: values }, options, parseInt(j), keys) + return result + + formatBasicPlot = (data, keys, options) -> + buildLayers data, keys, options, (d, key, i) -> + if Epoch.isString(options.x) + x = d[options.x] + else + x = options.x(d, parseInt(i)) + { x: x, y: options.y(d[key], parseInt(i)) } + + formatTimePlot = (data, keys, options, rangeName='y') -> + buildLayers data, keys, options, (d, key, i) -> + if Epoch.isString(options.time) + value = { time: d[options.time] } + else + value = { time: options.time(d, parseInt(i), options.startTime) } + value[rangeName] = options.y(d[key], parseInt(i)) + value + + format = (data=[], keys=[], options={}) -> + return [] unless Epoch.isNonEmptyArray(data) and Epoch.isNonEmptyArray(keys) + opt = Epoch.Util.defaults options, defaultOptions + + if opt.type == 'pie' or opt.type == 'time.gauge' + return [] + else if opt.type == 'time.heatmap' + formatTimePlot data, keys, opt, 'histogram' + else if opt.type.match /^time\./ + formatTimePlot data, keys, opt + else + formatBasicPlot data, keys, opt + + format.entry = (datum, keys=[], options={}) -> + return [] unless datum? and Epoch.isNonEmptyArray(keys) + unless options.startTime? + options.startTime = parseInt(new Date().getTime() / 1000) + (layer.values[0] for layer in format([datum], keys, options)) + + return format +)() + +# Convenience data formatting method for easily accessing the various formatters. +# @param [String] formatter Name of the formatter to use. +# @param [Array] data Data to format. +# @param [Object] options Options to pass to the formatter (if any). +Epoch.data = (formatter, args...) -> + return [] unless (formatFn = Epoch.Data.Format[formatter])? + formatFn.apply formatFn, args + + +# Method used by charts and models for handling option based data formatting. +# Abstracted here because we'd like to allow models and indivisual charts to +# perform this action depending on the context. +Epoch.Data.formatData = (data=[], type, dataFormat) -> + return data unless Epoch.isNonEmptyArray(data) + + if Epoch.isString(dataFormat) + opts = { type: type } + return Epoch.data(dataFormat, data, opts) + + return data unless Epoch.isObject(dataFormat) + return data unless dataFormat.name? and Epoch.isString(dataFormat.name) + return data unless Epoch.Data.Format[dataFormat.name]? + + args = [dataFormat.name, data] + if dataFormat.arguments? and Epoch.isArray(dataFormat.arguments) + args.push(a) for a in dataFormat.arguments + + if dataFormat.options? + opts = dataFormat.options + if type? + opts.type ?= type + args.push opts + else if type? + args.push {type: type} + + Epoch.data.apply(Epoch.data, args) + +# Method used to format incoming entries for real-time charts. +Epoch.Data.formatEntry = (datum, type, format) -> + return datum unless format? + + if Epoch.isString(format) + opts = { type: type } + return Epoch.Data.Format[format].entry datum, opts + + return datum unless Epoch.isObject(format) + return datum unless format.name? and Epoch.isString(format.name) + return datum unless Epoch.Data.Format[format.name]? + + dataFormat = Epoch.Util.defaults format, {} + + args = [datum] + if dataFormat.arguments? and Epoch.isArray(dataFormat.arguments) + args.push(a) for a in dataFormat.arguments + + if dataFormat.options? + opts = dataFormat.options + opts.type = type + args.push opts + else if type? + args.push {type: type} + + entry = Epoch.Data.Format[dataFormat.name].entry + entry.apply entry, args diff --git a/debian/missing-sources/epoch/src/epoch.coffee b/debian/missing-sources/epoch/src/epoch.coffee new file mode 100644 index 0000000..b2d06ca --- /dev/null +++ b/debian/missing-sources/epoch/src/epoch.coffee @@ -0,0 +1,17 @@ +window.Epoch ?= {} +window.Epoch.Chart ?= {} +window.Epoch.Time ?= {} +window.Epoch.Util ?= {} +window.Epoch.Formats ?= {} + +# Sends a warning to the developer console with the given message. +# @param [String] msg Message for the warning. +Epoch.warn = (msg) -> + (console.warn or console.log)("Epoch Warning: #{msg}") + +# Raises an exception with the given message (with the 'Epoch Error:' preamble). +# @param [String] msg Specific message for the exception. +Epoch.exception = (msg) -> + throw "Epoch Error: #{msg}" + +# "I think, baby, I was born just a little late!" -- Middle Class Rut diff --git a/debian/missing-sources/epoch/src/model.coffee b/debian/missing-sources/epoch/src/model.coffee new file mode 100644 index 0000000..76f6acd --- /dev/null +++ b/debian/missing-sources/epoch/src/model.coffee @@ -0,0 +1,55 @@ +# Data model for epoch charts. By instantiating a model and passing it to each +# of the charts on a page the application programmer can update data once and +# have each of the charts respond accordingly. +# +# In addition to setting basic / historical data via the setData method, the +# model also supports the push method, which when used will cause real-time +# plots to automatically update and animate. +class Epoch.Model extends Epoch.Events + defaults = + dataFormat: null + + # Creates a new Model. + # @option options dataFormat The default data fromat for the model. + # @option data Initial data for the model. + constructor: (options={}) -> + super() + options = Epoch.Util.defaults options, defaults + @dataFormat = options.dataFormat + @data = options.data + @loading = false + + # Sets the model's data. + # @param data Data to set for the model. + # @event data:updated Instructs listening charts that new data is available. + setData: (data) -> + @data = data + @trigger 'data:updated' + + # Pushes a new entry into the model. + # @param entry Entry to push. + # @event data:push Instructs listening charts that a new data entry is available. + push: (entry) -> + @entry = entry + @trigger 'data:push' + + # Determines if the model has data. + # @return true if the model has data, false otherwise. + hasData: -> + @data? + + # Retrieves and formats adata for the specific chart type and data format. + # @param [String] type Type of the chart for which to fetch the data. + # @param [String, Object] dataFormat (optional) Used to override the model's default data format. + # @return The model's data formatted based the parameters. + getData: (type, dataFormat) -> + dataFormat ?= @dataFormat + Epoch.Data.formatData @data, type, dataFormat + + # Retrieves the latest data entry that was pushed into the model. + # @param [String] type Type of the chart for which to fetch the data. + # @param [String, Object] dataFormat (optional) Used to override the model's default data format. + # @return The model's next data entry formatted based the parameters. + getNext: (type, dataFormat) -> + dataFormat ?= @dataFormat + Epoch.Data.formatEntry @entry, type, dataFormat diff --git a/debian/missing-sources/epoch/src/time.coffee b/debian/missing-sources/epoch/src/time.coffee new file mode 100644 index 0000000..402cc50 --- /dev/null +++ b/debian/missing-sources/epoch/src/time.coffee @@ -0,0 +1,615 @@ +# Real-time Plot Base Class. Uses an html5 canvas to recreate the basic d3 drawing routines +# while simultaneously reducing the load on the viewer's cpu (and, you know, not leaking +# memory which ultimately leads to a crashed browser). +# +# The class also handles the creation of axes and margins common to all time-series plots. +# Furthermore it layers the canvas below an SVG element to keep visual consistency when +# rendering text, glyphs, etc. +class Epoch.Time.Plot extends Epoch.Chart.Canvas + defaults = + range: null + fps: 24 + historySize: 120 + windowSize: 40 + queueSize: 10 + axes: ['bottom'] + ticks: + time: 15 + left: 5 + right: 5 + tickFormats: + top: Epoch.Formats.seconds + bottom: Epoch.Formats.seconds + left: Epoch.Formats.si + right: Epoch.Formats.si + + defaultAxisMargins = + top: 25 + right: 50 + bottom: 25 + left: 50 + + optionListeners = + 'option:margins': 'marginsChanged' + 'option:margins.top': 'marginsChanged' + 'option:margins.right': 'marginsChanged' + 'option:margins.bottom': 'marginsChanged' + 'option:margins.left': 'marginsChanged' + 'option:axes': 'axesChanged' + 'option:ticks': 'ticksChanged' + 'option:ticks.top': 'ticksChanged' + 'option:ticks.right': 'ticksChanged' + 'option:ticks.bottom': 'ticksChanged' + 'option:ticks.left': 'ticksChanged' + 'option:tickFormats': 'tickFormatsChanged' + 'option:tickFormats.top': 'tickFormatsChanged' + 'option:tickFormats.right': 'tickFormatsChanged' + 'option:tickFormats.bottom': 'tickFormatsChanged' + 'option:tickFormats.left': 'tickFormatsChanged' + + # Creates a new real-time plot. + # + # @param [Object] options Options for the plot. + # @option options [Integer] fps Number of frames per second to use when animating + # the plot. + # @option options [Integer] historySize Maximum number of elements to keep in history + # for the plot. + # @option options [Integer] windowSize Number of entries to simultaneously display + # when rendering the visualization. + # @option options [Integer] queueSize Number of elements to queue while not animating + # but still recieving elements. In some browsers, intervals will not fire if the + # page containing them is not the active tab. By setting a maximum limit to the + # number of unprocessed data points we can ensure that the memory footprint of the + # page does not get out of hand. + # @option options [Object] margins Explicit margins to use for the visualization. Note + # that these are optional and will be automatically generated based on which axes are + # used for the visualization. Margins are keyed by their position (top, left, bottom + # and/or right) and should map to [Integer] values. + # @option options [Array] axes Which axes to display when rendering the visualization + # (top, left, bottom, and/or right). + # @option options [Object] ticks Number of ticks to display on each axis available axes + # ares: time, left, and right. The number provided for the left and right axes are in + # absolute terms (i.e. there will be exactly that number of ticks). The time ticks + # denote how often a tick should be generated (e.g. if 5 is provided then a tick will + # be added every fifth time you push a new data entry into the visualization). + # @option options [Object] tickFormats Formatting functions for ticks on the given axes. + # The avaiable axes are: top, bottom, left, and right. + constructor: (@options) -> + givenMargins = Epoch.Util.copy(@options.margins) or {} + super(@options = Epoch.Util.defaults(@options, defaults)) + + if @options.model + @options.model.on 'data:push', => @pushFromModel() + + # Queue entering data to get around memory bloat and "non-active" tab issues + @_queue = [] + + # Margins + @margins = {} + for pos in ['top', 'right', 'bottom', 'left'] + @margins[pos] = if @options.margins? and @options.margins[pos]? + @options.margins[pos] + else if @hasAxis(pos) + defaultAxisMargins[pos] + else + 6 + + # SVG Overlay + @svg = @el.insert('svg', ':first-child') + .attr('width', @width) + .attr('height', @height) + .style('z-index', '1000') + + # Position the canvas "under" the SVG element + if @el.style('position') != 'absolute' and @el.style('position') != 'relative' + @el.style('position', 'relative') + + @canvas.style { position: 'absolute', 'z-index': '999' } + @_sizeCanvas() + + # Animation / Transitions + @animation = + interval: null + active: false + delta: => -(@w() / @options.fps), + tickDelta: => -( (@w() / @pixelRatio) / @options.fps ) + frame: 0, + duration: @options.fps + + # Add SVG Axes + @_buildAxes() + + # Callback used for animation + @animationCallback = => @_animate() + + # Listen for specific option changes + @onAll optionListeners + + # Positions and sizes the canvas based on margins and axes. + _sizeCanvas: -> + @canvas.attr + width: @innerWidth() + height: @innerHeight() + + @canvas.style + width: "#{@innerWidth() / @pixelRatio}px" + height: "#{@innerHeight() / @pixelRatio}px" + top: "#{@margins.top}px" + left: "#{@margins.left}px" + + # Removes any axes found in the SVG and adds both the time and range axes to the plot. + _buildAxes: -> + @svg.selectAll('.axis').remove() + @_prepareTimeAxes() + @_prepareRangeAxes() + + # Works exactly as in Epoch.Chart.Base with the addition of truncating value arrays + # to that of the historySize defined in the chart's options. + _annotateLayers: (prepared) -> + data = [] + for own i, layer of prepared + copy = Epoch.Util.copy(layer) + start = Math.max(0, layer.values.length - @options.historySize) + copy.values = layer.values.slice(start) + classes = ['layer'] + classes.push "category#{(i|0)+1}" + classes.push(Epoch.Util.dasherize layer.label) if layer.label? + copy.className = classes.join(' ') + copy.visible = true + data.push copy + return data + + # This method is called to provide a small offset for placement of horizontal ticks. + # The value returned will be added to the x value of each tick as they are being + # rendered. + # + # @return [Number] The horizontal offset for the top and bottom axes ticks. + _offsetX: -> 0 + + # Builds time axes (bottom and top) + _prepareTimeAxes: -> + if @hasAxis('bottom') + axis = @bottomAxis = @svg.append('g') + .attr('class', "x axis bottom canvas") + .attr('transform', "translate(#{@margins.left-1}, #{@innerHeight()/@pixelRatio+@margins.top})") + axis.append('path') + .attr('class', 'domain') + .attr('d', "M0,0H#{@innerWidth()/@pixelRatio+1}") + + if @hasAxis('top') + axis = @topAxis = @svg.append('g') + .attr('class', "x axis top canvas") + .attr('transform', "translate(#{@margins.left-1}, #{@margins.top})") + axis.append('path') + .attr('class', 'domain') + .attr('d', "M0,0H#{@innerWidth()/@pixelRatio+1}") + + @_resetInitialTimeTicks() + + # Resets the initial ticks for the time axes. + _resetInitialTimeTicks: -> + tickInterval = @options.ticks.time + @_ticks = [] + @_tickTimer = tickInterval + + @bottomAxis.selectAll('.tick').remove() if @bottomAxis? + @topAxis.selectAll('.tick').remove() if @topAxis? + + for layer in @data + continue unless Epoch.isNonEmptyArray(layer.values) + [i, k] = [@options.windowSize-1, layer.values.length-1] + while i >= 0 and k >= 0 + @_pushTick i, layer.values[k].time, false, true + i -= tickInterval + k -= tickInterval + break + + # Builds the range axes (left and right) + _prepareRangeAxes: -> + if @hasAxis('left') + @svg.append("g") + .attr("class", "y axis left") + .attr('transform', "translate(#{@margins.left-1}, #{@margins.top})") + .call(@leftAxis()) + + if @hasAxis('right') + @svg.append('g') + .attr('class', 'y axis right') + .attr('transform', "translate(#{@width - @margins.right}, #{@margins.top})") + .call(@rightAxis()) + + # @return [Object] The d3 left axis. + leftAxis: -> + ticks = @options.ticks.left + axis = d3.svg.axis().scale(@ySvgLeft()).orient('left') + .tickFormat(@options.tickFormats.left) + if ticks == 2 + axis.tickValues @extent((d) -> d.y) + else + axis.ticks(ticks) + + # @return [Object] The d3 right axis. + rightAxis: -> + extent = @extent((d) -> d.y) + ticks = @options.ticks.right + axis = d3.svg.axis().scale(@ySvgRight()).orient('right') + .tickFormat(@options.tickFormats.right) + if ticks == 2 + axis.tickValues @extent((d) -> d.y) + else + axis.ticks(ticks) + + # Determines if the visualization is displaying the axis with the given name. + # @param [String] name Name of the axis + # @return [Boolean] <code>true</code> if the axis was set in the options, <code>false</code> otherwise. + hasAxis: (name) -> + @options.axes.indexOf(name) > -1 + + # @return [Number] the width of the visualization area of the plot (full width - margins) + innerWidth: -> + (@width - (@margins.left + @margins.right)) * @pixelRatio + + # @return [Number] the height of the visualization area of the plot (full height - margins) + innerHeight: -> + (@height - (@margins.top + @margins.bottom)) * @pixelRatio + + # Abstract method for performing any preprocessing before queuing new entries + # @param entry [Object] The entry to prepare. + # @return [Object] The prepared entry. + _prepareEntry: (entry) -> entry + + # Abstract method for preparing a group of layered entries entering the visualization + # @param [Array] layers The layered entries to prepare. + # @return [Array] The prepared layers. + _prepareLayers: (layers) -> layers + + # This method will remove the first incoming entry from the visualization's queue + # and shift it into the working set (aka window). It then starts the animating the + # transition of the element into the visualization. + # @event transition:start in the case that animation is actually started. + _startTransition: -> + return if @animation.active == true or @_queue.length == 0 + @trigger 'transition:start' + @_shift() + @animation.active = true + @animation.interval = setInterval(@animationCallback, 1000/@options.fps) + + # Stops animating and clears the animation interval given there is no more + # incoming data to process. Also finalizes tick entering and exiting. + # @event transition:end After the transition has completed. + _stopTransition: -> + return unless @inTransition() + + # Shift data off the end + for layer in @data + continue unless layer.values.length > @options.windowSize + 1 + layer.values.shift() + + # Finalize tick transitions + [firstTick, lastTick] = [@_ticks[0], @_ticks[@_ticks.length-1]] + + if lastTick? and lastTick.enter + lastTick.enter = false + lastTick.opacity = 1 + + if firstTick? and firstTick.exit + @_shiftTick() + + # Reset the animation frame modulus + @animation.frame = 0 + + # Trigger that we are done transitioning + @trigger 'transition:end' + + # Clear the transition interval unless another entry is already queued + if @_queue.length > 0 + @_shift() + else + @animation.active = false + clearInterval @animation.interval + + # Determines if the plot is currently animating a transition. + # @return [Boolean] <code>true</code> if the plot is animating, <code>false</code> otherwise. + inTransition: -> + @animation.active + + # This method is used by the application programmer to introduce new data into + # the timeseries plot. The method queues the incoming data, ensures a fixed size + # for the data queue, and finally calls <code>_startTransition</code> method to + # begin animating the plot. + # @param [Array] layers Layered incoming visualization data. + # @event push Triggered after the new data has been pushed into the queue. + push: (layers) -> + layers = @_prepareLayers(layers) + + # Handle entry queue maximum size + if @_queue.length > @options.queueSize + @_queue.splice @options.queueSize, (@_queue.length - @options.queueSize) + return false if @_queue.length == @options.queueSize + + # Push the entry into the queue + @_queue.push layers.map((entry) => @_prepareEntry(entry)) + + @trigger 'push' + + # Begin the transition unless we are already doing so + @_startTransition() unless @inTransition() + + # Fetches new entry data from the model in response to a 'data:push' event. + pushFromModel: -> + @push @options.model.getNext(@options.type, @options.dataFormat) + + # Shift elements off the incoming data queue (see the implementation of + # push above). + # + # If there's data to be shoved into the visualization it will pull it + # off the queue and put it into the working dataset. It also calls through + # to @_updateTicks to handle horizontal (or "time") axes tick transitions + # since we're implementing independent of d3 as well. + # + # @event before:shift Before an element has been shifted off the queue. + # @event after:shift After the element has been shifted off the queue. + _shift: -> + @trigger 'before:shift' + entry = @_queue.shift() + layer.values.push(entry[i]) for own i, layer of @data + @_updateTicks(entry[0].time) + @_transitionRangeAxes() + @trigger 'after:shift' + + # Transitions the left and right axes when the range of the plot has changed. + _transitionRangeAxes: -> + if @hasAxis('left') + @svg.selectAll('.y.axis.left').transition() + .duration(500) + .ease('linear') + .call(@leftAxis()) + + if @hasAxis('right') + @svg.selectAll('.y.axis.right').transition() + .duration(500) + .ease('linear') + .call(@rightAxis()) + + # Performs the animation for transitioning elements in the visualization. + _animate: -> + return unless @inTransition() + @_stopTransition() if ++@animation.frame == @animation.duration + @draw(@animation.frame * @animation.delta()) + @_updateTimeAxes() + + # @param [Array] givenDomain A given domain for the scale + # @return [Function] The y scale for the plot + y: (givenDomain) -> + d3.scale.linear() + .domain(@_getScaleDomain(givenDomain)) + .range([@innerHeight(), 0]) + + # @param [Array] givenDomain Optional domain to override default + # @return [Function] The y scale for the svg portions of the plot + ySvg: (givenDomain) -> + d3.scale.linear() + .domain(@_getScaleDomain(givenDomain)) + .range([@innerHeight() / @pixelRatio, 0]) + + # @return [Function] The y scale for the svg portion of the plot for the left axis + ySvgLeft: -> + if @options.range? + @ySvg @options.range.left + else + @ySvg() + + # @return [Function] The y scale for the svg portion of the plot for the right axis + ySvgRight: -> + if @options.range? + @ySvg @options.range.right + else + @ySvg() + + # @return [Number] The width of a single section of the graph pertaining to a data point + w: -> + @innerWidth() / @options.windowSize + + # This is called every time we introduce new data (as a result of _shift) + # it checks to see if we also need to update the working tick set and + # makes the approriate changes for handling tick animation (enter, exit, + # and update in the d3 model). + # + # @param [Integer] newTime Current newest timestamp in the data + _updateTicks: (newTime) -> + return unless @hasAxis('top') or @hasAxis('bottom') + + # Incoming ticks + unless (++@_tickTimer) % @options.ticks.time + @_pushTick(@options.windowSize, newTime, true) + + # Outgoing ticks + return unless @_ticks.length > 0 + unless @_ticks[0].x - (@w()/@pixelRatio) >= 0 + @_ticks[0].exit = true + + # Makes and pushes a new tick into the visualization. + # + # @param bucket Index in the data window where the tick should initially be position + # @param time The unix timestamp associated with the tick + # @param enter Whether or not the tick should be considered as "newly entering" + # Used primarily for performing the tick opacity tween. + _pushTick: (bucket, time, enter=false, reverse=false) -> + return unless @hasAxis('top') or @hasAxis('bottom') + tick = + time: time + x: bucket*(@w()/@pixelRatio) + @_offsetX() + opacity: if enter then 0 else 1 + enter: if enter then true else false + exit: false + + if @hasAxis('bottom') + g = @bottomAxis.append('g') + .attr('class', 'tick major') + .attr('transform', "translate(#{tick.x+1},0)") + .style('opacity', tick.opacity) + + g.append('line') + .attr('y2', 6) + + g.append('text') + .attr('text-anchor', 'middle') + .attr('dy', 19) + .text(@options.tickFormats.bottom(tick.time)) + + tick.bottomEl = g + + if @hasAxis('top') + g = @topAxis.append('g') + .attr('class', 'tick major') + .attr('transform', "translate(#{tick.x+1},0)") + .style('opacity', tick.opacity) + + g.append('line') + .attr('y2', -6) + + g.append('text') + .attr('text-anchor', 'middle') + .attr('dy', -10) + .text(@options.tickFormats.top(tick.time)) + + tick.topEl = g + + if reverse + @_ticks.unshift tick + else + @_ticks.push tick + return tick + + # Shifts a tick that is no longer needed out of the visualization. + _shiftTick: -> + return unless @_ticks.length > 0 + tick = @_ticks.shift() + tick.topEl.remove() if tick.topEl? + tick.bottomEl.remove() if tick.bottomEl? + + # This performs animations for the time axes (top and bottom). + _updateTimeAxes: -> + return unless @hasAxis('top') or @hasAxis('bottom') + [dx, dop] = [@animation.tickDelta(), 1 / @options.fps] + + for tick in @_ticks + tick.x += dx + if @hasAxis('bottom') + tick.bottomEl.attr('transform', "translate(#{tick.x+1},0)") + if @hasAxis('top') + tick.topEl.attr('transform', "translate(#{tick.x+1},0)") + + if tick.enter + tick.opacity += dop + else if tick.exit + tick.opacity -= dop + + if tick.enter or tick.exit + tick.bottomEl.style('opacity', tick.opacity) if @hasAxis('bottom') + tick.topEl.style('opacity', tick.opacity) if @hasAxis('top') + + # Draws the visualization in the plot's canvas. + # @param delta The current x offset to apply to all elements when rendering. This number + # will be 0 when the plot is not animating and negative when it is. + # @abstract It does nothing on its own but is provided so that subclasses can + # define a custom rendering routine. + draw: (delta=0) -> super() + + dimensionsChanged: -> + super() + @svg.attr('width', @width).attr('height', @height) + @_sizeCanvas() + @_buildAxes() + @draw(@animation.frame * @animation.delta()) + + # Updates axes in response to an <code>option:axes</code> event. + axesChanged: -> + for pos in ['top', 'right', 'bottom', 'left'] + continue if @options.margins? and @options.margins[pos]? + if @hasAxis(pos) + @margins[pos] = defaultAxisMargins[pos] + else + @margins[pos] = 6 + @_sizeCanvas() + @_buildAxes() + @draw(@animation.frame * @animation.delta()) + + # Updates ticks in response to an <code>option.ticks.*</code> event. + ticksChanged: -> + @_resetInitialTimeTicks() + @_transitionRangeAxes() + @draw(@animation.frame * @animation.delta()) + + # Updates tick formats in response to an <code>option.tickFormats.*</code> event. + tickFormatsChanged: -> + @_resetInitialTimeTicks() + @_transitionRangeAxes() + @draw(@animation.frame * @animation.delta()) + + # Updates margins in response to an <code>option.margins.*</code> event. + marginsChanged: -> + return unless @options.margins? + for own pos, size of @options.margins + unless size? + @margins[pos] = 6 + else + @margins[pos] = size + + @_sizeCanvas() + @draw(@animation.frame * @animation.delta()) + + layerChanged: -> + @_transitionRangeAxes() + super() + + +# Base class for all "stacked" plot types (e.g. bar charts, area charts, etc.) +# @abstract It does not perform rendering but instead formats the data +# so as to ease the process of rendering stacked plots. +class Epoch.Time.Stack extends Epoch.Time.Plot + # Sets stacking information (y0) for each of the points in each layer + _stackLayers: -> + return unless (layers = @getVisibleLayers()).length > 0 + for i in [0...layers[0].values.length] + y0 = 0 + for layer in layers + layer.values[i].y0 = y0 + y0 += layer.values[i].y + + # Adds stacking information for layers entering the visualization. + # @param [Array] layers Layers to stack. + _prepareLayers: (layers) -> + y0 = 0 + for own i, d of layers + continue unless @data[i].visible + d.y0 = y0 + y0 += d.y + return layers + + # Ensures that elements are stacked when setting the initial data. + # @param [Array] data Layered data to set for the visualization. + setData: (data) -> + super(data) + @_stackLayers() + + # Finds the correct extent to use for range axes (left and right). + # @return [Array] An extent array with the first element equal to 0 + # and the second element equal to the maximum value amongst the + # stacked entries. + extent: -> + [max, layers] = [0, @getVisibleLayers()] + return [0, 0] unless layers.length + + for i in [0...layers[0].values.length] + sum = 0 + for j in [0...layers.length] + sum += layers[j].values[i].y + max = sum if sum > max + + [0, max] + + layerChanged: -> + @_stackLayers() + @_prepareLayers(layers) for layers in @_queue + super() diff --git a/debian/missing-sources/epoch/src/time/area.coffee b/debian/missing-sources/epoch/src/time/area.coffee new file mode 100644 index 0000000..22bf9db --- /dev/null +++ b/debian/missing-sources/epoch/src/time/area.coffee @@ -0,0 +1,80 @@ + +# Real-time stacked area chart implementation. +class Epoch.Time.Area extends Epoch.Time.Stack + constructor: (@options={}) -> + @options.type ?= 'time.area' + super(@options) + @draw() + + # Sets the appropriate styles to the graphics context given a particular layer. + # @param [Object] layer Layer for which to set the styles. + setStyles: (layer) -> + if layer? && layer.className? + styles = @getStyles "g.#{layer.className.replace(/\s/g,'.')} path.area" + else + styles = @getStyles "g path.area" + @ctx.fillStyle = styles.fill + if styles.stroke? + @ctx.strokeStyle = styles.stroke + if styles['stroke-width']? + @ctx.lineWidth = styles['stroke-width'].replace('px', '') + + # Draws areas for the chart + _drawAreas: (delta=0) -> + [y, w, layers] = [@y(), @w(), @getVisibleLayers()] + + for i in [layers.length-1..0] + continue unless (layer = layers[i]) + + @setStyles layer + @ctx.beginPath() + + [j, k, trans] = [@options.windowSize, layer.values.length, @inTransition()] + firstX = null + while (--j >= -2) and (--k >= 0) + entry = layer.values[k] + args = [(j+1)*w+delta, y(entry.y + entry.y0)] + args[0] += w if trans + if i == @options.windowSize - 1 + @ctx.moveTo.apply @ctx, args + else + @ctx.lineTo.apply @ctx, args + + if trans + borderX = (j+3)*w+delta + else + borderX = (j+2)*w+delta + + @ctx.lineTo(borderX, @innerHeight()) + @ctx.lineTo(@width*@pixelRatio+w+delta, @innerHeight()) + @ctx.closePath() + @ctx.fill() + + # Draws strokes for the chart + _drawStrokes: (delta=0) -> + [y, w, layers] = [@y(), @w(), @getVisibleLayers()] + + for i in [layers.length-1..0] + continue unless (layer = layers[i]) + @setStyles layer + @ctx.beginPath() + + [i, k, trans] = [@options.windowSize, layer.values.length, @inTransition()] + firstX = null + while (--i >= -2) and (--k >= 0) + entry = layer.values[k] + args = [(i+1)*w+delta, y(entry.y + entry.y0)] + args[0] += w if trans + if i == @options.windowSize - 1 + @ctx.moveTo.apply @ctx, args + else + @ctx.lineTo.apply @ctx, args + + @ctx.stroke() + + # Draws the area chart. + draw: (delta=0) -> + @clear() + @_drawAreas(delta) + @_drawStrokes(delta) + super() diff --git a/debian/missing-sources/epoch/src/time/bar.coffee b/debian/missing-sources/epoch/src/time/bar.coffee new file mode 100644 index 0000000..7bcb7be --- /dev/null +++ b/debian/missing-sources/epoch/src/time/bar.coffee @@ -0,0 +1,48 @@ + +# Real-time Bar Chart implementation. +class Epoch.Time.Bar extends Epoch.Time.Stack + constructor: (@options={}) -> + @options.type ?= 'time.bar' + super(@options) + @draw() + + # @return [Number] An offset used to align the ticks to the center of the rendered bars. + _offsetX: -> + 0.5 * @w() / @pixelRatio + + # Sets the styles for the graphics context given a layer class name. + # @param [String] className The class name to use when deriving the styles. + setStyles: (className) -> + styles = @getStyles "rect.bar.#{className.replace(/\s/g,'.')}" + @ctx.fillStyle = styles.fill + + if !styles.stroke? or styles.stroke == 'none' + @ctx.strokeStyle = 'transparent' + else + @ctx.strokeStyle = styles.stroke + + if styles['stroke-width']? + @ctx.lineWidth = styles['stroke-width'].replace('px', '') + + # Draws the stacked bar chart. + draw: (delta=0) -> + @clear() + [y, w] = [@y(), @w()] + + for layer in @getVisibleLayers() + continue unless Epoch.isNonEmptyArray(layer.values) + @setStyles(layer.className) + + [i, k, trans] = [@options.windowSize, layer.values.length, @inTransition()] + iBoundry = if trans then -1 else 0 + + while (--i >= iBoundry) and (--k >= 0) + entry = layer.values[k] + [ex, ey, ey0] = [i*w+delta, entry.y, entry.y0] + ex += w if trans + args = [ex+1, y(ey+ey0), w-2, @innerHeight()-y(ey)+0.5*@pixelRatio] + + @ctx.fillRect.apply(@ctx, args) + @ctx.strokeRect.apply(@ctx, args) + + super() diff --git a/debian/missing-sources/epoch/src/time/gauge.coffee b/debian/missing-sources/epoch/src/time/gauge.coffee new file mode 100644 index 0000000..8efadb2 --- /dev/null +++ b/debian/missing-sources/epoch/src/time/gauge.coffee @@ -0,0 +1,209 @@ + +# Real-time Gauge Visualization. Note: Looks best with a 4:3 aspect ratio (w:h) +class Epoch.Time.Gauge extends Epoch.Chart.Canvas + defaults = + type: 'time.gauge' + domain: [0, 1] + ticks: 10 + tickSize: 5 + tickOffset: 5 + fps: 34 + format: Epoch.Formats.percent + + optionListeners = + 'option:domain': 'domainChanged' + 'option:ticks': 'ticksChanged' + 'option:tickSize': 'tickSizeChanged' + 'option:tickOffset': 'tickOffsetChanged' + 'option:format': 'formatChanged' + + # Creates the new gauge chart. + # @param [Object] options Options for the gauge chart. + # @option options [Array] domain The domain to use when rendering values (default: [0, 1]). + # @option options [Integer] ticks Number of ticks to render (default: 10). + # @option options [Integer] tickSize The length (in pixels) for each tick (default: 5). + # @option options [Integer] tickOffset The number of pixels by which to offset ticks from the outer arc (default: 5). + # @option options [Integer] fps The number of animation frames to render per second (default: 34). + # @option options [Function] format The formatting function to use when rendering the gauge label + # (default: Epoch.Formats.percent). + constructor: (@options={}) -> + super(@options = Epoch.Util.defaults(@options, defaults)) + @value = @options.value or 0 + + if @options.model + @options.model.on 'data:push', => @pushFromModel() + + # SVG Labels Overlay + if @el.style('position') != 'absolute' and @el.style('position') != 'relative' + @el.style('position', 'relative') + + @svg = @el.insert('svg', ':first-child') + .attr('width', @width) + .attr('height', @height) + .attr('class', 'gauge-labels') + + @svg.style + 'position': 'absolute' + 'z-index': '1' + + @svg.append('g') + .attr('transform', "translate(#{@textX()}, #{@textY()})") + .append('text') + .attr('class', 'value') + .text(@options.format(@value)) + + # Animations + @animation = + interval: null + active: false + delta: 0 + target: 0 + + @_animate = => + if Math.abs(@animation.target - @value) < Math.abs(@animation.delta) + @value = @animation.target + clearInterval @animation.interval + @animation.active = false + else + @value += @animation.delta + + @svg.select('text.value').text(@options.format(@value)) + @draw() + + @onAll optionListeners + @draw() + + # Sets the value for the gauge to display and begins animating the guage. + # @param [Number] value Value to set for the gauge. + update: (value) -> + @animation.target = value + @animation.delta = (value - @value) / @options.fps + unless @animation.active + @animation.interval = setInterval @_animate, (1000/@options.fps) + @animation.active = true + + # Alias for the <code>update()</code> method. + # @param [Number] value Value to set for the gauge. + push: (value) -> + @update value + + # Responds to a model's 'data:push' event. + pushFromModel: -> + next = @options.model.getNext(@options.type, @options.dataFormat) + @update next + + # @return [Number] The radius for the gauge. + radius: -> @getHeight() / 1.58 + + # @return [Number] The center position x-coordinate for the gauge. + centerX: -> @getWidth() / 2 + + # @return [Number] The center position y-coordinate for the gauge. + centerY: -> 0.68 * @getHeight() + + # @return [Number] The x-coordinate for the gauge text display. + textX: -> @width / 2 + + # @return [Number] The y-coordinate for the gauge text display. + textY: -> 0.48 * @height + + # @return [Number] The angle to set for the needle given a value within the domain. + # @param [Number] value Value to translate into a needle angle. + getAngle: (value) -> + [a, b] = @options.domain + ((value - a) / (b - a)) * (Math.PI + 2*Math.PI/8) - Math.PI/2 - Math.PI/8 + + # Sets context styles given a particular selector. + # @param [String] selector The selector to use when setting the styles. + setStyles: (selector) -> + styles = @getStyles selector + @ctx.fillStyle = styles.fill + @ctx.strokeStyle = styles.stroke + @ctx.lineWidth = styles['stroke-width'].replace('px', '') if styles['stroke-width']? + + # Draws the gauge. + draw: -> + [cx, cy, r] = [@centerX(), @centerY(), @radius()] + [tickOffset, tickSize] = [@options.tickOffset, @options.tickSize] + + @clear() + + # Draw Ticks + t = d3.scale.linear() + .domain([0, @options.ticks]) + .range([ -(9/8)*Math.PI, Math.PI/8 ]) + + @setStyles '.epoch .gauge .tick' + @ctx.beginPath() + for i in [0..@options.ticks] + a = t(i) + [c, s] = [Math.cos(a), Math.sin(a)] + + x1 = c * (r-tickOffset) + cx + y1 = s * (r-tickOffset) + cy + x2 = c * (r-tickOffset-tickSize) + cx + y2 = s * (r-tickOffset-tickSize) + cy + + @ctx.moveTo x1, y1 + @ctx.lineTo x2, y2 + + @ctx.stroke() + + # Outer arc + @setStyles '.epoch .gauge .arc.outer' + @ctx.beginPath() + @ctx.arc cx, cy, r, -(9/8)*Math.PI, (1/8)*Math.PI, false + @ctx.stroke() + + # Inner arc + @setStyles '.epoch .gauge .arc.inner' + @ctx.beginPath() + @ctx.arc cx, cy, r-10, -(9/8)*Math.PI, (1/8)*Math.PI, false + @ctx.stroke() + + @drawNeedle() + + super() + + # Draws the needle. + drawNeedle: -> + [cx, cy, r] = [@centerX(), @centerY(), @radius()] + ratio = @value / @options.domain[1] + + @setStyles '.epoch .gauge .needle' + @ctx.beginPath() + @ctx.save() + @ctx.translate cx, cy + @ctx.rotate @getAngle(@value) + + @ctx.moveTo 4 * @pixelRatio, 0 + @ctx.lineTo -4 * @pixelRatio, 0 + @ctx.lineTo -1 * @pixelRatio, 19-r + @ctx.lineTo 1, 19-r + @ctx.fill() + + @setStyles '.epoch .gauge .needle-base' + @ctx.beginPath() + @ctx.arc 0, 0, (@getWidth() / 25), 0, 2*Math.PI + @ctx.fill() + + @ctx.restore() + + # Correctly responds to an <code>option:</code> + domainChanged: -> @draw() + + # Correctly responds to an <code>option:</code> + ticksChanged: -> @draw() + + # Correctly responds to an <code>option:</code> + tickSizeChanged: -> @draw() + + # Correctly responds to an <code>option:</code> + tickOffsetChanged: -> @draw() + + # Correctly responds to an <code>option:</code> + formatChanged: -> @svg.select('text.value').text(@options.format(@value)) + + + +# "The mother of a million sons... CIVILIZATION!" -- Justice diff --git a/debian/missing-sources/epoch/src/time/heatmap.coffee b/debian/missing-sources/epoch/src/time/heatmap.coffee new file mode 100644 index 0000000..d7f2c50 --- /dev/null +++ b/debian/missing-sources/epoch/src/time/heatmap.coffee @@ -0,0 +1,261 @@ + +# Real-time Heatmap Implementation. +class Epoch.Time.Heatmap extends Epoch.Time.Plot + defaults = + type: 'time.heatmap' + buckets: 10 + bucketRange: [0, 100] + opacity: 'linear' + bucketPadding: 2 + paintZeroValues: false + cutOutliers: false + + # Easy to use "named" color functions + colorFunctions = + root: (value, max) -> Math.pow(value/max, 0.5) + linear: (value, max) -> value / max + quadratic: (value, max) -> Math.pow(value/max, 2) + cubic: (value, max) -> Math.pow(value/max, 3) + quartic: (value, max) -> Math.pow(value/max, 4) + quintic: (value, max) -> Math.pow(value/max, 5) + + optionListeners = + 'option:buckets': 'bucketsChanged' + 'option:bucketRange': 'bucketRangeChanged' + 'option:opacity': 'opacityChanged' + 'option:bucketPadding': 'bucketPaddingChanged' + 'option:paintZeroValues': 'paintZeroValuesChanged' + 'option:cutOutliers': 'cutOutliersChanged' + + # Creates a new heatmap. + # @param [Object] options Options for the heatmap. + # @option options [Integer] buckets Number of vertical buckets to use when normalizing the + # incoming histogram data for visualization in the heatmap (default: 10). + # @option options [Array] bucketRange A range of acceptable values to be bucketed (default: [0, 100]). + # @option options [String, Function] opacity The opacity coloring function to use when rendering buckets + # in a column. The built-in functions (referenced by string) are: 'root', 'linear', 'quadratic', 'cubic', + # 'quartic', and 'quintic'. A custom function can be supplied given it accepts two parameters (value, max) + # and returns a numeric value from 0 to 1. Default: linear. + # @option options [Number] bucketPadding Amount of padding to apply around buckets (default: 2). + constructor: (@options={}) -> + super(@options = Epoch.Util.defaults(@options, defaults)) + @_setOpacityFunction() + @_setupPaintCanvas() + @onAll optionListeners + @draw() + + _setOpacityFunction: -> + if Epoch.isString(@options.opacity) + @_opacityFn = colorFunctions[@options.opacity] + Epoch.exception "Unknown coloring function provided '#{@options.opacity}'" unless @_opacityFn? + else if Epoch.isFunction(@options.opacity) + @_opacityFn = @options.opacity + else + Epoch.exception "Unknown type for provided coloring function." + + # Prepares initially set data for rendering. + # @param [Array] data Layered histogram data for the visualization. + setData: (data) -> + super(data) + for layer in @data + layer.values = layer.values.map((entry) => @_prepareEntry(entry)) + + # Distributes the full histogram in the entry into the defined buckets + # for the visualization. + # @param [Object] entry Entry to prepare for visualization. + _getBuckets: (entry) -> + prepared = + time: entry.time + max: 0 + buckets: (0 for i in [0...@options.buckets]) + + # Bucket size = (Range[1] - Range[0]) / number of buckets + bucketSize = (@options.bucketRange[1] - @options.bucketRange[0]) / @options.buckets + + for own value, count of entry.histogram + index = parseInt((value - @options.bucketRange[0]) / bucketSize) + + # Remove outliers from the preprared buckets if instructed to do so + if @options.cutOutliers and ((index < 0) or (index >= @options.buckets)) + continue + + # Bound the histogram to the range (aka, handle out of bounds values) + if index < 0 + index = 0 + else if index >= @options.buckets + index = @options.buckets - 1 + + prepared.buckets[index] += parseInt count + + for i in [0...prepared.buckets.length] + prepared.max = Math.max(prepared.max, prepared.buckets[i]) + + return prepared + + # @return [Function] The y scale for the heatmap. + y: -> + d3.scale.linear() + .domain(@options.bucketRange) + .range([@innerHeight(), 0]) + + # @return [Function] The y scale for the svg portions of the heatmap. + ySvg: -> + d3.scale.linear() + .domain(@options.bucketRange) + .range([@innerHeight() / @pixelRatio, 0]) + + # @return [Number] The height to render each bucket in a column (disregards padding). + h: -> + @innerHeight() / @options.buckets + + # @return [Number] The offset needed to center ticks at the middle of each column. + _offsetX: -> + 0.5 * @w() / @pixelRatio + + # Creates the painting canvas which is used to perform all the actual drawing. The contents + # of the canvas are then copied into the actual display canvas and through some image copy + # trickery at the end of a transition the illusion of motion over time is preserved. + # + # Using two canvases in this way allows us to render an incredible number of buckets in the + # visualization and animate them at high frame rates without smashing the cpu. + _setupPaintCanvas: -> + # Size the paint canvas to have a couple extra columns so we can perform smooth transitions + @paintWidth = (@options.windowSize + 1) * @w() + @paintHeight = @height * @pixelRatio + + # Create the "memory only" canvas and nab the drawing context + @paint = document.createElement('CANVAS') + @paint.width = @paintWidth + @paint.height = @paintHeight + @p = Epoch.Util.getContext @paint + + # Paint the initial data (rendering backwards from just before the fixed paint position) + @redraw() + + # Hook into the events to paint the next row after it's been shifted into the data + @on 'after:shift', '_paintEntry' + + # At the end of a transition we must reset the paint canvas by shifting the viewable + # buckets to the left (this allows for a fixed cut point and single renders below in @draw) + @on 'transition:end', '_shiftPaintCanvas' + @on 'transition:end', => @draw(@animation.frame * @animation.delta()) + + # Redraws the entire heatmap for the current data. + redraw: -> + return unless Epoch.isNonEmptyArray(@data) and Epoch.isNonEmptyArray(@data[0].values) + entryIndex = @data[0].values.length + drawColumn = @options.windowSize + + # This addresses a strange off-by-one issue when the chart is transitioning + drawColumn++ if @inTransition() + + while (--entryIndex >= 0) and (--drawColumn >= 0) + @_paintEntry(entryIndex, drawColumn) + @draw(@animation.frame * @animation.delta()) + + # Computes the correct color for a given bucket. + # @param [Integer] value Normalized value at the bucket. + # @param [Integer] max Normalized maximum for the column. + # @param [String] color Computed base color for the bucket. + _computeColor: (value, max, color) -> + Epoch.Util.toRGBA(color, @_opacityFn(value, max)) + + # Paints a single entry column on the paint canvas at the given column. + # @param [Integer] entryIndex Index of the entry to paint. + # @param [Integer] drawColumn Column on the paint canvas to place the visualized entry. + _paintEntry: (entryIndex=null, drawColumn=null) -> + [w, h] = [@w(), @h()] + + entryIndex ?= @data[0].values.length - 1 + drawColumn ?= @options.windowSize + + entries = [] + bucketTotals = (0 for i in [0...@options.buckets]) + maxTotal = 0 + + for layer in @getVisibleLayers() + entry = @_getBuckets( layer.values[entryIndex] ) + for own bucket, count of entry.buckets + bucketTotals[bucket] += count + maxTotal += entry.max + styles = @getStyles ".#{layer.className.split(' ').join('.')} rect.bucket" + entry.color = styles.fill + entries.push entry + + xPos = drawColumn * w + + @p.clearRect xPos, 0, w, @paintHeight + + j = @options.buckets + + for own bucket, sum of bucketTotals + color = @_avgLab(entries, bucket) + max = 0 + for entry in entries + max += (entry.buckets[bucket] / sum) * maxTotal + if sum > 0 or @options.paintZeroValues + @p.fillStyle = @_computeColor(sum, max, color) + @p.fillRect xPos, (j-1) * h, w-@options.bucketPadding, h-@options.bucketPadding + j-- + + # This shifts the image contents of the paint canvas to the left by 1 column width. + # It is called after a transition has ended (yay, slight of hand). + _shiftPaintCanvas: -> + data = @p.getImageData @w(), 0, @paintWidth-@w(), @paintHeight + @p.putImageData data, 0, 0 + + # Performs an averaging of the colors for muli-layer heatmaps using the lab color space. + # @param [Array] entries The layers for which the colors are to be averaged. + # @param [Number] bucket The bucket in the entries that must be averaged. + # @return [String] The css color code for the average of all the layer colors. + _avgLab: (entries, bucket) -> + [l, a, b, total] = [0, 0, 0, 0] + for entry in entries + continue unless entry.buckets[bucket]? + total += entry.buckets[bucket] + + for own i, entry of entries + if entry.buckets[bucket]? + value = entry.buckets[bucket]|0 + else + value = 0 + ratio = value / total + color = d3.lab(entry.color) + l += ratio * color.l + a += ratio * color.a + b += ratio * color.b + + d3.lab(l, a, b).toString() + + # Copies the paint canvas onto the display canvas, thus rendering the heatmap. + draw: (delta=0) -> + @clear() + @ctx.drawImage @paint, delta, 0 + super() + + # Changes the number of buckets in response to an <code>option:buckets</code> event. + bucketsChanged: -> @redraw() + + # Changes the range of the buckets in response to an <code>option:bucketRange</code> event. + bucketRangeChanged: -> + @_transitionRangeAxes() + @redraw() + + # Changes the opacity function in response to an <code>option:opacity</code> event. + opacityChanged: -> + @_setOpacityFunction() + @redraw() + + # Changes the bucket padding in response to an <code>option:bucketPadding</code> event. + bucketPaddingChanged: -> @redraw() + + # Changes whether or not to paint zeros in response to an <code>option:paintZeroValues</code> event. + paintZeroValuesChanged: -> @redraw() + + # Changes whether or not to cut outliers when bucketing in response to an + # <code>option:cutOutliers</code> event. + cutOutliersChanged: -> @redraw() + + layerChanged: -> @redraw() + +# "Audio... Audio... Audio... Video Disco..." - Justice diff --git a/debian/missing-sources/epoch/src/time/line.coffee b/debian/missing-sources/epoch/src/time/line.coffee new file mode 100644 index 0000000..8a4271b --- /dev/null +++ b/debian/missing-sources/epoch/src/time/line.coffee @@ -0,0 +1,39 @@ + +# Real-time line chart implementation +class Epoch.Time.Line extends Epoch.Time.Plot + constructor: (@options={}) -> + @options.type ?= 'time.line' + super(@options) + @draw() + + # Sets the graphics context styles based ont he given layer class name. + # @param [String] className The class name of the layer for which to set the styles. + setStyles: (className) -> + styles = @getStyles "g.#{className.replace(/\s/g,'.')} path.line" + @ctx.fillStyle = styles.fill + @ctx.strokeStyle = styles.stroke + @ctx.lineWidth = @pixelRatio * styles['stroke-width'].replace('px', '') + + # Draws the line chart. + draw: (delta=0) -> + @clear() + w = @w() + for layer in @getVisibleLayers() + continue unless Epoch.isNonEmptyArray(layer.values) + @setStyles(layer.className) + @ctx.beginPath() + y = @y(layer.range) + [i, k, trans] = [@options.windowSize, layer.values.length, @inTransition()] + + while (--i >= -2) and (--k >= 0) + entry = layer.values[k] + args = [(i+1)*w+delta, y(entry.y)] + args[0] += w if trans + if i == @options.windowSize - 1 + @ctx.moveTo.apply @ctx, args + else + @ctx.lineTo.apply @ctx, args + + @ctx.stroke() + + super() diff --git a/debian/missing-sources/epoch/tests/render/basic/area.html b/debian/missing-sources/epoch/tests/render/basic/area.html new file mode 100644 index 0000000..ea23851 --- /dev/null +++ b/debian/missing-sources/epoch/tests/render/basic/area.html @@ -0,0 +1,450 @@ +<!DOCTYPE html> +<html> + <head> + <link rel="stylesheet" type="text/css" href="../css/tests.css"> + <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script> + <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> + <script src="../../../dist/js/epoch.js"></script> + <script src="../js/data.js"></script> + <link rel="stylesheet" type="text/css" href="../../../dist/css/epoch.css"> + </head> + <body> + <h1>Basic Area Chart Test</h1> + <p class="breadcrumbs"><a href="../index.html">Epoch Chart Tests</a> » Basic Area</p> + + <ol> + <li><a href="#test-1">Single Series</a></li> + <li><a href="#test-2">Single Series II</a></li> + <li><a href="#test-3">Multi Series</a></li> + <li><a href="#test-4">Multi Series II</a></li> + <li><a href="#test-5">Single Series Transition</a></li> + <li><a href="#test-6">Multi Series Transition</a></li> + <li><a href="#test-7">Single Series to Multi Series Transition</a></li> + <li><a href="#test-8">Layer Color Override</a></li> + <li><a href="#test-9">Categorical Color Switching</a></li> + <li><a href="#test-10">Multi Series without Labels</a></li> + <li><a href="#test-11">Hide/Show Layers</a></li> + <li><a href="#test-12">Data Format</a></li> + </ol> + + + <!-- Test 1 --> + <div id="test-1" class="test"> + <h2>1. Single Series</h2> + <p>It should display a plot of <code>y = cos(x) + 1</code> over the range <code>[0, 2π)</code>.</p> + <div class="epoch"></div> + </div> + + <script> + $(function() { + var data = [{ label: 'A', values: [] }], + length = 64; + + for (var i = 0; i < length; i++) { + var x = i * 2 * Math.PI / length, + y = Math.cos(x) + 1; + data[0].values.push({x: x, y: y}); + } + + $('#test-1 .epoch').epoch({ type: 'area', data: data }); + }); + </script> + + + <!-- Test 2 --> + <div id="test-2" class="test"> + <h2>2. Single Series II</h2> + <p>It should display a plot of <code>y = sin(x) + 1</code> over the range <code>[0, 2π)</code>.</p> + <div class="epoch"></div> + </div> + + <script> + $(function() { + var data = [{ label: 'A', values: [] }], + length = 64; + + for (var i = 0; i < length; i++) { + var x = i * 2 * Math.PI / length, + y = Math.sin(x) + 1; + data[0].values.push({x: x, y: y}); + } + + $('#test-2 .epoch').epoch({ type: 'area', data: data }); + }); + </script> + + + <!-- Test 3 --> + <div id="test-3" class="test"> + <h2>3. Multi-series Plot</h2> + <p> + It should display a plot of the following functions stacked atop one another: + <ul> + <li><code>y = x</code></li> + <li><code>y = 2x</code></li> + <li><code>y = 3x</code></li> + </ul> + over the range <code>[0, 10)</code>. + </p> + <div class="epoch"></div> + </div> + + <script> + $(function(){ + var data = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []} + ], + length = 32; + + for (var i = 0; i < length; i++) { + var x = i * 10 / length; + data[0].values.push({x: x, y: x}); + data[1].values.push({x: x, y: 2*x}); + data[2].values.push({x: x, y: 3*x}); + } + + $('#test-3 .epoch').epoch({ type: 'area', data: data }); + }); + </script> + + + <!-- Test 4 --> + <div id="test-4" class="test"> + <h2>4. Multi-series Plot II</h2> + <p> + It should display a plot of the following functions stacked atop one another: + <ul> + <li><code>y = |x|</code></li> + <li><code>y = x<sup>2</sup></code></li> + <li><code>y = |x<sup>3</sup>|</code></li> + </ul> + over the range [-1, 1). + </p> + <div class="epoch"></div> + </div> + + <script> + $(function() { + var data = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []} + ], + length = 64; + + for (var i = 0; i < length; i++) { + var x = (i - length / 2) / (length / 2); + data[0].values.push({x: x, y: Math.abs(x)}); + data[1].values.push({x: x, y: Math.pow(x, 2)}); + data[2].values.push({x: x, y: Math.abs(Math.pow(x, 3))}); + } + + $('#test-4 .epoch').epoch({ type: 'area', data: data }); + }); + </script> + + + <!-- Test 5 --> + <div id="test-5" class="test"> + <h2>5. Single Series Transition</h2> + <p> + It should correctly transition between the plots <code>y = |x|</code> over the range [-10, 10) and <code>y = x<sup>2</sup></code> over the range [-20, 20). The transition should be initiated when pressing the buttons below the plot. + </p> + <div class="epoch"></div> + <p> + <button data-index="0">y = x</button> + <button data-index="1">y = x^2</button> + </p> + </div> + + <script> + $(function() { + var data1 = [{label: 'A', values: []}], + data2 = [{label: 'B', values: []}], + data = [data1, data2], + length = 64; + + for (var i = 0; i < length; i++) { + var x1 = (i - length / 2) * 10 / (length / 2), + y1 = Math.abs(x1), + x2 = (i - length / 2) * 20 / (length / 2), + y2 = Math.pow(x2, 2); + data1[0].values.push({x: x1, y: y1}); + data2[0].values.push({x: x2, y: y2}); + } + + var chart = $('#test-5 .epoch').epoch({ + type: 'area', + data: data1 + }); + + $('#test-5 button').on('click', function(e) { + var index = parseInt($(e.target).attr('data-index')); + chart.update(data[index]); + }); + }); + </script> + + <!-- Test 6 --> + <div id="test-6" class="test"> + <h2>6. Multi Series Transition</h2> + <p> + It should correctly render and transition between Set A: + <ul> + <li><code>y = x</code></li> + <li><code>y = 2*x</code></li> + <li><code>y = 3*x</code></li> + </ul> + over the range [1, 100). and Set B: + <ul> + <li><code>y = ln(x)</code></li> + <li><code>y = 2*ln(x)</code></li> + <li><code>y = 3*ln(x)</code></li> + </ul> + over the range [1, 100). The transition should be initiated when pressing the buttons below the plot. + <div class="epoch"></div> + <p> + <button data-index="0">Set A</button> + <button data-index="1">Set B</button> + </p> + </div> + + <script> + $(function() { + var data1 = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []} + ]; + + var data2 = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []} + ]; + + var data = [data1, data2], + length = 128; + + for (var i = 0; i < length; i++) { + var x = (i * 99 / length) + 1; + + data1[0].values.push({x: x, y: x}); + data1[1].values.push({x: x, y: 2*x}); + data1[2].values.push({x: x, y: 3*x}); + + data2[0].values.push({x: x, y: Math.log(x)}); + data2[1].values.push({x: x, y: 2*Math.log(x)}); + data2[2].values.push({x: x, y: 3*Math.log(x)}); + } + + var chart = $('#test-6 .epoch').epoch({ type: 'area', data: data1 }); + + $('#test-6 button').on('click', function(e) { + var index = parseInt($(e.target).attr('data-index')); + chart.update(data[index]); + }); + }); + </script> + + + <!-- Test 7 --> + <div id="test-7" class="test"> + <h2>7. Single Series to Multi Series Transition</h2> + <p> + It should correctly transition between a single series, plotting the functions: + <ul> + <li><code>y = x<sup>2</sup> - 0.5*x</code></li> + </ul> + To a multi series set, plotting the functions: + <ul> + <li><code>y = ln(x)</code></li> + <li><code>y = x</code></li> + <li><code>y = x * ln(x)</code></li> + </ul> + over the range [1, 4) for all plots. The transition should be initiated when pressing the buttons below the plot. + </p> + <div class="epoch"></div> + <p> + <button data-index="0">Single</button> + <button data-index="1">Multi</button> + </p> + </div> + + <script> + $(function() { + var data1 = [{label: 'A', values: []}], + data2 = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []} + ], + data = [data1, data2], + length = 64; + + for (var i = 0; i < length; i++) { + var x = (3*i/length) + 1; + data1[0].values.push({x: x, y: x*x - 0.5*x}); + data2[0].values.push({x: x, y: Math.log(x)}); + data2[1].values.push({x: x, y: x}); + data2[2].values.push({x: x, y: x * Math.log(x)}); + } + + var chart = $('#test-7 .epoch').epoch({ type: 'area', data: data1 }); + + $('#test-7 button').on('click', function(e) { + var index = parseInt($(e.target).attr('data-index')); + chart.update(data[index]); + }); + }); + </script> + + <!-- Test 8 --> + <div id="test-8" class="test"> + <h2>8. Layer Color Override</h2> + <p> + It should display the first layer of the plot as pink, the second layer as green, and the third layer as blue. + </p> + <div class="epoch"></div> + </div> + + <style> + #test-8 .epoch .a .area { fill: pink; } + #test-8 .epoch .b .area { fill: green; } + #test-8 .epoch .c .area { fill: blue; } + </style> + + <script> + $(function() { + var data = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []} + ], + length = 128; + + for (var i = 0; i < length; i++) { + var x = i * 10 / length + 1, + y = x * Math.log(x); + for (var j = 0; j < data.length; j++) { + data[j].values.push({x: x, y: y}); + } + } + + $('#test-8 .epoch').epoch({ type: 'area', data: data }); + }); + </script> + + <!-- Test 9 --> + <div id="test-9" class="test"> + <h2>9. Categorical Color Switching</h3> + <p> + It should change layer colors automatically when switching between the following categorical color classes on the containing element: + <ul> + <li><code>category10</code></li> + <li><code>category20</code></li> + <li><code>category20b</code></li> + <li><code>category20c</code></li> + </ul> + The colors should change when pressing the buttons for each categorical type below the chart. + </p> + + <div class="epoch category10"></div> + + <p> + <button data-class="category10">category10</button> + <button data-class="category20">category20</button> + <button data-class="category20b">category20b</button> + <button data-class="category20c">category20c</button> + </p> + </div> + <script> + $(function() { + var data = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []}, + {label: 'D', values: []}, + {label: 'E', values: []}, + {label: 'F', values: []}, + {label: 'G', values: []} + ], + length = 128, + className = 'category10'; + + for (var i = 0; i < length; i++) { + var x = i * 1 / length; + for (var j = 0; j < data.length; j++) { + data[j].values.push({x: x, y: Math.pow(x, j+1)}); + } + } + + $('#test-9 .epoch').epoch({ type: 'area', data: data }); + + $('#test-9 button').on('click', function(e) { + $('#test-9 .epoch').removeClass(className); + className = $(e.target).attr('data-class'); + $('#test-9 .epoch').addClass(className); + }); + }); + </script> + + <!-- Test 10 --> + <div id="test-10" class="test"> + <h2>10. Multi Series without Labels</h2> + <p> + Correctly render a multi-series plot of: + <ul> + <li><code>y = sin(x) + 1</code></li> + <li><code>y = cos(x) + 1</code></li> + </ul> + where the layers are given without labels. + </p> + <div class="epoch"></div> + </div> + <script> + $(function() { + var data = [{values: []}, {values: []}], + length = 128; + + for (var i = 0; i <= length; i++) { + var x = i * 4 * Math.PI / length; + data[0].values.push({x: x, y: Math.sin(x) + 1}); + data[1].values.push({x: x, y: Math.cos(x) + 1}); + } + + $('#test-10 .epoch').epoch({ type: 'area', data: data }); + }); + </script> + + <!-- Test 11 --> + <div id="test-11" class="test"> + <h2>11. Hide/Show Layers</h2> + <p>Correctly hide and show multiple layers</p> + <div class="epoch"></div> + <p> + <button data-index="0">Toggle A</button> + <button data-index="1">Toggle B</button> + <button data-index="2">Toggle C</button> + </p> + </div> + <script> + $(function() { + var chart = $('#test-11 .epoch').epoch({ + type: 'area', + data: data().add(function(x) { return x; }) + .add(function(x) { return 1.5*x; }) + .add(function(x) { return 2.0*x; }).get([0, 10], 1) + }); + + $('#test-11 button').click(function(e) { + var index = parseInt($(e.target).attr('data-index')); + chart.toggleLayer(index); + }); + }); + </script> + + </body> +</html> diff --git a/debian/missing-sources/epoch/tests/render/basic/bar.html b/debian/missing-sources/epoch/tests/render/basic/bar.html new file mode 100644 index 0000000..07b5122 --- /dev/null +++ b/debian/missing-sources/epoch/tests/render/basic/bar.html @@ -0,0 +1,745 @@ +<!DOCTYPE html> +<html> + <head> + <link rel="stylesheet" type="text/css" href="../css/tests.css"> + <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script> + <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> + <script src="../../../dist/js/epoch.js"></script> + <script src="../js/data.js"></script> + <link rel="stylesheet" type="text/css" href="../../../dist/css/epoch.css"> + </head> + <body> + <h1>Basic Bar Chart Test</h1> + <p class="breadcrumbs"><a href="../index.html">Epoch Chart Tests</a> » Basic Bar</p> + + <ol> + <li><a href="#test-1">Single Series</a></li> + <li><a href="#test-2">Single Series II</a></li> + <li><a href="#test-3">Multi Series</a></li> + <li><a href="#test-4">Multi Series II</a></li> + <li><a href="#test-5">Single Series Transition</a></li> + <li><a href="#test-6">Multi Series Transition</a></li> + <li><a href="#test-7">Single Series to Multi Series Transition</a></li> + <li><a href="#test-8">Layer Color Override</a></li> + <li><a href="#test-9">Categorical Color Switching</a></li> + <li><a href="#test-10">Multi Series without Labels</a></li> + <li><a href="#test-11">Horizontally Oriented Single Series</a></li> + <li><a href="#test-12">Horizontally Oriented Multi Series</a></li> + <li><a href="#test-13">Horizontally Oriented Multi Series Transition</a></li> + <li><a href="#test-14">Vertical to Horizontal Transition</a></li> + <li><a href="#test-15">Padding Changes</a></li> + <li><a href="#test-16">Hide/Show Layers</a></li> + <li><a href="#test-17">Data Formatting</a></li> + <li><a href="#test-18">Many bars</a></li> + </ol> + + + <!-- Test 1 --> + <div id="test-1" class="test"> + <h2>1. Single Series</h2> + <p>Display a plot of <code>y = cos(x) + 1</code> over the range <code>[0, 2π)</code>.</p> + <div class="epoch"></div> + </div> + + <script> + $(function() { + var data = [{ label: 'A', values: [] }], + length = 32; + + for (var i = 0; i < length; i++) { + var x = i * 2 * Math.PI / length, + y = Math.cos(x) + 1; + data[0].values.push({x: x, y: y}); + } + + $('#test-1 .epoch').epoch({ + type: 'bar', + data: data, + tickFormats: { + bottom: function(d) { + return parseFloat(d).toFixed(1); + } + } + }); + }); + </script> + + + <!-- Test 2 --> + <div id="test-2" class="test"> + <h2>2. Single Series II</h2> + <p>Display a plot of <code>y = sin(x) + 1</code> over the range <code>[0, 2π)</code>.</p> + <div class="epoch"></div> + </div> + + <script> + $(function() { + var data = [{ label: 'A', values: [] }], + length = 32; + + for (var i = 0; i < length; i++) { + var x = i * 2 * Math.PI / length, + y = Math.sin(x) + 1; + data[0].values.push({x: x, y: y}); + } + + $('#test-2 .epoch').epoch({ + type: 'bar', + data: data, + tickFormats: { + bottom: function(d) { + return parseFloat(d).toFixed(1); + } + } + }); + }); + </script> + + + <!-- Test 3 --> + <div id="test-3" class="test"> + <h2>3. Multi-series Plot</h2> + <p> + Display a plot of the following functions stacked atop one another: + <ul> + <li><code>y = x</code></li> + <li><code>y = 2x</code></li> + <li><code>y = 3x</code></li> + </ul> + over the range <code>[0, 10)</code>. + </p> + <div class="epoch"></div> + </div> + + <script> + $(function(){ + var data = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []} + ], + length = 10; + + for (var i = 0; i < length; i++) { + var x = i * 10 / length; + data[0].values.push({x: x, y: x}); + data[1].values.push({x: x, y: 2*x}); + data[2].values.push({x: x, y: 3*x}); + } + + $('#test-3 .epoch').epoch({ + type: 'bar', + data: data + }); + }); + </script> + + + <!-- Test 4 --> + <div id="test-4" class="test"> + <h2>4. Multi-series Plot II</h2> + <p> + Display a plot of the following functions stacked atop one another: + <ul> + <li><code>y = |x|</code></li> + <li><code>y = x<sup>2</sup></code></li> + <li><code>y = |x<sup>3</sup>|</code></li> + </ul> + over the range [-1, 1). + </p> + <div class="epoch"></div> + </div> + + <script> + $(function() { + var data = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []} + ], + length = 20; + + for (var i = 0; i < length; i++) { + var x = (i - length / 2) / (length / 2); + data[0].values.push({x: x, y: Math.abs(x)}); + data[1].values.push({x: x, y: Math.pow(x, 2)}); + data[2].values.push({x: x, y: Math.abs(Math.pow(x, 3))}); + } + + $('#test-4 .epoch').epoch({ type: 'bar', data: data }); + }); + </script> + + + <!-- Test 5 --> + <div id="test-5" class="test"> + <h2>5. Single Series Transition</h2> + <p> + Correctly transition between the plots <code>y = |x|</code> over the range [-10, 10) and <code>y = x<sup>2</sup></code> over the range [-20, 20). The transition is initiated by pressing the buttons below the plot. + </p> + <div class="epoch"></div> + <p> + <button data-index="0">y = x</button> + <button data-index="1">y = x^2</button> + </p> + </div> + + <script> + $(function() { + var data1 = [{label: 'A', values: []}], + data2 = [{label: 'B', values: []}], + data = [data1, data2], + length = 40; + + for (var i = 0; i < length; i++) { + var x1 = (i - length / 2) * 10 / (length / 2), + y1 = Math.abs(x1), + x2 = (i - length / 2) * 20 / (length / 2), + y2 = Math.pow(x2, 2); + data1[0].values.push({x: x1, y: y1}); + data2[0].values.push({x: x2, y: y2}); + } + + var chart = $('#test-5 .epoch').epoch({ + type: 'bar', + data: data1 + }); + + $('#test-5 button').on('click', function(e) { + var index = parseInt($(e.target).attr('data-index')); + chart.update(data[index]); + }); + }); + </script> + + <!-- Test 6 --> + <div id="test-6" class="test"> + <h2>6. Multi Series Transition</h2> + <p> + Correctly render and transition between Set A: + <ul> + <li><code>y = x</code></li> + <li><code>y = 2*x</code></li> + <li><code>y = 3*x</code></li> + </ul> + over the range [1, 100). and Set B: + <ul> + <li><code>y = ln(x)</code></li> + <li><code>y = 2*ln(x)</code></li> + <li><code>y = 3*ln(x)</code></li> + </ul> + over the range [1, 100). The transition is initiated by pressing the buttons below the plot. + <div class="epoch"></div> + <p> + <button data-index="0">Set A</button> + <button data-index="1">Set B</button> + </p> + </div> + + <script> + $(function() { + var data1 = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []} + ]; + + var data2 = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []} + ]; + + var data = [data1, data2], + length = 10; + + for (var i = 0; i < length; i++) { + var x = (i * 100 / length) + 1; + + data1[0].values.push({x: x, y: x}); + data1[1].values.push({x: x, y: 2*x}); + data1[2].values.push({x: x, y: 3*x}); + + data2[0].values.push({x: x, y: Math.log(x)}); + data2[1].values.push({x: x, y: 2*Math.log(x)}); + data2[2].values.push({x: x, y: 3*Math.log(x)}); + } + + var chart = $('#test-6 .epoch').epoch({ type: 'bar', data: data1 }); + + $('#test-6 button').on('click', function(e) { + var index = parseInt($(e.target).attr('data-index')); + chart.update(data[index]); + }); + }); + </script> + + + <!-- Test 7 --> + <div id="test-7" class="test"> + <h2>7. Single Series to Multi Series Transition</h2> + <p> + Correctly transition between a single series, plotting the functions: + <ul> + <li><code>y = x<sup>2</sup> - 0.5*x</code></li> + </ul> + To a multi series set, plotting the functions: + <ul> + <li><code>y = ln(x)</code></li> + <li><code>y = x</code></li> + <li><code>y = x * ln(x)</code></li> + </ul> + over the range [1, 4) for all plots. The transition is initiated by pressing the buttons below the plot. + </p> + <div class="epoch"></div> + <p> + <button data-index="0">Single</button> + <button data-index="1">Multi</button> + </p> + </div> + + <script> + $(function() { + var data1 = [{label: 'A', values: []}], + data2 = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []} + ], + data = [data1, data2], + length = 32; + + for (var i = 0; i < length; i++) { + var x = (3*i/length) + 1; + data1[0].values.push({x: x, y: x*x - 0.5*x}); + data2[0].values.push({x: x, y: Math.log(x)}); + data2[1].values.push({x: x, y: x}); + data2[2].values.push({x: x, y: x * Math.log(x)}); + } + + var chart = $('#test-7 .epoch').epoch({ + type: 'bar', + data: data1, + tickFormats: { + bottom: function(d) { return d.toFixed(1); } + } + }); + + $('#test-7 button').on('click', function(e) { + var index = parseInt($(e.target).attr('data-index')); + chart.update(data[index]); + }); + }); + </script> + + <!-- Test 8 --> + <div id="test-8" class="test"> + <h2>8. Layer Color Override</h2> + <p> + Display the first layer of the plot as pink, the second layer as green, and the third layer as blue. + </p> + <div class="epoch"></div> + </div> + + <style> + #test-8 .epoch .bar.a { fill: pink; } + #test-8 .epoch .bar.b { fill: green; } + #test-8 .epoch .bar.c { fill: blue; } + </style> + + <script> + $(function() { + var data = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []} + ], + length = 10; + + for (var i = 0; i < length; i++) { + var x = i * 10 / length + 1, + y = x * Math.log(x); + for (var j = 0; j < data.length; j++) { + data[j].values.push({x: x, y: y}); + } + } + + $('#test-8 .epoch').epoch({ type: 'bar', data: data }); + }); + </script> + + <!-- Test 9 --> + <div id="test-9" class="test"> + <h2>9. Categorical Color Switching</h3> + <p> + Change layer colors automatically when switching between the following categorical color classes on the containing element: + <ul> + <li><code>category10</code></li> + <li><code>category20</code></li> + <li><code>category20b</code></li> + <li><code>category20c</code></li> + </ul> + Change the categorical colors by pressing the buttons below the chart. + </p> + + <div class="epoch category10"></div> + + <p> + <button data-class="category10">category10</button> + <button data-class="category20">category20</button> + <button data-class="category20b">category20b</button> + <button data-class="category20c">category20c</button> + </p> + </div> + <script> + $(function() { + var data = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []}, + {label: 'D', values: []}, + {label: 'E', values: []}, + {label: 'F', values: []}, + {label: 'G', values: []} + ], + length = 4, + className = 'category10'; + + for (var i = 0; i < length; i++) { + var x = i * 1 / length; + for (var j = 0; j < data.length; j++) { + data[j].values.push({x: x, y: Math.pow(x, j+1)}); + } + } + + $('#test-9 .epoch').epoch({ + type: 'bar', + data: data, + tickFormats: { + bottom: function(d) { return d.toFixed(1); } + } + }); + + $('#test-9 button').on('click', function(e) { + $('#test-9 .epoch').removeClass(className); + className = $(e.target).attr('data-class'); + $('#test-9 .epoch').addClass(className); + }); + }); + </script> + + <!-- Test 10 --> + <div id="test-10" class="test"> + <h2>10. Multi Series without Labels</h2> + <p> + Correctly render a multi-series plot of: + <ul> + <li><code>y = sin(x) + 1</code></li> + <li><code>y = cos(x) + 1</code></li> + </ul> + where the layers are given without labels. + </p> + <div class="epoch"></div> + </div> + <script> + $(function() { + var data = [{values: []}, {values: []}], + length = 32; + + for (var i = 0; i <= length; i++) { + var x = i * 4 * Math.PI / length; + data[0].values.push({x: x, y: Math.sin(x) + 1}); + data[1].values.push({x: x, y: Math.cos(x) + 1}); + } + + $('#test-10 .epoch').epoch({ + type: 'bar', + data: data, + tickFormats: { + bottom: function(d) { return d.toFixed(1); } + } + }); + }); + </script> + + <!-- Test 11 --> + <div id="test-11" class="test"> + <h2>11. Horizontally Oriented Single Series</h2> + <p> + Correctly render the single series plot of: + <ul> + <li>A - 20</li> + <li>B - 30</li> + <li>C - 60</li> + </ul> + using a horizontal orientation. + </p> + <div class="epoch"></div> + </div> + <script> + $(function() { + var data = [{values: [ + {x: 'A', y: 20}, + {x: 'B', y: 30}, + {x: 'C', y: 60} + ]}]; + + $('#test-11 .epoch').epoch({ + type: 'bar', + orientation: 'horizontal', + data: data + }); + }); + </script> + + <!-- Test 12 --> + <div id="test-12" class="test"> + <h2>12. Horizontally Oriented Multi Series</h2> + <p> + Correctly render the multi series plot of: + <ul> + <li>A - 10, 30</li> + <li>B - 20, 50</li> + <li>C - 60, 10</li> + </ul> + using a horizontal orientation. + </p> + <div class="epoch"></div> + </div> + <script> + $(function() { + var data = [ + { values: [{x: 'A', y: 10}, {x: 'B', y: 20}, {x: 'C', y: 60}] }, + { values: [{x: 'A', y: 30}, {x: 'B', y: 50}, {x: 'C', y: 10}] } + ]; + + $('#test-12 .epoch').epoch({ + type: 'bar', + orientation: 'horizontal', + data: data + }); + }); + </script> + + <!-- Test 13 --> + <div id="test-13" class="test"> + <h2>13. Horizontally Oriented Multi Series Transition</h2> + <p> + Correctly render the Horizontally oriented multi series plot of: + <ul> + <li>A - 10, 10</li> + <li>B - 20, 20</li> + <li>C - 30, 30</li> + </ul> + and transition to the single series plot: + <ul> + <li>A - 5</li> + <li>B - 10</li> + <li>C - 40</li> + </ul> + </p> + <div class="epoch"></div> + <p> + <button data-index="0">Multi</button> + <button data-index="1">Single</button> + </p> + </div> + <script> + $(function() { + var data1 = [ + { values: [{x: 'A', y: 10}, {x: 'B', y: 20}, {x: 'C', y: 30}] }, + { values: [{x: 'A', y: 10}, {x: 'B', y: 20}, {x: 'C', y: 30}] } + ], + data2 = [ + { values: [{x: 'A', y: 5}, {x: 'B', y: 10}, {x: 'C', y: 40}] } + ], + data = [data1, data2]; + + var chart = $('#test-13 .epoch').epoch({ + type: 'bar', + orientation: 'horizontal', + data: data1 + }); + + $('#test-13 button').on('click', function(e) { + var index = parseInt($(e.target).attr('data-index')); + chart.update(data[index]); + }); + }); + </script> + + <div id="test-14" class="test"> + <h2>14. Vertical to Horizontal Transition</h2> + <div class="epoch"></div> + <p> + <button data-orientation="vertical">Vertical</button> + <button data-orientation="horizontal">Horizontal</button> + </p> + </div> + <script> + $(function() { + var data = [ + { values: [{x: 'A', y: 10}, {x: 'B', y: 20}, {x: 'C', y: 30}] }, + { values: [{x: 'A', y: 10}, {x: 'B', y: 20}, {x: 'C', y: 30}] } + ]; + + var chart = $('#test-14 .epoch').epoch({ + type: 'bar', + data: data + }); + + $('#test-14 button').click(function(e) { + var orientation = $(e.target).attr('data-orientation'); + chart.option('orientation', orientation); + }); + }); + </script> + + <div id="test-15" class="test"> + <h2>15. Padding Changes</h2> + <div class="epoch"></div> + <p> + <button data-index="0">Regular Padding</button> + <button data-index="1">Fat Padding</button> + </p> + </div> + <script> + $(function() { + var data = [ + { values: [{x: 'A', y: 10}, {x: 'B', y: 20}, {x: 'C', y: 30}] }, + { values: [{x: 'A', y: 10}, {x: 'B', y: 20}, {x: 'C', y: 30}] } + ]; + + var padding = [ + { + padding: { bar: 0.08, group: 0.1 }, + outerPadding: { bar: 0.08, group: 0.1 } + }, + { + padding: { bar: 0.2, group: 0.3 }, + outerPadding: { bar: 0.1, group: 0.2 } + } + ]; + + var chart = $('#test-15 .epoch').epoch({ type: 'bar', data: data }); + $('#test-15 button').click(function(e) { + var index = parseInt($(e.target).attr('data-index')); + chart.option('padding', padding[index].padding); + chart.option('outerPadding', padding[index].outerPadding); + }); + }); + </script> + + <div id="test-16" class="test"> + <h2>16. Hide/Show Layers</h2> + <div class="epoch"></div> + <p> + <button class="toggle" data-index="0">Toggle A</button> + <button class="toggle" data-index="1">Toggle B</button> + <button class="toggle" data-index="2">Toggle C</button> + | + <button class="orientation">Orientation</button> + </p> + </div> + <script> + $(function() { + var chart = $('#test-16 .epoch').epoch({ + type: 'bar', + data: data().add(function(x) { return x; }) + .add(function(x) { return 1.5*x; }) + .add(function(x) { return 2.0*x; }).get([0, 10], 1) + }); + + $('#test-16 button.toggle').click(function(e) { + var index = parseInt($(e.target).attr('data-index')); + chart.toggleLayer(index); + }); + + $('#test-16 button.orientation').click(function(e) { + if (chart.option('orientation') == 'vertical') + chart.option('orientation', 'horizontal') + else + chart.option('orientation', 'vertical') + }) + }); + </script> + + <div id="test-17" class="test"> + <h2>17. Data Formatting</h2> + <p>Ensure the chart works with the array, tuple, and key-value data formats.<p> + <div class="epoch array"></div> + <div class="epoch tuple"></div> + <div class="epoch keyvalue"></div> + </div> + <script> + $(function() { + $('#test-17 .array').epoch({ + type: 'bar', + data: [1, 2, 4, 8, 16, 32], + dataFormat: 'array' + }); + + $('#test-17 .tuple').epoch({ + type: 'bar', + dataFormat: 'tuple', + data: [ + [[0, 1], [1, 2], [2, 4], [3, 8]], + [[0, 1], [1, 3], [2, 9], [3, 27]] + ] + }); + + $('#test-17 .keyvalue').epoch({ + type: 'bar', + dataFormat: { + name: 'keyvalue', + arguments: [['a', 'b']], + options: { + x: function(d, i) { return d.x; } + } + }, + data: [ + {x: 1, a: 1, b: 1 }, + {x: 2, a: 2, b: 4 }, + {x: 3, a: 3, b: 6 }, + {x: 4, a: 4, b: 8 }, + {x: 5, a: 5, b: 10 } + ] + }); + }); + </script> + + <div id="test-18" class="test"> + <h2>18. Bar Ticks</h2> + <p>Ensure that we can use ticks option with bar charts.<p> + <div class="epoch"></div> + </div> + <script> + $(function() { + var data = [], + size = 100; + + for (var i = 0; i < size; i++) { + data.push({ + x: i + 1, + a: 1 + Math.random()*2*i, + b: 1 + Math.random()*2*i + }); + } + + var chart = $('#test-18 .epoch').epoch({ + type: 'bar', + data: data, + dataFormat: { + name: 'keyvalue', + arguments: [['a', 'b']], + options: { + x: function(d, i) { return d.x; } + } + }, + }); + + console.log(chart.data); + }); + </script> + </body> +</html> diff --git a/debian/missing-sources/epoch/tests/render/basic/histogram.html b/debian/missing-sources/epoch/tests/render/basic/histogram.html new file mode 100644 index 0000000..505054e --- /dev/null +++ b/debian/missing-sources/epoch/tests/render/basic/histogram.html @@ -0,0 +1,155 @@ +<!DOCTYPE html> +<html> + <head> + <link rel="stylesheet" type="text/css" href="../css/tests.css"> + <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script> + <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> + <script src="../../../dist/js/epoch.js"></script> + <script src="../js/data.js"></script> + + <script> + window.beta = function(a, b) { + a = a ? a : 2; + b = b ? b : 5; + + var histogram = new BetaData(a, b, 1).next()[0].histogram, + values = []; + + for(var x in histogram) + values.push({x: x, y: histogram[x]}) + + return [{values: values}]; + } + </script> + + <link rel="stylesheet" type="text/css" href="../../../dist/css/epoch.css"> + </head> + <body> + <h1>Basic Bar Chart Test</h1> + <p class="breadcrumbs"><a href="../index.html">Epoch Chart Tests</a> » Basic Bar</p> + + <ol> + <li><a href="#test-1">Beta(2, 5)</a></li> + <li><a href="#test-2">Beta(2, 5) Horizontal</a></li> + <li><a href="#test-3">Option: buckets</a></li> + <li><a href="#test-4">Options: bucketRange & cutOutliers</a></li> + </ol> + + <!-- Test 1 --> + <div id="test-1" class="test"> + <h2>1. Beta(2, 5)</h2> + <p>Plot a random selection of points from the Beta(2, 5) distribution as a histogram.</p> + <div class="epoch"></div> + <p><button>Refresh</button></p> + </div> + <script> + $(function() { + var chart = $('#test-1 .epoch').epoch({ + type: 'histogram', + buckets: 20, + data: beta(2, 5) + }); + + $('#test-1 button').click(function(e) { + chart.update( beta(2, 5) ); + }); + }); + </script> + + <!-- Test 2 --> + <div id="test-2" class="test"> + <h2>2. Beta(2, 5) Horizontal</h2> + <p>Plot a random selection of points from Beta(2, 5) and display in a horizontal histogram.</p> + <div class="epoch"></div> + <p><button>Refresh</button></p> + </div> + <script> + $(function() { + var chart = $('#test-2 .epoch').epoch({ + type: 'histogram', + buckets: 10, + data: beta(2, 5), + orientation: 'horizontal' + }); + + $('#test-2 button').click(function(e) { + chart.update( beta(2, 5) ); + }); + }); + </script> + + <!-- Test 3 --> + <div id="test-3" class="test"> + <h2>3. Option: buckets</h2> + <p>Plot Beta(2, 2) and change number of buckets on the fly.</p> + <div class="epoch"></div> + <p> + <button data-value="5">5 Buckets</button> + <button data-value="10">10 Buckets</button> + <button data-value="20">20 Buckets</button> + <button data-value="25">25 Buckets</button> + <button data-value="50">50 Buckets</button> | + <button class="refresh">Refresh</button> + </p> + </div> + <script> + $(function() { + var chart = $('#test-3 .epoch').epoch({ + type: 'histogram', + buckets: 10, + data: beta(2, 2) + }); + + $('#test-3 button[data-value]').click(function(e) { + var buckets = parseInt($(e.target).attr('data-value')); + chart.option('buckets', buckets); + }); + + $('#test-3 button.refresh').click(function(e) { + chart.update( beta(2, 2) ); + }); + }); + </script> + + <!-- Test 4 --> + <div id="test-4" class="test"> + <h2>4. Options: bucketRange & cutOutliers</h2> + <p>Plot beta(2, 5) and change the bucket range on the fly.</p> + <div class="epoch"></div> + <p> + <button data-range="0,100">Range [0, 100]</button> + <button data-range="0,50">Range [0, 50]</button> + <button data-range="25,75">Range [25, 75]</button> | + <button class="cutOutliers">Cut Outliers</button> + <button class="keepOutliers">Keep Outliers</button> | + <button class="refresh">Refresh</button> + </p> + </div> + <script> + $(function() { + var chart = $('#test-4 .epoch').epoch({ + type: 'histogram', + buckets: 25, + data: beta(2, 5) + }); + + $('#test-4 button[data-range]').click(function(e) { + var range = $(e.target).attr('data-range').split(',').map(function(d) { return +d; }); + chart.option('bucketRange', range); + }); + + $('#test-4 .cutOutliers').click(function(e) { + chart.option('cutOutliers', true); + }); + + $('#test-4 .keepOutliers').click(function(e) { + chart.option('cutOutliers', false); + }); + + $('#test-4 button.refresh').click(function(e) { + chart.update( beta(2, 5) ); + }); + }); + </script> + </body> +</html> diff --git a/debian/missing-sources/epoch/tests/render/basic/line.html b/debian/missing-sources/epoch/tests/render/basic/line.html new file mode 100644 index 0000000..37de38d --- /dev/null +++ b/debian/missing-sources/epoch/tests/render/basic/line.html @@ -0,0 +1,471 @@ +<!DOCTYPE html> +<html> + <head> + <link rel="stylesheet" type="text/css" href="../css/tests.css"> + <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script> + <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> + <script src="../../../dist/js/epoch.js"></script> + <script src="../js/data.js"></script> + <link rel="stylesheet" type="text/css" href="../../../dist/css/epoch.css"> + </head> + <body> + <h1>Basic Line Chart Test</h1> + <p class="breadcrumbs"><a href="../index.html">Epoch Chart Tests</a> » Basic Line</p> + + <ol> + <li><a href="#test-1">Single Series</a></li> + <li><a href="#test-2">Single Series II</a></li> + <li><a href="#test-3">Multi Series</a></li> + <li><a href="#test-4">Single Series Transition</a></li> + <li><a href="#test-5">Multi Series Transition</a></li> + <li><a href="#test-6">Single to Multi Series Transition</a></li> + <li><a href="#test-7">Color Override</a></li> + <li><a href="#test-8">Categorical Colors</a></li> + <li><a href="#test-9">Multi Series without Labels</a></li> + <li><a href="#test-10">Multi Series with Fixed Domain</a></li> + <li><a href="#test-11">Show/Hide Layers</a></li> + </ol> + + <!-- Test 1 --> + <div id="test-1" class="test"> + <h2>1. Single Series</h2> + <p>Display a plot of <code>y = cos(x)</code> over the range <code>[-2π, 2π)</code>.</p> + <div class="epoch"></div> + </div> + + <script> + $(function() { + var data = [{ label: 'A', values: [] }], + length = 128; + for (var i = 0; i < length; i++) { + var x = i * 4 * Math.PI / length - 2 * Math.PI, + y = Math.cos(x); + data[0].values.push({x: x, y: y}); + } + $('#test-1 .epoch').epoch({ type: 'line', data: data }); + }); + </script> + + <!-- Test 2 --> + <div id="test-2" class="test"> + <h2>2. Single Series II</h2> + <p>Display a plot of <code>y = e<sup>x</sup>*sin(x)</code> from <code>[0, &pi)</code>.</p> + <div class="epoch"></div> + </div> + + <script> + $(function() { + var data = [{ label: 'A', values: [] }], + length = 128; + for (var i = 0; i < length; i++) { + var x = i * Math.PI / length, + y = Math.exp(x) * Math.sin(x); + data[0].values.push({x: x, y: y}); + } + $('#test-2 .epoch').epoch({type: 'line', data: data}); + }) + </script> + + <!-- Test 3 --> + <div id="test-3" class="test"> + <h2>3. Multi Series</h2> + <p> + Display a plot of the following functions over the range [0, 2π]: + <ul> + <li><code>x*sin(x)</code></li> + <li><code>x*cos(x)</code></li> + </ul> + </p> + <div class="epoch"></div> + </div> + + <script> + $(function() { + var data = [ + { label: 'A', values: [] }, + { label: 'B', values: [] } + ], + length = 128; + for (var i = 0; i < length; i++) { + var x = i * 2 * Math.PI / length; + data[0].values.push({x: x, y: x*Math.sin(x)}); + data[1].values.push({x: x, y: x*Math.cos(x)}); + } + $('#test-3 .epoch').epoch({type: 'line', data: data}); + }); + </script> + + <!-- Test 4 --> + <div id="test-4" class="test"> + <h2>4. Single Series Transition</h2> + <p> + Correctly transition between the functions <code>y = 1 / x</code> and <code>y = x<sup>2</sup></code> over the range [1, 2). + Use the buttons below the chart to initiate the transitions. + </p> + <div class="epoch"></div> + <p> + <button data-index="0">y = 1 / x</button> + <button data-index="1">y = x^2</button> + </p> + </div> + + <script> + $(function() { + var data1 = [{label: 'A', values: []}], + data2 = [{label: 'B', values: []}], + data = [data1, data2], + length = 128; + + for (var i = 0; i < length; i++) { + var x = i / length + 1; + data1[0].values.push({x: x, y: 1/x}); + data2[0].values.push({x: x, y: x*x}); + } + + var chart = $('#test-4 .epoch').epoch({ type: 'line', data: data1 }); + + $('#test-4 button').on('click', function(e) { + var index = parseInt($(e.target).attr('data-index')); + chart.update(data[index]); + }); + }); + </script> + + <!-- Test 5 --> + <div id="test-5" class="test"> + <h2>5. Multi Series Transition</h2> + <p> + Correctly transition between Set A: + <ul> + <li><code>y = sin(x)</code></li> + <li><code>y = x - x<sup>3</sup>/3! + x<sup>5</sup>/5!</code></li> + </ul> + Set B: + <ul> + <li><code>y = cos(x)</code></li> + <li><code>y = 1 - x<sup>2</sup>/2! + x<sup>4</sup>/4!</code></li> + </ul> + and Set C: + <ul> + <li><code>y = sin(x) - (x - x<sup>3</sup>/3! + x<sup>5</sup>/5!)</code></li> + <li><code>y = cos(x) - (1 - x<sup>2</sup>/2! + x<sup>4</sup>/4!)</code></li> + </ul> + </p> + <div class="epoch"></div> + <p> + <button data-index="0">Set A</button> + <button data-index="1">Set B</button> + <button data-index="2">Set C</button> + </p> + </div> + + <script> + $(function() { + var fac = function(n) { + var result = n; + while(--n > 1) { + result *= n; + } + return result; + }; + + var taylorSin = function(x) { + return x - Math.pow(x, 3) / fac(3) + Math.pow(x, 5) / fac(5); + }; + + var taylorCos = function(x) { + return 1 - Math.pow(x, 2) / fac(2) + Math.pow(x, 4) / fac(4); + }; + + var data1 = [ + { label: 'A', values: [] }, + { label: 'B', values: [] } + ], + data2 = [ + { label: 'A', values: [] }, + { label: 'B', values: [] } + ], + data3 = [ + { label: 'A', values: [] }, + { label: 'B', values: [] } + ], + data = [data1, data2, data3], + length = 64; + + for (var i = 0; i <= length; i++) { + var x = i * 8 / length - 4; + data1[0].values.push({x: x, y: Math.sin(x)}); + data1[1].values.push({x: x, y: taylorSin(x)}); + data2[0].values.push({x: x, y: Math.cos(x)}); + data2[1].values.push({x: x, y: taylorCos(x)}); + data3[0].values.push({x: x, y: Math.sin(x) - taylorSin(x)}); + data3[1].values.push({x: x, y: Math.cos(x) - taylorCos(x)}); + } + + var chart = $('#test-5 .epoch').epoch({ type: 'line', data: data1 }); + + $('#test-5 button').on('click', function(e) { + var index = parseInt($(e.target).attr('data-index')); + chart.update(data[index]); + }); + }); + </script> + + <!-- Test 6 --> + <div id="test-6" class="test"> + <h2>6. Single to Multi Series Transition</h2> + <p> + Correctly transition between Set A: + <ul> + <li><code>y = ln(x)</code></li> + </ul> + Set B: + <ul> + <li><code>y = ln(x)</code></li> + <li><code>y = x * ln(x)</code></li> + <li><code>y = x * ln(x)<sup>2</sup></code></li> + </ul> + </p> + <div class="epoch"></div> + <p> + <button data-index="0">Single</button> + <button data-index="1">Multi</button> + </p> + </div> + + <script> + $(function() { + var data1 = [{label: 'A', values: []}], + data2 = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []} + ], + data = [data1, data2], + length = 128; + + for (var i = 0; i <= length; i++) { + var x = i * 2 / length + 1; + data1[0].values.push({x: x, y: Math.log(x)}); + data2[0].values.push({x: x, y: Math.log(x)}); + data2[1].values.push({x: x, y: x * Math.log(x)}); + data2[2].values.push({x: x, y: x * Math.log(x) * Math.log(x)}); + } + + var chart = $('#test-6 .epoch').epoch({ type: 'line', data: data1 }); + + $('#test-6 button').on('click', function(e) { + var index = parseInt($(e.target).attr('data-index')); + chart.update(data[index]); + }); + }); + </script> + + <!-- Test 7 --> + <div id="test-7" class="test"> + <h2>7. Color Override</h2> + <p> + Display the first layer of the plot as pink, the second layer as green, and the third layer as blue. + </p> + <div class="epoch"></div> + </div> + + <style> + #test-7 .epoch .a .line { stroke: pink; } + #test-7 .epoch .b .line { stroke: green; } + #test-7 .epoch .c .line { stroke: blue; } + </style> + + <script> + $(function() { + var data = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []} + ], + length = 64; + + for (var i = 0; i < length; i++) { + var x = i * 1 / length + 0.5; + for (var j = 0; j < data.length; j++) { + data[j].values.push({x: x, y: Math.pow(x, 2*j) * Math.log(x)}); + } + } + + $('#test-7 .epoch').epoch({ type: 'line', data: data }); + }); + </script> + + <!-- Test 8 --> + <div id="test-8" class="test"> + <h2>Categorical Colors</h2> + <p> + Change layer colors automatically when switching between the following categorical color classes on the containing element: + <ul> + <li><code>category10</code></li> + <li><code>category20</code></li> + <li><code>category20b</code></li> + <li><code>category20c</code></li> + </ul> + Change the categorical colors by pressing the buttons below the chart. + </p> + <div class="epoch category10"></div> + <p> + <button data-class="category10">category10</button> + <button data-class="category20">category20</button> + <button data-class="category20b">category20b</button> + <button data-class="category20c">category20c</button> + </p> + </div> + + <script> + $(function() { + var data = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []}, + {label: 'D', values: []}, + {label: 'E', values: []}, + {label: 'F', values: []}, + {label: 'G', values: []} + ], + length = 64, + className = 'category10'; + + for (var i = 0; i <= length; i++) { + var x = i * 1 / length; + for (var j = 0; j < data.length; j++) { + data[j].values.push({x: x, y: Math.pow(x, j+1)}); + } + } + + $('#test-8 .epoch').epoch({ type: 'line', data: data }); + + $('#test-8 button').on('click', function(e) { + $('#test-8 .epoch').removeClass(className); + className = $(e.target).attr('data-class'); + $('#test-8 .epoch').addClass(className); + }); + }); + </script> + + + <!-- Test 9 --> + <div id="test-9" class="test"> + <h2>9. Multi Series without Labels</h2> + <p> + Correctly render a multi-series plot of: + <ul> + <li><code>y = sin(x)</code></li> + <li><code>y = cos(x)</code></li> + </ul> + where the layers are given without labels. + </p> + <div class="epoch"></div> + </div> + <script> + $(function() { + var data = [{values: []}, {values: []}], + length = 128; + + for (var i = 0; i <= length; i++) { + var x = i * 4 * Math.PI / length; + data[0].values.push({x: x, y: Math.sin(x)}); + data[1].values.push({x: x, y: Math.cos(x)}); + } + + $('#test-9 .epoch').epoch({ type: 'line', data: data }); + }); + </script> + + <!-- Test 10 --> + <div id="test-10" class="test"> + <h2>10. Multi Series with Fixed Domain</h2> + <p> + Display a plot of the following functions: + <ul> + <li><code>x*sin(x)</code></li> + <li><code>x*cos(x)</code></li> + </ul> + On the domain <code>[0, 5]</code> and range <code>[0, 4]</code>. + </p> + <div class="epoch"></div> + </div> + + <script> + $(function() { + var data = [ + { label: 'A', values: [] }, + { label: 'B', values: [] } + ], + length = 128; + for (var i = 0; i < length; i++) { + var x = i * 2 * Math.PI / length; + data[0].values.push({x: x, y: x*Math.sin(x)}); + data[1].values.push({x: x, y: x*Math.cos(x)}); + } + $('#test-10 .epoch').epoch({ + type: 'line', + data: data, + domain: [0, 5], + range: [0, 4] + }); + }); + </script> + + <!-- Test 11 --> + <div id="test-11" class="test"> + <h2>11. Show/Hide Layers</h2> + <div class="epoch"></div> + <p> + <button data-index="0">Toggle A</button> + <button data-index="1">Toggle B</button> + <button data-index="2">Toggle C</button> + </p> + </div> + <script> + $(function() { + var chart = $('#test-11 .epoch').epoch({ + type: 'line', + data: data().add(function(x) { return x; }) + .add(function(x) { return 1.5*x; }) + .add(function(x) { return 2.0*x; }).get([0, 10], 1) + }); + + $('#test-11 button').click(function(e) { + var index = parseInt($(e.target).attr('data-index')); + chart.toggleLayer(index); + }); + }); + </script> + + <!-- Test 12 --> + <div id="test-12" class="test"> + <h2>12. Multi-axis</h2> + <div class="epoch"></div> + </div> + <script> + $(function() { + var d = [ + { range: 'left', values: [] }, + { range: 'right', values: [] } + ]; + + for (var i = 0; i < 40; i++) { + d[0].values.push({ x: i, y: 10*i }); + d[1].values.push({ x: i, y: Math.cos(2*Math.PI*i/40.0) }); + } + + var chart = $('#test-12 .epoch').epoch({ + type: 'line', + data: d, + axes: ['left', 'right'], + range: { + left: 'left', + right: 'right' + } + }); + }); + </script> + </body> +</html> diff --git a/debian/missing-sources/epoch/tests/render/basic/model.html b/debian/missing-sources/epoch/tests/render/basic/model.html new file mode 100644 index 0000000..2814fd8 --- /dev/null +++ b/debian/missing-sources/epoch/tests/render/basic/model.html @@ -0,0 +1,94 @@ +<!DOCTYPE html> +<html> + <head> + <link rel="stylesheet" type="text/css" href="../css/tests.css"> + <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script> + <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> + <script src="../../../dist/js/epoch.js"></script> + <script src="../js/data.js"></script> + <link rel="stylesheet" type="text/css" href="../../../dist/css/epoch.css"> + <style> + body { background: #333; color: #d0d0d0; } + a:link, a:visited { color: white; color: white; } + + .epoch { + height: 220px; + } + + #sparkline { height: 50px; } + + </style> + </head> + <body class="epoch-theme-dark"> + <h1>Basic Chart Model / Data Test</h1> + <p class="breadcrumbs"><a href="../index.html">Epoch Chart Tests</a> » Basic Chart Model / Data Test</p> + + <p> + <button class="set" data-index="0">Data A</button> + <button class="set" data-index="1">Data B</button> + <button class="set" data-index="2">Data C</button> + </p> + + <div id="sparkline" class="epoch"></div> + <div id="area" class="epoch"></div> + <div id="bar" class="epoch"></div> + + <script> + $(function() { + var dataA = []; + for (var j = 0; j < 3; j++) { + var layer = []; + for (var i = 0; i < 20; i++) { + layer.push(50 + Math.random()*20); + } + dataA.push(layer); + } + + var dataB = []; + for (var j = 0; j < 2; j++) { + var layer = []; + for (i = 0; i < 30; i++) { + layer.push(10 + Math.random() * 40) + } + dataB.push(layer); + } + + var dataC = []; + for (var i = 0; i < 50; i++) { + dataC.push(Math.random() * 100); + } + + var data = [dataA, dataB, dataC]; + + // Setup the model + window.model = new Epoch.Model({ dataFormat: 'array' }); + model.setData(dataA); + + // Make the charts and associate them with the model + var sparkline = $('#sparkline').epoch({ + type: 'line', + axes: ['left', 'right'], + model: model + }); + + var area = $('#area').epoch({ + type: 'area', + axes: ['left', 'right', 'bottom'], + model: model + }); + + window.bar = $('#bar').epoch({ + type: 'bar', + axes: ['left', 'right', 'bottom'], + model: model + }); + + // Click listeners for the buttons + $('button.set').click(function(e) { + var index = parseInt($(e.target).attr('data-index')); + model.setData(data[index]); + }); + }) + </script> + </body> +</html>
\ No newline at end of file diff --git a/debian/missing-sources/epoch/tests/render/basic/options.html b/debian/missing-sources/epoch/tests/render/basic/options.html new file mode 100644 index 0000000..5ff2d7e --- /dev/null +++ b/debian/missing-sources/epoch/tests/render/basic/options.html @@ -0,0 +1,267 @@ +<!DOCTYPE html> +<html> + <head> + <link rel="stylesheet" type="text/css" href="../css/tests.css"> + <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script> + <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> + <script src="../../../dist/js/epoch.js"></script> + <script src="../js/data.js"></script> + <link rel="stylesheet" type="text/css" href="../../../dist/css/epoch.css"> + <style> + body { background: #333; color: #d0d0d0; } + a:link, a:visited { color: white; color: white; } + </style> + </head> + <body class="epoch-theme-dark"> + <h1>Basic Chart Options and Events</h1> + <p class="breadcrumbs"><a href="../index.html">Epoch Chart Tests</a> » Basic Chart Options and Events</p> + <ol> + <li><a href="#test-1">Axes</a></li> + <li><a href="#test-2">Margins</a></li> + <li><a href="#test-3">Ticks and Tick Formats</a></li> + <li><a href="#test-4">Resize</a></li> + <li><a href="#test-5">Domain</a></li> + <li><a href="#test-6">Range</a></li> + </ol> + + <!-- Test 1 --> + <div id="test-1" class="test"> + <h2>1. Axes</h2> + <p> + Correctly add and remove axes when options are set. + </p> + <div class="epoch"></div> + <div class="controls"> + <button data-index="0">All</button> + <button data-index="1">[left, right]</button> + <button data-index="2">[top, bottom]</button> + <button data-index="3">[left, bottom]</button> + <button data-index="4">[top, right]</button> + <button data-index="5">None</button> + </div> + </div> + <script> + $(function() { + var axes = [ + ['top', 'left', 'bottom', 'right'], + ['left', 'right'], + ['top', 'bottom'], + ['left', 'bottom'], + ['top', 'right'], + [] + ]; + + var chart = $('#test-1 .epoch').epoch({ + type: 'line', + data: data().add(function(x) { return Math.cos(x) }).get([0, 2*Math.PI], Math.PI/25) + }); + + $('#test-1 button').click(function(e) { + chart.option('axes', axes[parseInt($(e.target).attr('data-index'))]); + }); + }); + </script> + + + <!-- Test 2 --> + <div id="test-2" class="test"> + <h2>2. Margins</h2> + <p> + Correctly resize margins when options are set. + </p> + <div class="epoch"></div> + <div class="controls"> + <button data-index="0">No Margins</button> + <button data-index="1">Big Margins</button> + <button data-index="2">Small Margins</button> + </div> + </div> + <script> + $(function() { + var sizes = [ + {margins: {top: null, left: null, bottom: null, right: null }}, + {margins: {top: 60, left: 60, bottom: 60, right: 60 }}, + {margins: {top: 20, left: 20, bottom: 20, right: 20 }} + ]; + + var chart = $('#test-2 .epoch').epoch({ + type: 'line', + data: data().add(function(x) { return Math.sin(x); }) + .add(function(x) { return Math.cos(x); }) + .get([0, 2*Math.PI], Math.PI/25), + axes: [] + }); + + $('#test-2 button').click(function(e) { + var margins = sizes[parseInt($(e.target).attr('data-index'))]; + console.log(margins); + chart.option(margins); + }); + }); + </script> + + <!-- Test 3 --> + <div id="test-3" class="test"> + <h2>3. Ticks and Tick Formats</h2> + <p> + Correctly resize margins when options are set. + </p> + <div class="epoch"></div> + <div class="controls"> + <button class="ticks">Toggle Ticks</button> + <button class="tickFormats">Toggle Tick Formats</button> + </div> + </div> + <script> + $(function() { + var ticksIndex = 0, ticks = [ + { + ticks: { + top: 14, + bottom: 14, + left: 5, + right: 5, + } + }, + { + ticks: { + top: 30, + left: 2, + bottom: 30, + right: 2 + } + } + ]; + + var tickFormatsIndex = 0, tickFormats = [ + { + tickFormats: { + top: Epoch.Formats.regular, + bottom: Epoch.Formats.regular, + left: Epoch.Formats.si, + right: Epoch.Formats.si + } + }, + { + tickFormats: { + top: function(d) { return parseInt(100*d)+'%' }, + bottom: function(d) { return parseInt(100*d)+'%' }, + left: function(d) { return parseInt(100*d)+'%' }, + right: function(d) { return parseInt(100*d)+'%' } + } + } + ]; + + var chart = $('#test-3 .epoch').epoch({ + type: 'area', + data: data().add(function(x) { return (Math.sin(x)+1) / (x+1); }) + .add(function(x) { return (Math.cos(x)+1) / (x+2); }) + .get([0, 2*Math.PI], Math.PI/25), + axes: ['top', 'left', 'right', 'bottom'] + }); + + $('#test-3 button.ticks').click(function(e) { + ticksIndex = (ticksIndex + 1) % ticks.length; + chart.option(ticks[ticksIndex]); + }); + + $('#test-3 button.tickFormats').click(function(e) { + tickFormatsIndex = (tickFormatsIndex + 1) % tickFormats.length; + chart.option(tickFormats[tickFormatsIndex]); + }); + }); + </script> + + <!-- Test 4 --> + <div id="test-4" class="test"> + <h2>4. Resize</h2> + <p> + Correctly resize the chart when the width and height options are set. + </p> + <div class="epoch" style="width: 600px; height: 200px"></div> + <div class="controls"> + <button data-index="0">Original</button> + <button data-index="1">Bigger</button> + </div> + </div> + <script> + $(function() { + var sizes = [ + [600, 200], + [800, 320] + ]; + + var chart = $('#test-4 .epoch').epoch({ + type: 'line', + data: data().add(function(x) { return Math.sin(x) }).get([0, 2*Math.PI], Math.PI/25) + }); + + $('#test-4 button').click(function(e) { + var index = parseInt($(e.target).attr('data-index')); + chart.option('width', sizes[index][0]); + chart.option('height', sizes[index][1]); + }); + }); + </script> + + <!-- Test 5 --> + <div id="test-5" class="test"> + <h2>6. Option: domain</h2> + <div class="epoch"></div> + <p> + <button data-index="0">Domain From Data Extent</button> + <button data-index="1">[0, π]</button> + </p> + </div> + <script> + $(function() { + var domains = [ + null, + [0, Math.PI] + ]; + + var chart = $('#test-5 .epoch').epoch({ + type: 'line', + data: data().add(function(x) { return 1 - Math.sin(x) }).get([0, 2*Math.PI], Math.PI/25) + }); + + $('#test-5 button').click(function(e) { + var index = parseInt($(e.target).attr('data-index')); + chart.option('domain', domains[index]) + }); + }); + </script> + + + <!-- Test 6 --> + <div id="test-6" class="test"> + <h2>7. Option: range</h2> + <div class="epoch"></div> + <p> + <button data-index="0">Range From Data Extent</button> + <button data-index="1">[0, 1.5]</button> + </p> + </div> + <script> + $(function() { + var ranges = [ + null, + [0, 1.5] + ]; + + var chart = $('#test-6 .epoch').epoch({ + type: 'area', + data: data().add(function(x) { return 1 - Math.cos(x) }).get([0, 2*Math.PI], Math.PI/25) + }); + + $('#test-6 button').click(function(e) { + var index = parseInt($(e.target).attr('data-index')); + console.log(ranges[index]); + chart.option('range', ranges[index]) + + }); + }); + </script> + + </body> +</html>
\ No newline at end of file diff --git a/debian/missing-sources/epoch/tests/render/basic/pie.html b/debian/missing-sources/epoch/tests/render/basic/pie.html new file mode 100644 index 0000000..fe105e8 --- /dev/null +++ b/debian/missing-sources/epoch/tests/render/basic/pie.html @@ -0,0 +1,346 @@ +<!DOCTYPE html> +<html> + <head> + <link rel="stylesheet" type="text/css" href="../css/tests.css"> + <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script> + <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> + <script src="../../../dist/js/epoch.js"></script> + <link rel="stylesheet" type="text/css" href="../../../dist/css/epoch.css"> + <style> + .test .epoch { + width: 350px; + height: 350px; + } + </style> + </head> + <body> + <h1>Basic Pie Chart Test</h1> + <p class="breadcrumbs"><a href="../index.html">Epoch Chart Tests</a> » Basic Pie</p> + <ol> + <li><a href="#test-1">Basic Pie Test</a></li> + <li><a href="#test-2">Basic Donut Test</a></li> + <li><a href="#test-3">Pie Tranisition I</a></li> + <li><a href="#test-4">Pie Tranisition II</a></li> + <li><a href="#test-5">Color Override</a></li> + <li><a href="#test-6">Categorical Colors</a></li> + <li><a href="#test-7">Pie Chart Layers without Labels</a></li> + <li><a href="#test-8">Margin Changes</a></li> + <li><a href="#test-9">Inner Changes</a></li> + <li><a href="#test-10">Show/Hide Layers</a></li> + </ol> + + <!-- Test 1 --> + <div id="test-1" class="test"> + <h2>Basic Pie Test</h2> + <p> + Correctly render a pie chart with three categories: + <ul> + <li>A - 20</li> + <li>B - 45</li> + <li>C - 35</li> + </ul> + </p> + <div class="epoch"></div> + </div> + + <script> + $(function() { + $('#test-1 .epoch').epoch({ + type: 'pie', + data: [ + {label: 'A', value: 20}, + {label: 'B', value: 45}, + {label: 'C', value: 35} + ] + }); + }); + </script> + + <!-- Test 2 --> + <div id="test-2" class="test"> + <h2>2. Basic Donut Test</h2> + <p> + Correctly render a donut chart with three categories: + <ul> + <li>A - 50</li> + <li>B - 30</li> + <li>C - 20</li> + </ul> + </p> + <div class="epoch"></div> + </div> + + <script> + $(function() { + $('#test-2 .epoch').epoch({ + type: 'pie', + inner: 100, + data: [ + {label: 'A', value: 50}, + {label: 'B', value: 30}, + {label: 'C', value: 20} + ] + }); + }); + </script> + + <!-- Test 3 --> + <div id="test-3" class="test"> + <h2>3. Pie Tranisition I</h2> + <p> + Correctly transition between set A: + <ul> + <li>A - 20</li> + <li>B - 80</li> + </ul> + and set B: + <ul> + <li>A - 20</li> + <li>B - 30</li> + <li>C - 50</li> + </ul> + Use the buttons below the chart to initiate the transitions. + </p> + <div class="epoch"></div> + <p> + <button data-index="0">Set A</button> + <button data-index="1">Set B</button> + </p> + </div> + + <script> + $(function() { + var data1 = [ + {label: 'A', value: 20}, + {label: 'B', value: 80} + ], + data2 = [ + {label: 'A', value: 20}, + {label: 'B', value: 30}, + {label: 'C', value: 50} + ], + data = [data1, data2], + chart = $('#test-3 .epoch').epoch({ type: 'pie', data: data1 }); + + $('#test-3 button').on('click', function(e) { + var index = parseInt($(e.target).attr('data-index')); + chart.update(data[index]); + }); + }); + </script> + + <!-- Test 4 --> + <div id="test-4" class="test"> + <h2>4. Pie Tranisition II</h2> + <p> + Correctly transition between set A: + <ul> + <li>A - 20</li> + <li>B - 80</li> + </ul> + and set B: + <ul> + <li>A - 50</li> + <li>B - 50</li> + </ul> + Use the buttons below the chart to initiate the transitions. + </p> + <div class="epoch"></div> + <p> + <button data-index="0">Set A</button> + <button data-index="1">Set B</button> + </p> + </div> + + <script> + $(function() { + var data1 = [ + {label: 'A', value: 20}, + {label: 'B', value: 80} + ], + data2 = [ + {label: 'A', value: 50}, + {label: 'B', value: 50} + ], + data = [data1, data2], + chart = $('#test-4 .epoch').epoch({ type: 'pie', data: data1 }); + + $('#test-4 button').on('click', function(e) { + var index = parseInt($(e.target).attr('data-index')); + chart.update(data[index]); + }); + }); + </script> + + <!-- Test 5 --> + <div id="test-5" class="test"> + <h2>5. Color Override</h2> + <p> + Override the colors as such: + <ul> + <li>A - Pink</li> + <li>B - Green</li> + <li>C - Blue</li> + </ul> + </p> + <div class="epoch"></div> + </div> + + <style> + #test-5 .arc.a path { fill: pink; } + #test-5 .arc.b path { fill: green; } + #test-5 .arc.c path { fill: blue; } + </style> + + <script> + $(function() { + $('#test-5 .epoch').epoch({ + type: 'pie', + data: [ + {label: 'A', value: 20}, + {label: 'B', value: 45}, + {label: 'C', value: 35} + ] + }); + }); + </script> + + <!-- Test 6 --> + <div id="test-6" class="test"> + <h2>6. Categorical Colors</h2> + <p> + Correctly transition between different categorical colors sets. + </p> + <div class="epoch category10"></div> + <p> + <button data-class="category10">category10</button> + <button data-class="category20">category20</button> + <button data-class="category20b">category20b</button> + <button data-class="category20c">category20c</button> + </p> + </div> + + + <script> + $(function() { + var data = [], + className = 'category10'; + + for (var j = 0; j < 10; j++) { + data.push({label: String.fromCharCode(65+j), value: 10}); + } + + $('#test-6 .epoch').epoch({ + type: 'pie', + inner: 100, + data: data + }); + + $('#test-6 button').on('click', function(e) { + $('#test-6 .epoch').removeClass(className); + className = $(e.target).attr('data-class'); + $('#test-6 .epoch').addClass(className); + }); + }); + </script> + + + <!-- Test 7 --> + <div id="test-7" class="test"> + <h2>7. Pie Chart Layers without Labels</h2> + <p> + Correctly render a pie chart with three categories: + <ul> + <li>30</li> + <li>35</li> + <li>35</li> + </ul> + when the layers are not provided labels. + </p> + <div class="epoch"></div> + </div> + + <script> + $(function() { + $('#test-7 .epoch').epoch({ + type: 'pie', + data: [ {value: 30}, {value: 35}, {value: 35} ] + }); + }); + </script> + + <!-- Test 8 --> + <div id="test-8" class="test"> + <h2>8. Margin Changes</h2> + <div class="epoch"></div> + <p> + <button data-index="0">Small</button> + <button data-index="1">Big</button> + </p> + </div> + <script> + $(function() { + var margins = [10, 40]; + var chart = $('#test-8 .epoch').epoch({ + type: 'pie', + data: [ {value: 30}, {value: 35}, {value: 35} ] + }); + $('#test-8 button').click(function(e) { + var margin = margins[parseInt($(e.target).attr('data-index'))]; + chart.option('margin', margin); + }); + }); + </script> + + <!-- Test 9 --> + <div id="test-9" class="test"> + <h2>9. Inner Changes</h2> + <div class="epoch"></div> + <p> + <button data-index="0">No Inner</button> + <button data-index="1">Small Inner</button> + <button data-index="2">Big Inner</button> + </p> + </div> + <script> + $(function() { + var innerMargins = [0, 50, 100]; + var chart = $('#test-9 .epoch').epoch({ + type: 'pie', + data: [ {value: 30}, {value: 35}, {value: 35} ] + }); + $('#test-9 button').click(function(e) { + var inner = innerMargins[parseInt($(e.target).attr('data-index'))]; + chart.option('inner', inner); + }); + }); + </script> + + <!-- Test 10 --> + <div id="test-10" class="test"> + <h2>10. Show/Hide Layers</h2> + <div class="epoch"></div> + <p> + <button data-index="0">Toggle A</button> + <button data-index="1">Toggle B</button> + <button data-index="2">Toggle C</button> + </p> + </div> + <script> + $(function() { + var chart = $('#test-10 .epoch').epoch({ + type: 'pie', + data: [ + {label: 'A', value: 20}, + {label: 'B', value: 45}, + {label: 'C', value: 35} + ] + }); + $('#test-10 button').click(function(e) { + var index = parseInt($(e.target).attr('data-index')); + chart.toggleLayer(index); + }); + }); + </script> + </body> +</html>
\ No newline at end of file diff --git a/debian/missing-sources/epoch/tests/render/basic/scatter.html b/debian/missing-sources/epoch/tests/render/basic/scatter.html new file mode 100644 index 0000000..31f1e42 --- /dev/null +++ b/debian/missing-sources/epoch/tests/render/basic/scatter.html @@ -0,0 +1,398 @@ +<!DOCTYPE html> +<html> + <head> + <link rel="stylesheet" type="text/css" href="../css/tests.css"> + <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script> + <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> + <script src="../../../dist/js/epoch.js"></script> + <script src="../js/data.js"></script> + <link rel="stylesheet" type="text/css" href="../../../dist/css/epoch.css"> + </head> + <body> + <h1>Basic Scatter Plot Test</h1> + <p class="breadcrumbs"><a href="../index.html">Epoch Chart Tests</a> » Basic Scatter</p> + <ol> + <li><a href="#test-1">Single Series</a></li> + <li><a href="#test-2">Multi Series</a></li> + <li><a href="#test-3">Single Series Transition</a></li> + <li><a href="#test-4">Multi Series Transition</a></li> + <li><a href="#test-5">Multi Series Transition II</a></li> + <li><a href="#test-6">Color Override</a></li> + <li><a href="#test-7">Categorical Colors</a></li> + <li><a href="#test-8">Multi Series without Labels</a></li> + <li><a href="#test-9">Single Series with Radius</a></li> + <li><a href="#test-10">Radius Change</a></li> + <li><a href="#test-11">Show/Hide Layers</a></li> + </ol> + + <!-- Test 1 --> + <div id="test-1" class="test"> + <h2>1. Single Series</h2> + <p>Render a single random series scatter plot.</p> + <div class="epoch"></div> + </div> + <script> + $(function() { + var data = [], + length = 128; + + for (var j = 0; j < length; j++) { + var x = Math.random() * 100, + y = Math.random() * 20; + data.push({x: x, y: y}); + } + + $('#test-1 .epoch').epoch({ type: 'scatter', data: [{label: 'A', values: data}]}); + }); + </script> + + <!-- Test 2 --> + <div id="test-2" class="test"> + <h2>2. Multi Series</h2> + <p>Render three random scatter series in the same plot.</p> + <div class="epoch"></div> + </div> + <script> + $(function() { + var data = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []} + ], + length = 64; + + for (var i = 0; i < length; i++) { + for (var j = 0; j < data.length; j++) { + var x = Math.random() * 100, + y = Math.random() * 20; + data[j].values.push({x: x, y: y}); + } + } + + $('#test-2 .epoch').epoch({type: 'scatter', data: data}); + }); + </script> + + <!-- Test 3 --> + <div id="test-3" class="test"> + <h2>3. Single Series Transition</h2> + <p> + Transition from one random series to another random series. Use the + buttons below the plot to initiate the transition. + </p> + <div class="epoch"></div> + <p> + <button data-index="0">Series A</button> + <button data-index="1">Series B</button> + </p> + </div> + <script> + $(function() { + var data = [ + [ + {label: 'A', values: []} + ], + [ + {label: 'A', values: []} + ] + ], + length = 64; + + for (var i = 0; i < length; i++) { + for (var j = 0; j < data.length; j++) { + for (var k = 0; k < data[j].length; k++) { + var x = Math.random() * 100, + y = Math.random() * 20; + data[j][k].values.push({x: x, y: y}); + } + } + } + + var chart = $('#test-3 .epoch').epoch({type: 'scatter', data: data[0]}); + $('#test-3 button').on('click', function(e) { + var index = parseInt($(e.target).attr('data-index')); + chart.update(data[index]); + }); + }); + </script> + + <!-- Test 4 --> + <div id="test-4" class="test"> + <h2>4. Multi Series Transition</h2> + <p> + Transition from a set of multiple randoms series random series to another of multiple random series. Use the + buttons below the plot to initiate the transition. + </p> + <div class="epoch"></div> + <p> + <button data-index="0">Series Set A</button> + <button data-index="1">Series Set B</button> + </p> + </div> + <script> + $(function() { + var data = [ + [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []} + ], + [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []} + ] + ], + length = 64; + + for (var i = 0; i < length; i++) { + for (var j = 0; j < data.length; j++) { + for (var k = 0; k < data[j].length; k++) { + var x = Math.random() * 100, + y = Math.random() * 20; + data[j][k].values.push({x: x, y: y}); + } + } + } + + var chart = $('#test-4 .epoch').epoch({type: 'scatter', data: data[0]}); + $('#test-4 button').on('click', function(e) { + var index = parseInt($(e.target).attr('data-index')); + chart.update(data[index]); + }); + }); + </script> + + <!-- Test 5 --> + <div id="test-5" class="test"> + <h2>5. Multi Series Transition II</h2> + <p> + Transition from a multi-series set of random data to a single series set of random data. + </p> + <div class="epoch"></div> + <p> + <button data-index="0">Series Set A</button> + <button data-index="1">Series Set B</button> + </p> + </div> + <script> + $(function() { + var data = [ + [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []} + ], + [ + {label: 'A', values: []} + ] + ], + length = 64; + + for (var i = 0; i < length; i++) { + for (var j = 0; j < data.length; j++) { + for (var k = 0; k < data[j].length; k++) { + var x = Math.random() * 100, + y = Math.random() * 20; + data[j][k].values.push({x: x, y: y}); + } + } + } + + var chart = $('#test-5 .epoch').epoch({type: 'scatter', data: data[0]}); + $('#test-5 button').on('click', function(e) { + var index = parseInt($(e.target).attr('data-index')); + chart.update(data[index]); + }); + }); + </script> + + <!-- Test 6 --> + <div id="test-6" class="test"> + <h2>6. Color Override</h2> + <p>The first series should be pink, the second green, and thrid blue.</p> + <div class="epoch"></div> + </div> + <style> + #test-6 .a .dot { fill: pink; } + #test-6 .b .dot { fill: green; } + #test-6 .c .dot { fill: blue; } + </style> + <script> + $(function() { + var data = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []} + ], + length = 64; + + for (var i = 0; i < length; i++) { + for (var j = 0; j < data.length; j++) { + var x = Math.random() * 100, + y = Math.random() * 20; + data[j].values.push({x: x, y: y}); + } + } + + $('#test-6 .epoch').epoch({type: 'scatter', data: data}); + }); + </script> + + <!-- Test 7 --> + <div id="test-7" class="test"> + <h2>7. Categorical Colors</h2> + <p> + Correctly transition between different categorical color sets. + </p> + <div class="epoch"></div> + <p> + <button data-class="category10">category10</button> + <button data-class="category20">category20</button> + <button data-class="category20b">category20b</button> + <button data-class="category20c">category20c</button> + </p> + </div> + <script> + $(function() { + var data = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []}, + {label: 'E', values: []}, + {label: 'F', values: []}, + {label: 'G', values: []}, + {label: 'H', values: []}, + {label: 'I', values: []}, + {label: 'J', values: []}, + {label: 'K', values: []} + ], + className = 'category10'; + + for (var i = 0; i < data.length; i++) { + var x = i * Math.PI * 2 / data.length, + y = Math.sin(x); + data[i].values.push({x: x, y: y}); + } + + $('#test-7 .epoch').epoch({ + type: 'scatter', + radius: 10, + data: data + }); + + + $('#test-7 button').on('click', function(e) { + $('#test-7 .epoch').removeClass(className); + className = $(e.target).attr('data-class'); + $('#test-7 .epoch').addClass(className); + }); + }); + </script> + + <!-- Test 8 --> + <div id="test-8" class="test"> + <h2>8. Multi Series without Labels</h2> + <p>Correctly render two random scatter plots when labels are not specified for the layers.</p> + <div class="epoch"></div> + </div> + <script> + $(function() { + var data = [{values: []}, {values: []}], + length = 64; + + for (var i = 0; i < length; i++) { + for (var j = 0; j < data.length; j++) { + var x = Math.random() * 100, + y = Math.random() * 20; + data[j].values.push({x: x, y: y}); + } + } + + $('#test-8 .epoch').epoch({type: 'scatter', data: data}); + }); + </script> + + <!-- Test 9 --> + <div id="test-9" class="test"> + <h2>9. Single Series with Radius</h2> + <p>Render a single random series scatter plot with different radii.</p> + <div class="epoch"></div> + <p> + <button>Regenerate</button> + </p> + </div> + <script> + $(function() { + var length = 128; + + var generateData = function(length) { + var data = []; + for (var j = 0; j < length; j++) { + var x = Math.random() * 100, + y = Math.random() * 20, + r = Math.random() * 6 + 2; + data.push({x: x, y: y, r: r}); + } + return [{label: 'A', values: data}]; + }; + + var chart = $('#test-9 .epoch').epoch({ type: 'scatter', data: generateData(length)}); + + $("#test-9 p button:eq(0)").click(function() { + chart.update(generateData(length)); + }); + }); + </script> + + <!-- Test 10 --> + <div id="test-10" class="test"> + <h2>10. Radius Change</h2> + <div class="epoch"></div> + <p> + <button data-index="0">Small</button> + <button data-index="1">Normal</button> + <button data-index="2">Large</button> + <button data-index="3">Very Large</button> + </p> + </div> + <script> + $(function() { + var radii = [2.0, 3.5, 7, 14]; + var chart = $('#test-10 .epoch').epoch({ + type: 'scatter', + data: data().random() + }); + $('#test-10 button').click(function(e) { + var index = parseInt($(e.target).attr('data-index')); + chart.option('radius', radii[index]); + }); + }); + </script> + + <!-- Test 11 --> + <div id="test-11" class="test"> + <h2>11. Show/Hide Layers</h2> + <div class="epoch"></div> + <p> + <button data-index="0">Toggle A</button> + <button data-index="1">Toggle B</button> + <button data-index="2">Toggle C</button> + </p> + </div> + <script> + $(function() { + var chart = $('#test-11 .epoch').epoch({ + type: 'scatter', + data: data().multiRandom(3) + }); + + $('#test-11 button').click(function(e) { + var index = parseInt($(e.target).attr('data-index')); + chart.toggleLayer(index); + }); + }); + </script> + + </body> +</html> diff --git a/debian/missing-sources/epoch/tests/render/css/tests.css b/debian/missing-sources/epoch/tests/render/css/tests.css new file mode 100644 index 0000000..7f4099e --- /dev/null +++ b/debian/missing-sources/epoch/tests/render/css/tests.css @@ -0,0 +1,12 @@ +body { + font-family: helvetica; +} + +/* Default test chart height of 220px. */ +.test .epoch { + height: 220px; +} + +.broken { + color: red; +} diff --git a/debian/missing-sources/epoch/tests/render/index.html b/debian/missing-sources/epoch/tests/render/index.html new file mode 100644 index 0000000..1ab15c1 --- /dev/null +++ b/debian/missing-sources/epoch/tests/render/index.html @@ -0,0 +1,57 @@ +<!DOCTYPE html> +<html> + <head> + <title>Epoch - Chart Rendering Tests</title> + <link rel="stylesheet" type="text/css" href="css/tests.css"> + <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script> + <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> + <script src="../../dist/js/epoch.js"></script> + <link rel="stylesheet" type="text/css" href="../../dist/css/epoch.css"> + </head> + <body> + <h1>Epoch Chart Rendering Tests</h1> + + <h2>Basic Charts</h2> + <p> + <ul> + <li><a href="basic/area.html">Area</a></li> + <li><a href="basic/bar.html">Bar</a></li> + <li> + <ul><li><a href="basic/histogram.html">Histogram</a></li></ul> + </li> + <li><a href="basic/line.html">Line</a></li> + <li><a href="basic/pie.html">Pie</a></li> + <li><a href="basic/scatter.html">Scatter</a></li> + <li><a href="basic/options.html">All Charts - Options and Events</a></li> + </ul> + </p> + + <h2>Real-time Charts</h2> + <p> + <ul> + <li><a href="real-time/area.html">Area</a></li> + <li><a href="real-time/bar.html">Bar</a></li> + <li><a href="real-time/gauge.html">Gauge</a></li> + <li><a href="real-time/heatmap.html">Heatmap</a></li> + <li><a href="real-time/line.html">Line</a></li> + <li><a href="real-time/options.html">All Charts - Options and Events</a></li> + </ul> + </p> + + <h2>Themes</h2> + <p> + <ul> + <li><a href="themes/default.html">Default</a></li> + <li><a href="themes/dark.html">Dark</a></li> + </ul> + </p> + + <h2>Data / Models</h2> + <p> + <ul> + <li><a href="basic/model.html">Basic Model</a></li> + <li><a href="real-time/model.html">Real-time Model</a></li> + </ul> + </p> + </body> +</html> diff --git a/debian/missing-sources/epoch/tests/render/js/data.js b/debian/missing-sources/epoch/tests/render/js/data.js new file mode 100644 index 0000000..a49da68 --- /dev/null +++ b/debian/missing-sources/epoch/tests/render/js/data.js @@ -0,0 +1,210 @@ +(function() { + + // Quick data generator + Data = function() { + this.layers = [] + }; + + Data.prototype.add = function(fn) { + fn = fn ? fn : function(x) { return x; }; + this.layers.push(fn); + return this; + }; + + Data.prototype.get = function(domain, step) { + domain = domain ? domain : [0, 10]; + step = step ? step : 1; + + var data = [] + for (var i = 0; i < this.layers.length; i++) { + layer = { label: String.fromCharCode(i + 65), values: [] }; + for (var x = domain[0]; x < domain[1]; x += step) { + layer.values.push({ x: x, y: this.layers[i](x) }); + } + data.push(layer); + } + return data; + }; + + Data.prototype.random = function(entries, domain, range) { + entries = entries ? entries : 50; + domain = domain ? domain : [0, 100]; + range = range ? range : [0, 100]; + + var values = []; + for (var i = 0; i < entries; i++) { + var x = (domain[1] - domain[0]) * Math.random() + domain[0], + y = (range[1] - range[0]) * Math.random() + range[1]; + values.push({ x: x, y: y }); + } + + return [{ label: 'A', values: values }]; + }; + + Data.prototype.multiRandom = function(numSeries, entries, domain, range) { + numSeries = numSeries ? numSeries : 3; + entries = entries ? entries : 50; + domain = domain ? domain : [0, 100]; + range = range ? range : [0, 100]; + + var data = []; + + for (var j = 0; j < numSeries; j++) { + var layer = { label: String.fromCharCode(65 + j), values: [] }; + for (var i = 0; i < entries; i++) { + var x = (domain[1] - domain[0]) * Math.random() + domain[0], + y = (range[1] - range[0]) * Math.random() + range[1]; + layer.values.push({ x: x, y: y }); + } + data.push(layer); + } + + return data; + }; + + window.data = function() { return new Data(); }; + + + // Quick real-time data generator + Time = function() { + Data.call(this); + }; + + Time.prototype = new Data() + + Time.prototype.get = function(domain, step) { + var data = Data.prototype.get.apply(this, arguments), + time = parseInt(new Date().getTime() / 1000); + + for (var i = 0; i < data[0].values.length; i++) { + for (var j = 0; j < this.layers.length; j++) { + delete data[j].values[i].x; + data[j].values[i].time = time + i; + } + } + + this.currentTime = time; + this.lastX = domain[1]; + + return data; + }; + + Time.prototype.next = function(step) { + this.currentTime++; + this.lastX += (step ? step : 1); + + var data = []; + for (var j = 0; j < this.layers.length; j++) { + data.push({ time: this.currentTime, y: this.layers[j](this.lastX) }) + } + + return data; + } + + window.time = function() { return new Time(); }; + + + + + window.nextTime = (function() { + var currentTime = parseInt(new Date().getTime() / 1000); + return function() { return currentTime++; } + })(); + + + /* + * Normal distribution random histogram data generator. + */ + var NormalData = function(layers) { + this.layers = layers; + this.timestamp = ((new Date()).getTime() / 1000)|0; + }; + + var normal = function() { + var U = Math.random(), + V = Math.random(); + return Math.sqrt(-2*Math.log(U)) * Math.cos(2*Math.PI*V); + }; + + NormalData.prototype.sample = function() { + return parseInt(normal() * 12.5 + 50); + } + + NormalData.prototype.rand = function() { + var histogram = {}; + + for (var i = 0; i < 1000; i ++) { + var r = this.sample(); + if (!histogram[r]) { + histogram[r] = 1; + } + else { + histogram[r]++; + } + } + + return histogram; + }; + + NormalData.prototype.history = function(entries) { + if (typeof(entries) != 'number' || !entries) { + entries = 60; + } + + var history = []; + for (var k = 0; k < this.layers; k++) { + history.push({ label: String.fromCharCode(65+k), values: [] }); + } + + for (var i = 0; i < entries; i++) { + for (var j = 0; j < this.layers; j++) { + history[j].values.push({time: this.timestamp, histogram: this.rand()}); + } + this.timestamp++; + } + + return history; + }; + + NormalData.prototype.next = function() { + var entry = []; + for (var i = 0; i < this.layers; i++) { + entry.push({ time: this.timestamp, histogram: this.rand() }); + } + this.timestamp++; + return entry; + } + + window.NormalData = NormalData; + + + /* + * Beta distribution histogram data generator. + */ + var BetaData = function(alpha, beta, layers) { + this.alpha = alpha; + this.beta = beta; + this.layers = layers; + this.timestamp = ((new Date()).getTime() / 1000)|0; + }; + + BetaData.prototype = new NormalData(); + + BetaData.prototype.sample = function() { + var X = 0, + Y = 0; + + for (var j = 1; j <= this.alpha; j++) + X += -Math.log(1 - Math.random()); + + for (var j = 1; j <= this.beta; j++) + Y += -Math.log(1 - Math.random()); + + return parseInt(100 * X / (X + Y)); + } + + window.BetaData = BetaData; + +})(); + + diff --git a/debian/missing-sources/epoch/tests/render/real-time/area.html b/debian/missing-sources/epoch/tests/render/real-time/area.html new file mode 100644 index 0000000..b66ab7d --- /dev/null +++ b/debian/missing-sources/epoch/tests/render/real-time/area.html @@ -0,0 +1,494 @@ +<!DOCTYPE html> +<html> + <head> + <link rel="stylesheet" type="text/css" href="../css/tests.css"> + <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script> + <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> + <script src="../../../dist/js/epoch.js"></script> + <link rel="stylesheet" type="text/css" href="../../../dist/css/epoch.css"> + + <script> + var nextTime = (function() { + var currentTime = parseInt(new Date().getTime() / 1000); + return function() { return currentTime++; } + })(); + </script> + </head> + <body> + <h1>Real-time Area Plot Test</h1> + <p class="breadcrumbs"><a href="../index.html">Epoch Chart Tests</a> » Real-time Area</p> + <ol> + <li><a href="#test-1">Single Series</a></li> + <li><a href="#test-2">Single Series Single Transition</a></li> + <li><a href="#test-3">Single Seires Stream</a></li> + <li><a href="#test-4">Multi Series</a></li> + <li><a href="#test-5">Multi Series Single Transition</a></li> + <li><a href="#test-6">Multi Seires Stream</a></li> + <li><a href="#test-7">Color Override</a></li> + <li><a href="#test-8">Categorical Colors</a></li> + <li><a href="#test-9">Show/Hide Layers</a></li> + </ol> + + <!-- Test 1 --> + <div id="test-1" class="test"> + <h2>1. Single Series</h2> + <p> + Correctly render a single series plot of <code>y = cos(x) + 1</code> over the range <code>[0, 2π]</code>. + </p> + <div class="epoch"></div> + </div> + + <script> + $(function() { + var data = [{ label: 'A', values: [] }], + length = 40; + + for (var i = 0; i < length; i++) { + var x = i * 2 * Math.PI / length, + y = Math.cos(x) + 1, + time = nextTime(); + data[0].values.push({time: time, y: y}); + } + + $('#test-1 .epoch').epoch({ type: 'time.area', data: data }); + }); + </script> + + <!-- Test 2 --> + <div id="test-2" class="test"> + <h2>2. Single Series Single Transition</h2> + <p> + Correctly render a single series plot of <code>y = sin(x) + 1</code>. When the button is pressed push a new data point to the chart and correctly animate/render the transiton. + </p> + <div class="epoch"></div> + <p><button>Next</button></p> + </div> + + <script> + $(function() { + var data = [{ label: 'A', values: [] }], + length = 40, + nextIndex = length; + + for (var i = 0; i < length; i++) { + var x = i * 2 * Math.PI / length, + y = Math.sin(x) + 1, + time = nextTime(); + data[0].values.push({time: time, y: y}); + } + + var chart = $('#test-2 .epoch').epoch({ + type: 'time.area', + data: data + }); + + $('#test-2 button').on('click', function(e) { + var x = nextIndex * 2 * Math.PI / length, + y = Math.sin(x) + 1, + time = nextTime(); + nextIndex++; + chart.push([{ time: time, y: y }]); + }); + }); + </script> + + + <!-- Test 3 --> + <div id="test-3" class="test"> + <h2>3. Single Seires Stream</h2> + <p>Correctly play / pause a single series stream of values from the plot <code>y = cos(x) + 1</code>.</p> + <div class="epoch"></div> + <p><button>Play</button></p> + </div> + + <script> + $(function() { + var data = [{ label: 'A', values: [] }], + length = 40, + nextIndex = length, + playing = false, + interval = null; + + for (var i = 0; i < length; i++) { + var x = i * 2 * Math.PI / length, + y = Math.cos(x) + 1, + time = nextTime(); + data[0].values.push({time: time, y: y}); + } + + var chart = $('#test-3 .epoch').epoch({ + type: 'time.area', + data: data + }); + + var pushPoint = function() { + var x = nextIndex * 2 * Math.PI / length, + y = Math.cos(x) + 1, + time = nextTime(); + chart.push([{ time: time, y: y}]); + nextIndex++; + }; + + $('#test-3 button').on('click', function(e) { + if (playing) { + $(e.target).text('Play'); + clearInterval(interval); + } + else { + $(e.target).text('Pause'); + pushPoint(); + interval = setInterval(pushPoint, 1000); + } + playing = !playing; + }); + }); + </script> + + <!-- Test 4 --> + <div id="test-4" class="test"> + <h2>4. Multi Series</h2> + <p>Correctly render a multi series plot of the following functions:</p> + <ul> + <li><code>y = log(x+1)</code></li> + <li><code>y = 2*log(x+1)</code></li> + <li><code>y = 3*log(x+1)</code></li> + </ul> + <div class="epoch"></div> + </div> + + <script> + $(function() { + var data = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []} + ], + length = 40; + + for (var i = 0; i < length; i++) { + var x = i + 1, + time = nextTime(); + for (var j = 0; j < data.length; j++) { + data[j].values.push({time: time, y: (j+1) * Math.log(x)}); + } + } + + $('#test-4 .epoch').epoch({ + type: 'time.area', + data: data, + axes: ['left', 'right', 'bottom'] + }); + }); + </script> + + + <!-- Test 5 --> + <div id="test-5" class="test"> + <h2>5. Multi Series Single Transition</h2> + <p> + Correctly render a multi series plot of the following functions: + <ul> + <li><code>y = x</code></li> + <li><code>y = x<sup>1.25</sup></code></li> + <li><code>y = x<sup>1.5</sup></code></li> + </ul> + and correctly render/animate the transiton after pressing the "Next" button. + </p> + <div class="epoch"></div> + <p> + <button>Next</button> + </p> + </div> + + <script> + $(function() { + var data = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []} + ], + length = 40, + scale = 0.1, + nextIndex = length; + + for (var i = 0; i < length; i++) { + var x = i * scale, + time = nextTime(); + data[0].values.push({time: time, y: x}); + data[1].values.push({time: time, y: Math.pow(x, 1.25)}); + data[2].values.push({time: time, y: Math.pow(x, 1.5)}); + } + + var chart = $('#test-5 .epoch').epoch({ + type: 'time.area', + data: data, + axes: ['left', 'right', 'bottom'] + }); + + $('#test-5 button').on('click', function(e) { + var x = nextIndex * scale, + time = nextTime(); + nextIndex++; + chart.push([ + {time: time, y: x}, + {time: time, y: Math.pow(x, 1.25)}, + {time: time, y: Math.pow(x, 1.5)} + ]); + }); + }); + </script> + + <!-- Test 6 --> + <div id="test-6" class="test"> + <h2>6. Multi Seires Stream</h2> + <p> + Correctly play / pause a multi series stream of values over the following plots: + <ul> + <li><code>x</code></li> + <li><code>x * log(x)</code></li> + <li><code>x * log<sup>2</sup>(x)</code></li> + </ul> + </p> + <div class="epoch"></div> + <p><button>Play</button></p> + </div> + + <script> + $(function() { + var data = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []} + ], + length = 40, + nextIndex = length, + scale = 0.1, + playing = false, + interval = null; + + for (var i = 0; i < length; i++) { + var x = (i+1) * scale, + time = nextTime(); + data[0].values.push({time: time, y: x}); + data[1].values.push({time: time, y: x * Math.log(x)}); + data[2].values.push({time: time, y: x * Math.pow(Math.log(x), 2)}); + } + + var chart = $('#test-6 .epoch').epoch({ + type: 'time.area', + data: data, + axes: ['right', 'bottom'] + }); + + var pushPoint = function() { + var x = (nextIndex +1) * scale, + time = nextTime(); + chart.push([ + { time: time, y: x}, + { time: time, y: x * Math.log(x)}, + { time: time, y: x * Math.pow(Math.log(x), 2)} + ]); + nextIndex++; + }; + + $('#test-6 button').on('click', function(e) { + if (playing) { + $(e.target).text('Play'); + clearInterval(interval); + } + else { + $(e.target).text('Pause'); + interval = setInterval(pushPoint, 1000); + pushPoint(); + } + playing = !playing; + }); + }); + </script> + + <!-- Test 7 --> + <div id="test-7" class="test"> + <h2>7. Color Override</h2> + <p>The first layer should pink, the second green, and the third blue.</p> + <div id="test-7-plot" class="epoch"></div> + </div> + + <style> + #test-7-plot .a .area { fill: pink; } + #test-7-plot .b .area { fill: green; } + #test-7-plot .c .area { fill: blue; } + </style> + + <script> + $(function() { + var data = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []} + ], + length = 40; + + for (var i = 0; i < length; i++) { + var x = i + 1, + time = nextTime(); + for (var j = 0; j < data.length; j++) { + data[j].values.push({time: time, y: (j+1) * Math.log(x)}); + } + } + + $('#test-7 .epoch').epoch({ + type: 'time.area', + data: data + }); + }); + </script> + + <!-- Test 8 --> + <div id="test-8" class="test"> + <h2>8. Categorical Colors</h2> + <p>Correctly render and switch between different categorical colors. Unlike with the basic charts this doesn't occur just by switching the class name on the containing element. The CSS query engine must be purge and the plot must be manually redrawn, like so:<code><pre> +Epoch.QueryCSS.purge(); +chart.draw(); +</pre></code> + </p> + + <div class="epoch category10"></div> + + <p> + <button data-class="category10">category10</button> + <button data-class="category20">category20</button> + <button data-class="category20b">category20b</button> + <button data-class="category20c">category20c</button> + </p> + </div> + + <script> + $(function() { + var data = [], + length = 41, + className = "category10", + layers = 8, + time = nextTime(); + + for (var i = 0; i < layers; i++) { + var layer = { label: String.fromCharCode(i+65), values: [] }; + for (var j = 0; j < length; j++) { + var x = j + 1, + y = Math.pow(x, 1 + 0.3 * Math.log(Math.log(Math.log(x+1) + 1) + 1)); + layer.values.push({ time: time + j, y: y }); + } + data.push(layer); + } + + var chart = $('#test-8 .epoch').epoch({ type: 'time.area', data: data }); + + $('#test-8 button').on('click', function(e) { + $('#test-8 .epoch').removeClass(className); + className = $(e.target).attr('data-class'); + $('#test-8 .epoch').addClass(className); + Epoch.QueryCSS.purge(); + chart.draw(); + }); + }); + </script> + + <!-- Test 9 --> + <div id="test-9" class="test"> + <h2>9. Show/Hide Layers</h2> + <div class="epoch"></div> + <p> + <button class="toggle" data-index="0">Toggle A</button> + <button class="toggle" data-index="1">Toggle B</button> + <button class="toggle" data-index="2">Toggle C</button> | + <button class="playback">Play</button> + </p> + </div> + <script> + $(function() { + var data = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []} + ], + length = 40, + nextIndex = length, + scale = 0.1, + playing = false, + interval = null; + + for (var i = 0; i < length; i++) { + var x = (i+1) * scale, + time = nextTime(); + data[0].values.push({time: time, y: x}); + data[1].values.push({time: time, y: 1.5*x}); + data[2].values.push({time: time, y: 2*x}); + } + + var chart = $('#test-9 .epoch').epoch({ + type: 'time.area', + data: data, + axes: ['right', 'bottom'] + }); + + var pushPoint = function() { + var x = (nextIndex +1) * scale, + time = nextTime(); + chart.push([ + { time: time, y: x}, + { time: time, y: 1.5*x}, + { time: time, y: 2*x} + ]); + nextIndex++; + }; + + $('#test-9 button.toggle').click(function(e) { + var index = parseInt($(e.target).attr('data-index')); + chart.toggleLayer(index); + }); + + $('#test-9 button.playback').on('click', function(e) { + if (playing) { + $(e.target).text('Play'); + clearInterval(interval); + } + else { + $(e.target).text('Pause'); + interval = setInterval(pushPoint, 1000); + pushPoint(); + } + playing = !playing; + }); + }); + </script> + + <!-- Test 10 --> + <div id="test-10" class="test"> + <h2>10. Fixed Range</h2> + <p> + Render a single series plot of <code>y = cos(x)</code>. Instead of automatically setting the range to <code>[-1, 1]</code>, the range is manually set to <code>[-2, 5]</code>. + </p> + <div class="epoch"></div> + </div> + + <script> + $(function() { + var data = [{ label: 'A', values: [] }], + length = 40; + + for (var i = 0; i < length; i++) { + var x = i * 2 * Math.PI / length, + y = Math.cos(x), + time = nextTime(); + data[0].values.push({time: time, y: y}); + } + + $('#test-10 .epoch').epoch({ + type: 'time.area', + axes: ['right', 'bottom'], + data: data, + range: [-2, 5] + }); + }); + </script> + </body> +</html>
\ No newline at end of file diff --git a/debian/missing-sources/epoch/tests/render/real-time/bar.html b/debian/missing-sources/epoch/tests/render/real-time/bar.html new file mode 100644 index 0000000..952119c --- /dev/null +++ b/debian/missing-sources/epoch/tests/render/real-time/bar.html @@ -0,0 +1,484 @@ +<!DOCTYPE html> +<html> + <head> + <link rel="stylesheet" type="text/css" href="../css/tests.css"> + <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script> + <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> + <script src="../../../dist/js/epoch.js"></script> + <link rel="stylesheet" type="text/css" href="../../../dist/css/epoch.css"> + + <script> + var nextTime = (function() { + var currentTime = parseInt(new Date().getTime() / 1000); + return function() { return currentTime++; } + })(); + </script> + </head> + <body> + <h1>Real-time Bar Test</h1> + <p class="breadcrumbs"><a href="../index.html">Epoch Chart Tests</a> » Real-time Bar</p> + <ol> + <li><a href="#test-1">Single Series</a></li> + <li><a href="#test-2">Single Series Single Transition</a></li> + <li><a href="#test-3">Single Seires Stream</a></li> + <li><a href="#test-4">Multi Series</a></li> + <li><a href="#test-5">Multi Series Single Transition</a></li> + <li><a href="#test-6">Multi Seires Stream</a></li> + <li><a href="#test-7">Color Override</a></li> + <li><a href="#test-8">Categorical Colors</a></li> + <li><a href="#test-9">Show/Hide Layers</a></li> + </ol> + + <!-- Test 1 --> + <div id="test-1" class="test"> + <h2>1. Single Series</h2> + <p> + Correctly render a single series plot of <code>y = cos(x) + 1</code> over the range <code>[0, 2π]</code>. + </p> + <div class="epoch"></div> + </div> + + <script> + $(function() { + var data = [{ label: 'A', values: [] }], + length = 40; + + for (var i = 0; i < length; i++) { + var x = i * 2 * Math.PI / length, + y = Math.cos(x) + 1, + time = nextTime(); + data[0].values.push({time: time, y: y}); + } + + $('#test-1 .epoch').epoch({ type: 'time.bar', data: data }); + }); + </script> + + <!-- Test 2 --> + <div id="test-2" class="test"> + <h2>2. Single Series Single Transition</h2> + <p> + Correctly render a single series plot of <code>y = sin(x) + 1</code>. When the button is pressed push a new data point to the chart and correctly animate/render the transiton. + </p> + <div class="epoch"></div> + <p><button>Next</button></p> + </div> + + <script> + $(function() { + var data = [{ label: 'A', values: [] }], + length = 40, + nextIndex = length; + + for (var i = 0; i < length; i++) { + var x = i * 2 * Math.PI / length, + y = Math.sin(x) + 1, + time = nextTime(); + data[0].values.push({time: time, y: y}); + } + + var chart = $('#test-2 .epoch').epoch({ + type: 'time.bar', + data: data + }); + + $('#test-2 button').on('click', function(e) { + var x = nextIndex * 2 * Math.PI / length, + y = Math.sin(x) + 1, + time = nextTime(); + nextIndex++; + chart.push([{ time: time, y: y }]); + }); + }); + </script> + + + <!-- Test 3 --> + <div id="test-3" class="test"> + <h2>3. Single Seires Stream</h2> + <p>Correctly play / pause a single series stream of values from the plot <code>y = cos(x) + 1</code>.</p> + <div class="epoch"></div> + <p><button>Play</button></p> + </div> + + <script> + $(function() { + var data = [{ label: 'A', values: [] }], + length = 40, + nextIndex = length, + playing = false, + interval = null; + + for (var i = 0; i < length; i++) { + var x = i * 2 * Math.PI / length, + y = Math.cos(x) + 1, + time = nextTime(); + data[0].values.push({time: time, y: y}); + } + + var chart = $('#test-3 .epoch').epoch({ + type: 'time.bar', + data: data + }); + + var pushPoint = function() { + var x = nextIndex * 2 * Math.PI / length, + y = Math.cos(x) + 1, + time = nextTime(); + chart.push([{ time: time, y: y}]); + nextIndex++; + }; + + $('#test-3 button').on('click', function(e) { + if (playing) { + $(e.target).text('Play'); + clearInterval(interval); + } + else { + $(e.target).text('Pause'); + pushPoint(); + interval = setInterval(pushPoint, 1000); + } + playing = !playing; + }); + }); + </script> + + <!-- Test 4 --> + <div id="test-4" class="test"> + <h2>4. Multi Series</h2> + <p>Correctly render a multi series plot of the following functions:</p> + <ul> + <li><code>y = log(x+1)</code></li> + <li><code>y = 2*log(x+1)</code></li> + <li><code>y = 3*log(x+1)</code></li> + </ul> + <div class="epoch"></div> + </div> + + <script> + $(function() { + var data = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []} + ], + length = 40; + + for (var i = 0; i < length; i++) { + var x = i + 1, + time = nextTime(); + for (var j = 0; j < data.length; j++) { + data[j].values.push({time: time, y: (j+1) * Math.log(x)}); + } + } + + $('#test-4 .epoch').epoch({ + type: 'time.bar', + data: data, + axes: ['left', 'right', 'bottom'] + }); + }); + </script> + + + <!-- Test 5 --> + <div id="test-5" class="test"> + <h2>5. Multi Series Single Transition</h2> + <p> + Correctly render a multi series plot of the following functions: + <ul> + <li><code>y = x</code></li> + <li><code>y = x<sup>1.25</sup></code></li> + <li><code>y = x<sup>1.5</sup></code></li> + </ul> + and correctly render/animate the transiton after pressing the "Next" button. + </p> + <div class="epoch"></div> + <p> + <button>Next</button> + </p> + </div> + + <script> + $(function() { + var data = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []} + ], + length = 40, + scale = 0.1, + nextIndex = length; + + for (var i = 0; i < length; i++) { + var x = i * scale, + time = nextTime(); + data[0].values.push({time: time, y: x}); + data[1].values.push({time: time, y: Math.pow(x, 1.25)}); + data[2].values.push({time: time, y: Math.pow(x, 1.5)}); + } + + var chart = $('#test-5 .epoch').epoch({ + type: 'time.bar', + data: data, + axes: ['left', 'right', 'bottom'] + }); + + $('#test-5 button').on('click', function(e) { + var x = nextIndex * scale, + time = nextTime(); + nextIndex++; + chart.push([ + {time: time, y: x}, + {time: time, y: Math.pow(x, 1.25)}, + {time: time, y: Math.pow(x, 1.5)} + ]); + }); + }); + </script> + + <!-- Test 6 --> + <div id="test-6" class="test"> + <h2>6. Multi Seires Stream</h2> + <p> + Correctly play / pause a multi series stream of values over the following plots: + <ul> + <li><code>x</code></li> + <li><code>x * log(x)</code></li> + <li><code>x * log<sup>2</sup>(x)</code></li> + </ul> + </p> + <div class="epoch"></div> + <p><button>Play</button></p> + </div> + + <script> + $(function() { + var data = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []} + ], + length = 40, + nextIndex = length, + scale = 0.1, + playing = false, + interval = null; + + for (var i = 0; i < length; i++) { + var x = (i+1) * scale, + time = nextTime(); + data[0].values.push({time: time, y: x}); + data[1].values.push({time: time, y: x * Math.log(x)}); + data[2].values.push({time: time, y: x * Math.pow(Math.log(x), 2)}); + } + + var chart = $('#test-6 .epoch').epoch({ + type: 'time.bar', + data: data, + axes: ['right', 'bottom'] + }); + + var pushPoint = function() { + var x = (nextIndex +1) * scale, + time = nextTime(); + chart.push([ + { time: time, y: x}, + { time: time, y: x * Math.log(x)}, + { time: time, y: x * Math.pow(Math.log(x), 2)} + ]); + nextIndex++; + }; + + $('#test-6 button').on('click', function(e) { + if (playing) { + $(e.target).text('Play'); + clearInterval(interval); + } + else { + $(e.target).text('Pause'); + interval = setInterval(pushPoint, 1000); + pushPoint(); + } + playing = !playing; + }); + }); + </script> + + <!-- Test 7 --> + <div id="test-7" class="test"> + <h2>7. Color Override</h2> + <p>The first layer should pink, the second green, and the third blue.</p> + <div id="test-7-plot" class="epoch"></div> + </div> + + <style> + #test-7-plot .bar.a { fill: pink; } + #test-7-plot .bar.b { fill: green; } + #test-7-plot .bar.c { fill: blue; } + </style> + + <script> + $(function() { + var data = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []} + ], + length = 40; + + for (var i = 0; i < length; i++) { + var x = i + 1, + time = nextTime(); + for (var j = 0; j < data.length; j++) { + data[j].values.push({time: time, y: (j+1) * Math.log(x)}); + } + } + + $('#test-7 .epoch').epoch({ + type: 'time.bar', + data: data + }); + }); + </script> + + <!-- Test 8 --> + <div id="test-8" class="test"> + <h2>8. Categorical Colors</h2> + <p>Correctly render and switch between different categorical colors. Unlike with the basic charts this doesn't occur just by switching the class name on the containing element. The CSS query engine must be purge and the plot must be manually redrawn, like so:<code><pre> +Epoch.QueryCSS.purge(); +chart.draw(); +</pre></code> + </p> + + <div class="epoch category10"></div> + + <p> + <button data-class="category10">category10</button> + <button data-class="category20">category20</button> + <button data-class="category20b">category20b</button> + <button data-class="category20c">category20c</button> + </p> + </div> + + <script> + $(function() { + var data = [], + length = 41, + className = "category10", + layers = 8, + time = nextTime(); + + for (var i = 0; i < layers; i++) { + var layer = { label: String.fromCharCode(i+65), values: [] }; + for (var j = 0; j < length; j++) { + var x = j + 1, + y = Math.pow(x, 1 + 0.3 * Math.log(Math.log(Math.log(x+1) + 1) + 1)); + layer.values.push({ time: time + j, y: y }); + } + data.push(layer); + } + + var chart = $('#test-8 .epoch').epoch({ type: 'time.bar', data: data }); + + $('#test-8 button').on('click', function(e) { + $('#test-8 .epoch').removeClass(className); + className = $(e.target).attr('data-class'); + $('#test-8 .epoch').addClass(className); + Epoch.QueryCSS.purge(); + chart.draw(); + }); + }); + </script> + + <!-- Test 9 --> + <div id="test-9" class="test"> + <h2>9. Show/Hide Layers</h2> + <div class="epoch"></div> + <p> + <button class="toggle" data-index="0">Toggle A</button> + <button class="toggle" data-index="1">Toggle B</button> + <button class="toggle" data-index="2">Toggle C</button> | + <button class="playback">Play</button> + </p> + </div> + <script> + $(function() { + var data = [ + { label: 'A', values: [] }, + { label: 'B', values: [] }, + { label: 'C', values: [] } + ], + time = nextTime(), + j = 0, + interval = null; + + for (var i = 0; i < data.length; i++) { + for (j = 0; j < 60; j++) { + data[i].values.push({time: time+j, y: 0.25*(i+4)*j }) + } + } + + function nextPoint() { + var entry = [] + for (var i = 0; i < data.length; i++) { + entry.push({time: time+j, y: 0.25*(i+4)*j }); + } + chart.push(entry); + j++; + } + + var chart = $('#test-9 .epoch').epoch({ type: 'time.bar', data: data }); + + $('#test-9 .toggle').click(function(e) { + var index = parseInt($(e.target).attr('data-index')); + chart.toggleLayer(index); + }); + + $('#test-9 .playback').click(function(e) { + if (interval == null) { + interval = setInterval(nextPoint, 1000); + nextPoint(); + $(e.target).text('Pause'); + } + else { + clearInterval(interval); + interval = null; + $(e.target).text('Play'); + } + }); + }); + </script> + + <!-- Test 10 --> + <div id="test-10" class="test"> + <h2>10. Fixed Range</h2> + <p> + Render a single series plot of <code>y = cos(x) + 1</code>. Instead of automatically setting the range to <code>[0, 2]</code>, the range is manually set to <code>[0, 5]</code>. + </p> + <div class="epoch"></div> + </div> + + <script> + $(function() { + var data = [{ label: 'A', values: [] }], + length = 40; + + for (var i = 0; i < length; i++) { + var x = i * 2 * Math.PI / length, + y = Math.cos(x) + 1, + time = nextTime(); + data[0].values.push({time: time, y: y}); + } + + $('#test-10 .epoch').epoch({ + type: 'time.bar', + axes: ['right', 'bottom'], + data: data, + range: [0, 5] + }); + }); + </script> + </body> +</html>
\ No newline at end of file diff --git a/debian/missing-sources/epoch/tests/render/real-time/gauge.html b/debian/missing-sources/epoch/tests/render/real-time/gauge.html new file mode 100644 index 0000000..614334d --- /dev/null +++ b/debian/missing-sources/epoch/tests/render/real-time/gauge.html @@ -0,0 +1,283 @@ +<!DOCTYPE html> +<html> + <head> + <link rel="stylesheet" type="text/css" href="../css/tests.css"> + <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script> + <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> + <script src="../../../dist/js/epoch.js"></script> + <link rel="stylesheet" type="text/css" href="../../../dist/css/epoch.css"> + + <script> + var nextTime = (function() { + var currentTime = parseInt(new Date().getTime() / 1000); + return function() { return currentTime++; } + })(); + </script> + </head> + <body> + <h1>Real-time Gauge Test</h1> + <p class="breadcrumbs"><a href="../index.html">Epoch Chart Tests</a> » Real-time Gauge</p> + <ol> + <li><a href="#test-1">Single Value Display</a></li> + <li><a href="#test-2">Single Transition</a></li> + <li><a href="#test-3">Stream Transition</a></li> + <li><a href="#test-4">Gauge Sizes</a></li> + <li><a href="#test-5">Color Override</a></li> + <li><a href="test-6">Option: domain</a></li> + <li><a href="test-7">Option: ticks</a></li> + <li><a href="test-8">Option: tickSize</a></li> + <li><a href="test-9">Option: tickOffset</a></li> + <li><a href="test-10">Option: format</a></li> + </ol> + + <!-- Test 1 --> + <div id="test-1" class="test"> + <h2>1. Single Value Display</h2> + <p>Display a single value of 25%</p> + <div class="epoch gauge-small"></div> + </div> + <script> + $(function() { + $('#test-1 .epoch').epoch({ type: 'time.gauge', value: 0.25 }); + }); + </script> + + + <!-- Test 2 --> + <div id="test-2" class="test"> + <h2>2. Single Transition</h2> + <p>Display value of 0% and transition to a random value when the button is pressed.</p> + <div class="epoch gauge-small"></div> + <p><button>Transition</button></p> + </div> + <script> + $(function() { + var chart = $('#test-2 .epoch').epoch({ + type: 'time.gauge', + value: 0.0 + }); + + $('#test-2 button').on('click', function(e) { + chart.update(Math.random()); + }); + }); + </script> + + + <!-- Test 3 --> + <div id="test-3" class="test"> + <h2>3. Stream Transition</h2> + <p>Display value of 0% and transition to a random value every second when the button is pressed.</p> + <div class="epoch gauge-small"></div> + <p><button>Play</button></p> + </div> + <script> + $(function() { + var chart = $('#test-3 .epoch').epoch({ + type: 'time.gauge', + value: 0.0 + }), + playing = false, + interval = null; + + $('#test-3 button').on('click', function(e) { + if (interval === null) { + interval = setInterval(function() { + chart.update(Math.random()); + }, 1000); + chart.update(Math.random()); + $(e.target).text('Pause'); + } + else { + clearInterval(interval); + interval = null; + $(e.target).text('Play'); + } + }); + }); + </script> + + <!-- Test 4 --> + <div id="test-4" class="test"> + <h2>4. Gauge Sizes</h2> + <p>Display the four built-in gauge sizes in this order: tiny, small, medium, large.</p> + <div class="epoch gauge-tiny"></div> + <div class="epoch gauge-small"></div> + <div class="epoch gauge-medium"></div> + <div class="epoch gauge-large"></div> + </div> + <script> + $(function() { + $('#test-4 .gauge-tiny').epoch({ type: 'time.gauge', value: 0.75 }); + $('#test-4 .gauge-small').epoch({ type: 'time.gauge', value: 0.75 }); + $('#test-4 .gauge-medium').epoch({ type: 'time.gauge', value: 0.75 }); + $('#test-4 .gauge-large').epoch({ type: 'time.gauge', value: 0.75 }); + }); + </script> + + <!-- Test 5 --> + <div id="test-5" class="test"> + <h2>5. Color Override</h2> + <p> + Override the basic gauge styles with the following + <ul> + <li>Outer arc 8px thickness colored green</li> + <li>Inner arc 2px thickness colored orange</li> + <li>Ticks should be 3px in width and red</li> + <li>Needle colored blue.</li> + <li>Needle base colored black</li> + </ul> + </p> + <div id="test-5-plot" class="epoch gauge-medium"></div> + </div> + + <style> + #test-5-plot .epoch .gauge .arc.outer { + stroke-width: 8px; + stroke: green; + } + + #test-5-plot .epoch .gauge .arc.inner { + stroke-width: 2px; + stroke: orange; + } + + #test-5-plot .epoch .gauge .tick { + stroke-width: 3px; + stroke: red; + } + + #test-5-plot .epoch .gauge .needle { + fill: blue; + } + + #test-5-plot .epoch .gauge .needle-base { + fill: black; + } + </style> + + <script> + $(function() { + $('#test-5 .epoch').epoch({ type: 'time.gauge', value: 0.5 }); + }); + </script> + + <!-- + Common Option Event Listener Events + --> + <script> + window.makeOptionTest = function(testNumber, chartOptions, optionName) { + chartOptions = chartOptions ? chartOptions : {}; + + if (!chartOptions.type) + chartOptions.type = 'time.gauge'; + if (!chartOptions.value) + chartOptions.value = 0.5; + if (!chartOptions.domain) + chartOptions.domain = [0, 1]; + + var domain = chartOptions.domain, + id = '#test-' + testNumber, + chart = $(id + ' .epoch').epoch(chartOptions), + interval = null, + next = function() { chart.push(Math.random()*(domain[1] - domain[0]) + domain[0]); }; + + $(id + ' .playback').click(function(e) { + if (!interval) { + interval = setInterval(next, 1500); + next(); + $(e.target).text('Pause'); + } + else { + clearInterval(interval); + interval = null; + $(e.target).text('Play'); + } + }); + + var formats = [ + Epoch.Formats.percent, + function(d) { return d.toFixed(2); } + ]; + + $(id + ' .option').click(function(e) { + var value = parseInt($(e.target).attr('data-value')); + + console.log(optionName, value); + + if (optionName == 'domain') + value = domain = $(e.target).attr('data-value').split(',').map(function(d) { return parseInt(d); }); + else if (optionName == 'format') + value = formats[value]; + chart.option(optionName, value); + }); + }; + </script> + + + <!-- Test 6 --> + <div id="test-6" class="test"> + <h2>6. Option: domain</h2> + <div class="epoch gauge-small"></div> + <p> + <button class="playback">Play</button> | + <button class="option" data-value="0,1">[0, 1]</button> + <button class="option" data-value="0,2">[0, 2]</button> + </p> + </div> + <script>$(function() { makeOptionTest(6, {}, 'domain'); });</script> + + <!-- Test 7 --> + <div id="test-7" class="test"> + <h2>7. Option: ticks</h2> + <div class="epoch gauge-small"></div> + <p> + <button class="playback">Play</button> | + <button class="option" data-value="5">5 Ticks</button> + <button class="option" data-value="10">10 Ticks</button> + <button class="option" data-value="20">20 Ticks</button> + <button class="option" data-value="40">40 Ticks</button> + </p> + </div> + <script>$(function() { makeOptionTest(7, {}, 'ticks'); });</script> + + <!-- Test 8 --> + <div id="test-8" class="test"> + <h2>8. Option: tickSize</h2> + <div class="epoch gauge-small"></div> + <p> + <button class="playback">Play</button> | + <button class="option" data-value="2">2px</button> + <button class="option" data-value="5">5px</button> + <button class="option" data-value="10">10px</button> + <button class="option" data-value="20">20px</button> + </p> + </div> + <script>$(function() { makeOptionTest(8, {}, 'tickSize'); });</script> + + <!-- Test 9 --> + <div id="test-9" class="test"> + <h2>9. Option: tickOffset</h2> + <div class="epoch gauge-small"></div> + <p> + <button class="playback">Play</button> | + <button class="option" data-value="5">5px</button> + <button class="option" data-value="10">10px</button> + <button class="option" data-value="20">20px</button> + </p> + </div> + <script>$(function() { makeOptionTest(9, {}, 'tickOffset'); });</script> + + <!-- Test 10 --> + <div id="test-10" class="test"> + <h2>10. Option: format</h2> + <div class="epoch gauge-small"></div> + <p> + <button class="playback">Play</button> | + <button class="option" data-value="0">Percent</button> + <button class="option" data-value="1">Normal</button> + </p> + </div> + <script>$(function() { makeOptionTest(10, {}, 'format'); });</script> + </body> +</html> diff --git a/debian/missing-sources/epoch/tests/render/real-time/heatmap.html b/debian/missing-sources/epoch/tests/render/real-time/heatmap.html new file mode 100644 index 0000000..ddabe2e --- /dev/null +++ b/debian/missing-sources/epoch/tests/render/real-time/heatmap.html @@ -0,0 +1,559 @@ +<!DOCTYPE html> +<html> + <head> + <link rel="stylesheet" type="text/css" href="../css/tests.css"> + <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script> + <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> + <script src="../../../dist/js/epoch.js"></script> + <script src="../js/data.js"></script> + <link rel="stylesheet" type="text/css" href="../../../dist/css/epoch.css"> + </head> + <body> + <h1>Real-time Heatmap Plot Test</h1> + <p class="breadcrumbs"><a href="../index.html">Epoch Chart Tests</a> » Real-time Heatmap</p> + <ol> + <li><a href="#test-1">Single Series - Normal</a></li> + <li><a href="#test-2">Single Series - Beta</a></li> + <li><a href="#test-3">Single Series - Normal, Single Update</a></li> + <li><a href="#test-4">Single Series - Beta, Stream Update</a></li> + <li><a href="#test-5">Single Series - Color Override</a></li> + <li><a href="#test-6">Multi Series - Normal + Beta</a></li> + <li><a href="#test-7">Multi Series Color Override</a></li> + <li><a href="#test-8">Range Independent Bucketing</a></li> + <li><a href="#test-9">Option: buckets</a></li> + <li><a href="#test-10">Option: bucketRange</a></li> + <li><a href="#test-11">Option: bucketPadding</a></li> + <li><a href="#test-12">Option: cutOutliers</a></li> + <li><a href="#test-13">Option: opacity</a></li> + <li><a href="#test-14">Show/Hide Layer</a></li> + </ol> + + <!-- Test 1 --> + <div id="test-1" class="test"> + <h2>1. Single Series - Normal</h2> + <p>Select random values from the normal distribution and display them with the heatmap.</p> + <div class="epoch"></div> + </div> + <script> + $(function() { + var data = new NormalData(1); + $('#test-1 .epoch').epoch({ + type: 'time.heatmap', + data: data.history(120), + windowSize: 120, + buckets: 20 + }); + }); + </script> + + <!-- Test 2 --> + <div id="test-2" class="test"> + <h2>2. Single Series - Beta </h2> + <p>Select random values from the Beta(2, 5) distribution and display them with the heatmap.</p> + <div class="epoch"></div> + </div> + <script> + $(function() { + var data = new BetaData(2, 5, 1); + $('#test-2 .epoch').epoch({ + type: 'time.heatmap', + data: data.history(120), + windowSize: 120, + buckets: 20 + }); + }); + </script> + + <!-- Test 3 --> + <div id="test-3" class="test"> + <h2>3. Single Sieres - Normal, Single Update</h2> + <p> + Plot the normal distribution and transition a new element when the button is pressed. + </p> + <div class="epoch"></div> + <p><button>Next</button></p> + </div> + <script> + $(function() { + var data = new NormalData(1); + var chart = $('#test-3 .epoch').epoch({ + type: 'time.heatmap', + data: data.history(120), + windowSize: 120, + buckets: 20 + }); + $('#test-3 button').on('click', function(e) { + chart.push(data.next()); + }); + }); + </script> + + <!-- Test 4 --> + <div id="test-4" class="test"> + <h2>4. Single Series - Beta, Stream Update</h2> + <p> + Plot the Beta(2, 5) distribution and begin streaming new elements each second once the + button is pressed. + </p> + <div class="epoch"></div> + <button>Play</button> + </div> + + <script> + $(function() { + var data = new BetaData(2, 5, 1), + interval = null; + var chart = $('#test-4 .epoch').epoch({ + type: 'time.heatmap', + data: data.history(120), + windowSize: 120, + buckets: 20 + }); + $('#test-4 button').on('click', function(e) { + if (interval === null) { + chart.push(data.next()); + interval = setInterval(function() { + chart.push(data.next()); + }, 1000); + $(e.target).text('Pause'); + } + else { + clearInterval(interval); + interval = null; + $(e.target).text('Play'); + } + }); + }); + </script> + + <!-- Test 5 --> + <div id="test-5" class="test"> + <h2>5. Single Series - Color Override</h2> + <p>Change the bucket base color to orange and plot the Beta(2, 2) distribution.</p> + <div id="test-5-plot" class="epoch"></div> + </div> + + <style> + #test-5-plot .a rect.bucket { fill: darkorange; } + </style> + + <script> + $(function() { + var data = new BetaData(2, 2, 1); + $('#test-5 .epoch').epoch({ + type: 'time.heatmap', + data: data.history(120), + windowSize: 120, + buckets: 20 + }); + }); + </script> + + <!-- Test 6 --> + <div id="test-6" class="test"> + <h2>6. Multi Series - Normal + Beta</h2> + <p> + Plot the Beta(2,5) and Normal distributions as two layers in a single heatmap. Stream elements to the plot + by pressing the button. The normal distribution layer is in blue and the beta in green. + </p> + <div class="epoch"></div> + <button>Play</button> + </div> + <script> + $(function() { + var normal = new NormalData(1), + normalData = normal.history(120)[0], + beta = new BetaData(2, 5, 1), + betaData = beta.history(120)[0], + data = [normalData, betaData], + interval = null; + + var chart = $('#test-6 .epoch').epoch({ + type: 'time.heatmap', + data: data, + windowSize: 120, + buckets: 20 + }); + + function pushNext() { + chart.push([normal.next()[0], beta.next()[0]]); + } + + $('#test-6 button').on('click', function(e) { + if (interval == null) { + pushNext(); + interval = setInterval(pushNext, 1000); + $(e.target).text('Pause'); + } + else { + clearInterval(interval); + interval = null; + $(e.target).text('Play'); + } + }); + }); + </script> + + <!-- Test 7 --> + <div id="test-7" class="test"> + <h2>7. Multi Series Color Override</h2> + <p> + Plot the normal distribution and the Beta(2, 5) distribution overrding normal to be in red, and beta to + be in purple. + </p> + <div id="test-7-plot" class="epoch"></div> + </div> + + <style> + #test-7-plot .a rect.bucket { fill: red; } + #test-7-plot .b rect.bucket { fill: purple; } + </style> + + <script> + $(function() { + var normal = new NormalData(1), + normalData = normal.history(120)[0], + beta = new BetaData(2, 5, 1), + betaData = beta.history(120)[0], + interval = null; + + normalData.label = 'A'; + betaData.label = 'B'; + + var chart = $('#test-7 .epoch').epoch({ + type: 'time.heatmap', + data: [normalData, betaData], + windowSize: 120, + buckets: 20 + }); + }); + </script> + + <!-- Test 8 --> + <div id="test-8" class="test"> + <h2>8. Range Independent Bucketing (<a href="https://github.com/fastly/epoch/issues/41">Issue #41</a>)</h2> + <p> + Discrete bucketing of sparse histogram values should produce similar looking graphs regardless + of numeric relation between the range of the plot and the number of buckets. + </p> + <h3>Range [0, 100] with 20 buckets</h3> + <div class="chart1 epoch"></div> + + <h3>Range [0, 100] with 45 buckets</h3> + <div class="chart2 epoch"></div> + </div> + <script> + $(function() { + var normal = new NormalData(1), + data = normal.history(120); + + console.log(data); + + $('#test-8 .chart1').epoch({ + type: 'time.heatmap', + data: data, + windowSize: 120, + buckets: 20 + }); + $('#test-8 .chart2').epoch({ + type: 'time.heatmap', + data: data, + windowSize: 120, + buckets: 45 + }); + }); + </script> + + <!-- Test 9 --> + <div id="test-9" class="test"> + <h2>9. Option: buckets</h2> + <div class="epoch"></div> + <p> + <button class="playback">Play</button> | + <button class="buckets" data-buckets="10">10 Buckets</button> + <button class="buckets" data-buckets="20">20 Buckets</button> + <button class="buckets" data-buckets="40">40 Buckets</button> + </p> + </div> + <script> + $(function() { + var beta = new BetaData(2, 5, 1), + data = beta.history(120); + + var chart = $('#test-9 .epoch').epoch({ + type: 'time.heatmap', + data: data, + buckets: 10 + }); + + var interval = null, + pushPoint = function () { chart.push(beta.next()); }; + + $('#test-9 .playback').click(function(e) { + if (interval == null) { + $(e.target).text('Pause'); + pushPoint(); + interval = setInterval(pushPoint, 1000); + } + else { + $(e.target).text('Play'); + clearInterval(interval); + interval = null; + } + }); + + $('#test-9 .buckets').click(function(e) { + var buckets = parseInt($(e.target).attr('data-buckets')); + chart.option('buckets', buckets); + }); + }); + </script> + + <!-- Test 10 --> + <div id="test-10" class="test"> + <h2>10. Option: bucketRange</h2> + <div class="epoch"></div> + <p> + <button class="playback">Play</button> | + <button class="bucketRange" data-bucket-range="0,100">[0, 100]</button> + <button class="bucketRange" data-bucket-range="0,75">[0, 75]</button> + <button class="bucketRange" data-bucket-range="0,50">[0, 50]</button> + <button class="bucketRange" data-bucket-range="0,25">[0, 25]</button> + <button class="bucketRange" data-bucket-range="10,75">[10,75]</button> + </p> + </div> + <script> + $(function() { + var beta = window.beta = new BetaData(2, 5, 1), + data = beta.history(120); + + var chart = $('#test-10 .epoch').epoch({ + type: 'time.heatmap', + data: data, + axes: ['left', 'right'], + buckets: 20, + windowSize: 100, + cutOutliers: true + }); + + var interval = null, + pushPoint = function () { chart.push(beta.next()); }; + + $('#test-10 .playback').click(function(e) { + if (interval == null) { + $(e.target).text('Pause'); + pushPoint(); + interval = setInterval(pushPoint, 1000); + } + else { + $(e.target).text('Play'); + clearInterval(interval); + interval = null; + } + }); + + $('#test-10 .bucketRange').click(function(e) { + var bucketRange = $(e.target).attr('data-bucket-range') + .split(',').map(function(d) { return parseInt(d); }); + chart.option('bucketRange', bucketRange); + }); + }); + </script> + + <!-- Test 11 --> + <div id="test-11" class="test"> + <h2>11. Option: bucketPadding</h2> + <div class="epoch"></div> + <p> + <button class="playback">Play</button> | + <button class="bucketPadding" data-bucket-padding="0">Padding: 0</button> + <button class="bucketPadding" data-bucket-padding="2">Padding: 2</button> + <button class="bucketPadding" data-bucket-padding="4">Padding: 4</button> + <button class="bucketPadding" data-bucket-padding="8">Padding: 8</button> + </p> + </div> + <script> + $(function() { + var beta = window.beta = new BetaData(2, 5, 1), + data = beta.history(120); + + var chart = $('#test-11 .epoch').epoch({ + type: 'time.heatmap', + data: data, + axes: [], + buckets: 15, + windowSize: 100 + }); + + var interval = null, + pushPoint = function () { chart.push(beta.next()); }; + + $('#test-11 .playback').click(function(e) { + if (interval == null) { + $(e.target).text('Pause'); + pushPoint(); + interval = setInterval(pushPoint, 1000); + } + else { + $(e.target).text('Play'); + clearInterval(interval); + interval = null; + } + }); + + $('#test-11 .bucketPadding').click(function(e) { + var bucketPadding = parseInt($(e.target).attr('data-bucket-padding')); + console.log(bucketPadding) + chart.option('bucketPadding', bucketPadding); + }); + }); + </script> + + <!-- Test 12 --> + <div id="test-12" class="test"> + <h2>12. Option: cutOutliers</h2> + <div class="epoch"></div> + <p> + <button class="playback">Play</button> | + <button class="cutOutliers" data-value="false">Keep Outliers</button> + <button class="cutOutliers" data-value="true">Cut Outliers</button> + </p> + </div> + <script> + $(function() { + var beta = window.beta = new BetaData(2, 5, 1), + data = beta.history(120); + + var chart = $('#test-12 .epoch').epoch({ + type: 'time.heatmap', + data: data, + axes: ['left', 'right'], + bucketRange: [0, 25], + buckets: 15, + windowSize: 100 + }); + + var interval = null, + pushPoint = function () { chart.push(beta.next()); }; + + $('#test-12 .playback').click(function(e) { + if (interval == null) { + $(e.target).text('Pause'); + pushPoint(); + interval = setInterval(pushPoint, 1000); + } + else { + $(e.target).text('Play'); + clearInterval(interval); + interval = null; + } + }); + + $('#test-12 .cutOutliers').click(function(e) { + var cutOutliers = $(e.target).attr('data-value') == "true" ? true : false; + chart.option('cutOutliers', cutOutliers); + }); + }); + </script> + + <div id="test-13" class="test"> + <h2>13. Option: opacity</h2> + <div class="epoch"></div> + <p> + <button class="playback">Play</button> | + <select class="opacity"> + <option value="root">√n</option> + <option value="linear" selected>n</option> + <option value="quadratic">n<sup>2</sup></option> + <option value="cubic">n<sup>3</sup></option> + <option value="quartic">n<sup>4</sup></option> + <option value="quintic">n<sup>5</sup></option> + </select> + </p> + </div> + <script> + $(function() { + var beta = window.beta = new BetaData(2, 5, 1), + data = beta.history(120); + + var chart = $('#test-13 .epoch').epoch({ + type: 'time.heatmap', + data: data, + axes: [], + buckets: 20, + windowSize: 100 + }); + + var interval = null, + pushPoint = function () { chart.push(beta.next()); }; + + $('#test-13 .playback').click(function(e) { + if (interval == null) { + $(e.target).text('Pause'); + pushPoint(); + interval = setInterval(pushPoint, 1000); + } + else { + $(e.target).text('Play'); + clearInterval(interval); + interval = null; + } + }); + + $('#test-13 select.opacity').on('change', function(e) { + var opacity = $('#test-13 select.opacity').val(); + chart.option('opacity', opacity); + }); + }); + </script> + + <div id="test-14" class="test"> + <h2>14. Show/Hide Layer</h2> + <div class="epoch"></div> + <p> + <button class="toggle" data-index="0">Toggle A</button> + <button class="toggle" data-index="1">Toggle B</button> | + <button class="playback">Play</button> + </p> + </div> + <script> + $(function() { + var normal = new NormalData(1), + normalData = normal.history(120)[0], + beta = new BetaData(2, 5, 1), + betaData = beta.history(120)[0], + data = [normalData, betaData], + interval = null; + + var chart = $('#test-14 .epoch').epoch({ + type: 'time.heatmap', + data: data, + windowSize: 120, + buckets: 20 + }); + + $('#test-14 .toggle').click(function(e) { + var index = parseInt($(e.target).attr('data-index')); + chart.toggleLayer(index); + }); + + function pushNext() { + chart.push([normal.next()[0], beta.next()[0]]); + } + + $('#test-14 .playback').on('click', function(e) { + if (interval == null) { + pushNext(); + interval = setInterval(pushNext, 1000); + $(e.target).text('Pause'); + } + else { + clearInterval(interval); + interval = null; + $(e.target).text('Play'); + } + }); + }); + </script> + </body> +</html> diff --git a/debian/missing-sources/epoch/tests/render/real-time/line.html b/debian/missing-sources/epoch/tests/render/real-time/line.html new file mode 100644 index 0000000..3346c98 --- /dev/null +++ b/debian/missing-sources/epoch/tests/render/real-time/line.html @@ -0,0 +1,596 @@ +<!DOCTYPE html> +<html> + <head> + <link rel="stylesheet" type="text/css" href="../css/tests.css"> + <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script> + <script src="http://d3js.org/d3.v3.js" charset="utf-8"></script> + <script src="../../../dist/js/epoch.js"></script> + <link rel="stylesheet" type="text/css" href="../../../dist/css/epoch.css"> + + <script> + var nextTime = (function() { + var currentTime = parseInt(new Date().getTime() / 1000); + return function() { return currentTime++; } + })(); + </script> + </head> + <body> + <h1>Real-time Line Plot Test</h1> + <p class="breadcrumbs"><a href="../index.html">Epoch Chart Tests</a> » Real-time Line</p> + <ol> + <li><a href="#test-1">Single Series</a></li> + <li><a href="#test-2">Single Series Single Transition</a></li> + <li><a href="#test-3">Single Seires Stream</a></li> + <li><a href="#test-4">Multi Series</a></li> + <li><a href="#test-5">Multi Series Single Transition</a></li> + <li><a href="#test-6">Multi Seires Stream</a></li> + <li><a href="#test-7">Color Override</a></li> + <li><a href="#test-8">Categorical Colors</a></li> + <li><a href="#test-9">Show/Hide Layers</a></li> + <li><a href="#test-10">Show/Hide Layers</a></li> + <li><a href="#test-11">Multiaxes</a></li> + </ol> + + <!-- Test 1 --> + <div id="test-1" class="test"> + <h2>1. Single Series</h2> + <p> + Correctly render a single series plot of <code>y = cos(x) + 1</code> over the range <code>[0, 2π]</code>. + </p> + <div class="epoch"></div> + </div> + + <script> + $(function() { + var data = [{ label: 'A', values: [] }], + length = 40; + + for (var i = 0; i < length; i++) { + var x = i * 2 * Math.PI / length, + y = Math.cos(x) + 1, + time = nextTime(); + data[0].values.push({time: time, y: y}); + } + + $('#test-1 .epoch').epoch({ type: 'time.line', data: data }); + }); + </script> + + <!-- Test 2 --> + <div id="test-2" class="test"> + <h2>2. Single Series Single Transition</h2> + <p> + Correctly render a single series plot of <code>y = sin(x) + 1</code>. When the button is pressed push a new data point to the chart and correctly animate/render the transiton. + </p> + <div class="epoch"></div> + <p><button>Next</button></p> + </div> + + <script> + $(function() { + var data = [{ label: 'A', values: [] }], + length = 40, + nextIndex = length; + + for (var i = 0; i < length; i++) { + var x = i * 2 * Math.PI / length, + y = Math.sin(x) + 1, + time = nextTime(); + data[0].values.push({time: time, y: y}); + } + + var chart = $('#test-2 .epoch').epoch({ + type: 'time.line', + data: data + }); + + $('#test-2 button').on('click', function(e) { + var x = nextIndex * 2 * Math.PI / length, + y = Math.sin(x) + 1, + time = nextTime(); + nextIndex++; + chart.push([{ time: time, y: y }]); + }); + }); + </script> + + + <!-- Test 3 --> + <div id="test-3" class="test"> + <h2>3. Single Seires Stream</h2> + <p>Correctly play / pause a single series stream of values from the plot <code>y = cos(x) + 1</code>.</p> + <div class="epoch"></div> + <p><button>Play</button></p> + </div> + + <script> + $(function() { + var data = [{ label: 'A', values: [] }], + length = 40, + nextIndex = length, + playing = false, + interval = null; + + for (var i = 0; i < length; i++) { + var x = i * 2 * Math.PI / length, + y = Math.cos(x) + 1, + time = nextTime(); + data[0].values.push({time: time, y: y}); + } + + var chart = $('#test-3 .epoch').epoch({ + type: 'time.line', + data: data + }); + + var pushPoint = function() { + var x = nextIndex * 2 * Math.PI / length, + y = Math.cos(x) + 1, + time = nextTime(); + chart.push([{ time: time, y: y}]); + nextIndex++; + }; + + $('#test-3 button').on('click', function(e) { + if (playing) { + $(e.target).text('Play'); + clearInterval(interval); + } + else { + $(e.target).text('Pause'); + pushPoint(); + interval = setInterval(pushPoint, 1000); + } + playing = !playing; + }); + }); + </script> + + <!-- Test 4 --> + <div id="test-4" class="test"> + <h2>4. Multi Series</h2> + <p>Correctly render a multi series plot of the following functions:</p> + <ul> + <li><code>y = log(x+1)</code></li> + <li><code>y = 2*log(x+1)</code></li> + <li><code>y = 3*log(x+1)</code></li> + </ul> + <div class="epoch"></div> + </div> + + <script> + $(function() { + var data = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []} + ], + length = 40; + + for (var i = 0; i < length; i++) { + var x = i + 1, + time = nextTime(); + for (var j = 0; j < data.length; j++) { + data[j].values.push({time: time, y: (j+1) * Math.log(x)}); + } + } + + $('#test-4 .epoch').epoch({ + type: 'time.line', + data: data, + axes: ['left', 'right', 'bottom'] + }); + }); + </script> + + + <!-- Test 5 --> + <div id="test-5" class="test"> + <h2>5. Multi Series Single Transition</h2> + <p> + Correctly render a multi series plot of the following functions: + <ul> + <li><code>y = x</code></li> + <li><code>y = x<sup>1.25</sup></code></li> + <li><code>y = x<sup>1.5</sup></code></li> + </ul> + and correctly render/animate the transiton after pressing the "Next" button. + </p> + <div class="epoch"></div> + <p> + <button>Next</button> + </p> + </div> + + <script> + $(function() { + var data = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []} + ], + length = 40, + scale = 0.1, + nextIndex = length; + + for (var i = 0; i < length; i++) { + var x = i * scale, + time = nextTime(); + data[0].values.push({time: time, y: x}); + data[1].values.push({time: time, y: Math.pow(x, 1.25)}); + data[2].values.push({time: time, y: Math.pow(x, 1.5)}); + } + + var chart = $('#test-5 .epoch').epoch({ + type: 'time.line', + data: data, + axes: ['left', 'right', 'bottom'] + }); + + $('#test-5 button').on('click', function(e) { + var x = nextIndex * scale, + time = nextTime(); + nextIndex++; + chart.push([ + {time: time, y: x}, + {time: time, y: Math.pow(x, 1.25)}, + {time: time, y: Math.pow(x, 1.5)} + ]); + }); + }); + </script> + + <!-- Test 6 --> + <div id="test-6" class="test"> + <h2>6. Multi Seires Stream</h2> + <p> + Correctly play / pause a multi series stream of values over the following plots: + <ul> + <li><code>x</code></li> + <li><code>x * log(x)</code></li> + <li><code>x * log<sup>2</sup>(x)</code></li> + </ul> + </p> + <div class="epoch"></div> + <p><button>Play</button></p> + </div> + + <script> + $(function() { + var data = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []} + ], + length = 40, + nextIndex = length, + scale = 0.1, + playing = false, + interval = null; + + for (var i = 0; i < length; i++) { + var x = (i+1) * scale, + time = nextTime(); + data[0].values.push({time: time, y: x}); + data[1].values.push({time: time, y: x * Math.log(x)}); + data[2].values.push({time: time, y: x * Math.pow(Math.log(x), 2)}); + } + + var chart = $('#test-6 .epoch').epoch({ + type: 'time.line', + data: data, + axes: ['right', 'bottom'] + }); + + var pushPoint = function() { + var x = (nextIndex +1) * scale, + time = nextTime(); + chart.push([ + { time: time, y: x}, + { time: time, y: x * Math.log(x)}, + { time: time, y: x * Math.pow(Math.log(x), 2)} + ]); + nextIndex++; + }; + + $('#test-6 button').on('click', function(e) { + if (playing) { + $(e.target).text('Play'); + clearInterval(interval); + } + else { + $(e.target).text('Pause'); + interval = setInterval(pushPoint, 1000); + pushPoint(); + } + playing = !playing; + }); + }); + </script> + + <!-- Test 7 --> + <div id="test-7" class="test"> + <h2>7. Color Override</h2> + <p>The first layer should pink, the second green, and the third blue.</p> + <div id="test-7-plot" class="epoch"></div> + </div> + + <style> + #test-7-plot .a .line { stroke: pink; } + #test-7-plot .b .line { stroke: green; } + #test-7-plot .c .line { stroke: blue; } + </style> + + <script> + $(function() { + var data = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []} + ], + length = 40; + + for (var i = 0; i < length; i++) { + var x = i + 1, + time = nextTime(); + for (var j = 0; j < data.length; j++) { + data[j].values.push({time: time, y: (j+1) * Math.log(x)}); + } + } + + $('#test-7 .epoch').epoch({ + type: 'time.line', + data: data + }); + }); + </script> + + <!-- Test 8 --> + <div id="test-8" class="test"> + <h2>8. Categorical Colors</h2> + <p>Correctly render and switch between different categorical colors. Unlike with the basic charts this doesn't occur just by switching the class name on the containing element. The CSS query engine must be purge and the plot must be manually redrawn, like so:<code><pre> +Epoch.QueryCSS.purge(); +chart.draw(); +</pre></code> + </p> + + <div class="epoch category10"></div> + + <p> + <button data-class="category10">category10</button> + <button data-class="category20">category20</button> + <button data-class="category20b">category20b</button> + <button data-class="category20c">category20c</button> + </p> + </div> + + <script> + $(function() { + var data = [], + length = 41, + className = "category10", + layers = 8, + time = nextTime(); + + for (var i = 0; i < layers; i++) { + var layer = { label: String.fromCharCode(i+65), values: [] }; + for (var j = 0; j < length; j++) { + var x = j + 1, + y = Math.pow(x, i * 0.02); + layer.values.push({ time: time + j, y: y }); + } + data.push(layer); + } + + var chart = $('#test-8 .epoch').epoch({ type: 'time.line', data: data }); + + $('#test-8 button').on('click', function(e) { + $('#test-8 .epoch').removeClass(className); + className = $(e.target).attr('data-class'); + $('#test-8 .epoch').addClass(className); + Epoch.QueryCSS.purge(); + chart.draw(); + }); + }); + </script> + + <!-- Test 9 --> + <div id="test-9" class="test"> + <h2>9. Show/Hide Layers</h2> + <div class="epoch"></div> + <p> + <button class="toggle" data-index="0">Toggle A</button> + <button class="toggle" data-index="1">Toggle B</button> + <button class="toggle" data-index="2">Toggle C</button> | + <button class="playback">Play</button> + </p> + </div> + <script> + $(function() { + var data = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []} + ], + length = 40, + nextIndex = length, + scale = 0.1, + playing = false, + interval = null; + + for (var i = 0; i < length; i++) { + var x = (i+1) * scale, + time = nextTime(); + data[0].values.push({time: time, y: x}); + data[1].values.push({time: time, y: x * Math.log(x)}); + data[2].values.push({time: time, y: x * Math.pow(Math.log(x), 2)}); + } + + var chart = $('#test-9 .epoch').epoch({ + type: 'time.line', + data: data, + axes: ['right', 'bottom'] + }); + + $('#test-9 .toggle').click(function(e) { + var index = parseInt($(e.target).attr('data-index')); + chart.toggleLayer(index); + }); + + var pushPoint = function() { + var x = (nextIndex +1) * scale, + time = nextTime(); + chart.push([ + { time: time, y: x}, + { time: time, y: x * Math.log(x)}, + { time: time, y: x * Math.pow(Math.log(x), 2)} + ]); + nextIndex++; + }; + + $('#test-9 .playback').on('click', function(e) { + if (playing) { + $(e.target).text('Play'); + clearInterval(interval); + } + else { + $(e.target).text('Pause'); + interval = setInterval(pushPoint, 1000); + pushPoint(); + } + playing = !playing; + }); + }); + </script> + + <!-- Test 10 --> + <div id="test-10" class="test"> + <h2>10. Fixed Range</h2> + <p> + Render a single series plot of <code>y = cos(x)</code>. Instead of automatically setting the range to <code>[-1, 1]</code>, the range is manually set to <code>[-2, 5]</code>. + </p> + <div class="epoch"></div> + </div> + + <script> + $(function() { + var data = [{ label: 'A', values: [] }], + length = 40; + + for (var i = 0; i < length; i++) { + var x = i * 2 * Math.PI / length, + y = Math.cos(x), + time = nextTime(); + data[0].values.push({time: time, y: y}); + } + + $('#test-10 .epoch').epoch({ + type: 'time.line', + axes: ['right', 'bottom'], + data: data, + range: [-2, 5] + }); + }); + </script> + + <!-- Test 11 --> + <div id="test-11" class="test"> + <h2>11. Multi-axes</h2> + <p> + Render a plot that uses independent axes ranges for the left and + right sides. + </p> + <div class="epoch"></div> + </div> + + <script> + $(function() { + var data = [ + { + label: 'A', + values: [], + range: [-5, 5] + }, + { + label: 'B', + values: [], + range: [0, 100] + } + ]; + var length = 40; + + for (var i = 0; i < length; i++) { + var x = i * 2 * Math.PI / length, + y = Math.cos(x) + x / 4, + y2 = i * 100/40, + time = nextTime(); + data[0].values.push({time: time, y: y}); + data[1].values.push({time: time, y: y2}); + } + + $('#test-11 .epoch').epoch({ + type: 'time.line', + axes: ['left', 'right'], + data: data, + range: { + right: [-5, 5], + left: [0, 100] + } + }); + }); + </script> + + <!-- Test 12 --> + <div id="test-12" class="test"> + <h2>12. Multi-axes with Labeled Ranges</h2> + <p> + Render a plot that uses independent axes ranges for the left and + right sides that are associated to labels in the chart data. + </p> + <div class="epoch"></div> + </div> + + <script> + $(function() { + var data = [ + { + label: 'A', + values: [], + range: 'range-one' + }, + { + label: 'B', + values: [], + range: 'range-two' + }, + { + label: 'C', + values: [], + range: 'range-one' + } + ]; + var length = 50; + + for (var i = 0; i < length; i++) { + var y = Math.random() * 100; + var y2 = Math.random() * 2; + var y3 = Math.random() * 50 + 75 + var time = nextTime(); + data[0].values.push({time: time, y: y}); + data[1].values.push({time: time, y: y2}); + data[2].values.push({time: time, y: y3}); + } + + $('#test-12 .epoch').epoch({ + type: 'time.line', + axes: ['left', 'right'], + data: data, + range: { + right: 'range-one', + left: 'range-two' + } + }); + }); + </script> + </body> +</html> diff --git a/debian/missing-sources/epoch/tests/render/real-time/model.html b/debian/missing-sources/epoch/tests/render/real-time/model.html new file mode 100644 index 0000000..6f53963 --- /dev/null +++ b/debian/missing-sources/epoch/tests/render/real-time/model.html @@ -0,0 +1,85 @@ +<!DOCTYPE html> +<html> + <head> + <link rel="stylesheet" type="text/css" href="../css/tests.css"> + <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script> + <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> + <script src="../../../dist/js/epoch.js"></script> + <script src="../js/data.js"></script> + <link rel="stylesheet" type="text/css" href="../../../dist/css/epoch.css"> + <style> + body { background: #333; color: #d0d0d0; } + a:link, a:visited { color: white; color: white; } + + .epoch { + height: 220px; + } + + #sparkline { height: 50px; } + + </style> + </head> + <body class="epoch-theme-dark"> + <h1>Real-time Chart Model / Data Test</h1> + <p class="breadcrumbs"><a href="../index.html">Epoch Chart Tests</a> » Real-time Chart Model / Data Test</p> + + <p><button class="next">Next</button></p> + + <div id="gauge" class="epoch gauge-small"></div> + <div id="sparkline" class="epoch"></div> + <div id="area" class="epoch"></div> + <div id="bar" class="epoch"></div> + + <script> + $(function() { + var rnd = function() { return Math.random(); }; + + var data = []; + for (var j = 0; j < 3; j++) { + var layer = []; + for (var i = 0; i < 80; i++) { + layer.push(rnd()); + } + data.push(layer); + } + + // Setup the model + window.model = new Epoch.Model({ + dataFormat: { + name: 'array', + options: { startTime: (new Date().getTime() / 1000)|0 } + } + }); + model.setData(data); + + // Make the charts and associate them with the model + window.sparkline = $('#sparkline').epoch({ + type: 'time.line', + axes: ['left', 'right'], + model: model + }); + + window.area = $('#area').epoch({ + type: 'time.area', + axes: ['left', 'right', 'bottom'], + model: model + }); + + window.bar = $('#bar').epoch({ + type: 'time.bar', + axes: ['left', 'right', 'bottom'], + model: model + }); + + window.gauge = $('#gauge').epoch({ + type: 'time.gauge', + model: model + }) + + $('button.next').click(function(e) { + model.push([rnd(), rnd(), rnd()]); + }); + }) + </script> + </body> +</html>
\ No newline at end of file diff --git a/debian/missing-sources/epoch/tests/render/real-time/options.html b/debian/missing-sources/epoch/tests/render/real-time/options.html new file mode 100644 index 0000000..a2da792 --- /dev/null +++ b/debian/missing-sources/epoch/tests/render/real-time/options.html @@ -0,0 +1,299 @@ +<!DOCTYPE html> +<html> + <head> + <link rel="stylesheet" type="text/css" href="../css/tests.css"> + <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script> + <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> + <script src="../../../dist/js/epoch.js"></script> + <script src="../js/data.js"></script> + <link rel="stylesheet" type="text/css" href="../../../dist/css/epoch.css"> + <style> + body { background: #333; color: #d0d0d0; } + a:link, a:visited { color: white; color: white; } + </style> + </head> + <body class="epoch-theme-dark"> + <h1>Real-time Chart Options and Events</h1> + <p class="breadcrumbs"><a href="../index.html">Epoch Chart Tests</a> » Real-time Options and Events</p> + <ol> + <li><a href="#test-1">Resize</a></li> + <li><a href="#test-2">Axes</a></li> + <li><a href="#test-3">Ticks</a></li> + <li><a href="#test-4">Tick Formats</a></li> + <li><a href="#test-5">Margins</a></li> + </ol> + + <!-- Test 1 --> + <div id="test-1" class="test"> + <h2>1. Resize</h2> + <p>Correctly Resize a Real-time Chart.</p> + <div class="epoch"></div> + <p> + <button class="playback">Play</button> | + <button class="size" data-index="0">Size: Small</button> + <button class="size" data-index="1">Size: Medium</button> + <button class="size" data-index="2">Size: Large</button> + </p> + </div> + <script> + $(function() { + var step = Math.PI / 30, + data = time().add(function(x) { return Math.cos(x) + 1; }), + interval = null; + + var sizes = [ + { width: 400, height: 100 }, + { width: 800, height: 150 }, + { width: $('#test-1 .epoch').width(), height: $('#test-1 .epoch').height() } + ]; + + var chart = $('#test-1 .epoch').epoch({ + type: 'time.line', + data: data.get([0, 2*Math.PI], step) + }) + + function pushPoint() { + chart.push(data.next(step)); + } + + $('#test-1 .playback').click(function(e) { + if (!interval) { + interval = setInterval(function() { pushPoint() }, 1000); + pushPoint(); + $('#test-1 .playback').text('Pause'); + } + else { + clearInterval(interval); + interval = null; + $('#test-1 .playback').text('Play'); + } + }); + + $('#test-1 .size').click(function(e) { + var size = sizes[parseInt($(e.target).attr('data-index'))]; + chart.option('width', size.width); + chart.option('height', size.height); + }); + }); + </script> + + <!-- Test 2 --> + <div id="test-2" class="test"> + <h2>2. Axes</h2> + <div class="epoch"></div> + <div class="controls"> + <button class="playback">Play</button> | + <button class="axes" data-index="0">All</button> + <button class="axes" data-index="1">[left, right]</button> + <button class="axes" data-index="2">[top, bottom]</button> + <button class="axes" data-index="3">[left, bottom]</button> + <button class="axes" data-index="4">[top, right]</button> + <button class="axes" data-index="5">None</button> + </div> + </div> + <script> + $(function() { + var step = Math.PI / 30, + data = time().add(function(x) { return Math.sin(x) + 1; }), + interval = null; + + var axes = [ + ['top', 'left', 'bottom', 'right'], + ['left', 'right'], + ['top', 'bottom'], + ['left', 'bottom'], + ['top', 'right'], + [] + ]; + + var chart = $('#test-2 .epoch').epoch({ + type: 'time.line', + data: data.get([0, 2*Math.PI], step) + }) + + function pushPoint() { + chart.push(data.next(step)); + } + + $('#test-2 .playback').click(function(e) { + if (!interval) { + interval = setInterval(function() { pushPoint() }, 1000); + pushPoint(); + $('#test-2 .playback').text('Pause'); + } + else { + clearInterval(interval); + interval = null; + $('#test-2 .playback').text('Play'); + } + }); + + $('#test-2 button.axes').click(function(e) { + chart.option('axes', axes[parseInt($(e.target).attr('data-index'))]); + }); + }); + </script> + + <!-- Test 3 --> + <div id="test-3" class="test"> + <h2>3. Ticks</h2> + <div class="epoch"></div> + <p> + <button class="playback">Play</button> | + <button class="ticks" data-index="0">Normal</button> + <button class="ticks" data-index="1">Many</button> + </p> + </div> + <script> + $(function() { + var step = Math.PI / 30, + data = time().add(function(x) { return Math.sqrt(x) * Math.sin(x) + 1; }), + interval = null; + + var ticks = [ + {time: 15, left: 5, right: 5}, + {time: 5, left: 15, right: 15} + ]; + + var chart = $('#test-3 .epoch').epoch({ + type: 'time.line', + data: data.get([0, 2*Math.PI], step), + axes: ['top', 'left', 'bottom', 'right'] + }) + + function pushPoint() { + chart.push(data.next(step)); + } + + $('#test-3 .playback').click(function(e) { + if (!interval) { + interval = setInterval(function() { pushPoint() }, 1000); + pushPoint(); + $('#test-3 .playback').text('Pause'); + } + else { + clearInterval(interval); + interval = null; + $('#test-3 .playback').text('Play'); + } + }); + + $('#test-3 .ticks').click(function(e) { + chart.option('ticks', ticks[parseInt($(e.target).attr('data-index'))]); + }); + }); + </script> + + <!-- Test 4 --> + <div id="test-4" class="test"> + <h2>4. Tick Formats</h2> + <div class="epoch"></div> + <p> + <button class="playback">Play</button> | + <button class="tickFormats" data-index="0">Normal</button> + <button class="tickFormats" data-index="1">Different</button> + </p> + </div> + <script> + $(function() { + var step = Math.PI / 30, + data = time().add(function(x) { return Math.abs(Math.sin(x)); }), + interval = null; + + var tickFormats = [ + { + top: Epoch.Formats.seconds, + bottom: Epoch.Formats.seconds, + left: Epoch.Formats.si, + right: Epoch.Formats.si + }, + { + top: Epoch.Formats.si, + bottom: Epoch.Formats.si, + left: Epoch.Formats.percent, + right: Epoch.Formats.percent + } + ]; + + var chart = $('#test-4 .epoch').epoch({ + type: 'time.area', + data: data.get([0, 2*Math.PI], step), + axes: ['top', 'left', 'bottom', 'right'] + }) + + function pushPoint() { + chart.push(data.next(step)); + } + + $('#test-4 .playback').click(function(e) { + if (!interval) { + interval = setInterval(function() { pushPoint() }, 1000); + pushPoint(); + $('#test-4 .playback').text('Pause'); + } + else { + clearInterval(interval); + interval = null; + $('#test-4 .playback').text('Play'); + } + }); + + $('#test-4 .tickFormats').click(function(e) { + chart.option('tickFormats', tickFormats[parseInt($(e.target).attr('data-index'))]); + }); + }); + </script> + + <!-- Test 5 --> + <div id="test-5" class="test"> + <h2>5. Margins</h2> + <div class="epoch"></div> + <p> + <button class="playback">Play</button> | + <button class="margins" data-index="0">None</button> + <button class="margins" data-index="1">Small</button> + <button class="margins" data-index="2">Big</button> + </p> + </div> + <script> + $(function() { + var step = Math.PI / 30, + data = time().add(function(x) { return 1 - Math.abs(Math.cos(x)); }), + interval = null; + + var margins = [ + { top: 6, bottom: 6, left: 6, right: 6 }, + { top: 20, bottom: 20, left: 20, right: 20 }, + { top: 100, bottom: 50, left: 50, right: 50 } + ]; + + var chart = $('#test-5 .epoch').epoch({ + type: 'time.area', + data: data.get([0, 2*Math.PI], step), + axes: [] + }) + + function pushPoint() { + chart.push(data.next(step)); + } + + $('#test-5 .playback').click(function(e) { + if (!interval) { + interval = setInterval(function() { pushPoint() }, 1000); + pushPoint(); + $('#test-5 .playback').text('Pause'); + } + else { + clearInterval(interval); + interval = null; + $('#test-5 .playback').text('Play'); + } + }); + + $('#test-5 .margins').click(function(e) { + chart.option('margins', margins[parseInt($(e.target).attr('data-index'))]); + }); + }); + </script> + </body> +</html>
\ No newline at end of file diff --git a/debian/missing-sources/epoch/tests/render/themes/dark.html b/debian/missing-sources/epoch/tests/render/themes/dark.html new file mode 100644 index 0000000..265ad93 --- /dev/null +++ b/debian/missing-sources/epoch/tests/render/themes/dark.html @@ -0,0 +1,211 @@ +<html> + <head> + <link rel="stylesheet" type="text/css" href="../css/tests.css"> + <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script> + <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> + <script src="../../../dist/js/epoch.js"></script> + <link rel="stylesheet" type="text/css" href="../../../dist/css/epoch.css"> + <style> + body { + background: #303030; + color: #d0d0d0; + } + + .epoch:after { + content:""; + display:table; + clear:both; + } + + .ref { + width: 120px; + height: 120px; + float: left; + margin: 2px; + color: #303030; + text-align: center; + line-height: 120px; + font-size: 31px; + } + </style> + + <script> + var data = [ + { values: [] }, + { values: [] }, + { values: [] }, + { values: [] }, + { values: [] }, + { values: [] }, + { values: [] }, + { values: [] }, + { values: [] }, + { values: [] }, + { values: [] }, + { values: [] }, + { values: [] }, + { values: [] }, + { values: [] }, + { values: [] }, + { values: [] }, + { values: [] }, + { values: [] }, + { values: [] } + ]; + for (var i = 0; i < 60; i++) { + for (j = 0; j < data.length; j++) { + data[j].values.push({time: i, y: 0.05*i + 0.5}) + } + } + </script> + </head> + <body class="epoch-theme-dark"> + <h1>Dark Theme</h1> + + <h2>Category 10</h2> + <div class="epoch category10" data-refs-range="1,5"></div> + <div class="epoch category10" data-refs-range="6,10"></div> + + <br> + + <div id="bar-10" class="epoch category10" style="height: 300px"></div> + <script> + $(function() { + $('#bar-10').epoch({ type: 'time.bar', axes: ['left', 'right'], data: data.slice(0, 10) }); + }); + </script> + + + + <h2>Category 20</h2> + <div class="epoch category20" data-refs-range="1,4"></div> + <div class="epoch category20" data-refs-range="5,8"></div> + <div class="epoch category20" data-refs-range="9,12"></div> + <div class="epoch category20" data-refs-range="13,16"></div> + <div class="epoch category20" data-refs-range="17,20"></div> + + <br> + + <div id="bar-20" class="epoch category20" style="height: 300px"></div> + <script> + $(function() { + $('#bar-20').epoch({ type: 'time.bar', axes: ['left', 'right'], data: data }); + }); + </script> + + + <h2>Category 20b</h2> + <div class="epoch category20b" data-refs-range="1,4"></div> + <div class="epoch category20b" data-refs-range="5,8"></div> + <div class="epoch category20b" data-refs-range="9,12"></div> + <div class="epoch category20b" data-refs-range="13,16"></div> + <div class="epoch category20b" data-refs-range="17,20"></div> + + <br> + + <div id="bar-20b" class="epoch category20b" style="height: 300px"></div> + <script> + $(function() { + $('#bar-20b').epoch({ type: 'time.bar', axes: ['left', 'right'], data: data }); + }); + </script> + + + <h2>Category 20c</h2> + <div class="epoch category20c" data-refs-range="1,4"></div> + <div class="epoch category20c" data-refs-range="5,8"></div> + <div class="epoch category20c" data-refs-range="9,12"></div> + <div class="epoch category20c" data-refs-range="13,16"></div> + <div class="epoch category20c" data-refs-range="17,20"></div> + + <br> + + <div id="bar-20c" class="epoch category20c" style="height: 300px"></div> + <script> + $(function() { + $('#bar-20c').epoch({ type: 'time.bar', axes: ['left', 'right'], data: data }); + }); + </script> + + <h2>Chart Examples</h2> + + <div class="epoch gauge-medium"></div> + <script> + $(function() { + $('.gauge-medium').epoch({ type: 'time.gauge', value: 0.25 }); + }); + </script> + + <div id="pie" class="epoch" style="width: 300px; height: 300px;"></div> + <script> + $(function() { + var data = [], + className = 'category10'; + + for (var j = 0; j < 10; j++) { + data.push({label: String.fromCharCode(65+j), value: 10}); + } + + $('#pie').epoch({ + type: 'pie', + inner: 80, + data: data + }); + }); + </script> + + + <div id="bar" class="epoch" style="height: 220px"></div> + <script> + $(function(){ + var data = [ + {label: 'A', values: []}, + {label: 'B', values: []}, + {label: 'C', values: []} + ], + length = 10; + + for (var i = 0; i < length; i++) { + var x = i * 10 / length; + data[0].values.push({x: x, y: x}); + data[1].values.push({x: x, y: 2*x}); + data[2].values.push({x: x, y: 3*x}); + } + + $('#bar').epoch({ + type: 'bar', + data: data + }); + }); + </script> + + <div id="line" class="epoch" style="height: 220px"></div> + <script> + $(function() { + var data = [ + { label: 'A', values: [] }, + { label: 'B', values: [] } + ], + length = 128; + for (var i = 0; i < length; i++) { + var x = i * 2 * Math.PI / length; + data[0].values.push({x: x, y: x*Math.sin(x)}); + data[1].values.push({x: x, y: x*Math.cos(x)}); + } + $('#line').epoch({type: 'line', data: data}); + }); + </script> + + + <script> + $(function() { + $('div[data-refs-range]').each(function(i, el) { + var range = $(el).attr('data-refs-range').split(','); + for (var i = parseInt(range[0]); i <= parseInt(range[1]); i++) { + $(el).append('<div class="ref category'+i+'">'+i+'</div>') + } + }); + }); + </script> + </body> +</html>
\ No newline at end of file diff --git a/debian/missing-sources/epoch/tests/render/themes/default.html b/debian/missing-sources/epoch/tests/render/themes/default.html new file mode 100644 index 0000000..9e8bd4e --- /dev/null +++ b/debian/missing-sources/epoch/tests/render/themes/default.html @@ -0,0 +1,70 @@ +<html> + <head> + <link rel="stylesheet" type="text/css" href="../css/tests.css"> + <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script> + <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> + <script src="../../../dist/js/epoch.js"></script> + <link rel="stylesheet" type="text/css" href="../../../dist/css/epoch.css"> + <style> + + .epoch:after { + content:""; + display:table; + clear:both; + } + + .ref { + width: 120px; + height: 120px; + float: left; + margin: 2px; + color: white; + text-align: center; + line-height: 120px; + font-size: 31px; + } + </style> + + <script> + $(function() { + $('div[data-refs-range]').each(function(i, el) { + var range = $(el).attr('data-refs-range').split(','); + for (var i = parseInt(range[0]); i <= parseInt(range[1]); i++) { + $(el).append('<div class="ref category'+i+'">'+i+'</div>') + } + }); + }); + </script> + + </head> + <body> + <h1>Default Theme</h1> + + <h2>Category 10</h2> + <div class="epoch category10" data-refs-range="1,5"></div> + <div class="epoch category10" data-refs-range="6,10"></div> + + <h2>Category 20</h2> + <div class="epoch category20" data-refs-range="1,4"></div> + <div class="epoch category20" data-refs-range="5,8"></div> + <div class="epoch category20" data-refs-range="9,12"></div> + <div class="epoch category20" data-refs-range="13,16"></div> + <div class="epoch category20" data-refs-range="17,20"></div> + + <h2>Category 20b</h2> + <div class="epoch category20b" data-refs-range="1,4"></div> + <div class="epoch category20b" data-refs-range="5,8"></div> + <div class="epoch category20b" data-refs-range="9,12"></div> + <div class="epoch category20b" data-refs-range="13,16"></div> + <div class="epoch category20b" data-refs-range="17,20"></div> + + <h2>Category 20c</h2> + <div class="epoch category20c" data-refs-range="1,4"></div> + <div class="epoch category20c" data-refs-range="5,8"></div> + <div class="epoch category20c" data-refs-range="9,12"></div> + <div class="epoch category20c" data-refs-range="13,16"></div> + <div class="epoch category20c" data-refs-range="17,20"></div> + + + </body> +</html>
\ No newline at end of file diff --git a/debian/missing-sources/epoch/tests/unit/core/charts.coffee b/debian/missing-sources/epoch/tests/unit/core/charts.coffee new file mode 100644 index 0000000..0f0bb9c --- /dev/null +++ b/debian/missing-sources/epoch/tests/unit/core/charts.coffee @@ -0,0 +1,573 @@ +sinon = require 'sinon' + +describe 'Epoch.Chart', -> + [defaultWidth, defaultHeight] = [320, 240] + + describe 'Base', -> + [testDivWidth, testDivHeight] = [800, 200] + [resizeDivWidth, resizeDivHeight] = [200, 200] + + before (done) -> + d3.select(doc.body).append('div').attr('id', 'testDiv').style + width: "#{testDivWidth}px" + height: "#{testDivHeight}px" + + d3.select(doc.body).append('div').attr('id', 'resizeDiv').style + width: "#{resizeDivWidth}px" + height: "#{resizeDivHeight}px" + + done() + + after (done) -> + d3.select('#testDiv').remove() + d3.select('#resizeDiv').remove() + done() + + describe 'constructor', -> + it 'should set default dimensions', -> + c = new Epoch.Chart.Base() + assert.equal c.width, defaultWidth, 'Did not set default width' + assert.equal c.height, defaultHeight, 'Did not set default height' + + it 'should allow dimensions to be set via options', -> + [width, height] = [500, 780] + c = new Epoch.Chart.Base({ width: width, height: height }) + assert.equal c.width, width, "Did not set width to #{width}" + assert.equal c.height, height, "Did not set height to #{height}" + + it 'should use the dimensions of the given element when applicable', -> + c = new Epoch.Chart.Base({ el: '#testDiv' }) + assert.equal c.width, testDivWidth, "Did not set width to that of the div" + assert.equal c.height, testDivHeight, "Did not set height to that of the div" + + it 'should set default data to an empty array', -> + c = new Epoch.Chart.Base() + assert.isArray c.data + assert.equal c.data.length, 0 + + it 'should set data when given as an option', -> + data = [ + {label: 'A', values: [{x: 0, y: 0}]}, + {label: 'B', values: [{x: 1, y: 1}]} + ] + c = new Epoch.Chart.Base({ data: data }) + assert.sameMembers(c.data, data) + + describe 'setData', -> + data = [ + {label: 'A', values: [{x: 10, y: 20}]}, + {label: 'B', values: [{x: 10, y: 20}]}, + {label: 'C', values: [{x: 10, y: 20}]} + ] + classNames = [ + ['layer', 'category1', 'a'], + ['layer', 'category2', 'b'], + ['layer', 'category3', 'c'] + ] + chart = null + + before (done) -> + (chart = new Epoch.Chart.Base()).setData(data) + done() + + it 'should set data correctly', -> + assert.sameMembers chart.data, data + for i in [0...data.length] + assert.equal chart.data[i].label, data[i].label + assert.equal chart.data[i].values[0], data[i].values[0] + + it 'should add the correct categories and class names', -> + for i in [0...data.length] + className = chart.data[i].className + for name in classNames[i] + assert (className.indexOf(name) > -1), "Missing class '#{name}'" + + describe 'draw', -> + it "should trigger the 'draw' event", (done) -> + errorCallback = -> + assert 'false', "The 'draw' event was never triggered" + done() + timeout = setTimeout(errorCallback, 1000) + chart = new Epoch.Chart.Base() + chart.on 'draw', -> + clearTimeout(timeout) + done() + chart.draw() + + describe 'update', -> + it 'should call draw by default', (done) -> + errorCallback = -> + assert false, "update did not call draw by default." + done() + timeout = setTimeout(errorCallback, 1000) + chart = new Epoch.Chart.Base() + chart.on 'draw', -> + clearTimeout(timeout) + done() + chart.update([]) + + it 'should not call draw when instructed', (done) -> + chart = new Epoch.Chart.Base() + chart.on 'draw', -> + assert false, "Update incorrectly called draw." + done() + chart.update([], false) + done() + + describe 'extent', -> + data = [ + {values: [ + {x: -1, y: 10}, + {x: 2, y: 20}, + {x: 4, y: 50}, + {x: 8, y: 9900} + ]}, + {values: [ + {x: 1, y: 170}, + {x: 7, y: -2380}, + {x: 19, y: 90}, + {x: 33, y: 17} + ]} + ] + + [xMin, xMax] = [-1, 33] + [yMin, yMax] = [-2380, 9900] + + chart = null + + before (done) -> + chart = new Epoch.Chart.Base({ data: data }) + done() + + it 'should find the correct extent given a y-comparitor', -> + [min, max] = chart.extent (d) -> d.y + assert.equal min, yMin, "Incorrect minimum y" + assert.equal max, yMax, "Incorrect maximum y" + + it 'should find the correct extent give an x-comparitor', -> + [min, max] = chart.extent (d) -> d.x + assert.equal min, xMin, "Incorrect minimum x" + assert.equal max, xMax, "Incorrect maximum x" + + describe 'option', -> + it 'should return all options for the chart when called with no arguments', -> + options = { a: 20, b: 30, c: { d: 40 } } + chart = new Epoch.Chart.Base options + assert.isObject chart.option() + assert.deepEqual chart.option(), options + + it 'should return a single value when given a key', -> + options = { a: 20, b: 30 } + chart = new Epoch.Chart.Base options + assert.equal chart.option('a'), options.a + assert.equal chart.option('b'), options.b + assert.isUndefined chart.option('c') + + it 'should return a deep value when given a hierarchical key', -> + options = + a: + b: 20 + c: + d: 30 + chart = new Epoch.Chart.Base options + assert.equal chart.option('a.b'), options.a.b + assert.equal chart.option('a.c.d'), options.a.c.d + + it 'should set an option given a string and a value', -> + chart = new Epoch.Chart.Base() + [key, value] = ['a', 'hello world'] + chart.option(key, value) + assert.equal chart.option(key), value + + it 'should set a deep value when given a hierarchical key', -> + chart = new Epoch.Chart.Base() + + map = + 'a.b': 'deep' + 'a.c.d': 'deeper' + 'b': 'shallow' + + for key, value of map + chart.option(key, value) + assert.equal chart.option(key), value + + it 'should set all options given an object', -> + original = { a: 20, b: { c: 30 } } + newOptions = { a: 15, d: { e: 10, f: 30 } } + chart = new Epoch.Chart.Base() + chart.option(newOptions) + assert.deepEqual chart.option(), newOptions + + it 'should trigger an event when an option is changed', (done) -> + [key, value] = ['a', 20] + eventName = "option:#{key}" + + errorCallback = -> + assert false, "Setting an option did not trigger the appropriate event: #{eventName}" + done() + timeout = setTimeout(errorCallback, 1000) + + chart = new Epoch.Chart.Base() + chart.on eventName, -> + clearTimeout(timeout) + done() + chart.option(key, value) + + it 'should resize the containing element when the width option is changed', -> + newWidth = resizeDivWidth + 20 + chart = new Epoch.Chart.Base({ el: '#resizeDiv' }) + chart.option('width', newWidth) + assert.equal d3.select('#resizeDiv').width(), newWidth + + it 'should resize the containing element when the height option is changed', -> + newHeight = resizeDivHeight + 20 + chart = new Epoch.Chart.Base({ el: '#resizeDiv' }) + chart.option('height', newHeight) + assert.equal d3.select('#resizeDiv').height(), newHeight + + describe '_getScaleDomain', -> + chart = null + + beforeEach -> + chart = new Epoch.Chart.Base + data: [ layerWithRange(-2030, 5050) ] + + it 'returns a given array', -> + assert.deepEqual(chart._getScaleDomain([0,1]), [0,1]) + + it 'returns @options.range if it is an array', -> + chart.options.range = [-100, 100] + assert.equal chart._getScaleDomain(), chart.options.range + + it 'returns @options.range.left if it is an array', -> + chart.options.range = {left: [-100, 100]} + assert.equal chart._getScaleDomain(), chart.options.range.left + + it 'returns @options.range.right if it is an array', -> + chart.options.range = {right: [-100, 100]} + assert.equal chart._getScaleDomain(), chart.options.range.right + + it 'returns the extent of the data', -> + assert.deepEqual chart._getScaleDomain(), chart.extent((d) -> d.y) + + describe 'with range grouped layers', -> + beforeEach -> + chart = new Epoch.Chart.Base + data: [ + layerWithRange(0, 10, 'left'), + layerWithRange(-5000, 5000, 'right'), + layerWithRange(-10, -5, 'left') + ] + + it 'returns the extent of the layers with the given range label', -> + assert.deepEqual chart._getScaleDomain('left'), [-10, 10] + + it 'returns the extent of the data if the label is invalid', -> + assert.deepEqual chart._getScaleDomain('foobar'), chart.extent((d) -> d.y) + + describe 'layers', -> + [chart, eventChart] = [null, null] + labels = ['A', 'B', 'C'] + data = [ + { label: 'A', data: [{x: 0, y: 0}] }, + { label: 'B', data: [{x: 1, y: 1}] }, + { label: 'C', data: [{x: 2, y: 2}] } + ] + data2 = [ + { label: 'A', data: [{x: 0, y: 0}] } + ] + + before (done) -> + chart = new Epoch.Chart.Base + el: doc.createElement('div') + data: data + eventChart = new Epoch.Chart.Base + el: doc.createElement('div') + data: data2 + done() + + describe '_findLayer', -> + it 'should find layers given a label', -> + for label in labels + layer = chart._findLayer(label) + assert.equal label, layer.label, "Could not find layer with label #{label}" + + it 'should find layers given an index', -> + for i in [0...data.length] + layer = chart._findLayer(i) + assert.equal labels[i], layer.label, "Could not find layer with index #{i}" + + it 'should return null if given an invalid label', -> + assert.isNull (chart._findLayer 'D') + assert.isNull (chart._findLayer 'not a thing') + + it 'should return null if given an index that is out of bounds', -> + assert.isNull (chart._findLayer -1) + assert.isNull (chart._findLayer 5) + + describe 'hideLayer', -> + it 'should hide a visible layer', -> + chart.hideLayer('A') + assert.isFalse chart._findLayer('A').visible + + it 'should keep a hidden layer hidden', -> + assert.isFalse chart._findLayer('A').visible + chart.hideLayer('A') + assert.isFalse chart._findLayer('A').visible + + it 'should trigger layer:hidden when a layer is hidden', (done) -> + errorCallback = -> + assert false, "layer:hidden was not triggered" + done() + timeout = setTimeout errorCallback, 1000 + eventChart.on 'layer:hidden', -> + clearTimeout timeout + done() + eventChart.hideLayer('A') + + describe 'showLayer', -> + it 'should have keep a visible layer visible', -> + assert.isTrue chart._findLayer('B').visible + chart.showLayer('B') + assert.isTrue chart._findLayer('B').visible + + it 'should make a hidden layer visible', -> + assert.isFalse chart._findLayer('A').visible + chart.showLayer('A') + assert.isTrue chart._findLayer('A').visible + + it 'should trigger layer:shown when a layer is shown', (done) -> + errorCallback = -> + assert false, "layer:shown was not triggered" + done() + timeout = setTimeout errorCallback, 1000 + eventChart.on 'layer:shown', -> + clearTimeout timeout + done() + eventChart.showLayer('A') + + describe 'toggleLayer', -> + it 'should hide a visible layer', -> + chart.hideLayer('A') + chart.toggleLayer('A') + assert.isTrue chart._findLayer('A').visible + + it 'should show a hidden layer', -> + chart.showLayer('B') + chart.toggleLayer('B') + assert.isFalse chart._findLayer('B').visible + + describe 'isLayerVisible', -> + it 'should report true if a layer is visible', -> + chart.showLayer('A') + assert.isTrue chart.isLayerVisible('A') + + it 'should report false if a layer is not visible', -> + chart.hideLayer('A') + assert.isFalse chart.isLayerVisible('B') + + describe 'getVisibleLayers', -> + it 'should only return visible layers', -> + chart.showLayer('A') + chart.showLayer('B') + chart.hideLayer('C') + visible = chart.getVisibleLayers() + assert.equal visible.length, 2 + assert.equal visible[0].label, 'A' + assert.equal visible[1].label, 'B' + + describe 'SVG', -> + [containerWidth, containerHeight] = [1000, 280] + container = null + + before (done) -> + container = doc.createElement('DIV') + container.id = 'svg-container' + doc.body.appendChild(container) + d3.select('#svg-container').style + 'width': "#{containerWidth}px" + 'height': "#{containerHeight}px" + done() + + after (done) -> + doc.body.removeChild(container) + done() + + describe 'constructor', -> + it 'should create a new SVG when not given an element', -> + chart = new Epoch.Chart.SVG() + assert.ok chart.svg, "SVG not created" + + it 'should set the default width and height of the SVG', -> + chart = new Epoch.Chart.SVG() + assert.equal chart.svg.attr('width'), defaultWidth, "Default width not set" + assert.equal chart.svg.attr('height'), defaultHeight, "Default height not set" + + it 'should set custom dimensions for the SVG via options', -> + [customWidth, customHeight] = [500, 600] + chart = new Epoch.Chart.SVG({ width: customWidth, height: customHeight }) + assert.equal chart.svg.attr('width'), customWidth, "Custom width not set" + assert.equal chart.svg.attr('height'), customHeight, "Custom height not set" + + it 'should set the container dimensions for the SVG', -> + chart = new Epoch.Chart.SVG({ el: '#svg-container' }) + assert.equal chart.svg.attr('width'), containerWidth + assert.equal chart.svg.attr('height'), containerHeight + + describe 'dimensionsChanged', -> + [width, height, chart] = [200, 100, null] + + before (done) -> + d3.select(doc.body).append('div').attr('id', 'svgResize').style + width: width + 'px' + height: height + 'px' + chart = new Epoch.Chart.SVG { el: '#svgResize' } + done() + + after (done) -> + d3.select('#svgResize').remove() + done() + + it 'should resize the SVG element when the width option is changed', -> + newWidth = width + 500 + chart.option 'width', newWidth + assert.equal chart.svg.attr('width'), newWidth + + it 'should resize the SVG element when the height option is changed', -> + newHeight = height + 500 + chart.option 'height', newHeight + assert.equal chart.svg.attr('height'), newHeight + + describe 'Canvas', -> + [containerWidth, containerHeight] = [1000, 280] + container = null + container_id = 'canvas-container' + containedChart = null + + before (done) -> + container = doc.createElement('DIV') + container.id = container_id + doc.body.appendChild(container) + d3.select('#' + container_id).style + 'width': "#{containerWidth}px" + 'height': "#{containerHeight}px" + + containedChart = new Epoch.Chart.Canvas + el: '#' + container_id + pixelRatio: 1 + + done() + + after (done) -> + doc.body.removeChild(container) + done() + + describe 'constructor', -> + it 'should correctly detect the pixelRatio', -> + chart = new Epoch.Chart.Canvas() + assert.equal chart.pixelRatio, 2 + + it 'should allow the pixelRatio to be explicitly overriden', -> + customPixelRatio = 4.2 + chart = new Epoch.Chart.Canvas({ pixelRatio: customPixelRatio }) + assert.equal chart.pixelRatio, customPixelRatio + + it 'should create a child canvas', -> + chart = new Epoch.Chart.Canvas() + assert.ok chart.canvas, "Did not create canvas" + assert.equal chart.canvas.node().tagName.toLowerCase(), 'canvas', 'Did not create a canvas node' + + it 'should append the child canvas to the containing element', -> + assert.equal containedChart.canvas.node().parentNode.id, container_id + + it 'should set the default dimensions for the canvas', -> + chart = new Epoch.Chart.Canvas({ pixelRatio: 1 }) + assert.equal chart.canvas.attr('width'), defaultWidth + assert.equal chart.canvas.attr('height'), defaultHeight + + it 'should allow custom dimensions for the canvas', -> + [customWidth, customHeight] = [999, 888] + chart = new Epoch.Chart.Canvas + width: customWidth + height: customHeight + pixelRatio: 1 + assert.equal chart.canvas.attr('width'), customWidth + assert.equal chart.canvas.attr('height'), customHeight + + it 'should set container dimensions for the canvas', -> + assert.equal containedChart.canvas.attr('width'), containerWidth + assert.equal containedChart.canvas.attr('height'), containerHeight + + it 'should fetch a graphics context from the canvas', -> + assert.ok containedChart.ctx, "Did not fetch graphics context from canvas" + + it 'should take pixel ratio into account when setting canvas dimension attributes', -> + pixelRatio = 3 + chart = new Epoch.Chart.Canvas({ pixelRatio: pixelRatio }) + assert.equal chart.canvas.attr('width'), defaultWidth * pixelRatio + assert.equal chart.canvas.attr('height'), defaultHeight * pixelRatio + + it 'should not take pixel ratio into account when setting canvas dimension styles', -> + chart = new Epoch.Chart.Canvas({ pixelRatio: 2 }) + assert.equal +chart.canvas.style('width').replace('px', ''), defaultWidth + assert.equal +chart.canvas.style('height').replace('px', ''), defaultHeight + + describe 'getWidth', -> + it 'should take pixel ratio into account', -> + pixelRatio = 2 + chart = new Epoch.Chart.Canvas({ pixelRatio: pixelRatio }) + assert.equal chart.getWidth(), pixelRatio * defaultWidth + + describe 'getHeight', -> + it 'should take pixel ratio into account', -> + pixelRatio = 2 + chart = new Epoch.Chart.Canvas({ pixelRatio: pixelRatio }) + assert.equal chart.getHeight(), pixelRatio * defaultHeight + + describe 'dimensionsChanged', -> + [width, height, chart, pixelRatio] = [200, 100, null, 2] + + before (done) -> + d3.select(doc.body).append('div').attr('id', 'canvasResize').style + width: width + 'px' + height: height + 'px' + chart = new Epoch.Chart.Canvas { el: '#canvasResize', pixelRatio: pixelRatio } + done() + + after (done) -> + d3.select('#canvasResize').remove() + done() + + it 'should resize the canvas element when the width option is changed', -> + newWidth = width + 500 + chart.option 'width', newWidth + assert.equal chart.canvas.attr('width'), pixelRatio * newWidth + assert.equal chart.canvas.width(), newWidth + + it 'should resize the canvas element when the height option is changed', -> + newHeight = height + 500 + chart.option 'height', newHeight + assert.equal chart.canvas.attr('height'), pixelRatio * newHeight + assert.equal chart.canvas.height(), newHeight + + describe 'redraw', -> + chart = null + drawSpy = null + purgeSpy = null + + before -> + chart = new Epoch.Chart.Canvas() + + beforeEach -> + drawSpy = sinon.spy chart, 'draw' + purgeSpy = sinon.spy Epoch.QueryCSS, 'purge' + + afterEach -> + chart.draw.restore() + Epoch.QueryCSS.purge.restore() + + it 'should purge QueryCSS cache and redraw the canvas based chart with new styles', -> + chart.redraw() + + assert drawSpy.calledOnce + assert purgeSpy.calledOnce diff --git a/debian/missing-sources/epoch/tests/unit/core/copy.coffee b/debian/missing-sources/epoch/tests/unit/core/copy.coffee new file mode 100644 index 0000000..2043241 --- /dev/null +++ b/debian/missing-sources/epoch/tests/unit/core/copy.coffee @@ -0,0 +1,47 @@ +describe 'Epoch.Util', -> + describe 'copy', -> + it 'should correctly create a shallow copy', -> + object = + a: 20 + b: 'hello' + + copy = Epoch.Util.copy(object) + + assert.equal copy.a, object.a + assert.equal copy.b, object.b + + it 'should not recursively copy objects', -> + object = + a: + foo: 'bar' + + copy = Epoch.Util.copy(object) + object.a.foo = 'baz' + assert.equal object.a.foo, copy.a.foo + + describe 'defaults', -> + it 'should set default values when keys are missing', -> + options = {a: 'foo', b: 'bar'} + defaults = {c: 'baz'} + result = Epoch.Util.defaults(options, defaults) + assert.equal result.c, defaults.c + + it 'should not set default values when keys are present', -> + options = { a: 'foo', b: 'bar' } + defaults = { a: 'wow', b: 'neat' } + result = Epoch.Util.defaults(options, defaults) + assert.equal result.a, options.a + assert.equal result.b, options.b + + it 'should recursively set defaults from sub objects', -> + options = + a: + b: 'foo' + defaults = + a: + b: '' + c: 'bar' + result = Epoch.Util.defaults(options, defaults) + + assert.equal result.a.b, options.a.b + assert.equal result.a.c, defaults.a.c diff --git a/debian/missing-sources/epoch/tests/unit/core/css.coffee b/debian/missing-sources/epoch/tests/unit/core/css.coffee new file mode 100644 index 0000000..00cce68 --- /dev/null +++ b/debian/missing-sources/epoch/tests/unit/core/css.coffee @@ -0,0 +1,85 @@ +describe 'Epoch.QueryCSS', -> + styleMap = + '#container rect': + 'fill': 'blue' + 'stroke': 'red' + 'stroke-width': '5px' + '#container rect.a': + 'fill': 'green' + 'stroke': 'yellow' + 'stroke-width': '1px' + 'rect#byid': + 'fill': 'purple' + 'stroke': '#94105A' + 'stroke-width': '15px' + 'body.alt-color rect#byid': + 'fill': '#abcdef1' + 'stroke': '#48419A' + 'stroke-width': '2em' + + [container, svg, styleTag] = [null, null, null] + + makeStyleSheet = -> + cssStatements = [] + for selector, rules of styleMap + cssStatements.push (selector + "{" + ("#{k}: #{v}" for k, v of rules).join(';') + "}") + css = cssStatements.join('\n') + styleTag = addStyleSheet(css) + + makeContainer = -> + container = d3.select(doc.body).append('div') + .attr('id', 'container') + svg = container.append('svg') + .attr('width', 10) + .attr('height', 10) + + assertStyles = (object, selector) -> + unless object? + assert(false, "Object contains no styles") + + unless (mapping = styleMap[selector])? + assert(false, "Could not find styles with selector: #{selector}") + + for key, value of mapping + assert.equal object[key], value, "Style mismatch on rule '#{key}'" + + before (done) -> + makeStyleSheet() + makeContainer() + done() + + after (done) -> + doc.head.removeChild(styleTag) + doc.body.removeChild(container.node()) + done() + + describe 'getStyles', -> + it 'should find styles for an svg element', -> + styles = Epoch.QueryCSS.getStyles('rect', container) + assertStyles styles, '#container rect' + + it 'should find styles using a specific class name', -> + styles = Epoch.QueryCSS.getStyles('rect.a', container) + assertStyles styles, '#container rect.a' + + it 'should find styles using an id', -> + styles = Epoch.QueryCSS.getStyles('rect#byid', container) + assertStyles styles, 'rect#byid' + + describe 'purge', -> + before (done) -> + d3.select(doc.body).attr('class', 'alt-color') + done() + + after (done) -> + d3.select(doc.body).attr('class', null) + done() + + it 'should find cached styles before a purge', -> + styles = Epoch.QueryCSS.getStyles('rect#byid', container) + assertStyles styles, 'rect#byid' + + it 'should find new styles after purging the cache', -> + Epoch.QueryCSS.purge() + styles = Epoch.QueryCSS.getStyles('rect#byid', container) + assertStyles styles, 'body.alt-color rect#byid' diff --git a/debian/missing-sources/epoch/tests/unit/core/d3.coffee b/debian/missing-sources/epoch/tests/unit/core/d3.coffee new file mode 100644 index 0000000..1b65c55 --- /dev/null +++ b/debian/missing-sources/epoch/tests/unit/core/d3.coffee @@ -0,0 +1,40 @@ +describe 'd3.selection', -> + [width, height] = [345, 543, null] + [element, id] = [null, 'd3-element'] + + before (done) -> + element = doc.createElement('DIV') + element.id = id + doc.body.appendChild(element) + d3.select('#' + id).style + 'width': width + "px" + 'height': height + "px" + done() + + describe 'width', -> + it 'should return the width of an element', -> + assert.equal d3.select('#' + id).width(), width + + it 'should set the width of an element given a number', -> + widthNumber = 50 + d3.select('#'+id).width(widthNumber) + assert.equal d3.select('#'+id).width(), widthNumber + + it 'should set the width of an element given a css pixel length', -> + widthString = '500px' + d3.select('#'+id).width(widthString) + assert.equal d3.select('#'+id).width(), +widthString.replace('px', '') + + describe 'height', -> + it 'should return the height of an element', -> + assert.equal d3.select('#' + id).height(), height + + it 'should set the height of an element given a number', -> + heightNumber = 75 + d3.select('#'+id).height(heightNumber) + assert.equal d3.select('#'+id).height(), heightNumber + + it 'should set the height of an element given a css pixel length', -> + heightString = '343px' + d3.select('#'+id).height(heightString) + assert.equal d3.select('#'+id).height(), +heightString.replace('px', '') diff --git a/debian/missing-sources/epoch/tests/unit/core/events.coffee b/debian/missing-sources/epoch/tests/unit/core/events.coffee new file mode 100644 index 0000000..38d9356 --- /dev/null +++ b/debian/missing-sources/epoch/tests/unit/core/events.coffee @@ -0,0 +1,111 @@ +describe 'Epoch.Events', -> + eventsObject = null + + before (done) -> + eventsObject = new Epoch.Events() + done() + + it 'should execute callbacks when events are triggered', (done) -> + errorCallback = -> + assert false, 'Event callback never executed' + done() + timeout = setTimeout errorCallback, 1000 + eventsObject.on 'event', -> + clearTimeout(timeout) + done() + eventsObject.trigger 'event' + + it 'should not execute callbacks that have been removed', (done) -> + errorCallback = -> + assert false, 'Event callback still executed' + done() + eventsObject.on 'example', errorCallback + eventsObject.off 'example', errorCallback + eventsObject.trigger 'example' + done() + + it 'should execute all callbacks associated with an event name', (done) -> + total = 4 + + errorCallback = -> + assert false, 'Not all callbacks were executed' + done() + timeout = setTimeout errorCallback, 1000 + + makeCallback = -> -> + total-- + if total == 0 + clearTimeout(timeout) + done() + eventsObject.on('multi', makeCallback()) for i in [0...total] + eventsObject.trigger 'multi' + + it 'should remove all callbacks when using .off([String])', (done) -> + makeCallback = -> -> + assert false, "A callback was still executed" + done() + eventsObject.on('multi2', makeCallback()) for i in [0...4] + eventsObject.off('multi2') + eventsObject.trigger('multi2') + setTimeout (-> done()), 200 + + it 'should execute methods on the object when using .on([String], [String])', (done) -> + errorCallback = -> + assert false, 'Trigger did not call the appropriate method.' + done() + timeout = setTimeout(errorCallback, 1000) + + eventsObject.method = -> + clearTimeout(timeout) + done() + + eventsObject.on 'method-event', 'method' + eventsObject.trigger 'method-event' + + it 'should register all events when executing .onAll([Object])', (done) -> + errorCallback = -> + assert false, 'Not all events were triggered.' + done() + timeout = setTimeout(errorCallback, 1000) + + eventNames = ['multi:a', 'multi:b', 'multi:c', 'multi:d'] + total = 0 + + eventCallback = -> + total += 1 + if total == eventNames.length + clearTimeout(timeout) + done() + + eventMap = {} + eventMap[name] = eventCallback for name in eventNames + + eventsObject.onAll(eventMap) + eventsObject.trigger(name) for name in eventNames + + + it 'should remove all events when executing .offAll([Array])', -> + eventCallback = -> + assert false, 'A removed callback was still triggered.' + + eventNames = ['multi-off:a', 'multi-off:b', 'multi-off:c', 'multi-off:d'] + eventMap = {} + eventMap[name] = eventCallback for name in eventNames + + eventsObject.onAll(eventMap) + eventsObject.offAll(eventNames) + eventsObject.trigger(name) for name in eventNames + + + it 'should remove specific event callbacks when executing .offAll([Object])', (done) -> + makeEventCallback = -> -> + assert false, 'A removed callback was still triggered.' + + eventNames = ['multi-off:a', 'multi-off:b', 'multi-off:c', 'multi-off:d'] + eventMap = {} + eventMap[name] = makeEventCallback() for name in eventNames + + eventsObject.onAll(eventMap) + eventsObject.offAll(eventMap) + eventsObject.trigger(name) for name in eventNames + setTimeout (-> done()), 200 diff --git a/debian/missing-sources/epoch/tests/unit/core/format.coffee b/debian/missing-sources/epoch/tests/unit/core/format.coffee new file mode 100644 index 0000000..a645b49 --- /dev/null +++ b/debian/missing-sources/epoch/tests/unit/core/format.coffee @@ -0,0 +1,68 @@ +describe 'Epoch.Util', -> + describe 'formatSI', -> + it 'should produce the same number for integers < 1000', -> + number = 678 + assert.equal Epoch.Util.formatSI(number), number + + it 'should only set a fixed decimal for integers when instructed', -> + number = 20 + assert.equal Epoch.Util.formatSI(number), number + assert.equal Epoch.Util.formatSI(number, 1, true), "#{number}.0" + + it 'should set the appropriate number of fixed digits', -> + number = 3.1415 + for i in [1..5] + match = Epoch.Util.formatSI(number, i).split('.')[1] + assert.isNotNull match + assert.isString match + assert.equal match.length, i + + it 'should set the appropriate postfix based on the number\'s order of magnitude', -> + for i, postfix of ['K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'] + number = Math.pow(10, ((i|0)+1)*3) + assert.equal Epoch.Util.formatSI(number), "1 #{postfix}" + + + describe 'formatBytes', -> + it 'should postfix numbers < 1024 with "B"', -> + number = 512 + assert.equal Epoch.Util.formatBytes(number), "#{number} B" + + it 'should only set a fixed decimal for integers when instructed', -> + assert.equal Epoch.Util.formatBytes(128), '128 B' + assert.equal Epoch.Util.formatBytes(128, 1, true), '128.0 B' + assert.equal Epoch.Util.formatBytes(1024), '1 KB' + assert.equal Epoch.Util.formatBytes(1024, 1, true), '1.0 KB' + + it 'should set the appropriate number of fixed digits', -> + number = 3.1415 + for i in [1..5] + fixed = Epoch.Util.formatBytes(number, i).replace(/\sB$/, '') + assert.isString fixed + match = fixed.split('.')[1] + assert.isNotNull match + assert.equal match.length, i + + it 'should set the appropriate postfix based on the number\'s order of magnitude', -> + for i, postfix of ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] + number = Math.pow(1024, (i|0)+1) + regexp = new RegExp(" #{postfix}$") + assert.isNotNull Epoch.Util.formatBytes(number).match(regexp) + + +describe 'Epoch.Formats', -> + describe 'regular', -> + it 'should return what it was given', -> + assert.equal Epoch.Formats.regular(10), 10 + assert.equal Epoch.Formats.regular("hello"), "hello" + + describe 'percent', -> + it 'should return a percent given a number', -> + assert.equal Epoch.Formats.percent(0.1), '10.0%' + assert.equal Epoch.Formats.percent(0.5), '50.0%' + assert.equal Epoch.Formats.percent(1), '100.0%' + assert.equal Epoch.Formats.percent(23.245), '2324.5%' + + describe 'seconds', -> + it 'should return a well formatted date given a timestamp', -> + assert.match Epoch.Formats.seconds(1404385979), /\d{2}:\d{2}:\d{2} AM|PM/ diff --git a/debian/missing-sources/epoch/tests/unit/core/is.coffee b/debian/missing-sources/epoch/tests/unit/core/is.coffee new file mode 100644 index 0000000..af08eb2 --- /dev/null +++ b/debian/missing-sources/epoch/tests/unit/core/is.coffee @@ -0,0 +1,88 @@ +describe 'Epoch.Util', -> + describe 'isArray', -> + it 'should return true if given an array', -> + assert.ok Epoch.isArray([]) + assert.ok Epoch.isArray([1, 2, 3]) + + it 'should return false if not given an array', -> + assert.notOk Epoch.isArray(2) + assert.notOk Epoch.isArray("hello") + assert.notOk Epoch.isArray({}) + + describe 'isObject', -> + it 'should return true if given an flat object', -> + assert.ok Epoch.isObject({}) + + it 'should return false if given a number object', -> + assert.notOk Epoch.isObject(new Number()) + + it 'should return false if given a non-object', -> + assert.notOk Epoch.isObject([]) + assert.notOk Epoch.isObject(2) + assert.notOk Epoch.isObject("string") + + describe 'isString', -> + it 'should return true if given a string', -> + assert.ok Epoch.isString("example") + assert.ok Epoch.isString(new String()) + + it 'should return false if given a non-string', -> + assert.notOk Epoch.isString(2) + assert.notOk Epoch.isString([]) + assert.notOk Epoch.isString({}) + + describe 'isFunction', -> + it 'should return true if given a function', -> + assert.ok Epoch.isFunction(->) + + it 'should return false if given a non-function', -> + assert.notOk Epoch.isFunction([]) + assert.notOk Epoch.isFunction({}) + assert.notOk Epoch.isFunction(42) + assert.notOk Epoch.isFunction("cool") + + describe 'isNumber', -> + it 'should return true if given a number', -> + assert.ok Epoch.isNumber(new Number()) + + it 'should return true if given an integer literal', -> + assert.ok Epoch.isNumber(1983) + + it 'should return true if given a floating point literal', -> + assert.ok Epoch.isNumber(3.1415) + + it 'should return false if given a non-number', -> + assert.notOk Epoch.isNumber(->) + assert.notOk Epoch.isNumber([]) + assert.notOk Epoch.isNumber({}) + assert.notOk Epoch.isNumber("nan") + + describe 'isElement', -> + it 'should return true given an html element', -> + p = doc.createElement('P') + assert.ok Epoch.isElement(p) + + it 'should return false given a non-element', -> + assert.notOk Epoch.isElement(1) + assert.notOk Epoch.isElement("1") + assert.notOk Epoch.isElement({}) + assert.notOk Epoch.isElement([]) + assert.notOk Epoch.isElement(->) + + describe 'isNonEmptyArray', -> + it 'should return true given a non-empty array', -> + assert.ok Epoch.isNonEmptyArray([1]) + assert.ok Epoch.isNonEmptyArray([1, 3]) + assert.ok Epoch.isNonEmptyArray(["foo", 4, "bar"]) + + it 'should return false given a non-array', -> + assert.notOk Epoch.isNonEmptyArray(2) + assert.notOk Epoch.isNonEmptyArray("five") + assert.notOk Epoch.isNonEmptyArray({}) + assert.notOk Epoch.isNonEmptyArray(->) + + it 'should return false given a null value', -> + assert.notOk Epoch.isNonEmptyArray(null) + + it 'should return false given an empty array', -> + assert.notOk Epoch.isNonEmptyArray([]) diff --git a/debian/missing-sources/epoch/tests/unit/core/util.coffee b/debian/missing-sources/epoch/tests/unit/core/util.coffee new file mode 100644 index 0000000..e02280c --- /dev/null +++ b/debian/missing-sources/epoch/tests/unit/core/util.coffee @@ -0,0 +1,92 @@ +describe 'Epoch.Util', -> + describe 'trim', -> + it 'should return null unless given a string', -> + assert.isNotNull Epoch.Util.trim('test string') + assert.isNull Epoch.Util.trim(34) + + it 'should trim leading and trailing whitespace', -> + assert.equal Epoch.Util.trim("\t\n\r indeed \n\t\t\r"), 'indeed' + + it 'should leave inner whitespace', -> + assert.equal Epoch.Util.trim('Hello world'), 'Hello world' + + describe 'dasherize', -> + it 'should dasherize regular strings', -> + assert.equal Epoch.Util.dasherize('Hello World'), 'hello-world' + + it 'should trim leading and trailing whitespace before dasherizing', -> + assert.equal Epoch.Util.dasherize(' Airieee is KewL '), 'airieee-is-kewl' + + describe 'domain', -> + testLayers = [ + { values: [{x: 'A', y: 10}, {x: 'B', y: 20}, {x: 'C', y: 40}] } + ] + + testLayers2 = [ + { values: [{x: 'A', y: 10}, {x: 'B', y: 20}, {x: 'C', y: 40}] }, + { values: [{x: 'D', y: 15}, {x: 'E', y: 30}, {x: 'F', y: 90}] } + ] + + it 'should find the correct domain of a set of keys and values', -> + xDomain = Epoch.Util.domain(testLayers, 'x') + assert.sameMembers xDomain, ['A', 'B', 'C'] + yDomain = Epoch.Util.domain(testLayers, 'y') + assert.sameMembers yDomain, [10, 20, 40] + + it 'should find all the values across multiple layers', -> + xDomain = Epoch.Util.domain(testLayers2, 'x') + assert.sameMembers xDomain, ['A', 'B', 'C', 'D', 'E', 'F'] + yDomain = Epoch.Util.domain(testLayers2, 'y') + assert.sameMembers yDomain, [10, 20, 40, 15, 30, 90] + + describe 'toRGBA', -> + it 'should produce the correct rgba style when given an rgba color style', -> + assert.equal Epoch.Util.toRGBA('rgba(1, 2, 3, 0.4)', 0.1), 'rgba(1,2,3,0.1)' + + it 'should produce the correct rgba style when given any rgb color style', -> + assert.equal Epoch.Util.toRGBA('black', 0.25), 'rgba(0,0,0,0.25)' + assert.equal Epoch.Util.toRGBA('#FF0000', 0.9), 'rgba(255,0,0,0.9)' + assert.equal Epoch.Util.toRGBA('rgb(10, 20, 40)', 0.99), 'rgba(10,20,40,0.99)' + + describe 'getComputedStyle', -> + overrideStyles = + 'width': '320px' + 'height': '240px' + 'background-color': 'blue' + + [style, div] = [null, null] + + before (done) -> + style = addStyleSheet('#get-style-div { padding-left: 30px; background: green }') + div = doc.createElement('div') + div.id = 'get-style-div' + doc.body.appendChild(div) + d3.select('#get-style-div').style(overrideStyles) + done() + + after (done) -> + doc.body.removeChild(div) + doc.head.removeChild(style) + done() + + it 'should find <style> styles', -> + styles = Epoch.Util.getComputedStyle(div) + assert.equal styles['padding-left'], '30px' + + it 'should find overriden styles', -> + styles = Epoch.Util.getComputedStyle(div) + for k, v of overrideStyles + assert.equal styles[k], v, "ComputedStyles['#{k}'] should be '#{v}'" + + describe 'flatten', -> + it 'should flatten a given multi-array', -> + multiarray = [[1, 2], 3, [4, 5, 6, [7]]] + expected = [1, 2, 3, 4, 5, 6, [7]] + assert.deepEqual Epoch.Util.flatten(multiarray), expected + + it 'should throw if given a non-array', -> + assert.throws (-> Epoch.Util.flatten null), /only accepts arrays/ + assert.throws (-> Epoch.Util.flatten 1), /only accepts arrays/ + assert.throws (-> Epoch.Util.flatten {}), /only accepts arrays/ + assert.throws (-> Epoch.Util.flatten 'hellooo'), /only accepts arrays/ + assert.throws (-> Epoch.Util.flatten new Error()), /only accepts arrays/ diff --git a/debian/missing-sources/epoch/tests/unit/data/array_format.coffee b/debian/missing-sources/epoch/tests/unit/data/array_format.coffee new file mode 100644 index 0000000..a043a1e --- /dev/null +++ b/debian/missing-sources/epoch/tests/unit/data/array_format.coffee @@ -0,0 +1,106 @@ +describe 'Epoch.Data.Format.array', -> + startTime = 1000 + + it 'should format flat arrays', -> + expected = [ {values: [{x: 0, y: 1}, {x: 1, y: 2}, {x: 2, y: 3}]} ] + assert.data expected, Epoch.Data.Format.array([1, 2, 3]) + + it 'should format multi-dimensional arrays', -> + expected = [ + { values: [{x: 0, y: 1}, {x: 1, y: 2}]}, + { values: [{x: 0, y: 3}, {x: 1, y: 4}]} + ] + assert.data expected, Epoch.Data.Format.array([[1, 2], [3, 4]]) + + it 'should respect the x option', -> + expected = [{values: [{x: 1, y: 1}, {x: 2, y: 2}]}] + result = Epoch.Data.Format.array [1, 2], {x: (d, i) -> i+1} + assert.data expected, result + + it 'should respect the y option', -> + expected = [{values: [{x: 0, y: 2}, {x: 1, y: 4}]}] + result = Epoch.Data.Format.array [1, 2], {y: (d) -> d*2} + assert.data expected, result + + it 'should format pie chart data with flat arrays', -> + input = [20, 30, 40] + expected = ({value: v} for v in input) + result = Epoch.Data.Format.array input, {type: 'pie'} + assert.equal expected.length, result.length, "Result did not have the expected number of layers" + for i in [0...expected.length] + assert.equal expected[i].value, result[i].value, "Result #{i} did not have the epected value" + + it 'should not format pie chart data with multi-dimensional arrays', -> + assert.equal Epoch.Data.Format.array([[1], [2]], {type: 'pie'}).length, 0 + + it 'should format real-time plot data with flat arrays', -> + input = [1, 2, 3] + expected = [{ values: ({time: startTime+parseInt(i), y: v} for i,v of input) }] + result = Epoch.Data.Format.array(input, {type: 'time.line', startTime: startTime}) + assert.timeData expected, result + + it 'should format real-time plot data with multi-dimensional arrays', -> + input = [[1, 2], [3, 4]] + expected = [] + for layer in input + expected.push {values: ({time: startTime+parseInt(i), y: v} for i, v of layer)} + result = Epoch.Data.Format.array(input, {type: 'time.line', startTime: startTime}) + assert.timeData expected, result + + it 'should format heatmap data with flat arrays', -> + input = [{'1': 1, '2': 2}, {'3': 3, '4': 4}] + expected = [{values: ({time: startTime+parseInt(i), histogram: h} for i, h of input)}] + result = Epoch.Data.Format.array(input, {type: 'time.heatmap', startTime: startTime}) + assert.data expected, result, ['time', 'heatmap'] + + it 'should format heatmap data with multi-dimensional arrays', -> + input = [ + [{'1': 1, '2': 2}, {'3': 3, '4': 4}], + [{'5': 5, '6': 6}, {'7': 7, '8': 8}] + ] + expected = [ + { values: ({time: startTime+parseInt(i), histogram: h} for i, h of input[0]) }, + { values: ({time: startTime+parseInt(i), histogram: h} for i, h of input[1]) }, + ] + result = Epoch.Data.Format.array(input, {type: 'time.heatmap', startTime: startTime}) + assert.data expected, result, ['time', 'heatmap'] + + it 'should correctly apply labels if the labels option is present', -> + labels = ['alpha', 'beta'] + result = Epoch.Data.Format.array [[1], [2]], {labels: labels} + for i in [0...labels.length] + assert.equal labels[i], result[i].label + + it 'should correctly apply labels if the autoLabels option is set', -> + labels = ['A', 'B', 'C'] + result = Epoch.Data.Format.array [[1], [2], [3]], {autoLabels: true} + for i in [0...labels.length] + assert.equal labels[i], result[i].label + + it 'should prefer the labels option to the autoLabels option if both are set', -> + labels = ['alpha', 'beta'] + result = Epoch.Data.Format.array [[1], [2]], {labels: labels, autoLabels: true} + for i in [0...labels.length] + assert.equal labels[i], result[i].label + + it 'should produce single series entries correctly', -> + result = Epoch.Data.Format.array.entry(2) + assert.isArray result + assert.equal 1, result.length + assert.isObject result[0] + assert.equal 0, result[0].x + assert.equal 2, result[0].y + + it 'should produce multi-series entries correctly', -> + expected = [ + { x: 0, y: 1 }, + { x: 0, y: 2 }, + { x: 0, y: 3 } + ] + result = Epoch.Data.Format.array.entry([1, 2, 3]) + assert.isArray result + assert.equal 3, result.length + for i in [0...expected.length] + assert.isObject result[i] + assert.equal expected[i].x, result[i].x + assert.equal expected[i].y, result[i].y diff --git a/debian/missing-sources/epoch/tests/unit/data/chart.coffee b/debian/missing-sources/epoch/tests/unit/data/chart.coffee new file mode 100644 index 0000000..ed115a1 --- /dev/null +++ b/debian/missing-sources/epoch/tests/unit/data/chart.coffee @@ -0,0 +1,121 @@ + +describe 'Epoch.Chart.options', -> + it 'should set the type option to "area" for basic area charts', -> + assert.equal new Epoch.Chart.Area().options.type, 'area' + + it 'should set the type option to "bar" for basic bar charts', -> + assert.equal new Epoch.Chart.Bar().options.type, 'bar' + + it 'should set the type option to "histogram" for basic histogram charts', -> + assert.equal new Epoch.Chart.Histogram().options.type, 'histogram' + + it 'should set the type option to "line" for basic line charts', -> + assert.equal new Epoch.Chart.Line().options.type, 'line' + + it 'should set the type option to "pie" for basic pie charts', -> + assert.equal new Epoch.Chart.Pie().options.type, 'pie' + + it 'should set the type option to "scatter" for basic scatter charts', -> + assert.equal new Epoch.Chart.Scatter().options.type, 'scatter' + + it 'should set the type option to "time.area" for real-time area charts', -> + assert.equal new Epoch.Time.Area().options.type, 'time.area' + + it 'should set the type option to "time.bar" for real-time bar charts', -> + assert.equal new Epoch.Time.Bar().options.type, 'time.bar' + + it 'should set the type option to "time.gauge" for real-time gauge charts', -> + assert.equal new Epoch.Time.Gauge().options.type, 'time.gauge' + + it 'should set the type option to "time.heatmap" for real-time heatmap charts', -> + assert.equal new Epoch.Time.Heatmap().options.type, 'time.heatmap' + + it 'should set the type option to "time.line" for real-time line charts', -> + assert.equal new Epoch.Time.Line().options.type, 'time.line' + +describe 'Epoch.Chart._formatData', -> + assertBasicData = (klassName, type) -> + data = [1, 2, 3, 4] + expected = Epoch.data 'array', data, {type: type} + chart = new Epoch.Chart[klassName] + data: data + dataFormat: 'array' + assert.data expected, chart.data + + assertTimeData = (klassName, type) -> + data = [1, 2, 3, 4] + expected = Epoch.data 'array', data, {type: type, time: (d, i) -> parseInt(i)} + chart = new Epoch.Time[klassName] + data: data + dataFormat: + name: 'array' + options: { time: (d, i) -> parseInt(i) } + assert.timeData expected, chart.data + + it 'should correctly detect and format array type data', -> + data = [1, 2, 3] + expected = Epoch.data 'array', data + chart = new Epoch.Chart.Base + data: data + dataFormat: 'array' + assert.data expected, chart.data + + it 'should correctly detect and format tuple type data', -> + data = [[1, 1], [2, 4], [3, 78]] + expected = Epoch.data 'tuple', data + chart = new Epoch.Chart.Base + data: data + dataFormat: 'tuple' + assert.data expected, chart.data + + it 'should correctly detect and format keyvalue type data', -> + data = [ {a: 20, b: 30, x: 10}, {a: 40, b: 50, x: 20} ] + expected = Epoch.data 'keyvalue', data, ['a', 'b'], { x: (d) -> d.x } + chart = new Epoch.Chart.Base + data: data + dataFormat: + name: 'keyvalue' + arguments: [['a', 'b']] + options: { x: (d, i) -> d.x } + assert.data expected, chart.data + + it 'should correctly format area chart data', -> + assertBasicData 'Area', 'area' + + it 'should correctly format bar chart data', -> + assertBasicData 'Bar', 'bar' + + it 'should correctly format line data', -> + assertBasicData 'Line', 'line' + + it 'should correctly format scatter data', -> + assertBasicData 'Scatter', 'scatter' + + it 'should correctly format pie data', -> + data = [1, 2, 3] + expected = data.map (d) -> {value: d} + result = (new Epoch.Chart.Pie(data: data, dataFormat: 'array')).data + for i in [0...expected.length] + assert.equal expected[i].value, result[i].value + + it 'should correctly format histogram data', -> + data = (parseInt(Math.random() * 100) for i in [0...100]) + format = Epoch.data('array', data, { type: 'histogram' }) + expected = (new Epoch.Chart.Histogram())._prepareData(format) + chart = new Epoch.Chart.Histogram({ data: data, dataFormat: 'array' }) + assert.data expected, chart.data + + it 'should correctly format real-time area data', -> + assertTimeData 'Area', 'time.area' + + it 'should correctly format real-time bar data', -> + assertTimeData 'Bar', 'time.bar' + + it 'should correctly format real-time heatmap data', -> + assertTimeData 'Heatmap', 'time.heatmap' + + it 'should correctly format real-time line data', -> + assertTimeData 'Line', 'time.line' + + + diff --git a/debian/missing-sources/epoch/tests/unit/data/keyvalue_format.coffee b/debian/missing-sources/epoch/tests/unit/data/keyvalue_format.coffee new file mode 100644 index 0000000..1ac5fcc --- /dev/null +++ b/debian/missing-sources/epoch/tests/unit/data/keyvalue_format.coffee @@ -0,0 +1,101 @@ +describe 'Epoch.Data.Format.keyvalue', -> + + data = [ + { x: 1, a: 20, b: 30, c: 40, hist: 10, time: 0 }, + { x: 2, a: 45, b: 83, c: 8, hist: 11, time: 1 }, + { x: 3, a: 17, b: 72, c: 54, hist: 12, time: 2 }, + { x: 4, a: 99, b: 19, c: 39, hist: 13, time: 3 } + ] + + it 'should format a single series for basic plots', -> + expected = [{ values: (data.map (d, i) -> { x: parseInt(i), y: d.a }) }] + result = Epoch.Data.Format.keyvalue(data, ['a']) + assert.data expected, result + + it 'should format multiple series for basic plots', -> + expected = [ + { values: (data.map (d, i) -> {x: parseInt(i), y: d.a }) }, + { values: (data.map (d, i) -> {x: parseInt(i), y: d.b }) }, + { values: (data.map (d, i) -> {x: parseInt(i), y: d.c }) } + ] + result = Epoch.Data.Format.keyvalue(data, ['a', 'b', 'c']) + assert.data expected, result + + it 'should format a single series for real-time plots', -> + expected = [ + { values: (data.map (d, i) -> { time: d.time, y: d.a }) } + ] + result = Epoch.Data.Format.keyvalue(data, ['a'], { type: 'time.line', time: (d) -> d.time }) + assert.data expected, result + + it 'should format multiple series for real-time plots', -> + expected = [ + { values: (data.map (d, i) -> { time: d.time, y: d.a }) } + { values: (data.map (d, i) -> { time: d.time, y: d.b }) } + { values: (data.map (d, i) -> { time: d.time, y: d.c }) } + ] + result = Epoch.Data.Format.keyvalue(data, ['a', 'b', 'c'], { type: 'time.line', time: (d) -> d.time }) + assert.data expected, result + + it 'should correctly format heatmap data', -> + expected = [ + { values: (data.map (d, i) -> {time: d.time, histogram: d.hist }) } + ] + result = Epoch.Data.Format.keyvalue(data, ['hist'], {type: 'time.heatmap', time: ((d) -> d.time) }) + assert.data expected, result + + it 'should return an empty set for type time.gauge and type pie', -> + assert.equal Epoch.Data.Format.keyvalue(data, ['a'], {type: 'pie'}).length, 0 + assert.equal Epoch.Data.Format.keyvalue(data, ['a'], {type: 'time.gauge'}).length, 0 + + it 'should respect the x option', -> + expected = [{ values: (data.map (d, i) -> {x: d.x, y: d.a }) }] + result = Epoch.Data.Format.keyvalue(data, ['a'], {x: (d) -> d.x}) + assert.data expected, result + + it 'should respect the y option', -> + expected = [{ values: (data.map (d, i) -> {x: parseInt(i), y: d.a + 2 }) }] + result = Epoch.Data.Format.keyvalue(data, ['a'], {y: (d) -> d + 2}) + assert.data expected, result + + it 'should apply key name labels by default', -> + labels = ['a', 'b', 'c', 'hist'] + layers = Epoch.Data.Format.keyvalue(data, ['a', 'b', 'c', 'hist']) + for i in [0...labels.length] + assert.equal labels[i], layers[i].label + + it 'should override key name labels with given labels', -> + labels = ['x', 'y', 'z'] + layers = Epoch.Data.Format.keyvalue(data, ['a', 'b', 'c'], {labels: labels}) + for i in [0...labels.length] + assert.equal labels[i], layers[i].label + + it 'should apply automatic labels only when labels are not given and key labels are off', -> + labels = ['A', 'B'] + layers = Epoch.Data.Format.keyvalue(data, ['a', 'b'], {keyLabels: false, autoLabels: true}) + for i in [0...labels.length] + assert.equal labels[i], layers[i].label + + it 'should produce single series entries correctly', -> + input = data[0] + keys = ['a'] + expected = [{x: 0, y: input.a}] + result = Epoch.Data.Format.keyvalue.entry(input, keys) + assert.isArray result + assert.equal 1, result.length + assert.isObject result[0] + assert.equal 0, result[0].x + assert.equal input.a, result[0].y + + it 'should produce multi-series entries correctly', -> + input = data[1] + keys = ['a', 'b', 'c'] + options = {x: 'x'} + expected = ({x: input.x, y: input[key]} for key in keys) + result = Epoch.Data.Format.keyvalue.entry(input, keys, options) + assert.isArray result + assert.equal expected.length, result.length + for i in [0...expected.length] + assert.isObject result[i] + assert.equal expected[i].x, result[i].x + assert.equal expected[i].y, result[i].y diff --git a/debian/missing-sources/epoch/tests/unit/data/tuple_format.coffee b/debian/missing-sources/epoch/tests/unit/data/tuple_format.coffee new file mode 100644 index 0000000..a55f559 --- /dev/null +++ b/debian/missing-sources/epoch/tests/unit/data/tuple_format.coffee @@ -0,0 +1,76 @@ +describe 'Epoch.Data.Format.tuple', -> + it 'should format flat tuple arrays', -> + input = [[1, 2], [3, 4], [5, 6]] + expected = [{values: input.map((d) -> {x: d[0], y: d[1]})}] + result = Epoch.Data.Format.tuple(input) + assert.data expected, result + + it 'should format nested layers of tuple arrays', -> + input = [ + [ [1, 2], [3, 4] ], + [ [5, 6], [7, 8] ] + ] + expected = input.map (series) -> + {values: series.map((d) -> {x: d[0], y: d[1]})} + result = Epoch.Data.Format.tuple(input) + assert.data expected, result + + it 'should respect the x option', -> + input = [[1, 2], [3, 4], [5, 6]] + expected = [{values: input.map((d, i) -> {x: i, y: d[1]})}] + result = Epoch.Data.Format.tuple(input, {x: (d, i) -> i}) + assert.data expected, result + + it 'should respect the y option', -> + input = [[1, 2], [3, 4], [5, 6]] + expected = [{values: input.map((d, i) -> {x: d[0], y: i})}] + result = Epoch.Data.Format.tuple(input, {y: (d, i) -> i}) + assert.data expected, result + + it 'should format flat tuples of real-time data', -> + input = [[1, 2], [3, 4], [5, 6]] + expected = [{values: input.map((d) -> {time: d[0], y: d[1]})}] + result = Epoch.Data.Format.tuple(input, {type: 'time.line'}) + assert.data expected, result + + it 'should format nested layers of real-time tuple data', -> + input = [ + [ [1, 2], [3, 4] ], + [ [5, 6], [7, 8] ] + ] + expected = input.map (series) -> + {values: series.map((d) -> {time: d[0], y: d[1]})} + result = Epoch.Data.Format.tuple(input, {type: 'time.line'}) + assert.data expected, result + + it 'should respect the time option', -> + input = [[1, 2], [3, 4], [5, 6]] + expected = [{values: input.map((d, i) -> {time: i, y: d[1]})}] + result = Epoch.Data.Format.tuple(input, {type: 'time.line', time: (d, i) -> i}) + assert.data expected, result + + it 'should ignore heatmap, pie, and gauge charts', -> + input = [[1, 2], [3, 4], [5, 6]] + assert.equal 0, Epoch.Data.Format.tuple(input, {type: 'time.heatmap'}).length + assert.equal 0, Epoch.Data.Format.tuple(input, {type: 'time.gauge'}).length + assert.equal 0, Epoch.Data.Format.tuple(input, {type: 'pie'}).length + + it 'should produce single series entries correctly', -> + input = [5, 6] + result = Epoch.Data.Format.tuple.entry(input) + assert.isArray result + assert.equal 1, result.length + assert.isObject result[0] + assert.equal input[0], result[0].x + assert.equal input[1], result[0].y + + it 'should produce multi-series entries correctly', -> + input = [[5, -10], [4, 8], [2, 3]] + expected = ({x: d[0], y: d[1]} for d in input) + result = Epoch.Data.Format.tuple.entry(input) + assert.isArray result + assert.equal expected.length, result.length + for i in [0...expected.length] + assert.isObject result[i] + assert.equal expected[i].x, result[i].x + assert.equal expected[i].y, result[i].y diff --git a/debian/missing-sources/epoch/tests/unit/init.coffee b/debian/missing-sources/epoch/tests/unit/init.coffee new file mode 100644 index 0000000..aa8edd2 --- /dev/null +++ b/debian/missing-sources/epoch/tests/unit/init.coffee @@ -0,0 +1,55 @@ +process.env.TZ = "America/Los_Angeles" + +jsdom = require('jsdom') +global.assert = require('chai').assert +url = require('url') + +html = "<html><head></head><body></body></html>" + +exec = require('child_process').exec +exec 'pwd', (err, out) -> console.log out + +before (done) -> + jsdom.env + html: html + scripts: ["http://d3js.org/d3.v3.min.js", "./dist/js/epoch.js"] + done: (errors, window) -> + global.Epoch = window.Epoch + # Override get context to use a test context by default + global.Epoch.Util.getContext = -> new window.Epoch.TestContext() + global.d3 = window.d3 + global.doc = window.document + # Set this to "retina" so we can test canvas based charts + window.devicePixelRatio = 2 + done() + +global.addStyleSheet = (css) -> + head = doc.head + style = doc.createElement('style') + style.type = 'text/css' + style.appendChild(doc.createTextNode(css)) + head.appendChild(style) + style + +global.layerWithRange = (min, max, range) -> + layer = { values: [{time: 0, y: min}, {time: 1, y: max}] } + layer.range = range if range? + layer + +# +# Helper assertion methods for data format testing +# +assert.data = (expected, result, checkAttributes) -> + checkAttributes ?= ['x', 'y'] + assert.equal expected.length, result.length + for i, layer of expected + resultLayer = result[i] + msg = "Result layer #{i} does not have expected number of values." + assert.equal layer.values.length, resultLayer.values.length, msg + for j in [0...layer.values.length] + for k in checkAttributes + msg = "Layer #{i} data point #{j} does not have the expected value for key #{k}" + assert.equal layer.values[j][k], resultLayer.values[j][k], msg + +assert.timeData = (expected, result) -> + assert.data(expected, result, ['time', 'y']) diff --git a/debian/missing-sources/epoch/tests/unit/time.coffee b/debian/missing-sources/epoch/tests/unit/time.coffee new file mode 100644 index 0000000..48593a2 --- /dev/null +++ b/debian/missing-sources/epoch/tests/unit/time.coffee @@ -0,0 +1,67 @@ +sinon = require 'sinon' + +describe 'Epoch.Time.Plot', -> + chart = null + + beforeEach -> + chart = new Epoch.Time.Plot(data: [layerWithRange(0, 100)]) + + describe 'y', -> + scaleDomain = [-524, 2324] + beforeEach -> sinon.stub(chart, '_getScaleDomain').returns(scaleDomain) + afterEach -> chart._getScaleDomain.restore() + + it 'should get the scale domain from the given domain', -> + y = chart.y('a') + assert.ok chart._getScaleDomain.calledWith('a') + assert.deepEqual y.domain(), scaleDomain + + describe 'ySvg', -> + scaleDomain = [3004, 10000000] + beforeEach -> sinon.stub(chart, '_getScaleDomain').returns(scaleDomain) + afterEach -> chart._getScaleDomain.restore() + + it 'should get the scale domain from the given domain', -> + y = chart.ySvg('a') + assert.ok chart._getScaleDomain.calledWith('a') + assert.deepEqual y.domain(), scaleDomain + + describe 'ySvgLeft', -> + beforeEach -> sinon.spy(chart, 'ySvg') + afterEach -> chart.ySvg.restore() + + it 'should use the left range when present', -> + chart.options.range = { left: 'apples' } + chart.ySvgLeft() + assert.ok chart.ySvg.calledWith('apples') + + it 'should not use the left range when missing', -> + chart.ySvgLeft() + assert.ok chart.ySvg.calledOnce + + describe 'ySvgRight', -> + beforeEach -> sinon.spy(chart, 'ySvg') + afterEach -> chart.ySvg.restore() + + it 'should use the right range when present', -> + chart.options.range = { right: 'oranges' } + chart.ySvgRight() + assert.ok chart.ySvg.calledWith('oranges') + + it 'should not use the right range when missing', -> + chart.ySvgRight() + assert.ok chart.ySvg.calledOnce + + describe 'leftAxis', -> + beforeEach -> sinon.spy chart, 'ySvgLeft' + afterEach -> chart.ySvgLeft.restore() + it 'uses the left svg scale', -> + chart.leftAxis() + assert.ok chart.ySvgLeft.calledOnce + + describe 'rightAxis', -> + beforeEach -> sinon.spy chart, 'ySvgRight' + afterEach -> chart.ySvgRight.restore() + it 'uses the right svg scale', -> + chart.rightAxis() + assert.ok chart.ySvgRight.calledOnce diff --git a/debian/missing-sources/epoch/tests/unit/time/line.coffee b/debian/missing-sources/epoch/tests/unit/time/line.coffee new file mode 100644 index 0000000..fa6c205 --- /dev/null +++ b/debian/missing-sources/epoch/tests/unit/time/line.coffee @@ -0,0 +1,15 @@ +sinon = require 'sinon' + +describe 'Epoch.Time.Line', -> + chart = null + beforeEach -> + chart = new Epoch.Time.Line + data: [{ range: 'foo', values: [{time: 0, y: 10}, {time: 1, y: 30}] }] + + describe 'draw', -> + beforeEach -> sinon.spy chart, 'y' + afterEach -> chart.y.restore() + + it 'should provide the layer\'s range to the y scale', -> + chart.draw() + assert.ok chart.y.calledWith('foo') diff --git a/debian/missing-sources/jquery.js b/debian/missing-sources/jquery.js new file mode 100644 index 0000000..eed1777 --- /dev/null +++ b/debian/missing-sources/jquery.js @@ -0,0 +1,9210 @@ +/*! + * jQuery JavaScript Library v2.1.4 + * http://jquery.com/ + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * + * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2015-04-28T16:01Z + */ + +(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) +// + +var arr = []; + +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 support = {}; + + + +var + // Use the correct document accordingly with window argument (sandbox) + document = window.document, + + version = "2.1.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 + // 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. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + 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(null); + }, + + // 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" && !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() {}, + + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray, + + isWindow: function( obj ) { + 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) + return !jQuery.isArray( obj ) && (obj - parseFloat( obj ) + 1) >= 0; + }, + + isPlainObject: function( obj ) { + // Not plain objects: + // - Any object or value whose internal [[Class]] property is not "[object Object]" + // - DOM nodes + // - window + if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + if ( obj.constructor && + !hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) { + return false; + } + + // If the function hasn't returned already, we're confident that + // |obj| is a plain object, created by {} or constructed with new Object + return true; + }, + + isEmptyObject: function( obj ) { + var name; + for ( name in obj ) { + return false; + } + return true; + }, + + type: function( obj ) { + if ( obj == null ) { + return obj + ""; + } + // Support: Android<4.0, iOS<6 (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call(obj) ] || "object" : + typeof obj; + }, + + // Evaluates a script in a global context + globalEval: function( code ) { + var script, + indirect = eval; + + code = jQuery.trim( code ); + + if ( code ) { + // If the code includes a valid, prologue position + // strict mode pragma, execute code by injecting a + // script tag into the document. + if ( code.indexOf("use strict") === 1 ) { + script = document.createElement("script"); + script.text = code; + document.head.appendChild( script ).parentNode.removeChild( script ); + } else { + // Otherwise, avoid the DOM node creation, insertion + // and removal by using an indirect global eval + indirect( code ); + } + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Support: IE9-11+ + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + }, + + // args is for internal usage only + each: function( obj, callback, args ) { + var value, + i = 0, + length = obj.length, + isArray = isArraylike( obj ); + + if ( args ) { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } + } + + return obj; + }, + + // Support: Android<4.1 + 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 ); + }, + + 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 value, + i = 0, + length = elems.length, + isArray = isArraylike( elems ), + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArray ) { + 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 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 ( !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: Date.now, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +}); + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object Error".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 = "length" in obj && obj.length, + type = jQuery.type( obj ); + + if ( type === "function" || jQuery.isWindow( obj ) ) { + return false; + } + + if ( obj.nodeType === 1 && length ) { + return true; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v2.2.0-pre + * http://sizzlejs.com/ + * + * Copyright 2008, 2014 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2014-12-16 + */ +(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 + + // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + // http://www.w3.org/TR/css3-syntax/#characters + characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", + + // Loosely modeled on CSS identifier characters + // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors + // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = characterEncoding.replace( "w", "w#" ), + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + + "*\\]", + + pseudos = ":(" + characterEncoding + ")(?:\\((" + + // 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( "^#(" + characterEncoding + ")" ), + "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), + "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), + "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 match, elem, m, nodeType, + // QSA vars + i, groups, old, nid, newContext, newSelector; + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + + context = context || document; + results = results || []; + nodeType = context.nodeType; + + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + if ( !seed && documentIsHTML ) { + + // Try to shortcut find operations when possible (e.g., not under DocumentFragment) + if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { + // Speed-up: Sizzle("#ID") + if ( (m = match[1]) ) { + if ( nodeType === 9 ) { + elem = context.getElementById( m ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document (jQuery #6963) + if ( elem && elem.parentNode ) { + // Handle the case where IE, Opera, and Webkit return items + // by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + } else { + // Context is not a document + if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && + contains( context, elem ) && elem.id === m ) { + results.push( elem ); + return results; + } + } + + // Speed-up: Sizzle("TAG") + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Speed-up: Sizzle(".CLASS") + } else if ( (m = match[3]) && support.getElementsByClassName ) { + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // QSA path + if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { + nid = old = expando; + newContext = context; + newSelector = nodeType !== 1 && selector; + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + groups = tokenize( selector ); + + if ( (old = context.getAttribute("id")) ) { + nid = old.replace( rescape, "\\$&" ); + } else { + context.setAttribute( "id", nid ); + } + nid = "[id='" + nid + "'] "; + + i = groups.length; + while ( i-- ) { + groups[i] = nid + toSelector( groups[i] ); + } + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context; + newSelector = groups.join(","); + } + + if ( newSelector ) { + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch(qsaError) { + } finally { + if ( !old ) { + 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 = attrs.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; + + // If no document and documentElement is available, return + if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Set our document + document = doc; + docElem = doc.documentElement; + parent = doc.defaultView; + + // Support: IE>8 + // If iframe document is assigned to "document" variable and if iframe has been reloaded, + // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936 + // IE6-8 do not support the defaultView property so parent will be undefined + if ( parent && parent !== parent.top ) { + // IE11 does not have attachEvent, so all must suffer + if ( parent.addEventListener ) { + parent.addEventListener( "unload", unloadHandler, false ); + } else if ( parent.attachEvent ) { + parent.attachEvent( "onunload", unloadHandler ); + } + } + + /* Support tests + ---------------------------------------------------------------------- */ + documentIsHTML = !isXML( doc ); + + /* 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( doc.createComment("") ); + return !div.getElementsByTagName("*").length; + }); + + // Support: IE<9 + support.getElementsByClassName = rnative.test( doc.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 !doc.getElementsByName || !doc.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 ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [ m ] : []; + } + }; + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute("id") === attrId; + }; + }; + } 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 ( 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( doc.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 + "-\f]' 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.2+, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.7+ + 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 = doc.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 does not implement inclusive descendent + // 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 === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { + return -1; + } + if ( b === doc || 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 === doc ? -1 : + b === doc ? 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 doc; +}; + +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 && + ( !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, outerCache, node, diff, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType; + + 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 + outerCache = parent[ expando ] || (parent[ expando ] = {}); + cache = outerCache[ type ] || []; + nodeIndex = cache[0] === dirruns && cache[1]; + diff = cache[0] === dirruns && 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 ) { + outerCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + // Use previously-cached element index if available + } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { + diff = cache[1]; + + // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) + } else { + // Use the same loop as above to seek `elem` from the start + while ( (node = ++nodeIndex && node && node[ dir ] || + (diff = nodeIndex = 0) || start.pop()) ) { + + if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { + // Cache the index of each encountered element + if ( useCache ) { + (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ]; + } + + 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, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from dir 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 ] = {}); + if ( (oldCache = outerCache[ 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 + outerCache[ 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; + } + + // Add elements passing elementMatchers directly to results + // Keep `i` a string if there are no elements so `matchedCount` will be "00" below + // 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; + while ( (matcher = elementMatchers[j++]) ) { + if ( matcher( elem, context, 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 ); + } + } + } + + // Apply set filters to unmatched elements + matchedCount += i; + 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 no seed and only one group + if ( match.length === 1 ) { + + // Take a shortcut and set the context if the root selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + support.getById && context.nodeType === 9 && documentIsHTML && + Expr.relative[ tokens[1].type ] ) { + + 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, + 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.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + + +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 ( indexOf.call( qualifier, elem ) >= 0 ) !== 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, + len = this.length, + ret = [], + 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; + } + } + }) ); + } + + 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 ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // 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 ( 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] ); + + // Support: Blackberry 4.6 + // gEBID returns nodes no longer in the document (#6963) + if ( elem && elem.parentNode ) { + // Inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || rootjQuery ).find( selector ); + + // 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 rootjQuery.ready !== "undefined" ? + rootjQuery.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.extend({ + 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; + }, + + sibling: function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; + } +}); + +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 = [], + 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.unique( 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.unique( + 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 jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return elem.contentDocument || 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.unique( matched ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + matched.reverse(); + } + } + + return this.pushStack( matched ); + }; +}); +var rnotwhite = (/\S+/g); + + + +// String to Object options format cache +var optionsCache = {}; + +// Convert String-formatted options into Object-formatted ones and store in cache +function createOptions( options ) { + var object = optionsCache[ options ] = {}; + jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) { + object[ flag ] = true; + }); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + ( optionsCache[ options ] || createOptions( options ) ) : + jQuery.extend( {}, options ); + + var // Last fire value (for non-forgettable lists) + memory, + // Flag to know if list was already fired + fired, + // Flag to know if list is currently firing + firing, + // First callback to fire (used internally by add and fireWith) + firingStart, + // End of the loop when firing + firingLength, + // Index of currently firing callback (modified by remove if needed) + firingIndex, + // Actual callback list + list = [], + // Stack of fire calls for repeatable lists + stack = !options.once && [], + // Fire callbacks + fire = function( data ) { + memory = options.memory && data; + fired = true; + firingIndex = firingStart || 0; + firingStart = 0; + firingLength = list.length; + firing = true; + for ( ; list && firingIndex < firingLength; firingIndex++ ) { + if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { + memory = false; // To prevent further calls using add + break; + } + } + firing = false; + if ( list ) { + if ( stack ) { + if ( stack.length ) { + fire( stack.shift() ); + } + } else if ( memory ) { + list = []; + } else { + self.disable(); + } + } + }, + // Actual Callbacks object + self = { + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + // First, we save the current length + var start = list.length; + (function add( args ) { + jQuery.each( args, function( _, arg ) { + var type = jQuery.type( arg ); + if ( type === "function" ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && type !== "string" ) { + // Inspect recursively + add( arg ); + } + }); + })( arguments ); + // Do we need to add the callbacks to the + // current firing batch? + if ( firing ) { + firingLength = list.length; + // With memory, if we're not firing then + // we should call right away + } else if ( memory ) { + firingStart = start; + fire( memory ); + } + } + return this; + }, + // Remove a callback from the list + remove: function() { + if ( list ) { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + // Handle firing indexes + if ( firing ) { + if ( index <= firingLength ) { + firingLength--; + } + if ( index <= firingIndex ) { + firingIndex--; + } + } + } + }); + } + return this; + }, + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); + }, + // Remove all callbacks from the list + empty: function() { + list = []; + firingLength = 0; + return this; + }, + // Have the list do nothing anymore + disable: function() { + list = stack = memory = undefined; + return this; + }, + // Is it disabled? + disabled: function() { + return !list; + }, + // Lock the list in its current state + lock: function() { + stack = undefined; + if ( !memory ) { + self.disable(); + } + return this; + }, + // Is it locked? + locked: function() { + return !stack; + }, + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( list && ( !fired || stack ) ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + if ( firing ) { + stack.push( args ); + } else { + fire( args ); + } + } + return this; + }, + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + 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() + .done( newDefer.resolve ) + .fail( newDefer.reject ) + .progress( newDefer.notify ); + } 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() + .done( updateFunc( i, resolveContexts, resolveValues ) ) + .fail( deferred.reject ) + .progress( updateFunc( i, progressContexts, progressValues ) ); + } else { + --remaining; + } + } + } + + // If we're not waiting on anything, resolve the master + if ( !remaining ) { + deferred.resolveWith( resolveContexts, resolveValues ); + } + + return deferred.promise(); + } +}); + + +// 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" ); + } + } +}); + +/** + * The ready event handler and self cleanup method + */ +function completed() { + document.removeEventListener( "DOMContentLoaded", completed, false ); + window.removeEventListener( "load", completed, false ); + 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. + // We once tried to use readyState "interactive" here, but it caused issues like the one + // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + setTimeout( jQuery.ready ); + + } else { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed, false ); + } + } + return readyList.promise( obj ); +}; + +// Kick off the DOM ready check even if the user does not +jQuery.ready.promise(); + + + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + len = elems.length, + bulk = key == null; + + // Sets many values + if ( jQuery.type( key ) === "object" ) { + chainable = true; + for ( i in key ) { + jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !jQuery.isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < len; i++ ) { + fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); + } + } + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + len ? fn( elems[0], key ) : emptyGet; +}; + + +/** + * Determines whether an object can have data + */ +jQuery.acceptData = function( owner ) { + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + /* jshint -W018 */ + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); +}; + + +function Data() { + // Support: Android<4, + // Old WebKit does not have Object.preventExtensions/freeze method, + // return new empty object instead with no [[set]] accessor + Object.defineProperty( this.cache = {}, 0, { + get: function() { + return {}; + } + }); + + this.expando = jQuery.expando + Data.uid++; +} + +Data.uid = 1; +Data.accepts = jQuery.acceptData; + +Data.prototype = { + key: function( owner ) { + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return the key for a frozen object. + if ( !Data.accepts( owner ) ) { + return 0; + } + + var descriptor = {}, + // Check if the owner object already has a cache key + unlock = owner[ this.expando ]; + + // If not, create one + if ( !unlock ) { + unlock = Data.uid++; + + // Secure it in a non-enumerable, non-writable property + try { + descriptor[ this.expando ] = { value: unlock }; + Object.defineProperties( owner, descriptor ); + + // Support: Android<4 + // Fallback to a less secure definition + } catch ( e ) { + descriptor[ this.expando ] = unlock; + jQuery.extend( owner, descriptor ); + } + } + + // Ensure the cache object + if ( !this.cache[ unlock ] ) { + this.cache[ unlock ] = {}; + } + + return unlock; + }, + set: function( owner, data, value ) { + var prop, + // There may be an unlock assigned to this node, + // if there is no entry for this "owner", create one inline + // and set the unlock as though an owner entry had always existed + unlock = this.key( owner ), + cache = this.cache[ unlock ]; + + // Handle: [ owner, key, value ] args + if ( typeof data === "string" ) { + cache[ data ] = value; + + // Handle: [ owner, { properties } ] args + } else { + // Fresh assignments by object are shallow copied + if ( jQuery.isEmptyObject( cache ) ) { + jQuery.extend( this.cache[ unlock ], data ); + // Otherwise, copy the properties one-by-one to the cache object + } else { + for ( prop in data ) { + cache[ prop ] = data[ prop ]; + } + } + } + return cache; + }, + get: function( owner, key ) { + // Either a valid cache is found, or will be created. + // New caches will be created and the unlock returned, + // allowing direct access to the newly created + // empty data object. A valid owner object must be provided. + var cache = this.cache[ this.key( owner ) ]; + + return key === undefined ? + cache : cache[ key ]; + }, + access: function( owner, key, value ) { + var stored; + // 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) ) { + + stored = this.get( owner, key ); + + return stored !== undefined ? + stored : this.get( owner, jQuery.camelCase(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, name, camel, + unlock = this.key( owner ), + cache = this.cache[ unlock ]; + + if ( key === undefined ) { + this.cache[ unlock ] = {}; + + } else { + // Support array or space separated string of keys + if ( jQuery.isArray( key ) ) { + // 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 = key.concat( key.map( jQuery.camelCase ) ); + } else { + camel = jQuery.camelCase( key ); + // Try the string as a key before any manipulation + if ( key in cache ) { + name = [ key, camel ]; + } else { + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + name = camel; + name = name in cache ? + [ name ] : ( name.match( rnotwhite ) || [] ); + } + } + + i = name.length; + while ( i-- ) { + delete cache[ name[ i ] ]; + } + } + }, + hasData: function( owner ) { + return !jQuery.isEmptyObject( + this.cache[ owner[ this.expando ] ] || {} + ); + }, + discard: function( owner ) { + if ( owner[ this.expando ] ) { + delete this.cache[ owner[ this.expando ] ]; + } + } +}; +var data_priv = new Data(); + +var data_user = 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 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, "-$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 + data_user.set( elem, key, data ); + } else { + data = undefined; + } + } + return data; +} + +jQuery.extend({ + hasData: function( elem ) { + return data_user.hasData( elem ) || data_priv.hasData( elem ); + }, + + data: function( elem, name, data ) { + return data_user.access( elem, name, data ); + }, + + removeData: function( elem, name ) { + data_user.remove( elem, name ); + }, + + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to data_priv methods, these can be deprecated. + _data: function( elem, name, data ) { + return data_priv.access( elem, name, data ); + }, + + _removeData: function( elem, name ) { + data_priv.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 = data_user.get( elem ); + + if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) { + 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 ] ); + } + } + } + data_priv.set( elem, "hasDataAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each(function() { + data_user.set( this, key ); + }); + } + + return access( this, function( value ) { + var data, + camelKey = jQuery.camelCase( key ); + + // 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 + // with the key as-is + data = data_user.get( elem, key ); + if ( data !== undefined ) { + return data; + } + + // Attempt to get data from the cache + // with the key camelized + data = data_user.get( elem, camelKey ); + if ( data !== undefined ) { + return data; + } + + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, camelKey, undefined ); + if ( data !== undefined ) { + return data; + } + + // We tried really hard, but the data doesn't exist. + return; + } + + // Set the data... + this.each(function() { + // First, attempt to store a copy or reference of any + // data that might've been store with a camelCased key. + var data = data_user.get( this, camelKey ); + + // For HTML5 data-* attribute interop, we have to + // store property names with dashes in a camelCase form. + // This might not apply to all properties...* + data_user.set( this, camelKey, value ); + + // *... In the case of properties that might _actually_ + // have dashes, we need to also store a copy of that + // unchanged property. + if ( key.indexOf("-") !== -1 && data !== undefined ) { + data_user.set( this, key, value ); + } + }); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each(function() { + data_user.remove( this, key ); + }); + } +}); + + +jQuery.extend({ + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = data_priv.get( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || jQuery.isArray( data ) ) { + queue = data_priv.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 data_priv.get( elem, key ) || data_priv.access( elem, key, { + empty: jQuery.Callbacks("once memory").add(function() { + data_priv.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 = data_priv.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 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 ); + }; + +var rcheckableType = (/^(?:checkbox|radio)$/i); + + + +(function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // Support: Safari<=5.1 + // 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: Safari<=5.1, Android<4.2 + // Older WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE<=11+ + // Make sure textarea (and checkbox) defaultValue is properly cloned + div.innerHTML = "<textarea>x</textarea>"; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; +})(); +var strundefined = typeof undefined; + + + +support.focusinBubbles = "onfocusin" in window; + + +var + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu)|click/, + rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +/* + * 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 = data_priv.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; + } + + // 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 !== strundefined && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } + + // 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 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, false ); + } + } + } + + 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 = data_priv.hasData( elem ) && data_priv.get( 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; + data_priv.remove( elem, "events" ); + } + }, + + trigger: function( event, data, elem, onlyHandlers ) { + + var i, cur, tmp, bubbleType, ontype, handle, special, + 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(".") >= 0 ) { + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split("."); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf(":") < 0 && "on" + type; + + // 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.namespace_re = 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 = ( data_priv.get( cur, "events" ) || {} )[ event.type ] && data_priv.get( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && jQuery.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) && + jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && jQuery.isFunction( 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; + elem[ type ](); + 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 = ( data_priv.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; + 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.namespace_re || event.namespace_re.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; + + // Find delegate handlers + // Black-hole SVG <use> instance trees (#13180) + // Avoid non-left-click bubbling in Firefox (#3861) + if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( 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 ) >= 0 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matches[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push({ elem: cur, handlers: matches }); + } + } + } + } + + // Add the remaining (directly-bound) handlers + if ( delegateCount < handlers.length ) { + handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); + } + + return handlerQueue; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split(" "), + filter: function( event, original ) { + + // 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 offsetX offsetY pageX pageY screenX screenY toElement".split(" "), + filter: function( event, original ) { + var eventDoc, doc, body, + button = original.button; + + // 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 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; + } + }, + + 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: Cordova 2.5 (WebKit) (#13255) + // All events should have a target; Cordova deviceready doesn't + if ( !event.target ) { + event.target = document; + } + + // Support: Safari 6.0+, Chrome<28 + // Target should not be a text node (#504, #13143) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + return fixHook.filter ? fixHook.filter( event, originalEvent ) : 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 ) { + 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 && jQuery.nodeName( this, "input" ) ) { + 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; + } + } + } + }, + + simulate: function( type, elem, event, bubble ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true, + originalEvent: {} + } + ); + if ( bubble ) { + jQuery.event.trigger( e, null, elem ); + } else { + jQuery.event.dispatch.call( elem, e ); + } + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } + } +}; + +jQuery.removeEvent = function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } +}; + +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<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 = { + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + + if ( e && e.preventDefault ) { + e.preventDefault(); + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + + if ( e && e.stopPropagation ) { + e.stopPropagation(); + } + }, + 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 +// Support: Chrome 15+ +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 mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || (related !== target && !jQuery.contains( target, related )) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +}); + +// Support: Firefox, Chrome, Safari +// Create "bubbling" focus and blur events +if ( !support.focusinBubbles ) { + 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 ), true ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + var doc = this.ownerDocument || this, + attaches = data_priv.access( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + data_priv.access( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this, + attaches = data_priv.access( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + data_priv.remove( doc, fix ); + + } else { + data_priv.access( doc, fix, attaches ); + } + } + }; + }); +} + +jQuery.fn.extend({ + + on: function( types, selector, data, fn, /*INTERNAL*/ 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 ) { + this.on( type, selector, data, types[ type ], one ); + } + return this; + } + + if ( data == null && fn == null ) { + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return this; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return this.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + }); + }, + one: function( types, selector, data, fn ) { + return this.on( types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each(function() { + jQuery.event.remove( this, types, fn, selector ); + }); + }, + + 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 + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, + rtagName = /<([\w:]+)/, + rhtml = /<|&#?\w+;/, + rnoInnerhtml = /<(?:script|style|link)/i, + // checked="checked" or checked + rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, + rscriptType = /^$|\/(?:java|ecma)script/i, + rscriptTypeMasked = /^true\/(.*)/, + rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g, + + // We have to close these tags to support XHTML (#13200) + wrapMap = { + + // Support: IE9 + option: [ 1, "<select multiple='multiple'>", "</select>" ], + + 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: IE9 +wrapMap.optgroup = wrapMap.option; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// Support: 1.x compatibility +// 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 = (elem.getAttribute("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; +} + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + data_priv.set( + elems[ i ], "globalEval", !refElements || data_priv.get( refElements[ i ], "globalEval" ) + ); + } +} + +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 ( data_priv.hasData( src ) ) { + pdataOld = data_priv.access( src ); + pdataCur = data_priv.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 ( data_user.hasData( src ) ) { + udataOld = data_user.access( src ); + udataCur = jQuery.extend( {}, udataOld ); + + data_user.set( dest, udataCur ); + } +} + +function getAll( context, tag ) { + var ret = context.getElementsByTagName ? context.getElementsByTagName( tag || "*" ) : + context.querySelectorAll ? context.querySelectorAll( tag || "*" ) : + []; + + return tag === undefined || tag && jQuery.nodeName( context, tag ) ? + jQuery.merge( [ context ], ret ) : + ret; +} + +// 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; + } +} + +jQuery.extend({ + 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: http://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; + }, + + buildFragment: function( elems, context, scripts, selection ) { + 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 ( jQuery.type( elem ) === "object" ) { + // Support: QtWebKit, PhantomJS + // 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 ] + elem.replace( rxhtmlTag, "<$1></$2>" ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: QtWebKit, PhantomJS + // 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++ ]) ) { + + // #4087 - If origin and destination elements are the same, and this is + // that element, do not do anything + if ( selection && jQuery.inArray( elem, selection ) !== -1 ) { + continue; + } + + 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; + }, + + cleanData: function( elems ) { + var data, elem, type, key, + special = jQuery.event.special, + i = 0; + + for ( ; (elem = elems[ i ]) !== undefined; i++ ) { + if ( jQuery.acceptData( elem ) ) { + key = elem[ data_priv.expando ]; + + if ( key && (data = data_priv.cache[ key ]) ) { + 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 ); + } + } + } + if ( data_priv.cache[ key ] ) { + // Discard any remaining `private` data + delete data_priv.cache[ key ]; + } + } + } + // Discard any remaining `user` data + delete data_user.cache[ elem[ data_user.expando ] ]; + } + } +}); + +jQuery.fn.extend({ + 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 this.domManip( arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + }); + }, + + prepend: function() { + return this.domManip( arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + }); + }, + + before: function() { + return this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + }); + }, + + after: function() { + return this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + }); + }, + + remove: function( selector, keepData /* Internal Use Only */ ) { + var elem, + elems = selector ? jQuery.filter( selector, this ) : this, + i = 0; + + for ( ; (elem = elems[i]) != null; i++ ) { + if ( !keepData && elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem ) ); + } + + if ( elem.parentNode ) { + if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { + setGlobalEval( getAll( elem, "script" ) ); + } + elem.parentNode.removeChild( elem ); + } + } + + return this; + }, + + 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 = value.replace( rxhtmlTag, "<$1></$2>" ); + + 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 arg = arguments[ 0 ]; + + // Make the changes, replacing each context element with the new content + this.domManip( arguments, function( elem ) { + arg = this.parentNode; + + jQuery.cleanData( getAll( this ) ); + + if ( arg ) { + arg.replaceChild( elem, this ); + } + }); + + // Force removal if there was no new content (e.g., from empty arguments) + return arg && (arg.length || arg.nodeType) ? this : this.remove(); + }, + + detach: function( selector ) { + return this.remove( selector, true ); + }, + + domManip: function( args, callback ) { + + // Flatten any nested arrays + args = concat.apply( [], args ); + + var fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = this.length, + set = this, + iNoClone = l - 1, + value = args[ 0 ], + isFunction = jQuery.isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( isFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return this.each(function( index ) { + var self = set.eq( index ); + if ( isFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + self.domManip( args, callback ); + }); + } + + if ( l ) { + fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + if ( first ) { + 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: QtWebKit + // jQuery.merge because push.apply(_, arraylike) throws + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( this[ 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 || "" ) && + !data_priv.access( 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.textContent.replace( rcleanScript, "" ) ); + } + } + } + } + } + } + + return this; + } +}); + +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: QtWebKit + // .get() because push.apply(_, arraylike) throws + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +}); + + +var iframe, + elemdisplay = {}; + +/** + * 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 style, + elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ), + + // getDefaultComputedStyle might be reliably used only on attached element + display = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ? + + // Use of this method is a temporary fix (more like optimization) until something better comes along, + // since it was removed from specification and supported only in FF + style.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 ].contentDocument; + + // 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 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" + if ( elem.ownerDocument.defaultView.opener ) { + return elem.ownerDocument.defaultView.getComputedStyle( elem, null ); + } + + return window.getComputedStyle( elem, null ); + }; + + + +function curCSS( elem, name, computed ) { + var width, minWidth, maxWidth, ret, + style = elem.style; + + computed = computed || getStyles( elem ); + + // Support: IE9 + // getPropertyValue is only needed for .css('filter') (#12537) + if ( computed ) { + ret = computed.getPropertyValue( name ) || computed[ name ]; + } + + if ( computed ) { + + if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { + ret = jQuery.style( elem, name ); + } + + // Support: iOS < 6 + // A tribute to the "awesome hack by Dean Edwards" + // iOS < 6 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels + // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values + if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) { + + // 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 + // 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 ); + } + }; +} + + +(function() { + var pixelPositionVal, boxSizingReliableVal, + docElem = document.documentElement, + container = document.createElement( "div" ), + div = document.createElement( "div" ); + + if ( !div.style ) { + return; + } + + // Support: IE9-11+ + // 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"; + + container.style.cssText = "border:0;width:0;height:0;top:0;left:-9999px;margin-top:1px;" + + "position:absolute"; + container.appendChild( div ); + + // Executing both pixelPosition & boxSizingReliable tests require only one layout + // so they're executed at the same time to save the second computation. + function computePixelPositionAndBoxSizingReliable() { + div.style.cssText = + // Support: Firefox<29, Android 2.3 + // Vendor-prefix box-sizing + "-webkit-box-sizing:border-box;-moz-box-sizing:border-box;" + + "box-sizing:border-box;display:block;margin-top:1%;top:1%;" + + "border:1px;padding:1px;width:4px;position:absolute"; + div.innerHTML = ""; + docElem.appendChild( container ); + + var divStyle = window.getComputedStyle( div, null ); + pixelPositionVal = divStyle.top !== "1%"; + boxSizingReliableVal = divStyle.width === "4px"; + + docElem.removeChild( container ); + } + + // Support: node.js jsdom + // Don't assume that getComputedStyle is a property of the global object + if ( window.getComputedStyle ) { + jQuery.extend( support, { + pixelPosition: function() { + + // This test is executed only once but we still do memoizing + // since we can use the boxSizingReliable pre-computing. + // No need to check if the test was already performed, though. + computePixelPositionAndBoxSizingReliable(); + return pixelPositionVal; + }, + boxSizingReliable: function() { + if ( boxSizingReliableVal == null ) { + computePixelPositionAndBoxSizingReliable(); + } + return boxSizingReliableVal; + }, + reliableMarginRight: function() { + + // Support: Android 2.3 + // Check if 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 + // This support function is only executed once so no memoizing is needed. + var ret, + marginDiv = div.appendChild( document.createElement( "div" ) ); + + // Reset CSS: box-sizing; display; margin; border; padding + marginDiv.style.cssText = 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:0"; + marginDiv.style.marginRight = marginDiv.style.width = "0"; + div.style.width = "1px"; + docElem.appendChild( container ); + + ret = !parseFloat( window.getComputedStyle( marginDiv, null ).marginRight ); + + docElem.removeChild( container ); + div.removeChild( marginDiv ); + + return ret; + } + }); + } +})(); + + +// A method for quickly swapping in/out CSS properties to get correct calculations. +jQuery.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 + // 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" ), + rrelNum = new RegExp( "^([+-])=(" + pnum + ")", "i" ), + + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: "0", + fontWeight: "400" + }, + + cssPrefixes = [ "Webkit", "O", "Moz", "ms" ]; + +// Return a css property mapped to a potentially vendor prefixed property +function vendorPropName( style, name ) { + + // Shortcut for names that are not vendor prefixed + if ( name in style ) { + return name; + } + + // Check for vendor prefixed names + var capName = name[0].toUpperCase() + name.slice(1), + origName = name, + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in style ) { + return name; + } + } + + return origName; +} + +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 = 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; + } + + // 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"; +} + +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 ] = data_priv.get( 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 ] = data_priv.access( elem, "olddisplay", defaultDisplay(elem.nodeName) ); + } + } else { + hidden = isHidden( elem ); + + if ( display !== "none" || !hidden ) { + data_priv.set( 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; +} + +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: { + "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: { + "float": "cssFloat" + }, + + // 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( style, 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 = rrelNum.exec( value )) ) { + value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) ); + // 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, add 'px' to the (except for certain CSS properties) + if ( type === "number" && !jQuery.cssNumber[ origName ] ) { + value += "px"; + } + + // Support: IE9-11+ + // 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 ) { + 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 = jQuery.camelCase( name ); + + // Make sure that we're working with the right name + name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, 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 || jQuery.isNumeric( 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 + // but it must have a current display style that would benefit + return rdisplayswap.test( jQuery.css( elem, "display" ) ) && elem.offsetWidth === 0 ? + jQuery.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, + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + styles + ) : 0 + ); + } + }; +}); + +// Support: Android 2.3 +jQuery.cssHooks.marginRight = addGetHookIf( support.reliableMarginRight, + function( elem, computed ) { + if ( computed ) { + return jQuery.swap( elem, { "display": "inline-block" }, + curCSS, [ elem, "marginRight" ] ); + } + } +); + +// 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 || "swing"; + this.options = options; + this.start = this.now = this.cur(); + this.end = end; + this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + }, + cur: function() { + var hooks = Tween.propHooks[ this.prop ]; + + return hooks && hooks.get ? + hooks.get( this ) : + Tween.propHooks._default.get( this ); + }, + run: function( percent ) { + var eased, + hooks = Tween.propHooks[ this.prop ]; + + if ( this.options.duration ) { + this.pos = eased = jQuery.easing[ this.easing ]( + percent, this.options.duration * percent, 0, 1, this.options.duration + ); + } else { + this.pos = eased = percent; + } + this.now = ( this.end - this.start ) * eased + this.start; + + if ( this.options.step ) { + this.options.step.call( this.elem, this.now, this ); + } + + 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; + + if ( tween.elem[ tween.prop ] != null && + (!tween.elem.style || 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.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) { + jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); + } else { + tween.elem[ tween.prop ] = tween.now; + } + } + } +}; + +// Support: IE9 +// 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; + } +}; + +jQuery.fx = Tween.prototype.init; + +// Back Compat <1.8 extension point +jQuery.fx.step = {}; + + + + +var + fxNow, timerId, + rfxtypes = /^(?:toggle|show|hide)$/, + rfxnum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ), + rrun = /queueHooks$/, + animationPrefilters = [ defaultPrefilter ], + tweeners = { + "*": [ function( prop, value ) { + var tween = this.createTween( prop, value ), + target = tween.cur(), + parts = rfxnum.exec( value ), + unit = parts && parts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), + + // Starting value computation is required for potential unit mismatches + start = ( jQuery.cssNumber[ prop ] || unit !== "px" && +target ) && + rfxnum.exec( jQuery.css( tween.elem, prop ) ), + scale = 1, + maxIterations = 20; + + if ( start && start[ 3 ] !== unit ) { + // Trust units reported by jQuery.css + unit = unit || start[ 3 ]; + + // Make sure we update the tween properties later on + parts = parts || []; + + // Iteratively approximate from a nonzero starting point + start = +target || 1; + + do { + // If previous iteration zeroed out, double until we get *something*. + // Use string for doubling so we don't accidentally see scale as unchanged below + scale = scale || ".5"; + + // Adjust and apply + start = start / scale; + jQuery.style( tween.elem, prop, start + 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 = tween.cur() / target) && scale !== 1 && --maxIterations ); + } + + // Update tween properties + if ( parts ) { + start = tween.start = +start || +target || 0; + tween.unit = unit; + // If a +=/-= token was provided, we're doing a relative animation + tween.end = parts[ 1 ] ? + start + ( parts[ 1 ] + 1 ) * parts[ 2 ] : + +parts[ 2 ]; + } + + return tween; + } ] + }; + +// Animations created synchronously will run synchronously +function createFxNow() { + setTimeout(function() { + fxNow = undefined; + }); + return ( fxNow = jQuery.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 = ( tweeners[ prop ] || [] ).concat( 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 = data_priv.get( 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() { + // Ensure the complete handler is 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 IE9-10 do 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" ? + data_priv.get( elem, "olddisplay" ) || defaultDisplay( elem.nodeName ) : display; + + if ( checkDisplay === "inline" && jQuery.css( elem, "float" ) === "none" ) { + 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 ]; + }); + } + + // 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 = data_priv.access( 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; + + data_priv.remove( 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 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 = animationPrefilters.length, + deferred = jQuery.Deferred().always( function() { + // Don't match elem in the :animated selector + delete tick.elem; + }), + tick = function() { + if ( stopped ) { + return false; + } + var currentTime = fxNow || createFxNow(), + remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), + // 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: {} }, options ), + originalProperties: properties, + originalOptions: options, + startTime: fxNow || createFxNow(), + duration: options.duration, + tweens: [], + createTween: function( prop, end ) { + var tween = jQuery.Tween( elem, animation.opts, prop, end, + animation.opts.specialEasing[ prop ] || animation.opts.easing ); + animation.tweens.push( tween ); + return tween; + }, + stop: function( gotoEnd ) { + var index = 0, + // If we are going to the end, we want to run all the tweens + // otherwise we skip this part + length = gotoEnd ? animation.tweens.length : 0; + if ( stopped ) { + return this; + } + stopped = true; + for ( ; index < length ; index++ ) { + animation.tweens[ index ].run( 1 ); + } + + // Resolve when we played the last frame; otherwise, reject + if ( gotoEnd ) { + deferred.resolveWith( elem, [ animation, gotoEnd ] ); + } else { + deferred.rejectWith( elem, [ animation, gotoEnd ] ); + } + return this; + } + }), + props = animation.props; + + propFilter( props, animation.opts.specialEasing ); + + for ( ; index < length ; index++ ) { + result = animationPrefilters[ index ].call( animation, elem, props, animation.opts ); + if ( 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, { + + tweener: function( props, callback ) { + if ( jQuery.isFunction( props ) ) { + callback = props; + props = [ "*" ]; + } else { + props = props.split(" "); + } + + var prop, + index = 0, + length = props.length; + + for ( ; index < length ; index++ ) { + prop = props[ index ]; + tweeners[ prop ] = tweeners[ prop ] || []; + tweeners[ prop ].unshift( callback ); + } + }, + + prefilter: function( callback, prepend ) { + if ( prepend ) { + animationPrefilters.unshift( callback ); + } else { + animationPrefilters.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 || data_priv.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 = data_priv.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 = data_priv.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 = 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 = setInterval( jQuery.fx.tick, jQuery.fx.interval ); + } +}; + +jQuery.fx.stop = function() { + 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://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 = setTimeout( next, time ); + hooks.stop = function() { + clearTimeout( timeout ); + }; + }); +}; + + +(function() { + var input = document.createElement( "input" ), + select = document.createElement( "select" ), + opt = select.appendChild( document.createElement( "option" ) ); + + input.type = "checkbox"; + + // Support: iOS<=5.1, Android<=4.2+ + // Default value for a checkbox should be "on" + support.checkOn = input.value !== ""; + + // Support: IE<=11+ + // Must access selectedIndex to make default options select + support.optSelected = opt.selected; + + // Support: Android<=2.3 + // Options inside disabled selects are incorrectly marked as disabled + select.disabled = true; + support.optDisabled = !opt.disabled; + + // Support: IE<=11+ + // 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 nodeHook, 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 hooks, ret, + nType = elem.nodeType; + + // don't get/set attributes on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === strundefined ) { + return jQuery.prop( elem, name, value ); + } + + // All attributes are lowercase + // Grab necessary hook if one is defined + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + name = name.toLowerCase(); + hooks = jQuery.attrHooks[ name ] || + ( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook ); + } + + if ( value !== undefined ) { + + if ( value === null ) { + jQuery.removeAttr( elem, name ); + + } else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + elem.setAttribute( name, value + "" ); + return value; + } + + } else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + ret = jQuery.find.attr( elem, name ); + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? + undefined : + ret; + } + }, + + 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 + elem[ propName ] = false; + } + + elem.removeAttribute( name ); + } + } + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !support.radioValue && value === "radio" && + jQuery.nodeName( elem, "input" ) ) { + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + } +}); + +// 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; + 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; + }; +}); + + + + +var rfocusable = /^(?:input|select|textarea|button)$/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({ + propFix: { + "for": "htmlFor", + "class": "className" + }, + + prop: function( elem, name, value ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // Don't get/set properties on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + if ( notxml ) { + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ? + ret : + ( elem[ name ] = value ); + + } else { + return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ? + ret : + elem[ name ]; + } + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + return elem.hasAttribute( "tabindex" ) || rfocusable.test( elem.nodeName ) || elem.href ? + elem.tabIndex : + -1; + } + } + } +}); + +if ( !support.optSelected ) { + jQuery.propHooks.selected = { + get: function( elem ) { + var parent = elem.parentNode; + if ( parent && parent.parentNode ) { + parent.parentNode.selectedIndex; + } + return null; + } + }; +} + +jQuery.each([ + "tabIndex", + "readOnly", + "maxLength", + "cellSpacing", + "cellPadding", + "rowSpan", + "colSpan", + "useMap", + "frameBorder", + "contentEditable" +], function() { + jQuery.propFix[ this.toLowerCase() ] = this; +}); + + + + +var rclass = /[\t\r\n\f]/g; + +jQuery.fn.extend({ + addClass: function( value ) { + var classes, elem, cur, clazz, j, finalValue, + proceed = typeof value === "string" && value, + i = 0, + len = this.length; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).addClass( value.call( this, j, this.className ) ); + }); + } + + if ( proceed ) { + // The disjunction here is for better compressibility (see removeClass) + classes = ( value || "" ).match( rnotwhite ) || []; + + for ( ; i < len; i++ ) { + elem = this[ i ]; + cur = elem.nodeType === 1 && ( elem.className ? + ( " " + elem.className + " " ).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 ( elem.className !== finalValue ) { + elem.className = finalValue; + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classes, elem, cur, clazz, j, finalValue, + proceed = arguments.length === 0 || typeof value === "string" && value, + i = 0, + len = this.length; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).removeClass( value.call( this, j, this.className ) ); + }); + } + if ( proceed ) { + classes = ( value || "" ).match( rnotwhite ) || []; + + for ( ; i < len; i++ ) { + elem = this[ i ]; + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && ( elem.className ? + ( " " + elem.className + " " ).replace( rclass, " " ) : + "" + ); + + if ( cur ) { + j = 0; + while ( (clazz = classes[j++]) ) { + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) >= 0 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = value ? jQuery.trim( cur ) : ""; + if ( elem.className !== finalValue ) { + elem.className = 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, this.className, stateVal), stateVal ); + }); + } + + return this.each(function() { + if ( type === "string" ) { + // Toggle individual class names + var className, + 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 ( type === strundefined || type === "boolean" ) { + if ( this.className ) { + // store className if set + data_priv.set( this, "__className__", this.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. + this.className = this.className || value === false ? "" : data_priv.get( this, "__className__" ) || ""; + } + }); + }, + + hasClass: function( selector ) { + var className = " " + selector + " ", + i = 0, + l = this.length; + for ( ; i < l; i++ ) { + if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) { + return true; + } + } + + return false; + } +}); + + + + +var rreturn = /\r/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) + jQuery.trim( jQuery.text( elem ) ); + } + }, + 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 ]; + + // IE6-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 + ( 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 ( (option.selected = jQuery.inArray( option.value, values ) >= 0) ) { + optionSet = true; + } + } + + // 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 ( jQuery.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); + } + } + }; + if ( !support.checkOn ) { + jQuery.valHooks[ this ].get = function( elem ) { + return elem.getAttribute("value") === null ? "on" : elem.value; + }; + } +}); + + + + +// 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 ); + }, + + 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 ); + } +}); + + +var nonce = jQuery.now(); + +var rquery = (/\?/); + + + +// Support: Android 2.3 +// Workaround failure to string-cast null input +jQuery.parseJSON = function( data ) { + return JSON.parse( data + "" ); +}; + + +// Cross-browser xml parsing +jQuery.parseXML = function( data ) { + var xml, tmp; + if ( !data || typeof data !== "string" ) { + return null; + } + + // Support: IE9 + try { + tmp = new DOMParser(); + xml = tmp.parseFromString( data, "text/xml" ); + } catch ( e ) { + xml = undefined; + } + + if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; +}; + + +var + rhash = /#.*$/, + rts = /([?&])_=[^&]*/, + rheaders = /^(.*?):[ \t]*([^\r\n]*)$/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 = window.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[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: 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: /xml/, + html: /html/, + json: /json/ + }, + + 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 transport, + // URL without anti-cache param + cacheURL, + // Response headers + responseHeadersString, + responseHeaders, + // timeout handle + timeoutTimer, + // Cross-domain detection vars + parts, + // To know if global events are to be dispatched + fireGlobals, + // Loop variable + i, + // 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 (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 || 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 ] ); + } + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = 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 ) { + 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 ( jQuery.isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + return jQuery.ajax({ + url: url, + type: method, + dataType: type, + data: data, + success: callback + }); + }; +}); + + +jQuery._evalUrl = function( url ) { + return jQuery.ajax({ + url: url, + type: "GET", + dataType: "script", + async: false, + global: false, + "throws": true + }); +}; + + +jQuery.fn.extend({ + wrapAll: function( html ) { + var wrap; + + 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 + 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 ( 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(); + } +}); + + +jQuery.expr.filters.hidden = function( elem ) { + // Support: Opera <= 12.12 + // Opera reports offsetWidths and offsetHeights less than zero on some elements + return elem.offsetWidth <= 0 && elem.offsetHeight <= 0; +}; +jQuery.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" ? 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(); + } +}); + + +jQuery.ajaxSettings.xhr = function() { + try { + return new XMLHttpRequest(); + } catch( e ) {} +}; + +var xhrId = 0, + xhrCallbacks = {}, + xhrSuccessStatus = { + // file protocol always yields status code 0, assume 200 + 0: 200, + // Support: IE9 + // #1450: sometimes IE returns 1223 when it should be 204 + 1223: 204 + }, + xhrSupported = jQuery.ajaxSettings.xhr(); + +// Support: IE9 +// 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 ](); + } + }); +} + +support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); +support.ajax = xhrSupported = !!xhrSupported; + +jQuery.ajaxTransport(function( options ) { + var callback; + + // Cross domain only allowed if supported through XMLHttpRequest + if ( support.cors || xhrSupported && !options.crossDomain ) { + return { + send: function( headers, complete ) { + var i, + xhr = options.xhr(), + id = ++xhrId; + + 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 ) { + delete xhrCallbacks[ id ]; + callback = xhr.onload = xhr.onerror = null; + + if ( type === "abort" ) { + xhr.abort(); + } else if ( type === "error" ) { + complete( + // file: protocol always yields status 0; see #8605, #14207 + xhr.status, + xhr.statusText + ); + } else { + complete( + xhrSuccessStatus[ xhr.status ] || xhr.status, + xhr.statusText, + // Support: IE9 + // Accessing binary-data responseText throws an exception + // (#11426) + typeof xhr.responseText === "string" ? { + text: xhr.responseText + } : undefined, + xhr.getAllResponseHeaders() + ); + } + } + }; + }; + + // Listen to events + xhr.onload = callback(); + xhr.onerror = callback("error"); + + // Create the abort callback + callback = xhrCallbacks[ id ] = 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(); + } + } + }; + } +}); + + + + +// Install script dataType +jQuery.ajaxSetup({ + accepts: { + script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript" + }, + contents: { + script: /(?:java|ecma)script/ + }, + 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({ + async: true, + 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 ); + } + } + ); + 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") && 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() { + // Restore preexisting value + 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 = jQuery.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 >= 0 ) { + selector = jQuery.trim( url.slice( off ) ); + 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 + type: type, + 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 ); + + }).complete( callback && function( jqXHR, status ) { + self.each( callback, 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; +}; + + + + +var docElem = window.document.documentElement; + +/** + * Gets a window from an element + */ +function getWindow( elem ) { + return jQuery.isWindow( elem ) ? elem : elem.nodeType === 9 && elem.defaultView; +} + +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 ( jQuery.isFunction( options ) ) { + options = options.call( elem, i, 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, + elem = this[ 0 ], + box = { top: 0, left: 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; + } + + // Support: BlackBerry 5, iOS 3 (original iPhone) + // If we don't have gBCR, just use 0,0 rather than error + if ( typeof elem.getBoundingClientRect !== strundefined ) { + box = elem.getBoundingClientRect(); + } + win = getWindow( doc ); + return { + top: box.top + win.pageYOffset - docElem.clientTop, + left: box.left + win.pageXOffset - docElem.clientLeft + }; + }, + + position: function() { + if ( !this[ 0 ] ) { + return; + } + + var offsetParent, offset, + elem = this[ 0 ], + parentOffset = { top: 0, left: 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" ) { + // Assume getBoundingClientRect is there 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 + 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 || docElem; + + while ( offsetParent && ( !jQuery.nodeName( offsetParent, "html" ) && jQuery.css( offsetParent, "position" ) === "static" ) ) { + offsetParent = offsetParent.offsetParent; + } + + return offsetParent || docElem; + }); + } +}); + +// 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 ) { + var win = getWindow( elem ); + + if ( val === undefined ) { + return win ? win[ prop ] : elem[ method ]; + } + + if ( win ) { + win.scrollTo( + !top ? val : window.pageXOffset, + top ? val : window.pageYOffset + ); + + } else { + elem[ method ] = val; + } + }, method, val, arguments.length, null ); + }; +}); + +// Support: Safari<7+, Chrome<37+ +// Add the top/left cssHooks using jQuery.fn.position +// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084 +// Blink bug: https://code.google.com/p/chromium/issues/detail?id=229280 +// 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 ( 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 + 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 ); + }; + }); +}); + + +// 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 ( typeof noGlobal === strundefined ) { + window.jQuery = window.$ = jQuery; +} + + + + +return jQuery; + +})); diff --git a/debian/missing-sources/selectize.bootstrap3.css b/debian/missing-sources/selectize.bootstrap3.css new file mode 100644 index 0000000..c5a8510 --- /dev/null +++ b/debian/missing-sources/selectize.bootstrap3.css @@ -0,0 +1,407 @@ +/** + * selectize.bootstrap3.css (v0.12.2) - Bootstrap 3 Theme + * Copyright (c) 2013–2015 Brian Reavis & contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + * + * @author Brian Reavis <brian@thirdroute.com> + */ +.selectize-control.plugin-drag_drop.multi > .selectize-input > div.ui-sortable-placeholder { + visibility: visible !important; + background: #f2f2f2 !important; + background: rgba(0, 0, 0, 0.06) !important; + border: 0 none !important; + -webkit-box-shadow: inset 0 0 12px 4px #ffffff; + box-shadow: inset 0 0 12px 4px #ffffff; +} +.selectize-control.plugin-drag_drop .ui-sortable-placeholder::after { + content: '!'; + visibility: hidden; +} +.selectize-control.plugin-drag_drop .ui-sortable-helper { + -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); +} +.selectize-dropdown-header { + position: relative; + padding: 3px 12px; + border-bottom: 1px solid #d0d0d0; + background: #f8f8f8; + -webkit-border-radius: 4px 4px 0 0; + -moz-border-radius: 4px 4px 0 0; + border-radius: 4px 4px 0 0; +} +.selectize-dropdown-header-close { + position: absolute; + right: 12px; + top: 50%; + color: #333333; + opacity: 0.4; + margin-top: -12px; + line-height: 20px; + font-size: 20px !important; +} +.selectize-dropdown-header-close:hover { + color: #000000; +} +.selectize-dropdown.plugin-optgroup_columns .optgroup { + border-right: 1px solid #f2f2f2; + border-top: 0 none; + float: left; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.selectize-dropdown.plugin-optgroup_columns .optgroup:last-child { + border-right: 0 none; +} +.selectize-dropdown.plugin-optgroup_columns .optgroup:before { + display: none; +} +.selectize-dropdown.plugin-optgroup_columns .optgroup-header { + border-top: 0 none; +} +.selectize-control.plugin-remove_button [data-value] { + position: relative; + padding-right: 24px !important; +} +.selectize-control.plugin-remove_button [data-value] .remove { + z-index: 1; + /* fixes ie bug (see #392) */ + position: absolute; + top: 0; + right: 0; + bottom: 0; + width: 17px; + text-align: center; + font-weight: bold; + font-size: 12px; + color: inherit; + text-decoration: none; + vertical-align: middle; + display: inline-block; + padding: 1px 0 0 0; + border-left: 1px solid rgba(0, 0, 0, 0); + -webkit-border-radius: 0 2px 2px 0; + -moz-border-radius: 0 2px 2px 0; + border-radius: 0 2px 2px 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.selectize-control.plugin-remove_button [data-value] .remove:hover { + background: rgba(0, 0, 0, 0.05); +} +.selectize-control.plugin-remove_button [data-value].active .remove { + border-left-color: rgba(0, 0, 0, 0); +} +.selectize-control.plugin-remove_button .disabled [data-value] .remove:hover { + background: none; +} +.selectize-control.plugin-remove_button .disabled [data-value] .remove { + border-left-color: rgba(77, 77, 77, 0); +} +.selectize-control.plugin-remove_button .remove-single { + position: absolute; + right: 28px; + top: 6px; + font-size: 23px; +} +.selectize-control { + position: relative; +} +.selectize-dropdown, +.selectize-input, +.selectize-input input { + color: #333333; + font-family: inherit; + font-size: inherit; + line-height: 20px; + -webkit-font-smoothing: inherit; +} +.selectize-input, +.selectize-control.single .selectize-input.input-active { + background: #ffffff; + cursor: text; + display: inline-block; +} +.selectize-input { + border: 1px solid #cccccc; + padding: 6px 12px; + display: inline-block; + width: 100%; + overflow: hidden; + position: relative; + z-index: 1; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + -webkit-box-shadow: none; + box-shadow: none; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.selectize-control.multi .selectize-input.has-items { + padding: 5px 12px 2px; +} +.selectize-input.full { + background-color: #ffffff; +} +.selectize-input.disabled, +.selectize-input.disabled * { + cursor: default !important; +} +.selectize-input.focus { + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15); +} +.selectize-input.dropdown-active { + -webkit-border-radius: 4px 4px 0 0; + -moz-border-radius: 4px 4px 0 0; + border-radius: 4px 4px 0 0; +} +.selectize-input > * { + vertical-align: baseline; + display: -moz-inline-stack; + display: inline-block; + zoom: 1; + *display: inline; +} +.selectize-control.multi .selectize-input > div { + cursor: pointer; + margin: 0 3px 3px 0; + padding: 1px 3px; + background: #efefef; + color: #333333; + border: 0 solid rgba(0, 0, 0, 0); +} +.selectize-control.multi .selectize-input > div.active { + background: #428bca; + color: #ffffff; + border: 0 solid rgba(0, 0, 0, 0); +} +.selectize-control.multi .selectize-input.disabled > div, +.selectize-control.multi .selectize-input.disabled > div.active { + color: #808080; + background: #ffffff; + border: 0 solid rgba(77, 77, 77, 0); +} +.selectize-input > input { + display: inline-block !important; + padding: 0 !important; + min-height: 0 !important; + max-height: none !important; + max-width: 100% !important; + margin: 0 !important; + text-indent: 0 !important; + border: 0 none !important; + background: none !important; + line-height: inherit !important; + -webkit-user-select: auto !important; + -webkit-box-shadow: none !important; + box-shadow: none !important; +} +.selectize-input > input::-ms-clear { + display: none; +} +.selectize-input > input:focus { + outline: none !important; +} +.selectize-input::after { + content: ' '; + display: block; + clear: left; +} +.selectize-input.dropdown-active::before { + content: ' '; + display: block; + position: absolute; + background: #ffffff; + height: 1px; + bottom: 0; + left: 0; + right: 0; +} +.selectize-dropdown { + position: absolute; + z-index: 10; + border: 1px solid #d0d0d0; + background: #ffffff; + margin: -1px 0 0 0; + border-top: 0 none; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + -webkit-border-radius: 0 0 4px 4px; + -moz-border-radius: 0 0 4px 4px; + border-radius: 0 0 4px 4px; +} +.selectize-dropdown [data-selectable] { + cursor: pointer; + overflow: hidden; +} +.selectize-dropdown [data-selectable] .highlight { + background: rgba(255, 237, 40, 0.4); + -webkit-border-radius: 1px; + -moz-border-radius: 1px; + border-radius: 1px; +} +.selectize-dropdown [data-selectable], +.selectize-dropdown .optgroup-header { + padding: 3px 12px; +} +.selectize-dropdown .optgroup:first-child .optgroup-header { + border-top: 0 none; +} +.selectize-dropdown .optgroup-header { + color: #777777; + background: #ffffff; + cursor: default; +} +.selectize-dropdown .active { + background-color: #f5f5f5; + color: #262626; +} +.selectize-dropdown .active.create { + color: #262626; +} +.selectize-dropdown .create { + color: rgba(51, 51, 51, 0.5); +} +.selectize-dropdown-content { + overflow-y: auto; + overflow-x: hidden; + max-height: 200px; +} +.selectize-control.single .selectize-input, +.selectize-control.single .selectize-input input { + cursor: pointer; +} +.selectize-control.single .selectize-input.input-active, +.selectize-control.single .selectize-input.input-active input { + cursor: text; +} +.selectize-control.single .selectize-input:after { + content: ' '; + display: block; + position: absolute; + top: 50%; + right: 17px; + margin-top: -3px; + width: 0; + height: 0; + border-style: solid; + border-width: 5px 5px 0 5px; + border-color: #333333 transparent transparent transparent; +} +.selectize-control.single .selectize-input.dropdown-active:after { + margin-top: -4px; + border-width: 0 5px 5px 5px; + border-color: transparent transparent #333333 transparent; +} +.selectize-control.rtl.single .selectize-input:after { + left: 17px; + right: auto; +} +.selectize-control.rtl .selectize-input > input { + margin: 0 4px 0 -2px !important; +} +.selectize-control .selectize-input.disabled { + opacity: 0.5; + background-color: #ffffff; +} +.selectize-dropdown, +.selectize-dropdown.form-control { + height: auto; + padding: 0; + margin: 2px 0 0 0; + z-index: 1000; + background: #ffffff; + border: 1px solid #cccccc; + border: 1px solid rgba(0, 0, 0, 0.15); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); +} +.selectize-dropdown .optgroup-header { + font-size: 12px; + line-height: 1.42857143; +} +.selectize-dropdown .optgroup:first-child:before { + display: none; +} +.selectize-dropdown .optgroup:before { + content: ' '; + display: block; + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; + margin-left: -12px; + margin-right: -12px; +} +.selectize-dropdown-content { + padding: 5px 0; +} +.selectize-dropdown-header { + padding: 6px 12px; +} +.selectize-input { + min-height: 34px; +} +.selectize-input.dropdown-active { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.selectize-input.dropdown-active::before { + display: none; +} +.selectize-input.focus { + border-color: #66afe9; + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6); +} +.has-error .selectize-input { + border-color: #a94442; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.has-error .selectize-input:focus { + border-color: #843534; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; +} +.selectize-control.multi .selectize-input.has-items { + padding-left: 9px; + padding-right: 9px; +} +.selectize-control.multi .selectize-input > div { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +.form-control.selectize-control { + padding: 0; + height: auto; + border: none; + background: none; + -webkit-box-shadow: none; + box-shadow: none; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} diff --git a/debian/missing-sources/selectize.css b/debian/missing-sources/selectize.css new file mode 100644 index 0000000..a763839 --- /dev/null +++ b/debian/missing-sources/selectize.css @@ -0,0 +1,323 @@ +/** + * selectize.css (v0.12.2) + * Copyright (c) 2013–2015 Brian Reavis & contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + * + * @author Brian Reavis <brian@thirdroute.com> + */ + +.selectize-control.plugin-drag_drop.multi > .selectize-input > div.ui-sortable-placeholder { + visibility: visible !important; + background: #f2f2f2 !important; + background: rgba(0, 0, 0, 0.06) !important; + border: 0 none !important; + -webkit-box-shadow: inset 0 0 12px 4px #ffffff; + box-shadow: inset 0 0 12px 4px #ffffff; +} +.selectize-control.plugin-drag_drop .ui-sortable-placeholder::after { + content: '!'; + visibility: hidden; +} +.selectize-control.plugin-drag_drop .ui-sortable-helper { + -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); +} +.selectize-dropdown-header { + position: relative; + padding: 5px 8px; + border-bottom: 1px solid #d0d0d0; + background: #f8f8f8; + -webkit-border-radius: 3px 3px 0 0; + -moz-border-radius: 3px 3px 0 0; + border-radius: 3px 3px 0 0; +} +.selectize-dropdown-header-close { + position: absolute; + right: 8px; + top: 50%; + color: #303030; + opacity: 0.4; + margin-top: -12px; + line-height: 20px; + font-size: 20px !important; +} +.selectize-dropdown-header-close:hover { + color: #000000; +} +.selectize-dropdown.plugin-optgroup_columns .optgroup { + border-right: 1px solid #f2f2f2; + border-top: 0 none; + float: left; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.selectize-dropdown.plugin-optgroup_columns .optgroup:last-child { + border-right: 0 none; +} +.selectize-dropdown.plugin-optgroup_columns .optgroup:before { + display: none; +} +.selectize-dropdown.plugin-optgroup_columns .optgroup-header { + border-top: 0 none; +} +.selectize-control.plugin-remove_button [data-value] { + position: relative; + padding-right: 24px !important; +} +.selectize-control.plugin-remove_button [data-value] .remove { + z-index: 1; + /* fixes ie bug (see #392) */ + position: absolute; + top: 0; + right: 0; + bottom: 0; + width: 17px; + text-align: center; + font-weight: bold; + font-size: 12px; + color: inherit; + text-decoration: none; + vertical-align: middle; + display: inline-block; + padding: 2px 0 0 0; + border-left: 1px solid #d0d0d0; + -webkit-border-radius: 0 2px 2px 0; + -moz-border-radius: 0 2px 2px 0; + border-radius: 0 2px 2px 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.selectize-control.plugin-remove_button [data-value] .remove:hover { + background: rgba(0, 0, 0, 0.05); +} +.selectize-control.plugin-remove_button [data-value].active .remove { + border-left-color: #cacaca; +} +.selectize-control.plugin-remove_button .disabled [data-value] .remove:hover { + background: none; +} +.selectize-control.plugin-remove_button .disabled [data-value] .remove { + border-left-color: #ffffff; +} +.selectize-control.plugin-remove_button .remove-single { + position: absolute; + right: 28px; + top: 6px; + font-size: 23px; +} +.selectize-control { + position: relative; +} +.selectize-dropdown, +.selectize-input, +.selectize-input input { + color: #303030; + font-family: inherit; + font-size: 13px; + line-height: 18px; + -webkit-font-smoothing: inherit; +} +.selectize-input, +.selectize-control.single .selectize-input.input-active { + background: #ffffff; + cursor: text; + display: inline-block; +} +.selectize-input { + border: 1px solid #d0d0d0; + padding: 8px 8px; + display: inline-block; + width: 100%; + overflow: hidden; + position: relative; + z-index: 1; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1); + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +.selectize-control.multi .selectize-input.has-items { + padding: 6px 8px 3px; +} +.selectize-input.full { + background-color: #ffffff; +} +.selectize-input.disabled, +.selectize-input.disabled * { + cursor: default !important; +} +.selectize-input.focus { + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15); +} +.selectize-input.dropdown-active { + -webkit-border-radius: 3px 3px 0 0; + -moz-border-radius: 3px 3px 0 0; + border-radius: 3px 3px 0 0; +} +.selectize-input > * { + vertical-align: baseline; + display: -moz-inline-stack; + display: inline-block; + zoom: 1; + *display: inline; +} +.selectize-control.multi .selectize-input > div { + cursor: pointer; + margin: 0 3px 3px 0; + padding: 2px 6px; + background: #f2f2f2; + color: #303030; + border: 0 solid #d0d0d0; +} +.selectize-control.multi .selectize-input > div.active { + background: #e8e8e8; + color: #303030; + border: 0 solid #cacaca; +} +.selectize-control.multi .selectize-input.disabled > div, +.selectize-control.multi .selectize-input.disabled > div.active { + color: #7d7d7d; + background: #ffffff; + border: 0 solid #ffffff; +} +.selectize-input > input { + display: inline-block !important; + padding: 0 !important; + min-height: 0 !important; + max-height: none !important; + max-width: 100% !important; + margin: 0 2px 0 0 !important; + text-indent: 0 !important; + border: 0 none !important; + background: none !important; + line-height: inherit !important; + -webkit-user-select: auto !important; + -webkit-box-shadow: none !important; + box-shadow: none !important; +} +.selectize-input > input::-ms-clear { + display: none; +} +.selectize-input > input:focus { + outline: none !important; +} +.selectize-input::after { + content: ' '; + display: block; + clear: left; +} +.selectize-input.dropdown-active::before { + content: ' '; + display: block; + position: absolute; + background: #f0f0f0; + height: 1px; + bottom: 0; + left: 0; + right: 0; +} +.selectize-dropdown { + position: absolute; + z-index: 10; + border: 1px solid #d0d0d0; + background: #ffffff; + margin: -1px 0 0 0; + border-top: 0 none; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + -webkit-border-radius: 0 0 3px 3px; + -moz-border-radius: 0 0 3px 3px; + border-radius: 0 0 3px 3px; +} +.selectize-dropdown [data-selectable] { + cursor: pointer; + overflow: hidden; +} +.selectize-dropdown [data-selectable] .highlight { + background: rgba(125, 168, 208, 0.2); + -webkit-border-radius: 1px; + -moz-border-radius: 1px; + border-radius: 1px; +} +.selectize-dropdown [data-selectable], +.selectize-dropdown .optgroup-header { + padding: 5px 8px; +} +.selectize-dropdown .optgroup:first-child .optgroup-header { + border-top: 0 none; +} +.selectize-dropdown .optgroup-header { + color: #303030; + background: #ffffff; + cursor: default; +} +.selectize-dropdown .active { + background-color: #f5fafd; + color: #495c68; +} +.selectize-dropdown .active.create { + color: #495c68; +} +.selectize-dropdown .create { + color: rgba(48, 48, 48, 0.5); +} +.selectize-dropdown-content { + overflow-y: auto; + overflow-x: hidden; + max-height: 200px; +} +.selectize-control.single .selectize-input, +.selectize-control.single .selectize-input input { + cursor: pointer; +} +.selectize-control.single .selectize-input.input-active, +.selectize-control.single .selectize-input.input-active input { + cursor: text; +} +.selectize-control.single .selectize-input:after { + content: ' '; + display: block; + position: absolute; + top: 50%; + right: 15px; + margin-top: -3px; + width: 0; + height: 0; + border-style: solid; + border-width: 5px 5px 0 5px; + border-color: #808080 transparent transparent transparent; +} +.selectize-control.single .selectize-input.dropdown-active:after { + margin-top: -4px; + border-width: 0 5px 5px 5px; + border-color: transparent transparent #808080 transparent; +} +.selectize-control.rtl.single .selectize-input:after { + left: 15px; + right: auto; +} +.selectize-control.rtl .selectize-input > input { + margin: 0 4px 0 -2px !important; +} +.selectize-control .selectize-input.disabled { + opacity: 0.5; + background-color: #fafafa; +} diff --git a/debian/missing-sources/selectize.js b/debian/missing-sources/selectize.js new file mode 100644 index 0000000..ce28377 --- /dev/null +++ b/debian/missing-sources/selectize.js @@ -0,0 +1,3124 @@ +/** + * selectize.js (v0.12.2) + * Copyright (c) 2013–2015 Brian Reavis & contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + * + * @author Brian Reavis <brian@thirdroute.com> + */ + +/*jshint curly:false */ +/*jshint browser:true */ + +(function(root, factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery','sifter','microplugin'], factory); + } else if (typeof exports === 'object') { + module.exports = factory(require('jquery'), require('sifter'), require('microplugin')); + } else { + root.Selectize = factory(root.jQuery, root.Sifter, root.MicroPlugin); + } +}(this, function($, Sifter, MicroPlugin) { + 'use strict'; + + var highlight = function($element, pattern) { + if (typeof pattern === 'string' && !pattern.length) return; + var regex = (typeof pattern === 'string') ? new RegExp(pattern, 'i') : pattern; + + var highlight = function(node) { + var skip = 0; + if (node.nodeType === 3) { + var pos = node.data.search(regex); + if (pos >= 0 && node.data.length > 0) { + var match = node.data.match(regex); + var spannode = document.createElement('span'); + spannode.className = 'highlight'; + var middlebit = node.splitText(pos); + var endbit = middlebit.splitText(match[0].length); + var middleclone = middlebit.cloneNode(true); + spannode.appendChild(middleclone); + middlebit.parentNode.replaceChild(spannode, middlebit); + skip = 1; + } + } else if (node.nodeType === 1 && node.childNodes && !/(script|style)/i.test(node.tagName)) { + for (var i = 0; i < node.childNodes.length; ++i) { + i += highlight(node.childNodes[i]); + } + } + return skip; + }; + + return $element.each(function() { + highlight(this); + }); + }; + + var MicroEvent = function() {}; + MicroEvent.prototype = { + on: function(event, fct){ + this._events = this._events || {}; + this._events[event] = this._events[event] || []; + this._events[event].push(fct); + }, + off: function(event, fct){ + var n = arguments.length; + if (n === 0) return delete this._events; + if (n === 1) return delete this._events[event]; + + this._events = this._events || {}; + if (event in this._events === false) return; + this._events[event].splice(this._events[event].indexOf(fct), 1); + }, + trigger: function(event /* , args... */){ + this._events = this._events || {}; + if (event in this._events === false) return; + for (var i = 0; i < this._events[event].length; i++){ + this._events[event][i].apply(this, Array.prototype.slice.call(arguments, 1)); + } + } + }; + + /** + * Mixin will delegate all MicroEvent.js function in the destination object. + * + * - MicroEvent.mixin(Foobar) will make Foobar able to use MicroEvent + * + * @param {object} the object which will support MicroEvent + */ + MicroEvent.mixin = function(destObject){ + var props = ['on', 'off', 'trigger']; + for (var i = 0; i < props.length; i++){ + destObject.prototype[props[i]] = MicroEvent.prototype[props[i]]; + } + }; + + var IS_MAC = /Mac/.test(navigator.userAgent); + + var KEY_A = 65; + var KEY_COMMA = 188; + var KEY_RETURN = 13; + var KEY_ESC = 27; + var KEY_LEFT = 37; + var KEY_UP = 38; + var KEY_P = 80; + var KEY_RIGHT = 39; + var KEY_DOWN = 40; + var KEY_N = 78; + var KEY_BACKSPACE = 8; + var KEY_DELETE = 46; + var KEY_SHIFT = 16; + var KEY_CMD = IS_MAC ? 91 : 17; + var KEY_CTRL = IS_MAC ? 18 : 17; + var KEY_TAB = 9; + + var TAG_SELECT = 1; + var TAG_INPUT = 2; + + // for now, android support in general is too spotty to support validity + var SUPPORTS_VALIDITY_API = !/android/i.test(window.navigator.userAgent) && !!document.createElement('form').validity; + + var isset = function(object) { + return typeof object !== 'undefined'; + }; + + /** + * Converts a scalar to its best string representation + * for hash keys and HTML attribute values. + * + * Transformations: + * 'str' -> 'str' + * null -> '' + * undefined -> '' + * true -> '1' + * false -> '0' + * 0 -> '0' + * 1 -> '1' + * + * @param {string} value + * @returns {string|null} + */ + var hash_key = function(value) { + if (typeof value === 'undefined' || value === null) return null; + if (typeof value === 'boolean') return value ? '1' : '0'; + return value + ''; + }; + + /** + * Escapes a string for use within HTML. + * + * @param {string} str + * @returns {string} + */ + var escape_html = function(str) { + return (str + '') + .replace(/&/g, '&') + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/"/g, '"'); + }; + + /** + * Escapes "$" characters in replacement strings. + * + * @param {string} str + * @returns {string} + */ + var escape_replace = function(str) { + return (str + '').replace(/\$/g, '$$$$'); + }; + + var hook = {}; + + /** + * Wraps `method` on `self` so that `fn` + * is invoked before the original method. + * + * @param {object} self + * @param {string} method + * @param {function} fn + */ + hook.before = function(self, method, fn) { + var original = self[method]; + self[method] = function() { + fn.apply(self, arguments); + return original.apply(self, arguments); + }; + }; + + /** + * Wraps `method` on `self` so that `fn` + * is invoked after the original method. + * + * @param {object} self + * @param {string} method + * @param {function} fn + */ + hook.after = function(self, method, fn) { + var original = self[method]; + self[method] = function() { + var result = original.apply(self, arguments); + fn.apply(self, arguments); + return result; + }; + }; + + /** + * Wraps `fn` so that it can only be invoked once. + * + * @param {function} fn + * @returns {function} + */ + var once = function(fn) { + var called = false; + return function() { + if (called) return; + called = true; + fn.apply(this, arguments); + }; + }; + + /** + * Wraps `fn` so that it can only be called once + * every `delay` milliseconds (invoked on the falling edge). + * + * @param {function} fn + * @param {int} delay + * @returns {function} + */ + var debounce = function(fn, delay) { + var timeout; + return function() { + var self = this; + var args = arguments; + window.clearTimeout(timeout); + timeout = window.setTimeout(function() { + fn.apply(self, args); + }, delay); + }; + }; + + /** + * Debounce all fired events types listed in `types` + * while executing the provided `fn`. + * + * @param {object} self + * @param {array} types + * @param {function} fn + */ + var debounce_events = function(self, types, fn) { + var type; + var trigger = self.trigger; + var event_args = {}; + + // override trigger method + self.trigger = function() { + var type = arguments[0]; + if (types.indexOf(type) !== -1) { + event_args[type] = arguments; + } else { + return trigger.apply(self, arguments); + } + }; + + // invoke provided function + fn.apply(self, []); + self.trigger = trigger; + + // trigger queued events + for (type in event_args) { + if (event_args.hasOwnProperty(type)) { + trigger.apply(self, event_args[type]); + } + } + }; + + /** + * A workaround for http://bugs.jquery.com/ticket/6696 + * + * @param {object} $parent - Parent element to listen on. + * @param {string} event - Event name. + * @param {string} selector - Descendant selector to filter by. + * @param {function} fn - Event handler. + */ + var watchChildEvent = function($parent, event, selector, fn) { + $parent.on(event, selector, function(e) { + var child = e.target; + while (child && child.parentNode !== $parent[0]) { + child = child.parentNode; + } + e.currentTarget = child; + return fn.apply(this, [e]); + }); + }; + + /** + * Determines the current selection within a text input control. + * Returns an object containing: + * - start + * - length + * + * @param {object} input + * @returns {object} + */ + var getSelection = function(input) { + var result = {}; + if ('selectionStart' in input) { + result.start = input.selectionStart; + result.length = input.selectionEnd - result.start; + } else if (document.selection) { + input.focus(); + var sel = document.selection.createRange(); + var selLen = document.selection.createRange().text.length; + sel.moveStart('character', -input.value.length); + result.start = sel.text.length - selLen; + result.length = selLen; + } + return result; + }; + + /** + * Copies CSS properties from one element to another. + * + * @param {object} $from + * @param {object} $to + * @param {array} properties + */ + var transferStyles = function($from, $to, properties) { + var i, n, styles = {}; + if (properties) { + for (i = 0, n = properties.length; i < n; i++) { + styles[properties[i]] = $from.css(properties[i]); + } + } else { + styles = $from.css(); + } + $to.css(styles); + }; + + /** + * Measures the width of a string within a + * parent element (in pixels). + * + * @param {string} str + * @param {object} $parent + * @returns {int} + */ + var measureString = function(str, $parent) { + if (!str) { + return 0; + } + + var $test = $('<test>').css({ + position: 'absolute', + top: -99999, + left: -99999, + width: 'auto', + padding: 0, + whiteSpace: 'pre' + }).text(str).appendTo('body'); + + transferStyles($parent, $test, [ + 'letterSpacing', + 'fontSize', + 'fontFamily', + 'fontWeight', + 'textTransform' + ]); + + var width = $test.width(); + $test.remove(); + + return width; + }; + + /** + * Sets up an input to grow horizontally as the user + * types. If the value is changed manually, you can + * trigger the "update" handler to resize: + * + * $input.trigger('update'); + * + * @param {object} $input + */ + var autoGrow = function($input) { + var currentWidth = null; + + var update = function(e, options) { + var value, keyCode, printable, placeholder, width; + var shift, character, selection; + e = e || window.event || {}; + options = options || {}; + + if (e.metaKey || e.altKey) return; + if (!options.force && $input.data('grow') === false) return; + + value = $input.val(); + if (e.type && e.type.toLowerCase() === 'keydown') { + keyCode = e.keyCode; + printable = ( + (keyCode >= 97 && keyCode <= 122) || // a-z + (keyCode >= 65 && keyCode <= 90) || // A-Z + (keyCode >= 48 && keyCode <= 57) || // 0-9 + keyCode === 32 // space + ); + + if (keyCode === KEY_DELETE || keyCode === KEY_BACKSPACE) { + selection = getSelection($input[0]); + if (selection.length) { + value = value.substring(0, selection.start) + value.substring(selection.start + selection.length); + } else if (keyCode === KEY_BACKSPACE && selection.start) { + value = value.substring(0, selection.start - 1) + value.substring(selection.start + 1); + } else if (keyCode === KEY_DELETE && typeof selection.start !== 'undefined') { + value = value.substring(0, selection.start) + value.substring(selection.start + 1); + } + } else if (printable) { + shift = e.shiftKey; + character = String.fromCharCode(e.keyCode); + if (shift) character = character.toUpperCase(); + else character = character.toLowerCase(); + value += character; + } + } + + placeholder = $input.attr('placeholder'); + if (!value && placeholder) { + value = placeholder; + } + + width = measureString(value, $input) + 4; + if (width !== currentWidth) { + currentWidth = width; + $input.width(width); + $input.triggerHandler('resize'); + } + }; + + $input.on('keydown keyup update blur', update); + update(); + }; + + var domToString = function(d) { + var tmp = document.createElement('div'); + + tmp.appendChild(d.cloneNode(true)); + + return tmp.innerHTML; + }; + + + var Selectize = function($input, settings) { + var key, i, n, dir, input, self = this; + input = $input[0]; + input.selectize = self; + + // detect rtl environment + var computedStyle = window.getComputedStyle && window.getComputedStyle(input, null); + dir = computedStyle ? computedStyle.getPropertyValue('direction') : input.currentStyle && input.currentStyle.direction; + dir = dir || $input.parents('[dir]:first').attr('dir') || ''; + + // setup default state + $.extend(self, { + order : 0, + settings : settings, + $input : $input, + tabIndex : $input.attr('tabindex') || '', + tagType : input.tagName.toLowerCase() === 'select' ? TAG_SELECT : TAG_INPUT, + rtl : /rtl/i.test(dir), + + eventNS : '.selectize' + (++Selectize.count), + highlightedValue : null, + isOpen : false, + isDisabled : false, + isRequired : $input.is('[required]'), + isInvalid : false, + isLocked : false, + isFocused : false, + isInputHidden : false, + isSetup : false, + isShiftDown : false, + isCmdDown : false, + isCtrlDown : false, + ignoreFocus : false, + ignoreBlur : false, + ignoreHover : false, + hasOptions : false, + currentResults : null, + lastValue : '', + caretPos : 0, + loading : 0, + loadedSearches : {}, + + $activeOption : null, + $activeItems : [], + + optgroups : {}, + options : {}, + userOptions : {}, + items : [], + renderCache : {}, + onSearchChange : settings.loadThrottle === null ? self.onSearchChange : debounce(self.onSearchChange, settings.loadThrottle) + }); + + // search system + self.sifter = new Sifter(this.options, {diacritics: settings.diacritics}); + + // build options table + if (self.settings.options) { + for (i = 0, n = self.settings.options.length; i < n; i++) { + self.registerOption(self.settings.options[i]); + } + delete self.settings.options; + } + + // build optgroup table + if (self.settings.optgroups) { + for (i = 0, n = self.settings.optgroups.length; i < n; i++) { + self.registerOptionGroup(self.settings.optgroups[i]); + } + delete self.settings.optgroups; + } + + // option-dependent defaults + self.settings.mode = self.settings.mode || (self.settings.maxItems === 1 ? 'single' : 'multi'); + if (typeof self.settings.hideSelected !== 'boolean') { + self.settings.hideSelected = self.settings.mode === 'multi'; + } + + self.initializePlugins(self.settings.plugins); + self.setupCallbacks(); + self.setupTemplates(); + self.setup(); + }; + + // mixins + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + MicroEvent.mixin(Selectize); + MicroPlugin.mixin(Selectize); + + // methods + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + $.extend(Selectize.prototype, { + + /** + * Creates all elements and sets up event bindings. + */ + setup: function() { + var self = this; + var settings = self.settings; + var eventNS = self.eventNS; + var $window = $(window); + var $document = $(document); + var $input = self.$input; + + var $wrapper; + var $control; + var $control_input; + var $dropdown; + var $dropdown_content; + var $dropdown_parent; + var inputMode; + var timeout_blur; + var timeout_focus; + var classes; + var classes_plugins; + + inputMode = self.settings.mode; + classes = $input.attr('class') || ''; + + $wrapper = $('<div>').addClass(settings.wrapperClass).addClass(classes).addClass(inputMode); + $control = $('<div>').addClass(settings.inputClass).addClass('items').appendTo($wrapper); + $control_input = $('<input type="text" autocomplete="off" />').appendTo($control).attr('tabindex', $input.is(':disabled') ? '-1' : self.tabIndex); + $dropdown_parent = $(settings.dropdownParent || $wrapper); + $dropdown = $('<div>').addClass(settings.dropdownClass).addClass(inputMode).hide().appendTo($dropdown_parent); + $dropdown_content = $('<div>').addClass(settings.dropdownContentClass).appendTo($dropdown); + + if(self.settings.copyClassesToDropdown) { + $dropdown.addClass(classes); + } + + $wrapper.css({ + width: $input[0].style.width + }); + + if (self.plugins.names.length) { + classes_plugins = 'plugin-' + self.plugins.names.join(' plugin-'); + $wrapper.addClass(classes_plugins); + $dropdown.addClass(classes_plugins); + } + + if ((settings.maxItems === null || settings.maxItems > 1) && self.tagType === TAG_SELECT) { + $input.attr('multiple', 'multiple'); + } + + if (self.settings.placeholder) { + $control_input.attr('placeholder', settings.placeholder); + } + + // if splitOn was not passed in, construct it from the delimiter to allow pasting universally + if (!self.settings.splitOn && self.settings.delimiter) { + var delimiterEscaped = self.settings.delimiter.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); + self.settings.splitOn = new RegExp('\\s*' + delimiterEscaped + '+\\s*'); + } + + if ($input.attr('autocorrect')) { + $control_input.attr('autocorrect', $input.attr('autocorrect')); + } + + if ($input.attr('autocapitalize')) { + $control_input.attr('autocapitalize', $input.attr('autocapitalize')); + } + + self.$wrapper = $wrapper; + self.$control = $control; + self.$control_input = $control_input; + self.$dropdown = $dropdown; + self.$dropdown_content = $dropdown_content; + + $dropdown.on('mouseenter', '[data-selectable]', function() { return self.onOptionHover.apply(self, arguments); }); + $dropdown.on('mousedown click', '[data-selectable]', function() { return self.onOptionSelect.apply(self, arguments); }); + watchChildEvent($control, 'mousedown', '*:not(input)', function() { return self.onItemSelect.apply(self, arguments); }); + autoGrow($control_input); + + $control.on({ + mousedown : function() { return self.onMouseDown.apply(self, arguments); }, + click : function() { return self.onClick.apply(self, arguments); } + }); + + $control_input.on({ + mousedown : function(e) { e.stopPropagation(); }, + keydown : function() { return self.onKeyDown.apply(self, arguments); }, + keyup : function() { return self.onKeyUp.apply(self, arguments); }, + keypress : function() { return self.onKeyPress.apply(self, arguments); }, + resize : function() { self.positionDropdown.apply(self, []); }, + blur : function() { return self.onBlur.apply(self, arguments); }, + focus : function() { self.ignoreBlur = false; return self.onFocus.apply(self, arguments); }, + paste : function() { return self.onPaste.apply(self, arguments); } + }); + + $document.on('keydown' + eventNS, function(e) { + self.isCmdDown = e[IS_MAC ? 'metaKey' : 'ctrlKey']; + self.isCtrlDown = e[IS_MAC ? 'altKey' : 'ctrlKey']; + self.isShiftDown = e.shiftKey; + }); + + $document.on('keyup' + eventNS, function(e) { + if (e.keyCode === KEY_CTRL) self.isCtrlDown = false; + if (e.keyCode === KEY_SHIFT) self.isShiftDown = false; + if (e.keyCode === KEY_CMD) self.isCmdDown = false; + }); + + $document.on('mousedown' + eventNS, function(e) { + if (self.isFocused) { + // prevent events on the dropdown scrollbar from causing the control to blur + if (e.target === self.$dropdown[0] || e.target.parentNode === self.$dropdown[0]) { + return false; + } + // blur on click outside + if (!self.$control.has(e.target).length && e.target !== self.$control[0]) { + self.blur(e.target); + } + } + }); + + $window.on(['scroll' + eventNS, 'resize' + eventNS].join(' '), function() { + if (self.isOpen) { + self.positionDropdown.apply(self, arguments); + } + }); + $window.on('mousemove' + eventNS, function() { + self.ignoreHover = false; + }); + + // store original children and tab index so that they can be + // restored when the destroy() method is called. + this.revertSettings = { + $children : $input.children().detach(), + tabindex : $input.attr('tabindex') + }; + + $input.attr('tabindex', -1).hide().after(self.$wrapper); + + if ($.isArray(settings.items)) { + self.setValue(settings.items); + delete settings.items; + } + + // feature detect for the validation API + if (SUPPORTS_VALIDITY_API) { + $input.on('invalid' + eventNS, function(e) { + e.preventDefault(); + self.isInvalid = true; + self.refreshState(); + }); + } + + self.updateOriginalInput(); + self.refreshItems(); + self.refreshState(); + self.updatePlaceholder(); + self.isSetup = true; + + if ($input.is(':disabled')) { + self.disable(); + } + + self.on('change', this.onChange); + + $input.data('selectize', self); + $input.addClass('selectized'); + self.trigger('initialize'); + + // preload options + if (settings.preload === true) { + self.onSearchChange(''); + } + + }, + + /** + * Sets up default rendering functions. + */ + setupTemplates: function() { + var self = this; + var field_label = self.settings.labelField; + var field_optgroup = self.settings.optgroupLabelField; + + var templates = { + 'optgroup': function(data) { + return '<div class="optgroup">' + data.html + '</div>'; + }, + 'optgroup_header': function(data, escape) { + return '<div class="optgroup-header">' + escape(data[field_optgroup]) + '</div>'; + }, + 'option': function(data, escape) { + return '<div class="option">' + escape(data[field_label]) + '</div>'; + }, + 'item': function(data, escape) { + return '<div class="item">' + escape(data[field_label]) + '</div>'; + }, + 'option_create': function(data, escape) { + return '<div class="create">Add <strong>' + escape(data.input) + '</strong>…</div>'; + } + }; + + self.settings.render = $.extend({}, templates, self.settings.render); + }, + + /** + * Maps fired events to callbacks provided + * in the settings used when creating the control. + */ + setupCallbacks: function() { + var key, fn, callbacks = { + 'initialize' : 'onInitialize', + 'change' : 'onChange', + 'item_add' : 'onItemAdd', + 'item_remove' : 'onItemRemove', + 'clear' : 'onClear', + 'option_add' : 'onOptionAdd', + 'option_remove' : 'onOptionRemove', + 'option_clear' : 'onOptionClear', + 'optgroup_add' : 'onOptionGroupAdd', + 'optgroup_remove' : 'onOptionGroupRemove', + 'optgroup_clear' : 'onOptionGroupClear', + 'dropdown_open' : 'onDropdownOpen', + 'dropdown_close' : 'onDropdownClose', + 'type' : 'onType', + 'load' : 'onLoad', + 'focus' : 'onFocus', + 'blur' : 'onBlur' + }; + + for (key in callbacks) { + if (callbacks.hasOwnProperty(key)) { + fn = this.settings[callbacks[key]]; + if (fn) this.on(key, fn); + } + } + }, + + /** + * Triggered when the main control element + * has a click event. + * + * @param {object} e + * @return {boolean} + */ + onClick: function(e) { + var self = this; + + // necessary for mobile webkit devices (manual focus triggering + // is ignored unless invoked within a click event) + if (!self.isFocused) { + self.focus(); + e.preventDefault(); + } + }, + + /** + * Triggered when the main control element + * has a mouse down event. + * + * @param {object} e + * @return {boolean} + */ + onMouseDown: function(e) { + var self = this; + var defaultPrevented = e.isDefaultPrevented(); + var $target = $(e.target); + + if (self.isFocused) { + // retain focus by preventing native handling. if the + // event target is the input it should not be modified. + // otherwise, text selection within the input won't work. + if (e.target !== self.$control_input[0]) { + if (self.settings.mode === 'single') { + // toggle dropdown + self.isOpen ? self.close() : self.open(); + } else if (!defaultPrevented) { + self.setActiveItem(null); + } + return false; + } + } else { + // give control focus + if (!defaultPrevented) { + window.setTimeout(function() { + self.focus(); + }, 0); + } + } + }, + + /** + * Triggered when the value of the control has been changed. + * This should propagate the event to the original DOM + * input / select element. + */ + onChange: function() { + this.$input.trigger('change'); + }, + + /** + * Triggered on <input> paste. + * + * @param {object} e + * @returns {boolean} + */ + onPaste: function(e) { + var self = this; + if (self.isFull() || self.isInputHidden || self.isLocked) { + e.preventDefault(); + } else { + // If a regex or string is included, this will split the pasted + // input and create Items for each separate value + if (self.settings.splitOn) { + setTimeout(function() { + var splitInput = $.trim(self.$control_input.val() || '').split(self.settings.splitOn); + for (var i = 0, n = splitInput.length; i < n; i++) { + self.createItem(splitInput[i]); + } + }, 0); + } + } + }, + + /** + * Triggered on <input> keypress. + * + * @param {object} e + * @returns {boolean} + */ + onKeyPress: function(e) { + if (this.isLocked) return e && e.preventDefault(); + var character = String.fromCharCode(e.keyCode || e.which); + if (this.settings.create && this.settings.mode === 'multi' && character === this.settings.delimiter) { + this.createItem(); + e.preventDefault(); + return false; + } + }, + + /** + * Triggered on <input> keydown. + * + * @param {object} e + * @returns {boolean} + */ + onKeyDown: function(e) { + var isInput = e.target === this.$control_input[0]; + var self = this; + + if (self.isLocked) { + if (e.keyCode !== KEY_TAB) { + e.preventDefault(); + } + return; + } + + switch (e.keyCode) { + case KEY_A: + if (self.isCmdDown) { + self.selectAll(); + return; + } + break; + case KEY_ESC: + if (self.isOpen) { + e.preventDefault(); + e.stopPropagation(); + self.close(); + } + return; + case KEY_N: + if (!e.ctrlKey || e.altKey) break; + case KEY_DOWN: + if (!self.isOpen && self.hasOptions) { + self.open(); + } else if (self.$activeOption) { + self.ignoreHover = true; + var $next = self.getAdjacentOption(self.$activeOption, 1); + if ($next.length) self.setActiveOption($next, true, true); + } + e.preventDefault(); + return; + case KEY_P: + if (!e.ctrlKey || e.altKey) break; + case KEY_UP: + if (self.$activeOption) { + self.ignoreHover = true; + var $prev = self.getAdjacentOption(self.$activeOption, -1); + if ($prev.length) self.setActiveOption($prev, true, true); + } + e.preventDefault(); + return; + case KEY_RETURN: + if (self.isOpen && self.$activeOption) { + self.onOptionSelect({currentTarget: self.$activeOption}); + e.preventDefault(); + } + return; + case KEY_LEFT: + self.advanceSelection(-1, e); + return; + case KEY_RIGHT: + self.advanceSelection(1, e); + return; + case KEY_TAB: + if (self.settings.selectOnTab && self.isOpen && self.$activeOption) { + self.onOptionSelect({currentTarget: self.$activeOption}); + + // Default behaviour is to jump to the next field, we only want this + // if the current field doesn't accept any more entries + if (!self.isFull()) { + e.preventDefault(); + } + } + if (self.settings.create && self.createItem()) { + e.preventDefault(); + } + return; + case KEY_BACKSPACE: + case KEY_DELETE: + self.deleteSelection(e); + return; + } + + if ((self.isFull() || self.isInputHidden) && !(IS_MAC ? e.metaKey : e.ctrlKey)) { + e.preventDefault(); + return; + } + }, + + /** + * Triggered on <input> keyup. + * + * @param {object} e + * @returns {boolean} + */ + onKeyUp: function(e) { + var self = this; + + if (self.isLocked) return e && e.preventDefault(); + var value = self.$control_input.val() || ''; + if (self.lastValue !== value) { + self.lastValue = value; + self.onSearchChange(value); + self.refreshOptions(); + self.trigger('type', value); + } + }, + + /** + * Invokes the user-provide option provider / loader. + * + * Note: this function is debounced in the Selectize + * constructor (by `settings.loadThrottle` milliseconds) + * + * @param {string} value + */ + onSearchChange: function(value) { + var self = this; + var fn = self.settings.load; + if (!fn) return; + if (self.loadedSearches.hasOwnProperty(value)) return; + self.loadedSearches[value] = true; + self.load(function(callback) { + fn.apply(self, [value, callback]); + }); + }, + + /** + * Triggered on <input> focus. + * + * @param {object} e (optional) + * @returns {boolean} + */ + onFocus: function(e) { + var self = this; + var wasFocused = self.isFocused; + + if (self.isDisabled) { + self.blur(); + e && e.preventDefault(); + return false; + } + + if (self.ignoreFocus) return; + self.isFocused = true; + if (self.settings.preload === 'focus') self.onSearchChange(''); + + if (!wasFocused) self.trigger('focus'); + + if (!self.$activeItems.length) { + self.showInput(); + self.setActiveItem(null); + self.refreshOptions(!!self.settings.openOnFocus); + } + + self.refreshState(); + }, + + /** + * Triggered on <input> blur. + * + * @param {object} e + * @param {Element} dest + */ + onBlur: function(e, dest) { + var self = this; + if (!self.isFocused) return; + self.isFocused = false; + + if (self.ignoreFocus) { + return; + } else if (!self.ignoreBlur && document.activeElement === self.$dropdown_content[0]) { + // necessary to prevent IE closing the dropdown when the scrollbar is clicked + self.ignoreBlur = true; + self.onFocus(e); + return; + } + + var deactivate = function() { + self.close(); + self.setTextboxValue(''); + self.setActiveItem(null); + self.setActiveOption(null); + self.setCaret(self.items.length); + self.refreshState(); + + // IE11 bug: element still marked as active + dest && dest.focus(); + + self.ignoreFocus = false; + self.trigger('blur'); + }; + + self.ignoreFocus = true; + if (self.settings.create && self.settings.createOnBlur) { + self.createItem(null, false, deactivate); + } else { + deactivate(); + } + }, + + /** + * Triggered when the user rolls over + * an option in the autocomplete dropdown menu. + * + * @param {object} e + * @returns {boolean} + */ + onOptionHover: function(e) { + if (this.ignoreHover) return; + this.setActiveOption(e.currentTarget, false); + }, + + /** + * Triggered when the user clicks on an option + * in the autocomplete dropdown menu. + * + * @param {object} e + * @returns {boolean} + */ + onOptionSelect: function(e) { + var value, $target, $option, self = this; + + if (e.preventDefault) { + e.preventDefault(); + e.stopPropagation(); + } + + $target = $(e.currentTarget); + if ($target.hasClass('create')) { + self.createItem(null, function() { + if (self.settings.closeAfterSelect) { + self.close(); + } + }); + } else { + value = $target.attr('data-value'); + if (typeof value !== 'undefined') { + self.lastQuery = null; + self.setTextboxValue(''); + self.addItem(value); + if (self.settings.closeAfterSelect) { + self.close(); + } else if (!self.settings.hideSelected && e.type && /mouse/.test(e.type)) { + self.setActiveOption(self.getOption(value)); + } + } + } + }, + + /** + * Triggered when the user clicks on an item + * that has been selected. + * + * @param {object} e + * @returns {boolean} + */ + onItemSelect: function(e) { + var self = this; + + if (self.isLocked) return; + if (self.settings.mode === 'multi') { + e.preventDefault(); + self.setActiveItem(e.currentTarget, e); + } + }, + + /** + * Invokes the provided method that provides + * results to a callback---which are then added + * as options to the control. + * + * @param {function} fn + */ + load: function(fn) { + var self = this; + var $wrapper = self.$wrapper.addClass(self.settings.loadingClass); + + self.loading++; + fn.apply(self, [function(results) { + self.loading = Math.max(self.loading - 1, 0); + if (results && results.length) { + self.addOption(results); + self.refreshOptions(self.isFocused && !self.isInputHidden); + } + if (!self.loading) { + $wrapper.removeClass(self.settings.loadingClass); + } + self.trigger('load', results); + }]); + }, + + /** + * Sets the input field of the control to the specified value. + * + * @param {string} value + */ + setTextboxValue: function(value) { + var $input = this.$control_input; + var changed = $input.val() !== value; + if (changed) { + $input.val(value).triggerHandler('update'); + this.lastValue = value; + } + }, + + /** + * Returns the value of the control. If multiple items + * can be selected (e.g. <select multiple>), this returns + * an array. If only one item can be selected, this + * returns a string. + * + * @returns {mixed} + */ + getValue: function() { + if (this.tagType === TAG_SELECT && this.$input.attr('multiple')) { + return this.items; + } else { + return this.items.join(this.settings.delimiter); + } + }, + + /** + * Resets the selected items to the given value. + * + * @param {mixed} value + */ + setValue: function(value, silent) { + var events = silent ? [] : ['change']; + + debounce_events(this, events, function() { + this.clear(silent); + this.addItems(value, silent); + }); + }, + + /** + * Sets the selected item. + * + * @param {object} $item + * @param {object} e (optional) + */ + setActiveItem: function($item, e) { + var self = this; + var eventName; + var i, idx, begin, end, item, swap; + var $last; + + if (self.settings.mode === 'single') return; + $item = $($item); + + // clear the active selection + if (!$item.length) { + $(self.$activeItems).removeClass('active'); + self.$activeItems = []; + if (self.isFocused) { + self.showInput(); + } + return; + } + + // modify selection + eventName = e && e.type.toLowerCase(); + + if (eventName === 'mousedown' && self.isShiftDown && self.$activeItems.length) { + $last = self.$control.children('.active:last'); + begin = Array.prototype.indexOf.apply(self.$control[0].childNodes, [$last[0]]); + end = Array.prototype.indexOf.apply(self.$control[0].childNodes, [$item[0]]); + if (begin > end) { + swap = begin; + begin = end; + end = swap; + } + for (i = begin; i <= end; i++) { + item = self.$control[0].childNodes[i]; + if (self.$activeItems.indexOf(item) === -1) { + $(item).addClass('active'); + self.$activeItems.push(item); + } + } + e.preventDefault(); + } else if ((eventName === 'mousedown' && self.isCtrlDown) || (eventName === 'keydown' && this.isShiftDown)) { + if ($item.hasClass('active')) { + idx = self.$activeItems.indexOf($item[0]); + self.$activeItems.splice(idx, 1); + $item.removeClass('active'); + } else { + self.$activeItems.push($item.addClass('active')[0]); + } + } else { + $(self.$activeItems).removeClass('active'); + self.$activeItems = [$item.addClass('active')[0]]; + } + + // ensure control has focus + self.hideInput(); + if (!this.isFocused) { + self.focus(); + } + }, + + /** + * Sets the selected item in the dropdown menu + * of available options. + * + * @param {object} $object + * @param {boolean} scroll + * @param {boolean} animate + */ + setActiveOption: function($option, scroll, animate) { + var height_menu, height_item, y; + var scroll_top, scroll_bottom; + var self = this; + + if (self.$activeOption) self.$activeOption.removeClass('active'); + self.$activeOption = null; + + $option = $($option); + if (!$option.length) return; + + self.$activeOption = $option.addClass('active'); + + if (scroll || !isset(scroll)) { + + height_menu = self.$dropdown_content.height(); + height_item = self.$activeOption.outerHeight(true); + scroll = self.$dropdown_content.scrollTop() || 0; + y = self.$activeOption.offset().top - self.$dropdown_content.offset().top + scroll; + scroll_top = y; + scroll_bottom = y - height_menu + height_item; + + if (y + height_item > height_menu + scroll) { + self.$dropdown_content.stop().animate({scrollTop: scroll_bottom}, animate ? self.settings.scrollDuration : 0); + } else if (y < scroll) { + self.$dropdown_content.stop().animate({scrollTop: scroll_top}, animate ? self.settings.scrollDuration : 0); + } + + } + }, + + /** + * Selects all items (CTRL + A). + */ + selectAll: function() { + var self = this; + if (self.settings.mode === 'single') return; + + self.$activeItems = Array.prototype.slice.apply(self.$control.children(':not(input)').addClass('active')); + if (self.$activeItems.length) { + self.hideInput(); + self.close(); + } + self.focus(); + }, + + /** + * Hides the input element out of view, while + * retaining its focus. + */ + hideInput: function() { + var self = this; + + self.setTextboxValue(''); + self.$control_input.css({opacity: 0, position: 'absolute', left: self.rtl ? 10000 : -10000}); + self.isInputHidden = true; + }, + + /** + * Restores input visibility. + */ + showInput: function() { + this.$control_input.css({opacity: 1, position: 'relative', left: 0}); + this.isInputHidden = false; + }, + + /** + * Gives the control focus. + */ + focus: function() { + var self = this; + if (self.isDisabled) return; + + self.ignoreFocus = true; + self.$control_input[0].focus(); + window.setTimeout(function() { + self.ignoreFocus = false; + self.onFocus(); + }, 0); + }, + + /** + * Forces the control out of focus. + * + * @param {Element} dest + */ + blur: function(dest) { + this.$control_input[0].blur(); + this.onBlur(null, dest); + }, + + /** + * Returns a function that scores an object + * to show how good of a match it is to the + * provided query. + * + * @param {string} query + * @param {object} options + * @return {function} + */ + getScoreFunction: function(query) { + return this.sifter.getScoreFunction(query, this.getSearchOptions()); + }, + + /** + * Returns search options for sifter (the system + * for scoring and sorting results). + * + * @see https://github.com/brianreavis/sifter.js + * @return {object} + */ + getSearchOptions: function() { + var settings = this.settings; + var sort = settings.sortField; + if (typeof sort === 'string') { + sort = [{field: sort}]; + } + + return { + fields : settings.searchField, + conjunction : settings.searchConjunction, + sort : sort + }; + }, + + /** + * Searches through available options and returns + * a sorted array of matches. + * + * Returns an object containing: + * + * - query {string} + * - tokens {array} + * - total {int} + * - items {array} + * + * @param {string} query + * @returns {object} + */ + search: function(query) { + var i, value, score, result, calculateScore; + var self = this; + var settings = self.settings; + var options = this.getSearchOptions(); + + // validate user-provided result scoring function + if (settings.score) { + calculateScore = self.settings.score.apply(this, [query]); + if (typeof calculateScore !== 'function') { + throw new Error('Selectize "score" setting must be a function that returns a function'); + } + } + + // perform search + if (query !== self.lastQuery) { + self.lastQuery = query; + result = self.sifter.search(query, $.extend(options, {score: calculateScore})); + self.currentResults = result; + } else { + result = $.extend(true, {}, self.currentResults); + } + + // filter out selected items + if (settings.hideSelected) { + for (i = result.items.length - 1; i >= 0; i--) { + if (self.items.indexOf(hash_key(result.items[i].id)) !== -1) { + result.items.splice(i, 1); + } + } + } + + return result; + }, + + /** + * Refreshes the list of available options shown + * in the autocomplete dropdown menu. + * + * @param {boolean} triggerDropdown + */ + refreshOptions: function(triggerDropdown) { + var i, j, k, n, groups, groups_order, option, option_html, optgroup, optgroups, html, html_children, has_create_option; + var $active, $active_before, $create; + + if (typeof triggerDropdown === 'undefined') { + triggerDropdown = true; + } + + var self = this; + var query = $.trim(self.$control_input.val()); + var results = self.search(query); + var $dropdown_content = self.$dropdown_content; + var active_before = self.$activeOption && hash_key(self.$activeOption.attr('data-value')); + + // build markup + n = results.items.length; + if (typeof self.settings.maxOptions === 'number') { + n = Math.min(n, self.settings.maxOptions); + } + + // render and group available options individually + groups = {}; + groups_order = []; + + for (i = 0; i < n; i++) { + option = self.options[results.items[i].id]; + option_html = self.render('option', option); + optgroup = option[self.settings.optgroupField] || ''; + optgroups = $.isArray(optgroup) ? optgroup : [optgroup]; + + for (j = 0, k = optgroups && optgroups.length; j < k; j++) { + optgroup = optgroups[j]; + if (!self.optgroups.hasOwnProperty(optgroup)) { + optgroup = ''; + } + if (!groups.hasOwnProperty(optgroup)) { + groups[optgroup] = document.createDocumentFragment(); + groups_order.push(optgroup); + } + groups[optgroup].appendChild(option_html); + } + } + + // sort optgroups + if (this.settings.lockOptgroupOrder) { + groups_order.sort(function(a, b) { + var a_order = self.optgroups[a].$order || 0; + var b_order = self.optgroups[b].$order || 0; + return a_order - b_order; + }); + } + + // render optgroup headers & join groups + html = document.createDocumentFragment(); + for (i = 0, n = groups_order.length; i < n; i++) { + optgroup = groups_order[i]; + if (self.optgroups.hasOwnProperty(optgroup) && groups[optgroup].childNodes.length) { + // render the optgroup header and options within it, + // then pass it to the wrapper template + html_children = document.createDocumentFragment(); + html_children.appendChild(self.render('optgroup_header', self.optgroups[optgroup])); + html_children.appendChild(groups[optgroup]); + + html.appendChild(self.render('optgroup', $.extend({}, self.optgroups[optgroup], { + html: domToString(html_children), + dom: html_children + }))); + } else { + html.appendChild(groups[optgroup]); + } + } + + $dropdown_content.html(html); + + // highlight matching terms inline + if (self.settings.highlight && results.query.length && results.tokens.length) { + for (i = 0, n = results.tokens.length; i < n; i++) { + highlight($dropdown_content, results.tokens[i].regex); + } + } + + // add "selected" class to selected options + if (!self.settings.hideSelected) { + for (i = 0, n = self.items.length; i < n; i++) { + self.getOption(self.items[i]).addClass('selected'); + } + } + + // add create option + has_create_option = self.canCreate(query); + if (has_create_option) { + $dropdown_content.prepend(self.render('option_create', {input: query})); + $create = $($dropdown_content[0].childNodes[0]); + } + + // activate + self.hasOptions = results.items.length > 0 || has_create_option; + if (self.hasOptions) { + if (results.items.length > 0) { + $active_before = active_before && self.getOption(active_before); + if ($active_before && $active_before.length) { + $active = $active_before; + } else if (self.settings.mode === 'single' && self.items.length) { + $active = self.getOption(self.items[0]); + } + if (!$active || !$active.length) { + if ($create && !self.settings.addPrecedence) { + $active = self.getAdjacentOption($create, 1); + } else { + $active = $dropdown_content.find('[data-selectable]:first'); + } + } + } else { + $active = $create; + } + self.setActiveOption($active); + if (triggerDropdown && !self.isOpen) { self.open(); } + } else { + self.setActiveOption(null); + if (triggerDropdown && self.isOpen) { self.close(); } + } + }, + + /** + * Adds an available option. If it already exists, + * nothing will happen. Note: this does not refresh + * the options list dropdown (use `refreshOptions` + * for that). + * + * Usage: + * + * this.addOption(data) + * + * @param {object|array} data + */ + addOption: function(data) { + var i, n, value, self = this; + + if ($.isArray(data)) { + for (i = 0, n = data.length; i < n; i++) { + self.addOption(data[i]); + } + return; + } + + if (value = self.registerOption(data)) { + self.userOptions[value] = true; + self.lastQuery = null; + self.trigger('option_add', value, data); + } + }, + + /** + * Registers an option to the pool of options. + * + * @param {object} data + * @return {boolean|string} + */ + registerOption: function(data) { + var key = hash_key(data[this.settings.valueField]); + if (typeof key === 'undefined' || key === null || this.options.hasOwnProperty(key)) return false; + data.$order = data.$order || ++this.order; + this.options[key] = data; + return key; + }, + + /** + * Registers an option group to the pool of option groups. + * + * @param {object} data + * @return {boolean|string} + */ + registerOptionGroup: function(data) { + var key = hash_key(data[this.settings.optgroupValueField]); + if (!key) return false; + + data.$order = data.$order || ++this.order; + this.optgroups[key] = data; + return key; + }, + + /** + * Registers a new optgroup for options + * to be bucketed into. + * + * @param {string} id + * @param {object} data + */ + addOptionGroup: function(id, data) { + data[this.settings.optgroupValueField] = id; + if (id = this.registerOptionGroup(data)) { + this.trigger('optgroup_add', id, data); + } + }, + + /** + * Removes an existing option group. + * + * @param {string} id + */ + removeOptionGroup: function(id) { + if (this.optgroups.hasOwnProperty(id)) { + delete this.optgroups[id]; + this.renderCache = {}; + this.trigger('optgroup_remove', id); + } + }, + + /** + * Clears all existing option groups. + */ + clearOptionGroups: function() { + this.optgroups = {}; + this.renderCache = {}; + this.trigger('optgroup_clear'); + }, + + /** + * Updates an option available for selection. If + * it is visible in the selected items or options + * dropdown, it will be re-rendered automatically. + * + * @param {string} value + * @param {object} data + */ + updateOption: function(value, data) { + var self = this; + var $item, $item_new; + var value_new, index_item, cache_items, cache_options, order_old; + + value = hash_key(value); + value_new = hash_key(data[self.settings.valueField]); + + // sanity checks + if (value === null) return; + if (!self.options.hasOwnProperty(value)) return; + if (typeof value_new !== 'string') throw new Error('Value must be set in option data'); + + order_old = self.options[value].$order; + + // update references + if (value_new !== value) { + delete self.options[value]; + index_item = self.items.indexOf(value); + if (index_item !== -1) { + self.items.splice(index_item, 1, value_new); + } + } + data.$order = data.$order || order_old; + self.options[value_new] = data; + + // invalidate render cache + cache_items = self.renderCache['item']; + cache_options = self.renderCache['option']; + + if (cache_items) { + delete cache_items[value]; + delete cache_items[value_new]; + } + if (cache_options) { + delete cache_options[value]; + delete cache_options[value_new]; + } + + // update the item if it's selected + if (self.items.indexOf(value_new) !== -1) { + $item = self.getItem(value); + $item_new = $(self.render('item', data)); + if ($item.hasClass('active')) $item_new.addClass('active'); + $item.replaceWith($item_new); + } + + // invalidate last query because we might have updated the sortField + self.lastQuery = null; + + // update dropdown contents + if (self.isOpen) { + self.refreshOptions(false); + } + }, + + /** + * Removes a single option. + * + * @param {string} value + * @param {boolean} silent + */ + removeOption: function(value, silent) { + var self = this; + value = hash_key(value); + + var cache_items = self.renderCache['item']; + var cache_options = self.renderCache['option']; + if (cache_items) delete cache_items[value]; + if (cache_options) delete cache_options[value]; + + delete self.userOptions[value]; + delete self.options[value]; + self.lastQuery = null; + self.trigger('option_remove', value); + self.removeItem(value, silent); + }, + + /** + * Clears all options. + */ + clearOptions: function() { + var self = this; + + self.loadedSearches = {}; + self.userOptions = {}; + self.renderCache = {}; + self.options = self.sifter.items = {}; + self.lastQuery = null; + self.trigger('option_clear'); + self.clear(); + }, + + /** + * Returns the jQuery element of the option + * matching the given value. + * + * @param {string} value + * @returns {object} + */ + getOption: function(value) { + return this.getElementWithValue(value, this.$dropdown_content.find('[data-selectable]')); + }, + + /** + * Returns the jQuery element of the next or + * previous selectable option. + * + * @param {object} $option + * @param {int} direction can be 1 for next or -1 for previous + * @return {object} + */ + getAdjacentOption: function($option, direction) { + var $options = this.$dropdown.find('[data-selectable]'); + var index = $options.index($option) + direction; + + return index >= 0 && index < $options.length ? $options.eq(index) : $(); + }, + + /** + * Finds the first element with a "data-value" attribute + * that matches the given value. + * + * @param {mixed} value + * @param {object} $els + * @return {object} + */ + getElementWithValue: function(value, $els) { + value = hash_key(value); + + if (typeof value !== 'undefined' && value !== null) { + for (var i = 0, n = $els.length; i < n; i++) { + if ($els[i].getAttribute('data-value') === value) { + return $($els[i]); + } + } + } + + return $(); + }, + + /** + * Returns the jQuery element of the item + * matching the given value. + * + * @param {string} value + * @returns {object} + */ + getItem: function(value) { + return this.getElementWithValue(value, this.$control.children()); + }, + + /** + * "Selects" multiple items at once. Adds them to the list + * at the current caret position. + * + * @param {string} value + * @param {boolean} silent + */ + addItems: function(values, silent) { + var items = $.isArray(values) ? values : [values]; + for (var i = 0, n = items.length; i < n; i++) { + this.isPending = (i < n - 1); + this.addItem(items[i], silent); + } + }, + + /** + * "Selects" an item. Adds it to the list + * at the current caret position. + * + * @param {string} value + * @param {boolean} silent + */ + addItem: function(value, silent) { + var events = silent ? [] : ['change']; + + debounce_events(this, events, function() { + var $item, $option, $options; + var self = this; + var inputMode = self.settings.mode; + var i, active, value_next, wasFull; + value = hash_key(value); + + if (self.items.indexOf(value) !== -1) { + if (inputMode === 'single') self.close(); + return; + } + + if (!self.options.hasOwnProperty(value)) return; + if (inputMode === 'single') self.clear(silent); + if (inputMode === 'multi' && self.isFull()) return; + + $item = $(self.render('item', self.options[value])); + wasFull = self.isFull(); + self.items.splice(self.caretPos, 0, value); + self.insertAtCaret($item); + if (!self.isPending || (!wasFull && self.isFull())) { + self.refreshState(); + } + + if (self.isSetup) { + $options = self.$dropdown_content.find('[data-selectable]'); + + // update menu / remove the option (if this is not one item being added as part of series) + if (!self.isPending) { + $option = self.getOption(value); + value_next = self.getAdjacentOption($option, 1).attr('data-value'); + self.refreshOptions(self.isFocused && inputMode !== 'single'); + if (value_next) { + self.setActiveOption(self.getOption(value_next)); + } + } + + // hide the menu if the maximum number of items have been selected or no options are left + if (!$options.length || self.isFull()) { + self.close(); + } else { + self.positionDropdown(); + } + + self.updatePlaceholder(); + self.trigger('item_add', value, $item); + self.updateOriginalInput({silent: silent}); + } + }); + }, + + /** + * Removes the selected item matching + * the provided value. + * + * @param {string} value + */ + removeItem: function(value, silent) { + var self = this; + var $item, i, idx; + + $item = (value instanceof $) ? value : self.getItem(value); + value = hash_key($item.attr('data-value')); + i = self.items.indexOf(value); + + if (i !== -1) { + $item.remove(); + if ($item.hasClass('active')) { + idx = self.$activeItems.indexOf($item[0]); + self.$activeItems.splice(idx, 1); + } + + self.items.splice(i, 1); + self.lastQuery = null; + if (!self.settings.persist && self.userOptions.hasOwnProperty(value)) { + self.removeOption(value, silent); + } + + if (i < self.caretPos) { + self.setCaret(self.caretPos - 1); + } + + self.refreshState(); + self.updatePlaceholder(); + self.updateOriginalInput({silent: silent}); + self.positionDropdown(); + self.trigger('item_remove', value, $item); + } + }, + + /** + * Invokes the `create` method provided in the + * selectize options that should provide the data + * for the new item, given the user input. + * + * Once this completes, it will be added + * to the item list. + * + * @param {string} value + * @param {boolean} [triggerDropdown] + * @param {function} [callback] + * @return {boolean} + */ + createItem: function(input, triggerDropdown) { + var self = this; + var caret = self.caretPos; + input = input || $.trim(self.$control_input.val() || ''); + + var callback = arguments[arguments.length - 1]; + if (typeof callback !== 'function') callback = function() {}; + + if (typeof triggerDropdown !== 'boolean') { + triggerDropdown = true; + } + + if (!self.canCreate(input)) { + callback(); + return false; + } + + self.lock(); + + var setup = (typeof self.settings.create === 'function') ? this.settings.create : function(input) { + var data = {}; + data[self.settings.labelField] = input; + data[self.settings.valueField] = input; + return data; + }; + + var create = once(function(data) { + self.unlock(); + + if (!data || typeof data !== 'object') return callback(); + var value = hash_key(data[self.settings.valueField]); + if (typeof value !== 'string') return callback(); + + self.setTextboxValue(''); + self.addOption(data); + self.setCaret(caret); + self.addItem(value); + self.refreshOptions(triggerDropdown && self.settings.mode !== 'single'); + callback(data); + }); + + var output = setup.apply(this, [input, create]); + if (typeof output !== 'undefined') { + create(output); + } + + return true; + }, + + /** + * Re-renders the selected item lists. + */ + refreshItems: function() { + this.lastQuery = null; + + if (this.isSetup) { + this.addItem(this.items); + } + + this.refreshState(); + this.updateOriginalInput(); + }, + + /** + * Updates all state-dependent attributes + * and CSS classes. + */ + refreshState: function() { + var invalid, self = this; + if (self.isRequired) { + if (self.items.length) self.isInvalid = false; + self.$control_input.prop('required', invalid); + } + self.refreshClasses(); + }, + + /** + * Updates all state-dependent CSS classes. + */ + refreshClasses: function() { + var self = this; + var isFull = self.isFull(); + var isLocked = self.isLocked; + + self.$wrapper + .toggleClass('rtl', self.rtl); + + self.$control + .toggleClass('focus', self.isFocused) + .toggleClass('disabled', self.isDisabled) + .toggleClass('required', self.isRequired) + .toggleClass('invalid', self.isInvalid) + .toggleClass('locked', isLocked) + .toggleClass('full', isFull).toggleClass('not-full', !isFull) + .toggleClass('input-active', self.isFocused && !self.isInputHidden) + .toggleClass('dropdown-active', self.isOpen) + .toggleClass('has-options', !$.isEmptyObject(self.options)) + .toggleClass('has-items', self.items.length > 0); + + self.$control_input.data('grow', !isFull && !isLocked); + }, + + /** + * Determines whether or not more items can be added + * to the control without exceeding the user-defined maximum. + * + * @returns {boolean} + */ + isFull: function() { + return this.settings.maxItems !== null && this.items.length >= this.settings.maxItems; + }, + + /** + * Refreshes the original <select> or <input> + * element to reflect the current state. + */ + updateOriginalInput: function(opts) { + var i, n, options, label, self = this; + opts = opts || {}; + + if (self.tagType === TAG_SELECT) { + options = []; + for (i = 0, n = self.items.length; i < n; i++) { + label = self.options[self.items[i]][self.settings.labelField] || ''; + options.push('<option value="' + escape_html(self.items[i]) + '" selected="selected">' + escape_html(label) + '</option>'); + } + if (!options.length && !this.$input.attr('multiple')) { + options.push('<option value="" selected="selected"></option>'); + } + self.$input.html(options.join('')); + } else { + self.$input.val(self.getValue()); + self.$input.attr('value',self.$input.val()); + } + + if (self.isSetup) { + if (!opts.silent) { + self.trigger('change', self.$input.val()); + } + } + }, + + /** + * Shows/hide the input placeholder depending + * on if there items in the list already. + */ + updatePlaceholder: function() { + if (!this.settings.placeholder) return; + var $input = this.$control_input; + + if (this.items.length) { + $input.removeAttr('placeholder'); + } else { + $input.attr('placeholder', this.settings.placeholder); + } + $input.triggerHandler('update', {force: true}); + }, + + /** + * Shows the autocomplete dropdown containing + * the available options. + */ + open: function() { + var self = this; + + if (self.isLocked || self.isOpen || (self.settings.mode === 'multi' && self.isFull())) return; + self.focus(); + self.isOpen = true; + self.refreshState(); + self.$dropdown.css({visibility: 'hidden', display: 'block'}); + self.positionDropdown(); + self.$dropdown.css({visibility: 'visible'}); + self.trigger('dropdown_open', self.$dropdown); + }, + + /** + * Closes the autocomplete dropdown menu. + */ + close: function() { + var self = this; + var trigger = self.isOpen; + + if (self.settings.mode === 'single' && self.items.length) { + self.hideInput(); + } + + self.isOpen = false; + self.$dropdown.hide(); + self.setActiveOption(null); + self.refreshState(); + + if (trigger) self.trigger('dropdown_close', self.$dropdown); + }, + + /** + * Calculates and applies the appropriate + * position of the dropdown. + */ + positionDropdown: function() { + var $control = this.$control; + var offset = this.settings.dropdownParent === 'body' ? $control.offset() : $control.position(); + offset.top += $control.outerHeight(true); + + this.$dropdown.css({ + width : $control.outerWidth(), + top : offset.top, + left : offset.left + }); + }, + + /** + * Resets / clears all selected items + * from the control. + * + * @param {boolean} silent + */ + clear: function(silent) { + var self = this; + + if (!self.items.length) return; + self.$control.children(':not(input)').remove(); + self.items = []; + self.lastQuery = null; + self.setCaret(0); + self.setActiveItem(null); + self.updatePlaceholder(); + self.updateOriginalInput({silent: silent}); + self.refreshState(); + self.showInput(); + self.trigger('clear'); + }, + + /** + * A helper method for inserting an element + * at the current caret position. + * + * @param {object} $el + */ + insertAtCaret: function($el) { + var caret = Math.min(this.caretPos, this.items.length); + if (caret === 0) { + this.$control.prepend($el); + } else { + $(this.$control[0].childNodes[caret]).before($el); + } + this.setCaret(caret + 1); + }, + + /** + * Removes the current selected item(s). + * + * @param {object} e (optional) + * @returns {boolean} + */ + deleteSelection: function(e) { + var i, n, direction, selection, values, caret, option_select, $option_select, $tail; + var self = this; + + direction = (e && e.keyCode === KEY_BACKSPACE) ? -1 : 1; + selection = getSelection(self.$control_input[0]); + + if (self.$activeOption && !self.settings.hideSelected) { + option_select = self.getAdjacentOption(self.$activeOption, -1).attr('data-value'); + } + + // determine items that will be removed + values = []; + + if (self.$activeItems.length) { + $tail = self.$control.children('.active:' + (direction > 0 ? 'last' : 'first')); + caret = self.$control.children(':not(input)').index($tail); + if (direction > 0) { caret++; } + + for (i = 0, n = self.$activeItems.length; i < n; i++) { + values.push($(self.$activeItems[i]).attr('data-value')); + } + if (e) { + e.preventDefault(); + e.stopPropagation(); + } + } else if ((self.isFocused || self.settings.mode === 'single') && self.items.length) { + if (direction < 0 && selection.start === 0 && selection.length === 0) { + values.push(self.items[self.caretPos - 1]); + } else if (direction > 0 && selection.start === self.$control_input.val().length) { + values.push(self.items[self.caretPos]); + } + } + + // allow the callback to abort + if (!values.length || (typeof self.settings.onDelete === 'function' && self.settings.onDelete.apply(self, [values]) === false)) { + return false; + } + + // perform removal + if (typeof caret !== 'undefined') { + self.setCaret(caret); + } + while (values.length) { + self.removeItem(values.pop()); + } + + self.showInput(); + self.positionDropdown(); + self.refreshOptions(true); + + // select previous option + if (option_select) { + $option_select = self.getOption(option_select); + if ($option_select.length) { + self.setActiveOption($option_select); + } + } + + return true; + }, + + /** + * Selects the previous / next item (depending + * on the `direction` argument). + * + * > 0 - right + * < 0 - left + * + * @param {int} direction + * @param {object} e (optional) + */ + advanceSelection: function(direction, e) { + var tail, selection, idx, valueLength, cursorAtEdge, $tail; + var self = this; + + if (direction === 0) return; + if (self.rtl) direction *= -1; + + tail = direction > 0 ? 'last' : 'first'; + selection = getSelection(self.$control_input[0]); + + if (self.isFocused && !self.isInputHidden) { + valueLength = self.$control_input.val().length; + cursorAtEdge = direction < 0 + ? selection.start === 0 && selection.length === 0 + : selection.start === valueLength; + + if (cursorAtEdge && !valueLength) { + self.advanceCaret(direction, e); + } + } else { + $tail = self.$control.children('.active:' + tail); + if ($tail.length) { + idx = self.$control.children(':not(input)').index($tail); + self.setActiveItem(null); + self.setCaret(direction > 0 ? idx + 1 : idx); + } + } + }, + + /** + * Moves the caret left / right. + * + * @param {int} direction + * @param {object} e (optional) + */ + advanceCaret: function(direction, e) { + var self = this, fn, $adj; + + if (direction === 0) return; + + fn = direction > 0 ? 'next' : 'prev'; + if (self.isShiftDown) { + $adj = self.$control_input[fn](); + if ($adj.length) { + self.hideInput(); + self.setActiveItem($adj); + e && e.preventDefault(); + } + } else { + self.setCaret(self.caretPos + direction); + } + }, + + /** + * Moves the caret to the specified index. + * + * @param {int} i + */ + setCaret: function(i) { + var self = this; + + if (self.settings.mode === 'single') { + i = self.items.length; + } else { + i = Math.max(0, Math.min(self.items.length, i)); + } + + if(!self.isPending) { + // the input must be moved by leaving it in place and moving the + // siblings, due to the fact that focus cannot be restored once lost + // on mobile webkit devices + var j, n, fn, $children, $child; + $children = self.$control.children(':not(input)'); + for (j = 0, n = $children.length; j < n; j++) { + $child = $($children[j]).detach(); + if (j < i) { + self.$control_input.before($child); + } else { + self.$control.append($child); + } + } + } + + self.caretPos = i; + }, + + /** + * Disables user input on the control. Used while + * items are being asynchronously created. + */ + lock: function() { + this.close(); + this.isLocked = true; + this.refreshState(); + }, + + /** + * Re-enables user input on the control. + */ + unlock: function() { + this.isLocked = false; + this.refreshState(); + }, + + /** + * Disables user input on the control completely. + * While disabled, it cannot receive focus. + */ + disable: function() { + var self = this; + self.$input.prop('disabled', true); + self.$control_input.prop('disabled', true).prop('tabindex', -1); + self.isDisabled = true; + self.lock(); + }, + + /** + * Enables the control so that it can respond + * to focus and user input. + */ + enable: function() { + var self = this; + self.$input.prop('disabled', false); + self.$control_input.prop('disabled', false).prop('tabindex', self.tabIndex); + self.isDisabled = false; + self.unlock(); + }, + + /** + * Completely destroys the control and + * unbinds all event listeners so that it can + * be garbage collected. + */ + destroy: function() { + var self = this; + var eventNS = self.eventNS; + var revertSettings = self.revertSettings; + + self.trigger('destroy'); + self.off(); + self.$wrapper.remove(); + self.$dropdown.remove(); + + self.$input + .html('') + .append(revertSettings.$children) + .removeAttr('tabindex') + .removeClass('selectized') + .attr({tabindex: revertSettings.tabindex}) + .show(); + + self.$control_input.removeData('grow'); + self.$input.removeData('selectize'); + + $(window).off(eventNS); + $(document).off(eventNS); + $(document.body).off(eventNS); + + delete self.$input[0].selectize; + }, + + /** + * A helper method for rendering "item" and + * "option" templates, given the data. + * + * @param {string} templateName + * @param {object} data + * @returns {string} + */ + render: function(templateName, data) { + var value, id, label; + var html = ''; + var cache = false; + var self = this; + var regex_tag = /^[\t \r\n]*<([a-z][a-z0-9\-_]*(?:\:[a-z][a-z0-9\-_]*)?)/i; + + if (templateName === 'option' || templateName === 'item') { + value = hash_key(data[self.settings.valueField]); + cache = !!value; + } + + // pull markup from cache if it exists + if (cache) { + if (!isset(self.renderCache[templateName])) { + self.renderCache[templateName] = {}; + } + if (self.renderCache[templateName].hasOwnProperty(value)) { + return self.renderCache[templateName][value]; + } + } + + // render markup + html = $(self.settings.render[templateName].apply(this, [data, escape_html])); + + // add mandatory attributes + if (templateName === 'option' || templateName === 'option_create') { + html.attr('data-selectable', ''); + } + else if (templateName === 'optgroup') { + id = data[self.settings.optgroupValueField] || ''; + html.attr('data-group', id); + } + if (templateName === 'option' || templateName === 'item') { + html.attr('data-value', value || ''); + } + + // update cache + if (cache) { + self.renderCache[templateName][value] = html[0]; + } + + return html[0]; + }, + + /** + * Clears the render cache for a template. If + * no template is given, clears all render + * caches. + * + * @param {string} templateName + */ + clearCache: function(templateName) { + var self = this; + if (typeof templateName === 'undefined') { + self.renderCache = {}; + } else { + delete self.renderCache[templateName]; + } + }, + + /** + * Determines whether or not to display the + * create item prompt, given a user input. + * + * @param {string} input + * @return {boolean} + */ + canCreate: function(input) { + var self = this; + if (!self.settings.create) return false; + var filter = self.settings.createFilter; + return input.length + && (typeof filter !== 'function' || filter.apply(self, [input])) + && (typeof filter !== 'string' || new RegExp(filter).test(input)) + && (!(filter instanceof RegExp) || filter.test(input)); + } + + }); + + + Selectize.count = 0; + Selectize.defaults = { + options: [], + optgroups: [], + + plugins: [], + delimiter: ',', + splitOn: null, // regexp or string for splitting up values from a paste command + persist: true, + diacritics: true, + create: false, + createOnBlur: false, + createFilter: null, + highlight: true, + openOnFocus: true, + maxOptions: 1000, + maxItems: null, + hideSelected: null, + addPrecedence: false, + selectOnTab: false, + preload: false, + allowEmptyOption: false, + closeAfterSelect: false, + + scrollDuration: 60, + loadThrottle: 300, + loadingClass: 'loading', + + dataAttr: 'data-data', + optgroupField: 'optgroup', + valueField: 'value', + labelField: 'text', + optgroupLabelField: 'label', + optgroupValueField: 'value', + lockOptgroupOrder: false, + + sortField: '$order', + searchField: ['text'], + searchConjunction: 'and', + + mode: null, + wrapperClass: 'selectize-control', + inputClass: 'selectize-input', + dropdownClass: 'selectize-dropdown', + dropdownContentClass: 'selectize-dropdown-content', + + dropdownParent: null, + + copyClassesToDropdown: true, + + /* + load : null, // function(query, callback) { ... } + score : null, // function(search) { ... } + onInitialize : null, // function() { ... } + onChange : null, // function(value) { ... } + onItemAdd : null, // function(value, $item) { ... } + onItemRemove : null, // function(value) { ... } + onClear : null, // function() { ... } + onOptionAdd : null, // function(value, data) { ... } + onOptionRemove : null, // function(value) { ... } + onOptionClear : null, // function() { ... } + onOptionGroupAdd : null, // function(id, data) { ... } + onOptionGroupRemove : null, // function(id) { ... } + onOptionGroupClear : null, // function() { ... } + onDropdownOpen : null, // function($dropdown) { ... } + onDropdownClose : null, // function($dropdown) { ... } + onType : null, // function(str) { ... } + onDelete : null, // function(values) { ... } + */ + + render: { + /* + item: null, + optgroup: null, + optgroup_header: null, + option: null, + option_create: null + */ + } + }; + + + $.fn.selectize = function(settings_user) { + var defaults = $.fn.selectize.defaults; + var settings = $.extend({}, defaults, settings_user); + var attr_data = settings.dataAttr; + var field_label = settings.labelField; + var field_value = settings.valueField; + var field_optgroup = settings.optgroupField; + var field_optgroup_label = settings.optgroupLabelField; + var field_optgroup_value = settings.optgroupValueField; + + /** + * Initializes selectize from a <input type="text"> element. + * + * @param {object} $input + * @param {object} settings_element + */ + var init_textbox = function($input, settings_element) { + var i, n, values, option; + + var data_raw = $input.attr(attr_data); + + if (!data_raw) { + var value = $.trim($input.val() || ''); + if (!settings.allowEmptyOption && !value.length) return; + values = value.split(settings.delimiter); + for (i = 0, n = values.length; i < n; i++) { + option = {}; + option[field_label] = values[i]; + option[field_value] = values[i]; + settings_element.options.push(option); + } + settings_element.items = values; + } else { + settings_element.options = JSON.parse(data_raw); + for (i = 0, n = settings_element.options.length; i < n; i++) { + settings_element.items.push(settings_element.options[i][field_value]); + } + } + }; + + /** + * Initializes selectize from a <select> element. + * + * @param {object} $input + * @param {object} settings_element + */ + var init_select = function($input, settings_element) { + var i, n, tagName, $children, order = 0; + var options = settings_element.options; + var optionsMap = {}; + + var readData = function($el) { + var data = attr_data && $el.attr(attr_data); + if (typeof data === 'string' && data.length) { + return JSON.parse(data); + } + return null; + }; + + var addOption = function($option, group) { + $option = $($option); + + var value = hash_key($option.val()); + if (!value && !settings.allowEmptyOption) return; + + // if the option already exists, it's probably been + // duplicated in another optgroup. in this case, push + // the current group to the "optgroup" property on the + // existing option so that it's rendered in both places. + if (optionsMap.hasOwnProperty(value)) { + if (group) { + var arr = optionsMap[value][field_optgroup]; + if (!arr) { + optionsMap[value][field_optgroup] = group; + } else if (!$.isArray(arr)) { + optionsMap[value][field_optgroup] = [arr, group]; + } else { + arr.push(group); + } + } + return; + } + + var option = readData($option) || {}; + option[field_label] = option[field_label] || $option.text(); + option[field_value] = option[field_value] || value; + option[field_optgroup] = option[field_optgroup] || group; + + optionsMap[value] = option; + options.push(option); + + if ($option.is(':selected')) { + settings_element.items.push(value); + } + }; + + var addGroup = function($optgroup) { + var i, n, id, optgroup, $options; + + $optgroup = $($optgroup); + id = $optgroup.attr('label'); + + if (id) { + optgroup = readData($optgroup) || {}; + optgroup[field_optgroup_label] = id; + optgroup[field_optgroup_value] = id; + settings_element.optgroups.push(optgroup); + } + + $options = $('option', $optgroup); + for (i = 0, n = $options.length; i < n; i++) { + addOption($options[i], id); + } + }; + + settings_element.maxItems = $input.attr('multiple') ? null : 1; + + $children = $input.children(); + for (i = 0, n = $children.length; i < n; i++) { + tagName = $children[i].tagName.toLowerCase(); + if (tagName === 'optgroup') { + addGroup($children[i]); + } else if (tagName === 'option') { + addOption($children[i]); + } + } + }; + + return this.each(function() { + if (this.selectize) return; + + var instance; + var $input = $(this); + var tag_name = this.tagName.toLowerCase(); + var placeholder = $input.attr('placeholder') || $input.attr('data-placeholder'); + if (!placeholder && !settings.allowEmptyOption) { + placeholder = $input.children('option[value=""]').text(); + } + + var settings_element = { + 'placeholder' : placeholder, + 'options' : [], + 'optgroups' : [], + 'items' : [] + }; + + if (tag_name === 'select') { + init_select($input, settings_element); + } else { + init_textbox($input, settings_element); + } + + instance = new Selectize($input, $.extend(true, {}, defaults, settings_element, settings_user)); + }); + }; + + $.fn.selectize.defaults = Selectize.defaults; + $.fn.selectize.support = { + validity: SUPPORTS_VALIDITY_API + }; + + + Selectize.define('drag_drop', function(options) { + if (!$.fn.sortable) throw new Error('The "drag_drop" plugin requires jQuery UI "sortable".'); + if (this.settings.mode !== 'multi') return; + var self = this; + + self.lock = (function() { + var original = self.lock; + return function() { + var sortable = self.$control.data('sortable'); + if (sortable) sortable.disable(); + return original.apply(self, arguments); + }; + })(); + + self.unlock = (function() { + var original = self.unlock; + return function() { + var sortable = self.$control.data('sortable'); + if (sortable) sortable.enable(); + return original.apply(self, arguments); + }; + })(); + + self.setup = (function() { + var original = self.setup; + return function() { + original.apply(this, arguments); + + var $control = self.$control.sortable({ + items: '[data-value]', + forcePlaceholderSize: true, + disabled: self.isLocked, + start: function(e, ui) { + ui.placeholder.css('width', ui.helper.css('width')); + $control.css({overflow: 'visible'}); + }, + stop: function() { + $control.css({overflow: 'hidden'}); + var active = self.$activeItems ? self.$activeItems.slice() : null; + var values = []; + $control.children('[data-value]').each(function() { + values.push($(this).attr('data-value')); + }); + self.setValue(values); + self.setActiveItem(active); + } + }); + }; + })(); + + }); + + Selectize.define('dropdown_header', function(options) { + var self = this; + + options = $.extend({ + title : 'Untitled', + headerClass : 'selectize-dropdown-header', + titleRowClass : 'selectize-dropdown-header-title', + labelClass : 'selectize-dropdown-header-label', + closeClass : 'selectize-dropdown-header-close', + + html: function(data) { + return ( + '<div class="' + data.headerClass + '">' + + '<div class="' + data.titleRowClass + '">' + + '<span class="' + data.labelClass + '">' + data.title + '</span>' + + '<a href="javascript:void(0)" class="' + data.closeClass + '">×</a>' + + '</div>' + + '</div>' + ); + } + }, options); + + self.setup = (function() { + var original = self.setup; + return function() { + original.apply(self, arguments); + self.$dropdown_header = $(options.html(options)); + self.$dropdown.prepend(self.$dropdown_header); + }; + })(); + + }); + + Selectize.define('optgroup_columns', function(options) { + var self = this; + + options = $.extend({ + equalizeWidth : true, + equalizeHeight : true + }, options); + + this.getAdjacentOption = function($option, direction) { + var $options = $option.closest('[data-group]').find('[data-selectable]'); + var index = $options.index($option) + direction; + + return index >= 0 && index < $options.length ? $options.eq(index) : $(); + }; + + this.onKeyDown = (function() { + var original = self.onKeyDown; + return function(e) { + var index, $option, $options, $optgroup; + + if (this.isOpen && (e.keyCode === KEY_LEFT || e.keyCode === KEY_RIGHT)) { + self.ignoreHover = true; + $optgroup = this.$activeOption.closest('[data-group]'); + index = $optgroup.find('[data-selectable]').index(this.$activeOption); + + if(e.keyCode === KEY_LEFT) { + $optgroup = $optgroup.prev('[data-group]'); + } else { + $optgroup = $optgroup.next('[data-group]'); + } + + $options = $optgroup.find('[data-selectable]'); + $option = $options.eq(Math.min($options.length - 1, index)); + if ($option.length) { + this.setActiveOption($option); + } + return; + } + + return original.apply(this, arguments); + }; + })(); + + var getScrollbarWidth = function() { + var div; + var width = getScrollbarWidth.width; + var doc = document; + + if (typeof width === 'undefined') { + div = doc.createElement('div'); + div.innerHTML = '<div style="width:50px;height:50px;position:absolute;left:-50px;top:-50px;overflow:auto;"><div style="width:1px;height:100px;"></div></div>'; + div = div.firstChild; + doc.body.appendChild(div); + width = getScrollbarWidth.width = div.offsetWidth - div.clientWidth; + doc.body.removeChild(div); + } + return width; + }; + + var equalizeSizes = function() { + var i, n, height_max, width, width_last, width_parent, $optgroups; + + $optgroups = $('[data-group]', self.$dropdown_content); + n = $optgroups.length; + if (!n || !self.$dropdown_content.width()) return; + + if (options.equalizeHeight) { + height_max = 0; + for (i = 0; i < n; i++) { + height_max = Math.max(height_max, $optgroups.eq(i).height()); + } + $optgroups.css({height: height_max}); + } + + if (options.equalizeWidth) { + width_parent = self.$dropdown_content.innerWidth() - getScrollbarWidth(); + width = Math.round(width_parent / n); + $optgroups.css({width: width}); + if (n > 1) { + width_last = width_parent - width * (n - 1); + $optgroups.eq(n - 1).css({width: width_last}); + } + } + }; + + if (options.equalizeHeight || options.equalizeWidth) { + hook.after(this, 'positionDropdown', equalizeSizes); + hook.after(this, 'refreshOptions', equalizeSizes); + } + + + }); + + Selectize.define('remove_button', function(options) { + options = $.extend({ + label : '×', + title : 'Remove', + className : 'remove', + append : true + }, options); + + var singleClose = function(thisRef, options) { + + options.className = 'remove-single'; + + var self = thisRef; + var html = '<a href="javascript:void(0)" class="' + options.className + '" tabindex="-1" title="' + escape_html(options.title) + '">' + options.label + '</a>'; + + /** + * Appends an element as a child (with raw HTML). + * + * @param {string} html_container + * @param {string} html_element + * @return {string} + */ + var append = function(html_container, html_element) { + return html_container + html_element; + }; + + thisRef.setup = (function() { + var original = self.setup; + return function() { + // override the item rendering method to add the button to each + if (options.append) { + var id = $(self.$input.context).attr('id'); + var selectizer = $('#'+id); + + var render_item = self.settings.render.item; + self.settings.render.item = function(data) { + return append(render_item.apply(thisRef, arguments), html); + }; + } + + original.apply(thisRef, arguments); + + // add event listener + thisRef.$control.on('click', '.' + options.className, function(e) { + e.preventDefault(); + if (self.isLocked) return; + + self.clear(); + }); + + }; + })(); + }; + + var multiClose = function(thisRef, options) { + + var self = thisRef; + var html = '<a href="javascript:void(0)" class="' + options.className + '" tabindex="-1" title="' + escape_html(options.title) + '">' + options.label + '</a>'; + + /** + * Appends an element as a child (with raw HTML). + * + * @param {string} html_container + * @param {string} html_element + * @return {string} + */ + var append = function(html_container, html_element) { + var pos = html_container.search(/(<\/[^>]+>\s*)$/); + return html_container.substring(0, pos) + html_element + html_container.substring(pos); + }; + + thisRef.setup = (function() { + var original = self.setup; + return function() { + // override the item rendering method to add the button to each + if (options.append) { + var render_item = self.settings.render.item; + self.settings.render.item = function(data) { + return append(render_item.apply(thisRef, arguments), html); + }; + } + + original.apply(thisRef, arguments); + + // add event listener + thisRef.$control.on('click', '.' + options.className, function(e) { + e.preventDefault(); + if (self.isLocked) return; + + var $item = $(e.currentTarget).parent(); + self.setActiveItem($item); + if (self.deleteSelection()) { + self.setCaret(self.items.length); + } + }); + + }; + })(); + }; + + if (this.settings.mode === 'single') { + singleClose(this, options); + return; + } else { + multiClose(this, options); + } + }); + + + Selectize.define('restore_on_backspace', function(options) { + var self = this; + + options.text = options.text || function(option) { + return option[this.settings.labelField]; + }; + + this.onKeyDown = (function() { + var original = self.onKeyDown; + return function(e) { + var index, option; + if (e.keyCode === KEY_BACKSPACE && this.$control_input.val() === '' && !this.$activeItems.length) { + index = this.caretPos - 1; + if (index >= 0 && index < this.items.length) { + option = this.options[this.items[index]]; + if (this.deleteSelection(e)) { + this.setTextboxValue(options.text.apply(this, [option])); + this.refreshOptions(true); + } + e.preventDefault(); + return; + } + } + return original.apply(this, arguments); + }; + })(); + }); + + + return Selectize; +}));
\ No newline at end of file diff --git a/debian/missing-sources/topojson.js b/debian/missing-sources/topojson.js new file mode 100644 index 0000000..43b43f5 --- /dev/null +++ b/debian/missing-sources/topojson.js @@ -0,0 +1,534 @@ +!function() { + var topojson = { + version: "1.6.19", + mesh: function(topology) { return object(topology, meshArcs.apply(this, arguments)); }, + meshArcs: meshArcs, + merge: function(topology) { return object(topology, mergeArcs.apply(this, arguments)); }, + mergeArcs: mergeArcs, + feature: featureOrCollection, + neighbors: neighbors, + presimplify: presimplify + }; + + function stitchArcs(topology, arcs) { + var stitchedArcs = {}, + fragmentByStart = {}, + fragmentByEnd = {}, + fragments = [], + emptyIndex = -1; + + // Stitch empty arcs first, since they may be subsumed by other arcs. + arcs.forEach(function(i, j) { + var arc = topology.arcs[i < 0 ? ~i : i], t; + if (arc.length < 3 && !arc[1][0] && !arc[1][1]) { + t = arcs[++emptyIndex], arcs[emptyIndex] = i, arcs[j] = t; + } + }); + + arcs.forEach(function(i) { + var e = ends(i), + start = e[0], + end = e[1], + f, g; + + if (f = fragmentByEnd[start]) { + delete fragmentByEnd[f.end]; + f.push(i); + f.end = end; + if (g = fragmentByStart[end]) { + delete fragmentByStart[g.start]; + var fg = g === f ? f : f.concat(g); + fragmentByStart[fg.start = f.start] = fragmentByEnd[fg.end = g.end] = fg; + } else { + fragmentByStart[f.start] = fragmentByEnd[f.end] = f; + } + } else if (f = fragmentByStart[end]) { + delete fragmentByStart[f.start]; + f.unshift(i); + f.start = start; + if (g = fragmentByEnd[start]) { + delete fragmentByEnd[g.end]; + var gf = g === f ? f : g.concat(f); + fragmentByStart[gf.start = g.start] = fragmentByEnd[gf.end = f.end] = gf; + } else { + fragmentByStart[f.start] = fragmentByEnd[f.end] = f; + } + } else { + f = [i]; + fragmentByStart[f.start = start] = fragmentByEnd[f.end = end] = f; + } + }); + + function ends(i) { + var arc = topology.arcs[i < 0 ? ~i : i], p0 = arc[0], p1; + if (topology.transform) p1 = [0, 0], arc.forEach(function(dp) { p1[0] += dp[0], p1[1] += dp[1]; }); + else p1 = arc[arc.length - 1]; + return i < 0 ? [p1, p0] : [p0, p1]; + } + + function flush(fragmentByEnd, fragmentByStart) { + for (var k in fragmentByEnd) { + var f = fragmentByEnd[k]; + delete fragmentByStart[f.start]; + delete f.start; + delete f.end; + f.forEach(function(i) { stitchedArcs[i < 0 ? ~i : i] = 1; }); + fragments.push(f); + } + } + + flush(fragmentByEnd, fragmentByStart); + flush(fragmentByStart, fragmentByEnd); + arcs.forEach(function(i) { if (!stitchedArcs[i < 0 ? ~i : i]) fragments.push([i]); }); + + return fragments; + } + + function meshArcs(topology, o, filter) { + var arcs = []; + + if (arguments.length > 1) { + var geomsByArc = [], + geom; + + function arc(i) { + var j = i < 0 ? ~i : i; + (geomsByArc[j] || (geomsByArc[j] = [])).push({i: i, g: geom}); + } + + function line(arcs) { + arcs.forEach(arc); + } + + function polygon(arcs) { + arcs.forEach(line); + } + + function geometry(o) { + if (o.type === "GeometryCollection") o.geometries.forEach(geometry); + else if (o.type in geometryType) geom = o, geometryType[o.type](o.arcs); + } + + var geometryType = { + LineString: line, + MultiLineString: polygon, + Polygon: polygon, + MultiPolygon: function(arcs) { arcs.forEach(polygon); } + }; + + geometry(o); + + geomsByArc.forEach(arguments.length < 3 + ? function(geoms) { arcs.push(geoms[0].i); } + : function(geoms) { if (filter(geoms[0].g, geoms[geoms.length - 1].g)) arcs.push(geoms[0].i); }); + } else { + for (var i = 0, n = topology.arcs.length; i < n; ++i) arcs.push(i); + } + + return {type: "MultiLineString", arcs: stitchArcs(topology, arcs)}; + } + + function mergeArcs(topology, objects) { + var polygonsByArc = {}, + polygons = [], + components = []; + + objects.forEach(function(o) { + if (o.type === "Polygon") register(o.arcs); + else if (o.type === "MultiPolygon") o.arcs.forEach(register); + }); + + function register(polygon) { + polygon.forEach(function(ring) { + ring.forEach(function(arc) { + (polygonsByArc[arc = arc < 0 ? ~arc : arc] || (polygonsByArc[arc] = [])).push(polygon); + }); + }); + polygons.push(polygon); + } + + function exterior(ring) { + return cartesianRingArea(object(topology, {type: "Polygon", arcs: [ring]}).coordinates[0]) > 0; // TODO allow spherical? + } + + polygons.forEach(function(polygon) { + if (!polygon._) { + var component = [], + neighbors = [polygon]; + polygon._ = 1; + components.push(component); + while (polygon = neighbors.pop()) { + component.push(polygon); + polygon.forEach(function(ring) { + ring.forEach(function(arc) { + polygonsByArc[arc < 0 ? ~arc : arc].forEach(function(polygon) { + if (!polygon._) { + polygon._ = 1; + neighbors.push(polygon); + } + }); + }); + }); + } + } + }); + + polygons.forEach(function(polygon) { + delete polygon._; + }); + + return { + type: "MultiPolygon", + arcs: components.map(function(polygons) { + var arcs = []; + + // Extract the exterior (unique) arcs. + polygons.forEach(function(polygon) { + polygon.forEach(function(ring) { + ring.forEach(function(arc) { + if (polygonsByArc[arc < 0 ? ~arc : arc].length < 2) { + arcs.push(arc); + } + }); + }); + }); + + // Stitch the arcs into one or more rings. + arcs = stitchArcs(topology, arcs); + + // If more than one ring is returned, + // at most one of these rings can be the exterior; + // this exterior ring has the same winding order + // as any exterior ring in the original polygons. + if ((n = arcs.length) > 1) { + var sgn = exterior(polygons[0][0]); + for (var i = 0, t; i < n; ++i) { + if (sgn === exterior(arcs[i])) { + t = arcs[0], arcs[0] = arcs[i], arcs[i] = t; + break; + } + } + } + + return arcs; + }) + }; + } + + function featureOrCollection(topology, o) { + return o.type === "GeometryCollection" ? { + type: "FeatureCollection", + features: o.geometries.map(function(o) { return feature(topology, o); }) + } : feature(topology, o); + } + + function feature(topology, o) { + var f = { + type: "Feature", + id: o.id, + properties: o.properties || {}, + geometry: object(topology, o) + }; + if (o.id == null) delete f.id; + return f; + } + + function object(topology, o) { + var absolute = transformAbsolute(topology.transform), + arcs = topology.arcs; + + function arc(i, points) { + if (points.length) points.pop(); + for (var a = arcs[i < 0 ? ~i : i], k = 0, n = a.length, p; k < n; ++k) { + points.push(p = a[k].slice()); + absolute(p, k); + } + if (i < 0) reverse(points, n); + } + + function point(p) { + p = p.slice(); + absolute(p, 0); + return p; + } + + function line(arcs) { + var points = []; + for (var i = 0, n = arcs.length; i < n; ++i) arc(arcs[i], points); + if (points.length < 2) points.push(points[0].slice()); + return points; + } + + function ring(arcs) { + var points = line(arcs); + while (points.length < 4) points.push(points[0].slice()); + return points; + } + + function polygon(arcs) { + return arcs.map(ring); + } + + function geometry(o) { + var t = o.type; + return t === "GeometryCollection" ? {type: t, geometries: o.geometries.map(geometry)} + : t in geometryType ? {type: t, coordinates: geometryType[t](o)} + : null; + } + + var geometryType = { + Point: function(o) { return point(o.coordinates); }, + MultiPoint: function(o) { return o.coordinates.map(point); }, + LineString: function(o) { return line(o.arcs); }, + MultiLineString: function(o) { return o.arcs.map(line); }, + Polygon: function(o) { return polygon(o.arcs); }, + MultiPolygon: function(o) { return o.arcs.map(polygon); } + }; + + return geometry(o); + } + + function reverse(array, n) { + var t, j = array.length, i = j - n; while (i < --j) t = array[i], array[i++] = array[j], array[j] = t; + } + + function bisect(a, x) { + var lo = 0, hi = a.length; + while (lo < hi) { + var mid = lo + hi >>> 1; + if (a[mid] < x) lo = mid + 1; + else hi = mid; + } + return lo; + } + + function neighbors(objects) { + var indexesByArc = {}, // arc index -> array of object indexes + neighbors = objects.map(function() { return []; }); + + function line(arcs, i) { + arcs.forEach(function(a) { + if (a < 0) a = ~a; + var o = indexesByArc[a]; + if (o) o.push(i); + else indexesByArc[a] = [i]; + }); + } + + function polygon(arcs, i) { + arcs.forEach(function(arc) { line(arc, i); }); + } + + function geometry(o, i) { + if (o.type === "GeometryCollection") o.geometries.forEach(function(o) { geometry(o, i); }); + else if (o.type in geometryType) geometryType[o.type](o.arcs, i); + } + + var geometryType = { + LineString: line, + MultiLineString: polygon, + Polygon: polygon, + MultiPolygon: function(arcs, i) { arcs.forEach(function(arc) { polygon(arc, i); }); } + }; + + objects.forEach(geometry); + + for (var i in indexesByArc) { + for (var indexes = indexesByArc[i], m = indexes.length, j = 0; j < m; ++j) { + for (var k = j + 1; k < m; ++k) { + var ij = indexes[j], ik = indexes[k], n; + if ((n = neighbors[ij])[i = bisect(n, ik)] !== ik) n.splice(i, 0, ik); + if ((n = neighbors[ik])[i = bisect(n, ij)] !== ij) n.splice(i, 0, ij); + } + } + } + + return neighbors; + } + + function presimplify(topology, triangleArea) { + var absolute = transformAbsolute(topology.transform), + relative = transformRelative(topology.transform), + heap = minAreaHeap(); + + if (!triangleArea) triangleArea = cartesianTriangleArea; + + topology.arcs.forEach(function(arc) { + var triangles = [], + maxArea = 0, + triangle; + + // To store each point’s effective area, we create a new array rather than + // extending the passed-in point to workaround a Chrome/V8 bug (getting + // stuck in smi mode). For midpoints, the initial effective area of + // Infinity will be computed in the next step. + for (var i = 0, n = arc.length, p; i < n; ++i) { + p = arc[i]; + absolute(arc[i] = [p[0], p[1], Infinity], i); + } + + for (var i = 1, n = arc.length - 1; i < n; ++i) { + triangle = arc.slice(i - 1, i + 2); + triangle[1][2] = triangleArea(triangle); + triangles.push(triangle); + heap.push(triangle); + } + + for (var i = 0, n = triangles.length; i < n; ++i) { + triangle = triangles[i]; + triangle.previous = triangles[i - 1]; + triangle.next = triangles[i + 1]; + } + + while (triangle = heap.pop()) { + var previous = triangle.previous, + next = triangle.next; + + // If the area of the current point is less than that of the previous point + // to be eliminated, use the latter's area instead. This ensures that the + // current point cannot be eliminated without eliminating previously- + // eliminated points. + if (triangle[1][2] < maxArea) triangle[1][2] = maxArea; + else maxArea = triangle[1][2]; + + if (previous) { + previous.next = next; + previous[2] = triangle[2]; + update(previous); + } + + if (next) { + next.previous = previous; + next[0] = triangle[0]; + update(next); + } + } + + arc.forEach(relative); + }); + + function update(triangle) { + heap.remove(triangle); + triangle[1][2] = triangleArea(triangle); + heap.push(triangle); + } + + return topology; + }; + + function cartesianRingArea(ring) { + var i = -1, + n = ring.length, + a, + b = ring[n - 1], + area = 0; + + while (++i < n) { + a = b; + b = ring[i]; + area += a[0] * b[1] - a[1] * b[0]; + } + + return area * .5; + } + + function cartesianTriangleArea(triangle) { + var a = triangle[0], b = triangle[1], c = triangle[2]; + return Math.abs((a[0] - c[0]) * (b[1] - a[1]) - (a[0] - b[0]) * (c[1] - a[1])); + } + + function compareArea(a, b) { + return a[1][2] - b[1][2]; + } + + function minAreaHeap() { + var heap = {}, + array = [], + size = 0; + + heap.push = function(object) { + up(array[object._ = size] = object, size++); + return size; + }; + + heap.pop = function() { + if (size <= 0) return; + var removed = array[0], object; + if (--size > 0) object = array[size], down(array[object._ = 0] = object, 0); + return removed; + }; + + heap.remove = function(removed) { + var i = removed._, object; + if (array[i] !== removed) return; // invalid request + if (i !== --size) object = array[size], (compareArea(object, removed) < 0 ? up : down)(array[object._ = i] = object, i); + return i; + }; + + function up(object, i) { + while (i > 0) { + var j = ((i + 1) >> 1) - 1, + parent = array[j]; + if (compareArea(object, parent) >= 0) break; + array[parent._ = i] = parent; + array[object._ = i = j] = object; + } + } + + function down(object, i) { + while (true) { + var r = (i + 1) << 1, + l = r - 1, + j = i, + child = array[j]; + if (l < size && compareArea(array[l], child) < 0) child = array[j = l]; + if (r < size && compareArea(array[r], child) < 0) child = array[j = r]; + if (j === i) break; + array[child._ = i] = child; + array[object._ = i = j] = object; + } + } + + return heap; + } + + function transformAbsolute(transform) { + if (!transform) return noop; + var x0, + y0, + kx = transform.scale[0], + ky = transform.scale[1], + dx = transform.translate[0], + dy = transform.translate[1]; + return function(point, i) { + if (!i) x0 = y0 = 0; + point[0] = (x0 += point[0]) * kx + dx; + point[1] = (y0 += point[1]) * ky + dy; + }; + } + + function transformRelative(transform) { + if (!transform) return noop; + var x0, + y0, + kx = transform.scale[0], + ky = transform.scale[1], + dx = transform.translate[0], + dy = transform.translate[1]; + return function(point, i) { + if (!i) x0 = y0 = 0; + var x1 = (point[0] - dx) / kx | 0, + y1 = (point[1] - dy) / ky | 0; + point[0] = x1 - x0; + point[1] = y1 - y0; + x0 = x1; + y0 = y1; + }; + } + + function noop() {} + + if (typeof define === "function" && define.amd) define(topojson); + else if (typeof module === "object" && module.exports) module.exports = topojson; + else this.topojson = topojson; +}(); diff --git a/debian/not-installed b/debian/not-installed new file mode 100644 index 0000000..c6d2cd2 --- /dev/null +++ b/debian/not-installed @@ -0,0 +1,9 @@ +usr/lib/*/knot-resolver/kres_modules/http/LICENSE +usr/include/libkres/*.h +usr/lib/*/*.so +usr/lib/*/pkgconfig/libkres.pc +usr/share/doc/knot-resolver/AUTHORS +usr/share/doc/knot-resolver/COPYING +usr/share/doc/knot-resolver/NEWS +usr/share/doc/knot-resolver/html/.buildinfo +usr/lib/sysusers.d/knot-resolver.conf diff --git a/debian/po/POTFILES.in b/debian/po/POTFILES.in new file mode 100644 index 0000000..a810d1a --- /dev/null +++ b/debian/po/POTFILES.in @@ -0,0 +1 @@ +[type: gettext/rfc822deb] knot-resolver.templates diff --git a/debian/po/de.po b/debian/po/de.po new file mode 100644 index 0000000..292d30d --- /dev/null +++ b/debian/po/de.po @@ -0,0 +1,69 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Helge Kreutzmann <debian@helgefjell.de>, 2020. +# +msgid "" +msgstr "" +"Project-Id-Version: knot-resolver 5.1.2-3\n" +"Report-Msgid-Bugs-To: knot-resolver@packages.debian.org\n" +"POT-Creation-Date: 2020-09-14 11:52+0200\n" +"PO-Revision-Date: 2020-08-29 20:33+0200\n" +"Last-Translator: Helge Kreutzmann <debian@helgefjell.de>\n" +"Language-Team: German <debian-l10n-german@lists.debian.org>\n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#. Type: note +#. Description +#: ../knot-resolver.templates:1001 +msgid "Upgrading from Knot Resolver < 5.x" +msgstr "Upgrade von Knot Resolver < 5.x" + +#. Type: note +#. Description +#: ../knot-resolver.templates:1001 +msgid "Knot Resolver configuration file requires manual upgrade." +msgstr "Konfigurationsdatei von Knot Resolver benötigt manuelles Upgrade" + +# FIXME: Up to → Before? +#. Type: note +#. Description +#: ../knot-resolver.templates:1001 +msgid "" +"Up to Knot Resolver 4.x, network interface bindings and service start were " +"done by default via systemd sockets. These systemd sockets are no longer " +"supported, and upgrading from a 3.x version (as in Debian Buster) requires " +"manual action to (re)enable the service." +msgstr "" +"Bis Knot Resolver 4.x erfolgte die Anbindung der Netzwerkschnittstellen und " +"das Starten des Dienstes standardmäßig über Systemd-Sockets. Diese Systemd-" +"Sockets werden nicht mehr unterstützt und ein Upgrade von einer 3.x-Version " +"(wie in Debian Buster) benötigt einen manuellen Eingriff, um den Dienst " +"(wieder) zu aktivieren." + +#. Type: note +#. Description +#: ../knot-resolver.templates:1001 +msgid "" +"Please refer to kresd.systemd(7) and to the /usr/share/doc/knot-resolver/" +"upgrading.html file (from knot-resolver-doc). An online version of the " +"latter is available at https://knot-resolver.readthedocs.io/en/stable/" +"upgrading.html#x-to-5-x" +msgstr "" +"Bitte lesen Sie kresd.systemd(7) und die Datei /usr/share/doc/knot-resolver/" +"upgrading.html (aus knot-resolver-doc). Eine Online-Version Letzterer ist " +"unter https://knot-resolver.readthedocs.io/en/stable/upgrading.html#x-to-5-x " +"verfügbar." + +#. Type: note +#. Description +#: ../knot-resolver.templates:1001 +msgid "" +"For convenience, a suggested networking configuration can be found in the " +"file /var/lib/knot-resolver/.upgrade-4-to-5/kresd.conf.net" +msgstr "" +"Der Einfachheit halber kann eine vorgeschlagene Netzwerkkonfiguration in der " +"Datei /var/lib/knot-resolver/.upgrade-4-to-5/kresd.conf.net gefunden werden." diff --git a/debian/po/es.po b/debian/po/es.po new file mode 100644 index 0000000..bbac38e --- /dev/null +++ b/debian/po/es.po @@ -0,0 +1,68 @@ +# knot-resolver po-debconf translation to Spanish. +# Copyright (C) 2021 +# This file is distributed under the same license as the knot-resolver package. +# Camaleón <noelamac@gmail.com>, 2021. +# +msgid "" +msgstr "" +"Project-Id-Version: knot-resolver\n" +"Report-Msgid-Bugs-To: knot-resolver@packages.debian.org\n" +"POT-Creation-Date: 2020-09-14 11:52+0200\n" +"PO-Revision-Date: 2021-04-15 17:20+0200\n" +"Last-Translator: Camaleón <noelamac@gmail.com>\n" +"Language-Team: Debian Spanish <debian-l10n-spanish@lists.debian.org>\n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#. Type: note +#. Description +#: ../knot-resolver.templates:1001 +msgid "Upgrading from Knot Resolver < 5.x" +msgstr "Actualización desde Knot Resolver < 5.x" + +#. Type: note +#. Description +#: ../knot-resolver.templates:1001 +msgid "Knot Resolver configuration file requires manual upgrade." +msgstr "Tiene que actualizar manualmente el archivo de configuración de " +"Knot Resolver." + +#. Type: note +#. Description +#: ../knot-resolver.templates:1001 +msgid "" +"Up to Knot Resolver 4.x, network interface bindings and service start were " +"done by default via systemd sockets. These systemd sockets are no longer " +"supported, and upgrading from a 3.x version (as in Debian Buster) requires " +"manual action to (re)enable the service." +msgstr "" +"Hasta la versión 4.x de Knot Resolver, los enlaces de interfaz de red y el " +"inicio del servicio se realizaban a través de sockets de systemd. Ya no se " +"admiten estos sockets de systemd, y la actualización desde una versión 3.x " +"(como en Debian Buster) requiere un ajuste manual para reactivar el servicio." + +#. Type: note +#. Description +#: ../knot-resolver.templates:1001 +msgid "" +"Please refer to kresd.systemd(7) and to the /usr/share/doc/knot-resolver/" +"upgrading.html file (from knot-resolver-doc). An online version of the " +"latter is available at https://knot-resolver.readthedocs.io/en/stable/" +"upgrading.html#x-to-5-x" +msgstr "" +"Por favor, consulte kresd.systemd(7) y el archivo «/usr/share/doc/" +"knot-resolver/upgrading.html (en el paquete knot-resolver-doc). Tiene " +"disponible una versión en línea de este último documento en «https://knot-" +"resolver.readthedocs.io/en/stable/upgrading.html#x-to-5-x»." + +#. Type: note +#. Description +#: ../knot-resolver.templates:1001 +msgid "" +"For convenience, a suggested networking configuration can be found in the " +"file /var/lib/knot-resolver/.upgrade-4-to-5/kresd.conf.net" +msgstr "" +"Para su comodidad, dispone de una configuración de red recomendada en el " +"archivo «/var/lib/knot-resolver/.upgrade-4-to-5/kresd.conf.net»." diff --git a/debian/po/fr.po b/debian/po/fr.po new file mode 100644 index 0000000..d57b8b8 --- /dev/null +++ b/debian/po/fr.po @@ -0,0 +1,73 @@ +# Translation of knote-resolver debconf templates to french. +# Copyright (C) 2020, French l10n team <debian-l10n-french@lists.debian.org> +# This file is distributed under the same license as the knote-resolver package. +# +# Jean-Pierre Giraud <jean-pierregiraud@neuf.fr>, 2020. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: knot-resolver@packages.debian.org\n" +"POT-Creation-Date: 2020-09-14 11:52+0200\n" +"PO-Revision-Date: 2020-08-22 12:50+0100\n" +"Last-Translator: Jean-Pierre Giraud <jean-pierregiraud@neuf.fr>\n" +"Language-Team: French <debian-l10n-french@lists.debian.org>\n" +"Language: fr_FR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"X-Generator: Lokalize 2.0\n" + +#. Type: note +#. Description +#: ../knot-resolver.templates:1001 +msgid "Upgrading from Knot Resolver < 5.x" +msgstr "Mise à niveau à partir de Knot < 5.x" + +#. Type: note +#. Description +#: ../knot-resolver.templates:1001 +msgid "Knot Resolver configuration file requires manual upgrade." +msgstr "" +"Le fichier de configuration de Knot Resolver requiert une mise à niveau " +"manuelle." + +#. Type: note +#. Description +#: ../knot-resolver.templates:1001 +msgid "" +"Up to Knot Resolver 4.x, network interface bindings and service start were " +"done by default via systemd sockets. These systemd sockets are no longer " +"supported, and upgrading from a 3.x version (as in Debian Buster) requires " +"manual action to (re)enable the service." +msgstr "" +"Jusqu'à Knot Resolver 4.x, les liaisons avec l'interface réseau et le " +"démarrage du service étaient réalisés par défaut au moyen de sockets de " +"systemd. Ces sockets de systemd ne sont plus pris en charge et la mise à " +"niveau à partir d'une version 3.x (comme dans Debian Buster) nécessite une " +"action manuelle pour (ré)activer le service." + +#. Type: note +#. Description +#: ../knot-resolver.templates:1001 +msgid "" +"Please refer to kresd.systemd(7) and to the /usr/share/doc/knot-resolver/" +"upgrading.html file (from knot-resolver-doc). An online version of the " +"latter is available at https://knot-resolver.readthedocs.io/en/stable/" +"upgrading.html#x-to-5-x" +msgstr "" +"Veuillez consulter kresd.systemd(7) et le fichier /usr/share/doc/knot-" +"resolver/upgrading.html (dans knot-resolver-doc). Une version en ligne de ce " +"fichier est disponible à l'adresse https://knot-resolver.readthedocs.io/en/" +"stable/upgrading.html#x-to-5-x ." + +#. Type: note +#. Description +#: ../knot-resolver.templates:1001 +msgid "" +"For convenience, a suggested networking configuration can be found in the " +"file /var/lib/knot-resolver/.upgrade-4-to-5/kresd.conf.net" +msgstr "" +"Pour des raisons de commodité, une suggestion de configuration de réseau est " +"disponible dans le fichier /var/lib/knot-resolver/.upgrade-4-to-5/kresd.conf." +"net ." diff --git a/debian/po/nl.po b/debian/po/nl.po new file mode 100644 index 0000000..d33f241 --- /dev/null +++ b/debian/po/nl.po @@ -0,0 +1,71 @@ +# Dutch translation of po-debconf messages for knot-resolver. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the knot-resolver package. +# Frans Spiesschaert <Frans.Spiesschaert@yucom.be>, 2020. +# +msgid "" +msgstr "" +"Project-Id-Version: knot-resolver_5.1.2-2\n" +"Report-Msgid-Bugs-To: knot-resolver@packages.debian.org\n" +"POT-Creation-Date: 2020-09-14 11:52+0200\n" +"PO-Revision-Date: 2020-08-11 23:00+0200\n" +"Last-Translator: Frans Spiesschaert <Frans.Spiesschaert@yucom.be>\n" +"Language-Team: Debian Dutch l10n Team <debian-l10n-dutch@lists.debian.org>\n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Gtranslator 3.30.1\n" + +#. Type: note +#. Description +#: ../knot-resolver.templates:1001 +msgid "Upgrading from Knot Resolver < 5.x" +msgstr "Er vindt een opwaardering van Knot Resolver < 5.x plaats" + +#. Type: note +#. Description +#: ../knot-resolver.templates:1001 +msgid "Knot Resolver configuration file requires manual upgrade." +msgstr "" +"Het configuratiebestand van Knot Resolver heeft een handmatige opwaardering " +"nodig." + +#. Type: note +#. Description +#: ../knot-resolver.templates:1001 +msgid "" +"Up to Knot Resolver 4.x, network interface bindings and service start were " +"done by default via systemd sockets. These systemd sockets are no longer " +"supported, and upgrading from a 3.x version (as in Debian Buster) requires " +"manual action to (re)enable the service." +msgstr "" +"Tot Knot Resolver 4.x werden netwerkinterfacebindingen en het starten van de " +"service standaard uitgevoerd via systemd-sockets. Deze systemd-sockets " +"worden niet langer ondersteund en een opwaardering vanaf een 3.x versie " +"(zoals in Debian Buster) vereist een handmatige interventie om de dienst " +"(opnieuw) te activeren." + +#. Type: note +#. Description +#: ../knot-resolver.templates:1001 +msgid "" +"Please refer to kresd.systemd(7) and to the /usr/share/doc/knot-resolver/" +"upgrading.html file (from knot-resolver-doc). An online version of the " +"latter is available at https://knot-resolver.readthedocs.io/en/stable/" +"upgrading.html#x-to-5-x" +msgstr "" +"Raadpleeg kresd.systemd(7) en het bestand /usr/share/doc/knot-resolver/" +"upgrading.html (uit knot-resolver-doc). Een onlineversie van dit laatste is " +"beschikbaar op https://knot-resolver.readthedocs.io/en/stable/upgrading." +"html#x-to-5-x" + +#. Type: note +#. Description +#: ../knot-resolver.templates:1001 +msgid "" +"For convenience, a suggested networking configuration can be found in the " +"file /var/lib/knot-resolver/.upgrade-4-to-5/kresd.conf.net" +msgstr "" +"Om het u makkelijker te maken, is een voorgestelde netwerkconfiguratie te " +"vinden in het bestand /var/lib/knot-resolver/.upgrade-4-to-5/kresd.conf.net" diff --git a/debian/po/pt.po b/debian/po/pt.po new file mode 100644 index 0000000..383ddc9 --- /dev/null +++ b/debian/po/pt.po @@ -0,0 +1,71 @@ +# Translation of knot-resolver debconf messages to European Portuguese +# Copyright (C) 2021 THE knot-resolver'S COPYRIGHT HOLDER +# This file is distributed under the same license as the knot-resolver package. +# +# Américo Monteiro <a_monteiro@gmx.com>, 2021. +msgid "" +msgstr "" +"Project-Id-Version: knot-resolver_5.2.1-1\n" +"Report-Msgid-Bugs-To: knot-resolver@packages.debian.org\n" +"POT-Creation-Date: 2020-09-14 11:52+0200\n" +"PO-Revision-Date: 2021-02-08 21:59+0000\n" +"Last-Translator: Américo Monteiro <a_monteiro@gmx.com>\n" +"Language-Team: Portuguese <>\n" +"Language: pt\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Lokalize 2.0\n" + +#. Type: note +#. Description +#: ../knot-resolver.templates:1001 +msgid "Upgrading from Knot Resolver < 5.x" +msgstr "Actualizar a partir de Knot Resolver < 5.x" + +#. Type: note +#. Description +#: ../knot-resolver.templates:1001 +msgid "Knot Resolver configuration file requires manual upgrade." +msgstr "" +"O ficheiro de configuração do Knot Resolver requer actualização manual." + +#. Type: note +#. Description +#: ../knot-resolver.templates:1001 +msgid "" +"Up to Knot Resolver 4.x, network interface bindings and service start were " +"done by default via systemd sockets. These systemd sockets are no longer " +"supported, and upgrading from a 3.x version (as in Debian Buster) requires " +"manual action to (re)enable the service." +msgstr "" +"Até ao Knot Resolver 4.x, as uniões de interface de rede e o arranque do " +"serviço eram feitos por predefinição via sockets do systemd. Estes sockets " +"do systemd não são mais suportados, e actualizar a partir de uma versão 3.x " +"(como em Debian Buster) requer acção manual para (re)activar o serviço." + +#. Type: note +#. Description +#: ../knot-resolver.templates:1001 +msgid "" +"Please refer to kresd.systemd(7) and to the /usr/share/doc/knot-resolver/" +"upgrading.html file (from knot-resolver-doc). An online version of the " +"latter is available at https://knot-resolver.readthedocs.io/en/stable/" +"upgrading.html#x-to-5-x" +msgstr "" +"Por favor consulte kresd.systemd(7) e o ficheiro /usr/share/doc/knot-resolver/" +"upgrading.html (de knot-resolver-doc). Está disponível uma versão online " +"mais recente em https://knot-resolver.readthedocs.io/en/stable/" +"upgrading.html#x-to-5-x" + +#. Type: note +#. Description +#: ../knot-resolver.templates:1001 +msgid "" +"For convenience, a suggested networking configuration can be found in the " +"file /var/lib/knot-resolver/.upgrade-4-to-5/kresd.conf.net" +msgstr "" +"Para conveniência, pode ser encontrada uma configuração de rede sugerida " +"no ficheiro /var/lib/knot-resolver/.upgrade-4-to-5/kresd.conf.net" + diff --git a/debian/po/templates.pot b/debian/po/templates.pot new file mode 100644 index 0000000..a30afe3 --- /dev/null +++ b/debian/po/templates.pot @@ -0,0 +1,58 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the knot-resolver package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: knot-resolver\n" +"Report-Msgid-Bugs-To: knot-resolver@packages.debian.org\n" +"POT-Creation-Date: 2020-09-14 11:52+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#. Type: note +#. Description +#: ../knot-resolver.templates:1001 +msgid "Upgrading from Knot Resolver < 5.x" +msgstr "" + +#. Type: note +#. Description +#: ../knot-resolver.templates:1001 +msgid "Knot Resolver configuration file requires manual upgrade." +msgstr "" + +#. Type: note +#. Description +#: ../knot-resolver.templates:1001 +msgid "" +"Up to Knot Resolver 4.x, network interface bindings and service start were " +"done by default via systemd sockets. These systemd sockets are no longer " +"supported, and upgrading from a 3.x version (as in Debian Buster) requires " +"manual action to (re)enable the service." +msgstr "" + +#. Type: note +#. Description +#: ../knot-resolver.templates:1001 +msgid "" +"Please refer to kresd.systemd(7) and to the /usr/share/doc/knot-resolver/" +"upgrading.html file (from knot-resolver-doc). An online version of the " +"latter is available at https://knot-resolver.readthedocs.io/en/stable/" +"upgrading.html#x-to-5-x" +msgstr "" + +#. Type: note +#. Description +#: ../knot-resolver.templates:1001 +msgid "" +"For convenience, a suggested networking configuration can be found in the " +"file /var/lib/knot-resolver/.upgrade-4-to-5/kresd.conf.net" +msgstr "" diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..44e6bc4 --- /dev/null +++ b/debian/rules @@ -0,0 +1,57 @@ +#!/usr/bin/make -f + +# see FEATURE AREAS in dpkg-buildflags(1) +export DEB_BUILD_MAINT_OPTIONS = hardening=+all + +# see ENVIRONMENT in dpkg-buildflags(1) +# package maintainers to append CFLAGS +export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic -fno-omit-frame-pointer +# package maintainers to append LDFLAGS +export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed + +# see EXAMPLES in dpkg-buildflags(1) and read /usr/share/dpkg/* +DPKG_EXPORT_BUILDFLAGS = 1 +include /usr/share/dpkg/default.mk + +ifneq (,$(findstring arm64,$(DEB_TARGET_ARCH))) + CONFIG_TESTS=disabled +else + CONFIG_TESTS=enabled +endif + +%: + dh $@ + +override_dh_auto_configure: + dh_auto_configure -- \ + -Dkeyfile_default=/usr/share/dns/root.key \ + -Dinstall_root_keys=disabled \ + -Dmanaged_ta=disabled \ + -Droot_hints=/usr/share/dns/root.hints \ + -Dinstall_kresd_conf=enabled \ + -Dsystemd_files=enabled \ + -Dclient=enabled \ + -Dutils=enabled \ + -Ddoc=enabled \ + -Ddnstap=enabled \ + -Dunit_tests=enabled \ + -Dconfig_tests=$(CONFIG_TESTS) \ + -Dextra_tests=disabled \ + -Dmalloc=jemalloc + +override_dh_auto_build: + dh_auto_build -- doc + dh_auto_build + +override_dh_auto_install: + sed -e's/@DEB_HOST_MULTIARCH@/$(DEB_HOST_MULTIARCH)/g' <debian/knot-resolver.postinst.in >debian/knot-resolver.postinst + dh_auto_install + +override_dh_installinit: + dh_installinit -pknot-resolver --name=kresd --no-start + +override_dh_missing: + dh_missing --fail-missing + +override_dh_installchangelogs: + dh_installchangelogs NEWS diff --git a/debian/salsa-ci.yml b/debian/salsa-ci.yml new file mode 100644 index 0000000..984a817 --- /dev/null +++ b/debian/salsa-ci.yml @@ -0,0 +1,7 @@ +--- +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 + +lintian: + allow_failure: true diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..163aaf8 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/debian/source/lintian-overrides b/debian/source/lintian-overrides new file mode 100644 index 0000000..984a554 --- /dev/null +++ b/debian/source/lintian-overrides @@ -0,0 +1,9 @@ +# the lines in epoch.js are just human-written long lines: +knot-resolver source: source-contains-prebuilt-javascript-object debian/missing-sources/epoch.js line length is 287 characters (>256) +knot-resolver source: source-is-missing debian/missing-sources/epoch.js line length is 287 characters (>256) +# the style line in datamaps.world.js is an ugly one-liner but still human-modifiable +knot-resolver source: very-long-line-length-in-source-file debian/missing-sources/datamaps.world.js line length is 832 characters (>512) +knot-resolver source: source-contains-prebuilt-javascript-object debian/missing-sources/datamaps.world.js line length is 826 characters (>512) +knot-resolver source: source-is-missing debian/missing-sources/datamaps.world.js line length is 826 characters (>512) +# same for dygraph.js +knot-resolver source: source-is-missing debian/missing-sources/dygraph.js line length is 847 characters (>512) diff --git a/debian/tests/control b/debian/tests/control new file mode 100644 index 0000000..949d99b --- /dev/null +++ b/debian/tests/control @@ -0,0 +1,6 @@ +Tests: roundtrip +Depends: gnutls-bin, + knot-dnsutils, + knot-resolver, + socat +Restrictions: needs-root, skip-not-installable diff --git a/debian/tests/roundtrip b/debian/tests/roundtrip new file mode 100755 index 0000000..42c7519 --- /dev/null +++ b/debian/tests/roundtrip @@ -0,0 +1,134 @@ +#!/bin/bash + +# Author: Daniel Kahn Gillmor <dkg@fifthhorseman.net> +# 2018-08-30 +# License: GPLv3+ + +# error on exit +set -e +# for handling jobspecs: +set -m + +if [ -z "$AUTOPKGTEST_ARTIFACTS" ]; then + d="$(mktemp -d)" + remove="$d" +else + d="$AUTOPKGTEST_ARTIFACTS" +fi +ip="${TESTIP:-127.$(( $RANDOM % 256 )).$(( $RANDOM % 256 )).$(( $RANDOM % 256 ))}" +kresd="${KRESD:-/usr/sbin/kresd}" +kdig="${KDIG:-$(command -v kdig)}" + +declare -a kresd_args=(--addr="$ip@8053" --tls="$ip@8853" --noninteractive --config="$d/kresd.conf" --verbose --verbose --verbose) +if [ -n "$MODULE_DIR" ]; then + kresd_args+=(-m "$MODULE_DIR") +fi + +printf "%s + %s roundtrip tests\n------------\n workdir: %s\n IP addr: %s\n kresd args: %s\n" "$kresd" "$kdig" "$d" "$ip" "${kresd_args[*]}" + +section() { + printf "\n%s\n" "$1" + sed 's/./-/g' <<<"$1" +} + +cleanup () { + section "cleaning up" + find "$d" -ls + tail -n +1 -v "$d"/*.err + ctrl_socket=/run/knot-resolver/control/1 + if [ -S "$ctrl_socket" ]; then + echo 'quit()' | socat STDIO "UNIX-CONNECT:$ctrl_socket" + wait %1 + else + pkill -KILL kresd ; true + fi + + if [ "$remove" ]; then + printf "\ncleaning up working directory %s\n" "$remove" + rm -rf "$remove" + fi +} +trap cleanup EXIT + +section "make Certificate Authority key and certificate" +cat > "$d/ca.template" <<EOF +cn = "testing certificate authority (NOT FOR PRODUCTION)" +expiration_days = 12 +ca +path_len = 1 +nc_permit_dns = example +cert_signing_key +EOF +certtool --stdout-info --generate-privkey --outfile "$d/ca-key.pem" +certtool --stdout-info --generate-self-signed --template "$d/ca.template" --load-privkey "$d/ca-key.pem" --outfile "$d/ca-cert.pem" + +section "make Bogus Certificate Authority key and certificate" +certtool --stdout-info --generate-privkey --outfile "$d/bogus-key.pem" +certtool --stdout-info --generate-self-signed --template "$d/ca.template" --load-privkey "$d/bogus-key.pem" --outfile "$d/bogus-cert.pem" + +section "make End Entity key and certificate" +cat > "$d/ee.template" <<EOF +cn = "test.example" +dns_name = test.example +expiration_days = 10 +signing_key +tls_www_server +EOF +certtool --stdout-info --generate-privkey --outfile "$d/ee-key.pem" +certtool --stdout-info --pubkey-info --load-privkey "$d/ee-key.pem" --outfile "$d/ee-pubkey.pem" +certtool --stdout-info --generate-certificate --load-ca-privkey "$d/ca-key.pem" --load-ca-certificate "$d/ca-cert.pem" --template "$d/ee.template" --load-pubkey "$d/ee-pubkey.pem" --outfile "$d/ee-cert.pem" + +section "set up kresd daemon on $ip on ports 8053 (UDP, TCP) and 8853 (TLS)" +cat > "$d/kresd.conf" <<EOF +verbose(true) +modules = { 'hints > iterate' } +net.tls("$d/ee-cert.pem", "$d/ee-key.pem") +hints["monkeys.example"] = "127.15.23.5" + +local path = '/run/knot-resolver/control/1' +local ok, err = pcall(net.listen, path, nil, { kind = 'control' }) +if not ok then + warn('bind to '..path..' failed '..err) +end +EOF +"$kresd" "${kresd_args[@]}" "$d" 2> "$d/kresd.err" & + +sleep 1 + +section "test UDP with kdig" +x=$("$kdig" +short +time=2 +retry=0 @"$ip:8053" monkeys.example) +[ "$x" = "127.15.23.5" ] +echo "successful UDP request to $ip on port 8053" + +section "test TCP with kdig" +x=$("$kdig" +short +tcp @"$ip:8053" monkeys.example) +[ "$x" = "127.15.23.5" ] +echo "successful TCP request to $ip on port 8053" + +section "test opportunistic DNS-over-TLS with kdig" +x=$("$kdig" +short +tls @"$ip:8853" monkeys.example) +[ "$x" = "127.15.23.5" ] +echo "successful opportunistic DNS-over-TLS request to $ip on port 8853" + +section "test strict DNS-over-TLS with kdig" +x=$("$kdig" +short +tls +tls-ca="$d/ca-cert.pem" +tls-hostname=test.example @"$ip:8853" monkeys.example) +[ "$x" = "127.15.23.5" ] +echo "successful strict DNS-over-TLS request to $ip on port 8853" + +section "test invalid name with strict DNS-over-TLS with kdig" +# Kdig returns non-zero code if error since version 2.7.5 +x=$("$kdig" +tls +tls-ca="$d/ca-cert.pem" +tls-hostname=notright.example @"$ip:8853" monkeys.example 2>"$d/badname.err" || true) +if [ "$x" ]; then + printf >&2 "got: %s\nShould not have succeeded since name did not match!" "$x" + false +fi +echo "successful strict DNS-over-TLS request failure when name mismatch to $ip on port 8853" + +section "test bad authority with strict DNS-over-TLS with kdig" +# Kdig returns non-zero code if error since version 2.7.5 +x=$("$kdig" +tls-ca="$d/bogus-cert.pem" +tls-hostname=test.example @"$ip:8853" monkeys.example 2>"$d/badca.err" || true) +if [ "$x" ]; then + printf >&2 "got: %s\nShould not have succeeded since authority was wrong!" "$x" + false +fi +echo "successful strict DNS-over-TLS request failure to $ip on port 8853" diff --git a/debian/upstream/signing-key.asc b/debian/upstream/signing-key.asc new file mode 100644 index 0000000..1b34475 --- /dev/null +++ b/debian/upstream/signing-key.asc @@ -0,0 +1,126 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBGImJYQBEACoQzmvkGLYGeltnY2MzvjZlpZk/M670+vxI6e7ofto4AQQmvGW +0eann8bUl8qZnliOvqq57akCRTYLaT9ArFjfaaYs60qFD7KCFTAe4GHry/J4aWiP +Q7jftfpDerTLr+LvikJ9dJrMLV/TA79fgEb3DcGOtuZMventwJbZKS5iiAgaN/mW +ZZIh/kQNvylKy4aTm21yx2XFx4eVDRd1aucJWTrR9DmM3Nm02rpNvQxjxWfDU0cg +ne6iWFHseUDA40B5r/hwaEYD4cDOXh+adI0WkH5662TkrewzxE1LE+uyScTEL57i +zTHyeEW+1DHSyprtTZggUThRay4RLPIW4PVV+kkv/9kwtk1LWIbHbzilbKkKKoea +uIzTGJnxGGbV0uyRbH3QJTtddags5rBskemqPgg9momwMIgUtBSQn10zMX/s5bUF +yP9MwKVNzEkgtxeWvjSmKCpqmmD8oiXO1iL5bcZsJfJH1p9nHqDVG4+EJjSbu+wr +eM424BxXap+bdvoxKr5ofTswEyvlwn47aejQQVciDDb+AN8nufJeOrfyYQ798DXj +PhkR3CFVGfLCGkAAoTrhEePmn1DqKJMxlDB8YSZKTvF+TNBLVpVExu/MsVMX7mMh +uG+OFrDn/mCFe+q0fqMEQOpa5XFzhzaRd93khifR+sYnF5fiE92nG60psQARAQAB +tCBBbGVzIE1yYXplayA8YWxlcy5tcmF6ZWtAbmljLmN6PokCVAQTAQgAPhYhBDBX +7ppEjzYtdCBad5qxINoKdvbeBQJiJiWEAhsDBQkDwmcABQsJCAcCBhUKCQgLAgQW +AgMBAh4BAheAAAoJEJqxINoKdvbe5tsQAI4KnAGf+YkPGLdCTQo7bi9eww3zVoJk +m0WKTf67zLd7JC8puCGPWRVaVSBsuUKDrp57I3h736+Jx0XiwRA6xlFmgvVIWrFr +swGXlLbdutezXE2ke5y+86uj6k5TQKn3jEWfmkRigFLlUy5U+VJEt6b/i7e9EmMb +T9dI4d7JW7MPY74jYlFJ4klZvp/BL5ldz9R2yprOnwBX4Bjwm4FFR71NPC/iK2Dn +19pSH+YeRpsTU1I1vBMzz4qlEkhhE1+pqYbxYpyfkmSxJxGvD7FFr8GHnFwcnqqv +zZuinIz+eGf7iSql6yxz7OxDnVhBYnSQpCkSWtGdhHEVtQo0uA7gcxcdz5URBScQ +ofR/Zs/fvEvWqRXKm+I/qC1nK/glfaoA81xI6OGOPkrYEoLi9WKGK0nWSbnux9qY +/vyzVAKUDjEtYFm/Nk/23D4tBcC0/tZfuVFedWa6rcqKVNoeRdu2Nol9L4quL601 +vm/kIQDhUsQmWbvIkwx6pHZoFw+9COY1NFINOqguBYzyMIlVJpo6Xns+1gldJ7vI +0w6F+wpmUkFNnEACQ3dG/uf6WNzEbgEdKFQ2xgLN4zY7nJI3QLRlrXntk6BzG7Lj +iQe7ZS4uhaQQ9bzTuvhi3qKJEJUGaPQRvYJoCoDQs1gLR/rep0qQpRsUt7Tzb0uZ +xeeZHSvvhDq5uQINBGImJYQBEADaE8cZEkr06+TnlQ+8qSBHRFJSEunNKjUr8t0y +zwPgFZhRIRtw3mCFqDEv/eBPw9tsQeW6aJz5+VI5t7Ifkt8krHPE8GT5N33MyUKj +8sRk1oQNt/2JdDCOy5ZigyqFj31/9XeIWvlQbBLUTL0V3EPAmcipegEx9VFD81FW +qH4TbBer0oLsa7zFnZ455FIn1a76UNC+73DE3+eV1V51euhx3TIgn76SXe8Qf+jg +Asv7Ylfe++g3Smkt5UAgEZQOFwPbYMjS4U5sRq8hpJw+nBkI2n/vHoLeSKWZjt6J +/yth+tLQmsQRwyYk97Tp8mzPpmWSMWIGPDrfyBdV1DmToQkWDozuBs5Mz8gVvrMu +2mNe4O0m5Xv7e80Gm/OJxSeB/o8tEXFE12JEM1c88OZ8Iz/PcfSgotbcS5zVxRcm +yU/ZqMRNvJHtG932DIhZ/s0NdAxnsAeCz9gaR6Z4orVXb8G7/S1pe0ab66k19VX5 +wFoPIz92SUq91VbrCsjfHi4FeE7h2OL9QP10Liv9qOxJFYvy2fvWG9EXuknjaZsD +igmC1DuX/7uWJbrGbp9spnuNsgm7wYG5nFH8fTnNKtaMl2/D8D5WbB0k+MJnBDeW +MyDKPsaCStX2ih90DGSOkWlZy1k3X+v+PTPPxu1ry/MePSo0UHud9L2oWLbnz27I +bBRymwARAQABiQI8BBgBCAAmFiEEMFfumkSPNi10IFp3mrEg2gp29t4FAmImJYQC +GwwFCQPCZwAACgkQmrEg2gp29t7Trw//cN9UEaN1WaH4jFDKIST+xdLnSLvk0Izt +hHqJzzW9sRQYINvqz89EkTUrjelbW3Ib41YVNNk3S7hOm5OJm/fRHdQtItzuY/9E +RafF5+PUiDj5TRDa8bRV7sCAQLoztfr1ozadbGE7e8HrrZv4DcZ10joCpLRZm04j +QMBeZcaaEa2Wh7Xf9VW4aBJ/DU5DevsKeTRpCuwtw2lEf+NMl3PogPa9glm7liQu +DKU4Zlf/31ZCudAb80PugOzWM2i1DG26G/b7z+8N59m6dBYWeSvcqU7Z9Q4rU7bV +R/6nST8GyLIU/PsDalvKm2B3NDcXerN8lT9zU1QBzT3OyL6Uy/UzXl6YnpxFT8m/ +Gm4uiLS+gWD2TXh5BKGsuU4zIHakjglA7Gtb6PJ56+Pkz95apx7vAhmnJxgw7qAX +KAH0Rng7IxEeL8Fj67tzWTQPs0PuAty7qIgbeft8hvhtk9vfFZb8k3bhX59b/+TO +tbTOCjVhmq9bZdaw60SBl3LuyUlnlGmrqvPzVUdcAc2zvnxjEt9yVioS/M8zNRC/ +SmWbagZ/xyf2iiEAbo0y12t3heztdG8jMoMrImmUEubfPiAfCN2ys5zPiXW2jio5 +vvopiu23y37ew3zoAZrnfmFW0D8A8iD2g0kl/Ysb2dXIYocQ423ADN1Em/+1f8j5 +3P6DgfI04ROZAg0EWAOSdgEQAMcRDAsGV+ptULOruphwEUVNzVYIAW2qIp6E6uwh +Ocq6xnDieFjCUpDFy6aCPTXEk6Ft8cEIF4aUPJ1ip6YqDgh9jafBLI/Bllgwmfsv +sAasJ7K2vhUy8MnujuoNmOOKbiJ+/4ZTB2TemVfHqgwXidy16wjxoA64tQcCfQ9+ +sZ82FahqCU4qB04joMaOEj9oaS/L6Osj/5ex0xX7d/VG4Q58j5i7hJcWMnKECaoQ +4FHHF0eoqt4ginPcuDfzJbCJUZ/CBBP6cUjP0GkcfeVaDW5LzEzd7Nx4GZFhXzNP +0+9t5nnsGVpNHqstp7lFjBXf3s9IsGqouQHkfRMNFQUGfAGxShst5tgeYkI7J2/I +2/kAWQuYtns7iPaMWjVY9Iy3dOEzrBHCqucHgaL7qifKtl8TUOdVkypvFndVdr4T +hLljCt8ItQSsYSf53jQ62hFmZBeFE9UCx2OKYyP2gUz1GjFKBSsODohrefQisoVv +77McAVuqRHmDirNeI4wQbrbUZgR7HawMQrP88PgJUqU0j16V3hOgfn3aQDDMmZ36 +wCL5F+izQ0TPG4V3R6QA55ahRGjTOhhNJY+Uw6YUy9ykfos/eV4mZjvHjP9vWeD/ +7tScqidUAtU8VtFsSU6Qh8C0spxQSh/BXWOvsPJgY9qUm2IMk+iYHTjVkcKPZHHJ +dj+hABEBAAG0KVZsYWRpbcOtciDEjHVuw6F0IChwZXJzb25hbCkgPHZAY3VuYXQu +Y3o+iQJUBBMBCAA+FiEEtgBkYLYKgOeCBiRJ50ffH5V1o6oFAmFmsBgCGyMFCRTR +36EFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQ50ffH5V1o6r5GBAAxkNaB8zi +TVWBZ5geqcQeoqCeTrZjDZ1pY32BbjefGb0z0fmRzH9ESQBGH+b46PO7cibOt3sY +d4ZX8GxG/vnwsU3dOspaIQfHH99yXr+sCRkcr2k7DWTMFSX/5JkhlrouAURpIRN8 +y4+OO/TkWFY3HAIEy3VkhwIoJSUEDfSINb1KHnotNzHc7s1Z3yb14K14XV/LnQL/ +/NTz32NBGSDAt3C3+w4h5WKhg+kPcuno1VVUHM4AcqqLkhEbNN/QwQULWbrQMqcB ++JJWlEgSqkhn2nrw/i8q+tlK7FBi4DhID99xRljt2HcPt7gqhnZ/2OxfoHGHui/f +FPgmeK+c/wHZ22FH4ly0XwsSdIjskhRf/XPTaBlgeorfyIEBFBeFkgjdFUNSpl1M +Tso6ZYxXkLg1UxEFtI+X5Eh7zpirfs6jwc44aMHKabJY3V/MBmYqUBRR3H6xnIFd +gfjVeFAR65w7MElJXRx2kvJ5ixpV5W2+U//MvhkpN6Vk1Y2G9NXmQeGXz2l+2A9V +4qpTn/nVAIchYFtVPzsAJlyPAqB7I45uNMQOcsGdn4W5lLgD4MNdgwIUce9jQ1Oo +F0BnZSCJGLjU0ZxVUOd7fwF+P+Y8RBxBIhVcvxaluI3YmT0roqCXaJaYGAWgXZCF +9g0NnfvR0dRRZRUeT/qLxJNZksK+iFtLIlu0MFZsYWRpbcOtciDEjHVuw6F0ICh3 +b3JrKSA8dmxhZGltaXIuY3VuYXRAbmljLmN6PokCVAQTAQgAPgIbIwUJFNHfoQUL +CQgHAgYVCAkKCwIEFgIDAQIeAQIXgBYhBLYAZGC2CoDnggYkSedH3x+VdaOqBQJh +ZrAYAAoJEOdH3x+VdaOqVjIP/1N5v2soWf7T2OcromFmL76QDqAIwzYtuwookFK8 +UBiGEAw+lz8dHSPqiQF8IiaVxroWeKp7vHKjSeRCr2a9eKsYzTdz3jStwPnKZGpP +5OMJb2x5dZNxyKaL1Fyidq0N9LDIkxanMORPYVA2c1tHmFJFJoj8EmUhIYN8iAl5 +ZG2u3vELI9B3ZvNJb8OJUxAOcvPVyuxKMzpJY2C8K4Ec2joLugG+aCoZh7DTi4EO +tqAOxr/jfCPEdPHEIKEOdqXy5DtoiA5e6uHA+DpseBC/jM2jrvO+IRqnFvr7M7+Q +yfv0ZbkFefsJp5tjojCv6xJPtz/BKwh9wxNtUJrZfFL71Otz4/0YAwiX4z+kljop +x0i4ALVUOiWb7i/2PiP23n4JnPAfLdlClk9CeTkCOu0CAgBvrKi1w/7eEnGqOgTu +4OaGfFaOtTjuFqKS0wOdBRGST5h3pKjxrDxN8HgI+ZpF1EfMd6ovfLWpMVIwE9cV +ueimcpAqfSubQECTjwnsDSmzc26apaCHL8FaXf2pG9J5uDczxdcUzVWfgb+16Wdg +bFoqJ82O2SCysTcEkrNH8m2R5i7iCaO5WlQFLf2FHOBWLd3RjV1ztCImv4di5skw +QLwgW2+wi0QowVPgPc38JAPa73INRhaPK3JUcwqNEr9JUNfRGyL+eTwTX4b5YKHd +dJ9KtCRWbGFkaW3DrXIgxIx1bsOhdCA8dmN1bmF0QGdtYWlsLmNvbT6JAlcEEwEI +AEECGyMFCRTR36EFCwkIBwIGFQgJCgsCBBYCAwECHgECF4AWIQS2AGRgtgqA54IG +JEnnR98flXWjqgUCYWawGAIZAQAKCRDnR98flXWjqjpMEACClbUGJ3e0p1Nty0Lf +kRsoWZMoyeWSoBA0owUsQOyquKKaSj+40qHMDOhbNfJoLpwFsU5zJL4I2rhDlW+z +Y5Het5x1afS0lExzXK3RMPWwhUwMmWevUpW+VFjvPyBhdlEP8E0Ayj6z/p5GEN9Z +9yruZfz9ru48KDvwUYz/f2V0cAPqOIcSM6euGLXnK4ExML+8Wiqgmht8qBozD0Lo +odyAWMR1D+XkAMR+ugwUfs449+x7zzzfkRh8CfggB9mgMK0i2b66MdWFHYW2c9jc +3SIEaWZM56pEG9BCZpcoiiGgCOMWQ7CEn2dOfH2Ah1ZiICyDnL9XvESYJBoRLiNG +cLrD31GJzUrDPRLzK9f8j4AlWN7bNECw5pmPYE2vizFOuasojBV6hlg+c0bXpQSK +ybg1eQb0rE5yOWrA0pP5yNc6ZIBxtjbYLbwmWltb9mWMznVSNfcjvA7QXe3srYYL +AOW1KPz2Ly8qCTZXcpXx2Z2mk4FTNDIj4maQxLTIURHWHckHrXxENxMXNcbaZ4d4 +LVz5f1RBhsFfmAXpVu/FTvXIPlIWaNVlQDWBMxlVidpeztB4XgPUqKWmtMNyCTsN +PeuxrRiyIm2ccXE+fcuPDstLIeQdX+U/rijx1f588i6nt2oD0vy9tEchFeiczql1 +YzYNM5U8u/rCYiOxmQWf7gZecbkCDQRYA5J2ARAAyHww3huLEtsdyqgjiGMhtEKO +Lmp7yFl450HY9oPcHS02U5BC1370ssNShrdOCi2ACDbe41Zxx85WcuaO1OVqung2 +umX047mj2xQsiTAFRDLZsQu8cQFoEy/DBL2bk7ThfK1Lh+NyZAs0UaPpDkGodS0D +e9osA+4T6Nf4POYaeavbYVFSdDKS4lUboBqApKnD/TzKFxFcpuFx6FN92lteTbOo +jGMiLoZvELY86Kn9KuFZ8FM2ZSNHx1Z75KouufGrdkeCoZYVYiuzT+fnt2it4dIp +IlnF+yxMt5LB/MSrmECB5CAFJtxzuMccm6yDUZQSWWi9vUgxIJwvt5w0CIBT353D +GeP4WnH0r5YoBKoRbh7i4fT0lWvMXTG/V2lqyzBdClMebyHffMgba26Kj6oeDygD +fC5aGsVaqw1Ue/qQ5QRqTJcJV7xVLTtS1EamVqkfKwPS0zTfnrF1jQtnO/P4qkfg +BRRG9BXGGrykHpXOyqmX6Z0wbV2P4j+p02oSecDl5yVXplJfsXfbS/xXnaSkaN/7 +mCU29ul26cAVNxDkDPunztSFi9K9LM2T/XWYJQGXM71OpmONQJGF24lx7Wp/kobn +HtbjGDzjDPC4eSL7MA56qtrWaLM+4ePKANct2q0q6c0uSLs0Q2zochS64Mcg0YzL +1sinWPN1rXLDk3lwpIsAEQEAAYkCWgQYAQgADwUCYWawGAIbDAUJFNHfoQA/CRDn +R98flXWjqgkQ50ffH5V1o6oJEOdH3x+VdaOqCRDnR98flXWjqhYhBLYAZGC2CoDn +ggYkSedH3x+VdaOqtBgQALeChWgHEGe/8nwKWrC8CMQyKyiJRlSfvERi5M40PYxw +KC7IHo+ekMdlLc8kVkv5WSq3zQcNoRMjHutZlCVpGJat5PwiCine6I3Z5JfKsU7f +JE4KXPD0jr9kCy/IHlCWsKLZTHH0LOk98tqZcFQwFM5nQEzu8Us86BxlZs5IaN0j +ILD2qUC+EYeg0hd0kya+16UNao9NxCeUEoSwxhtB4IIMCJTUPx0pWErCHxeCUjvx +Pd2h5A106DT/3fa8uhPHr9goHtfreV0soGuxu1rqGrMn7XcorsGdt9XX4tyPHcXD +XCZMLedjYTu7OcYQ63hi+ZWUXJPvcIrB4StCumaFECwLodpdDWB7n6OF8r+X//eP +bjS8qt++A20LGD4l7BrWxDeymrnqLmWN8RBf2xV4ytVfcRx1ercFMGiNRUvGSh+k +gP6J7D4yT4xGTq+fCtS6BHyWMB0loXcWv0Im/6znpIpUcvXyL68s4Jiqukt94rKl +wb/IB6MSWannxk8UzWfGgFXeGUrR8YWLuQSo26AceNOyu2gMW+k9kYi0sqkBzMPk +1zWZN/gSNpDyu7AWGpv4RtOKhJhaux8zwtwqozqSx+plpnYz5ifwCNMuznM6T0j4 +msKQiK1N7lIwKBGaT2P8SJ/ecaJyudIz4ds2+GUl6fxqSR27Egs+EUY0BmS7wESr +=dRuR +-----END PGP PUBLIC KEY BLOCK----- diff --git a/debian/watch b/debian/watch new file mode 100644 index 0000000..26bed73 --- /dev/null +++ b/debian/watch @@ -0,0 +1,4 @@ +version=4 +opts=pgpsigurlmangle=s/$/.asc/ \ +https://secure.nic.cz/files/knot-resolver/ \ +(?:|.*/)@PACKAGE@-([\d.]*)@ARCHIVE_EXT@ |